diff options
200 files changed, 68138 insertions, 0 deletions
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h new file mode 100644 index 00000000000..1178fad93c3 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h @@ -0,0 +1,1609 @@ +/** + @file AABSPTree.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-11 + @edited 2008-11-19 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + + */ + +#ifndef G3D_KDTREE_H +#define G3D_KDTREE_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Table.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/Triangle.h" +#include "G3D/Ray.h" +#include "G3D/GCamera.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/CollisionDetection.h" +#include "G3D/GCamera.h" +#include "G3D/BoundsTrait.h" +#include <algorithm> + +// If defined, in debug mode the tree is checked for consistency +// as a way of detecting corruption due to implementation bugs +// #define VERIFY_TREE + +template<> struct BoundsTrait<class G3D::Vector2> { + static void getBounds(const G3D::Vector2& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(v, 0)); } +}; + +template<> struct BoundsTrait<class G3D::Vector3> { + static void getBounds(const G3D::Vector3& v, G3D::AABox& out) { out = G3D::AABox(v); } +}; + +template<> struct BoundsTrait<class G3D::Vector4> { + static void getBounds(const G3D::Vector4& v, G3D::AABox& out) { out = G3D::AABox(v.xyz()); } +}; + +template<> struct BoundsTrait<class G3D::AABox> { + static void getBounds(const G3D::AABox& v, G3D::AABox& out) { out = v; } +}; + +template<> struct BoundsTrait<class G3D::Sphere> { + static void getBounds(const G3D::Sphere& s, G3D::AABox& out) { s.getBounds(out); } +}; + +template<> struct BoundsTrait<class G3D::Box> { + static void getBounds(const G3D::Box& b, G3D::AABox& out) { b.getBounds(out); } +}; + +template<> struct BoundsTrait<class G3D::Vector2*> { + static void getBounds(const G3D::Vector2*& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(*v, 0)); } +}; + +template<> struct BoundsTrait<class G3D::Vector3*> { + static void getBounds(const G3D::Vector3*& v, G3D::AABox& out) { out = G3D::AABox(*v); } +}; + +template<> struct BoundsTrait<class G3D::Vector4*> { + static void getBounds(const G3D::Vector4*& v, G3D::AABox& out) { out = G3D::AABox(v->xyz()); } +}; + +template<> struct BoundsTrait<class G3D::AABox*> { + static void getBounds(const G3D::AABox*& v, G3D::AABox& out) { out = *v; } +}; + +template<> struct BoundsTrait<class G3D::Sphere*> { + static void getBounds(const G3D::Sphere*& s, G3D::AABox& out) { s->getBounds(out); } +}; + +template<> struct BoundsTrait<class G3D::Box*> { + static void getBounds(const G3D::Box*& b, G3D::AABox& out) { b->getBounds(out); } +}; + + +template<> struct BoundsTrait<class G3D::Triangle*> { + static void getBounds(const G3D::Triangle*& t, G3D::AABox& out) { t->getBounds(out); } +}; + +namespace G3D { + namespace _internal { + + /** + Wraps a pointer value so that it can be treated as the instance itself; + convenient for inserting pointers into a Table but using the + object equality instead of pointer equality. + */ + template<class Type> + class Indirector { + public: + Type* handle; + + inline Indirector(Type* h) : handle(h) {} + + inline Indirector() : handle(NULL) {} + + /** Returns true iff the values referenced by the handles are equivalent. */ + inline bool operator==(const Indirector& m) const { + return *handle == *(m.handle); + } + + inline bool operator==(const Type& m) const { + return *handle == m; + } + + inline size_t hashCode() const { + return handle->hashCode(); + } + }; + } // namespace internal +} // namespace G3D + +template <class Handle> struct HashTrait<typename G3D::_internal::Indirector<Handle> > { + static size_t hashCode(const G3D::_internal::Indirector<Handle>& key) { return key.hashCode(); } +}; + +namespace G3D { + +/** + A set that supports spatial queries using a KD tree (axis-aligned + BSP tree) for speed. + + KDTree allows you to quickly find objects in 3D that lie within + a box or along a ray. For large sets of objects it is much faster + than testing each object for a collision. + + KDTree is as powerful as but more general than a Quad Tree, Oct + Tree, or regular KD tree that cycles through axes, but less general than an unconstrained BSP tree + (which is much slower to create). + + Internally, objects + are arranged into a tree according to their + axis-aligned bounds. This increases the cost of insertion to + O(log n) but allows fast overlap queries. + + <B>Template Parameters</B> + <DT>The template parameter <I>T</I> must be one for which + the following functions are all overloaded: + + <pre> + T::T();</CODE> <I>(public constructor of no arguments)</I> + template <> struct HashTrait<T> { static size_t hashCode(int key); }; + template<> struct BoundsTrait<T> { static void getBounds(const T& obj, G3D::AABox& out); }; + </pre> + + G3D provides these for common classes like G3D::Vector3 and G3D::Sphere. + If you use a custom class, or a pointer to a custom class, you will need + to define those functions. + + <B>Moving %Set Members</B> + <DT>It is important that objects do not move without updating the + KDTree. If the axis-aligned bounds of an object are about + to change, KDTree::remove it before they change and + KDTree::insert it again afterward. For objects + where the hashCode and == operator are invariant with respect + to the 3D position, + you can use the KDTree::update method as a shortcut to + insert/remove an object in one step after it has moved. + + + Note: Do not mutate any value once it has been inserted into KDTree. Values + are copied interally. All KDTree iterators convert to pointers to constant + values to reinforce this. + + If you want to mutate the objects you intend to store in a KDTree + simply insert <I>pointers</I> to your objects instead of the objects + themselves, and ensure that the above operations are defined. (And + actually, because values are copied, if your values are large you may + want to insert pointers anyway, to save space and make the balance + operation faster.) + + <B>Dimensions</B> + Although designed as a 3D-data structure, you can use the KDTree + for data distributed along 2 or 1 axes by simply returning bounds + that are always zero along one or more dimensions. + +*/ +template< class T, + class BoundsFunc = BoundsTrait<T>, + class HashFunc = HashTrait<T>, + class EqualsFunc = EqualsTrait<T> > +class KDTree { +protected: +#define TreeType KDTree<T, BoundsFunc, HashFunc, EqualsFunc> + + /** Wrapper for a value that includes a cache of its bounds. + Except for the test value used in a set-query operation, there + is only ever one instance of the handle associated with any + value and the memberTable and Nodes maintain pointers to that + heap-allocated value. + */ + class Handle { + public: + /** The bounds of each object are constrained to AABox::large */ + AABox bounds; + + /** Center of bounds. We cache this value to avoid recomputing it + during the median sort, and because MSVC 6 std::sort goes into + an infinite loop if we compute the midpoint on the fly (possibly + a floating point roundoff issue, where B<A and A<B both are true).*/ + Vector3 center; + + T value; + + Handle() {} + + inline Handle(const T& v) : value(v) { + BoundsFunc::getBounds(v, bounds); + bounds = bounds.intersect(AABox::large()); + center = bounds.center(); + } + + inline bool operator==(const Handle& other) const { + return EqualsFunc::equals(value, other.value); + } + + inline size_t hashCode() const { + return HashFunc::hashCode(value); + } + }; + + /** Returns the bounds of the sub array. Used by makeNode. */ + static AABox computeBounds( + const Array<Handle*>& point, + int beginIndex, + int endIndex) { + + Vector3 lo = Vector3::inf(); + Vector3 hi = -lo; + + debugAssertM(beginIndex <= endIndex, "No points"); + for (int p = beginIndex; p <= endIndex; ++p) { + // This code is written with the vector min and max expanded + // because otherwise it compiles incorrectly with -O3 on + // gcc 3.4 + + const Vector3& pLo = point[p]->bounds.low(); + const Vector3& pHi = point[p]->bounds.high(); + for (int a = 0; a < 3; ++a) { + lo[a] = G3D::min(lo[a], pLo[a]); + hi[a] = G3D::max(hi[a], pHi[a]); + } + } + + return AABox(lo, hi); + } + + /** Compares centers */ + class CenterComparator { + public: + Vector3::Axis sortAxis; + + CenterComparator(Vector3::Axis a) : sortAxis(a) {} + + inline int operator()(Handle* A, const Handle* B) const { + float a = A->center[sortAxis]; + float b = B->center[sortAxis]; + + if (a < b) { + return 1; + } else if (a > b) { + return -1; + } else { + return 0; + } + } + }; + + + /** Compares bounds for strict >, <, or overlap*/ + class BoundsComparator { + public: + Vector3::Axis sortAxis; + + BoundsComparator(Vector3::Axis a) : sortAxis(a) {} + + inline int operator()(Handle* A, const Handle* B) const { + const AABox& a = A->bounds; + const AABox& b = B->bounds; + + if (a.high()[sortAxis] < b.low()[sortAxis]) { + return 1; + } else if (a.low()[sortAxis] > b.high()[sortAxis]) { + return -1; + } else { + return 0; + } + } + }; + + + /** Compares bounds to the sort location */ + class Comparator { + public: + Vector3::Axis sortAxis; + float sortLocation; + + Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {} + + inline int operator()(Handle* ignore, const Handle* handle) const { + const AABox& box = handle->bounds; + debugAssert(ignore == NULL); + + if (box.high()[sortAxis] < sortLocation) { + // Box is strictly below the sort location + return -1; + } else if (box.low()[sortAxis] > sortLocation) { + // Box is strictly above the sort location + return 1; + } else { + // Box overlaps the sort location + return 0; + } + } + }; + + // Using System::malloc with this class provided no speed improvement. + class Node { + public: + + /** Spatial bounds on all values at this node and its children, based purely on + the parent's splitting planes. May be infinite. */ + AABox splitBounds; + + Vector3::Axis splitAxis; + + /** Location along the specified axis */ + float splitLocation; + + /** child[0] contains all values strictly + smaller than splitLocation along splitAxis. + + child[1] contains all values strictly + larger. + + Both may be NULL if there are not enough + values to bother recursing. + */ + Node* child[2]; + + /** Array of values at this node (i.e., values + straddling the split plane + all values if + this is a leaf node). + + This is an array of pointers because that minimizes + data movement during tree building, which accounts + for about 15% of the time cost of tree building. + */ + Array<Handle*> valueArray; + + /** For each object in the value array, a copy of its bounds. + Packing these into an array at the node level + instead putting them in the valueArray improves + cache coherence, which is about a 3x performance + increase when performing intersection computations. + */ + Array<AABox> boundsArray; + + /** Creates node with NULL children */ + Node() { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + splitBounds = AABox(-Vector3::inf(), Vector3::inf()); + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** + Doesn't clone children. + */ + Node(const Node& other) : valueArray(other.valueArray), boundsArray(other.boundsArray) { + splitAxis = other.splitAxis; + splitLocation = other.splitLocation; + splitBounds = other.splitBounds; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** Copies the specified subarray of pt into point, NULLs the children. + Assumes a second pass will set splitBounds. */ + Node(const Array<Handle*>& pt) : valueArray(pt) { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + + boundsArray.resize(valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + boundsArray[i] = valueArray[i]->bounds; + } + } + + /** Deletes the children (but not the values) */ + ~Node() { + for (int i = 0; i < 2; ++i) { + delete child[i]; + } + } + + /** Returns true if this node is a leaf (no children) */ + inline bool isLeaf() const { + return (child[0] == NULL) && (child[1] == NULL); + } + + + /** + Recursively appends all handles and children's handles + to the array. + */ + void getHandles(Array<Handle*>& handleArray) const { + handleArray.append(valueArray); + for (int i = 0; i < 2; ++i) { + if (child[i] != NULL) { + child[i]->getHandles(handleArray); + } + } + } + + void verifyNode(const Vector3& lo, const Vector3& hi) { + // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n", + // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z); + + debugAssertM(lo == splitBounds.low(), + format("lo = %s, splitBounds.lo = %s", + lo.toString().c_str(), splitBounds.low().toString().c_str())); + debugAssert(hi == splitBounds.high()); + + for (int i = 0; i < valueArray.length(); ++i) { + const AABox& b = valueArray[i]->bounds; + debugAssert(b == boundsArray[i]); + + for(int axis = 0; axis < 3; ++axis) { + debugAssert(b.low()[axis] <= b.high()[axis]); + debugAssert(b.low()[axis] >= lo[axis]); + debugAssert(b.high()[axis] <= hi[axis]); + } + } + + if (child[0] || child[1]) { + debugAssert(lo[splitAxis] < splitLocation); + debugAssert(hi[splitAxis] > splitLocation); + } + + Vector3 newLo = lo; + newLo[splitAxis] = splitLocation; + Vector3 newHi = hi; + newHi[splitAxis] = splitLocation; + + if (child[0] != NULL) { + child[0]->verifyNode(lo, newHi); + } + + if (child[1] != NULL) { + child[1]->verifyNode(newLo, hi); + } + } + + + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + static void serializeStructure(const Node* n, BinaryOutput& bo) { + if (n == NULL) { + bo.writeUInt8(0); + } else { + bo.writeUInt8(1); + n->splitBounds.serialize(bo); + serialize(n->splitAxis, bo); + bo.writeFloat32(n->splitLocation); + for (int c = 0; c < 2; ++c) { + serializeStructure(n->child[c], bo); + } + } + } + + /** Clears the member table */ + static Node* deserializeStructure(BinaryInput& bi) { + if (bi.readUInt8() == 0) { + return NULL; + } else { + Node* n = new Node(); + n->splitBounds.deserialize(bi); + deserialize(n->splitAxis, bi); + n->splitLocation = bi.readFloat32(); + for (int c = 0; c < 2; ++c) { + n->child[c] = deserializeStructure(bi); + } + return n; + } + } + + /** Returns the deepest node that completely contains bounds. */ + Node* findDeepestContainingNode(const AABox& bounds) { + + // See which side of the splitting plane the bounds are on + if (bounds.high()[splitAxis] < splitLocation) { + // Bounds are on the low side. Recurse into the child + // if it exists. + if (child[0] != NULL) { + return child[0]->findDeepestContainingNode(bounds); + } + } else if (bounds.low()[splitAxis] > splitLocation) { + // Bounds are on the high side, recurse into the child + // if it exists. + if (child[1] != NULL) { + return child[1]->findDeepestContainingNode(bounds); + } + } + + // There was no containing child, so this node is the + // deepest containing node. + return this; + } + + + /** Appends all members that intersect the box. + If useSphere is true, members that pass the box test + face a second test against the sphere. */ + void getIntersectingMembers( + const AABox& box, + const Sphere& sphere, + Array<T>& members, + bool useSphere) const { + + // Test all values at this node + for (int v = 0; v < boundsArray.size(); ++v) { + const AABox& bounds = boundsArray[v]; + if (bounds.intersects(box) && + (! useSphere || bounds.intersects(sphere))) { + members.append(valueArray[v]->value); + } + } + + // If the left child overlaps the box, recurse into it + if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) { + child[0]->getIntersectingMembers(box, sphere, members, useSphere); + } + + // If the right child overlaps the box, recurse into it + if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) { + child[1]->getIntersectingMembers(box, sphere, members, useSphere); + } + } + + /** + Recurse through the tree, assigning splitBounds fields. + */ + void assignSplitBounds(const AABox& myBounds) { + splitBounds = myBounds; + + AABox childBounds[2]; + myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]); + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + // Verify the split + for (int v = 0; v < boundsArray.size(); ++v) { + const AABox& bounds = boundsArray[v]; + debugAssert(myBounds.contains(bounds)); + } +# endif + + for (int c = 0; c < 2; ++c) { + if (child[c]) { + child[c]->assignSplitBounds(childBounds[c]); + } + } + } + + /** Returns true if the ray intersects this node */ + bool intersects(const Ray& ray, float distance) const { + // See if the ray will ever hit this node or its children + Vector3 location; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + CollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds); + + bool canHitThisNode = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + + return canHitThisNode; + } + + template<typename RayCallback> + void intersectRay( + const Ray& ray, + RayCallback& intersectCallback, + float& distance, + bool intersectCallbackIsFast) const { + + if (! intersects(ray, distance)) { + // The ray doesn't hit this node, so it can't hit the children of the node. + return; + } + + // Test for intersection against every object at this node. + for (int v = 0; v < valueArray.size(); ++v) { + bool canHitThisObject = true; + + if (! intersectCallbackIsFast) { + // See if + Vector3 location; + const AABox& bounds = boundsArray[v]; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + CollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, bounds, location, alreadyInsideBounds); + + canHitThisObject = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + } + + if (canHitThisObject) { + // It is possible that this ray hits this object. Look for the intersection using the + // callback. + const T& value = valueArray[v]->value; + intersectCallback(ray, value, distance); + } + } + + // There are three cases to consider next: + // + // 1. the ray can start on one side of the splitting plane and never enter the other, + // 2. the ray can start on one side and enter the other, and + // 3. the ray can travel exactly down the splitting plane + + enum {NONE = -1}; + int firstChild = NONE; + int secondChild = NONE; + + if (ray.origin[splitAxis] < splitLocation) { + + // The ray starts on the small side + firstChild = 0; + + if (ray.direction[splitAxis] > 0) { + // The ray will eventually reach the other side + secondChild = 1; + } + + } else if (ray.origin[splitAxis] > splitLocation) { + + // The ray starts on the large side + firstChild = 1; + + if (ray.direction[splitAxis] < 0) { + secondChild = 0; + } + } else { + // The ray starts on the splitting plane + if (ray.direction[splitAxis] < 0) { + // ...and goes to the small side + firstChild = 0; + } else if (ray.direction[splitAxis] > 0) { + // ...and goes to the large side + firstChild = 1; + } + } + + // Test on the side closer to the ray origin. + if ((firstChild != NONE) && child[firstChild]) { + child[firstChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast); + } + + if (ray.direction[splitAxis] != 0) { + // See if there was an intersection before hitting the splitting plane. + // If so, there is no need to look on the far side and recursion terminates. + float distanceToSplittingPlane = (splitLocation - ray.origin[splitAxis]) / ray.direction[splitAxis]; + if (distanceToSplittingPlane > distance) { + // We aren't going to hit anything else before hitting the splitting plane, + // so don't bother looking on the far side of the splitting plane at the other + // child. + return; + } + } + + // Test on the side farther from the ray origin. + if ((secondChild != NONE) && child[secondChild]) { + child[secondChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast); + } + + } + }; + + + /** + Recursively subdivides the subarray. + + Clears the source array as soon as it is no longer needed. + + Call assignSplitBounds() on the root node after making a tree. + */ + Node* makeNode( + Array<Handle*>& source, + int valuesPerNode, + int numMeanSplits, + Array<Handle*>& temp) { + + Node* node = NULL; + + if (source.size() <= valuesPerNode) { + // Make a new leaf node + node = new Node(source); + + // Set the pointers in the memberTable + for (int i = 0; i < source.size(); ++i) { + memberTable.set(Member(source[i]), node); + } + source.clear(); + + } else { + // Make a new internal node + node = new Node(); + + const AABox& bounds = computeBounds(source, 0, source.size() - 1); + const Vector3& extent = bounds.high() - bounds.low(); + + Vector3::Axis splitAxis = extent.primaryAxis(); + + float splitLocation; + + // Arrays for holding the children + Array<Handle*> lt, gt; + + if (numMeanSplits <= 0) { + + source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis)); + + // Choose the split location to be the center of whatever fell in the center + splitLocation = node->valueArray[0]->center[splitAxis]; + + // Some of the elements in the lt or gt array might really overlap the split location. + // Move them as needed. + for (int i = 0; i < lt.size(); ++i) { + const AABox& bounds = lt[i]->bounds; + if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { + node->valueArray.append(lt[i]); + // Remove this element and process the new one that + // is swapped in in its place. + lt.fastRemove(i); --i; + } + } + + for (int i = 0; i < gt.size(); ++i) { + const AABox& bounds = gt[i]->bounds; + if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { + node->valueArray.append(gt[i]); + // Remove this element and process the new one that + // is swapped in in its place. + gt.fastRemove(i); --i; + } + } + + if ((node->valueArray.size() > (source.size() / 2)) && + (source.size() > 6)) { + // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the + // objects. We could try to split on a different axis, or use a different partition (e.g., the extents mean, + // or geometric mean). This implementation falls back on the extents mean, since that case is already handled + // below. + numMeanSplits = 1; + } + } + + // Note: numMeanSplits may have been increased by the code in the previous case above in order to + // force a re-partition. + + if (numMeanSplits > 0) { + // Split along the mean + splitLocation = + bounds.high()[splitAxis] * 0.5f + + bounds.low()[splitAxis] * 0.5f; + + debugAssertM(isFinite(splitLocation), + "Internal error: split location must be finite."); + + source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation)); + + // The Comparator ensures that elements are strictly on the correct side of the split + } + + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size()); + // Verify that all objects ended up on the correct side of the split. + // (i.e., make sure that the Array partition was correct) + for (int i = 0; i < lt.size(); ++i) { + const AABox& bounds = lt[i]->bounds; + debugAssert(bounds.high()[splitAxis] < splitLocation); + } + + for (int i = 0; i < gt.size(); ++i) { + const AABox& bounds = gt[i]->bounds; + debugAssert(bounds.low()[splitAxis] > splitLocation); + } + + for (int i = 0; i < node->valueArray.size(); ++i) { + const AABox& bounds = node->valueArray[i]->bounds; + debugAssert(bounds.high()[splitAxis] >= splitLocation); + debugAssert(bounds.low()[splitAxis] <= splitLocation); + } +# endif + + // The source array is no longer needed + source.clear(); + + node->splitAxis = splitAxis; + node->splitLocation = splitLocation; + + // Update the bounds array and member table + node->boundsArray.resize(node->valueArray.size()); + for (int i = 0; i < node->valueArray.size(); ++i) { + Handle* v = node->valueArray[i]; + node->boundsArray[i] = v->bounds; + memberTable.set(Member(v), node); + } + + if (lt.size() > 0) { + node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp); + } + + if (gt.size() > 0) { + node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp); + } + + } + + return node; + } + + /** + Recursively clone the passed in node tree, setting + pointers for members in the memberTable as appropriate. + called by the assignment operator. + */ + Node* cloneTree(Node* src) { + Node* dst = new Node(*src); + + // Make back pointers + for (int i = 0; i < dst->valueArray.size(); ++i) { + memberTable.set(Member(dst->valueArray[i]), dst); + } + + // Clone children + for (int i = 0; i < 2; ++i) { + if (src->child[i] != NULL) { + dst->child[i] = cloneTree(src->child[i]); + } + } + + return dst; + } + + /** + Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but + stores only Handle* internally to avoid memory copies. + */ + typedef _internal::Indirector<Handle> Member; + + typedef Table<Member, Node*> MemberTable; + + /** Maps members to the node containing them */ + MemberTable memberTable; + + Node* root; + +public: + + /** To construct a balanced tree, insert the elements and then call + KDTree::balance(). */ + KDTree() : root(NULL) {} + + + KDTree(const KDTree& src) : root(NULL) { + *this = src; + } + + + KDTree& operator=(const KDTree& src) { + delete root; + // Clone tree takes care of filling out the memberTable. + root = cloneTree(src.root); + return *this; + } + + + ~KDTree() { + clear(); + } + + /** + Throws out all elements of the set. + */ + void clear() { + typedef typename Table<_internal::Indirector<Handle>, Node*>::Iterator It; + + // Delete all handles stored in the member table + It cur = memberTable.begin(); + It end = memberTable.end(); + while (cur != end) { + delete cur->key.handle; + cur->key.handle = NULL; + ++cur; + } + memberTable.clear(); + + // Delete the tree structure itself + delete root; + root = NULL; + } + + int size() const { + return memberTable.size(); + } + + /** + Inserts an object into the set if it is not + already present. O(log n) time. Does not + cause the tree to be balanced. + */ + void insert(const T& value) { + if (contains(value)) { + // Already in the set + return; + } + + Handle* h = new Handle(value); + + if (root == NULL) { + // This is the first node; create a root node + root = new Node(); + } + + Node* node = root->findDeepestContainingNode(h->bounds); + + // Insert into the node + node->valueArray.append(h); + node->boundsArray.append(h->bounds); + + // Insert into the node table + Member m(h); + memberTable.set(m, node); + } + + /** Inserts each elements in the array in turn. If the tree + begins empty (no structure and no elements), this is faster + than inserting each element in turn. You still need to balance + the tree at the end.*/ + void insert(const Array<T>& valueArray) { + if (root == NULL) { + // Optimized case for an empty tree; don't bother + // searching or reallocating the root node's valueArray + // as we incrementally insert. + root = new Node(); + root->valueArray.resize(valueArray.size()); + root->boundsArray.resize(root->valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + // Insert in opposite order so that we have the exact same + // data structure as if we inserted each (i.e., order is reversed + // from array). + Handle* h = new Handle(valueArray[i]); + int j = valueArray.size() - i - 1; + root->valueArray[j] = h; + root->boundsArray[j] = h->bounds; + memberTable.set(Member(h), root); + } + + } else { + // Insert at appropriate tree depth. + for (int i = 0; i < valueArray.size(); ++i) { + insert(valueArray[i]); + } + } + } + + + /** + Returns true if this object is in the set, otherwise + returns false. O(1) time. + */ + bool contains(const T& value) { + // Temporarily create a handle and member + Handle h(value); + return memberTable.containsKey(Member(&h)); + } + + + /** + Removes an object from the set in O(1) time. + It is an error to remove members that are not already + present. May unbalance the tree. + + Removing an element never causes a node (split plane) to be removed... + nodes are only changed when the tree is rebalanced. This behavior + is desirable because it allows the split planes to be serialized, + and then deserialized into an empty tree which can be repopulated. + */ + void remove(const T& value) { + debugAssertM(contains(value), + "Tried to remove an element from a " + "KDTree that was not present"); + + // Get the list of elements at the node + Handle h(value); + Member m(&h); + + Array<Handle*>& list = memberTable[m]->valueArray; + + Handle* ptr = NULL; + + // Find the element and remove it + for (int i = list.length() - 1; i >= 0; --i) { + if (list[i]->value == value) { + // This was the element. Grab the pointer so that + // we can delete it below + ptr = list[i]; + + // Remove the handle from the node + list.fastRemove(i); + + // Remove the corresponding bounds + memberTable[m]->boundsArray.fastRemove(i); + break; + } + } + + // Remove the member + memberTable.remove(m); + + // Delete the handle data structure + delete ptr; + ptr = NULL; + } + + + /** + If the element is in the set, it is removed. + The element is then inserted. + + This is useful when the == and hashCode methods + on <I>T</I> are independent of the bounds. In + that case, you may call update(v) to insert an + element for the first time and call update(v) + again every time it moves to keep the tree + up to date. + */ + void update(const T& value) { + if (contains(value)) { + remove(value); + } + insert(value); + } + + + /** + Rebalances the tree (slow). Call when objects + have moved substantially from their original positions + (which unbalances the tree and causes the spatial + queries to be slow). + + @param valuesPerNode Maximum number of elements to put at + a node. + + @param numMeanSplits numMeanSplits = 0 gives a + fully axis aligned BSP-tree, where the balance operation attempts to balance + the tree so that every splitting plane has an equal number of left + and right children (i.e. it is a <B>median</B> split along that axis). + This tends to maximize average performance. + + You can override this behavior by + setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT + creates a full oct-tree, which tends to optimize peak performance at the expense of + average performance. It tends to have better clustering behavior when + members are not uniformly distributed. + */ + void balance(int valuesPerNode = 5, int numMeanSplits = 3) { + if (root == NULL) { + // Tree is empty + return; + } + + // Get all handles and delete the old tree structure + Node* oldRoot = root; + for (int c = 0; c < 2; ++c) { + if (root->child[c] != NULL) { + root->child[c]->getHandles(root->valueArray); + + // Delete the child; this will delete all structure below it + delete root->child[c]; + root->child[c] = NULL; + } + } + + Array<Handle*> temp; + // Make a new root. Work with a copy of the value array because + // makeNode clears the source array as it progresses + Array<Handle*> copy(oldRoot->valueArray); + root = makeNode(copy, valuesPerNode, numMeanSplits, temp); + + // Throw away the old root node + delete oldRoot; + oldRoot = NULL; + + // Walk the tree, assigning splitBounds. We start with unbounded + // space. This will override the current member table. + const AABox& LARGE = AABox::large(); + root->assignSplitBounds(LARGE); + +# ifdef _DEBUG + { + // Ensure that the balanced tree is still correct + root->verifyNode(LARGE.low(), LARGE.high()); + } +# endif + } + +protected: + + /** + @param parentMask The mask that this node returned from culledBy. + */ + static void getIntersectingMembers( + const Array<Plane>& plane, + Array<T>& members, + Node* node, + uint32 parentMask) { + + int dummy; + + if (parentMask == 0) { + // None of these planes can cull anything + for (int v = node->valueArray.size() - 1; v >= 0; --v) { + members.append(node->valueArray[v]->value); + } + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c]) { + getIntersectingMembers(plane, members, node->child[c], 0); + } + } + } else { + + // Test values at this node against remaining planes + for (int v = node->boundsArray.size() - 1; v >= 0; --v) { + if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) { + members.append(node->valueArray[v]->value); + } + } + + uint32 childMask = 0xFFFFFF; + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c] && + ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) { + // This node was not culled + getIntersectingMembers(plane, members, node->child[c], childMask); + } + } + } + } + +public: + + /** + Returns all members inside the set of planes. + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const { + if (root == NULL) { + return; + } + + getIntersectingMembers(plane, members, root, 0xFFFFFF); + } + + /** + Typically used to find all visible + objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects + <B>not<B> culled by frustum. + + Example: + <PRE> + Array<Object*> visible; + tree.getIntersectingMembers(camera.frustum(), visible); + // ... Draw all objects in the visible array. + </PRE> + @param members The results are appended to this array. + */ + void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const { + Array<Plane> plane; + + for (int i = 0; i < frustum.faceArray.size(); ++i) { + plane.append(frustum.faceArray[i].plane); + } + + getIntersectingMembers(plane, members); + } + + /** + C++ STL style iterator variable. See beginBoxIntersection(). + The iterator overloads the -> (dereference) operator, so this + acts like a pointer to the current member. + */ + // This iterator turns Node::getIntersectingMembers into a + // coroutine. It first translates that method from recursive to + // stack based, then captures the system state (analogous to a Scheme + // continuation) after each element is appended to the member array, + // and allowing the computation to be restarted. + class BoxIntersectionIterator { + private: + friend class TreeType; + + /** True if this is the "end" iterator instance */ + bool isEnd; + + /** The box that we're testing against. */ + AABox box; + + /** Node that we're currently looking at. Undefined if isEnd + is true. */ + Node* node; + + /** Nodes waiting to be processed */ + // We could use backpointers within the tree and careful + // state management to avoid ever storing the stack-- but + // it is much easier this way and only inefficient if the + // caller uses post increment (which they shouldn't!). + Array<Node*> stack; + + /** The next index of current->valueArray to return. + Undefined when isEnd is true.*/ + int nextValueArrayIndex; + + BoxIntersectionIterator() : isEnd(true) {} + + BoxIntersectionIterator(const AABox& b, const Node* root) : + isEnd(root == NULL), box(b), + node(const_cast<Node*>(root)), nextValueArrayIndex(-1) { + + // We intentionally start at the "-1" index of the current + // node so we can use the preincrement operator to move + // ourselves to element 0 instead of repeating all of the + // code from the preincrement method. Note that this might + // cause us to become the "end" instance. + ++(*this); + } + + public: + + inline bool operator!=(const BoxIntersectionIterator& other) const { + return ! (*this == other); + } + + bool operator==(const BoxIntersectionIterator& other) const { + if (isEnd) { + return other.isEnd; + } else if (other.isEnd) { + return false; + } else { + // Two non-end iterators; see if they match. This is kind of + // silly; users shouldn't call == on iterators in general unless + // one of them is the end iterator. + if ((box != other.box) || (node != other.node) || + (nextValueArrayIndex != other.nextValueArrayIndex) || + (stack.length() != other.stack.length())) { + return false; + } + + // See if the stacks are the same + for (int i = 0; i < stack.length(); ++i) { + if (stack[i] != other.stack[i]) { + return false; + } + } + + // We failed to find a difference; they must be the same + return true; + } + } + + /** + Pre increment. + */ + BoxIntersectionIterator& operator++() { + ++nextValueArrayIndex; + + bool foundIntersection = false; + while (! isEnd && ! foundIntersection) { + + // Search for the next node if we've exhausted this one + while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) { + // If we entered this loop, then the iterator has exhausted the elements at + // node (possibly because it just switched to a child node with no members). + // This loop continues until it finds a node with members or reaches + // the end of the whole intersection search. + + // If the right child overlaps the box, push it onto the stack for + // processing. + if ((node->child[1] != NULL) && + (box.high()[node->splitAxis] > node->splitLocation)) { + stack.push(node->child[1]); + } + + // If the left child overlaps the box, push it onto the stack for + // processing. + if ((node->child[0] != NULL) && + (box.low()[node->splitAxis] < node->splitLocation)) { + stack.push(node->child[0]); + } + + if (stack.length() > 0) { + // Go on to the next node (which may be either one of the ones we + // just pushed, or one from farther back the tree). + node = stack.pop(); + nextValueArrayIndex = 0; + } else { + // That was the last node; we're done iterating + isEnd = true; + } + } + + // Search for the next intersection at this node until we run out of children + while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) { + if (box.intersects(node->boundsArray[nextValueArrayIndex])) { + foundIntersection = true; + } else { + ++nextValueArrayIndex; + // If we exhaust this node, we'll loop around the master loop + // to find a new node. + } + } + } + + return *this; + } + + private: + /** + Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code. + */ + BoxIntersectionIterator operator++(int); + /*{ + BoxIntersectionIterator old = *this; + ++this; + return old; + }*/ + + public: + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + const T& operator*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return node->valueArray[nextValueArrayIndex]->value; + } + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + T const * operator->() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex]->value); + } + + /** Overloaded cast operator so the iterator can masquerade as a pointer + to a member */ + operator T*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex]->value); + } + }; + + + /** + Iterates through the members that intersect the box + */ + BoxIntersectionIterator beginBoxIntersection(const AABox& box) const { + return BoxIntersectionIterator(box, root); + } + + BoxIntersectionIterator endBoxIntersection() const { + // The "end" iterator instance + return BoxIntersectionIterator(); + } + + /** + Appends all members whose bounds intersect the box. + See also KDTree::beginBoxIntersection. + */ + void getIntersectingMembers(const AABox& box, Array<T>& members) const { + if (root == NULL) { + return; + } + root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false); + } + + + /** + Invoke a callback for every member along a ray until the closest intersection is found. + + @param callback either a function or an instance of a class with an overloaded operator() of the form: + + <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object + before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to + the intersection, otherwise leaves it unmodified. A common example is: + + <pre> + class Entity { + public: + + void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) { + float d = maxDist; + + // ... search for intersection distance d + + if ((d > 0) && (d < maxDist)) { + // Intersection occured + maxDist = d; + outLocation = ...; + outNormal = ...; + } + } + }; + + // Finds the surface normal and location of the first intersection with the scene + class Intersection { + public: + Entity* closestEntity; + Vector3 hitLocation; + Vector3 hitNormal; + + void operator()(const Ray& ray, const Entity* entity, float& distance) { + entity->intersect(ray, distance, hitLocation, hitNormal); + } + }; + + KDTree<Entity*> scene; + + Intersection intersection; + float distance = inf(); + scene.intersectRay(camera.worldRay(x, y), intersection, distance); + </pre> + + + @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection. + On return, this is set to the distance to the first intersection encountered. + + @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked. + If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true. + */ + template<typename RayCallback> + void intersectRay( + const Ray& ray, + RayCallback& intersectCallback, + float& distance, + bool intersectCallbackIsFast = false) const { + + root->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast); + + } + + + /** + @brief Finds all members whose bounding boxes intersect the sphere. The actual + elements may not intersect the sphere. + + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const { + if (root == NULL) { + return; + } + + AABox box; + sphere.getBounds(box); + root->getIntersectingMembers(box, sphere, members, true); + + } + + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + void serializeStructure(BinaryOutput& bo) const { + Node::serializeStructure(root, bo); + } + + /** Clears the member table */ + void deserializeStructure(BinaryInput& bi) { + clear(); + root = Node::deserializeStructure(bi); + } + + /** + Returns an array of all members of the set. See also KDTree::begin. + */ + void getMembers(Array<T>& members) const { + Array<Member> temp; + memberTable.getKeys(temp); + for (int i = 0; i < temp.size(); ++i) { + members.append(*(temp.handle)); + } + } + + + /** + C++ STL style iterator variable. See begin(). + Overloads the -> (dereference) operator, so this acts like a pointer + to the current member. + */ + class Iterator { + private: + friend class TreeType; + + // Note: this is a Table iterator, we are currently defining + // Set iterator + typename Table<Member, Node*>::Iterator it; + + Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {} + + public: + + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + bool operator==(const Iterator& other) const { + return it == other.it; + } + + /** + Pre increment. + */ + Iterator& operator++() { + ++it; + return *this; + } + + private: + /** + Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code. + */ + Iterator operator++(int);/* { + Iterator old = *this; + ++(*this); + return old; + }*/ + public: + + const T& operator*() const { + return it->key.handle->value; + } + + T* operator->() const { + return &(it->key.handle->value); + } + + operator T*() const { + return &(it->key.handle->value); + } + }; + + + /** + C++ STL style iterator method. Returns the first member. + Use preincrement (++entry) to get to the next element (iteration + order is arbitrary). + Do not modify the set while iterating. + */ + Iterator begin() const { + return Iterator(memberTable.begin()); + } + + + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + Iterator end() const { + return Iterator(memberTable.end()); + } +#undef TreeType +}; + +/** @deprecated For backwards compatibility */ +#define AABSPTree KDTree + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABox.h b/externals/g3dlite/G3D.lib/include/G3D/AABox.h new file mode 100644 index 00000000000..76c5d6d5195 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AABox.h @@ -0,0 +1,281 @@ +/** + @file AABox.h + + Axis-aligned box class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-10 + @edited 2006-02-10 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_AABOX_H +#define G3D_AABOX_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/debug.h" +#include "G3D/Array.h" +#include "G3D/Plane.h" + +namespace G3D { + +/** + An axis-aligned box. + */ +class AABox { +private: + + /** Optional argument placeholder */ + static int dummy; + + Vector3 lo; + Vector3 hi; + +public: + + /** Does not initialize the fields */ + inline AABox() {} + + /** + Constructs a zero-area AABox at v. + */ + inline explicit AABox(const Vector3& v) { + lo = hi = v; + } + + /** Assumes that low is less than or equal to high along each dimension. + To have this automatically enforced, use + <code>AABox(low.min(high), low.max(high));</code> + */ + inline AABox(const Vector3& low, const Vector3& high) { + set(low, high); + } + + /** Assumes that low is less than or equal to high along each dimension. + */ + inline void set(const Vector3& low, const Vector3& high) { + debugAssert( + (low.x <= high.x) && + (low.y <= high.y) && + (low.z <= high.z)); + lo = low; + hi = high; + } + + /** + Grows to include the bounds of a + */ + inline void merge(const AABox& a) { + lo = lo.min(a.lo); + hi = hi.max(a.hi); + } + + inline void merge(const Vector3& a) { + lo = lo.min(a); + hi = hi.max(a); + } + + void serialize(class BinaryOutput& b) const; + + void deserialize(class BinaryInput& b); + + inline const Vector3& low() const { + return lo; + } + + inline const Vector3& high() const { + return hi; + } + + /** + The largest possible finite box. + */ + static inline const AABox& maxFinite() { + static const AABox b = AABox(Vector3::minFinite(), + Vector3::maxFinite()); + return b; + } + + /** A large finite box. This is smaller than FLT_MAX + because it leaves room to add boxes together. */ + static inline const AABox& large() { + static const AABox b = AABox(Vector3::minFinite() * 0.5f, + Vector3::maxFinite() * 0.5f); + return b; + } + + static inline const AABox& inf() { + static const AABox b = AABox(-Vector3::inf(), Vector3::inf()); + return b; + } + + static inline const AABox& zero() { + static const AABox b = AABox(Vector3::zero(), Vector3::zero()); + return b; + } + + /** + Returns the centroid of the box. + */ + inline Vector3 center() const { + return (lo + hi) * 0.5; + } + + Vector3 corner(int index) const; + + /** + Distance from corner(0) to the next corner along axis a. + */ + inline float extent(int a) const { + debugAssert(a < 3); + return hi[a] - lo[a]; + } + + + inline Vector3 extent() const { + return hi - lo; + } + + + /** + Splits the box into two AABoxes along the specified axis. low contains + the part that was closer to negative infinity along axis, high contains + the other part. Either may have zero volume. + */ + void split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const; + + /** + Conservative culling test for up to 32 planes. + Returns true if there exists a <CODE>plane[p]</CODE> for + which the entire object is in the negative half space + (opposite the plane normal). + + <CODE>testMask</CODE> and <CODE>childMask</CODE> + are used for optimizing bounding volume hierarchies. + The version of this method that produces childMask + is slower than the version without; it should only + be used for parent nodes. + + @param cullingPlaneIndex The index of the first plane for which + the entire object is in the negative half-space. The function + exits early when one plane is found. -1 when the function + returns false (i.e. when no plane culls the whole object). + + @param testMask If bit <I>p</I> is 0, the + bounding volume automatically passes the culling test for + <CODE>plane[p]</CODE> (i.e. it is known that the volume + is entirely within the positive half space). The function + must return false if testMask is 0 and test all planes + when testMask is -1 (0xFFFFFFFF). + + @param childMask Test mask for the children of this volume. + + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex, + const uint32 testMask, + uint32& childMask) const; + + /** + Conservative culling test that does not produce a mask for children. + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex = dummy, + const uint32 testMask = 0xFFFFFFFF) const; + + /** less than or equal to containment */ + inline bool contains(const AABox& other) const { + return + (other.hi.x <= hi.x) && + (other.hi.y <= hi.y) && + (other.hi.z <= hi.z) && + (other.lo.x >= lo.x) && + (other.lo.y >= lo.y) && + (other.lo.z >= lo.z); + } + + inline bool contains( + const Vector3& point) const { + return + (point.x >= lo.x) && + (point.y >= lo.y) && + (point.z >= lo.z) && + (point.x <= hi.x) && + (point.y <= hi.y) && + (point.z <= hi.z); + } + + inline float area() const { + Vector3 diag = hi - lo; + return 2.0f * (diag.x * diag.y + diag.y * diag.z + diag.x * diag.z); + } + + inline float volume() const { + Vector3 diag = hi - lo; + return diag.x * diag.y * diag.z; + } + + Vector3 randomInteriorPoint() const; + + Vector3 randomSurfacePoint() const; + + /** Returns true if there is any overlap */ + bool intersects(const AABox& other) const; + + /** Returns true if there is any overlap. + @cite Jim Arvo's algorithm from Graphics Gems II*/ + bool intersects(const class Sphere& other) const; + + /** Return the intersection of the two boxes */ + AABox intersect(const AABox& other) const { + Vector3 H = hi.min(other.hi); + Vector3 L = lo.max(other.lo).min(H); + return AABox(L, H); + } + + inline size_t hashCode() const { + return lo.hashCode() + hi.hashCode(); + } + + inline bool operator==(const AABox& b) const { + return (lo == b.lo) && (hi == b.hi); + } + + inline bool operator!=(const AABox& b) const { + return !((lo == b.lo) && (hi == b.hi)); + } + + inline AABox operator+(const Vector3& v) const { + AABox out; + out.lo = lo + v; + out.hi = hi + v; + return out; + } + + inline AABox operator-(const Vector3& v) const { + AABox out; + out.lo = lo - v; + out.hi = hi - v; + return out; + } + + void getBounds(AABox& out) const { + out = *this; + } +}; + +} + +template <> struct HashTrait<G3D::AABox> { + static size_t hashCode(const G3D::AABox& key) { return key.hashCode(); } +}; + + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h new file mode 100644 index 00000000000..8254dd73c93 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h @@ -0,0 +1,506 @@ +/** + @file AnyVal.h + @author Morgan McGuire + @created 2006-06-11 + @edited 2008-07-14 + */ + +#ifndef G3D_ANYVAL_H +#define G3D_ANYVAL_H + +#include "G3D/platform.h" +#include <string> +#include "G3D/Array.h" +#include "G3D/TextInput.h" + +namespace G3D { +// Forward declarations for G3D types +class Vector2; +class Vector3; +class Vector4; +class Color1; +class Color3; +class Color4; +class Quat; +class Matrix2; +class Matrix3; +class Matrix4; +class CoordinateFrame; +class TextInput; +class TextOutput; +class BinaryInput; +class BinaryOutput; +class Rect2D; +class AABox; + +/** + A generic value, useful for defining property trees that can + be loaded from and saved to disk. The values are intentionally + restricted to a small set. + + When written to files, the syntax is as follows. Note that you can + nest arrays and tables in order to create full tree (i.e., XML-like) + structures as configuration files: + + <table> + <tr><td>NULL</td><td><code>Nil</code></td></tr> + <tr><td>double</td><td><i>The number in printf double format</i></td></tr> + <tr><td>bool</td><td><code>true</code> <i>or</i> <code>false</code></td></tr> + <tr><td>std::string</td><td><i>The string in double-quotes (</i><code>"</code><i>)</i></td></tr> + <tr><td>Rect2D</td><td><code>R(</code><i>x<sub>0</sub></i><code>,</code><i>y<sub>0</sub></i><code>,</code><i>x<sub>1</sub></i><code>,</code><i>y<sub>1</sub></i><code>)</code></td></tr> + <tr><td>Color1</td><td><code>C1(</code><i>value</i><code>)</code></td></tr> + <tr><td>Color3</td><td><code>C3(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>)</code></td></tr> + <tr><td>Color4</td><td><code>C4(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>,</code><i>a</i><code>)</code></td></tr> + <tr><td>Vector2</td><td><code>V2(</code><i>x</i><code>,</code><i>y</i><code>)</code></td></tr> + <tr><td>Vector3</td><td><code>V3(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>)</code></td></tr> + <tr><td>Vector4</td><td><code>V4(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>,</code><i>w</i><code>)</code></td></tr> + <tr><td>Quat</td><td><code>V(</code>x<code>,</code>y<code>,</code>z<code>,</code>w<code>)</code></td></tr> + <tr><td>AABox</td><td><code>AAB(</code>low Vector3<code>, </code>high Vector3<code>)</code></td></tr> + <tr><td>Matrix2</td><td><code>M2(</code>r0c0<code>, </code>r0c1<code>, + <br> </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> </code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, + <br> </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> </code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>, + <br> </code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>, + <br> </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> </code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>, + <br> </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> </code><i>symbol<sub>1</sub></i><code> = </code><i>value<sub>1</sub></i> + <br><code> </code>... + <br><code> </code><i>symbol<sub>n-1</sub></i><code> = </code><i>value<sub>n-1</sub></i><code>}</code></td></tr> + </table> + + See also boost::any for a more general purpose but slightly harder to use + "any" for C++. + + The semantics of operator[] and the get() methods are slightly different; + operator[] acts more like a scripting language that automatically extends + arrays and tables instead of generating errors. get() has more strict semantics, + like a C++ class. + + AnyVal uses copy-on-mutate, so that <code>AnyVal a = b</code> semantically copies <code>b</code> (like <code>int a = b</code> would), although in practice + it delays the copy until one is mutated so that it is still fast to "copy" large arrays and tables. + + Reading example: + <pre> + AnyVal property = AnyVal::fromFile("c:/tmp/test.txt")); + + Vector3 vel = property["angular velocity"] + + <i>Using defaults to handle errors: + If there was no "enabled" value, this will return the default instead of failing</i> + bool enabled = property["enabled"].boolean(true); + + </pre> + + Writing to a file: + <pre> + AnyVal dict(AnyVal::TABLE); + + dict["enabled"] = AnyVal(true); + dict["weight"] = 100; + dict["angular velocity"] = Vector3(1, -3, 4.5); + + TextOutput t("c:/tmp/test.txt"); + dict.serialize(t); + t.commit(); + </pre> + + Example of a data file: + <pre> + { + heights = [1, 17, 32] + model = + { + color = C3(1, 1, 1) + filename = "foo.md2" + } + position = V3(23, 14, 0) + name = "Elmer" + } + </pre> + + <p> + <b>What's the difference from boost::any?</b> + <br>I think that AnyVal will be easier for novice C++ users. It addresses the problem that + even though G3D::TextInput makes reading configuration files extremely simple, many people + still don't use it. So AnyVal makes it ridiculously simple to read and write a tree of G3D + types to a file. + + <i>AnyVal:</i> +<pre> +{ +AnyVal tree(TextInput("config.txt")); + +bool enabled = tree.get("enabled", false); +Vector3 direction = tree.get("direction", Vector3::zero()); +... +} +</pre> + +<i>boost:</i> +<pre> +{ +bool enabled = false; +Vector3 direction; +Table<boost::any> tree; + + ...write lots of file parsing code... + + if (tree.containsKey("enabled")) { + const boost::any& val = tree["enabled"]; + try { + enabled = any_cast<bool>(val); + } catch(const boost::bad_any_cast &) { + } + } + + if (tree.containsKey("direction")) { + const boost::any& val = tree["direction"]; + try { + direction = any_cast<Vector3>(val); + } catch(const boost::bad_any_cast &) { + } + } + ... +} +</pre> + */ +class AnyVal { +public: + + /** Array and table values are all Any.*/ + enum Type { + NIL, + NUMBER, + BOOLEAN, + STRING, + VECTOR2, + VECTOR3, + VECTOR4, + MATRIX2, + MATRIX3, + MATRIX4, + QUAT, + COORDINATEFRAME, + COORDINATEFRAME2D, + CFRAME = COORDINATEFRAME, + CFRAME2D = COORDINATEFRAME2D, + COLOR1, + COLOR3, + COLOR4, + RECT2D, + AABOX2D = RECT2D, + AABOX, + ARRAY, + TABLE}; + + /** Base class for all AnyVal exceptions.*/ + class Exception { + public: + virtual ~Exception() {} + }; + + /** Thrown when an inappropriate operation is performed (e.g., operator[] on a number) */ + class WrongType : public Exception { + public: + Type expected; + Type actual; + WrongType() : expected(NIL), actual(NIL) {} + WrongType(Type e, Type a) : expected(e), actual(a) {} + }; + + /** Thrown by operator[] when a key is not present. */ + class KeyNotFound : public Exception { + public: + std::string key; + KeyNotFound() {} + KeyNotFound(const std::string& k) : key(k) {} + }; + + class IndexOutOfBounds : public Exception { + public: + int index; + int size; + IndexOutOfBounds() : index(0), size(0) {} + IndexOutOfBounds(int i, int s) : index(i), size(s) {} + }; + + /** Thrown when deserialize() when the input is incorrectly formatted. */ + class CorruptText : public Exception { + public: + std::string message; + + /** Token where the problem occurred.*/ + G3D::Token token; + + CorruptText() {} + CorruptText(const std::string& s, const G3D::Token& t) : message(s), token(t) {} + }; + +private: + + Type m_type; + void* m_value; + + /** For table and array types, *m_value is shared between multiple + instances. Mutation is allowed only if the reference count is + exactly 1, otherwise the mutating instance must copy the + value. This is not used for other types. + */ + int* m_referenceCount; + + /** Decrements the reference count (if there is one). If the + reference count is zero or does not exist. Calls delete on @a + m_value and sets it to NULL. + */ + void deleteValue(); + + /** Returns a copy of the value. */ + void* copyValue() const; + + /** Assumes isSharedType. Ensures that this has a unique reference */ + void makeMutable(); + + /** True if this is a shared value between multiple instances. */ + inline bool isShared() const { + return m_referenceCount && (*m_referenceCount > 1); + } + + /** True when m_value is a double pointer */ + inline bool isSharedType() const { + return (m_type == TABLE) || (m_type == ARRAY); + } + +public: + + AnyVal(); + + /** Deserialize */ + explicit AnyVal(G3D::TextInput& t); + + static AnyVal fromFile(const std::string& filename); + + void load(const std::string& filename); + + void save(const std::string& filename) const; + + ///** Deserialize */ + //explicit AnyVal(G3D::BinaryInput& t); + + /** Construct a number */ + AnyVal(double); + AnyVal(int); + + // Explicit to avoid ambiguity with the 'double' constructor + // when an integer type is constructed + AnyVal(bool); + AnyVal(const G3D::Vector2&); + AnyVal(const G3D::Vector3&); + AnyVal(const G3D::Vector4&); + + AnyVal(const G3D::Color1&); + AnyVal(const G3D::Color3&); + AnyVal(const G3D::Color4&); + + AnyVal(const std::string&); + AnyVal(const char*); + + AnyVal(const G3D::Quat&); + + AnyVal(const G3D::Rect2D&); + AnyVal(const G3D::AABox&); + + AnyVal(const G3D::CoordinateFrame&); + AnyVal(const G3D::Matrix2&); + AnyVal(const G3D::Matrix3&); + AnyVal(const G3D::Matrix4&); + + AnyVal(const AnyVal&); + + AnyVal(Type arrayOrTable); + + AnyVal& operator=(const AnyVal&); + + /** Frees the underlying storage */ + ~AnyVal(); + + Type type() const; + + bool isNil() const { + return type() == NIL; + } + + void serialize(G3D::TextOutput& t) const; + //void serialize(G3D::BinaryOutput& t) const; + void deserialize(G3D::TextInput& t); + //void deserialize(G3D::BinaryInput& t); + + /** Array dereference. If the index is out of bounds, IndexOutOfBounds is thrown */ + const AnyVal& operator[](int) const; + + /** Extend this array by one element. */ + void append(const AnyVal&); + + /** If the index is out of bounds, the array is resized. If the index is negative, + IndexOutOfBounds is thrown.*/ + AnyVal& operator[](int); + + /** If @a i is out of bounds or this is not an ARRAY, defaultVal is returned.*/ + const AnyVal& get(int i, const AnyVal& defaultVal) const; + + /** If out of bounds, IndexOutOfBounds is thrown. */ + const AnyVal& get(int i) const; + + /** Returns defaultVal if this is not a TABLE or the key is not found. */ + const AnyVal& get(const std::string& key, const AnyVal& defaultVal) const; + + /** Throws KeyNotFound exception if the key is not present.*/ + const AnyVal& get(const std::string& key) const; + + /** Table reference */ + const AnyVal& operator[](const std::string&) const; + + /** Table reference. If the element does not exist, it is created. */ + AnyVal& operator[](const std::string&); + + /** Table reference */ + const AnyVal& operator[](const char*) const; + + /** Table reference. If the element does not exist, it is created. */ + AnyVal& operator[](const char*); + + /** If this value is not a number throws a WrongType exception. */ + double number() const; + + /** If this value is not a number, returns defaultVal. */ + double number(double defaultVal) const; + + operator double () const { + return number(); + } + + operator float () const { + return (float)number(); + } + + bool boolean() const; + bool boolean(bool b) const; + + operator bool() const { + return boolean(); + } + + const std::string& string() const; + const std::string& string(const std::string& defaultVal) const; + + operator const std::string& () const { + return string(); + } + + const G3D::Rect2D& rect2D() const; + const G3D::Rect2D& rect2D(const G3D::Rect2D& defaultVal) const; + + operator const Rect2D& () const { + return rect2D(); + } + + const G3D::AABox& aabox() const; + const G3D::AABox& aabox(const G3D::AABox& defaultVal) const; + + operator const AABox& () const { + return aabox(); + } + + const G3D::Vector2& vector2() const; + const G3D::Vector2& vector2(const G3D::Vector2& defaultVal) const; + + operator const Vector2& () const { + return vector2(); + } + + const G3D::Vector3& vector3() const; + const G3D::Vector3& vector3(const G3D::Vector3& defaultVal) const; + + operator const Vector3& () { + return vector3(); + } + + const G3D::Vector4& vector4() const; + const G3D::Vector4& vector4(const G3D::Vector4& defaultVal) const; + + operator const Vector4& () const { + return vector4(); + } + + const G3D::Color1& color1() const; + const G3D::Color1& color1(const G3D::Color1& defaultVal) const; + + const G3D::Color3& color3() const; + const G3D::Color3& color3(const G3D::Color3& defaultVal) const; + + operator const Color3& () const { + return color3(); + } + + const G3D::Color4& color4() const; + const G3D::Color4& color4(const G3D::Color4& defaultVal) const; + + operator const Color4& () const { + return color4(); + } + + const G3D::CoordinateFrame& coordinateFrame() const; + const G3D::CoordinateFrame& coordinateFrame(const G3D::CoordinateFrame& defaultVal) const; + + operator const CoordinateFrame& () const { + return coordinateFrame(); + } + + const G3D::Matrix2& matrix2() const; + const G3D::Matrix2& matrix2(const G3D::Matrix2& defaultVal) const; + + operator const Matrix2& () const { + return matrix2(); + } + + const G3D::Matrix3& matrix3() const; + const G3D::Matrix3& matrix3(const G3D::Matrix3& defaultVal) const; + + operator const Matrix3& () const { + return matrix3(); + } + + const G3D::Matrix4& matrix4() const; + const G3D::Matrix4& matrix4(const G3D::Matrix4& defaultVal) const; + + operator const Matrix4& () const { + return matrix4(); + } + + const G3D::Quat& quat() const; + const G3D::Quat& quat(const G3D::Quat& defaultVal) const; + + operator const Quat& () const { + return quat(); + } + + std::string toString() const; + + /** Number of elements for an array or table.*/ + int size() const; + + /** For a table, returns the keys. */ + void getKeys(G3D::Array<std::string>&) const; +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Array.h b/externals/g3dlite/G3D.lib/include/G3D/Array.h new file mode 100644 index 00000000000..ae4eea8ab40 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Array.h @@ -0,0 +1,1180 @@ +/** + @file Array.h + + @maintainer Morgan McGuire, graphics3d.com + @cite Portions written by Aaron Orenstein, a@orenstein.name + + @created 2001-03-11 + @edited 2008-07-09 + + Copyright 2000-2008, Morgan McGuire, morgan@cs.williams.edu + All rights reserved. + */ + +#ifndef G3D_ARRAY_H +#define G3D_ARRAY_H + +#include "G3D/platform.h" +#include "G3D/debug.h" +#include "G3D/System.h" +#ifdef G3D_DEBUG +// For formatting error messages +# include "G3D/format.h" +#endif +#include <vector> +#include <algorithm> + +#ifdef _MSC_VER +# include <new> + +# pragma warning (push) + // debug information too long +# pragma warning( disable : 4312) +# pragma warning( disable : 4786) +#endif + + +namespace G3D { + +/** + Constant for passing to Array::resize + */ +const bool DONT_SHRINK_UNDERLYING_ARRAY = false; + +/** Constant for Array::sort */ +const int SORT_INCREASING = 1; +/** Constant for Array::sort */ +const int SORT_DECREASING = -1; + +/** + Dynamic 1D array. + + Objects must have a default constructor (constructor that + takes no arguments) in order to be used with this template. + You will get the error "no appropriate default constructor found" + if they do not. + + Do not use with objects that overload placement <code>operator new</code>, + since the speed of Array is partly due to pooled allocation. + + If SSE is defined Arrays allocate the first element aligned to + 16 bytes. + + + Array is highly optimized compared to std::vector. + Array operations are less expensive than on std::vector and for large + amounts of data, Array consumes only 1.5x the total size of the + data, while std::vector consumes 2.0x. The default + array takes up zero heap space. The first resize (or append) + operation grows it to a reasonable internal size so it is efficient + to append to small arrays. Memory is allocated using + System::alignedMalloc, which produces pointers aligned to 16-byte + boundaries for use with SSE instructions and uses pooled storage for + fast allocation. When Array needs to copy + data internally on a resize operation it correctly invokes copy + constructors of the elements (the MSVC6 implementation of + std::vector uses realloc, which can create memory leaks for classes + containing references and pointers). Array provides a guaranteed + safe way to access the underlying data as a flat C array -- + Array::getCArray. Although (T*)std::vector::begin() can be used for + this purpose, it is not guaranteed to succeed on all platforms. + + To serialize an array, see G3D::serialize. + + The template parameter MIN_ELEMENTS indicates the smallest number of + elements that will be allocated. The default of 10 is designed to avoid + the overhead of repeatedly allocating the array as it grows from 1, to 2, and so on. + If you are creating a lot of small Arrays, however, you may want to set this smaller + to reduce the memory cost. Once the array has been allocated, it will never + deallocate the underlying array unless MIN_ELEMENTS is set to 0, MIN_BYTES is 0, and the array + is empty. + + Do not subclass an Array. + */ +template <class T, int MIN_ELEMENTS = 10, size_t MIN_BYTES = 32> +class Array { +private: + /** 0...num-1 are initialized elements, num...numAllocated-1 are not */ + T* data; + + int num; + int numAllocated; + + void init(int n, int a) { + debugAssert(n <= a); + debugAssert(n >= 0); + this->num = 0; + this->numAllocated = 0; + data = NULL; + if (a > 0) { + resize(n); + } else { + data = NULL; + } + } + + void _copy(const Array &other) { + init(other.num, other.num); + for (int i = 0; i < num; i++) { + data[i] = other.data[i]; + } + } + + /** + Returns true iff address points to an element of this array. + Used by append. + */ + inline bool inArray(const T* address) { + return (address >= data) && (address < data + num); + } + + + /** Only compiled if you use the sort procedure. */ + static bool __cdecl compareGT(const T& a, const T& b) { + return a > b; + } + + + /** + Allocates a new array of size numAllocated (not a parameter to the method) + and then copies at most oldNum elements from the old array to it. Destructors are + called for oldNum elements of the old array. + */ + void realloc(int oldNum) { + T* oldData = data; + + // The allocation is separate from the constructor invocation because we don't want + // to pay for the cost of constructors until the newly allocated + // elements are actually revealed to the application. They + // will be constructed in the resize() method. + + data = (T*)System::alignedMalloc(sizeof(T) * numAllocated, 16); + + // Call the copy constructors + {const int N = G3D::min(oldNum, numAllocated); + const T* end = data + N; + T* oldPtr = oldData; + for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) { + + // Use placement new to invoke the constructor at the location + // that we determined. Use the copy constructor to make the assignment. + const T* constructed = new (ptr) T(*oldPtr); + + (void)constructed; + debugAssertM(constructed == ptr, + "new returned a different address than the one provided by Array."); + }} + + // Call destructors on the old array (if there is no destructor, this will compile away) + {const T* end = oldData + oldNum; + for (T* ptr = oldData; ptr < end; ++ptr) { + ptr->~T(); + }} + + + System::alignedFree(oldData); + } + +public: + + /** + G3D C++ STL style iterator variable. Call begin() to get + the first iterator, pre-increment (++i) the iterator to get to + the next value. Use dereference (*i) to access the element. + */ + typedef T* Iterator; + /** G3D C++ STL style const iterator in same style as Iterator. */ + typedef const T* ConstIterator; + + /** stl porting compatibility helper */ + typedef Iterator iterator; + /** stl porting compatibility helper */ + typedef ConstIterator const_iterator; + /** stl porting compatibility helper */ + typedef T value_type; + /** stl porting compatibility helper */ + typedef int size_type; + /** stl porting compatibility helper */ + typedef int difference_type; + + /** + C++ STL style iterator method. Returns the first iterator element. + Do not change the size of the array while iterating. + */ + Iterator begin() { + return data; + } + + ConstIterator begin() const { + return data; + } + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + ConstIterator end() const { + return data + num; + } + + Iterator end() { + return data + num; + } + + /** + The array returned is only valid until the next append() or resize call, or + the Array is deallocated. + */ + T* getCArray() { + return data; + } + + /** + The array returned is only valid until the next append() or resize call, or + the Array is deallocated. + */ + const T* getCArray() const { + return data; + } + + /** Creates a zero length array (no heap allocation occurs until resize). */ + Array() { + init(0, 0); + } + + /** + Creates an array of size. + */ + Array(int size) { + init(size, size); + } + + /** + Copy constructor + */ + Array(const Array& other) { + _copy(other); + } + + /** + Destructor does not delete() the objects if T is a pointer type + (e.g. T = int*) instead, it deletes the <B>pointers themselves</B> and + leaves the objects. Call deleteAll if you want to dealocate + the objects referenced. Do not call deleteAll if <CODE>T</CODE> is not a pointer + type (e.g. do call Array<Foo*>::deleteAll, do <B>not</B> call Array<Foo>::deleteAll). + */ + ~Array() { + // Invoke the destructors on the elements + for (int i = 0; i < num; i++) { + (data + i)->~T(); + } + + System::alignedFree(data); + // Set to 0 in case this Array is global and gets referenced during app exit + data = NULL; + num = 0; + numAllocated = 0; + } + + + /** + Removes all elements. Use resize(0, false) or fastClear if you want to + remove all elements without deallocating the underlying array + so that future append() calls will be faster. + */ + void clear(bool shrink = true) { + resize(0, shrink); + } + + /** resize(0, false) + @deprecated*/ + void fastClear() { + clear(false); + } + + /** + Assignment operator. + */ + Array& operator=(const Array& other) { + resize(other.num); + for (int i = 0; i < num; ++i) { + data[i] = other[i]; + } + return *this; + } + + Array& operator=(const std::vector<T>& other) { + resize((int)other.size()); + for (int i = 0; i < num; ++i) { + data[i] = other[i]; + } + return *this; + } + + /** + Number of elements in the array. + */ + inline int size() const { + return num; + } + + /** + Number of elements in the array. (Same as size; this is just + here for convenience). + */ + inline int length() const { + return size(); + } + + /** + Swaps element index with the last element in the array then + shrinks the array by one. + */ + void fastRemove(int index, bool shrinkIfNecessary = false) { + debugAssert(index >= 0); + debugAssert(index < num); + data[index] = data[num - 1]; + resize(size() - 1, shrinkIfNecessary); + } + + /** Resizes without shrinking the underlying array. Same as resize(n, false). + @deprecated*/ + void fastResize(int n) { + resize(n, false); + } + + + /** + Inserts at the specified index and shifts all other elements up by one. + */ + void insert(int n, const T& value) { + // Add space for the extra element + resize(num + 1, false); + + for (int i = num - 1; i > n; --i) { + data[i] = data[i - 1]; + } + data[n] = value; + } + + /** @param shrinkIfNecessary if false, memory will never be + reallocated when the array shrinks. This makes resizing much + faster but can waste memory. */ + void resize(int n, bool shrinkIfNecessary = true) { + int oldNum = num; + num = n; + + // Call the destructors on newly hidden elements if there are any + for (int i = num; i < oldNum; ++i) { + (data + i)->~T(); + } + + // Once allocated, always maintain MIN_ELEMENTS elements or 32 bytes, whichever is higher. + static const int minSize = G3D::max(MIN_ELEMENTS, (int)(MIN_BYTES / sizeof(T))); + + if ((MIN_ELEMENTS == 0) && (MIN_BYTES == 0) && (n == 0) && shrinkIfNecessary) { + // Deallocate the array completely + numAllocated = 0; + System::alignedFree(data); + data = NULL; + return; + } + + if (num > numAllocated) { + // Grow the underlying array + + if (numAllocated == 0) { + // First allocation; grow to exactly the size requested to avoid wasting space. + numAllocated = n; + debugAssert(oldNum == 0); + realloc(oldNum); + } else { + + if (num < minSize) { + // Grow to at least the minimum size + numAllocated = minSize; + + } else { + + // Increase the underlying size of the array. Grow aggressively + // up to 64k, less aggressively up to 400k, and then grow relatively + // slowly (1.5x per resize) to avoid excessive space consumption. + // + // These numbers are tweaked according to performance tests. + + float growFactor = 3.0; + + size_t oldSizeBytes = numAllocated * sizeof(T); + if (oldSizeBytes > 400000) { + // Avoid bloat + growFactor = 1.5; + } else if (oldSizeBytes > 64000) { + // This is what std:: uses at all times + growFactor = 2.0; + } + + numAllocated = (num - numAllocated) + (int)(numAllocated * growFactor); + + if (numAllocated < minSize) { + numAllocated = minSize; + } + } + + realloc(oldNum); + } + + } else if ((num <= numAllocated / 3) && shrinkIfNecessary && (num > minSize)) { + // Shrink the underlying array + + // Only copy over old elements that still remain after resizing + // (destructors were called for others if we're shrinking) + realloc(iMin(num, oldNum)); + + } + + // Call the constructors on newly revealed elements. + // Do not use parens because we don't want the intializer + // invoked for POD types. + for (int i = oldNum; i < num; ++i) { + new (data + i) T; + } + } + + /** + Add an element to the end of the array. Will not shrink the underlying array + under any circumstances. It is safe to append an element that is already + in the array. + */ + inline void append(const T& value) { + + if (num < numAllocated) { + // This is a simple situation; just stick it in the next free slot using + // the copy constructor. + new (data + num) T(value); + ++num; + } else if (inArray(&value)) { + // The value was in the original array; resizing + // is dangerous because it may move the value + // we have a reference to. + T tmp = value; + append(tmp); + } else { + // Here we run the empty initializer where we don't have to, but + // this simplifies the computation. + resize(num + 1, DONT_SHRINK_UNDERLYING_ARRAY); + data[num - 1] = value; + } + } + + + inline void append(const T& v1, const T& v2) { + if (inArray(&v1) || inArray(&v2)) { + // Copy into temporaries so that the references won't break when + // the array resizes. + T t1 = v1; + T t2 = v2; + append(t1, t2); + } else if (num + 1 < numAllocated) { + // This is a simple situation; just stick it in the next free slot using + // the copy constructor. + new (data + num) T(v1); + new (data + num + 1) T(v2); + num += 2; + } else { + // Resize the array. Note that neither value is already in the array. + resize(num + 2, DONT_SHRINK_UNDERLYING_ARRAY); + data[num - 2] = v1; + data[num - 1] = v2; + } + } + + + inline void append(const T& v1, const T& v2, const T& v3) { + if (inArray(&v1) || inArray(&v2) || inArray(&v3)) { + T t1 = v1; + T t2 = v2; + T t3 = v3; + append(t1, t2, t3); + } else if (num + 2 < numAllocated) { + // This is a simple situation; just stick it in the next free slot using + // the copy constructor. + new (data + num) T(v1); + new (data + num + 1) T(v2); + new (data + num + 2) T(v3); + num += 3; + } else { + resize(num + 3, DONT_SHRINK_UNDERLYING_ARRAY); + data[num - 3] = v1; + data[num - 2] = v2; + data[num - 1] = v3; + } + } + + + inline void append(const T& v1, const T& v2, const T& v3, const T& v4) { + if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4)) { + T t1 = v1; + T t2 = v2; + T t3 = v3; + T t4 = v4; + append(t1, t2, t3, t4); + } else if (num + 3 < numAllocated) { + // This is a simple situation; just stick it in the next free slot using + // the copy constructor. + new (data + num) T(v1); + new (data + num + 1) T(v2); + new (data + num + 2) T(v3); + new (data + num + 3) T(v4); + num += 4; + } else { + resize(num + 4, DONT_SHRINK_UNDERLYING_ARRAY); + data[num - 4] = v1; + data[num - 3] = v2; + data[num - 2] = v3; + data[num - 1] = v4; + } + } + + /** + Returns true if the given element is in the array. + */ + bool contains(const T& e) const { + for (int i = 0; i < size(); ++i) { + if ((*this)[i] == e) { + return true; + } + } + + return false; + } + + /** + Append the elements of array. Cannot be called with this array + as an argument. + */ + void append(const Array<T>& array) { + debugAssert(this != &array); + int oldNum = num; + int arrayLength = array.length(); + + resize(num + arrayLength, false); + + for (int i = 0; i < arrayLength; i++) { + data[oldNum + i] = array.data[i]; + } + } + + /** + Pushes a new element onto the end and returns its address. + This is the same as A.resize(A.size() + 1, false); A.last() + */ + inline T& next() { + resize(num + 1, false); + return last(); + } + + /** + Pushes an element onto the end (appends) + */ + inline void push(const T& value) { + append(value); + } + + inline void push(const Array<T>& array) { + append(array); + } + + /** Alias to provide std::vector compatibility */ + inline void push_back(const T& v) { + push(v); + } + + /** "The member function removes the last element of the controlled sequence, which must be non-empty." + For compatibility with std::vector. */ + inline void pop_back() { + pop(); + } + + /** + "The member function returns the storage currently allocated to hold the controlled + sequence, a value at least as large as size()" + For compatibility with std::vector. + */ + int capacity() const { + return numAllocated; + } + + /** + "The member function returns a reference to the first element of the controlled sequence, + which must be non-empty." + For compatibility with std::vector. + */ + T& front() { + return (*this)[0]; + } + + /** + "The member function returns a reference to the first element of the controlled sequence, + which must be non-empty." + For compatibility with std::vector. + */ + const T& front() const { + return (*this)[0]; + } + + /** + Removes the last element and returns it. By default, shrinks the underlying array. + */ + inline T pop(bool shrinkUnderlyingArrayIfNecessary = true) { + debugAssert(num > 0); + T temp = data[num - 1]; + resize(num - 1, shrinkUnderlyingArrayIfNecessary); + return temp; + } + + /** Pops the last element and discards it without returning anything. Faster than pop. + By default, does not shrink the underlying array.*/ + inline void popDiscard(bool shrinkUnderlyingArrayIfNecessary = false) { + debugAssert(num > 0); + resize(num - 1, shrinkUnderlyingArrayIfNecessary); + } + + + /** + "The member function swaps the controlled sequences between *this and str." + Note that this is slower than the optimal std implementation. + + For compatibility with std::vector. + */ + void swap(Array<T>& str) { + Array<T> temp = str; + str = *this; + *this = temp; + } + + + /** + Performs bounds checks in debug mode + */ + inline T& operator[](int n) { + debugAssertM((n >= 0) && (n < num), format("Array index out of bounds. n = %d, size() = %d", n, num)); + debugAssert(data!=NULL); + return data[n]; + } + + inline T& operator[](unsigned int n) { + debugAssertM(n < (unsigned int)num, format("Array index out of bounds. n = %d, size() = %d", n, num)); + return data[n]; + } + + /** + Performs bounds checks in debug mode + */ + inline const T& operator[](int n) const { + debugAssert((n >= 0) && (n < num)); + debugAssert(data!=NULL); + return data[n]; + } + + inline const T& operator[](unsigned int n) const { + debugAssert((n < (unsigned int)num)); + debugAssert(data!=NULL); + return data[n]; + } + + inline T& randomElement() { + debugAssert(num > 0); + debugAssert(data!=NULL); + return data[iRandom(0, num - 1)]; + } + + inline const T& randomElement() const { + debugAssert(num > 0); + debugAssert(data!=NULL); + return data[iRandom(0, num - 1)]; + } + + /** + Returns the last element, performing a check in + debug mode that there is at least one element. + */ + inline const T& last() const { + debugAssert(num > 0); + debugAssert(data!=NULL); + return data[num - 1]; + } + + /** Returns element lastIndex() */ + inline T& last() { + debugAssert(num > 0); + debugAssert(data!=NULL); + return data[num - 1]; + } + + /** Returns <i>size() - 1</i> */ + inline int lastIndex() const { + debugAssertM(num > 0, "Array is empty"); + return num - 1; + } + + inline int firstIndex() const { + debugAssertM(num > 0, "Array is empty"); + return 0; + } + + /** Returns element firstIndex(), performing a check in debug mode to ensure that there is at least one */ + inline T& first() { + debugAssertM(num > 0, "Array is empty"); + return data[0]; + } + + inline const T& first() const { + debugAssertM(num > 0, "Array is empty"); + return data[0]; + } + + /** Returns iFloor(size() / 2), throws an assertion in debug mode if the array is empty */ + inline int middleIndex() const { + debugAssertM(num > 0, "Array is empty"); + return num >> 1; + } + + /** Returns element middleIndex() */ + inline const T& middle() const { + debugAssertM(num > 0, "Array is empty"); + return data[num >> 1]; + } + + /** Returns element middleIndex() */ + inline T& middle() { + debugAssertM(num > 0, "Array is empty"); + return data[num >> 1]; + } + + /** + Calls delete on all objects[0...size-1] + and sets the size to zero. + */ + void deleteAll() { + for (int i = 0; i < num; i++) { + delete data[i]; + } + resize(0); + } + + /** + Returns the index of (the first occurance of) an index or -1 if + not found. + */ + int findIndex(const T& value) const { + for (int i = 0; i < num; ++i) { + if (data[i] == value) { + return i; + } + } + return -1; + } + + /** + Finds an element and returns the iterator to it. If the element + isn't found then returns end(). + */ + Iterator find(const T& value) { + for (int i = 0; i < num; ++i) { + if (data[i] == value) { + return data + i; + } + } + return end(); + } + + ConstIterator find(const T& value) const { + for (int i = 0; i < num; ++i) { + if (data[i] == value) { + return data + i; + } + } + return end(); + } + + /** + Removes count elements from the array + referenced either by index or Iterator. + */ + void remove(Iterator element, int count = 1) { + debugAssert((element >= begin()) && (element < end())); + debugAssert((count > 0) && (element + count) <= end()); + Iterator last = end() - count; + + while(element < last) { + element[0] = element[count]; + ++element; + } + + resize(num - count); + } + + void remove(int index, int count = 1) { + debugAssert((index >= 0) && (index < num)); + debugAssert((count > 0) && (index + count <= num)); + + remove(begin() + index, count); + } + + /** + Reverse the elements of the array in place. + */ + void reverse() { + T temp; + + int n2 = num / 2; + for (int i = 0; i < n2; ++i) { + temp = data[num - 1 - i]; + data[num - 1 - i] = data[i]; + data[i] = temp; + } + } + + /** + Sort using a specific less-than function, e.g.: + + <PRE> + bool __cdecl myLT(const MyClass& elem1, const MyClass& elem2) { + return elem1.x < elem2.x; + } + </PRE> + + Note that for pointer arrays, the <CODE>const</CODE> must come + <I>after</I> the class name, e.g., <CODE>Array<MyClass*></CODE> uses: + + <PRE> + bool __cdecl myLT(MyClass*const& elem1, MyClass*const& elem2) { + return elem1->x < elem2->x; + } + </PRE> + */ + void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) { + std::sort(data, data + num, lessThan); + } + + + /** + Sorts the array in increasing order using the > or < operator. To + invoke this method on Array<T>, T must override those operator. + You can overide these operators as follows: + <code> + bool T::operator>(const T& other) const { + return ...; + } + bool T::operator<(const T& other) const { + return ...; + } + </code> + */ + void sort(int direction = SORT_INCREASING) { + if (direction == SORT_INCREASING) { + std::sort(data, data + num); + } else { + std::sort(data, data + num, compareGT); + } + } + + /** + Sorts elements beginIndex through and including endIndex. + */ + void sortSubArray(int beginIndex, int endIndex, int direction = SORT_INCREASING) { + if (direction == SORT_INCREASING) { + std::sort(data + beginIndex, data + endIndex + 1); + } else { + std::sort(data + beginIndex, data + endIndex + 1, compareGT); + } + } + + void sortSubArray(int beginIndex, int endIndex, bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) { + std::sort(data + beginIndex, data + endIndex + 1, lessThan); + } + + /** + The StrictWeakOrdering can be either a class that overloads the function call operator() or + a function pointer of the form <code>bool (__cdecl *lessThan)(const T& elem1, const T& elem2)</code> + */ + template<typename StrictWeakOrdering> + void sortSubArray(int beginIndex, int endIndex, StrictWeakOrdering& lessThan) { + std::sort(data + beginIndex, data + endIndex + 1, lessThan); + } + + /** Uses < and == to evaluate operator(); this is the default comparator for Array::partition. */ + class DefaultComparator { + public: + inline int operator()(const T& A, const T& B) const { + if (A < B) { + return 1; + } else if (A == B) { + return 0; + } else { + return -1; + } + } + }; + + /** The output arrays are resized with fastClear() so that if they are already of the same size + as this array no memory is allocated during partitioning. + + @param comparator A function, or class instance with an overloaded operator() that compares + two elements of type <code>T</code> and returns 0 if they are equal, -1 if the second is smaller, + and 1 if the first is smaller (i.e., following the conventions of std::string::compare). For example: + + <pre> + int compare(int A, int B) { + if (A < B) { + return 1; + } else if (A == B) { + return 0; + } else { + return -1; + } + } + </pre> + */ + template<typename Comparator> + void partition( + const T& partitionElement, + Array<T>& ltArray, + Array<T>& eqArray, + Array<T>& gtArray, + const Comparator& comparator) const { + + // Make sure all arrays are independent + debugAssert(<Array != this); + debugAssert(&eqArray != this); + debugAssert(>Array != this); + debugAssert(<Array != &eqArray); + debugAssert(<Array != >Array); + debugAssert(&eqArray != >Array); + + // Clear the arrays + ltArray.fastClear(); + eqArray.fastClear(); + gtArray.fastClear(); + + // Form a table of buckets for lt, eq, and gt + Array<T>* bucket[3] = {<Array, &eqArray, >Array}; + + for (int i = 0; i < num; ++i) { + int c = comparator(partitionElement, data[i]); + debugAssertM(c >= -1 && c <= 1, "Comparator returned an illegal value."); + + // Insert into the correct bucket, 0, 1, or 2 + bucket[c + 1]->append(data[i]); + } + } + + /** + Uses < and == on elements to perform a partition. See partition(). + */ + void partition( + const T& partitionElement, + Array<T>& ltArray, + Array<T>& eqArray, + Array<T>& gtArray) const { + + partition(partitionElement, ltArray, eqArray, gtArray, typename Array<T>::DefaultComparator()); + } + + /** + Paritions the array into those below the median, those above the median, and those elements + equal to the median in expected O(n) time using quickselect. If the array has an even + number of different elements, the median for partition purposes is the largest value + less than the median. + + @param tempArray used for working scratch space + @param comparator see parition() for a discussion.*/ + template<typename Comparator> + void medianPartition( + Array<T>& ltMedian, + Array<T>& eqMedian, + Array<T>& gtMedian, + Array<T>& tempArray, + const Comparator& comparator) const { + + ltMedian.fastClear(); + eqMedian.fastClear(); + gtMedian.fastClear(); + + // Handle trivial cases first + switch (size()) { + case 0: + // Array is empty; no parition is possible + return; + + case 1: + // One element + eqMedian.append(first()); + return; + + case 2: + { + // Two element array; median is the smaller + int c = comparator(first(), last()); + + switch (c) { + case -1: + // first was bigger + eqMedian.append(last()); + gtMedian.append(first()); + break; + + case 0: + // Both equal to the median + eqMedian.append(first(), last()); + break; + + case 1: + // Last was bigger + eqMedian.append(first()); + gtMedian.append(last()); + break; + } + } + return; + } + + // All other cases use a recursive randomized median + + // Number of values less than all in the current arrays + int ltBoost = 0; + + // Number of values greater than all in the current arrays + int gtBoost = 0; + + // For even length arrays, force the gt array to be one larger than the + // lt array: + // [1 2 3] size = 3, choose half = (s + 1) /2 + // + int lowerHalfSize, upperHalfSize; + if (isEven(size())) { + lowerHalfSize = size() / 2; + upperHalfSize = lowerHalfSize + 1; + } else { + lowerHalfSize = upperHalfSize = (size() + 1) / 2; + } + const T* xPtr = NULL; + + // Maintain pointers to the arrays; we'll switch these around during sorting + // to avoid copies. + const Array<T>* source = this; + Array<T>* lt = <Median; + Array<T>* eq = &eqMedian; + Array<T>* gt = >Median; + Array<T>* extra = &tempArray; + + while (true) { + // Choose a random element -- choose the middle element; this is theoretically + // suboptimal, but for loosly sorted array is actually the best strategy + + xPtr = &(source->middle()); + if (source->size() == 1) { + // Done; there's only one element left + break; + } + const T& x = *xPtr; + + // Note: partition (fast) clears the arrays for us + source->partition(x, *lt, *eq, *gt, comparator); + + int L = lt->size() + ltBoost + eq->size(); + int U = gt->size() + gtBoost + eq->size(); + if ((L >= lowerHalfSize) && + (U >= upperHalfSize)) { + + // x must be the partition median + break; + + } else if (L < lowerHalfSize) { + + // x must be smaller than the median. Recurse into the 'gt' array. + ltBoost += lt->size() + eq->size(); + + // The new gt array will be the old source array, unless + // that was the this pointer (i.e., unless we are on the + // first iteration) + Array<T>* newGt = (source == this) ? extra : const_cast<Array<T>*>(source); + + // Now set up the gt array as the new source + source = gt; + gt = newGt; + + } else { + + // x must be bigger than the median. Recurse into the 'lt' array. + gtBoost += gt->size() + eq->size(); + + // The new lt array will be the old source array, unless + // that was the this pointer (i.e., unless we are on the + // first iteration) + Array<T>* newLt = (source == this) ? extra : const_cast<Array<T>*>(source); + + // Now set up the lt array as the new source + source = lt; + lt = newLt; + } + } + + // Now that we know the median, make a copy of it (since we're about to destroy the array that it + // points into). + T median = *xPtr; + xPtr = NULL; + + // Partition the original array (note that this fast clears for us) + partition(median, ltMedian, eqMedian, gtMedian, comparator); + } + + /** + Computes a median partition using the default comparator and a dynamically allocated temporary + working array. If the median is not in the array, it is chosen to be the largest value smaller + than the true median. + */ + void medianPartition( + Array<T>& ltMedian, + Array<T>& eqMedian, + Array<T>& gtMedian) const { + + Array<T> temp; + medianPartition(ltMedian, eqMedian, gtMedian, temp, DefaultComparator()); + } + + + /** Redistributes the elements so that the new order is statistically independent + of the original order. O(n) time.*/ + void randomize() { + T temp; + + for (int i = size() - 1; i >= 0; --i) { + int x = iRandom(0, i); + + temp = data[i]; + data[i] = data[x]; + data[x] = temp; + } + } + + +}; + + +/** Array::contains for C-arrays */ +template<class T> bool contains(const T* array, int len, const T& e) { + for (int i = len - 1; i >= 0; --i) { + if (array[i] == e) { + return true; + } + } + return false; +} + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h b/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h new file mode 100644 index 00000000000..9d58e02efe5 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h @@ -0,0 +1,166 @@ +/** + @file AtomicInt32.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2005-09-01 + @edited 2006-06-21 + */ +#ifndef G3D_ATOMICINT32_H +#define G3D_ATOMICINT32_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" + +#if defined(G3D_OSX) + #include <libkern/OSAtomic.h> +#endif + +namespace G3D { + +/** + An integer that may safely be used on different threads without + external locking. + + On Win32, Linux, FreeBSD, and Mac OS X this is implemented without locks. + + <B>BETA API</B> This is unsupported and may change + */ +class AtomicInt32 { +private: +# if defined(G3D_WIN32) + volatile long m_value; +# elif defined(G3D_OSX) + int32_t m_value; +# else + volatile int32 m_value; +# endif + + +public: + + /** Initial value is undefined. */ + AtomicInt32() {} + + /** Atomic set */ + explicit AtomicInt32(const int32 x) { + m_value = x; + } + + /** Atomic set */ + AtomicInt32(const AtomicInt32& x) { + m_value = x.m_value; + } + + /** Atomic set */ + const AtomicInt32& operator=(const int32 x) { + m_value = x; + return *this; + } + + /** Atomic set */ + void operator=(const AtomicInt32& x) { + m_value = x.m_value; + } + + /** Returns the current value */ + int32 value() const { + return m_value; + } + + /** Returns the old value, before the add. */ + int32 add(const int32 x) { +# if defined(G3D_WIN32) + + return InterlockedExchangeAdd(&m_value, x); + +# elif defined(G3D_LINUX) || defined(G3D_FREEBSD) + + int32 old; + asm volatile ("lock; xaddl %0,%1" + : "=r"(old), "=m"(m_value) /* outputs */ + : "0"(x), "m"(m_value) /* inputs */ + : "memory", "cc"); + return old; + +# elif defined(G3D_OSX) + + int32 old = m_value; + OSAtomicAdd32(x, &m_value); + return old; + +# endif + } + + /** Returns old value. */ + int32 sub(const int32 x) { + return add(-x); + } + + void increment() { +# if defined(G3D_WIN32) + // Note: returns the newly incremented value + InterlockedIncrement(&m_value); +# elif defined(G3D_LINUX) || defined(G3D_FREEBSD) + add(1); +# elif defined(G3D_OSX) + // Note: returns the newly incremented value + OSAtomicIncrement32(&m_value); +# endif + } + + /** Returns zero if the result is zero after decrement, non-zero otherwise.*/ + int32 decrement() { +# if defined(G3D_WIN32) + // Note: returns the newly decremented value + return InterlockedDecrement(&m_value); +# elif defined(G3D_LINUX) || defined(G3D_FREEBSD) + unsigned char nz; + + asm volatile ("lock; decl %1;\n\t" + "setnz %%al" + : "=a" (nz) + : "m" (m_value) + : "memory", "cc"); + return nz; +# elif defined(G3D_OSX) + // Note: returns the newly decremented value + return OSAtomicDecrement32(&m_value); +# endif + } + + + /** Atomic test-and-set: if <code>*this == comperand</code> then <code>*this := exchange</code> else do nothing. + In both cases, returns the old value of <code>*this</code>. + + Performs an atomic comparison of this with the Comperand value. + If this is equal to the Comperand value, the Exchange value is stored in this. + Otherwise, no operation is performed. + + Under VC6 the sign bit may be lost. + + */ + int32 compareAndSet(const int32 comperand, const int32 exchange) { +# if defined(G3D_WIN32) + return InterlockedCompareExchange(&m_value, exchange, comperand); +# elif defined(G3D_LINUX) || defined(G3D_FREEBSD) + int32 ret; + asm volatile ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (exchange), "m" (m_value), "0"(comperand) + : "memory", "cc"); + return ret; +# elif defined(G3D_OSX) + int32 old = m_value; + + OSAtomicCompareAndSwap32(comperand, exchange, &m_value); + + return old; +# endif + } + +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h new file mode 100644 index 00000000000..53f0c144bf6 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h @@ -0,0 +1,140 @@ +/** + @file BinaryFormat.h + @maintainer Morgan McGuire, matrix@graphics3d.com + + @author 2005-06-03 + @edited 2005-06-03 + + Copyright 2000-2005, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_BINARYFORMAT_H +#define G3D_BINARYFORMAT_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" + +namespace G3D { + +class Vector2; +class Vector2int16; +class Vector3; +class Vector3int16; +class Vector4; +class Vector4int16; +class Color3; +class Color3uint8; +class Color4; +class Color4uint8; + +/** + Some values like float16 and int128 have no current CPU data structure that implements them but are useful + for file formats and for GPUs. + + CHUNK_BINFMT data follows the protocol. + */ +// Must be packed int 16 bits for the chunk reader +// We can't name these just "INT8" etc. because some libraries #define names like that +enum BinaryFormat { + FIRST_BINFMT = 1000, + + BOOL8_BINFMT, + UINT8_BINFMT, INT8_BINFMT, UINT16_BINFMT, INT16_BINFMT, UINT32_BINFMT, INT32_BINFMT, UINT64_BINFMT, INT64_BINFMT, UINT128_BINFMT, INT128_BINFMT, + FLOAT16_BINFMT, FLOAT32_BINFMT, FLOAT64_BINFMT, + VECTOR2_BINFMT, VECTOR2INT16_BINFMT, + VECTOR3_BINFMT, VECTOR3INT16_BINFMT, + VECTOR4_BINFMT, VECTOR4INT16_BINFMT, + COLOR3_BINFMT, COLOR3UINT8_BINFMT, COLOR3INT16_BINFMT, + COLOR4_BINFMT, COLOR4UINT8_BINFMT, COLOR4INT16_BINFMT, + STRING_BINFMT, STRINGEVEN_BINFMT, STRING8_BINFMT, STRING16_BINFMT, STRING32_BINFMT, + + CHUNK_BINFMT, + + CUSTOM_BINFMT, + + LAST_BINFMT +}; + +} + +/** A macro that maps G3D types to format constants. + (e.g. binaryFormatOf(Vector3) == VECTOR3_BINFMT). +*/ +// This implementation is designed to meet the following constraints: +// 1. Work around the many MSVC++ partial template bugs +// 2. Work for primitive types (e.g. int) +#define binaryFormatOf(T) (G3D::_internal::_BinaryFormat<T>::x()) + +namespace G3D { +namespace _internal { + + +template<class T> class _BinaryFormat { +public: + static BinaryFormat x() { + return CUSTOM_BINFMT; + } +}; +}} + + +/** + Macro to declare the underlying format (as will be returned by glFormatOf) + of a type. For example, + + <PRE> + DECLARE_BINARYFORMATOF(Vector4, VECTOR4_BINFMT) + </PRE> + + Use this so you can make vertex arrays of your own classes and not just + the standard ones. + */ +#define DECLARE_BINARYFORMATOF(CType, EnumType) \ +namespace G3D { \ + namespace _internal { \ + template<> class _BinaryFormat<CType> { \ + public: \ + static BinaryFormat x() { \ + return EnumType; \ + } \ + }; \ + } \ +} + +DECLARE_BINARYFORMATOF( bool, BOOL8_BINFMT ) + +DECLARE_BINARYFORMATOF( uint8, UINT8_BINFMT ) +DECLARE_BINARYFORMATOF( int8, INT8_BINFMT ) +DECLARE_BINARYFORMATOF( uint16, UINT16_BINFMT ) +DECLARE_BINARYFORMATOF( int16, INT16_BINFMT ) +DECLARE_BINARYFORMATOF( uint32, UINT32_BINFMT ) +DECLARE_BINARYFORMATOF( int32, INT32_BINFMT ) +DECLARE_BINARYFORMATOF( uint64, UINT64_BINFMT ) +DECLARE_BINARYFORMATOF( int64, INT64_BINFMT ) + +DECLARE_BINARYFORMATOF( float32, FLOAT32_BINFMT ) +DECLARE_BINARYFORMATOF( float64, FLOAT64_BINFMT ) + +DECLARE_BINARYFORMATOF( Vector2, VECTOR2_BINFMT ) +DECLARE_BINARYFORMATOF( Vector2int16, VECTOR2INT16_BINFMT ) +DECLARE_BINARYFORMATOF( Vector3, VECTOR3_BINFMT ) +DECLARE_BINARYFORMATOF( Vector3int16, VECTOR3INT16_BINFMT ) +DECLARE_BINARYFORMATOF( Vector4, VECTOR4_BINFMT ) +DECLARE_BINARYFORMATOF( Vector4int16, VECTOR4INT16_BINFMT ) + +DECLARE_BINARYFORMATOF( Color3, COLOR3_BINFMT ) +DECLARE_BINARYFORMATOF( Color3uint8, COLOR3UINT8_BINFMT ) +DECLARE_BINARYFORMATOF( Color4, COLOR4_BINFMT ) +DECLARE_BINARYFORMATOF( Color4uint8, COLOR4UINT8_BINFMT ) + +namespace G3D { + +/** Returns -1 if the format is custom, otherwise the byte size + of a single element in this format.*/ +int32 byteSize(BinaryFormat f); + + +} //G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h new file mode 100644 index 00000000000..c0ab9052402 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h @@ -0,0 +1,441 @@ +/** + @file BinaryInput.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-08-09 + @edited 2006-07-19 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_BINARYINPUT_H +#define G3D_BINARYINPUT_H + +#ifdef _MSC_VER +// Disable conditional expression is constant, which occurs incorrectly on inlined functions +# pragma warning(push) +# pragma warning( disable : 4127 ) +#endif + +#include <assert.h> +#include <string> +#include <vector> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdio.h> +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Color4.h" +#include "G3D/Color3.h" +#include "G3D/Vector4.h" +#include "G3D/Vector3.h" +#include "G3D/Vector2.h" +#include "G3D/g3dmath.h" +#include "G3D/debug.h" +#include "G3D/System.h" + + +namespace G3D { + +#if defined(G3D_WIN32) || defined(G3D_LINUX) + // Allow writing of integers to non-word aligned locations. + // This is legal on x86, but not on other platforms. + #define G3D_ALLOW_UNALIGNED_WRITES +#endif + +/** + Sequential or random access byte-order independent binary file access. + Files compressed with zlib and beginning with an unsigned 32-bit int + size are transparently decompressed when the compressed = true flag is + specified to the constructor. + + For every readX method there are also versions that operate on a whole + Array, std::vector, or C-array. e.g. readFloat32(Array<float32>& array, n) + These methods resize the array or std::vector to the appropriate size + before reading. For a C-array, they require the pointer to reference + a memory block at least large enough to hold <I>n</I> elements. + + Most classes define serialize/deserialize methods that use BinaryInput, + BinaryOutput, TextInput, and TextOutput. There are text serializer + functions for primitive types (e.g. int, std::string, float, double) but not + binary serializers-- you <B>must</b> call the BinaryInput::readInt32 or + other appropriate function. This is because it would be very hard to + debug the error sequence: <CODE>serialize(1.0, bo); ... float f; deserialize(f, bi);</CODE> + in which a double is serialized and then deserialized as a float. + */ +class BinaryInput { +private: + + // The initial buffer will be no larger than this, but + // may grow if a large memory read occurs. 50 MB + enum {INITIAL_BUFFER_LENGTH = 50000000}; + + /** + is the file big or little endian + */ + G3DEndian m_fileEndian; + std::string m_filename; + + bool m_swapBytes; + + /** Next position to read from in bitString during readBits. */ + int m_bitPos; + + /** Bits currently being read by readBits. + Contains at most 8 (low) bits. Note that + beginBits/readBits actually consumes one extra byte, which + will be restored by writeBits.*/ + uint32 m_bitString; + + /** 1 when between beginBits and endBits, 0 otherwise. */ + int m_beginEndBits; + + /** When operating on huge files, we cannot load the whole file into memory. + This is the file position to which buffer[0] corresponds. + */ + int64 m_alreadyRead; + + /** + Length of the entire file, in bytes. + For the length of the buffer, see bufferLength + */ + int64 m_length; + + /** Length of the array referenced by buffer. May go past the end of the file!*/ + int64 m_bufferLength; + uint8* m_buffer; + + /** + Next byte in file, relative to buffer. + */ + int64 m_pos; + + /** + When true, the buffer is freed in the destructor. + */ + bool m_freeBuffer; + + /** Ensures that we are able to read at least minLength from startPosition (relative + to start of file). */ + void loadIntoMemory(int64 startPosition, int64 minLength = 0); + + /** Verifies that at least this number of bytes can be read.*/ + inline void prepareToRead(int64 nbytes) { + debugAssertM(m_length > 0, m_filename + " not found or corrupt."); + debugAssertM(m_pos + nbytes + m_alreadyRead <= m_length, "Read past end of file."); + + if (m_pos + nbytes > m_bufferLength) { + loadIntoMemory(m_pos + m_alreadyRead, nbytes); + } + } + + // Not implemented on purpose, don't use + BinaryInput(const BinaryInput&); + BinaryInput& operator=(const BinaryInput&); + bool operator==(const BinaryInput&); + + /** Buffer is compressed; replace it with a decompressed version */ + void decompress(); +public: + + /** false, constant to use with the copyMemory option */ + static const bool NO_COPY; + + /** + If the file cannot be opened, a zero length buffer is presented. + Automatically opens files that are inside zipfiles. + + @param compressed Set to true if and only if the file was + compressed using BinaryOutput's zlib compression. This has + nothing to do with whether the input is in a zipfile. + */ + BinaryInput( + const std::string& filename, + G3DEndian fileEndian, + bool compressed = false); + + /** + Creates input stream from an in memory source. + Unless you specify copyMemory = false, the data is copied + from the pointer, so you may deallocate it as soon as the + object is constructed. It is an error to specify copyMemory = false + and compressed = true. + + To decompress part of a file, you can follow the following paradigm: + + <PRE> + BinaryInput master(...); + + // read from master to point where compressed data exists. + + BinaryInput subset(master.getCArray() + master.getPosition(), + master.length() - master.getPosition(), + master.endian(), true, true); + + // Now read from subset (it is ok for master to go out of scope) + </PRE> + */ + BinaryInput( + const uint8* data, + int64 dataLen, + G3DEndian dataEndian, + bool compressed = false, + bool copyMemory = true); + + virtual ~BinaryInput(); + + /** Change the endian-ness of the file. This only changes the + interpretation of the file for future read calls; the + underlying data is unmodified.*/ + void setEndian(G3DEndian endian); + + G3DEndian endian() const { + return m_fileEndian; + } + + std::string getFilename() const { + return m_filename; + } + + /** + Returns a pointer to the internal memory buffer. + May throw an exception for huge files. + */ + const uint8* getCArray() const { + if (m_alreadyRead > 0) { + throw "Cannot getCArray for a huge file"; + } + return m_buffer; + } + + /** + Performs bounds checks in debug mode. [] are relative to + the start of the file, not the current position. + Seeks to the new position before reading (and leaves + that as the current position) + */ + inline uint8 operator[](int64 n) { + setPosition(n); + return readUInt8(); + } + + /** + Returns the length of the file in bytes. + */ + inline int64 getLength() const { + return m_length; + } + + inline int64 size() const { + return getLength(); + } + + /** + Returns the current byte position in the file, + where 0 is the beginning and getLength() - 1 is the end. + */ + inline int64 getPosition() const { + return m_pos + m_alreadyRead; + } + + /** + Sets the position. Cannot set past length. + May throw a char* when seeking backwards more than 10 MB on a huge file. + */ + inline void setPosition(int64 p) { + debugAssertM(p <= m_length, "Read past end of file"); + m_pos = p - m_alreadyRead; + if ((m_pos < 0) || (m_pos > m_bufferLength)) { + loadIntoMemory(m_pos + m_alreadyRead); + } + } + + /** + Goes back to the beginning of the file. + */ + inline void reset() { + setPosition(0); + } + + inline int8 readInt8() { + prepareToRead(1); + return m_buffer[m_pos++]; + } + + inline bool readBool8() { + return (readInt8() != 0); + } + + inline uint8 readUInt8() { + prepareToRead(1); + return ((uint8*)m_buffer)[m_pos++]; + } + + uint16 inline readUInt16() { + prepareToRead(2); + + m_pos += 2; + if (m_swapBytes) { + uint8 out[2]; + out[0] = m_buffer[m_pos - 1]; + out[1] = m_buffer[m_pos - 2]; + return *(uint16*)out; + } else { + #ifdef G3D_ALLOW_UNALIGNED_WRITES + return *(uint16*)(&m_buffer[m_pos - 2]); + #else + uint8 out[2]; + out[0] = m_buffer[m_pos - 2]; + out[1] = m_buffer[m_pos - 1]; + return *(uint16*)out; + #endif + } + + } + + inline int16 readInt16() { + uint16 a = readUInt16(); + return *(int16*)&a; + } + + inline uint32 readUInt32() { + prepareToRead(4); + + m_pos += 4; + if (m_swapBytes) { + uint8 out[4]; + out[0] = m_buffer[m_pos - 1]; + out[1] = m_buffer[m_pos - 2]; + out[2] = m_buffer[m_pos - 3]; + out[3] = m_buffer[m_pos - 4]; + return *(uint32*)out; + } else { + #ifdef G3D_ALLOW_UNALIGNED_WRITES + return *(uint32*)(&m_buffer[m_pos - 4]); + #else + uint8 out[4]; + out[0] = m_buffer[m_pos - 4]; + out[1] = m_buffer[m_pos - 3]; + out[2] = m_buffer[m_pos - 2]; + out[3] = m_buffer[m_pos - 1]; + return *(uint32*)out; + #endif + } + } + + + inline int32 readInt32() { + uint32 a = readUInt32(); + return *(int32*)&a; + } + + uint64 readUInt64(); + + inline int64 readInt64() { + uint64 a = readUInt64(); + return *(int64*)&a; + } + + inline float32 readFloat32() { + union { + uint32 a; + float32 b; + }; + a = readUInt32(); + return b; + } + + inline float64 readFloat64() { + union { + uint64 a; + float64 b; + }; + a = readUInt64(); + return b; + } + + void readBytes(void* bytes, int64 n); + + /** + Reads an n character string. The string is not + required to end in NULL in the file but will + always be a proper std::string when returned. + */ + std::string readString(int64 n); + + /** + Reads until NULL or the end of the file is encountered. + */ + std::string readString(); + + /** + Reads until NULL or the end of the file is encountered. + If the string has odd length (including NULL), reads + another byte. + */ + std::string readStringEven(); + + + std::string readString32(); + + Vector4 readVector4(); + Vector3 readVector3(); + Vector2 readVector2(); + + Color4 readColor4(); + Color3 readColor3(); + + /** + Skips ahead n bytes. + */ + inline void skip(int64 n) { + setPosition(m_pos + m_alreadyRead + n); + } + + /** + Returns true if the position is not at the end of the file + */ + inline bool hasMore() const { + return m_pos + m_alreadyRead < m_length; + } + + /** Prepares for bit reading via readBits. Only readBits can be + called between beginBits and endBits without corrupting the + data stream. */ + void beginBits(); + + /** Can only be called between beginBits and endBits */ + uint32 readBits(int numBits); + + /** Ends bit-reading. */ + void endBits(); + +# define DECLARE_READER(ucase, lcase)\ + void read##ucase(lcase* out, int64 n);\ + void read##ucase(std::vector<lcase>& out, int64 n);\ + void read##ucase(Array<lcase>& out, int64 n); + + DECLARE_READER(Bool8, bool) + DECLARE_READER(UInt8, uint8) + DECLARE_READER(Int8, int8) + DECLARE_READER(UInt16, uint16) + DECLARE_READER(Int16, int16) + DECLARE_READER(UInt32, uint32) + DECLARE_READER(Int32, int32) + DECLARE_READER(UInt64, uint64) + DECLARE_READER(Int64, int64) + DECLARE_READER(Float32, float32) + DECLARE_READER(Float64, float64) +# undef DECLARE_READER +}; + + +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h new file mode 100644 index 00000000000..d81ec56a67b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h @@ -0,0 +1,421 @@ +/** + @file BinaryOutput.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-08-09 + @edited 2008-01-24 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_BINARYOUTPUT_H +#define G3D_BINARYOUTPUT_H + +#include "G3D/platform.h" +#include <assert.h> +#include <string> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdio.h> +#include "G3D/Color4.h" +#include "G3D/Color3.h" +#include "G3D/Vector4.h" +#include "G3D/Vector3.h" +#include "G3D/Vector2.h" +#include "G3D/g3dmath.h" +#include "G3D/debug.h" +#include "G3D/BinaryInput.h" +#include "G3D/System.h" + +#ifdef _MSC_VER +# pragma warning (push) +// Conditional is constant (wrong in inline) +# pragma warning (disable : 4127) +#endif +namespace G3D { + +/** + Sequential or random access byte-order independent binary file access. + + The compress() call can be used to compress with zlib. + + Any method call can trigger an out of memory error (thrown as char*) + when writing to "<memory>" instead of a file. + + Compressed writing and seeking backwards is not supported for huge files + (i.e., BinaryOutput may have to dump the contents to disk if they + exceed available RAM). + */ +class BinaryOutput { +private: + std::string m_filename; + + bool m_committed; + + /** 0 outside of beginBits...endBits, 1 inside */ + int m_beginEndBits; + + /** The current string of bits being built up by beginBits...endBits. + This string is treated semantically, as if the lowest bit was + on the left and the highest was on the right.*/ + int8 m_bitString; + + /** Position (from the lowest bit) currently used in bitString.*/ + int m_bitPos; + + // True if the file endianess does not match the machine endian + bool m_swapBytes; + + G3DEndian m_fileEndian; + + uint8* m_buffer; + + /** Size of the elements used */ + int m_bufferLen; + + /** Underlying size of memory allocaded */ + int m_maxBufferLen; + + /** Next byte in file */ + int m_pos; + + /** is this initialized? */ + bool m_init; + + /** Number of bytes already written to the file.*/ + size_t m_alreadyWritten; + + bool m_ok; + + void reserveBytesWhenOutOfMemory(size_t bytes); + + void reallocBuffer(size_t bytes, size_t oldBufferLen); + + /** + Make sure at least bytes can be written, resizing if + necessary. + */ + inline void reserveBytes(int bytes) { + debugAssert(bytes > 0); + size_t oldBufferLen = (size_t)m_bufferLen; + + m_bufferLen = iMax(m_bufferLen, (m_pos + bytes)); + if (m_bufferLen > m_maxBufferLen) { + reallocBuffer(bytes, oldBufferLen); + } + } + + // Not implemented on purpose, don't use + BinaryOutput(const BinaryOutput&); + BinaryOutput& operator=(const BinaryOutput&); + bool operator==(const BinaryOutput&); + +public: + + /** + You must call setEndian() if you use this (memory) constructor. + */ + BinaryOutput(); + + /** + Doesn't actually open the file; commit() does that. + Use "<memory>" as the filename if you're going to commit + to memory. + */ + BinaryOutput( + const std::string& filename, + G3DEndian fileEndian); + + ~BinaryOutput(); + + /** Compresses the data in the buffer in place, + preceeding it with a little-endian uint32 indicating + the uncompressed size. + + Call immediately before commit(). + + Cannot be used for huge files (ones where the data + was already written to disk)-- will throw char*. + */ + void compress(); + + /** True if no errors have been encountered.*/ + bool ok() const; + + /** + Returns a pointer to the internal memory buffer. + */ + inline const uint8* getCArray() const { + return m_buffer; + } + + void setEndian(G3DEndian fileEndian); + + G3DEndian endian() const { + return m_fileEndian; + } + + std::string getFilename() const { + return m_filename; + } + + /** + Write the bytes to disk. It is ok to call this + multiple times; it will just overwrite the previous file. + + Parent directories are created as needed if they do + not exist. + + <B>Not</B> called from the destructor; you must call + it yourself. + + @param flush If true (default) the file is ready for reading when the method returns, otherwise + the method returns immediately and writes the file in the background. + */ + void commit(bool flush = true); + + /** + Write the bytes to memory (which must be of + at least size() bytes). + */ + void commit(uint8*); + + /** + A memory BinaryOutput may be reset so that it can be written to again + without allocating new memory. The underlying array will not be deallocated, + but the reset structure will act like a newly intialized one. + */ + void reset(); + + + inline int length() const { + return (int)m_bufferLen + (int)m_alreadyWritten; + } + + inline int size() const { + return length(); + } + + /** + Sets the length of the file to n, padding + with 0's past the current end. Does not + change the position of the next byte to be + written unless n < size(). + + Throws char* when resetting a huge file to be shorter + than its current length. + */ + inline void setLength(int n) { + n = n - (int)m_alreadyWritten; + + if (n < 0) { + throw "Cannot resize huge files to be shorter."; + } + + if (n < m_bufferLen) { + m_pos = n; + } + if (n > m_bufferLen) { + reserveBytes(n - m_bufferLen); + } + } + + /** + Returns the current byte position in the file, + where 0 is the beginning and getLength() - 1 is the end. + */ + inline int64 position() const { + return (int64)m_pos + (int64)m_alreadyWritten; + } + + + /** + Sets the position. Can set past length, in which case + the file is padded with zeros up to one byte before the + next to be written. + + May throw a char* exception when seeking backwards on a huge file. + */ + inline void setPosition(int64 p) { + p = p - (int64)m_alreadyWritten; + + if (p > m_bufferLen) { + setLength((int)(p + (int64)m_alreadyWritten)); + } + + if (p < 0) { + throw "Cannot seek more than 10 MB backwards on huge files."; + } + + m_pos = (int)p; + } + + + void writeBytes( + const void* b, + int count) { + + reserveBytes(count); + debugAssert(m_pos >= 0); + debugAssert(m_bufferLen >= count); + System::memcpy(m_buffer + m_pos, b, count); + m_pos += count; + } + + /** + Writes a signed 8-bit integer to the current position. + */ + inline void writeInt8(int8 i) { + reserveBytes(1); + m_buffer[m_pos] = *(uint8*)&i; + m_pos++; + } + + inline void writeBool8(bool b) { + writeInt8(b ? 1 : 0); + } + + inline void writeUInt8(uint8 i) { + reserveBytes(1); + m_buffer[m_pos] = i; + m_pos++; + } + + void writeUInt16(uint16 u); + + inline void writeInt16(int16 i) { + writeUInt16(*(uint16*)&i); + } + + void writeUInt32(uint32 u); + + inline void writeInt32(int32 i) { + debugAssert(m_beginEndBits == 0); + writeUInt32(*(uint32*)&i); + } + + void writeUInt64(uint64 u); + + inline void writeInt64(int64 i) { + writeUInt64(*(uint64*)&i); + } + + inline void writeFloat32(float32 f) { + debugAssert(m_beginEndBits == 0); + union { + float32 a; + uint32 b; + }; + a = f; + writeUInt32(b); + } + + inline void writeFloat64(float64 f) { + union { + float64 a; + uint64 b; + }; + a = f; + writeUInt64(b); + } + + /** + Write a string with NULL termination. + */ + inline void writeString(const std::string& s) { + writeString(s.c_str()); + } + + void writeString(const char* s); + + /** + Write a string, ensuring that the total length + including NULL is even. + */ + void writeStringEven(const std::string& s) { + writeStringEven(s.c_str()); + } + + void writeStringEven(const char* s); + + + void writeString32(const char* s); + + /** + Write a string with a 32-bit length field in front + of it. + */ + void writeString32(const std::string& s) { + writeString32(s.c_str()); + } + + void writeVector4(const Vector4& v); + + void writeVector3(const Vector3& v); + + void writeVector2(const Vector2& v); + + void writeColor4(const Color4& v); + + void writeColor3(const Color3& v); + + /** + Skips ahead n bytes. + */ + inline void skip(int n) { + if (m_pos + n > m_bufferLen) { + setLength((int)m_pos + (int)m_alreadyWritten + n); + } + m_pos += n; + } + + /** Call before a series of BinaryOutput::writeBits calls. Only writeBits + can be called between beginBits and endBits without corrupting the stream.*/ + void beginBits(); + + /** Write numBits from bitString to the output stream. Bits are numbered from + low to high. + + Can only be + called between beginBits and endBits. Bits written are semantically + little-endian, regardless of the actual endian-ness of the system. That is, + <CODE>writeBits(0xABCD, 16)</CODE> writes 0xCD to the first byte and + 0xAB to the second byte. However, if used with BinaryInput::readBits, the ordering + is transparent to the caller. + */ + void writeBits(uint32 bitString, int numBits); + + /** Call after a series of BinaryOutput::writeBits calls. This will + finish out with zeros the last byte into which bits were written.*/ + void endBits(); + + +# define DECLARE_WRITER(ucase, lcase)\ + void write##ucase(const lcase* out, int n);\ + void write##ucase(const std::vector<lcase>& out, int n);\ + void write##ucase(const Array<lcase>& out, int n); + + DECLARE_WRITER(Bool8, bool) + DECLARE_WRITER(UInt8, uint8) + DECLARE_WRITER(Int8, int8) + DECLARE_WRITER(UInt16, uint16) + DECLARE_WRITER(Int16, int16) + DECLARE_WRITER(UInt32, uint32) + DECLARE_WRITER(Int32, int32) + DECLARE_WRITER(UInt64, uint64) + DECLARE_WRITER(Int64, int64) + DECLARE_WRITER(Float32, float32) + DECLARE_WRITER(Float64, float64) +# undef DECLARE_WRITER + +}; + +} + +#ifdef _MSC_VER +# pragma warning (pop) +#endif + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h b/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h new file mode 100644 index 00000000000..31525dab73b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h @@ -0,0 +1,20 @@ +/** + @file BoundsTrait.h + + @maintainer Morgan McGuire, morgan@cs.williams.edu + @created 2008-10-01 + @edited 2008-10-01 + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_BOUNDSTRAIT_H +#define G3D_BOUNDSTRAIT_H + +#include "G3D/platform.h" + +template<typename Value> +struct BoundsTrait{}; + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/Box.h b/externals/g3dlite/G3D.lib/include/G3D/Box.h new file mode 100644 index 00000000000..83e06a2f069 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Box.h @@ -0,0 +1,193 @@ +/** + @file Box.h + + Box class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A> + @created 2001-06-02 + @edited 2007-06-05 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_BOX_H +#define G3D_BOX_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Array.h" +#include "G3D/Plane.h" + +namespace G3D { + +class CoordinateFrame; + +/** + An arbitrary 3D box, useful as a bounding box. + + + To construct a box from a coordinate frame, center and extent, use the idiom: + + <CODE>Box box = cframe.toObjectSpace(Box(center - extent/2, center + extent/2));</CODE> + */ +class Box { +private: + + static int32 dummy; + + friend class CoordinateFrame; + + /** + <PRE> + 3 2 7 6 + + 0 1 4 5 + + front back (seen through front) + </PRE> + */ + Vector3 _corner[8]; + + /** + Unit axes. + */ + Vector3 _axis[3]; + + Vector3 _center; + + /** + Extent along each axis. + */ + Vector3 _extent; + + float _area; + float _volume; + + void init( + const Vector3& min, + const Vector3& max); + +public: + + /** + Does not initialize the fields. + */ + Box(); + + /** + Constructs a box from two opposite corners. + */ + Box( + const Vector3& min, + const Vector3& max); + + static Box inf(); + + Box(class BinaryInput& b); + + Box(const class AABox& b); + + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** + Returns the object to world transformation for + this box. localFrame().worldToObject(...) takes + objects into the space where the box axes are + (1,0,0), (0,1,0), (0,0,1). Note that there + is no scaling in this transformation. + */ + CoordinateFrame localFrame() const; + + void getLocalFrame(CoordinateFrame& frame) const; + + /** + Returns the centroid of the box. + */ + inline Vector3 center() const { + return _center; + } + + + inline Vector3 corner(int i) const { + debugAssert(i < 8); + return _corner[i]; + } + + /** + Unit length. + */ + inline Vector3 axis(int a) const { + debugAssert(a < 3); + return _axis[a]; + } + + /** + Distance from corner(0) to the next corner + along the box's local axis a. + */ + inline float extent(int a) const { + debugAssert(a < 3); + return (float)_extent[a]; + } + + inline Vector3 extent() const { + return _extent; + } + + /** + Returns the four corners of a face (0 <= f < 6). + The corners are returned to form a counter clockwise quad facing outwards. + */ + void getFaceCorners( + int f, + Vector3& v0, + Vector3& v1, + Vector3& v2, + Vector3& v3) const; + + + /** + See AABox::culledBy + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex, + const uint32 testMask, + uint32& childMask) const; + + /** + Conservative culling test that does not produce a mask for children. + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex = dummy, + const uint32 testMask = -1) const; + + bool contains( + const Vector3& point) const; + + float area() const; + + float volume() const; + + void getRandomSurfacePoint(Vector3& P, Vector3& N = Vector3::dummy) const; + + /** + Uniformly distributed on the interior (includes surface) + */ + Vector3 randomInteriorPoint() const; + + void getBounds(class AABox&) const; + + bool isFinite() const { + return G3D::isFinite(_volume); + } +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Capsule.h b/externals/g3dlite/G3D.lib/include/G3D/Capsule.h new file mode 100644 index 00000000000..237dcdab69d --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Capsule.h @@ -0,0 +1,90 @@ +/** + @file Capsule.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-07 + @edited 2005-08-20 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_CAPSULE_H +#define G3D_CAPSULE_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" + +namespace G3D { + +class Line; +class AABox; +/** + A shape formed by extruding a sphere along a line segment. + */ +class Capsule { +private: + Vector3 p1; + Vector3 p2; + + float _radius; +public: + + + /** Uninitialized */ + Capsule(); + Capsule(class BinaryInput& b); + Capsule(const Vector3& _p1, const Vector3& _p2, float _r); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** The line down the center of the capsule */ + Line axis() const; + + inline float radius() const { + return _radius; + } + + /** Argument may be 0 or 1 */ + inline Vector3 point(int i) const { + debugAssert(i == 0 || i == 1); + return (i == 0) ? p1 : p2; + } + + /** Distance between the sphere centers. The total extent of the cylinder is + 2r + h. */ + inline float height() const { + return (p1 - p2).magnitude(); + } + + inline Vector3 center() const { + return (p1 + p2) / 2.0; + } + + /** Get a reference frame in which the center of mass is the origin and Y is the axis of the capsule.*/ + void getReferenceFrame(class CoordinateFrame& cframe) const; + + /** + Returns true if the point is inside the capsule or on its surface. + */ + bool contains(const Vector3& p) const; + + float volume() const; + + float area() const; + + /** Get axis aligned bounding box */ + void getBounds(AABox& out) const; + + /** Random world space point with outward facing normal. */ + void getRandomSurfacePoint(Vector3& P, Vector3& N) const; + + /** Point selected uniformly at random over the volume. */ + Vector3 randomInteriorPoint() const; +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h b/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h new file mode 100644 index 00000000000..62f92c18d33 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h @@ -0,0 +1,1178 @@ +/** + @file CollisionDetection.h + + + Moving collision detection for simple primitives. + + @author Morgan McGuire, matrix@graphics3d.com + @cite Spherical collision based on Paul Nettle's + ftp://ftp.3dmaileffects.com/pub/FluidStudios/CollisionDetection/Fluid_Studios_Generic_Collision_Detection_for_Games_Using_Ellipsoids.pdf + and comments by Max McGuire. Ray-sphere intersection by Eric Haines. + Box-Box intersection written by Kevin Egan. + Thanks to Max McGuire of Iron Lore for various bug fixes. + + @created 2001-11-19 + @edited 2006-01-10 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_COLLISIONDETECTION_H +#define G3D_COLLISIONDETECTION_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Plane.h" +#include "G3D/Box.h" +#include "G3D/Triangle.h" +#include "G3D/Array.h" +#include "G3D/Ray.h" +#include "G3D/Line.h" + +namespace G3D { + + +/** + Collision detection primitives and tools for building + higher order collision detection schemes. + + These routines provide <I>moving</I> and static collision detection. + Moving collision detection allows the calculation of collisions that + occur during a period of time -- as opposed to the intersection of + two static bodies. + + Moving collision detection routines detect collisions between + <I>only</I> static primitives and moving spheres or points. Since the + reference frame can be user defined, these functions can be used to + detect the collision between two moving bodies by subtracting + the velocity vector of one object from the velocity vector of the + sphere or point the detection is to occur with. This unified + velocity vector will act as if both objects are moving simultaneously. + + Collisions are detected for single-sided objects only. That is, + no collision is detected when <I>leaving</I> a primitive or passing + through a plane or triangle opposite the normal... except for the + point-sphere calculation or when otherwise noted. + + For a sphere, the collision location returned is the point in world + space where the surface of the sphere and the fixed object meet. + It is <B>not</B> the position of the center of the sphere at + the time of the collision. + + The collision normal returned is the surface normal to the fixed + object at the collision location. + + <p> + <b>Static Collision Detection:</b> (Neither object is moving) + + <table> + <tr><td></td><td><b>Vector3</b></td><td><b>LineSegment</b></td><td><b>Ray *</b></td><td><b>Line</b></td><td><b>Plane</b></td><td><b>Triangle</b></td><td><b>Sphere</b></td><td><b>Cylinder</b></td><td><b>Capsule</b></td><td><b>AABox</b></td><td><b>Box</b></td></tr> + <tr><td><b>Vector3</b></td><td>Vector3::operator== Vector3::fuzzyEq G3D::distance</td><td bgcolor=#C0C0C0 colspan=10 ></td></tr> + <tr><td><b>LineSegment</b></td><td>LineSegment::closestPoint LineSegment::distance CollisionDetection::closestPointOnLineSegment</td><td></td><td bgcolor=#C0C0C0 colspan=9 ></td></tr> + <tr><td><b>Ray *</b></td><td>Ray::closestPoint Ray::distance</td><td></td><td></td><td bgcolor=#C0C0C0 colspan=8 ></td></tr> + <tr><td><b>Line</b></td><td>Line::closestPoint Line::distance</td><td></td><td>CollisionDetection::closestPointsBetweenLineAndLine</td><td></td><td bgcolor=#C0C0C0 colspan=7 ></td></tr> + <tr><td><b>Plane</b></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=6 ></td></tr> + <tr><td><b>Triangle</b></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=5 ></td></tr> + <tr><td><b>Sphere</b></td><td>Sphere::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=4 ></td></tr> + <tr><td><b>Cylinder</b></td><td>Cylinder::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=3 ></td></tr> + <tr><td><b>Capsule</b></td><td>Capsule::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=2 ></td></tr> + <tr><td><b>AABox</b></td><td>AABox::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=1 ></td></tr> + <tr><td><b>Box</b></td><td>Box::contains</td><td>(treat as Ray)</td><td>CollisionDetection::collisionTimeForMovingPointFixedBox</td><td>(treat as Ray)</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedPlane</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedPlane</td><td>CollisionDetection::penetrationDepthForFixedSphereFixedBox</td><td>None (use OPCODE)</td><td>CollisionDetection::movingSpherePassesThroughFixedBox</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedBox</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedBox</td></tr> + </table> + + <p> + <b>Moving Collision Detection:</b> + + <i>* Note: Moving collision detection against certain primitives is equivalent to static collision + detection against a bigger primitive. Ray, Line Segment == ``moving Point''; Capsule ==``moving Sphere''; Plane == ``moving Line''</i> + */ +class CollisionDetection { +private: + + /** + Default parameter if value passed to a function as reference is + not to be calculated. Must be explicitly supported by function. + */ + static Vector3 ignore; + + /** + Default parameter if value passed to a function as reference is + not to be calculated. Must be explicitly supported by function. + */ + static bool ignoreBool; + + /** + Default parameter if value passed to a function as reference is + not to be calculated. Must be explicitly supported by function. + */ + static Array<Vector3> ignoreArray; + + + // Static class! + CollisionDetection() {} + virtual ~CollisionDetection() {} + +public: + + /** + Converts an index [0, 15] to the corresponding separating axis. + Does not return normalized vector in the edge-edge case + (indices 6 through 15). + + @param separatingAxisIndex Separating axis. + @param box1 Box 1. + @param box2 Box 2. + + @return Axis that separates the two boxes. + */ + static Vector3 separatingAxisForSolidBoxSolidBox( + const int separatingAxisIndex, + const Box & box1, + const Box & box2); + + /** + Tests whether two boxes have axes that are parallel to + each other. If they are, axis1 and axis2 are set to be + the parallel axes for both box1 and box2 respectively. + + @param ca Dot products of each of the boxes axes + @param epsilon Fudge factor (small unit by which the dot + products may vary and still be considered + zero). + @param axis1 Parallel Axis 1. [Post Condition] + @param axis2 Parallel Axis 2. [Post Condition] + + @return true - If boxes have a parallel axis + @return false - otherwise. + */ + static bool parallelAxisForSolidBoxSolidBox( + const double* ca, + const double epsilon, + int & axis1, + int & axis2); + + /** + Calculates the projected distance between the two boxes along + the specified separating axis, negative distances correspond + to an overlap along that separating axis. The distance is not + divided by denominator dot(L, L), see + penetrationDepthForFixedSphereFixedBox() for more details + + @param separatingAxisIndex + @param a Box 1's bounding sphere vector + @param b Box 2's bounding sphere vector + @param D Vector between Box 1 and Box 2's center points + @param c Pointer to array of dot products of the axes of Box 1 + and Box 2. + @param ca Pointer to array of unsigned dot products of the axes + of Box 1 and Box 2. + @param ad Pointer to array of dot products of Box 1 axes and D. + @param bd Pointer to array of dot products of Box 2 axes and D. + + @return Projected distance between the two boxes along the + specified separating axis. + */ + static float projectedDistanceForSolidBoxSolidBox( + const int separatingAxisIndex, + const Vector3 & a, + const Vector3 & b, + const Vector3 & D, + const double* c, + const double* ca, + const double* ad, + const double* bd); + + + /** + Creates a set of standard information about two boxes in order to + solve for their collision. This information includes a vector to + the radius of the bounding sphere for each box, the vector between + each boxes' center and a series of dot products between differing + important vectors. These dot products include those between the axes + of both boxes (signed and unsigned values), and the dot products + between all the axes of box1 and the boxes' center vector and box2 + and the boxes' center vector. + + @pre The following space requirements must be met: + - c[] 9 elements + - ca[] 9 elements + - ad[] 3 elements + - bd[] 3 elements + + @cite dobted from David Eberly's papers, variables used in this function + correspond to variables used in pages 6 and 7 in the pdf + http://www.magic-software.com/Intersection.html + http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf + + @note Links are out-dated. (Kept to preserve origin and authorship) + + @param box1 Box 1 + @param box2 Box 2 + @param a Box 1's bounding sphere vector + @param b Box 2's bounding sphere vector + @param D Vector between Box 1 and Box 2's center points + @param c Pointer to array of dot products of the axes of Box 1 + and Box 2. + @param ca Pointer to array of unsigned dot products of the axes + of Box 1 and Box 2. + @param ad Pointer to array of dot products of Box 1 axes and D. + @param bd Pointer to array of dot products of Box 2 axes and D. + */ + static void fillSolidBoxSolidBoxInfo( + const Box & box1, + const Box & box2, + Vector3 & a, + Vector3 & b, + Vector3 & D, + double* c, + double* ca, + double* ad, + double* bd); + + /** + Performs a simple bounding sphere check between two boxes to determine + whether these boxes could <i>possibly</i> intersect. This is a very + cheap operation (three dot products, two sqrts and a few others). If + it returns true, an intersection is possible, but not necessarily + guaranteed. + + @param a Vector from box A's center to an outer vertex + @param b Vector from box B's center to an outer vertex + @param D Distance between the centers of the two boxes + + @return true - if possible intersection + @return false - otherwise (This does not guarantee an intersection) + */ + static bool conservativeBoxBoxTest( + const Vector3 & a, + const Vector3 & b, + const Vector3 & D); + + /** + Determines whether two fixed solid boxes intersect. + + @note To speed up collision detection, the lastSeparatingAxis from + the previous time step can be passed in and that plane can be + checked first. If the separating axis was not saved, or if the + two boxes intersected then lastSeparatingAxis should equal -1. + + @cite Adobted from David Eberly's papers, variables used in this function + correspond to variables used in pages 6 and 7 in the pdf + http://www.magic-software.com/Intersection.html + http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf + + @param box1 Box 1. + @param box2 Box 2. + @param lastSeparatingAxis Last separating axis. + (optimization - see note) + + @return true - Intersection. + @return false - otherwise. + */ + static bool fixedSolidBoxIntersectsFixedSolidBox( + const Box& box1, + const Box& box2, + const int lastSeparatingAxis = -1); + + /** + Calculates the closest points on two lines with each other. If the + lines are parallel then using the starting point, else calculate the + closest point on each line to the other. + + @note This is very similiar to calculating the intersection of two lines. + Logically then, the two points calculated would be identical if calculated + with inifinite precision, but with the finite precision of floating point + calculations, these values could (will) differ as the line slope approaches + zero or inifinity. + + @cite variables and algorithm based on derivation at the following website: + http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm + + @param line1 Line 1. + @param line2 Line 2. + @param closest1 Closest point on line 1. + @param closest2 Closest point on line 2. + */ + static void closestPointsBetweenLineAndLine( + const Line & line1, + const Line & line2, + Vector3 & closest1, + Vector3 & closest2); + + /** + Calculates the depth of penetration between two fixed boxes. + Contact normal faces away from box1 and into box2. If there is + contact, only one contact point is returned. The minimally + violated separating plane is computed + - if the separating axis corresponds to a face + the contact point is half way between the deepest vertex + and the face + - if the separating axis corresponds to two edges + the contact point is the midpoint of the smallest line + segment between the two edge lines + + @note This is very similiar to calculating the intersection of two lines. + Logically then, the two points calculated would be identical if calculated + with inifinite precision, but with the finite precision of floating point + calculations, these values could (will) differ as the line slope approaches + zero or inifinity. + + @cite adobted from David Eberly's papers, variables used in this function + correspond to variables used in pages 6 and 7 in the pdf + http://www.magic-software.com/Intersection.html + http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf + + @param box1 Box 1 + @param box2 Box 2 + @param contactPoints Contact point between boxes. [Post Condition] + @param contactNormals Surface normal at contact point. [Post Condition] + @param lastSeparatingAxis Last separating axis. (Used for optimization) + + @return Depth of penetration between the two boxes. If there is no + intersection between the boxes, then a negative value is returned. + */ + static float penetrationDepthForFixedBoxFixedBox( + const Box& box1, + const Box& box2, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals, + const int lastSeparatingAxis = -1); + + /** + Calculates the depth of penetration between two fixed spheres as well + as the deepest point of Sphere A that penetrates Sphere B. The normal + returned points <B>away</B> from the object A, although it may + represent a perpendicular to either the faces of object B or object A + depending on their relative orientations. + + @param sphereA Fixed Sphere A. + @param sphereB Fixed Sphere B. + @param contactPoints Sphere A's deepest point that penetrates Sphere B. + [Post Condition] + @param contactNormals Normal at penetration point. [Post Condition] + + @return Depth of penetration. If there is no intersection between the + objects then the depth will be a negative value. + */ + static float penetrationDepthForFixedSphereFixedSphere( + const class Sphere& sphereA, + const Sphere& sphereB, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals = ignoreArray); + + /** + Calculates the depth of penetration between a fixed sphere and a fixed + box as well as the deepest point of the sphere that penetrates the box + and the normal at that intersection. + + @note There are three possible intersections between a sphere and box. + - Sphere completely contained in the box + - Sphere intersects one edge + - Sphere intersects one vertex + + The contact point and contact normal vary for each of these situations. + - Sphere contained in Box: + - Normal is based on side of least penetration (as is the depth calculation). + - Point is based on center of sphere + - Sphere intersects one edge + - Normal is based on vector from the box center to the point of depth. + - Point is closest point to the sphere on the line + - Sphere intersects one vertex + - Normal is based on vector from the box center to the vertex of penetration. + - Point is vertex of penetration. + + @cite Adapted from Jim Arvo's method in Graphics Gems + See also http://www.win.tue.nl/~gino/solid/gdc2001depth.pdf + + @param sphere Fixed Sphere. + @param box Fixed Box. + @param contactPoints Sphere point that penetrates the box. [Post Condition] + @param contactNormals Normal at the penetration point. [Post Condition] + + @return Depth of penetration. If there is no intersection between the + objects then the depth will be a negative value. + */ + static float penetrationDepthForFixedSphereFixedBox( + const Sphere& sphere, + const Box& box, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals = ignoreArray); + + /** + Calculates the depth of penetration between a Fixed Sphere and a Fixed + Plane as well as the deepest point of the sphere that penetrates the plane + and the plane normal at that intersection. + + @param sphere Fixed Sphere. + @param plane Fixed Plane. + @param contactPoints Sphere point that penetrates the plane. + [Post Condition] + @param contactNormals Normal at penetration point. [Post Condition] + + @return Depth of penetration. If there is no intersection between the + objects then the depth will be a negative value. + */ + static float penetrationDepthForFixedSphereFixedPlane( + const Sphere& sphereA, + const class Plane& planeB, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals = ignoreArray); + + /** + Calculates the depth of penetration between a fixed box and a fixed + plane as well as the vertexes of the box that penetrate the plane + and the plane normals at those intersections. + + @param box Fixed Box. + @param plane Fixed Plane. + @param contactPoints Box points that penetrate the plane. + [Post Condition] + @param contactNormals Normals at penetration points [Post Condition] + + @return Depth of penetration. If there is no intersection between the + objects then the depth will be a negative value. + */ + static float penetrationDepthForFixedBoxFixedPlane( + const Box& box, + const Plane& plane, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals = ignoreArray); + + /** + Calculates time between the intersection of a moving point and a fixed + plane. + + @note This is only a one sided collision test. The side defined by + the plane's surface normal is the only one tested. For a two sided + collision, call the function once for each side's surface normal. + + @param point Moving point. + @param velocity Point's velocity. + @param plane Fixed plane. + @param location Location of collision. [Post Condition] + (Infinite vector on no collision) + @param outNormal Plane's surface normal. [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingPointFixedPlane( + const Vector3& point, + const Vector3& velocity, + const class Plane& plane, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving point and a fixed + triangle. + + @note This is only a one sided collision test. The side defined by + the triangle's surface normal is the only one tested. For a two sided + collision, call the function once for each side's surface normal. + + @param orig Moving point. + @param dir Point's velocity. + @param v0 Triangle vertex 1. + @param v1 Triangle vertex 2. + @param v2 Triangle vertex 3 + @param location Location of collision. [Post Condition] + (Infinite vector on no collision) + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + inline static float collisionTimeForMovingPointFixedTriangle( + const Vector3& orig, + const Vector3& dir, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2) { + return Ray::fromOriginAndDirection(orig, dir).intersectionTime(v0, v1, v2); + } + + /** + Calculates time between the intersection of a moving point and a fixed + triangle. + + @note This is only a one sided collision test. The side defined by + the triangle's surface normal is the only one tested. For a two sided + collision, call the function once for each side's surface normal. + + @param orig Moving point. + @param dir Point's velocity. + @param v0 Triangle vertex 1. + @param v1 Triangle vertex 2. + @param v2 Triangle vertex 3 + @param location Location of collision. [Post Condition] + (Infinite vector on no collision) + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + inline static float collisionTimeForMovingPointFixedTriangle( + const Vector3& orig, + const Vector3& dir, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + Vector3& location) { + float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2); + if (t < inf()) { + location = orig + dir * t; + } + return t; + } + + /** + Calculates time between the intersection of a moving point and a fixed + triangle. + + @note This is only a one sided collision test. The side defined by + the triangle's surface normal is the only one tested. For a two sided + collision, call the function once for each side's surface normal. + + @param orig Moving point. + @param dir Point's velocity. + @param tri Fixed triangle. + @param location Location of collision. [Post Condition] + (Infinite vector on no collision) + @param normal Triangle's surface normal. [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + inline static float collisionTimeForMovingPointFixedTriangle( + const Vector3& orig, + const Vector3& dir, + const Triangle& tri, + Vector3& location = ignore, + Vector3& normal = ignore) { + + float t = collisionTimeForMovingPointFixedTriangle( + orig, dir, tri.vertex(0), tri.vertex(1), tri.vertex(2)); + + if ((t < inf()) && (&location != &ignore)) { + location = orig + dir * t; + normal = tri.normal(); + } + return t; + } + + /** + Calculates time between the intersection of a moving point and a fixed + triangle. + + @note This is only a one sided collision test. The side defined by + the triangle's surface normal is the only one tested. For a two sided + collision, call the function once for each side's surface normal. + + @param orig Moving point. + @param dir Point's velocity. + @param v0 Triangle vertex 1. + @param v1 Triangle vertex 2. + @param v2 Triangle vertex 3 + @param location Location of collision. [Post Condition] + (Infinite vector on no collision) + @param normal Triangle's surface normal. [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + inline static float collisionTimeForMovingPointFixedTriangle( + const Vector3& orig, + const Vector3& dir, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + Vector3& location, + Vector3& normal) { + float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2); + if (t < inf()) { + location = orig + dir * t; + normal = (v2 - v0).cross(v1 - v0).direction(); + } + return t; + } + + /** + Unlike other methods, does not support an output normal. + If the ray origin is inside the box, returns inf() but inside + is set to true. + <B>Beta API</B> + + @cite Andrew Woo, from "Graphics Gems", Academic Press, 1990 + @cite Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) + @cite Epsilon value added by Klaus Hartmann + @cite http://www.codercorner.com/RayAABB.cpp + */ + static float collisionTimeForMovingPointFixedAABox( + const Vector3& point, + const Vector3& velocity, + const class AABox& box, + Vector3& outLocation, + bool& inside = ignoreBool, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving point and a fixed + Axis-Aligned Box (AABox). + + @note Avoids the sqrt from collisionTimeForMovingPointFixedAABox. + + @param point Moving point. + @param velocity Sphere's velocity. + @param box Fixed AAbox. + @param location Location of collision. [Post Condition] + @param Inside Does the ray originate inside the box? [Post Condition] + @param normal Box's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static bool collisionLocationForMovingPointFixedAABox( + const Vector3& point, + const Vector3& velocity, + const class AABox& box, + Vector3& outLocation, + bool& inside = ignoreBool, + Vector3& normal = ignore); + + /** + Calculates time between the intersection of a moving point and a fixed + sphere. + + @note When ray is starts inside the rectangle, the exiting intersection + is detected. + + @param point Moving point. + @param velocity Point's velocity. + @param Sphere Fixed Sphere. + @param location Location of collision. [Post Condition] + @param outNormal Sphere's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingPointFixedSphere( + const Vector3& point, + const Vector3& velocity, + const class Sphere& sphere, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving point and a fixed + box. + + @note If the point is already inside the box, no collision: inf is returned. + + @param point Moving point. + @param velocity Sphere's velocity. + @param box Fixed box. + @param location Position of collision. [Post Condition] + @param outNormal Box's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingPointFixedBox( + const Vector3& point, + const Vector3& velocity, + const class Box& box, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving point and a fixed + rectangle defined by the points v0, v1, v2, & v3. + + @note This is only a one sided collision test. The side defined by + the rectangle's surface normal is the only one tested. For a two sided + collision, call the function once for each side's surface normal. + + @param point Moving point. + @param velocity Sphere's velocity. + @param v0 Rectangle vertex 1. + @param v1 Rectangle vertex 2. + @param v2 Rectangle vertex 3 + @param v3 Rectangle vertex 4. + @param location Location of collision [Post Condition] + @param outNormal Rectangle's surface normal. [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingPointFixedRectangle( + const Vector3& point, + const Vector3& velocity, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving point and a fixed + capsule. + + @param point Moving point. + @param velocity Point's velocity. + @param capsule Fixed capsule. + @param location Location of collision. [Post Condition] + @param outNormal Capsule's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingPointFixedCapsule( + const Vector3& point, + const Vector3& velocity, + const class Capsule& capsule, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving sphere and a fixed + triangle. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param plane Fixed Plane. + @param location Location of collision -- not center position of sphere + at the collision time. [Post Condition] + @param outNormal Box's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingSphereFixedPlane( + const class Sphere& sphere, + const Vector3& velocity, + const class Plane& plane, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving sphere and a fixed + triangle. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param triangle Fixed Triangle. (collisions can happen on the back side of the triangle) + @param outLocation Location of collision, if collision occurs -- not center position of sphere + at the collision time. If there is interpenetration at the start, this point may be inside + the sphere. + @param b Barycentric coordinates. These are not valid unless collision occurs. + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingSphereFixedTriangle( + const class Sphere& sphere, + const Vector3& velocity, + const Triangle& triangle, + Vector3& outLocation, + float b[3] = (float*)&ignore); + + /** + Calculates time between the intersection of a moving sphere and a fixed + rectangle defined by the points v0, v1, v2, & v3. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param v0 Rectangle vertex 1. + @param v1 Rectangle vertex 2. + @param v2 Rectangle vertex 3 + @param v3 Rectangle vertex 4. + @param location Location of collision -- not center position of sphere + at the collision time. [Post Condition] + @param outNormal Box's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingSphereFixedRectangle( + const class Sphere& sphere, + const Vector3& velocity, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving sphere and a fixed + box. + + @note This function will not detect an intersection between a moving object + that is already interpenetrating the fixed object. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param box Fixed box. + @param location Location of collision -- not center position of sphere + at the collision time. [Post Condition] + @param outNormal Box's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingSphereFixedBox( + const class Sphere& sphere, + const Vector3& velocity, + const class Box& box, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving sphere and a fixed + sphere. + + @note This won't detect a collision if the sphere is already interpenetrating + the fixed sphere. + + @param movingSphere Moving sphere. + @param velocity Sphere's velocity. + @param fixedSphere Fixed Sphere. + @param location Location of collision -- not center position of sphere + at the collision time. [Post Condition] + @param outNormal Sphere's surface normal to collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingSphereFixedSphere( + const class Sphere& sphere, + const Vector3& velocity, + const class Sphere& fixedSphere, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Calculates time between the intersection of a moving sphere and a fixed + capsule. + + @note This won't detect a collision if the sphere is already + interpenetrating the capsule. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param capsule Fixed capsule. + @param location Location of collision -- not center position of sphere + at the collision time. [Post Condition] + @param outNormal Capsule's surface normal to the collision [Post Condition] + + @return Time til collision. If there is no collision then the return + value will be inf(). + */ + static float collisionTimeForMovingSphereFixedCapsule( + const class Sphere& sphere, + const Vector3& velocity, + const class Capsule& capsule, + Vector3& outLocation, + Vector3& outNormal = ignore); + + /** + Finds the direction of bounce that a sphere would have when it + intersects an object with the given time of collision, the + collision location and the collision normal. + + @note This function works like a pong style ball bounce. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param collisionTime Time of collision. + @param collisionLocation Collision location. + @param collisionNormal Surface collision normal. + + @return Direction of bounce. + */ + static Vector3 bounceDirection( + const class Sphere& sphere, + const Vector3& velocity, + const float collisionTime, + const Vector3& collisionLocation, + const Vector3& collisionNormal); + + /** + Finds the direction of slide given a moving sphere, its velocity, the + time of collision and the collision location. This function works as + if the sphere intersects the surface and continues to hug it. + + @note The result will work well for calculating the movement of a player + who collides with an object and continues moving along the object instead + of just bouncing off it. + + @param sphere Moving sphere. + @param velocity Sphere's velocity. + @param collisionTime Time of collision + @param collisionLocation Collision location. + + @return Direction of slide. + */ + static Vector3 slideDirection( + const class Sphere& sphere, + const Vector3& velocity, + const float collisionTime, + const Vector3& collisionLocation); + + /** + Finds the closest point on a line segment to a given point. + + @param v0 line vertex 1. + @param v1 line vertex 2. + @param point External point. + + @return Closests point to <code>point</code> on the line segment. + */ + static Vector3 closestPointOnLineSegment( + const Vector3& v0, + const Vector3& v1, + const Vector3& point); + + /** + Finds the closest point on a line segment to a given point. + + @note This is an optimization to closestPointOnLineSegment. Edge length + and direction can be used in this function if already pre-calculated. This + prevents doing the same work twice. + + @param v0 line vertex 0. + @param v1 line vertex 1. + @param edgeDirection The direction of the segment (unit length). + @param edgeLength The length of the segment. + @param point External point. + + @return Closests point to <code>point</code> on the line segment. + */ + static Vector3 closestPointOnLineSegment( + const Vector3& v0, + const Vector3& v1, + const Vector3& edgeDirection, + float edgeLength, + const Vector3& point); + + /** + Finds the closest point on the perimeter of the triangle to an external point; + given a triangle defined by three points v0, v1, & v2, and the external point. + + @param v0 Triangle vertex 0. + @param v1 Triangle vertex 1. + @param v2 Triangle vertex 2. + @param point External point. + + @return Closests point to <code>point</code> on the perimeter of the + triangle. + */ + static Vector3 closestPointOnTrianglePerimeter( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& point); + + /** + Finds the closest point on the perimeter of the triangle to an external point; + given a triangle defined by the array of points v, its edge directions and + their lengths, as well as the external point. + + @note This is an optimization to closestPointToTrianglePerimeter. Edge length + and direction can be used in this function if already pre-calculated. This + prevents doing the same work twice. + + @param v0 Triangle vertex 0. + @param v1 Triangle vertex 1. + @param v2 Triangle vertex 2. + @param point External point. + @param edgeIndex The point lies on the edge between v[edgeIndex] and v[(edgeIndex + 1) % 3] + + @return Closests point to <code>point</code> on the perimeter of the + triangle. + */ + static Vector3 closestPointOnTrianglePerimeter( + const Vector3 v[3], + const Vector3 edgeDirection[3], + const float edgeLength[3], + const Vector3& point, + int& edgeIndex); + + /** + Tests whether a point is contained within the triangle defined by + v0, v1, and v2 and its plane's normal. + + @param v0 Triangle vertex 0. + @param v1 Triangle vertex 1. + @param v2 Triangle vertex 2. + @param normal Normal to triangle's plane. + @param point The point in question. + @param primaryAxis Primary axis of triangle. This will be detected + if not given. This parameter is provided as an optimization. + @param b Barycentric coordinates; b[i] is the weight on v[i] + + @return true - if point is inside the triangle. + @return false - otherwise + */ + static bool isPointInsideTriangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& normal, + const Vector3& point, + float b[3], + Vector3::Axis primaryAxis = Vector3::DETECT_AXIS); + + inline static bool isPointInsideTriangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& normal, + const Vector3& point, + Vector3::Axis primaryAxis = Vector3::DETECT_AXIS) { + + float b[3]; + return isPointInsideTriangle(v0, v1, v2, normal, point, b, primaryAxis); + } + + /** + Tests for the intersection of a moving sphere and a fixed box in a + given time limit. + + @note Returns true if any part of the sphere is inside the box + during the time period (inf means "ever"). Useful for + performing bounding-box collision detection. + + @param sphere Moving sphere. + @param velocity Velocity of moving sphere. + @param box Fixed box. + @param timeLimit Time limit for intersection test. + + @return true - if the two objects will touch. + @return false - if there is no intersection. + */ + static bool movingSpherePassesThroughFixedBox( + const Sphere& sphere, + const Vector3& velocity, + const Box& box, + double timeLimit = inf()); + + /** + Tests for the intersection of a moving sphere and a fixed sphere in a + given time limit. + + @note This function will not detect an intersection between a moving object + that is already interpenetrating the fixed object. + + @param sphere Moving sphere. + @param velocity Velocity of moving sphere. + @param fixedSphere Fixed sphere. + @param timeLimit Time limit for intersection test. + + @return true - if the two spheres will touch. + @return false - if there is no intersection. + */ + static bool movingSpherePassesThroughFixedSphere( + const Sphere& sphere, + const Vector3& velocity, + const Sphere& fixedSphere, + double timeLimit = inf()); + + /** + Tests for the intersection of two fixed spheres. + + @param sphere1 Fixed sphere 1. + @param sphere2 Fixed sphere 2. + + @return true - if the two spheres touch. + @return false - if there is no intersection. + */ + static bool fixedSolidSphereIntersectsFixedSolidSphere( + const Sphere& sphere1, + const Sphere& sphere2); + + /** + Tests for the intersection of a fixed sphere and a fixed box. + + @param sphere Fixed sphere. + @param box Fixed box. + + @return true - if the two objects touch. + @return false - if there is no intersection. + */ + static bool fixedSolidSphereIntersectsFixedSolidBox( + const Sphere& sphere, + const Box& box); + + static bool fixedSolidSphereIntersectsFixedTriangle( + const Sphere& sphere, + const Triangle& triangle); + + /** + Tests whether a point is inside a rectangle defined by the vertexes + v0, v1, v2, & v3, and the rectangle's plane normal. + + @param v0 Rectangle vertex 1. + @param v1 Rectangle vertex 2. + @param v2 Rectangle vertex 3. + @param v3 Rectangle vertex 4. + @param normal Normal to rectangle's plane. + @param point The point in question. + + @return true - if point is inside the rectangle. + @return false - otherwise + */ + static bool isPointInsideRectangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& normal, + const Vector3& point); + + /** + Finds the closest point on the perimeter of the rectangle to an + external point; given a rectangle defined by four points v0, v1, + v2, & v3, and the external point. + + @param v0 Rectangle vertex 1. + @param v1 Rectangle vertex 2. + @param v2 Rectangle vertex 3. + @param v3 Rectangle vertex 4. + @param point External point. + + @return Closests point to <code>point</code> on the perimeter of the + rectangle. + */ + static Vector3 closestPointToRectanglePerimeter( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& point); + + /** + Finds the closest point in the rectangle to an external point; Given + a rectangle defined by four points v0, v1, v2, & v3, and the external + point. + + @param v0 Rectangle vertex 1. + @param v1 Rectangle vertex 2. + @param v2 Rectangle vertex 3 + @param v3 Rectangle vertex 4. + @param point External point. + + @return Closet point in the rectangle to the external point. + */ + static Vector3 closestPointToRectangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& point); +}; + +} // namespace + +#endif // G3D_COLLISIONDETECTION_H diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color1.h b/externals/g3dlite/G3D.lib/include/G3D/Color1.h new file mode 100644 index 00000000000..1958da2b6cd --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Color1.h @@ -0,0 +1,129 @@ +/** + @file Color1.h + + Monochrome Color class + + @maintainer Morgan McGuire, morgan@cs.williams.edu + @created 2007-01-31 + @edited 2008-10-02 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_COLOR1_H +#define G3D_COLOR1_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/HashTrait.h" +#include <string> + +namespace G3D { + +/** + Monochrome color. This is just a float, but it has nice semantics + because a scaling by 255 automatically occurs when switching between + fixed point (Color1uint8) and floating point (Color1) formats. + */ +class Color1 { +private: + // Hidden operators + bool operator<(const Color1&) const; + bool operator>(const Color1&) const; + bool operator<=(const Color1&) const; + bool operator>=(const Color1&) const; + +public: + float value; + + /** + Initializes to 0 + */ + inline Color1() : value(0) {} + + Color1(class BinaryInput& bi); + + inline explicit Color1(float v) : value(v) { + } + + Color1 (const class Color1uint8& other); + + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); + + Color1 operator+ (const Color1& other) const { + return Color1(value + other.value); + } + + Color1 operator+ (const float other) const { + return Color1(value + other); + } + + Color1& operator+= (const Color1 other) { + value += other.value; + return *this; + } + + Color1& operator-= (const Color1 other) { + value -= other.value; + return *this; + } + + Color1 operator- (const Color1& other) const { + return Color1(value - other.value); + } + + Color1 operator- (const float other) const { + return Color1(value - other); + } + + Color1 operator- () const { + return Color1(-value); + } + + Color1 operator* (const Color1& other) const { + return Color1(value * other.value); + } + + Color1 operator* (const float other) const { + return Color1(value * other); + } + + Color1 operator/ (const Color1& other) const { + return Color1(value / other.value); + } + + Color1 operator/ (const float other) const { + return Color1(value / other); + } + + inline Color1 max(const Color1& other) const { + return Color1(G3D::max(value, other.value)); + } + + inline Color1 min(const Color1& other) const { + return Color1(G3D::min(value, other.value)); + } + + inline Color1 lerp(const Color1& other, float a) const { + return Color1(value + (other.value - value) * a); + + } + + inline size_t hashCode() const { + return (size_t)(value * 0xFFFFFF); + } +}; + +} + +template <> +struct HashTrait<G3D::Color1> { + static size_t hashCode(const G3D::Color1& key) { + return key.hashCode(); + } +}; + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h new file mode 100644 index 00000000000..ffaa2d43ac4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h @@ -0,0 +1,107 @@ +/** + @file Color1uint8.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2007-01-30 + @edited 2007-01-30 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_COLOR1UINT8_H +#define G3D_COLOR1UINT8_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" + +namespace G3D { + +#if defined(G3D_WIN32) + // Switch to tight alignment + #pragma pack(push, 1) +#endif + + +/** + Represents a Color1 as a packed integer. Convenient + for creating unsigned int vertex arrays. + + <B>WARNING</B>: Integer color formats are different than + integer vertex formats. The color channels are automatically + scaled by 255 (because OpenGL automatically scales integer + colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255) + but Vector3(1,1,1) == Vector3int16(1,1,1). + + <B>Note</B>: + Conversion of a float32 to uint8 is accomplished by min(iFloor(f * 256)) and + back to float32 by u / 255.0f. This gives equal size intervals. +Consider a number line from 0 to 1 and a corresponding one from 0 to 255. If we use iRound(x * 255), then the mapping for three critical intervals are: + +<pre> +let s = 0.5/255 + float int size +[0, s) -> 0 s +[s, s * 3) -> 1 2*s +(1 - s, 1] -> 255 s +</pre> + +If we use max(floor(x * 256), 255), then we get: + +<pre> +let s = 1/256 + float int size +[0, s) -> 0 s +[s, 2 * s) -> 1 s +(1 - s, 1] -> 255 s +</PRE> +and the intervals are all the same size, thus giving equal precision to all values. + */ +class Color1uint8 { +private: + // Hidden operators + bool operator<(const Color1uint8&) const; + bool operator>(const Color1uint8&) const; + bool operator<=(const Color1uint8&) const; + bool operator>=(const Color1uint8&) const; + +public: + + uint8 value; + + Color1uint8() : value(0) {} + + explicit Color1uint8(const uint8 _v) : value(_v) {} + + Color1uint8(const class Color1& c); + + Color1uint8(class BinaryInput& bi); + + void serialize(class BinaryOutput& bo) const; + + void deserialize(class BinaryInput& bi); + + inline bool operator==(const Color1uint8& other) const { + return value == other.value; + } + + inline bool operator!=(const Color1uint8& other) const { + return value != other.value; + } + +} + +#if defined(G3D_LINUX) || defined(G3D_OSX) + __attribute((aligned(1))) +#endif + +; + +#ifdef G3D_WIN32 + #pragma pack(pop) +#endif + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color3.h b/externals/g3dlite/G3D.lib/include/G3D/Color3.h new file mode 100644 index 00000000000..b8d0f94829a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Color3.h @@ -0,0 +1,390 @@ +/** + @file Color3.h + + Color class + + @maintainer Morgan McGuire, matrix@graphics3d.com + @cite Portions based on Dave Eberly's Magic Software Library + at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A> + + @created 2001-06-02 + @edited 2008-07-17 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_COLOR3_H +#define G3D_COLOR3_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/HashTrait.h" +#include "G3D/Color1.h" +#include <string> + +namespace G3D { + +/** + Do not subclass-- this implementation makes assumptions about the + memory layout. + */ +class Color3 { +private: + // Hidden operators + bool operator<(const Color3&) const; + bool operator>(const Color3&) const; + bool operator<=(const Color3&) const; + bool operator>=(const Color3&) const; + +public: + /** + Does not initialize fields. + */ + Color3(); + + explicit Color3(class BinaryInput& bi); + + Color3(float r, float g, float b); + Color3(float v) : r(v), g(v), b(v) {} + + explicit Color3(const class Vector3& v); + + explicit Color3(const float value[3]); + + /** + Initialize from another color. + */ + Color3 (const Color3& other); + + Color3 (const class Color3uint8& other); + + /** + Initialize from an HTML-style color (e.g. 0xFF0000 == RED) + */ + static Color3 fromARGB(uint32); + + /** Returns one of the color wheel colors (e.g. RED, GREEN, CYAN). + Does not include white, black, or gray. */ + static const Color3& wheelRandom(); + + /** + * Channel value. + */ + float r, g, b; + + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); + + // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b + // + // WARNING. These member functions rely on + // (1) Color3 not having virtual functions + // (2) the data packed in a 3*sizeof(float) memory block + const float& operator[] (int i) const; + float& operator[] (int i); + + // assignment and comparison + Color3& operator= (const Color3& rkVector); + bool operator== (const Color3& rkVector) const; + bool operator!= (const Color3& rkVector) const; + size_t hashCode() const; + + // arithmetic operations + Color3 operator+ (const Color3& rkVector) const; + Color3 operator- (const Color3& rkVector) const; + Color3 operator* (float fScalar) const; + Color3 operator* (const Color3& rkVector) const; + Color3 operator/ (float fScalar) const; + Color3 operator- () const; + + // arithmetic updates + Color3& operator+= (const Color3& rkVector); + Color3& operator-= (const Color3& rkVector); + Color3& operator*= (const Color3& rkVector); + Color3& operator*= (float fScalar); + Color3& operator/= (float fScalar); + + bool fuzzyEq(const Color3& other) const; + bool fuzzyNe(const Color3& other) const; + + inline operator float* () { + return (float*)this; + } + + operator const float* () const { + return (float*)this; + } + + // vector operations + float length () const; + Color3 direction() const; + float squaredLength () const; + float dot (const Color3& rkVector) const; + float unitize (float fTolerance = 1e-06); + Color3 cross (const Color3& rkVector) const; + Color3 unitCross (const Color3& rkVector) const; + + inline Color3 pow(const Color3& other) const { + return Color3(::pow(r, other.r), ::pow(g, other.g), ::pow(b, other.b)); + } + + inline Color3 pow(float other) const { + return Color3(::pow(r, other), ::pow(g, other), ::pow(b, other)); + } + + /**@return the largest component */ + inline float max() const { + return G3D::max(G3D::max(r, g), b); + } + + inline Color3 max(const Color3& other) const { + return Color3(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b)); + } + + inline Color3 min(const Color3& other) const { + return Color3(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b)); + } + + inline Color3 lerp(const Color3& other, float a) const { + return (*this) + (other - *this) * a; + + } + + inline float sum() const { + return r + g + b; + } + + inline float average() const { + return sum() / 3.0f; + } + + + /** + * Converts from HSV to RGB , note: toHSV(fromHSV(_hsv)) may not be _hsv, if it is at a grey point or black point. + * The components of _hsv should lie in the unit interval. + * @cite Alvy Ray Smith SIGGRAPH 1978 "Color Gamut Transform Pairs" + **/ + static Color3 fromHSV(const Vector3& _hsv); + static Vector3 toHSV(const Color3& _rgb); + + /** Duplicates the matlab jet colormap maps [0,1] --> (r,g,b) where blue is close to 0 and red is close to 1. */ + static Color3 jetColorMap(const float& val); + + /** Returns colors with maximum saturation and value @param hue [0, 1]*/ + static Color3 rainbowColorMap(float hue); + + std::string toString() const; + + /** Random unit vector */ + static Color3 random(); + + // Special values. + // Intentionally not inlined: see Matrix3::identity() for details. + static const Color3& red(); + static const Color3& green(); + static const Color3& blue(); + static const Color3& purple(); + static const Color3& cyan(); + static const Color3& yellow(); + static const Color3& brown(); + static const Color3& orange(); + static const Color3& black(); + static const Color3& gray(); + static const Color3& white(); + + static const Color3& zero(); + static const Color3& one(); + + inline Color3 bgr() const { + return Color3(b, g, r); + } +}; + +inline G3D::Color3 operator* (float s, const G3D::Color3& c) { + return c * s; +} + +inline G3D::Color3 operator* (G3D::Color1& s, const G3D::Color3& c) { + return c * s.value; +} + +inline G3D::Color3 operator* (const G3D::Color3& c, G3D::Color1& s) { + return c * s.value; +} + + +//---------------------------------------------------------------------------- +inline Color3::Color3 () { +} + +//---------------------------------------------------------------------------- + +inline Color3::Color3(float fX, float fY, float fZ) { + r = fX; + g = fY; + b = fZ; +} + +//---------------------------------------------------------------------------- +inline Color3::Color3(const float afCoordinate[3]) { + r = afCoordinate[0]; + g = afCoordinate[1]; + b = afCoordinate[2]; +} + +//---------------------------------------------------------------------------- +inline Color3::Color3 (const Color3& rkVector) { + r = rkVector.r; + g = rkVector.g; + b = rkVector.b; +} + +//---------------------------------------------------------------------------- +inline float& Color3::operator[] (int i) { + return ((float*)this)[i]; +} + +//---------------------------------------------------------------------------- + +inline const float& Color3::operator[] (int i) const { + return ((float*)this)[i]; +} + +//---------------------------------------------------------------------------- + +inline bool Color3::fuzzyEq(const Color3& other) const { + return G3D::fuzzyEq((*this - other).squaredLength(), 0); +} + +//---------------------------------------------------------------------------- + +inline bool Color3::fuzzyNe(const Color3& other) const { + return G3D::fuzzyNe((*this - other).squaredLength(), 0); +} + + +//---------------------------------------------------------------------------- +inline Color3& Color3::operator= (const Color3& rkVector) { + r = rkVector.r; + g = rkVector.g; + b = rkVector.b; + return *this; +} + +//---------------------------------------------------------------------------- +inline bool Color3::operator== (const Color3& rkVector) const { + return ( r == rkVector.r && g == rkVector.g && b == rkVector.b ); +} + +//---------------------------------------------------------------------------- +inline bool Color3::operator!= (const Color3& rkVector) const { + return ( r != rkVector.r || g != rkVector.g || b != rkVector.b ); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::operator+ (const Color3& rkVector) const { + return Color3(r + rkVector.r, g + rkVector.g, b + rkVector.b); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::operator- (const Color3& rkVector) const { + return Color3(r -rkVector.r, g - rkVector.g, b - rkVector.b); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::operator* (float fScalar) const { + return Color3(fScalar*r, fScalar*g, fScalar*b); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::operator* (const Color3& rkVector) const { + return Color3(r * rkVector.r, g * rkVector.g, b * rkVector.b); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::operator- () const { + return Color3( -r, -g, -b); +} + +//---------------------------------------------------------------------------- +inline Color3& Color3::operator+= (const Color3& rkVector) { + r += rkVector.r; + g += rkVector.g; + b += rkVector.b; + return *this; +} + +//---------------------------------------------------------------------------- +inline Color3& Color3::operator-= (const Color3& rkVector) { + r -= rkVector.r; + g -= rkVector.g; + b -= rkVector.b; + return *this; +} + +//---------------------------------------------------------------------------- +inline Color3& Color3::operator*= (float fScalar) { + r *= fScalar; + g *= fScalar; + b *= fScalar; + return *this; +} + +//---------------------------------------------------------------------------- +inline Color3& Color3::operator*= (const Color3& rkVector) { + r *= rkVector.r; + g *= rkVector.g; + b *= rkVector.b; + return *this; +} +//---------------------------------------------------------------------------- +inline float Color3::squaredLength () const { + return r*r + g*g + b*b; +} + +//---------------------------------------------------------------------------- +inline float Color3::length () const { + return sqrtf(r*r + g*g + b*b); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::direction () const { + float lenSquared = r * r + g * g + b * b; + + if (lenSquared != 1.0f) { + return *this / sqrtf(lenSquared); + } else { + return *this; + } +} + +//---------------------------------------------------------------------------- +inline float Color3::dot (const Color3& rkVector) const { + return r*rkVector.r + g*rkVector.g + b*rkVector.b; +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::cross (const Color3& rkVector) const { + return Color3(g*rkVector.b - b*rkVector.g, b*rkVector.r - r*rkVector.b, + r*rkVector.g - g*rkVector.r); +} + +//---------------------------------------------------------------------------- +inline Color3 Color3::unitCross (const Color3& rkVector) const { + Color3 kCross(g*rkVector.b - b*rkVector.g, b*rkVector.r - r*rkVector.b, + r*rkVector.g - g*rkVector.r); + kCross.unitize(); + return kCross; +} +} // namespace + + +template <> struct HashTrait<G3D::Color3> { + static size_t hashCode(const G3D::Color3& key) { + return key.hashCode(); + } +}; + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h new file mode 100644 index 00000000000..20e225a0734 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h @@ -0,0 +1,127 @@ +/** + @file Color3uint8.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2003-04-07 + @edited 2006-06-24 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_COLOR3UINT8_H +#define G3D_COLOR3UINT8_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" + +namespace G3D { + +/** + Represents a Color3 as a packed integer. Convenient + for creating unsigned int vertex arrays. Used by + G3D::GImage as the underlying format. + + <B>WARNING</B>: Integer color formats are different than + integer vertex formats. The color channels are automatically + scaled by 255 (because OpenGL automatically scales integer + colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255) + but Vector3(1,1,1) == Vector3int16(1,1,1). + */ + +#if defined(G3D_WIN32) + // Switch to tight alignment + #pragma pack(push, 1) +#endif + +class Color3uint8 { +private: + // Hidden operators + bool operator<(const Color3uint8&) const; + bool operator>(const Color3uint8&) const; + bool operator<=(const Color3uint8&) const; + bool operator>=(const Color3uint8&) const; + +public: + uint8 r; + uint8 g; + uint8 b; + + Color3uint8() : r(0), g(0), b(0) {} + + Color3uint8(const uint8 _r, const uint8 _g, const uint8 _b) : r(_r), g(_g), b(_b) {} + + Color3uint8(const class Color3& c); + + Color3uint8(class BinaryInput& bi); + + inline static Color3uint8 fromARGB(uint32 i) { + Color3uint8 c; + c.r = (i >> 16) & 0xFF; + c.g = (i >> 8) & 0xFF; + c.b = i & 0xFF; + return c; + } + + inline Color3uint8 bgr() const { + return Color3uint8(b, g, r); + } + + /** + Returns the color packed into a uint32 + (the upper byte is 0xFF) + */ + inline uint32 asUInt32() const { + return (0xFF << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b; + } + + void serialize(class BinaryOutput& bo) const; + + void deserialize(class BinaryInput& bi); + + // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b + // + // WARNING. These member functions rely on + // (1) Color3 not having virtual functions + // (2) the data packed in a 3*sizeof(uint8) memory block + G3D::uint8& operator[] (int i) const; + operator G3D::uint8* (); + operator const G3D::uint8* () const; + + bool operator==(const Color3uint8& other) const { + return (other.r == r) && (other.g == g) && (other.b == b); + } + + bool operator!=(const Color3uint8& other) const { + return (other.r != r) && (other.g != g) && (other.b != b); + } +} + +#if defined(G3D_LINUX) || defined(G3D_OSX) + __attribute((aligned(1))) +#endif + +; + +#ifdef G3D_WIN32 + #pragma pack(pop) +#endif + + +inline G3D::uint8& Color3uint8::operator[] (int i) const { + debugAssert((unsigned int)i < 3); + return ((G3D::uint8*)this)[i]; +} + +//---------------------------------------------------------------------------- +inline Color3uint8::operator G3D::uint8* () { + return (G3D::uint8*)this; +} + +inline Color3uint8::operator const G3D::uint8* () const { + return (G3D::uint8*)this; +} +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color4.h b/externals/g3dlite/G3D.lib/include/G3D/Color4.h new file mode 100644 index 00000000000..6b1b3b4c4bb --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Color4.h @@ -0,0 +1,324 @@ +/** + @file Color4.h + + Color class + + @maintainer Morgan McGuire, matrix@graphics3d.com + @cite Portions based on Dave Eberly's Magic Software Library + at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A> + + @created 2002-06-25 + @edited 2008-07-16 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_COLOR4_H +#define G3D_COLOR4_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Color3.h" +#include <string> + +namespace G3D { + +/** + Do not subclass-- this implementation makes assumptions about the + memory layout. + */ +class Color4 { +private: + // Hidden operators + bool operator<(const Color4&) const; + bool operator>(const Color4&) const; + bool operator<=(const Color4&) const; + bool operator>=(const Color4&) const; + +public: + + /** + * Does not initialize fields. + */ + Color4 (); + + Color4(const Color3& c3, float a = 1.0); + + Color4(const class Color4uint8& c); + + Color4(class BinaryInput& bi); + + Color4(const class Vector4& v); + + /** + * Initialize from G3D::Reals. + */ + Color4(float r, float g, float b, float a = 1.0); + + /** + * Initialize from array of G3D::Reals. + */ + Color4(float value[4]); + + /** + * Initialize from another color. + */ + Color4(const Color4& other); + + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); + + /** + Initialize from an HTML-style color (e.g. 0xFFFF0000 == RED) + */ + static Color4 fromARGB(uint32); + + /** + * Channel values. + */ + float r, g, b, a; + + inline Color3 rgb() const { + return Color3(r, g, b); + } + + // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b, v[3] = V.a + // + // WARNING. These member functions rely on + // (1) Color4 not having virtual functions + // (2) the data packed in a 3*sizeof(float) memory block + float& operator[] (int i) const; + operator float* (); + operator const float* () const; + + // assignment and comparison + Color4& operator= (const Color4& rkVector); + bool operator== (const Color4& rkVector) const; + bool operator!= (const Color4& rkVector) const; + size_t hashCode() const; + + // arithmetic operations + Color4 operator+ (const Color4& rkVector) const; + Color4 operator- (const Color4& rkVector) const; + Color4 operator* (float fScalar) const; + Color4 operator/ (float fScalar) const; + Color4 operator- () const; + friend Color4 operator* (double fScalar, const Color4& rkVector); + + // arithmetic updates + Color4& operator+= (const Color4& rkVector); + Color4& operator-= (const Color4& rkVector); + Color4& operator*= (float fScalar); + Color4& operator/= (float fScalar); + + bool fuzzyEq(const Color4& other) const; + bool fuzzyNe(const Color4& other) const; + + std::string toString() const; + + inline Color4 max(const Color4& other) const { + return Color4(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b), G3D::max(a, other.a)); + } + + inline Color4 min(const Color4& other) const { + return Color4(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b), G3D::min(a, other.a)); + } + + /** r + g + b + a */ + inline float sum() const { + return r + g + b + a; + } + + inline Color4 lerp(const Color4& other, float a) const { + return (*this) + (other - *this) * a; + + } + + // Special values. + // Intentionally not inlined: see Matrix3::identity() for details. + static const Color4& zero(); + static const Color4& clear(); + + static const Color4& inf(); + + inline Color3 bgr() const { + return Color3(b, g, r); + } +}; + +/** + Extends the c3 with alpha = 1.0 + */ +Color4 operator*(const Color3& c3, const Color4& c4); + + +inline Color4 operator*(const Color3& c3, const Color4& c4) { + return Color4(c3.r * c4.r, c3.g * c4.g, c3.b * c4.b, c4.a); +} + +//---------------------------------------------------------------------------- + +inline Color4::Color4 () { + // For efficiency in construction of large arrays of vectors, the + // default constructor does not initialize the vector. +} + +//---------------------------------------------------------------------------- + +inline Color4::Color4(const Color3& c3, float a) { + r = c3.r; + g = c3.g; + b = c3.b; + this->a = a; +} + +//---------------------------------------------------------------------------- + +inline Color4::Color4( + float r, + float g, + float b, + float a) : + r(r), g(g), b(b), a(a) { +} + +//---------------------------------------------------------------------------- +inline Color4::Color4 (float afCoordinate[4]) { + r = afCoordinate[0]; + g = afCoordinate[1]; + b = afCoordinate[2]; + a = afCoordinate[3]; +} + +//---------------------------------------------------------------------------- + +inline Color4::Color4( + const Color4& other) { + + r = other.r; + g = other.g; + b = other.b; + a = other.a; +} + +//---------------------------------------------------------------------------- + +inline float& Color4::operator[] (int i) const { + return ((float*)this)[i]; +} + +//---------------------------------------------------------------------------- +inline Color4::operator float* () { + return (float*)this; +} + +inline Color4::operator const float* () const { + return (float*)this; +} + +//---------------------------------------------------------------------------- + +inline bool Color4::fuzzyEq(const Color4& other) const { + Color4 dif = (*this - other); + return G3D::fuzzyEq(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0); +} + +//---------------------------------------------------------------------------- + +inline bool Color4::fuzzyNe(const Color4& other) const { + Color4 dif = (*this - other); + return G3D::fuzzyNe(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0); +} + + +//---------------------------------------------------------------------------- +inline Color4& Color4::operator= (const Color4& other) { + r = other.r; + g = other.g; + b = other.b; + a = other.a; + return *this; +} + +//---------------------------------------------------------------------------- + +inline bool Color4::operator== (const Color4& other) const { + return ( r == other.r && g == other.g && b == other.b && a == other.a); +} + +//---------------------------------------------------------------------------- + +inline bool Color4::operator!= (const Color4& other) const { + return ( r != other.r || g != other.g || b != other.b || a != other.a); +} + +//---------------------------------------------------------------------------- +inline Color4 Color4::operator+ (const Color4& other) const { + return Color4(r + other.r, g + other.g, b + other.b, a + other.a); +} + +//---------------------------------------------------------------------------- +inline Color4 Color4::operator- (const Color4& other) const { + return Color4(r - other.r, g - other.g, b - other.b, a - other.a); +} + +//---------------------------------------------------------------------------- + +inline Color4 Color4::operator* (float fScalar) const { + return Color4(fScalar * r, fScalar * g, fScalar * b, fScalar * a); +} + +//---------------------------------------------------------------------------- + +inline Color4 Color4::operator- () const { + return Color4(-r, -g, -b, -a); +} + +//---------------------------------------------------------------------------- + +inline Color4 operator* (float fScalar, const Color4& other) { + return Color4(fScalar * other.r, fScalar * other.g, + fScalar * other.b, fScalar * other.a); +} + +//---------------------------------------------------------------------------- + +inline Color4& Color4::operator+= (const Color4& other) { + r += other.r; + g += other.g; + b += other.b; + a += other.a; + return *this; +} + +//---------------------------------------------------------------------------- + +inline Color4& Color4::operator-= (const Color4& other) { + r -= other.r; + g -= other.g; + b -= other.b; + a -= other.a; + return *this; +} + +//---------------------------------------------------------------------------- + +inline Color4& Color4::operator*= (float fScalar) { + r *= fScalar; + g *= fScalar; + b *= fScalar; + a *= fScalar; + return *this; +} + +} // namespace + +template <> +struct HashTrait<G3D::Color4> { + static size_t hashCode(const G3D::Color4& key) { + return key.hashCode(); + } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h new file mode 100644 index 00000000000..7804a6e1e51 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h @@ -0,0 +1,133 @@ +/** + @file Color4uint8.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2003-04-07 + @edited 2006-03-24 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef COLOR4UINT8_H +#define COLOR4UINT8_H + +#include "G3D/g3dmath.h" +#include "G3D/platform.h" +#include "G3D/Color3uint8.h" + +namespace G3D { + +/** + Represents a Color4 as a packed integer. Convenient + for creating unsigned int vertex arrays. Used by + G3D::GImage as the underlying format. + + <B>WARNING</B>: Integer color formats are different than + integer vertex formats. The color channels are automatically + scaled by 255 (because OpenGL automatically scales integer + colors back by this factor). So Color4(1,1,1) == Color4uint8(255,255,255) + but Vector3(1,1,1) == Vector3int16(1,1,1). + + */ + +#ifdef G3D_WIN32 + // Switch to tight alignment + #pragma pack(push, 1) +#endif + +class Color4uint8 { +private: + // Hidden operators + bool operator<(const Color4uint8&) const; + bool operator>(const Color4uint8&) const; + bool operator<=(const Color4uint8&) const; + bool operator>=(const Color4uint8&) const; + +public: + uint8 r; + uint8 g; + uint8 b; + uint8 a; + + Color4uint8() : r(0), g(0), b(0), a(0) {} + + Color4uint8(const class Color4& c); + + Color4uint8(const uint8 _r, const uint8 _g, const uint8 _b, const uint8 _a) : r(_r), g(_g), b(_b), a(_a) {} + + Color4uint8(const Color3uint8& c, const uint8 _a) : r(c.r), g(c.g), b(c.b), a(_a) {} + + Color4uint8(class BinaryInput& bi); + + inline static Color4uint8 fromARGB(uint32 i) { + Color4uint8 c; + c.a = (i >> 24) & 0xFF; + c.r = (i >> 16) & 0xFF; + c.g = (i >> 8) & 0xFF; + c.b = i & 0xFF; + return c; + } + + inline uint32 asUInt32() const { + return ((uint32)a << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b; + } + + // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b + // + // WARNING. These member functions rely on + // (1) Color4uint8 not having virtual functions + // (2) the data packed in a 3*sizeof(uint8) memory block + G3D::uint8& operator[] (int i) const; + operator G3D::uint8* (); + operator const G3D::uint8* () const; + + + inline Color3uint8 bgr() const { + return Color3uint8(b, g, r); + } + + void serialize(class BinaryOutput& bo) const; + + void deserialize(class BinaryInput& bi); + + inline Color3uint8 rgb() const { + return Color3uint8(r, g, b); + } + + bool operator==(const Color4uint8& other) const { + return *reinterpret_cast<const uint32*>(this) == *reinterpret_cast<const uint32*>(&other); + } + + bool operator!=(const Color4uint8& other) const { + return *reinterpret_cast<const uint32*>(this) != *reinterpret_cast<const uint32*>(&other); + } + +} +#if defined(G3D_LINUX) || defined(G3D_OSX) + __attribute((aligned(1))) +#endif +; + +#ifdef G3D_WIN32 + #pragma pack(pop) +#endif + + +inline G3D::uint8& Color4uint8::operator[] (int i) const { + return ((G3D::uint8*)this)[i]; +} + +//---------------------------------------------------------------------------- +inline Color4uint8::operator G3D::uint8* () { + return (G3D::uint8*)this; +} + +inline Color4uint8::operator const G3D::uint8* () const { + return (G3D::uint8*)this; +} + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Cone.h b/externals/g3dlite/G3D.lib/include/G3D/Cone.h new file mode 100644 index 00000000000..c09b9b6846c --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Cone.h @@ -0,0 +1,68 @@ +/** + @file Cone.h + + Cone class + + @maintainer Morgan McGuire, matrix@graphics3d.com + @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A> + + @created 2001-06-02 + @edited 2006-02-23 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_CONE_H +#define G3D_CONE_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" + +namespace G3D { + +/** + An infinite cone. + */ +class Cone { + +private: + Vector3 tip; + Vector3 direction; + + /** Angle from the center line to the edge. */ + float angle; + +public: + + /** + @param angle Angle from the center line to the edge, in radians + */ + Cone(const Vector3& tip, const Vector3& direction, float angle); + + /** + Forms the smallest cone that contains the box. Undefined if + the tip is inside or on the box. + */ + Cone(const Vector3& tip, const class Box& box); + + virtual ~Cone() {} + + /** + Returns true if the cone touches, intersects, or contains b. + + If c.intersects(s) and c.intersects(Sphere(s.center, s.radius * 2) + then the sphere s is entirely within cone c. + */ + bool intersects(const class Sphere& s) const; + + /** + True if v is a point inside the cone. + */ + bool contains(const class Vector3& v) const; +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h b/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h new file mode 100644 index 00000000000..6ae9ba136ff --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h @@ -0,0 +1,179 @@ +/** + @file ConvexPolyhedron.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-11-11 + @edited 2006-04-10 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_CONVEXPOLYHEDRON_H +#define G3D_CONVEXPOLYHEDRON_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Vector2.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Plane.h" +#include "G3D/Line.h" +#include "G3D/Array.h" + +namespace G3D { + +class DirectedEdge { +public: + Vector3 start; + Vector3 stop; +}; + +class ConvexPolygon { +private: + + friend class ConvexPolyhedron; + + Array<Vector3> _vertex; + +public: + + ConvexPolygon() {} + ConvexPolygon(const Array<Vector3>& __vertex); + virtual ~ConvexPolygon() {} + + /** + Counter clockwise winding order. + */ + inline const Vector3& vertex(int i) const { + return _vertex[i]; + } + + inline void setVertex(int i, const Vector3& v) { + _vertex[i] = v; + } + + /** + Zero vertices indicates an empty polygon (zero area). + */ + inline int numVertices() const { + return _vertex.size(); + } + + inline void setNumVertices(int n) { + _vertex.resize(n); + } + + /** + O(n) in the number of edges + */ + bool isEmpty() const; + + /** + Cuts the polygon at the plane. If the polygon is entirely above or below + the plane, one of the returned polygons will be empty. + + @param above The part of the polygon above (on the side the + normal points to or in the plane) the plane + @param below The part of the polygon below the plane. + @param newEdge If a new edge was introduced, this is that edge (on the above portion; the below portion is the opposite winding. + */ + void cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge& newEdge); + void cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below); + + /** + When a cut plane grazes a vertex in the polygon, two near-identical vertices may be created. + The closeness of these two points can cause a number of problems, such as ConvexPolygon::normal() + returning an infinite vector. It should be noted, however, that not all applications are + sensitive to near-identical vertices. + + removeDuplicateVertices() detects and eliminates redundant vertices. + */ + void removeDuplicateVertices(); + + /** + O(n) in the number of edges + */ + float getArea() const; + + inline Vector3 normal() const { + debugAssert(_vertex.length() >= 3); + return (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).direction(); + } + + /** + Returns the same polygon with inverse winding. + */ + ConvexPolygon inverse() const; +}; + + + +class ConvexPolyhedron { +public: + /** + Zero faces indicates an empty polyhedron + */ + Array<ConvexPolygon> face; + + ConvexPolyhedron() {} + ConvexPolyhedron(const Array<ConvexPolygon>& _face); + + /** + O(n) in the number of edges + */ + bool isEmpty() const; + + /** + O(n) in the number of edges + */ + float getVolume() const; + + /** + Cuts the polyhedron at the plane. If the polyhedron is entirely above or below + the plane, one of the returned polyhedra will be empty. + + @param above The part of the polyhedron above (on the side the + normal points to or in the plane) the plane + @param below The part of the polyhedron below the plane. + */ + void cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below); +}; + +/** + + */ +class ConvexPolygon2D { +private: + + Array<Vector2> m_vertex; + +public: + + ConvexPolygon2D() {} + + /** + Points are counter-clockwise in a Y = down, X = right coordinate + system. + + @param reverse If true, the points are reversed (i.e. winding direction is changed) + before the polygon is created. + */ + ConvexPolygon2D(const Array<Vector2>& pts, bool reverse = false); + + inline int numVertices() const { + return m_vertex.size(); + } + + inline const Vector2& vertex(int index) const { + debugAssert((index >= 0) && (index <= m_vertex.size())); + return m_vertex[index]; + } + + /** @param reverseWinding If true, the winding direction of the polygon is reversed for this test.*/ + bool contains(const Vector2& p, bool reverseWinding = false) const; +}; + + +} // namespace +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h b/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h new file mode 100644 index 00000000000..705c6c93ae2 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h @@ -0,0 +1,315 @@ +/** + @file CoordinateFrame.h + + @maintainer Morgan McGuire, morgan@cs.williams.edu + + @created 2001-03-04 + @edited 2008-07-14 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. +*/ + +#ifndef G3D_COORDINATEFRAME_H +#define G3D_COORDINATEFRAME_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/Matrix3.h" +#include "G3D/Array.h" +#include <math.h> +#include <string> +#include <stdio.h> +#include <cstdarg> +#include <assert.h> + +namespace G3D { + +/** + A rigid body RT (rotation-translation) transformation. + +CoordinateFrame abstracts a 4x4 matrix that maps object space to world space: + + v_world = C * v_object + +CoordinateFrame::rotation is the upper 3x3 submatrix, CoordinateFrame::translation +is the right 3x1 column. The 4th row is always [0 0 0 1], so it isn't stored. +So you don't have to remember which way the multiplication and transformation work, +it provides explicit toWorldSpace and toObjectSpace methods. Also, points, vectors +(directions), and surface normals transform differently, so they have separate methods. + +Some helper functions transform whole primitives like boxes in and out of object space. + +Convert to Matrix4 using CoordinateFrame::toMatrix4. You <I>can</I> construct a CoordinateFrame +from a Matrix4 using Matrix4::approxCoordinateFrame, however, because a Matrix4 is more +general than a CoordinateFrame, some information may be lost. + +@sa G3D::UprightFrame, G3D::PhysicsFrame, G3D::Matrix4, G3D::Quat +*/ +class CoordinateFrame { +public: + + /** Takes object space points to world space. */ + Matrix3 rotation; + + /** Takes object space points to world space. */ + Vector3 translation; + + inline bool operator==(const CoordinateFrame& other) const { + return (translation == other.translation) && (rotation == other.rotation); + } + + inline bool operator!=(const CoordinateFrame& other) const { + return !(*this == other); + } + + bool fuzzyEq(const CoordinateFrame& other) const; + + bool fuzzyIsIdentity() const; + + bool isIdentity() const; + + /** + Initializes to the identity coordinate frame. + */ + inline CoordinateFrame() : + rotation(Matrix3::identity()), translation(Vector3::zero()) { + } + + CoordinateFrame(const Vector3& _translation) : + rotation(Matrix3::identity()), translation(_translation) { + } + + CoordinateFrame(const Matrix3 &rotation, const Vector3 &translation) : + rotation(rotation), translation(translation) { + } + + CoordinateFrame(const Matrix3 &rotation) : + rotation(rotation), translation(Vector3::zero()) { + } + + CoordinateFrame(const class UprightFrame& f); + + static CoordinateFrame fromXYZYPRRadians(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f); + + /** Construct a coordinate frame from translation = (x,y,z) and + rotations (in that order) about Y, object space X, object space + Z. Note that because object-space axes are used, these are not + equivalent to Euler angles; they are known as Tait-Bryan + rotations and are more convenient for intuitive positioning.*/ + static CoordinateFrame fromXYZYPRDegrees(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f); + + CoordinateFrame(class BinaryInput& b); + + void deserialize(class BinaryInput& b); + + void serialize(class BinaryOutput& b) const; + + CoordinateFrame(const CoordinateFrame &other) : + rotation(other.rotation), translation(other.translation) {} + + /** + Computes the inverse of this coordinate frame. + */ + inline CoordinateFrame inverse() const { + CoordinateFrame out; + out.rotation = rotation.transpose(); + out.translation = -out.rotation * translation; + return out; + } + + inline ~CoordinateFrame() {} + + /** See also Matrix4::approxCoordinateFrame */ + class Matrix4 toMatrix4() const; + + void getXYZYPRRadians(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const; + void getXYZYPRDegrees(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const; + + + /** + Produces an XML serialization of this coordinate frame. + @deprecated + */ + std::string toXML() const; + + /** + Returns the heading of the lookVector as an angle in radians relative to + the world -z axis. That is, a counter-clockwise heading where north (-z) + is 0 and west (-x) is PI/2. + + Note that the heading ignores the Y axis, so an inverted + object has an inverted heading. + */ + inline float getHeading() const { + Vector3 look = rotation.getColumn(2); + float angle = -(float) atan2(-look.x, look.z); + return angle; + } + + /** + Takes the coordinate frame into object space. + this->inverse() * c + */ + inline CoordinateFrame toObjectSpace(const CoordinateFrame& c) const { + return this->inverse() * c; + } + + inline Vector4 toObjectSpace(const Vector4& v) const { + return this->inverse().toWorldSpace(v); + } + + inline Vector4 toWorldSpace(const Vector4& v) const { + return Vector4(rotation * Vector3(v.x, v.y, v.z) + translation * v.w, v.w); + } + + /** + Transforms the point into world space. + */ + inline Vector3 pointToWorldSpace(const Vector3& v) const { + return Vector3( + rotation[0][0] * v[0] + rotation[0][1] * v[1] + rotation[0][2] * v[2] + translation[0], + rotation[1][0] * v[0] + rotation[1][1] * v[1] + rotation[1][2] * v[2] + translation[1], + rotation[2][0] * v[0] + rotation[2][1] * v[1] + rotation[2][2] * v[2] + translation[2]); + } + + /** + Transforms the point into object space. Assumes that the rotation matrix is orthonormal. + */ + inline Vector3 pointToObjectSpace(const Vector3& v) const { + float p[3]; + p[0] = v[0] - translation[0]; + p[1] = v[1] - translation[1]; + p[2] = v[2] - translation[2]; + debugAssert(G3D::fuzzyEq(rotation.determinant(), 1.0f)); + return Vector3( + rotation[0][0] * p[0] + rotation[1][0] * p[1] + rotation[2][0] * p[2], + rotation[0][1] * p[0] + rotation[1][1] * p[1] + rotation[2][1] * p[2], + rotation[0][2] * p[0] + rotation[1][2] * p[1] + rotation[2][2] * p[2]); + } + + /** + Transforms the vector into world space (no translation). + */ + inline Vector3 vectorToWorldSpace(const Vector3& v) const { + return rotation * v; + } + + inline Vector3 normalToWorldSpace(const Vector3& v) const { + return rotation * v; + } + + class Ray toObjectSpace(const Ray& r) const; + + Ray toWorldSpace(const Ray& r) const; + + /** + Transforms the vector into object space (no translation). + */ + inline Vector3 vectorToObjectSpace(const Vector3 &v) const { + // Multiply on the left (same as rotation.transpose() * v) + return v * rotation; + } + + inline Vector3 normalToObjectSpace(const Vector3 &v) const { + // Multiply on the left (same as rotation.transpose() * v) + return v * rotation; + } + + void pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const; + + void normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const; + + void vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const; + + void pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const; + + void normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const; + + void vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const; + + class Box toWorldSpace(const class AABox& b) const; + + class Box toWorldSpace(const class Box& b) const; + + class Cylinder toWorldSpace(const class Cylinder& b) const; + + class Capsule toWorldSpace(const class Capsule& b) const; + + class Plane toWorldSpace(const class Plane& p) const; + + class Sphere toWorldSpace(const class Sphere& b) const; + + class Triangle toWorldSpace(const class Triangle& t) const; + + class Box toObjectSpace(const AABox& b) const; + + class Box toObjectSpace(const Box& b) const; + + class Plane toObjectSpace(const Plane& p) const; + + class Sphere toObjectSpace(const Sphere& b) const; + + Triangle toObjectSpace(const Triangle& t) const; + + /** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/ + CoordinateFrame operator*(const CoordinateFrame &other) const { + return CoordinateFrame(rotation * other.rotation, + pointToWorldSpace(other.translation)); + } + + CoordinateFrame operator+(const Vector3& v) const { + return CoordinateFrame(rotation, translation + v); + } + + CoordinateFrame operator-(const Vector3& v) const { + return CoordinateFrame(rotation, translation - v); + } + + void lookAt(const Vector3& target); + + void lookAt( + const Vector3& target, + Vector3 up); + + /** The direction this camera is looking (its negative z axis)*/ + inline Vector3 lookVector() const { + return -rotation.getColumn(2); + } + + /** Returns the ray starting at the camera origin travelling in direction CoordinateFrame::lookVector. */ + class Ray lookRay() const; + + /** Up direction for this camera (its y axis). */ + inline Vector3 upVector() const { + return rotation.getColumn(1); + } + + inline Vector3 rightVector() const { + return rotation.getColumn(0); + } + + /** + If a viewer looks along the look vector, this is the viewer's "left". + Useful for strafing motions and building alternative coordinate frames. + */ + inline Vector3 leftVector() const { + return -rotation.getColumn(0); + } + + /** + Linearly interpolates between two coordinate frames, using + Quat::slerp for the rotations. + */ + CoordinateFrame lerp( + const CoordinateFrame& other, + float alpha) const; + +}; + +typedef CoordinateFrame CFrame; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Crypto.h b/externals/g3dlite/G3D.lib/include/G3D/Crypto.h new file mode 100644 index 00000000000..f8266b8721b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Crypto.h @@ -0,0 +1,96 @@ +/** + @file Crypto.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + + @created 2006-03-29 + @edited 2006-04-06 + */ + +#ifndef G3D_CRYPTO_H +#define G3D_CRYPTO_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include <string> + +namespace G3D { + +/** See G3D::Crypto::md5 */ +class MD5Hash { +private: + + uint8 value[16]; + +public: + + MD5Hash() { + for (int i = 0; i < 16; ++i) { + value[i] = 0; + } + } + + explicit MD5Hash(class BinaryInput& b); + + uint8& operator[](int i) { + return value[i]; + } + + const uint8& operator[](int i) const { + return value[i]; + } + + bool operator==(const MD5Hash& other) const { + bool match = true; + for (int i = 0; i < 16; ++i) { + match = match && (other.value[i] == value[i]); + } + return match; + } + + inline bool operator!=(const MD5Hash& other) const { + return !(*this == other); + } + + void deserialize(class BinaryInput& b); + + void serialize(class BinaryOutput& b) const; +}; + + +/** Cryptography and hashing helper functions */ +class Crypto { +public: + + /** + Computes the CRC32 value of a byte array. CRC32 is designed to be a hash + function that produces different values for similar strings. + + This implementation is compatible with PKZIP and GZIP. + + Based on http://www.gamedev.net/reference/programming/features/crc32/ + */ + static uint32 crc32(const void* bytes, size_t numBytes); + + /** + Computes the MD5 hash (message digest) of a byte stream, as defined by + http://www.ietf.org/rfc/rfc1321.txt. + + @cite Based on implementation by L. Peter Deutsch, ghost@aladdin.com + */ + MD5Hash md5(const void* bytes, size_t numBytes); + + /** + Returns the nth prime less than 2000 in constant time. The first prime has index + 0 and is the number 2. + */ + static int smallPrime(int n); + + /** Returns 1 + the largest value that can be passed to smallPrime. */ + static int numSmallPrimes(); +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h b/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h new file mode 100644 index 00000000000..c341d29a2b9 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h @@ -0,0 +1,92 @@ +/** + @file Cylinder.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-07 + @edited 2005-09-26 + + Copyright 2000-2005, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_Cylinder_H +#define G3D_Cylinder_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" + +namespace G3D { + +class Line; +class AABox; +/** + Right cylinder + */ +class Cylinder { +private: + Vector3 p1; + Vector3 p2; + + float mRadius; + +public: + + /** Uninitialized */ + Cylinder(); + Cylinder(class BinaryInput& b); + Cylinder(const Vector3& _p1, const Vector3& _p2, float _r); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** The line down the center of the Cylinder */ + Line axis() const; + + /** + A reference frame in which the center of mass is at the origin and + the Y-axis is the cylinder's axis. If the cylinder is transformed, this reference frame + may freely rotate around its axis.*/ + void getReferenceFrame(class CoordinateFrame& cframe) const; + + /** Returns point 0 or 1 */ + inline const Vector3& point(int i) const { + debugAssert(i >= 0 && i <= 1); + return (i == 0) ? p1 : p2; + } + + /** + Returns true if the point is inside the Cylinder or on its surface. + */ + bool contains(const Vector3& p) const; + + float area() const; + + float volume() const; + + float radius() const; + + /** Center of mass */ + inline Vector3 center() const { + return (p1 + p2) / 2.0f; + } + + inline float height() const { + return (p1 - p2).magnitude(); + } + + /** + Get close axis aligned bounding box. + With vertical world orientation, the top and bottom might not be very tight. */ + void getBounds(AABox& out) const; + + /** Random world space point with outward facing normal. */ + void getRandomSurfacePoint(Vector3& P, Vector3& N) const; + + /** Point selected uniformly at random over the volume. */ + Vector3 randomInteriorPoint() const; +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Discovery.h b/externals/g3dlite/G3D.lib/include/G3D/Discovery.h new file mode 100644 index 00000000000..acd1c2c01d0 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Discovery.h @@ -0,0 +1,589 @@ +/** + @file Discovery.h + + <H2>Discovery</H2> + Discovery is the process by which computers on a Local Area Network (LAN) find + one another. The Discovery API allows clients to make a list of servers running + the same application. The application typically presents this list to the user + so he can choose which server to connect to. + <P> + Features of the Discovery API: + <P> + Low network traffic + <BR> - Broadcasts mainly occur only when a new machine enters/leaves the network + <BR>Responsive + <BR> - Servers appear immediately after launching + <BR> - Servers disappear immedatiately after they shut down + <BR>Extensible + <BR> - Add your own game information (e.g. score, num players, map name) + <BR>Versioned + <BR> - Tracks incompatible servers so end-users know to upgrade their client/server + + <H2>Using the Discovery API</H2> + + Subclass DiscoveryAdvertisement to add fields describing a server running your + application. For a game, these might be the current map name and the number + of players. + <P> + On the client, create an instance of DiscoveryClient<your advertisement subclass>. + On the server, create an instance of DiscoveryServer and initialize it with an + instance of your advertisement subclass. From your main loop, call doNetwork() + on the client and server instances. When your server shuts down, invoke cleanup() + on it. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-06-26 + @edited 2008-11-24 + */ + +#ifndef G3D_DISCOVERY_H +#define G3D_DISCOVERY_H + +#include "G3D/platform.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/NetworkDevice.h" +#include "G3D/Log.h" +#include <time.h> + +/** + Different versions of G3D discovery protocols can't communicate with each other. + */ +#define G3D_DISCOVERY_PROTOCOL_NAME "G3D DISC" +#define G3D_DISCOVERY_PROTOCOL_VERSION 1 + +namespace G3D { + +/** + If a machine is running two different programs using discovery they + must have different ports. However different programs can share the + same ports if they run on the same LAN with different servers. + + @deprecated See Discovery2::Settings + */ +class DiscoverySettings { +public: + + /** + Name of the program using discovery; used so that mutliple + programs can use the same discovery ports on the same network. + */ + const char* appProtocolName; + + /** + Version of the network protocol of the program using discovery. + Used so that discovery can identify incompatible versions of + the server. + */ + int appProtocolVersion; + + /** + Port on which the server broadcasts its identity. The client + and server must agree on this value. + */ + uint16 serverBroadcastPort; + + /** + Port on which the client broadcasts a server request. The client + and server must agree on this value. + */ + uint16 clientBroadcastPort; + + /** + Clients connect into this port using a reliable conduit + to receive the advertisement from a server. The client + doesn't look at this value; it uses whatever the server + sends it. + */ + uint16 serverAdvertisementPort; + + /** + You can use the default G3D discovery ports as long as no other + program with the same protocol name is using this port. You + <B>can</B> run two different G3D discovery programs on the same + two ports as long as they have different application protocol + strings. + */ + DiscoverySettings( + const char* _appProtocolName, + int _appProtocolVersion, + uint16 _serverBroadcast = 6173, + uint16 _clientBroadcast = 6174, + uint16 _serverAdvertisementPort = 6175) : + appProtocolName(_appProtocolName), + appProtocolVersion(_appProtocolVersion), + serverBroadcastPort(_serverBroadcast), + clientBroadcastPort(_clientBroadcast), + serverAdvertisementPort(_serverAdvertisementPort) {} +}; + +/** + Make your own subclass of this advertisement. Add fields + (e.g. numPlayers, currentScore) to increase the amount + of information advertised. + + Overrides must provide a default constructor. + @deprecated See Discovery2::Settings + */ +class DiscoveryAdvertisement { +public: + + /** + Address to connect to on the server for the actual game. + The IP portion is ignored (the client figures out the IP + address from the packet itself) but the port is essential. + Note that this port must not be the discovery port. + */ + NetAddress address; + + /** + (Only used on the client) + Time since this advertisement was updated. + */ + RealTime lastUpdateTime; + + /** + Overrides must call DiscoveryAdvertisement::serialize(b) first. + */ + virtual void serialize(BinaryOutput& b) const; + + /** + Overrides must call DiscoveryAdvertisement::deserialize(b) first. + */ + virtual void deserialize(BinaryInput& b); + + /** + An empty virtual destructor for virtual methods. + */ + virtual ~DiscoveryAdvertisement() {} +}; + + +/** + Sent by servers to describe their location. + */ +class DiscoveryServerAddressMessage { +public: + + /** + Not part of the message; these settings are used to determine + if the correct protocol is being used. + */ + const DiscoverySettings* settings; + + + /** + Set to true if this server is running the correct protocol. + */ + bool correctProtocol; + + /** + This is set during the serialize process from the server's settings. + If different from the client's settings the discovery system will + classify this server as incompatible. + */ + int serverProtocolVersion[2]; + + Array<NetAddress> address; + + DiscoveryServerAddressMessage() {} + DiscoveryServerAddressMessage(const DiscoverySettings* s) : settings(s) {} + + void serialize(BinaryOutput& b) const; + + void deserialize(BinaryInput& b); +}; + + +/** + Base class for DiscoveryClient and DiscoveryServer. + */ +class Discovery { +public: + + const DiscoverySettings* settings; + + enum { + SERVER_SHUTDOWN_MESSAGE = 2, + SERVER_BROADCAST_MESSAGE = 3, + CLIENT_BROADCAST_MESSAGE = 4}; + + /** + Only called from subclasses. + */ + virtual void init( + const DiscoverySettings* _settings) { + settings = _settings; + } + + /** + An empty virtual destructor for virtual methods. + */ + virtual ~Discovery() {} +}; + +/** @deprecated See Discovery2::Server*/ +class DiscoveryServer : private Discovery { +private: + + class ShutdownMessage { + public: + void serialize(BinaryOutput& b) const { (void)b; } + + void deserialize(BinaryInput& b) { (void)b; } + }; + + /** + For broadcast. + */ + LightweightConduitRef net; + + /** + Listen for clients wanting to hear the advertisement over + a reliable connection. + */ + NetListenerRef listener; + + DiscoveryAdvertisement* advertisement; + + /** + Broadcast across the lightweight conduit. + */ + DiscoveryServerAddressMessage addressMessage; + + /** + Servers periodically broadcast (unsolicited) in case + anyone missed the previous message. + */ + RealTime lastBroadcast; + + void sendAnnouncement() const; + + void sendShutDown() const; + +public: + + /** + You may update the advertisement (synchronously with calling doNetwork) + after it has been passed in. This allows a server to change the advertised + number of players or score for a game, for example. + + You must set the port of the @a _advertisement G3D::DiscoveryAdvertisement::address + to the port which the G3D::NetListener for the actual program protocol (not discovery) + is running. That field how the client knows what address to connect to using + G3D::ReliableConduit or G3D::LightweightConduit to actually initiate communication. + */ + virtual void init( + const DiscoverySettings* _settings, + DiscoveryAdvertisement* _advertisement); + + /** Returns the broadcast address in use.*/ + NetAddress broadcastAddress() const; + + /** + Returns true if this discovery server has been initialized + and is functioning properly. + */ + bool ok() const; + + /** + Call periodically to let the server do its job. + */ + void doNetwork(); + + /** + Broadcast a shutdown message. + */ + void cleanup(); +}; + + +/** + Used by DiscoveryClient to report servers running a different version + of this application's protocol. + */ +class IncompatibleServerDescription { +public: + NetAddress address; + int protocolVersion[2]; + RealTime lastUpdateTime; + + std::string toString() const; +}; + + +/** + Only one DiscoveryClient can be active on a given port at a time on + a single computer. + + AdType must be a subclass of DiscoveryAdvertisement. + + @deprecated See Discovery2::Client + */ +template<class AdType> +class DiscoveryClient : private Discovery { +public: + + /** + List of servers. Do not access on a second thread while in + doNetwork. + */ + Array<AdType> serverList; + + /** + List of servers running the same application but a different protocol. + It is useful to show these to users to help them recognize version + conflicts between client and server. + Do not access on a second thread while in doNetwork. + */ + Array<IncompatibleServerDescription> incompatibleServerList; + +private: + + class BroadcastMessage { + public: + void serialize(BinaryOutput& b) const {} + + void deserialize(BinaryInput& b) {} + }; + + /** + The client periodically checks servers to make sure they are still up + and to update its information about them. + */ + RealTime lastServerCheck; + + LightweightConduitRef net; + + /** + Returns an index in serverList of the server with the given address. + Returns -1 if there is none. Only checks IP addresses. + */ + int findServerListIndex(const NetAddress& addr) const { + for (int i = 0; i < serverList.size(); ++i) { + if (addr.ip() == serverList[i].address.ip()) { + return i; + } + } + + return -1; + } + + /** + Returns true if this discovery client has been initialized + and is functioning properly. + */ + bool ok() const { + return net->ok(); + } + + /** + Adds a server to the incompatible list if it is not already there. + */ + void addToIncompatibleList(const NetAddress& addr, uint32 p0, uint32 p1) { + const RealTime now = System::time(); + + bool alreadyHere = false; + + // Incorrect protocol; add to the incompatible list + for (int i = 0; i < incompatibleServerList.size(); ++i) { + IncompatibleServerDescription& server = incompatibleServerList[i]; + + if (server.address == addr) { + server.lastUpdateTime = now; + alreadyHere = true; + break; + } + } + + if (! alreadyHere) { + IncompatibleServerDescription server; + + server.lastUpdateTime = now; + server.address = addr; + server.protocolVersion[0] = p0; + server.protocolVersion[1] = p1; + + incompatibleServerList.append(server); + } + } + + /** + Connects to the specified server, reads its advertisement, + and adds it to the active server list. Returns true if the server + can be reached. + */ + bool readAdvertisement(const NetAddress& address) { + std::string hostname = address.toString(); + + RealTime TIMEOUT = 2.0; + + ReliableConduitRef server = ReliableConduit::create(address); + + if (! server->ok()) { + return false; + } + + AdType advertisement; + + // Read the advertisement + RealTime stopWaiting = System::time() + TIMEOUT; + bool timedOut = false; + + while (! server->messageWaiting() && ! timedOut && server->ok()) { + System::sleep(0.1); + timedOut = (System::time() > stopWaiting); + } + + if (timedOut) { + Log::common()->printf("Discovery: Timed out while reading advertisment from %s\n", + hostname.c_str()); + return false; + } + + + if (! server->ok()) { + Log::common()->printf("Discovery: Server %s dropped connection\n", hostname.c_str()); + return false; + } + + // Read the advertisement + debugAssert(server->messageWaiting()); + if (! server->receive(advertisement)) { + Log::common()->printf("Discovery: Server %s failed to send advertisement\n", hostname.c_str()); + return false; + } + + // Update existing server info or create a new entry + int index = findServerListIndex(address); + if (index == -1) { + index = serverList.size(); + serverList.resize(index + 1); + } + + // Update element index + advertisement.address = address; + serverList[index] = advertisement; + + return true; + } + + /** + Remove this address from our list if we previously + had a server there. + */ + void removeServer(const NetAddress& address) { + int index = findServerListIndex(address); + if (index > -1) { + serverList.fastRemove(index); + } + } + + /** + Tries to connect to the server through the addresses in the array. + */ + void addToServerList(const Array<NetAddress>& addressArray) { + // Try to connect to each address listed + for (int a = addressArray.size() - 1; a >= 0; --a) { + const NetAddress& address = addressArray[a]; + + if (readAdvertisement(address)) { + // We've connected to the server + break; + } else { + removeServer(address); + } + } + } + + void checkRandomServer() { + if (serverList.size() >= 1) { + int index = iRandom(0, serverList.size() - 1); + + Array<NetAddress> address; + address.append(serverList[index].address); + + // Remove this server + serverList.fastRemove(index); + + // Add it back with new info (or leave it removed if no response) + addToServerList(address); + } + } + +public: + + void init( + const DiscoverySettings* _settings) { + + Discovery::init(_settings); + + lastServerCheck = System::time(); + + net = LightweightConduit::create(settings->serverBroadcastPort, true, true); + + // Send announcement + NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], + settings->clientBroadcastPort); + BroadcastMessage tmp; + net->send(broadcast, CLIENT_BROADCAST_MESSAGE, tmp); + } + + /** Shut down the discovery client. */ + void cleanup() { + net = NULL; + } + + /** + Call this regularly (several times per second) to + update the server list. Not threadsafe-- you must not touch + the server list while this is running. This will not block. + */ + void doNetwork() { + if (net->messageWaiting()) { + NetAddress sender; + + switch (net->waitingMessageType()) { + case SERVER_SHUTDOWN_MESSAGE: + // Remove the server + net->receive(sender); + removeServer(sender); + break; + + case SERVER_BROADCAST_MESSAGE: + // Check the G3D protocol and the network protocol, then read the ad + DiscoveryServerAddressMessage msg(settings); + net->receive(sender, msg); + + if (msg.correctProtocol && (msg.address.size() > 0)) { + // Add the actual return address as the first one to be tried. + msg.address.append(NetAddress(sender.ip(), msg.address[0].port())); + + addToServerList(msg.address); + + } else { + + addToIncompatibleList( + sender, + msg.serverProtocolVersion[0], + msg.serverProtocolVersion[1]); + } + break; + } + } + + // Periodically re-check servers in the list to see if they crashed + // (if they shut down, they should have broadcast a shut down message). + RealTime now = System::time(); + const RealTime UPDATE_TIME_INTERVAL = 30; + + if (now > lastServerCheck + UPDATE_TIME_INTERVAL) { + lastServerCheck = now; + checkRandomServer(); + } + } +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h b/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h new file mode 100644 index 00000000000..acf17615b45 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h @@ -0,0 +1,26 @@ +/** + @file EqualsTrait.h + + @maintainer Morgan McGuire, morgan@cs.williams.edu + @created 2008-10-01 + @edited 2008-10-01 + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_EQUALSTRAIT_H +#define G3D_EQUALSTRAIT_H + +#include "G3D/platform.h" + +/** Default implementation of EqualsTrait. + @see G3D::Table for specialization requirements. +*/ +template<typename Key> struct EqualsTrait { + static bool equals(const Key& a, const Key& b) { + return a == b; + } +}; + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3D.h b/externals/g3dlite/G3D.lib/include/G3D/G3D.h new file mode 100644 index 00000000000..42ab18e2c24 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/G3D.h @@ -0,0 +1,150 @@ +/** + @file graphics3D.h + + This header includes all of the graphics3D libraries in + appropriate namespaces. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-08-25 + @edited 2008-11-01 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. +*/ + +#ifndef G3D_GRAPHICS3D_H +#define G3D_GRAPHICS3D_H + +#define NOMINMAX 1 +#ifdef min + #undef min +#endif +#ifdef max + #undef max +#endif + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Queue.h" +#include "G3D/Crypto.h" +#include "G3D/format.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/Color1.h" +#include "G3D/Color3.h" +#include "G3D/Color4.h" +#include "G3D/Matrix2.h" +#include "G3D/Matrix3.h" +#include "G3D/Matrix4.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/PhysicsFrame.h" +#include "G3D/Plane.h" +#include "G3D/Line.h" +#include "G3D/Ray.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/AABox.h" +#include "G3D/WrapMode.h" +#include "G3D/Cone.h" +#include "G3D/Quat.h" +#include "G3D/stringutils.h" +#include "G3D/prompt.h" +#include "G3D/Table.h" +#include "G3D/Set.h" +#include "G3D/GUniqueID.h" +#include "G3D/BinaryFormat.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/debug.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/g3dmath.h" +#include "G3D/uint128.h" +#include "G3D/fileutils.h" +#include "G3D/ReferenceCount.h" + +template<class T> struct HashTrait< G3D::ReferenceCountedPointer<T> > { + static size_t hashCode(G3D::ReferenceCountedPointer<T> key) { return reinterpret_cast<size_t>( key.pointer() ); } +}; + +#include "G3D/GImage.h" +#include "G3D/CollisionDetection.h" +#include "G3D/Log.h" +#include "G3D/serialize.h" +#include "G3D/TextInput.h" +#include "G3D/NetAddress.h" +#include "G3D/NetworkDevice.h" +#include "G3D/System.h" +#include "G3D/splinefunc.h" +#include "G3D/Spline.h" +#include "G3D/UprightFrame.h" +#include "G3D/LineSegment.h" +#include "G3D/Capsule.h" +#include "G3D/Cylinder.h" +#include "G3D/Triangle.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color4uint8.h" +#include "G3D/Vector2int16.h" +#include "G3D/Vector3int16.h" +#include "G3D/Vector3int32.h" +#include "G3D/Vector4int8.h" +#include "G3D/ConvexPolyhedron.h" +#include "G3D/Discovery.h" +#include "G3D/MeshAlg.h" +#include "G3D/vectorMath.h" +#include "G3D/Rect2D.h" +#include "G3D/GCamera.h" +#include "G3D/GLight.h" +#include "G3D/AABSPTree.h" +#include "G3D/PointAABSPTree.h" +#include "G3D/TextOutput.h" +#include "G3D/MeshBuilder.h" +#include "G3D/Stopwatch.h" +#include "G3D/AtomicInt32.h" +#include "G3D/GThread.h" +#include "G3D/ThreadSet.h" +#include "G3D/RegistryUtil.h" +#include "G3D/AnyVal.h" +#include "G3D/PointHashGrid.h" +#include "G3D/Map2D.h" +#include "G3D/Image1.h" +#include "G3D/Image1uint8.h" +#include "G3D/Image3.h" +#include "G3D/Image3uint8.h" +#include "G3D/Image4.h" +#include "G3D/Image4uint8.h" +#include "G3D/filter.h" +#include "G3D/WeakCache.h" +#include "G3D/Pointer.h" +#include "G3D/Matrix.h" +#include "G3D/ImageFormat.h" + +#ifdef _MSC_VER +# pragma comment(lib, "zlib") +# pragma comment(lib, "ws2_32") +# pragma comment(lib, "winmm") +# pragma comment(lib, "imagehlp") +# pragma comment(lib, "gdi32") +# pragma comment(lib, "user32") +# pragma comment(lib, "kernel32") +# pragma comment(lib, "version") +# pragma comment(lib, "advapi32") +# pragma comment(lib, "png") +# pragma comment(lib, "jpeg") +# pragma comment(lib, "zip") +# ifdef _DEBUG + // Don't link against G3D when building G3D itself. +# ifndef G3D_BUILDING_LIBRARY_DLL +# pragma comment(lib, "G3Dd.lib") +# endif +# else + // Don't link against G3D when building G3D itself. +# ifndef G3D_BUILDING_LIBRARY_DLL +# pragma comment(lib, "G3D.lib") +# endif +# endif +#endif + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h b/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h new file mode 100644 index 00000000000..feba3d6d390 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h @@ -0,0 +1,26 @@ +/** + @file G3DAll.h + + Includes all G3D and GLG3D files and uses the G3D namespace. + + This requires OpenGL and SDL headers. If you don't want all of this, + #include <G3D.h> separately. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-01-01 + @edited 2006-08-13 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_G3DALL_H +#define G3D_G3DALL_H + +#include "G3D/G3D.h" +#include "GLG3D/GLG3D.h" + +using namespace G3D; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h b/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h new file mode 100644 index 00000000000..bc4c873f290 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h @@ -0,0 +1,44 @@ +/** + @file G3DGameUnits.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + @created 2002-10-05 + @edited 2006-11-10 + */ + +#ifndef G3D_GAMEUNITS_H +#define G3D_GAMEUNITS_H + +#include "G3D/platform.h" + +namespace G3D { +/** + Time, in seconds. + */ +typedef double GameTime; +typedef double SimTime; + +/** + Actual wall clock time in seconds. + */ +typedef double RealTime; + +enum AMPM {AM, PM}; + +enum {SECOND=1, MINUTE=60, HOUR = 60*60, DAY=24*60*60, SUNRISE=24*60*60/4, SUNSET=24*60*60*3/4, MIDNIGHT=0, METER=1, KILOMETER=1000}; + +#define CENTIMETER (0.01) +#define DECIMETER (0.1) + +/** + Converts a 12 hour clock time into the number of seconds since + midnight. Note that 12:00 PM is noon and 12:00 AM is midnight. + + Example: <CODE>toSeconds(10, 00, AM)</CODE> + */ +SimTime toSeconds(int hour, int minute, double seconds, AMPM ap); +SimTime toSeconds(int hour, int minute, AMPM ap); + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/GCamera.h b/externals/g3dlite/G3D.lib/include/G3D/GCamera.h new file mode 100644 index 00000000000..4dfa500883b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/GCamera.h @@ -0,0 +1,294 @@ +/** + @file GCamera.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2005-07-20 + @edited 2007-07-24 +*/ + +#ifndef G3D_GCamera_H +#define G3D_GCamera_H + +#include "G3D/platform.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Vector3.h" +#include "G3D/Plane.h" +#include "G3D/debugAssert.h" + +namespace G3D { + +class Matrix4; +class Rect2D; + +/** + Abstraction of a pinhole camera. + + The area a camera sees is called a frustum. It is bounded by the near plane, the far plane, and the sides + of the view frame projected into the scene. It has the shape of a pyramid with the top cut off. + + Cameras can project points from 3D to 2D. The "unit" projection matches OpenGL. It maps the entire view frustum + to a cube of unit radius (i.e., edges of length 2) centered at the origin. The non-unit projection then maps + that cube to the specified pixel viewport in X and Y and the range [0, 1] in Z. The projection is reversable + as long as the projected Z value is known. + + All viewport arguments are the pixel bounds of the viewport-- e.g., + RenderDevice::viewport(). + */ +class GCamera { + +public: + /** + Stores the direction of the field of view + */ + enum FOVDirection {HORIZONTAL, VERTICAL}; + +private: + + + /** field of view (in radians) */ + float m_fieldOfView; + + /** Clipping plane, *not* imaging plane. Negative numbers. */ + float m_nearPlaneZ; + + /** Negative */ + float m_farPlaneZ; + + /** Stores the camera's location and orientation */ + CoordinateFrame m_cframe; + + /** Horizontal or Vertical */ + FOVDirection m_direction; + +public: + + class Frustum { + public: + class Face { + public: + /** Counter clockwise indices into vertexPos */ + int vertexIndex[4]; + + /** The plane containing the face. */ + Plane plane; + }; + + /** The vertices, in homogeneous space. If w == 0, + a vertex is at infinity. */ + Array<Vector4> vertexPos; + + /** The faces in the frustum. When the + far plane is at infinity, there are 5 faces, + otherwise there are 6. The faces are in the order + N,R,L,B,T,[F]. + */ + Array<Face> faceArray; + }; + + GCamera(); + + virtual ~GCamera(); + + /** Returns the current coordinate frame */ + const CoordinateFrame& coordinateFrame() const { + return m_cframe; + } + + /** Sets c to the camera's coordinate frame */ + void getCoordinateFrame(CoordinateFrame& c) const; + + /** Sets a new coordinate frame for the camera */ + void setCoordinateFrame(const CoordinateFrame& c); + + /** Sets P equal to the camera's projection matrix */ + void getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const; + + /** Converts projected points from OpenGL standards + (-1, 1) to normal 3D coordinate standards (0, 1) */ + Vector3 convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const; + + /** + Sets the vertical field of view, in radians. The + initial angle is toRadians(55). Must specify the direction of the angle + */ + void setFieldOfView(float angle, FOVDirection direction); + + /** Returns the current field of view angle and direction */ + inline void getFieldOfView(float& angle, FOVDirection& direction) const { + angle = m_fieldOfView; + direction = m_direction; + } + + /** + Projects a world space point onto a width x height screen. The + returned coordinate uses pixmap addressing: x = right and y = + down. The resulting z value is 0 at the near plane, 1 at the far plane, + and is a linear compression of unit cube projection. + + If the point is behind the camera, Vector3::inf() is returned. + */ + Vector3 project(const G3D::Vector3& point, + const class Rect2D& viewport) const; + + /** + Projects a world space point onto a unit cube. The resulting + x,y,z values range between -1 and 1, where z is -1 + at the near plane and 1 at the far plane and varies hyperbolically in between. + + If the point is behind the camera, Vector3::inf() is returned. + */ + Vector3 projectUnit(const G3D::Vector3& point, + const class Rect2D& viewport) const; + + /** + Gives the world-space coordinates of screen space point v, where + v.x is in pixels from the left, v.y is in pixels from + the top, and v.z is on the range 0 (near plane) to 1 (far plane). + */ + Vector3 unproject(const Vector3& v, const Rect2D& viewport) const; + + /** + Gives the world-space coordinates of unit cube point v, where + v varies from -1 to 1 on all axes. The unproject first + transforms the point into a pixel location for the viewport, then calls unproject + */ + Vector3 unprojectUnit(const Vector3& v, const Rect2D& viewport) const; + + /** + Returns the pixel area covered by a shape of the given + world space area at the given z value (z must be negative). + */ + float worldToScreenSpaceArea(float area, float z, const class Rect2D& viewport) const; + + /** + Returns the world space 3D viewport corners. These + are at the near clipping plane. The corners are constructed + from the nearPlaneZ, viewportWidth, and viewportHeight. + "left" and "right" are from the GCamera's perspective. + */ + void getNearViewportCorners(const class Rect2D& viewport, + Vector3& outUR, Vector3& outUL, + Vector3& outLL, Vector3& outLR) const; + + /** + Returns the world space 3D viewport corners. These + are at the Far clipping plane. The corners are constructed + from the nearPlaneZ, farPlaneZ, viewportWidth, and viewportHeight. + "left" and "right" are from the GCamera's perspective. + */ + void getFarViewportCorners(const class Rect2D& viewport, + Vector3& outUR, Vector3& outUL, + Vector3& outLL, Vector3& outLR) const; + + /** + Returns the image plane depth, assumes imagePlane + is the same as the near clipping plane. + returns a positive number. + */ + float imagePlaneDepth() const; + + /** + Returns the world space ray passing through the center of pixel + (x, y) on the image plane. The pixel x and y axes are opposite + the 3D object space axes: (0,0) is the upper left corner of the screen. + They are in viewport coordinates, not screen coordinates. + + The ray origin is at the origin. To start it at the image plane, + move it forward by imagePlaneDepth/ray.direction.z + + Integer (x, y) values correspond to + the upper left corners of pixels. If you want to cast rays + through pixel centers, add 0.5 to x and y. + */ + Ray worldRay( + float x, + float y, + const class Rect2D& viewport) const; + + /** + Returns a negative z-value. + */ + inline float nearPlaneZ() const { + return m_nearPlaneZ; + } + + /** + Returns a negative z-value. + */ + inline float farPlaneZ() const { + return m_farPlaneZ; + } + + /** + Sets a new value for the far clipping plane + Expects a negative value + */ + inline void setFarPlaneZ(float z) { + debugAssert(z < 0); + m_farPlaneZ = z; + } + + /** + Sets a new value for the near clipping plane + Expects a negative value + */ + inline void setNearPlaneZ(float z) { + debugAssert(z < 0); + m_nearPlaneZ = z; + } + + /** + Returns the camera space width of the viewport at the near plane. + */ + float viewportWidth(const class Rect2D& viewport) const; + + /** + Returns the camera space height of the viewport at the near plane. + */ + float viewportHeight(const class Rect2D& viewport) const; + + void setPosition(const Vector3& t); + + /** Rotate the camera in place to look at the target. Does not + persistently look at that location when the camera moves; + i.e., if you move the camera and still want it to look at the + old target, you must call lookAt again after moving the + camera.)*/ + void lookAt(const Vector3& position, const Vector3& up = Vector3::unitY()); + + /** + Returns the clipping planes of the frustum, in world space. + The planes have normals facing <B>into</B> the view frustum. + + The plane order is guaranteed to be: + Near, Right, Left, Top, Bottom, [Far] + + If the far plane is at infinity, the resulting array will have + 5 planes, otherwise there will be 6. + + The viewport is used only to determine the aspect ratio of the screen; the + absolute dimensions and xy values don't matter. + */ + void getClipPlanes + ( + const Rect2D& viewport, + Array<Plane>& outClip) const; + + /** + Returns the world space view frustum, which is a truncated pyramid describing + the volume of space seen by this camera. + */ + void frustum(const Rect2D& viewport, GCamera::Frustum& f) const; + + GCamera::Frustum frustum(const Rect2D& viewport) const; + + /** Read and Write camera parameters */ + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); + +}; + +} // namespace G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/GImage.h b/externals/g3dlite/G3D.lib/include/G3D/GImage.h new file mode 100644 index 00000000000..12003514d6a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/GImage.h @@ -0,0 +1,550 @@ +/** + @file GImage.h + + See G3D::GImage for details. + + @cite JPEG compress/decompressor is the <A HREF="http://www.ijg.org/files/">IJG library</A>, used in accordance with their license. + @cite JPG code by John Chisholm, using the IJG Library + @cite TGA code by Morgan McGuire + @cite BMP code by John Chisholm, based on code by Edward "CGameProgrammer" Resnick <A HREF="mailto:cgp@gdnmail.net">mailto:cgp@gdnmail.net</A> at <A HREF="ftp://ftp.flipcode.com/cotd/LoadPicture.txt">ftp://ftp.flipcode.com/cotd/LoadPicture.txt</A> + @cite PCX format described in the ZSOFT PCX manual http://www.nist.fss.ru/hr/doc/spec/pcx.htm#2 + @cite PNG compress/decompressor is the <A HREF="http://www.libpng.org/pub/png/libpng.html">libpng library</A>, used in accordance with their license. + @cite PPM code by Morgan McGuire based on http://netpbm.sourceforge.net/doc/ppm.html + + @maintainer Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2007-01-31 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + + */ + +#ifndef G3D_GIMAGE_H +#define G3D_GIMAGE_H + +#include "G3D/platform.h" +#include <string> +#include "G3D/Array.h" +#include "G3D/g3dmath.h" +#include "G3D/stringutils.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color4uint8.h" + +namespace G3D { +class BinaryInput; +class BinaryOutput; +/** + Interface to image compression & file formats. + + Supported formats (decode and encode): Color JPEG, PNG, (Uncompressed)TGA 24, (Uncompressed)TGA 32, BMP 1, BMP 4, BMP 8, BMP 24, PPM (P6), and PPM ASCII (P1, P2, P3). + 8-bit paletted PCX, 24-bit PCX, and ICO are supported for decoding only. + + Sample usage: + + <PRE> + #include "graphics3D.h" + + // Loading from disk: + G3D::GImage im1 = G3D::GImage("test.jpg"); + + // Loading from memory: + G3D::GImage im2 = G3D::GImage(data, length); + + // im.pixel is a pointer to RGB color data. If you want + // an alpha channel, call RGBtoRGBA or RGBtoARGB for + // conversion. + + // Saving to memory: + G3D::GImage im3 = G3D::GImage(width, height); + // (Set the pixels of im3...) + uint8* data2; + int len2; + im3.encode(G3D::GImage::JPEG, data2, len2); + + // Saving to disk + im3.save("out.jpg"); + </PRE> + + The free Image Magick Magick Wand API + (http://www.imagemagick.org/www/api/magick_wand.html) provides a more powerful + API for image manipulation and wider set of image load/save formats. It is + recommended over GImage (we don't include it directly in G3D because their license + is more restrictive than the BSD one). + + */ +class GImage { +private: + uint8* _byte; + +public: + + class Error { + public: + Error( + const std::string& reason, + const std::string& filename = "") : + reason(reason), filename(filename) {} + + std::string reason; + std::string filename; + }; + + enum Format {JPEG, BMP, TGA, PCX, ICO, PNG, PPM_ASCII, PPM, AUTODETECT, UNKNOWN}; + + int width; + int height; + + /** + The number of channels; either 3 (RGB) or 4 (RGBA) + */ + int channels; + + inline const uint8* byte() const { + return _byte; + } + + /** Returns a pointer to the upper left pixel + as Color3uint8. + */ + inline const Color3uint8* pixel3() const { + debugAssertM(channels == 3, format("Tried to call GImage::pixel3 on an image with %d channels", channels)); + return (Color3uint8*)_byte; + } + + /** Returns a pointer to the upper left pixel + as Color4uint8. + */ + inline const Color4uint8* pixel4() const { + debugAssertM(channels == 4, format("Tried to call GImage::pixel4 on an image with %d channels", channels)); + return (Color4uint8*)_byte; + } + + inline const Color1uint8* pixel1() const { + debugAssertM(channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", channels)); + return (Color1uint8*)_byte; + } + + inline Color1uint8* pixel1() { + debugAssertM(channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", channels)); + return (Color1uint8*)_byte; + } + + /** Returns the pixel at (x, y), where (0,0) is the upper left. */ + inline const Color1uint8& pixel1(int x, int y) const { + debugAssert(x >= 0 && x < width); + debugAssert(y >= 0 && y < height); + return pixel1()[x + y * width]; + } + + /** Returns the pixel at (x, y), where (0,0) is the upper left. */ + inline Color1uint8& pixel1(int x, int y) { + debugAssert(x >= 0 && x < width); + debugAssert(y >= 0 && y < height); + return pixel1()[x + y * width]; + } + + /** Returns the pixel at (x, y), where (0,0) is the upper left. */ + inline const Color3uint8& pixel3(int x, int y) const { + debugAssert(x >= 0 && x < width); + debugAssert(y >= 0 && y < height); + return pixel3()[x + y * width]; + } + + inline Color3uint8& pixel3(int x, int y) { + debugAssert(x >= 0 && x < width); + debugAssert(y >= 0 && y < height); + return pixel3()[x + y * width]; + } + + /** Returns the pixel at (x, y), where (0,0) is the upper left. */ + inline const Color4uint8& pixel4(int x, int y) const { + debugAssert(x >= 0 && x < width); + debugAssert(y >= 0 && y < height); + return pixel4()[x + y * width]; + } + + inline Color4uint8& pixel4(int x, int y) { + debugAssert(x >= 0 && x < width); + debugAssert(y >= 0 && y < height); + return pixel4()[x + y * width]; + } + + inline uint8* byte() { + return _byte; + } + + inline Color3uint8* pixel3() { + debugAssert(channels == 3); + return (Color3uint8*)_byte; + } + + inline Color4uint8* pixel4() { + debugAssert(channels == 4); + return (Color4uint8*)_byte; + } + +private: + + void encodeBMP( + BinaryOutput& out) const; + + /** + The TGA file will be either 24- or 32-bit depending + on the number of channels. + */ + void encodeTGA( + BinaryOutput& out) const; + + /** + Converts this image into a JPEG + */ + void encodeJPEG( + BinaryOutput& out) const; + + /** + Converts this image into a JPEG + */ + void encodePNG( + BinaryOutput& out) const; + + void encodePPM( + BinaryOutput& out) const; + + void encodePPMASCII( + BinaryOutput& out) const; + + void decodeTGA( + BinaryInput& input); + + void decodeBMP( + BinaryInput& input); + + void decodeJPEG( + BinaryInput& input); + + void decodePCX( + BinaryInput& input); + + void decodeICO( + BinaryInput& input); + + void decodePNG( + BinaryInput& input); + + void decodePPM( + BinaryInput& input); + + void decodePPMASCII( + BinaryInput& input); + + /** + Given [maybe] a filename, memory buffer, and [maybe] a format, + returns the most likely format of this file. + */ + static Format resolveFormat( + const std::string& filename, + const uint8* data, + int dataLen, + Format maybeFormat); + + void _copy( + const GImage& other); + +public: + static Format resolveFormat(const std::string& filename); + + GImage() { + width = height = channels = 0; + _byte = NULL; + } + + /** + Load an encoded image from disk and decode it. + Throws GImage::Error if something goes wrong. + */ + GImage( + const std::string& filename, + Format format = AUTODETECT); + + /** + Decodes an image stored in a buffer. + */ + GImage( + const unsigned char*data, + int length, + Format format = AUTODETECT); + + /** + Create an empty image of the given size. + */ + GImage( + int width, + int height, + int channels = 3); + + GImage( + const GImage& other); + + GImage& operator=(const GImage& other); + + /** + Returns a new GImage that has 4 channels. RGB is + taken from this GImage and the alpha from the red + channel of the supplied image. The new GImage is passed + as a reference parameter for speed. + */ + void insertRedAsAlpha(const GImage& alpha, GImage& output) const; + + GImage G3D_DEPRECATED insertRedAsAlpha(const GImage& alpha) const; + + /** + Returns a new GImage with 3 channels, removing + the alpha channel if there is one. The new GImage + is passed as a reference parameter for speed. + */ + void stripAlpha(GImage& output) const; + + GImage G3D_DEPRECATED stripAlpha() const; + + /** + Loads an image from disk (clearing the old one first). + */ + void load( + const std::string& filename, + Format format = AUTODETECT); + + /** + Frees memory and resets to a 0x0 image. + */ + void clear(); + + /** + Deallocates the pixels. + */ + virtual ~GImage(); + + /** + Resizes the internal buffer to (width x height) with the + number of channels specified. All data is set to 0 (black). + */ + void resize(int width, int height, int channels); + + + /** + Copies src sub-image data into dest at a certain offset. + The dest variable must already contain an image that is large + enough to contain the src sub-image at the specified offset. + Returns true on success and false if the src sub-image cannot + completely fit within dest at the specified offset. Both + src and dest must have the same number of channels. + */ + static bool pasteSubImage(GImage & dest, const GImage & src, + int destX, int destY, int srcX, int srcY, int srcWidth, int srcHeight); + + /** + creates dest from src sub-image data. + Returns true on success and false if the src sub-image + is not within src. + */ + static bool copySubImage(GImage & dest, const GImage & src, + int srcX, int srcY, int srcWidth, int srcHeight); + + void convertToRGBA(); + + void convertToRGB(); + + /** Averages color channels if they exist */ + void convertToL8(); + + /** + Returns true if format is supported. Format + should be an extension string (e.g. "BMP"). + */ + static bool supportedFormat( + const std::string& format); + + /** + Converts a string to an enum, returns UNKNOWN if not recognized. + */ + static Format stringToFormat( + const std::string& format); + + /** + Encode and save to disk. + */ + void save( + const std::string& filename, + Format format = AUTODETECT) const; + + /** + The caller must delete the returned buffer. + */ + void encode( + Format format, + uint8*& outData, + int& outLength) const; + + /** + Does not commit the BinaryOutput when done. + */ + void encode( + Format format, + BinaryOutput& out) const; + + /** + Decodes the buffer into this image. + @format Must be the correct format. + */ + void decode( + BinaryInput& input, + Format format); + + /** Returns the size of this object in bytes */ + int sizeInMemory() const; + + + /** Ok for in == out */ + static void R8G8B8_to_Y8U8V8(int width, int height, const uint8* in, uint8* out); + + /** Ok for in == out */ + static void Y8U8V8_to_R8G8B8(int width, int height, const uint8* in, uint8* out); + + /** + @param in RGB buffer of numPixels * 3 bytes + @param out Buffer of numPixels * 4 bytes + @param numPixels Number of RGB pixels to convert + */ + static void RGBtoRGBA( + const uint8* in, + uint8* out, + int numPixels); + + static void RGBtoARGB( + const uint8* in, + uint8* out, + int numPixels); + + /** Safe for in == out */ + static void RGBtoBGR( + const uint8* in, + uint8* out, + int numPixels); + + /** + Win32 32-bit HDC format. + */ + static void RGBtoBGRA( + const uint8* in, + uint8* out, + int numPixels); + + static void RGBAtoRGB( + const uint8* in, + uint8* out, + int numPixels); + /** + Uses the red channel of the second image as an alpha channel. + */ + static void RGBxRGBtoRGBA( + const uint8* colorRGB, + const uint8* alphaRGB, + uint8* out, + int numPixels); + + /** + Flips the image along the vertical axis. + Safe for in == out. + */ + static void flipRGBVertical( + const uint8* in, + uint8* out, + int width, + int height); + + static void flipRGBAVertical( + const uint8* in, + uint8* out, + int width, + int height); + + /** + Given a tangent space bump map, computes a new image where the + RGB channels are a tangent space normal map and the alpha channel + is the original bump map. Assumes the input image is tileable. + + In the resulting image, x = red = tangent, y = green = binormal, and z = blue = normal. + + Particularly useful as part of the idiom: + <PRE> + GImage normal; + computeNormalMap(GImage(filename), normal); + return Texture::fromGImage(filename, normal); + </PRE> + + @param lowPassBump If true, a 9x9 filter of 1's is used to low-pass filter the elevations, + which produces better results for parallax mapping. + + @param scaleHeightByNz After computing normals, scale the height by |N.z|, a trick that + reduces texture swim in steep areas for parallax mapping. + + @param whiteHeightInPixels How high should the brightest input value be considered for purposes + of normal computation, compared to the horizontal and vertical size of a pixel. + A value of 255 means that a 255 x 255 bump image with a full black-to-white gradient will + produce a 45-degree ramp (this also results in "cubic" voxels). + A special (default) value of -1 means scale the effective white height so that it is equal + to the larger spatial dimension. + */ + static void computeNormalMap( + const class GImage& bump, + class GImage& normal, + float whiteHeightInPixels = -1.0f, + bool lowPassBump = false, + bool scaleHeightByNz = false); + + static void computeNormalMap( + int width, + int height, + int channels, + const uint8* src, + GImage& normal, + float whiteHeightInPixels, + bool lowPassBump, + bool scaleHeightByNz); + + /** + Bayer demosaicing using the filter proposed in + + HIGH-QUALITY LINEAR INTERPOLATION FOR DEMOSAICING OF BAYER-PATTERNED COLOR IMAGES + Henrique S. Malvar, Li-wei He, and Ross Cutler + + The filter wraps at the image boundaries. + + Assumes in != out. + */ + static void BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); + static void BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); + static void BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); + static void BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); + + /** Fast conversion; the output has 1/2 the size of the input in each direction. Assumes in != out. + See G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC for a much better result. */ + static void BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int inWidth, int inHeight, const uint8* in, uint8* out); + + /** Attempt to undo fast conversion of G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8; + the green channel will lose data. Assumes in != out + The input should have size 3 * inWidth * inHeight. The output should have size + 2 * inWidth * 2 * inHeight. + */ + static void Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out); + + /** Overwrites every pixel with one of the two colors in a checkerboard pattern. + The fields used from the two colors depend on the current number of channels in @a im. + */ + static void makeCheckerboard(GImage& im, int checkerSize = 1, const Color4uint8& color1 = Color4uint8(255,255,255,255), const Color4uint8& color2 = Color4uint8(0,0,0,255)); +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/GLight.h b/externals/g3dlite/G3D.lib/include/G3D/GLight.h new file mode 100644 index 00000000000..b8fa7b54261 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/GLight.h @@ -0,0 +1,72 @@ +/** + @file GLight.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-11-12 + @edited 2006-02-08 +*/ + +#ifndef G3D_GLIGHT_H +#define G3D_GLIGHT_H + +#include "G3D/platform.h" +#include "G3D/Vector4.h" +#include "G3D/Vector3.h" +#include "G3D/Color4.h" + +namespace G3D { + +/** + A light representation that closely follows the OpenGL light format. + */ +class GLight { +public: + /** World space position (for a directional light, w = 0 */ + Vector4 position; + + /** Direction in which the light faces, if a spot light. This is the "look vector" of the light source. */ + Vector3 spotDirection; + + /** In <B>degrees</B>. 180 = no cutoff (point/dir) >90 = spot light */ + float spotCutoff; + + /** Constant, linear, quadratic */ + float attenuation[3]; + + /** May be outside the range [0, 1] */ + Color3 color; + + /** If false, this light is ignored */ + bool enabled; + + /** If false, this light does not create specular highlights (useful when using negative lights). */ + bool specular; + + /** If false, this light does not create diffuse illumination (useful when rendering a specular-only pass). */ + bool diffuse; + + GLight(); + + /** @param toLight will be normalized */ + static GLight directional(const Vector3& toLight, const Color3& color, bool specular = true, bool diffuse = true); + + static GLight point(const Vector3& pos, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0.5f, bool specular = true, bool diffuse = true); + + /** @param pointDirection Will be normalized. Points in the direction that light propagates. + @param cutOffAngleDegrees Must be on the range [0, 90]. This is the angle from the point direction + to the edge of the light cone. + */ + static GLight spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0, bool specular = true, bool diffuse = true); + + /** Returns the sphere within which this light has some noticable effect. May be infinite. + @param cutoff The value at which the light intensity is considered negligible. */ + class Sphere effectSphere(float cutoff = 30.0f / 255) const; + + bool operator==(const GLight& other) const; + bool operator!=(const GLight& other) const; +}; + +} // namespace +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/GThread.h b/externals/g3dlite/G3D.lib/include/G3D/GThread.h new file mode 100644 index 00000000000..63f1c235bda --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/GThread.h @@ -0,0 +1,169 @@ +/** + @file GThread.h + + @created 2005-09-22 + @edited 2007-01-31 + + */ + +#ifndef G3D_GTHREAD_H +#define G3D_GTHREAD_H + +#include "G3D/platform.h" +#include "G3D/ReferenceCount.h" +#include <string> + +#ifndef G3D_WIN32 +# include <pthread.h> +# include <signal.h> +#endif + + +namespace G3D { + +typedef ReferenceCountedPointer<class GThread> GThreadRef; + +/** + Platform independent thread implementation. You can either subclass and + override GThread::threadMain or call the create method with a method. + + Beware of reference counting and threads. If circular references exist between + GThread subclasses then neither class will ever be deallocated. Also, + dropping all pointers (and causing deallocation) of a GThread does NOT + stop the underlying process. + + @sa G3D::GMutex, G3D::AtomicInt32 +*/ +class GThread : public ReferenceCountedObject { +private: + // "Status" is a reserved work on FreeBSD + enum GStatus {STATUS_CREATED, STATUS_STARTED, STATUS_RUNNING, STATUS_COMPLETED}; + + // Not implemented on purpose, don't use + GThread(const GThread &); + GThread& operator=(const GThread&); + bool operator==(const GThread&); + +#ifdef G3D_WIN32 + static DWORD WINAPI internalThreadProc(LPVOID param); +#else + static void* internalThreadProc(void* param); +#endif //G3D_WIN32 + + volatile GStatus m_status; + + // Thread handle to hold HANDLE and pthread_t +#ifdef G3D_WIN32 + HANDLE m_handle; + HANDLE m_event; +#else + pthread_t m_handle; +#endif //G3D_WIN32 + + std::string m_name; + +public: + + GThread(const std::string& name); + + virtual ~GThread(); + + /** Constructs a basic GThread without requiring a subclass. + + @param proc The global or static function for the threadMain() */ + static GThreadRef create(const std::string& name, void (*proc)(void*), void* param); + + /** @deprecated use overload that accepts void* param */ + static GThreadRef create(const std::string& name, void (*proc)()); + + /** Starts the thread and executes threadMain(). Returns false if + the thread failed to start (either because it was already started + or because the OS refused).*/ + bool start(); + + /** Terminates the thread without notifying or + waiting for a cancelation point. */ + void terminate(); + + /** + Returns true if threadMain is currently executing. This will + only be set when the thread is actually running and might not + be set when start() returns. */ + bool running() const; + + /** True after start() has been called, even through the thread + may have already completed(), or be currently running().*/ + bool started() const; + + /** Returns true if the thread has exited. */ + bool completed() const; + + /** Waits for the thread to finish executing. */ + void waitForCompletion(); + + /** Returns thread name */ + inline const std::string& name() { + return m_name; + } + + /** Overriden by the thread implementor */ + virtual void threadMain() = 0; +}; + + +/** + Mutual exclusion lock used for synchronization. + @sa G3D::GThread, G3D::AtomicInt32 +*/ +class GMutex { +private: +# ifdef G3D_WIN32 + CRITICAL_SECTION m_handle; +# else + pthread_mutex_t m_handle; +# endif + + // Not implemented on purpose, don't use + GMutex(const GMutex &mlock); + GMutex &operator=(const GMutex &); + bool operator==(const GMutex&); + +public: + GMutex(); + ~GMutex(); + + /** Locks the mutex or blocks until available. */ + void lock(); + + /** Unlocks the mutex. */ + void unlock(); +}; + + +/** + Automatically locks while in scope. +*/ +class GMutexLock { +private: + GMutex* m; + + // Not implemented on purpose, don't use + GMutexLock(const GMutexLock &mlock); + GMutexLock &operator=(const GMutexLock &); + bool operator==(const GMutexLock&); + +public: + GMutexLock(GMutex* mutex) { + m = mutex; + m->lock(); + } + + ~GMutexLock() { + m->unlock(); + } +}; + + +} // namespace G3D + +#endif //G3D_GTHREAD_H diff --git a/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h b/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h new file mode 100644 index 00000000000..44ec6e03405 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h @@ -0,0 +1,69 @@ +/** + @file GUniqueID.h + @author Morgan McGuire, morgan@cs.williams.edu + */ +#ifndef G3D_GUNIQUEID_H +#define G3D_GUNIQUEID_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Table.h" + +namespace G3D { + +/** Globally unique identifiers. The probability of two different + programs generating the same value from UniqueID::create is + vanishingly small. + + UniqueIDs optionally contain a 10-bit application specific tag + that distinguishes their type. +*/ +class GUniqueID { +private: + + uint64 id; + +public: + + GUniqueID() : id(0) {} + + bool uninitialized() const { + return id == 0; + } + + uint16 tag() const { + return id >> 54; + } + + operator uint64() const { + return id; + } + + bool operator==(const GUniqueID& other) const { + return id == other.id; + } + + bool operator!=(const GUniqueID& other) const { + return id != other.id; + } + + void serialize(class BinaryOutput& b) const; + + void deserialize(class BinaryInput& b); + + void serialize(class TextOutput& t) const; + + void deserialize(class TextInput& t); + + /** Create a new ID */ + static GUniqueID create(uint16 tag = 0); +}; + +} // G3D + +/** For Table and Set */ +template<> struct HashTrait<class G3D::GUniqueID> { + static size_t hashCode(G3D::GUniqueID id) { return (size_t)(G3D::uint64)id; } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h b/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h new file mode 100644 index 00000000000..702903ff09b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h @@ -0,0 +1,63 @@ +/** + @file HashTrait.h + + @maintainer Morgan McGuire, morgan@cs.williams.edu + @created 2008-10-01 + @edited 2008-10-01 + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_HASHTRAIT_H +#define G3D_HASHTRAIT_H + +#include "G3D/platform.h" +#include "G3D/Crypto.h" +#include "G3D/g3dmath.h" +#include "G3D/uint128.h" + +/** Must be specialized for custom types. + @see G3D::Table for specialization requirements. +*/ +template <typename T> struct HashTrait{}; + +template <typename T> struct HashTrait<T*> { + static size_t hashCode(const void* k) { return reinterpret_cast<size_t>(k); } +}; + +template <> struct HashTrait <int> { + static size_t hashCode(int k) { return static_cast<size_t>(k); } +}; + +template <> struct HashTrait <G3D::uint32> { + static size_t hashCode(G3D::uint32 k) { return static_cast<size_t>(k); } +}; + +template <> struct HashTrait <G3D::uint64> { + static size_t hashCode(G3D::uint64 k) { return static_cast<size_t>(k); } +}; + +template <> struct HashTrait <std::string> { + static size_t hashCode(const std::string& k) { return static_cast<size_t>(G3D::Crypto::crc32(k.c_str(), k.size())); } +}; + +template <> struct HashTrait<G3D::uint128> { + // Use the FNV-1 hash (http://isthe.com/chongo/tech/comp/fnv/#FNV-1). + static size_t hashCode(G3D::uint128 key) { + static const G3D::uint128 FNV_PRIME_128(1 << 24, 0x159); + static const G3D::uint128 FNV_OFFSET_128(0xCF470AAC6CB293D2LL, 0xF52F88BF32307F8FLL); + + G3D::uint128 hash = FNV_OFFSET_128; + G3D::uint128 mask(0, 0xFF); + for (int i = 0; i < 16; ++i) { + hash *= FNV_PRIME_128; + hash ^= (mask & key); + key >>= 8; + } + + G3D::uint64 foldedHash = hash.hi ^ hash.lo; + return static_cast<size_t>((foldedHash >> 32) ^ (foldedHash & 0xFFFFFFFF)); + } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image1.h b/externals/g3dlite/G3D.lib/include/G3D/Image1.h new file mode 100644 index 00000000000..f0850710d2c --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Image1.h @@ -0,0 +1,79 @@ +/** + @file Image1.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + + +#ifndef G3D_IMAGE1_H +#define G3D_IMAGE1_H + +#include "G3D/platform.h" +#include "G3D/Map2D.h" +#include "G3D/Color1.h" +#include "G3D/GImage.h" + +namespace G3D { + +typedef ReferenceCountedPointer<class Image1> Image1Ref; + +/** + Luminance image with 32-bit floating point storage. + + See also G3D::Image1uint8, G3D::GImage. + */ +class Image1 : public Map2D<Color1, Color1> { +public: + + typedef Image1 Type; + typedef Image1Ref Ref; + +protected: + + Image1(int w, int h, WrapMode wrap); + + void copyGImage(const class GImage& im); + void copyArray(const Color1* src, int w, int h); + void copyArray(const Color3* src, int w, int h); + void copyArray(const Color4* src, int w, int h); + void copyArray(const Color1uint8* src, int w, int h); + void copyArray(const Color3uint8* src, int w, int h); + void copyArray(const Color4uint8* src, int w, int h); + +public: + + const class ImageFormat* format() const; + + /** Creates an all-zero width x height image. */ + static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR); + + /** Creates a 0 x 0 image. */ + static Ref createEmpty(WrapMode wrap = WrapMode::ERROR); + + static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT); + + static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + + static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im); + + static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR); + + /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input, + it is stripped. */ + void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Saves in any of the formats supported by G3D::GImage. */ + void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); +}; + +} // G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h new file mode 100644 index 00000000000..7225ca35db8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h @@ -0,0 +1,80 @@ +/** + @file Image1uint8.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + +#ifndef G3D_IMAGE1UINT8_H +#define G3D_IMAGE1UINT8_H + +#include "G3D/platform.h" +#include "G3D/Map2D.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color1.h" +#include "G3D/GImage.h" + +namespace G3D { + +typedef ReferenceCountedPointer<class Image1uint8> Image1uint8Ref; + +/** + Compact storage for luminance 8-bit images. + + See also G3D::Image3, G3D::GImage + */ +class Image1uint8 : public Map2D<Color1uint8, Color1> { +public: + + typedef Image1uint8 Type; + typedef Image1uint8Ref Ref; + +protected: + + Image1uint8(int w, int h, WrapMode wrap); + + void copyGImage(const class GImage& im); + void copyArray(const Color1* src, int w, int h); + void copyArray(const Color3* src, int w, int h); + void copyArray(const Color4* src, int w, int h); + void copyArray(const Color1uint8* src, int w, int h); + void copyArray(const Color3uint8* src, int w, int h); + void copyArray(const Color4uint8* src, int w, int h); + +public: + + const class ImageFormat* format() const; + + /** Creates an all-zero width x height image. */ + static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR); + + /** Creates a 0 x 0 image. */ + static Ref createEmpty(WrapMode wrap = WrapMode::ERROR); + + static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT); + + static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR); + + static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + + static Ref fromImage1(const ReferenceCountedPointer<class Image1>& im); + static Ref fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im); + + /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input, + it is stripped. */ + void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Saves in any of the formats supported by G3D::GImage. */ + void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); +}; + +} // G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image3.h b/externals/g3dlite/G3D.lib/include/G3D/Image3.h new file mode 100644 index 00000000000..c67669c1c5e --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Image3.h @@ -0,0 +1,79 @@ +/** + @file Image3.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + + +#ifndef G3D_IMAGE3_H +#define G3D_IMAGE3_H + +#include "G3D/platform.h" +#include "G3D/Map2D.h" +#include "G3D/Color3.h" +#include "G3D/GImage.h" + +namespace G3D { + +typedef ReferenceCountedPointer<class Image3> Image3Ref; + +/** + RGB image with 32-bit floating point storage for each channel. + + See also G3D::Image3uint8, G3D::GImage. + */ +class Image3 : public Map2D<Color3, Color3> { +public: + + typedef Image3 Type; + typedef Image3Ref Ref; + +protected: + + Image3(int w, int h, WrapMode wrap); + + void copyGImage(const class GImage& im); + void copyArray(const Color1* src, int w, int h); + void copyArray(const Color3* src, int w, int h); + void copyArray(const Color4* src, int w, int h); + void copyArray(const Color1uint8* src, int w, int h); + void copyArray(const Color3uint8* src, int w, int h); + void copyArray(const Color4uint8* src, int w, int h); + +public: + + const class ImageFormat* format() const; + + /** Creates an all-zero width x height image. */ + static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR); + + /** Creates a 0 x 0 image. */ + static Ref createEmpty(WrapMode wrap = WrapMode::ERROR); + + static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT); + + static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + + static Ref fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im); + + static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR); + + /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input, + it is stripped. */ + void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Saves in any of the formats supported by G3D::GImage. */ + void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); +}; + +} // G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h new file mode 100644 index 00000000000..9ee1ef6678b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h @@ -0,0 +1,85 @@ +/** + @file Image3uint8.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + +#ifndef G3D_IMAGE3UINT8_H +#define G3D_IMAGE3UINT8_H + +#include "G3D/platform.h" +#include "G3D/Map2D.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color3.h" +#include "G3D/GImage.h" + +namespace G3D { + +typedef ReferenceCountedPointer<class Image3uint8> Image3uint8Ref; + +/** + Compact storage for RGB 8-bit per channel images. + + See also G3D::Image3, G3D::GImage + */ +class Image3uint8 : public Map2D<Color3uint8, Color3> { +public: + + typedef Image3uint8 Type; + typedef Image3uint8Ref Ref; + +protected: + + Image3uint8(int w, int h, WrapMode wrap); + + void copyGImage(const class GImage& im); + void copyArray(const Color1* src, int w, int h); + void copyArray(const Color3* src, int w, int h); + void copyArray(const Color4* src, int w, int h); + void copyArray(const Color1uint8* src, int w, int h); + void copyArray(const Color3uint8* src, int w, int h); + void copyArray(const Color4uint8* src, int w, int h); + +public: + + const class ImageFormat* format() const; + + /** Creates an all-zero width x height image. */ + static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR); + + + /** Creates a 0 x 0 image. */ + static Ref createEmpty(WrapMode wrap = WrapMode::ERROR); + + + static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT); + + static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR); + + static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + + static Ref fromImage3(const ReferenceCountedPointer<class Image3>& im); + static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im); + + /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input, + it is stripped. */ + void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Saves in any of the formats supported by G3D::GImage. */ + void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Extracts color channel 0 <= c <= 2 and returns it as a new monochrome image. */ + ReferenceCountedPointer<class Image1uint8> getChannel(int c) const; +}; + +} // G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image4.h b/externals/g3dlite/G3D.lib/include/G3D/Image4.h new file mode 100644 index 00000000000..04df43f9527 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Image4.h @@ -0,0 +1,80 @@ +/** + @file Image4.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + + +#ifndef G3D_IMAGE4_H +#define G3D_IMAGE4_H + +#include "G3D/platform.h" +#include "G3D/Map2D.h" +#include "G3D/Color4.h" +#include "G3D/GImage.h" + +namespace G3D { + +typedef ReferenceCountedPointer<class Image4> Image4Ref; + +/** + RGBA image with 32-bit floating point storage for each channel. + + Whenever a method needs to convert from RGB to ARGB, A=1 is assumed. + + See also G3D::Image4uint8, G3D::GImage. + */ +class Image4 : public Map2D<Color4, Color4> { +public: + + typedef Image4 Type; + typedef Image4Ref Ref; + +protected: + + Image4(int w, int h, WrapMode wrap); + + void copyGImage(const class GImage& im); + void copyArray(const Color1* src, int w, int h); + void copyArray(const Color3* src, int w, int h); + void copyArray(const Color4* src, int w, int h); + void copyArray(const Color1uint8* src, int w, int h); + void copyArray(const Color3uint8* src, int w, int h); + void copyArray(const Color4uint8* src, int w, int h); + +public: + + const class ImageFormat* format() const; + + /** Creates an all-zero width x height image. */ + static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR); + + /** Creates a 0 x 0 image. */ + static Ref createEmpty(WrapMode wrap = WrapMode::ERROR); + + static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT); + + static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + + static Ref fromImage4uint8(const ReferenceCountedPointer<class Image4uint8>& im); + + static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR); + + /** Loads from any of the file formats supported by G3D::GImage. */ + void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Saves in any of the formats supported by G3D::GImage. */ + void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); +}; + +} // G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h new file mode 100644 index 00000000000..11daf97c83c --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h @@ -0,0 +1,85 @@ +/** + @file Image4uint8.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + +#ifndef G3D_IMAGE4UINT8_H +#define G3D_IMAGE4UINT8_H + +#include "G3D/platform.h" +#include "G3D/Map2D.h" +#include "G3D/Color4uint8.h" +#include "G3D/Color4.h" +#include "G3D/GImage.h" +#include "G3D/Image1uint8.h" + +namespace G3D { + +typedef ReferenceCountedPointer<class Image4uint8> Image4uint8Ref; + +/** + Compact storage for RGBA 8-bit per channel images. + + See also G3D::Image4, G3D::GImage + */ +class Image4uint8 : public Map2D<Color4uint8, Color4> { +public: + + typedef Image4uint8 Type; + typedef Image4uint8Ref Ref; + +protected: + + Image4uint8(int w, int h, WrapMode wrap); + + void copyGImage(const class GImage& im); + void copyArray(const Color1* src, int w, int h); + void copyArray(const Color3* src, int w, int h); + void copyArray(const Color4* src, int w, int h); + void copyArray(const Color1uint8* src, int w, int h); + void copyArray(const Color3uint8* src, int w, int h); + void copyArray(const Color4uint8* src, int w, int h); + +public: + + const class ImageFormat* format() const; + + /** Creates an all-zero width x height image. */ + static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR); + + + /** Creates a 0 x 0 image. */ + static Ref createEmpty(WrapMode wrap = WrapMode::ERROR); + + + static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT); + + static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR); + + static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR); + + static Ref fromImage4(const ReferenceCountedPointer<class Image4>& im); + + /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input, + it is stripped. */ + void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Saves in any of the formats supported by G3D::GImage. */ + void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT); + + /** Extracts color channel 0 <= c <= 3 and returns it as a new monochrome image. */ + ReferenceCountedPointer<class Image1uint8> getChannel(int c) const; +}; + +} // G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h b/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h new file mode 100644 index 00000000000..a1334fb1a5a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h @@ -0,0 +1,362 @@ +/** + @file ImageFormat.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-05-23 + @edited 2008-07-17 +*/ + +#ifndef GLG3D_ImageFormat_H +#define GLG3D_ImageFormat_H + +#include "G3D/platform.h" +#include "G3D/Table.h" +#include "G3D/enumclass.h" + +namespace G3D { + +/** Information about common image formats. + Don't construct these; use the methods provided. + + For most formats, the number indicates the number of bits per channel and a suffix of "F" indicates + floating point. This does not hold for the YUV and DXT formats.*/ +class ImageFormat { +public: + + // Must update ImageFormat::name() when this enum changes. + enum Code { + CODE_NONE = -1, + CODE_L8, + CODE_L16, + CODE_L16F, + CODE_L32F, + + CODE_A8, + CODE_A16, + CODE_A16F, + CODE_A32F, + + CODE_LA4, + CODE_LA8, + CODE_LA16, + CODE_LA16F, + CODE_LA32F, + + CODE_RGB5, + CODE_RGB5A1, + CODE_RGB8, + CODE_RGB10, + CODE_RGB10A2, + CODE_RGB16, + CODE_RGB16F, + CODE_RGB32F, + + CODE_ARGB8, + CODE_BGR8, + + CODE_RGBA8, + CODE_RGBA16, + CODE_RGBA16F, + CODE_RGBA32F, + + CODE_BAYER_RGGB8, + CODE_BAYER_GRBG8, + CODE_BAYER_GBRG8, + CODE_BAYER_BGGR8, + CODE_BAYER_RGGB32F, + CODE_BAYER_GRBG32F, + CODE_BAYER_GBRG32F, + CODE_BAYER_BGGR32F, + + CODE_HSV8, + CODE_HSV32F, + + CODE_YUV420_PLANAR, + CODE_YUV422, + CODE_YUV444, + + CODE_RGB_DXT1, + CODE_RGBA_DXT1, + CODE_RGBA_DXT3, + CODE_RGBA_DXT5, + + CODE_DEPTH16, + CODE_DEPTH24, + CODE_DEPTH32, + CODE_DEPTH32F, + + CODE_STENCIL1, + CODE_STENCIL4, + CODE_STENCIL8, + CODE_STENCIL16, + + CODE_DEPTH24_STENCIL8, + + CODE_NUM + }; + + enum ColorSpace { + COLOR_SPACE_NONE, + COLOR_SPACE_RGB, + COLOR_SPACE_HSV, + COLOR_SPACE_YUV + }; + + enum BayerPattern { + BAYER_PATTERN_NONE, + BAYER_PATTERN_RGGB, + BAYER_PATTERN_GRBG, + BAYER_PATTERN_GBRG, + BAYER_PATTERN_BGGR + }; + + /** Number of channels (1 for a depth texture). */ + int numComponents; + bool compressed; + + /** Useful for serializing. */ + Code code; + + ColorSpace colorSpace; + + /** If this is a Bayer format, what is the pattern. */ + BayerPattern bayerPattern; + + /** The OpenGL format equivalent to this one, e.g, GL_RGB8 Zero if there is no equivalent. This is actually a GLenum */ + int openGLFormat; + + /** The OpenGL base format equivalent to this one (e.g., GL_RGB, GL_ALPHA). Zero if there is no equivalent. */ + int openGLBaseFormat; + + int luminanceBits; + + /** Number of bits per pixel storage for alpha values; Zero for compressed textures and non-RGB. */ + int alphaBits; + + /** Number of bits per pixel storage for red values; Zero for compressed textures and non-RGB. */ + int redBits; + + /** Number of bits per pixel storage for green values; Zero for compressed textures and non-RGB. */ + int greenBits; + + /** Number of bits per pixel storage for blue values; Zero for compressed textures and non-RGB. */ + int blueBits; + + /** Number of bits per pixel */ + int stencilBits; + + /** Number of depth bits (for depth textures; e.g. shadow maps) */ + int depthBits; + + /** Amount of CPU memory per pixel when packed into an array, discounting any end-of-row padding. */ + int cpuBitsPerPixel; + + /** Amount of CPU memory per pixel when packed into an array, discounting any end-of-row padding. @deprecated Use cpuBitsPerPixel*/ + int packedBitsPerTexel; + + /** + Amount of GPU memory per pixel on most graphics cards, for formats supported by OpenGL. This is + only an estimate--the actual amount of memory may be different on your actual card. + + This may be greater than the sum of the per-channel bits + because graphics cards need to pad to the nearest 1, 2, or + 4 bytes. + */ + int openGLBitsPerPixel; + + /** @deprecated Use openGLBitsPerPixel */ + int hardwareBitsPerTexel; + + /** The OpenGL bytes format of the data buffer used with this texture format, e.g., GL_UNSIGNED_BYTE */ + int openGLDataFormat; + + /** True if there is no alpha channel for this texture. */ + bool opaque; + + /** True if the bit depths specified are for float formats. */ + bool floatingPoint; + + /** Human readable name of this texture.*/ + std::string name() const; + +private: + + ImageFormat( + int numComponents, + bool compressed, + int glFormat, + int glBaseFormat, + int luminanceBits, + int alphaBits, + int redBits, + int greenBits, + int blueBits, + int depthBits, + int stencilBits, + int hardwareBitsPerTexel, + int packedBitsPerTexel, + int glDataFormat, + bool opaque, + bool floatingPoint, + Code code, + ColorSpace colorSpace, + BayerPattern bayerPattern = BAYER_PATTERN_NONE); + +public: + + static const ImageFormat* L8(); + + static const ImageFormat* L16(); + + static const ImageFormat* L16F(); + + static const ImageFormat* L32F(); + + static const ImageFormat* A8(); + + static const ImageFormat* A16(); + + static const ImageFormat* A16F(); + + static const ImageFormat* A32F(); + + static const ImageFormat* LA4(); + + static const ImageFormat* LA8(); + + static const ImageFormat* LA16(); + + static const ImageFormat* LA16F(); + + static const ImageFormat* LA32F(); + + static const ImageFormat* BGR8(); + + static const ImageFormat* RGB5(); + + static const ImageFormat* RGB5A1(); + + static const ImageFormat* RGB8(); + + static const ImageFormat* RGB10(); + + static const ImageFormat* RGB10A2(); + + static const ImageFormat* RGB16(); + + static const ImageFormat* RGB16F(); + + static const ImageFormat* RGB32F(); + + static const ImageFormat* RGBA8(); + + static const ImageFormat* RGBA16(); + + static const ImageFormat* RGBA16F(); + + static const ImageFormat* RGBA32F(); + + static const ImageFormat* RGB_DXT1(); + + static const ImageFormat* RGBA_DXT1(); + + static const ImageFormat* RGBA_DXT3(); + + static const ImageFormat* RGBA_DXT5(); + + static const ImageFormat* DEPTH16(); + + static const ImageFormat* DEPTH24(); + + static const ImageFormat* DEPTH32(); + + static const ImageFormat* DEPTH32F(); + + static const ImageFormat* STENCIL1(); + + static const ImageFormat* STENCIL4(); + + static const ImageFormat* STENCIL8(); + + static const ImageFormat* STENCIL16(); + + static const ImageFormat* DEPTH24_STENCIL8(); + + static const ImageFormat* YUV420_PLANAR(); + + static const ImageFormat* YUV422(); + + static const ImageFormat* YUV444(); + + /** + NULL pointer; indicates that the G3D::Texture class should choose + either RGBA8 or RGB8 depending on the presence of an alpha channel + in the input. + */ + static const ImageFormat* AUTO() { return NULL; } + + /** Returns DEPTH16, DEPTH24, or DEPTH32 according to the bits + specified. You can use "glGetInteger(GL_DEPTH_BITS)" to match + the screen's format.*/ + static const ImageFormat* depth(int depthBits = 24); + + /** Returns STENCIL1, STENCIL4, STENCIL8 or STENCIL16 according to the bits + specified. You can use "glGetInteger(GL_STENCIL_BITS)" to match + the screen's format.*/ + static const ImageFormat* stencil(int bits = 8); + + /** Returns the matching ImageFormat* identified by the Code. May return NULL + if this format's code is reserved but not yet implemented by G3D. */ + static const ImageFormat* fromCode(ImageFormat::Code code); + + + + /** For use with ImageFormat::convert. */ + class BayerAlgorithm { + public: + enum Value { + NEAREST, + BILINEAR, + mhc, + HIGH_QUALITY = mhc + }; + private: + + Value value; + + public: + + G3D_DECLARE_ENUM_CLASS_METHODS(BayerAlgorithm); + }; + + /** Converts between arbitrary formats on the CPU. Not all format conversions are supported or directly supported. + Formats without direct conversions will attempt to convert through RGBA first. + + A conversion routine might only support source or destination padding or y inversion or none. + If support is needed and not available in any of the direct conversion routines, then no conversion is done. + + YUV422 expects data in YUY2 format (Y, U, Y2, v). Most YUV formats require width and heights that are multiples of 2. + + Returns true if a conversion was available, false if none occurred. + */ + static bool convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, + const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, + bool invertY = false, BayerAlgorithm bayerAlg = BayerAlgorithm::HIGH_QUALITY); + + /* Checks if a conversion between two formats is available. */ + static bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false); +}; + +typedef ImageFormat TextureFormat; + +} + +template <> +struct HashTrait<const G3D::ImageFormat*> { + static size_t hashCode(const G3D::ImageFormat* key) { return reinterpret_cast<size_t>(key); } +}; + + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Line.h b/externals/g3dlite/G3D.lib/include/G3D/Line.h new file mode 100644 index 00000000000..ff6dc8d08e7 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Line.h @@ -0,0 +1,105 @@ +/** + @file Line.h + + Line class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-06-02 + @edited 2006-02-28 + */ + +#ifndef G3D_LINE_H +#define G3D_LINE_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" + +namespace G3D { + +class Plane; + +/** + An infinite 3D line. + */ +class Line { +protected: + + Vector3 _point; + Vector3 _direction; + + Line(const Vector3& point, const Vector3& direction) { + _point = point; + _direction = direction.direction(); + } + +public: + + /** Undefined (provided for creating Array<Line> only) */ + inline Line() {} + + Line(class BinaryInput& b); + + void serialize(class BinaryOutput& b) const; + + void deserialize(class BinaryInput& b); + + virtual ~Line() {} + + /** + Constructs a line from two (not equal) points. + */ + static Line fromTwoPoints(const Vector3 &point1, const Vector3 &point2) { + return Line(point1, point2 - point1); + } + + /** + Creates a line from a point and a (nonzero) direction. + */ + static Line fromPointAndDirection(const Vector3& point, const Vector3& direction) { + return Line(point, direction); + } + + /** + Returns the closest point on the line to point. + */ + Vector3 closestPoint(const Vector3& pt) const; + + /** + Returns the distance between point and the line + */ + double distance(const Vector3& point) const { + return (closestPoint(point) - point).magnitude(); + } + + /** Returns a point on the line */ + Vector3 point() const; + + /** Returns the direction (or negative direction) of the line */ + Vector3 direction() const; + + /** + Returns the point where the line and plane intersect. If there + is no intersection, returns a point at infinity. + */ + Vector3 intersection(const Plane &plane) const; + + + /** Finds the closest point to the two lines. + + @param minDist Returns the minimum distance between the lines. + + @cite http://objectmix.com/graphics/133793-coordinates-closest-points-pair-skew-lines.html + */ + Vector3 closestPoint(const Line& B, float& minDist) const; + + inline Vector3 closestPoint(const Line& B) const { + float m; + return closestPoint(B, m); + } +}; + +};// namespace + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h b/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h new file mode 100644 index 00000000000..092b0c9ca6d --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h @@ -0,0 +1,115 @@ +/** + @file LineSegment.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-08 + @edited 2008-02-02 + */ + +#ifndef G3D_LINESEGMENT_H +#define G3D_LINESEGMENT_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" + +namespace G3D { + +/** + An finite segment of an infinite 3D line. + */ +class LineSegment { +protected: + + Vector3 _point; + + /** Not normalized */ + Vector3 direction; + + LineSegment(const Vector3& __point, const Vector3& _direction) : _point(__point), direction(_direction) { + } + +public: + + inline LineSegment() : _point(Vector3::zero()), direction(Vector3::zero()) {} + + LineSegment(class BinaryInput& b); + + void serialize(class BinaryOutput& b) const; + + void deserialize(class BinaryInput& b); + + virtual ~LineSegment() {} + + /** + * Constructs a line from two (not equal) points. + */ + static LineSegment fromTwoPoints(const Vector3 &point1, const Vector3 &point2) { + return LineSegment(point1, point2 - point1); + } + + /** Call with 0 or 1 */ + Vector3 point(int i) const; + + inline float length() const { + return direction.magnitude(); + } + + /** + * Returns the closest point on the line segment to point. + */ + Vector3 closestPoint(const Vector3 &point) const; + + /** + Returns the distance between point and the line + */ + double distance(const Vector3& p) const { + return (closestPoint(p) - p).magnitude(); + } + + double distanceSquared(const Vector3& p) const { + return (closestPoint(p) - p).squaredMagnitude(); + } + + /** Returns true if some part of this segment is inside the sphere */ + bool intersectsSolidSphere(const class Sphere& s) const; + + Vector3 randomPoint() const; + +}; + + +class LineSegment2D { +private: + + Vector2 m_origin; + + /** Not normalized */ + Vector2 m_direction; + + /** Length of m_direction */ + float m_length; + +public: + + LineSegment2D() {} + + static LineSegment2D fromTwoPoints(const Vector2& p0, const Vector2& p1); + + /** Returns the intersection of these segements (including + testing endpoints), or Vector2::inf() if they do not intersect. */ + Vector2 intersection(const LineSegment2D& other) const; + + Vector2 point(int i) const; + + Vector2 closestPoint(const Vector2& Q) const; + + float distance(const Vector2& p) const; + + float length() const; +}; + +} // namespace + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Log.h b/externals/g3dlite/G3D.lib/include/G3D/Log.h new file mode 100644 index 00000000000..19b4b65c82d --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Log.h @@ -0,0 +1,109 @@ +/** + @file Log.h + + @maintainer Morgan McGuire, morgan@graphics3d.com + @cite Backtrace by Aaron Orenstein + @created 2001-08-04 + @edited 2005-11-04 + */ + +#ifndef G3D_LOG_H +#define G3D_LOG_H + +#include <stdio.h> +#include <string> +#include "G3D/platform.h" + +#ifndef G3D_WIN32 + #include <stdarg.h> +#endif + +namespace G3D { + +/** Prints to the common system log, log.txt, which is usually + in the working directory of the program. If your disk is + not writable or is slow, it will attempt to write to "c:/tmp/log.txt" or + "c:/temp/log.txt" on Windows systems instead. + + Unlike printf or debugPrintf, + this function guarantees that all output is committed before it returns. + This is very useful for debugging a crash, which might hide the last few + buffered print statements otherwise. + + Many G3D routines write useful warnings and debugging information to the + system log, which makes it a good first place to go when tracking down + a problem. + */ +void logPrintf(const char* fmt, ...); + +/** + System log for debugging purposes. The first log opened + is the "common log" and can be accessed with the static + method common(). If you access common() and a common log + does not yet exist, one is created for you. + */ +class Log { +private: + + /** + Log messages go here. + */ + FILE* logFile; + + std::string filename; + + static Log* commonLog; + + int stripFromStackBottom; + + /** + Prints the time & stack trace. + */ + void printHeader(); + +public: + + /** + @param stripFromStackBottom Number of call stacks to strip from the + bottom of the stack when printing a trace. Useful for hiding + routines like "main" and "WinMain". If the specified file cannot + be opened for some reason, tries to open "c:/tmp/log.txt" or + "c:/temp/log.txt" instead. + */ + Log(const std::string& filename = "log.txt", + int stripFromStackBottom = 0); + + virtual ~Log(); + + /** + Returns the handle to the file log. + */ + FILE* getFile() const; + + /** + Marks the beginning of a logfile section. + */ + void section(const std::string& s); + + /** + Given arguments like printf, writes characters to the debug text overlay. + */ + // We want G3D_CHECK_PRINTF_ARGS here, but that conflicts with the + // overload. + void __cdecl printf(const char* fmt, ...) G3D_CHECK_PRINTF_METHOD_ARGS; + + void __cdecl vprintf(const char*, va_list argPtr) G3D_CHECK_VPRINTF_METHOD_ARGS; + + static Log* common(); + + static std::string getCommonLogFilename(); + + void print(const std::string& s); + + + void println(const std::string& s); +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Map2D.h b/externals/g3dlite/G3D.lib/include/G3D/Map2D.h new file mode 100644 index 00000000000..48e2e957a5f --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Map2D.h @@ -0,0 +1,665 @@ +/** + @file Map2D.h + + More flexible support than provided by G3D::GImage. + + @maintainer Morgan McGuire, morgan@cs.brown.edu + @created 2004-10-10 + @edited 2007-07-18 + */ +#ifndef G3D_MAP2D_H +#define G3D_MAP2D_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Array.h" +#include "G3D/Vector2int16.h" +#include "G3D/ReferenceCount.h" +#include "G3D/AtomicInt32.h" +#include "G3D/GThread.h" +#include "G3D/Rect2D.h" +#include "G3D/WrapMode.h" + +#include <string> + +namespace G3D { +namespace _internal { + +/** The default compute type for a type is the type itself. */ +template<typename Storage> class _GetComputeType { +public: + typedef Storage Type; +}; + +} // _internal +} // G3D + +// This weird syntax is needed to support VC6, which doesn't +// properly implement template overloading. +#define DECLARE_COMPUTE_TYPE(StorageType, ComputeType) \ +namespace G3D { \ + namespace _internal { \ + template<> class _GetComputeType < StorageType > { \ + public: \ + typedef ComputeType Type; \ + }; \ + } \ +} + +DECLARE_COMPUTE_TYPE( float32, float64) +DECLARE_COMPUTE_TYPE( float64, float64) + +DECLARE_COMPUTE_TYPE( int8, float32) +DECLARE_COMPUTE_TYPE( int16, float32) +DECLARE_COMPUTE_TYPE( int32, float64) +DECLARE_COMPUTE_TYPE( int64, float64) + +DECLARE_COMPUTE_TYPE( uint8, float32) +DECLARE_COMPUTE_TYPE( uint16, float32) +DECLARE_COMPUTE_TYPE( uint32, float64) +DECLARE_COMPUTE_TYPE( uint64, float64) + +DECLARE_COMPUTE_TYPE( Vector2, Vector2) +DECLARE_COMPUTE_TYPE( Vector2int16, Vector2) + +DECLARE_COMPUTE_TYPE( Vector3, Vector3) +DECLARE_COMPUTE_TYPE( Vector3int16, Vector3) + +DECLARE_COMPUTE_TYPE( Vector4, Vector4) + +DECLARE_COMPUTE_TYPE( Color3, Color3) +DECLARE_COMPUTE_TYPE( Color3uint8, Color3) + +DECLARE_COMPUTE_TYPE( Color4, Color4) +DECLARE_COMPUTE_TYPE( Color4uint8, Color4) +#undef DECLARE_COMPUTE_TYPE + +namespace G3D { + +/** + Map of values across a discrete 2D plane. Can be thought of as a generic class for 2D images, + allowing flexibility as to pixel format and convenient methods. + In fact, the "pixels" can be any values + on a grid that can be sensibly interpolated--RGB colors, scalars, 4D vectors, and so on. + + Other "image" classes in G3D: + + G3D::GImage - Supports file formats, fast, Color3uint8 and Color4uint8 formats. No interpolation. + + G3D::Texture::Ref - Represents image on the graphics card (not directly readable on the CPU). Supports 2D, 3D, and a variety of interpolation methods, loads file formats. + + G3D::Image3 - A subclass of Map2D<Color3> that supports image loading and saving and conversion to Texture. + + G3D::Image4 - A subclass of Map2D<Color4> that supports image loading and saving and conversion to Texture. + + G3D::Image3uint8 - A subclass of Map2D<Color3uint8> that supports image loading and saving and conversion to Texture. + + G3D::Image4uint8 - A subclass of Map2D<Color4uint8> that supports image loading and saving and conversion to Texture. + + There are two type parameters-- the first (@ Storage) is the type + used to store the "pixel" values efficiently and + the second (@a Compute) is + the type operated on by computation. The Compute::Compute(Storage&) constructor + is used to convert between storage and computation types. + @a Storage is often an integer version of @a Compute, for example + <code>Map2D<double, uint8></code>. By default, the computation type is: + + <pre> + Storage Computation + + uint8 float32 + uint16 float32 + uint32 float64 + uint64 float64 + + int8 float32 + int16 float32 + int32 float64 + int64 float64 + + float32 float64 + float64 float64 + + Vector2 Vector2 + Vector2int16 Vector2 + + Vector3 Vector3 + Vector3int16 Vector3 + + Vector4 Vector4 + + Color3 Color3 + Color3uint8 Color3 + + Color4 Color4 + Color4uint8 Color4 + </pre> + Any other storage type defaults to itself as the computation type. + + The computation type can be any that + supports lerp, +, -, *, /, and an empty constructor. + + Assign value: + + <code>im->set(x, y, 7);</code> or + <code>im->get(x, y) = 7;</code> + + Read value: + + <code>int c = im(x, y);</code> + + Can also sample with nearest neighbor, bilinear, and bicubic + interpolation. + + Sampling follows OpenGL conventions, where + pixel values represent grid points and (0.5, 0.5) is half-way + between two vertical and two horizontal grid points. + To draw an image of dimensions w x h with nearest neighbor + sampling, render pixels from [0, 0] to [w - 1, h - 1]. + + Under the WrapMode::CLAMP wrap mode, the value of bilinear interpolation + becomes constant outside [1, w - 2] horizontally. Nearest neighbor + interpolation is constant outside [0, w - 1] and bicubic outside + [3, w - 4]. The class does not offer quadratic interpolation because + the interpolation filter could not center over a pixel. + + @author Morgan McGuire, morgan@cs.williams.edu + */ +template< typename Storage, +typename Compute = typename G3D::_internal::_GetComputeType<Storage>::Type> +class Map2D : public ReferenceCountedObject { + +// +// It doesn't make sense to automatically convert from Compute back to Storage +// because the rounding rule (and scaling) is application dependent. +// Thus the interpolation methods all return type Compute. +// + +public: + + typedef Storage StorageType; + typedef Compute ComputeType; + typedef Map2D<Storage, Compute> Type; + typedef ReferenceCountedPointer<Map2D> Ref; + +protected: + + Storage ZERO; + + /** Width, in pixels. */ + uint32 w; + + /** Height, in pixels. */ + uint32 h; + + WrapMode _wrapMode; + + /** 0 if no mutating method has been invoked + since the last call to setChanged(); */ + AtomicInt32 m_changed; + + Array<Storage> data; + + /** Handles the exceptional cases from get */ + const Storage& slowGet(int x, int y, WrapMode wrap) { + switch (wrap) { + case WrapMode::CLAMP: + return fastGet(iClamp(x, 0, w - 1), iClamp(y, 0, h - 1)); + + case WrapMode::TILE: + return fastGet(iWrap(x, w), iWrap(y, h)); + + case WrapMode::ZERO: + return ZERO; + + case WrapMode::ERROR: + alwaysAssertM(((uint32)x < w) && ((uint32)y < h), + format("Index out of bounds: (%d, %d), w = %d, h = %d", + x, y, w, h)); + + // intentionally fall through + case WrapMode::IGNORE: + // intentionally fall through + default: + { + static Storage temp; + return temp; + } + } + } + +public: + + /** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */ + inline const Storage& fastGet(int x, int y) const { + debugAssert(((uint32)x < w) && ((uint32)y < h)); + return data[x + y * w]; + } + + /** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */ + inline void fastSet(int x, int y, const Storage& v) { + debugAssert(((uint32)x < w) && ((uint32)y < h)); + data[x + y * w] = v; + } + +protected: + + /** Given four control points and a value on the range [0, 1) + evaluates the Catmull-rom spline between the times of the + middle two control points */ + Compute bicubic(const Compute* ctrl, double s) const { + + // f = B * S * ctrl' + + // B matrix: Catmull-Rom spline basis + static const double B[4][4] = { + { 0.0, -0.5, 1.0, -0.5}, + { 1.0, 0.0, -2.5, 1.5}, + { 0.0, 0.5, 2.0, -1.5}, + { 0.0, 0.0, -0.5, 0.5}}; + + // S: Powers of the fraction + double S[4]; + double s2 = s * s; + S[0] = 1.0; + S[1] = s; + S[2] = s2; + S[3] = s2 * s; + + Compute sum(ZERO); + + for (int c = 0; c < 4; ++c) { + double coeff = 0.0; + for (int power = 0; power < 4; ++power) { + coeff += B[c][power] * S[power]; + } + sum += ctrl[c] * coeff; + } + + return sum; + } + + + Map2D(int w, int h, WrapMode wrap) : w(0), h(0), _wrapMode(wrap), m_changed(1) { + ZERO = Storage(Compute(Storage()) * 0); + resize(w, h); + } + +public: + + /** + Although Map2D is not threadsafe (except for the setChanged() method), + you can use this mutex to create your own threadsafe access to a Map2D. + Not used by the default implementation. + */ + GMutex mutex; + + static Ref create(int w = 0, int h = 0, WrapMode wrap = WrapMode::ERROR) { + return new Map2D(w, h, wrap); + } + + /** Resizes without clearing, leaving garbage. + */ + void resize(uint32 newW, uint32 newH) { + if ((newW != w) || (newH != h)) { + w = newW; + h = newH; + data.resize(w * h); + setChanged(true); + } + } + + /** + Returns true if this map has been written to since the last call to setChanged(false). + This is useful if you are caching a texture map other value that must be recomputed + whenever this changes. + */ + bool changed() { + return m_changed.value() != 0; + } + + /** Set/unset the changed flag. */ + void setChanged(bool c) { + m_changed = c ? 1 : 0; + } + + /** Returns a pointer to the underlying row-major data. There is no padding at the end of the row. + Be careful--this will be reallocated during a resize. You should call setChanged(true) if you mutate the array.*/ + Storage* getCArray() { + return data.getCArray(); + } + + + const Storage* getCArray() const { + return data.getCArray(); + } + + + /** Row-major array. You should call setChanged(true) if you mutate the array. */ + Array<Storage>& getArray() { + return data; + } + + + const Array<Storage>& getArray() const { + return data; + } + + /** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */ + inline bool inBounds(int x, int y) const { + return (((uint32)x < w) && ((uint32)y < h)); + } + + /** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */ + inline bool inBounds(const Vector2int16& v) const { + return inBounds(v.x, v.y); + } + + /** Get the value at (x, y). + + Note that the type of image->get(x, y) is + the storage type, not the computation + type. If the constructor promoting Storage to Compute rescales values + (as, for example Color3(Color3uint8&) does), this will not match the value + returned by Map2D::nearest. + */ + inline const Storage& get(int x, int y, WrapMode wrap) const { + if (((uint32)x < w) && ((uint32)y < h)) { + return data[x + y * w]; + } else { + // Remove the const to allow a slowGet on this object + // (we're returning a const reference so this is ok) + return const_cast<Type*>(this)->slowGet(x, y, wrap); + } +# ifndef G3D_WIN32 + // gcc gives a useless warning that the above code might reach the end of the function; + // we use this line to supress the warning. + return ZERO; +# endif + } + + inline const Storage& get(int x, int y) const { + return get(x, y, _wrapMode); + } + + inline const Storage& get(const Vector2int16& p) const { + return get(p.x, p.y, _wrapMode); + } + + inline const Storage& get(const Vector2int16& p, WrapMode wrap) const { + return get(p.x, p.y, wrap); + } + + inline Storage& get(int x, int y, WrapMode wrap) { + return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y, wrap)); +# ifndef G3D_WIN32 + // gcc gives a useless warning that the above code might reach the end of the function; + // we use this line to supress the warning. + return ZERO; +# endif + } + + inline Storage& get(int x, int y) { + return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y)); +# ifndef G3D_WIN32 + // gcc gives a useless warning that the above code might reach the end of the function; + // we use this line to supress the warning. + return ZERO; +# endif + } + + inline Storage& get(const Vector2int16& p) { + return get(p.x, p.y); + } + + /** Sets the changed flag to true */ + inline void set(const Vector2int16& p, const Storage& v) { + set(p.x, p.y, v); + } + + /** Sets the changed flag to true */ + void set(int x, int y, const Storage& v, WrapMode wrap) { + setChanged(true); + if (((uint32)x < w) && ((uint32)y < h)) { + // In bounds, wrapping isn't an issue. + data[x + y * w] = v; + } else { + const_cast<Storage&>(slowGet(x, y, wrap)) = v; + } + } + + void set(int x, int y, const Storage& v) { + set(x, y, v, _wrapMode); + } + + + void setAll(const Storage& v) { + for(int i = 0; i < data.size(); ++i) { + data[i] = v; + } + setChanged(true); + } + + /** flips if @a flip is true*/ + void maybeFlipVertical(bool flip) { + if (flip) { + flipVertical(); + } + } + + virtual void flipVertical() { + int halfHeight = h/2; + Storage* d = data.getCArray(); + for (int y = 0; y < halfHeight; ++y) { + int o1 = y * w; + int o2 = (h - y - 1) * w; + for (int x = 0; x < (int)w; ++x) { + int i1 = o1 + x; + int i2 = o2 + x; + Storage temp = d[i1]; + d[i1] = d[i2]; + d[i2] = temp; + } + } + setChanged(true); + } + + virtual void flipHorizontal() { + int halfWidth = w / 2; + Storage* d = data.getCArray(); + for (int x = 0; x < halfWidth; ++x) { + for (int y = 0; y < (int)h; ++y) { + int i1 = y * w + x; + int i2 = y * w + (w - x - 1); + Storage temp = d[i1]; + d[i1] = d[i2]; + d[i2] = temp; + } + } + setChanged(true); + } + + /** + Crops this map so that it only contains pixels between (x, y) and (x + w - 1, y + h - 1) inclusive. + */ + virtual void crop(int newX, int newY, int newW, int newH) { + alwaysAssertM(newX + newW <= (int)w, "Cannot grow when cropping"); + alwaysAssertM(newY + newH <= (int)h, "Cannot grow when cropping"); + alwaysAssertM(newX >= 0 && newY >= 0, "Origin out of bounds."); + + // Always safe to copy towards the upper left, provided + // that we're iterating towards the lower right. This lets us avoid + // reallocating the underlying array. + for (int y = 0; y < newH; ++y) { + for (int x = 0; x < newW; ++x) { + data[x + y * newW] = data[(x + newX) + (y + newY) * w]; + } + } + + resize(newW, newH); + } + + /** iRounds to the nearest x0 and y0. */ + virtual void crop(const Rect2D& rect) { + crop(iRound(rect.x0()), iRound(rect.y0()), iRound(rect.x1()) - iRound(rect.x0()), iRound(rect.y1()) - iRound(rect.y0())); + } + + /** Returns the nearest neighbor. Pixel values are considered + to be at the upper left corner, so <code>image->nearest(x, y) == image(x, y)</code> + */ + inline Compute nearest(float x, float y, WrapMode wrap) const { + return Compute(get(iRound(x), iRound(y), wrap)); + } + + inline Compute nearest(float x, float y) const { + return nearest(x, y, _wrapMode); + } + + inline Compute nearest(const Vector2& p) const { + return nearest(p.x, p.y); + } + + /** Returns the average value of all elements of the map */ + Compute average() const { + if ((w == 0) || (h == 0)) { + return ZERO; + } + + // To avoid overflows, compute the average of row averages + + Compute rowSum = ZERO; + for (unsigned int y = 0; y < h; ++y) { + Compute sum = ZERO; + int offset = y * w; + for (unsigned int x = 0; x < w; ++x) { + sum += Compute(data[offset + x]); + } + rowSum += sum * (1.0f / w); + } + + return rowSum * (1.0f / h); + } + + /** + Needs to access elements from (floor(x), floor(y)) + to (floor(x) + 1, floor(y) + 1) and will use + the wrap mode appropriately (possibly generating + out of bounds errors). + + Guaranteed to match nearest(x, y) at integers. */ + Compute bilinear(float x, float y, WrapMode wrap) const { + int i = iFloor(x); + int j = iFloor(y); + + float fX = x - i; + float fY = y - j; + + // Horizontal interpolation, first row + Compute t0(get(i, j, wrap)); + Compute t1(get(i + 1, j, wrap)); + Compute A = lerp(t0, t1, fX); + + // Horizontal interpolation, second row + Compute t2(get(i, j + 1, wrap)); + Compute t3(get(i + 1, j + 1, wrap)); + Compute B = lerp(t2, t3, fX); + + // Vertical interpolation + return lerp(A, B, fY); + } + + Compute bilinear(float x, float y) const { + return bilinear(x, y, _wrapMode); + } + + inline Compute bilinear(const Vector2& p) const { + return bilinear(p.x, p.y, _wrapMode); + } + + inline Compute bilinear(const Vector2& p, WrapMode wrap) const { + return bilinear(p.x, p.y, wrap); + } + + /** + Uses Catmull-Rom splines to interpolate between grid + values. Guaranteed to match nearest(x, y) at integers. + */ + Compute bicubic(float x, float y, WrapMode wrap) const { + int i = iFloor(x); + int j = iFloor(y); + float fX = x - i; + float fY = y - j; + + // 'static' prevents constructors from being called + // every time through this loop. + static Compute vsample[4]; + for (int v = 0; v < 4; ++v) { + + // Horizontal interpolation + static Compute hsample[4]; + for (int u = 0; u < 4; ++u) { + hsample[u] = Compute(get(i + u - 1, j + v - 1, wrap)); + } + + vsample[v] = bicubic(hsample, fX); + } + + // Vertical interpolation + return bicubic(vsample, fY); + } + + Compute bicubic(float x, float y) const { + return bicubic(x, y, _wrapMode); + } + + inline Compute bicubic(const Vector2& p, WrapMode wrap) const { + return bicubic(p.x, p.y, wrap); + } + + inline Compute bicubic(const Vector2& p) const { + return bicubic(p.x, p.y, _wrapMode); + } + + /** Pixel width */ + inline int32 width() const { + return (int32)w; + } + + + /** Pixel height */ + inline int32 height() const { + return (int32)h; + } + + + /** Dimensions in pixels */ + Vector2int16 size() const { + return Vector2int16(w, h); + } + + /** Rectangle from (0, 0) to (w, h) */ + Rect2D rect2DBounds() const { + return Rect2D::xywh(0, 0, w, h); + } + + /** Number of bytes occupied by the image data and this structure */ + size_t sizeInMemory() const { + return data.size() * sizeof(Storage) + sizeof(*this); + } + + + WrapMode wrapMode() const { + return _wrapMode; + } + + + void setWrapMode(WrapMode m) { + _wrapMode = m; + } +}; + + + +} + +#endif // G3D_IMAGE_H diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix.h new file mode 100644 index 00000000000..481af940324 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix.h @@ -0,0 +1,634 @@ +/** + @file Matrix.h + @author Morgan McGuire, morgan@cs.williams.edu + + @created 2005-10-23 + @edited 2007-07-18 + */ + +#ifndef G3D_MATRIX_H +#define G3D_MATRIX_H + +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/Matrix3.h" +#include "G3D/Matrix4.h" +#include "G3D/ReferenceCount.h" + +namespace G3D { + +/** + N x M matrix. + + The actual data is tracked internally by a reference counted pointer; + it is efficient to pass and assign Matrix objects because no data is actually copied. + This avoids the headache of pointers and allows natural math notation: + + <PRE> + Matrix A, B, C; + // ... + + C = A * f(B); + C = C.inverse(); + + A = Matrix::identity(4); + C = A; + C.set(0, 0, 2.0); // Triggers a copy of the data so that A remains unchanged. + + // etc. + + </PRE> + + The Matrix::debugNumCopyOps and Matrix::debugNumAllocOps counters + increment every time an operation forces the copy and allocation of matrices. You + can use these to detect slow operations when efficiency is a major concern. + + Some methods accept an output argument instead of returning a value. For example, + <CODE>A = B.transpose()</CODE> can also be invoked as <CODE>B.transpose(A)</CODE>. + The latter may be more efficient, since Matrix may be able to re-use the storage of + A (if it has approximatly the right size and isn't currently shared with another matrix). + + @sa G3D::Matrix3, G3D::Matrix4, G3D::Vector2, G3D::Vector3, G3D::Vector4, G3D::CoordinateFrame + + @beta + */ +class Matrix { +public: + /** + Internal precision. Currently float, but this may become a templated class in the future + to allow operations like Matrix<double> and Matrix<ComplexFloat>. + + Not necessarily a plain-old-data type (e.g., could ComplexFloat), but must be something + with no constructor, that can be safely memcpyd, and that has a bit pattern of all zeros + when zero.*/ + typedef float T; + + /** Incremented every time the elements of a matrix are copied. Useful for profiling your + own code that uses Matrix to determine when it is slow due to copying.*/ + static int debugNumCopyOps; + + /** Incremented every time a new matrix object is allocated. Useful for profiling your + own code that uses Matrix to determine when it is slow due to allocation.*/ + static int debugNumAllocOps; + +private: +public: + + /** Used internally by Matrix. + + Does not throw exceptions-- assumes the caller has taken care of + argument checking. */ + class Impl : public ReferenceCountedObject { + public: + + static void* operator new(size_t size) { + return System::malloc(size); + } + + static void operator delete(void* p) { + System::free(p); + } + + ~Impl(); + + private: + friend class Matrix; + + /** elt[r][c] = the element. Pointers into data.*/ + T** elt; + + /** Row major data for the entire matrix. */ + T* data; + + /** The number of rows */ + int R; + + /** The number of columns */ + int C; + + int dataSize; + + /** If R*C is much larger or smaller than the current, deletes all previous data + and resets to random data. Otherwise re-uses existing memory and just resets + R, C, and the row pointers. */ + void setSize(int newRows, int newCols); + + inline Impl() : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {} + + Impl(const Matrix3& M); + + Impl(const Matrix4& M); + + inline Impl(int r, int c) : elt(NULL), data(NULL), R(0), C(0), dataSize(0) { + setSize(r, c); + } + + Impl& operator=(const Impl& m); + + inline Impl(const Impl& B) : elt(NULL), data(NULL), R(0), C(0), dataSize(0) { + // Use the assignment operator + *this = B; + } + + void setZero(); + + inline void set(int r, int c, T v) { + debugAssert(r < R); + debugAssert(c < C); + elt[r][c] = v; + } + + inline const T& get(int r, int c) const { + debugAssert(r < R); + debugAssert(c < C); + return elt[r][c]; + } + + /** Multiplies this by B and puts the result in out. */ + void mul(const Impl& B, Impl& out) const; + + /** Ok if out == this or out == B */ + void add(const Impl& B, Impl& out) const; + + /** Ok if out == this or out == B */ + void add(T B, Impl& out) const; + + /** Ok if out == this or out == B */ + void sub(const Impl& B, Impl& out) const; + + /** Ok if out == this or out == B */ + void sub(T B, Impl& out) const; + + /** B - this */ + void lsub(T B, Impl& out) const; + + /** Ok if out == this or out == B */ + void arrayMul(const Impl& B, Impl& out) const; + + /** Ok if out == this or out == B */ + void mul(T B, Impl& out) const; + + /** Ok if out == this or out == B */ + void arrayDiv(const Impl& B, Impl& out) const; + + /** Ok if out == this or out == B */ + void div(T B, Impl& out) const; + + void negate(Impl& out) const; + + /** Slow way of computing an inverse; for reference */ + void inverseViaAdjoint(Impl& out) const; + + /** Use Gaussian elimination with pivots to solve for the inverse destructively in place. */ + void inverseInPlaceGaussJordan(); + + void adjoint(Impl& out) const; + + /** Matrix of all cofactors */ + void cofactor(Impl& out) const; + + /** + Cofactor [r][c] is defined as C[r][c] = -1 ^(r+c) * det(A[r][c]), + where A[r][c] is the (R-1)x(C-1) matrix formed by removing row r and + column c from the original matrix. + */ + T cofactor(int r, int c) const; + + /** Ok if out == this or out == B */ + void transpose(Impl& out) const; + + T determinant() const; + + /** Determinant computed without the given row and column */ + T determinant(int r, int c) const; + + void arrayLog(Impl& out) const; + + void arrayExp(Impl& out) const; + + void arraySqrt(Impl& out) const; + + void arrayCos(Impl& out) const; + + void arraySin(Impl& out) const; + + void swapRows(int r0, int r1); + + void swapAndNegateCols(int c0, int c1); + + void mulRow(int r, const T& v); + + void abs(Impl& out) const; + + /** Makes a (R-1)x(C-1) copy of this matrix */ + void withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const; + + bool anyNonZero() const; + + bool allNonZero() const; + + void setRow(int r, const T* vals); + + void setCol(int c, const T* vals); + }; +private: + + typedef ReferenceCountedPointer<Impl> ImplRef; + + ImplRef impl; + + inline Matrix(ImplRef i) : impl(i) {} + inline Matrix(Impl* i) : impl(ImplRef(i)) {} + + /** Used by SVD */ + class SortRank { + public: + T value; + int col; + + inline bool operator>(const SortRank& x) const { + return x.value > value; + } + + inline bool operator<(const SortRank& x) const { + return x.value < value; + } + + inline bool operator>=(const SortRank& x) const { + return x.value >= value; + } + + inline bool operator<=(const SortRank& x) const { + return x.value <= value; + } + + inline bool operator==(const SortRank& x) const { + return x.value == value; + } + + inline bool operator!=(const SortRank& x) const { + return x.value != value; + } + }; + + Matrix vectorPseudoInverse() const; + Matrix partitionPseudoInverse() const; + Matrix colPartPseudoInverse() const; + Matrix rowPartPseudoInverse() const; + + Matrix col2PseudoInverse(const Matrix& B) const; + Matrix col3PseudoInverse(const Matrix& B) const; + Matrix col4PseudoInverse(const Matrix& B) const; + Matrix row2PseudoInverse(const Matrix& B) const; + Matrix row3PseudoInverse(const Matrix& B) const; + Matrix row4PseudoInverse(const Matrix& B) const; + +public: + + Matrix() : impl(new Impl(0, 0)) {} + + Matrix(const Matrix3& M) : impl(new Impl(M)) {} + + Matrix(const Matrix4& M) : impl(new Impl(M)) {} + + template<class S> + static Matrix fromDiagonal(const Array<S>& d) { + Matrix D = zero(d.length(), d.length()); + for (int i = 0; i < d.length(); ++i) { + D.set(i, i, d[i]); + } + return D; + } + + static Matrix fromDiagonal(const Matrix& d); + + /** Returns a new matrix that is all zero. */ + Matrix(int R, int C) : impl(new Impl(R, C)) { + impl->setZero(); + } + + /** Returns a new matrix that is all zero. */ + static Matrix zero(int R, int C); + + /** Returns a new matrix that is all one. */ + static Matrix one(int R, int C); + + /** Returns a new identity matrix */ + static Matrix identity(int N); + + /** Uniformly distributed values between zero and one. */ + static Matrix random(int R, int C); + + /** The number of rows */ + inline int rows() const { + return impl->R; + } + + /** Number of columns */ + inline int cols() const { + return impl->C; + } + + /** Generally more efficient than A * B */ + Matrix& operator*=(const T& B); + + /** Generally more efficient than A / B */ + Matrix& operator/=(const T& B); + + /** Generally more efficient than A + B */ + Matrix& operator+=(const T& B); + + /** Generally more efficient than A - B */ + Matrix& operator-=(const T& B); + + /** No performance advantage over A * B because + matrix multiplication requires intermediate + storage. */ + Matrix& operator*=(const Matrix& B); + + /** Generally more efficient than A + B */ + Matrix& operator+=(const Matrix& B); + + /** Generally more efficient than A - B */ + Matrix& operator-=(const Matrix& B); + + /** Returns a new matrix that is a subset of this one, + from r1:r2 to c1:c2, inclusive.*/ + Matrix subMatrix(int r1, int r2, int c1, int c2) const; + + /** Matrix multiplication. To perform element-by-element multiplication, + see arrayMul. */ + inline Matrix operator*(const Matrix& B) const { + Matrix C(impl->R, B.impl->C); + impl->mul(*B.impl, *C.impl); + return C; + } + + /** See also A *= B, which is more efficient in many cases */ + inline Matrix operator*(const T& B) const { + Matrix C(impl->R, impl->C); + impl->mul(B, *C.impl); + return C; + } + + /** See also A += B, which is more efficient in many cases */ + inline Matrix operator+(const Matrix& B) const { + Matrix C(impl->R, impl->C); + impl->add(*B.impl, *C.impl); + return C; + } + + /** See also A -= B, which is more efficient in many cases */ + inline Matrix operator-(const Matrix& B) const { + Matrix C(impl->R, impl->C); + impl->sub(*B.impl, *C.impl); + return C; + } + + /** See also A += B, which is more efficient in many cases */ + inline Matrix operator+(const T& v) const { + Matrix C(impl->R, impl->C); + impl->add(v, *C.impl); + return C; + } + + /** See also A -= B, which is more efficient in many cases */ + inline Matrix operator-(const T& v) const { + Matrix C(impl->R, impl->C); + impl->sub(v, *C.impl); + return C; + } + + + Matrix operator>(const T& scalar) const; + + Matrix operator<(const T& scalar) const; + + Matrix operator>=(const T& scalar) const; + + Matrix operator<=(const T& scalar) const; + + Matrix operator==(const T& scalar) const; + + Matrix operator!=(const T& scalar) const; + + /** scalar B - this */ + inline Matrix lsub(const T& B) const { + Matrix C(impl->R, impl->C); + impl->lsub(B, *C.impl); + return C; + } + + inline Matrix arrayMul(const Matrix& B) const { + Matrix C(impl->R, impl->C); + impl->arrayMul(*B.impl, *C.impl); + return C; + } + + Matrix3 toMatrix3() const; + + Matrix4 toMatrix4() const; + + Vector2 toVector2() const; + + Vector3 toVector3() const; + + Vector4 toVector4() const; + + /** Mutates this */ + void arrayMulInPlace(const Matrix& B); + + /** Mutates this */ + void arrayDivInPlace(const Matrix& B); + + // Declares an array unary method and its explicit-argument counterpart +# define DECLARE_METHODS_1(method)\ + inline Matrix method() const {\ + Matrix C(impl->R, impl->C);\ + impl->method(*C.impl);\ + return C;\ + }\ + void method(Matrix& out) const; + + + DECLARE_METHODS_1(abs) + DECLARE_METHODS_1(arrayLog) + DECLARE_METHODS_1(arrayExp) + DECLARE_METHODS_1(arraySqrt) + DECLARE_METHODS_1(arrayCos) + DECLARE_METHODS_1(arraySin) + DECLARE_METHODS_1(negate) + +# undef DECLARE_METHODS_1 + + inline Matrix operator-() const { + return negate(); + } + + /** + A<SUP>-1</SUP> computed using the Gauss-Jordan algorithm, + for square matrices. + Run time is <I>O(R<sup>3</sup>)</I>, where <I>R</i> is the + number of rows. + */ + inline Matrix inverse() const { + Impl* A = new Impl(*impl); + A->inverseInPlaceGaussJordan(); + return Matrix(A); + } + + inline T determinant() const { + return impl->determinant(); + } + + /** + A<SUP>T</SUP> + */ + inline Matrix transpose() const { + Impl* A = new Impl(cols(), rows()); + impl->transpose(*A); + return Matrix(A); + } + + /** Transpose in place; more efficient than transpose */ + void transpose(Matrix& out) const; + + inline Matrix adjoint() const { + Impl* A = new Impl(cols(), rows()); + impl->adjoint(*A); + return Matrix(A); + } + + /** + (A<SUP>T</SUP>A)<SUP>-1</SUP>A<SUP>T</SUP>) computed + using SVD. + + @param tolerance Use -1 for automatic tolerance. + */ + Matrix pseudoInverse(float tolerance = -1) const; + + /** Called from pseudoInverse when the matrix has size > 4 along some dimension.*/ + Matrix svdPseudoInverse(float tolerance = -1) const; + + /** + (A<SUP>T</SUP>A)<SUP>-1</SUP>A<SUP>T</SUP>) computed + using Gauss-Jordan elimination. + */ + inline Matrix gaussJordanPseudoInverse() const { + Matrix trans = transpose(); + return (trans * (*this)).inverse() * trans; + } + + /** Singular value decomposition. Factors into three matrices + such that @a this = @a U * fromDiagonal(@a d) * @a V.transpose(). + + The matrix must have at least as many rows as columns. + + Run time is <I>O(C<sup>2</sup>*R)</I>. + + @param sort If true (default), the singular values + are arranged so that D is sorted from largest to smallest. + */ + void svd(Matrix& U, Array<T>& d, Matrix& V, bool sort = true) const; + + void set(int r, int c, T v); + + void setCol(int c, const Matrix& vec); + + void setRow(int r, const Matrix& vec); + + Matrix col(int c) const; + + Matrix row(int r) const; + + T get(int r, int c) const; + + Vector2int16 size() const { + return Vector2int16(rows(), cols()); + } + + int numElements() const { + return rows() * cols(); + } + + void swapRows(int r0, int r1); + + /** Swaps columns c0 and c1 and negates both */ + void swapAndNegateCols(int c0, int c1); + + void mulRow(int r, const T& v); + + /** Returns true if any element is non-zero */ + bool anyNonZero() const; + + /** Returns true if all elements are non-zero */ + bool allNonZero() const; + + inline bool allZero() const { + return !anyNonZero(); + } + + inline bool anyZero() const { + return !allNonZero(); + } + + /** Serializes in Matlab source format */ + void serialize(TextOutput& t) const; + + std::string toString(const std::string& name) const; + + std::string toString() const { + static const std::string name = ""; + return toString(name); + } + + /** 2-norm squared: sum(squares). (i.e., dot product with itself) */ + double normSquared() const; + + /** 2-norm (sqrt(sum(squares)) */ + double norm() const; + + /** + Low-level SVD functionality. Useful for applications that do not want + to construct a Matrix but need to perform the SVD operation. + + this = U * D * V' + + Assumes that rows >= cols + + @return NULL on success, a string describing the error on failure. + @param U rows x cols matrix to be decomposed, gets overwritten with U, a rows x cols matrix with orthogonal columns. + @param D vector of singular values of a (diagonal of the D matrix). Length cols. + @param V returns the right orthonormal transformation matrix, size cols x cols + + @cite Based on Dianne Cook's implementation, which is adapted from + svdecomp.c in XLISP-STAT 2.1, which is code from Numerical Recipes + adapted by Luke Tierney and David Betz. The Numerical Recipes code + is adapted from Forsythe et al, who based their code on Golub and + Reinsch's original implementation. + */ + static const char* svdCore(float** U, int rows, int cols, float* D, float** V); + +}; + +} + +inline G3D::Matrix operator-(const G3D::Matrix::T& v, const G3D::Matrix& M) { + return M.lsub(v); +} + +inline G3D::Matrix operator*(const G3D::Matrix::T& v, const G3D::Matrix& M) { + return M * v; +} + +inline G3D::Matrix operator+(const G3D::Matrix::T& v, const G3D::Matrix& M) { + return M + v; +} + +inline G3D::Matrix abs(const G3D::Matrix& M) { + return M.abs(); +} + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h new file mode 100644 index 00000000000..eaf4aefa220 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h @@ -0,0 +1,69 @@ +#ifndef G3D_MATRIX2_H +#define G3D_MATRIX2_H + +#include "G3D/platform.h" +#include "G3D/Vector2.h" + +namespace G3D { + +/** @beta */ +class Matrix2 { +private: + + float data[2][2]; + +public: + + inline Matrix2() { + data[0][0] = 1.0f; data[0][1] = 0.0f; + data[1][0] = 0.0f; data[1][1] = 1.0f; + } + + inline Matrix2(float v00, float v01, float v10, float v11) { + data[0][0] = v00; data[0][1] = v01; + data[1][0] = v10; data[1][1] = v11; + } + + inline Vector2 operator*(const Vector2& v) const { + return Vector2(data[0][0] * v[0] + data[0][1] * v[1], + data[1][0] * v[0] + data[1][1] * v[1]); + } + + inline Matrix2 inverse() const { + return Matrix2(data[0][0], data[1][0], + data[0][1], data[1][1]) * (1.0f / determinant()); + } + + inline Matrix2 transpose() const { + return Matrix2(data[0][0], data[1][0], + data[0][1], data[1][1]); + } + + inline float determinant() const { + return data[0][0] * data[1][1] - data[0][1] * data[1][0]; + } + + inline Matrix2 operator*(float f) const { + return Matrix2(data[0][0] * f, data[0][1] * f, + data[1][0] * f, data[1][1] * f); + } + + inline Matrix2 operator/(float f) const { + return Matrix2(data[0][0] / f, data[0][1] / f, + data[1][0] / f, data[1][1] / f); + } + + inline float* operator[](int i) { + debugAssert(i >= 0 && i <= 2); + return data[i]; + } + + inline const float* operator[](int i) const { + debugAssert(i >= 0 && i <= 1); + return data[i]; + } +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h new file mode 100644 index 00000000000..ad09cd3860f --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h @@ -0,0 +1,323 @@ +/** + @file Matrix3.h + + 3x3 matrix class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A> + + @created 2001-06-02 + @edited 2006-04-05 + */ + +#ifndef G3D_MATRIX3_H +#define G3D_MATRIX3_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/debugAssert.h" + +#include <cstring> + +namespace G3D { + +/** + 3x3 matrix. Do not subclass. + */ +class Matrix3 { +private: + + float elt[3][3]; + + // Hidden operators + bool operator<(const Matrix3&) const; + bool operator>(const Matrix3&) const; + bool operator<=(const Matrix3&) const; + bool operator>=(const Matrix3&) const; + +public: + + /** Initial values are undefined for performance. See also + Matrix3::zero(), Matrix3::identity(), Matrix3::fromAxisAngle, etc.*/ + inline Matrix3() {} + + Matrix3 (class BinaryInput& b); + Matrix3 (const float aafEntry[3][3]); + Matrix3 (const Matrix3& rkMatrix); + Matrix3 (float fEntry00, float fEntry01, float fEntry02, + float fEntry10, float fEntry11, float fEntry12, + float fEntry20, float fEntry21, float fEntry22); + + bool fuzzyEq(const Matrix3& b) const; + + /** Constructs a matrix from a quaternion. + @cite Graphics Gems II, p. 351--354 + @cite Implementation from Watt and Watt, pg 362*/ + Matrix3(const class Quat& q); + + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** + Sets all elements. + */ + void set(float fEntry00, float fEntry01, float fEntry02, + float fEntry10, float fEntry11, float fEntry12, + float fEntry20, float fEntry21, float fEntry22); + + /** + * member access, allows use of construct mat[r][c] + */ + inline float* operator[] (int iRow) { + debugAssert(iRow >= 0); + debugAssert(iRow < 3); + return (float*)&elt[iRow][0]; + } + + inline const float* operator[] (int iRow) const { + debugAssert(iRow >= 0); + debugAssert(iRow < 3); + return (const float*)&elt[iRow][0]; + } + + inline operator float* () { + return (float*)&elt[0][0]; + } + + inline operator const float* () const{ + return (const float*)&elt[0][0]; + } + + /** @deprecated */ + Vector3 getColumn (int iCol) const; + /** @deprecated */ + Vector3 getRow (int iRow) const; + + Vector3 column(int c) const; + const Vector3& row(int r) const; + + void setColumn(int iCol, const Vector3 &vector); + void setRow(int iRow, const Vector3 &vector); + + // assignment and comparison + inline Matrix3& operator= (const Matrix3& rkMatrix) { + memcpy(elt, rkMatrix.elt, 9 * sizeof(float)); + return *this; + } + + bool operator== (const Matrix3& rkMatrix) const; + bool operator!= (const Matrix3& rkMatrix) const; + + // arithmetic operations + Matrix3 operator+ (const Matrix3& rkMatrix) const; + Matrix3 operator- (const Matrix3& rkMatrix) const; + /** Matrix-matrix multiply */ + Matrix3 operator* (const Matrix3& rkMatrix) const; + Matrix3 operator- () const; + + Matrix3& operator+= (const Matrix3& rkMatrix); + Matrix3& operator-= (const Matrix3& rkMatrix); + Matrix3& operator*= (const Matrix3& rkMatrix); + + /** + * matrix * vector [3x3 * 3x1 = 3x1] + */ + inline Vector3 operator* (const Vector3& v) const { + Vector3 kProd; + + for (int r = 0; r < 3; ++r) { + kProd[r] = + elt[r][0] * v[0] + + elt[r][1] * v[1] + + elt[r][2] * v[2]; + } + + return kProd; + } + + + /** + * vector * matrix [1x3 * 3x3 = 1x3] + */ + friend Vector3 operator* (const Vector3& rkVector, + const Matrix3& rkMatrix); + + /** + * matrix * scalar + */ + Matrix3 operator* (float fScalar) const; + + /** scalar * matrix */ + friend Matrix3 operator* (double fScalar, const Matrix3& rkMatrix); + friend Matrix3 operator* (float fScalar, const Matrix3& rkMatrix); + friend Matrix3 operator* (int fScalar, const Matrix3& rkMatrix); + +private: + /** Multiplication where out != A and out != B */ + static void _mul(const Matrix3& A, const Matrix3& B, Matrix3& out); +public: + + /** Optimized implementation of out = A * B. It is safe (but slow) to call + with A, B, and out possibly pointer equal to one another.*/ + // This is a static method so that it is not ambiguous whether "this" + // is an input or output argument. + inline static void mul(const Matrix3& A, const Matrix3& B, Matrix3& out) { + if ((&out == &A) || (&out == &B)) { + // We need a temporary anyway, so revert to the stack method. + out = A * B; + } else { + // Optimized in-place multiplication. + _mul(A, B, out); + } + } + +private: + static void _transpose(const Matrix3& A, Matrix3& out); +public: + + /** Optimized implementation of out = A.transpose(). It is safe (but slow) to call + with A and out possibly pointer equal to one another. + + Note that <CODE>A.transpose() * v</CODE> can be computed + more efficiently as <CODE>v * A</CODE>. + */ + inline static void transpose(const Matrix3& A, Matrix3& out) { + if (&A == &out) { + out = A.transpose(); + } else { + _transpose(A, out); + } + } + + /** Returns true if the rows and column L2 norms are 1.0 and the rows are orthogonal. */ + bool isOrthonormal() const; + + Matrix3 transpose () const; + bool inverse (Matrix3& rkInverse, float fTolerance = 1e-06) const; + Matrix3 inverse (float fTolerance = 1e-06) const; + float determinant () const; + + /** singular value decomposition */ + void singularValueDecomposition (Matrix3& rkL, Vector3& rkS, + Matrix3& rkR) const; + /** singular value decomposition */ + void singularValueComposition (const Matrix3& rkL, + const Vector3& rkS, const Matrix3& rkR); + + /** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */ + void orthonormalize(); + + /** orthogonal Q, diagonal D, upper triangular U stored as (u01,u02,u12) */ + void qDUDecomposition (Matrix3& rkQ, Vector3& rkD, + Vector3& rkU) const; + + float spectralNorm () const; + + /** matrix must be orthonormal */ + void toAxisAngle(Vector3& rkAxis, float& rfRadians) const; + + static Matrix3 fromDiagonal(const Vector3& d) { + return Matrix3(d.x, 0, 0, + 0, d.y, 0, + 0, 0, d.z); + } + + static Matrix3 fromAxisAngle(const Vector3& rkAxis, float fRadians); + + /** + * The matrix must be orthonormal. The decomposition is yaw*pitch*roll + * where yaw is rotation about the Up vector, pitch is rotation about the + * right axis, and roll is rotation about the Direction axis. + */ + bool toEulerAnglesXYZ (float& rfYAngle, float& rfPAngle, + float& rfRAngle) const; + bool toEulerAnglesXZY (float& rfYAngle, float& rfPAngle, + float& rfRAngle) const; + bool toEulerAnglesYXZ (float& rfYAngle, float& rfPAngle, + float& rfRAngle) const; + bool toEulerAnglesYZX (float& rfYAngle, float& rfPAngle, + float& rfRAngle) const; + bool toEulerAnglesZXY (float& rfYAngle, float& rfPAngle, + float& rfRAngle) const; + bool toEulerAnglesZYX (float& rfYAngle, float& rfPAngle, + float& rfRAngle) const; + static Matrix3 fromEulerAnglesXYZ (float fYAngle, float fPAngle, float fRAngle); + static Matrix3 fromEulerAnglesXZY (float fYAngle, float fPAngle, float fRAngle); + static Matrix3 fromEulerAnglesYXZ (float fYAngle, float fPAngle, float fRAngle); + static Matrix3 fromEulerAnglesYZX (float fYAngle, float fPAngle, float fRAngle); + static Matrix3 fromEulerAnglesZXY (float fYAngle, float fPAngle, float fRAngle); + static Matrix3 fromEulerAnglesZYX (float fYAngle, float fPAngle, float fRAngle); + + /** eigensolver, matrix must be symmetric */ + void eigenSolveSymmetric (float afEigenvalue[3], + Vector3 akEigenvector[3]) const; + + static void tensorProduct (const Vector3& rkU, const Vector3& rkV, + Matrix3& rkProduct); + std::string toString() const; + + static const float EPSILON; + + // Special values. + // The unguaranteed order of initialization of static variables across + // translation units can be a source of annoying bugs, so now the static + // special values (like Vector3::ZERO, Color3::WHITE, ...) are wrapped + // inside static functions that return references to them. + // These functions are intentionally not inlined, because: + // "You might be tempted to write [...] them as inline functions + // inside their respective header files, but this is something you + // must definitely not do. An inline function can be duplicated + // in every file in which it appears œóõ½ and this duplication + // includes the static object definition. Because inline functions + // automatically default to internal linkage, this would result in + // having multiple static objects across the various translation + // units, which would certainly cause problems. So you must + // ensure that there is only one definition of each wrapping + // function, and this means not making the wrapping functions inline", + // according to Chapter 10 of "Thinking in C++, 2nd ed. Volume 1" by Bruce Eckel, + // http://www.mindview.net/ + static const Matrix3& zero(); + static const Matrix3& identity(); + +protected: + + // support for eigensolver + void tridiagonal (float afDiag[3], float afSubDiag[3]); + bool qLAlgorithm (float afDiag[3], float afSubDiag[3]); + + // support for singular value decomposition + static const float ms_fSvdEpsilon; + static const int ms_iSvdMaxIterations; + static void bidiagonalize (Matrix3& kA, Matrix3& kL, + Matrix3& kR); + static void golubKahanStep (Matrix3& kA, Matrix3& kL, + Matrix3& kR); + + // support for spectral norm + static float maxCubicRoot (float afCoeff[3]); + +}; + + +//---------------------------------------------------------------------------- +/** <code>v * M == M.transpose() * v</code> */ +inline Vector3 operator* (const Vector3& rkPoint, const Matrix3& rkMatrix) { + Vector3 kProd; + + for (int r = 0; r < 3; ++r) { + kProd[r] = + rkPoint[0] * rkMatrix.elt[0][r] + + rkPoint[1] * rkMatrix.elt[1][r] + + rkPoint[2] * rkMatrix.elt[2][r]; + } + + return kProd; +} + + +} // namespace + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h new file mode 100644 index 00000000000..5bb6cecfdf9 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h @@ -0,0 +1,206 @@ +/** + @file Matrix4.h + + 4x4 matrix class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-10-02 + @edited 2007-04-05 + */ + +#ifndef G3D_MATRIX4_H +#define G3D_MATRIX4_H + +#ifdef _MSC_VER +// Disable conditional expression is constant, which occurs incorrectly on inlined functions +# pragma warning (push) +# pragma warning( disable : 4127 ) +#endif + +#include "G3D/platform.h" +#include "G3D/debugAssert.h" +#include "G3D/Matrix3.h" +#include "G3D/Vector3.h" + +namespace G3D { + +/** + A 4x4 matrix. + + See also G3D::CoordinateFrame, G3D::Matrix3, G3D::Quat + */ +class Matrix4 { +private: + + float elt[4][4]; + + /** + Computes the determinant of the 3x3 matrix that lacks excludeRow + and excludeCol. + */ + float subDeterminant(int excludeRow, int excludeCol) const; + + // Hidden operators + bool operator<(const Matrix4&) const; + bool operator>(const Matrix4&) const; + bool operator<=(const Matrix4&) const; + bool operator>=(const Matrix4&) const; + +public: + Matrix4( + float r1c1, float r1c2, float r1c3, float r1c4, + float r2c1, float r2c2, float r2c3, float r2c4, + float r3c1, float r3c2, float r3c3, float r3c4, + float r4c1, float r4c2, float r4c3, float r4c4); + + /** + init should be <B>row major</B>. + */ + Matrix4(const float* init); + + /** + a is the upper left 3x3 submatrix and b is the upper right 3x1 submatrix. The last row of the created matrix is (0,0,0,1). + */ + Matrix4(const class Matrix3& upper3x3, const class Vector3& lastCol = Vector3::zero()); + + Matrix4(const class CoordinateFrame& c); + + Matrix4(const double* init); + + Matrix4(); + + /** Produces an RT transformation that nearly matches this Matrix4. + Because a Matrix4 may not be precisely a rotation and translation, + this may introduce error. */ + class CoordinateFrame approxCoordinateFrame() const; + + // Special values. + // Intentionally not inlined: see Matrix3::identity() for details. + static const Matrix4& identity(); + static const Matrix4& zero(); + + inline float* operator[](int r) { + debugAssert(r >= 0); + debugAssert(r < 4); + return (float*)&elt[r]; + } + + inline const float* operator[](int r) const { + debugAssert(r >= 0); + debugAssert(r < 4); + return (const float*)&elt[r]; + } + + inline operator float* () { + return (float*)&elt[0][0]; + } + + inline operator const float* () const { + return (const float*)&elt[0][0]; + } + + Matrix4 operator*(const Matrix4& other) const; + + class Matrix3 upper3x3() const; + + /** Homogeneous multiplication. Let k = M * [v w]^T. result = k.xyz() / k.w */ + class Vector3 homoMul(const class Vector3& v, float w) const; + + /** + Constructs an orthogonal projection matrix from the given parameters. + Near and far are the <b>NEGATIVE</b> of the near and far plane Z values + (to follow OpenGL conventions). + */ + static Matrix4 orthogonalProjection( + float left, + float right, + float bottom, + float top, + float nearval, + float farval); + + static Matrix4 orthogonalProjection( + const class Rect2D& rect, + float nearval, + float farval); + + static Matrix4 perspectiveProjection( + float left, + float right, + float bottom, + float top, + float nearval, + float farval); + + void setRow(int r, const class Vector4& v); + void setColumn(int c, const Vector4& v); + + /** @deprecated */ + Vector4 getRow(int r) const; + /** @deprecated */ + Vector4 getColumn(int c) const; + + const Vector4& row(int r) const; + Vector4 column(int c) const; + + Matrix4 operator*(const float s) const; + Vector4 operator*(const Vector4& vector) const; + + Matrix4 transpose() const; + + bool operator!=(const Matrix4& other) const; + bool operator==(const Matrix4& other) const; + + float determinant() const; + Matrix4 inverse() const; + + /** + Transpose of the cofactor matrix (used in computing the inverse). + Note: This is in fact only one type of adjoint. More generally, + an adjoint of a matrix is any mapping of a matrix which possesses + certain properties. This returns the so-called adjugate + or classical adjoint. + */ + Matrix4 adjoint() const; + Matrix4 cofactor() const; + + /** Serializes row-major */ + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + std::string toString() const; + + /** 3D scale matrix */ + inline static Matrix4 scale(const Vector3& v) { + return Matrix4(v.x, 0, 0, 0, + 0, v.y, 0, 0, + 0, 0, v.z, 0, + 0, 0, 0, 1); + } + + /** 3D scale matrix */ + inline static Matrix4 scale(float x, float y, float z) { + return scale(Vector3(x, y, z)); + } + + /** 3D scale matrix */ + inline static Matrix4 scale(float s) { + return scale(s,s,s); + } + + /** 3D translation matrix */ + inline static Matrix4 translation(const Vector3& v) { + return Matrix4(Matrix3::identity(), v); + } +}; + + + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h b/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h new file mode 100644 index 00000000000..be381eff117 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h @@ -0,0 +1,718 @@ +/** + @file MeshAlg.h + + Indexed Mesh algorithms. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-09-14 + @edited 2008-07-30 +*/ + +#ifndef G3D_MESHALG_H +#define G3D_MESHALG_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Vector3.h" +#include "G3D/CoordinateFrame.h" + +namespace G3D { + +/** + Indexed <B>mesh alg</B>orithms. You have to build your own mesh class. + <P> + No mesh class is provided with G3D because there isn't an "ideal" + mesh format-- one application needs keyframed animation, another + skeletal animation, a third texture coordinates, a fourth + cannot precompute information, etc. Instead of compromising, this + class implements the hard parts of mesh computation and you can write + your own ideal mesh class on top of it. + */ +class MeshAlg { +public: + + enum Primitive {LINES, LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, + TRIANGLE_FAN, QUADS, QUAD_STRIP, POINTS}; + + + /** Adjacency information for a vertex. + Does not contain the vertex position or normal, + which are stored in the MeshAlg::Geometry object. + <CODE>Vertex</CODE>s must be stored in an array + parallel to (indexed in the same way as) + MeshAlg::Geometry::vertexArray. + */ + class Vertex { + public: + Vertex() {} + + /** + Array of edges adjacent to this vertex. + Let e = edgeIndex[i]. + edge[(e >= 0) ? e : ~e].vertexIndex[0] == this + vertex index. + + Edges may be listed multiple times if they are + degenerate. + */ + Array<int> edgeIndex; + + /** + Returns true if e or ~e is in the edgeIndex list. + */ + inline bool inEdge(int e) const { + return edgeIndex.contains(~e) || edgeIndex.contains(e); + } + + /** + Array of faces containing this vertex. Faces + may be listed multiple times if they are degenerate. + */ + Array<int> faceIndex; + + inline bool inFace(int f) const { + debugAssert(f >= 0); + return faceIndex.contains(f); + } + }; + + + /** + Oriented, indexed triangle. + */ + class Face { + public: + Face(); + + /** + Used by Edge::faceIndex to indicate a missing face. + This is a large negative value. + */ + static const int NONE; + + + /** + Vertices in the face in counter-clockwise order. + Degenerate faces may include the same vertex multiple times. + */ + int vertexIndex[3]; + + inline bool containsVertex(int v) const { + return contains(vertexIndex, 3, v); + } + + /** + Edge indices in counter-clockwise order. Edges are + undirected, so it is important to know which way + each edge is pointing in a face. This is encoded + using negative indices. + + If <CODE>edgeIndex[i] >= 0</CODE> then this face + contains the directed edge + between vertex indices + <CODE>edgeArray[face.edgeIndex[i]].vertexIndex[0]</CODE> + and + <CODE>edgeArray[face.edgeIndex[i]].vertexIndex[1]</CODE>. + + If <CODE>edgeIndex[i] < 0</CODE> then + <CODE>~edgeIndex[i]</CODE> (i.e. the two's + complement of) is used and this face contains the directed + edge between vertex indices + <CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[0]</CODE> + and + <CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[1]</CODE>. + + Degenerate faces may include the same edge multiple times. + */ + // Temporarily takes on the value Face::NONE during adjacency + // computation to indicate an edge that has not yet been assigned. + int edgeIndex[3]; + + inline bool containsEdge(int e) const { + if (e < 0) { + e = ~e; + } + return contains(edgeIndex, 3, e) || contains(edgeIndex, 3, ~e); + } + + /** Contains the forward edge e if e >= 0 and the backward edge + ~e otherwise. */ + inline bool containsDirectedEdge(int e) const { + return contains(edgeIndex, 3, e); + } + }; + + + /** Oriented, indexed edge */ + class Edge { + public: + Edge(); + + /** Degenerate edges may include the same vertex times. */ + int vertexIndex[2]; + + inline bool containsVertex(int v) const { + return contains(vertexIndex, 2, v); + } + + /** + The edge is directed <B>forward</B> in face 0 + <B>backward</B> in face 1. Face index of MeshAlg::Face::NONE + indicates a boundary (a.k.a. crack, broken) edge. + */ + int faceIndex[2]; + + /** Returns true if f is contained in the faceIndex array in either slot. + To see if it is forward in that face, just check edge.faceIndex[0] == f.*/ + inline bool inFace(int f) const { + return contains(faceIndex, 2, f); + } + + /** + Returns true if either faceIndex is NONE. + */ + inline bool boundary() const { + return (faceIndex[0] == Face::NONE) || + (faceIndex[1] == Face::NONE); + } + + /** + Returns the reversed edge. + */ + inline Edge reverse() const { + Edge e; + e.vertexIndex[0] = vertexIndex[1]; + e.vertexIndex[1] = vertexIndex[0]; + e.faceIndex[0] = faceIndex[1]; + e.faceIndex[1] = faceIndex[0]; + return e; + } + }; + + + /** + Convenient for passing around the per-vertex data that changes under + animation. The faces and edges are needed to interpret + these values. + */ + class Geometry { + public: + /** Vertex positions */ + Array<Vector3> vertexArray; + + /** Vertex normals */ + Array<Vector3> normalArray; + + /** + Assignment is optimized using SSE. + */ + Geometry& operator=(const Geometry& src); + + void clear() { + vertexArray.clear(); + normalArray.clear(); + } + }; + + /** + Given a set of vertices and a set of indices for traversing them + to create triangles, computes other mesh properties. + + <B>Colocated vertices are treated as separate.</B> To have + colocated vertices collapsed (necessary for many algorithms, + like shadowing), weld the mesh before computing adjacency. + + <I>Recent change: In version 6.00, colocated vertices were automatically + welded by this routine and degenerate faces and edges were removed. That + is no longer the case.</I> + + Where two faces meet, there are two opposite directed edges. These + are collapsed into a single bidirectional edge in the edgeArray. + If four faces meet exactly at the same edge, that edge will appear + twice in the array, and so on. If an edge is a boundary of the mesh + (i.e. if the edge has only one adjacent face) it will appear in the + array with one face index set to MeshAlg::Face::NONE. + + @param vertexGeometry %Vertex positions to use when deciding colocation. + @param indexArray Order to traverse vertices to make triangles + @param faceArray <I>Output</I> + @param edgeArray <I>Output</I>. Sorted so that boundary edges are at the end of the array. + @param vertexArray <I>Output</I> + */ + static void computeAdjacency( + const Array<Vector3>& vertexGeometry, + const Array<int>& indexArray, + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array<Vertex>& vertexArray); + + /** + @deprecated Use the other version of computeAdjacency, which takes Array<Vertex>. + @param facesAdjacentToVertex <I>Output</I> adjacentFaceArray[v] is an array of + indices for faces touching vertex index v + */ + static void computeAdjacency( + const Array<Vector3>& vertexArray, + const Array<int>& indexArray, + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array< Array<int> >& facesAdjacentToVertex); + + /** + Computes some basic mesh statistics including: min, max mean and median, + edge lengths; and min, mean, median, and max face area. + + @param vertexGeometry %Vertex positions to use when deciding colocation. + @param indexArray Order to traverse vertices to make triangles + @param minEdgeLength Minimum edge length + @param meanEdgeLength Mean edge length + @param medianEdgeLength Median edge length + @param maxEdgeLength Max edge length + @param minFaceArea Minimum face area + @param meanFaceArea Mean face area + @param medianFaceArea Median face area + @param maxFaceArea Max face area + */ + static void computeAreaStatistics( + const Array<Vector3>& vertexArray, + const Array<int>& indexArray, + double& minEdgeLength, + double& meanEdgeLength, + double& medianEdgeLength, + double& maxEdgeLength, + double& minFaceArea, + double& meanFaceArea, + double& medianFaceArea, + double& maxFaceArea); + +private: + /** + Computes the tangent space basis vectors for + a counter-clockwise oriented face. + + @cite Max McGuire + */ + static void computeTangentVectors( + const Vector3& normal, + const Vector3 position[3], + const Vector2 texCoord[3], + Vector3& tangent, + Vector3& binormal); + + /** Helper for weldAdjacency */ + static void weldBoundaryEdges( + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array<Vertex>& vertexArray); + +public: + + /** + Computes tangent and binormal vectors, + which provide a (mostly) consistent + parameterization over the surface for + effects like bump mapping. In the resulting coordinate frame, + T = x (varies with texture s coordinate), B = y (varies with negative texture t coordinate), + and N = z for a right-handed coordinate frame. If a billboard is vertical on the screen + in view of the camera, the tangent space matches the camera's coordinate frame. + + The vertex, texCoord, tangent, and binormal + arrays are parallel arrays. + + The resulting tangent and binormal might not be exactly + perpendicular to each other. They are guaranteed to + be perpendicular to the normal. + + @cite Max McGuire + */ + static void computeTangentSpaceBasis( + const Array<Vector3>& vertexArray, + const Array<Vector2>& texCoordArray, + const Array<Vector3>& vertexNormalArray, + const Array<Face>& faceArray, + Array<Vector3>& tangent, + Array<Vector3>& binormal); + + /** @deprecated */ + static void computeNormals( + const Array<Vector3>& vertexArray, + const Array<Face>& faceArray, + const Array< Array<int> >& adjacentFaceArray, + Array<Vector3>& vertexNormalArray, + Array<Vector3>& faceNormalArray); + + /** + Vertex normals are weighted by the area of adjacent faces. + Nelson Max showed this is superior to uniform weighting for + general meshes in jgt. + + @param vertexNormalArray Output. Unit length + @param faceNormalArray Output. Degenerate faces produce zero magnitude normals. Unit length + @see weld + */ + static void computeNormals( + const Array<Vector3>& vertexGeometry, + const Array<Face>& faceArray, + const Array<Vertex>& vertexArray, + Array<Vector3>& vertexNormalArray, + Array<Vector3>& faceNormalArray); + + /** Computes unit length normals in place using the other computeNormals methods. + If you already have a face array use another method; it will be faster. + @see weld*/ + static void computeNormals( + Geometry& geometry, + const Array<int>& indexArray); + + /** + Computes face normals only. Significantly faster (especially if + normalize is false) than computeNormals. + @see weld + */ + static void computeFaceNormals( + const Array<Vector3>& vertexArray, + const Array<Face>& faceArray, + Array<Vector3>& faceNormals, + bool normalize = true); + + /** + Classifies each face as a backface or a front face relative + to the observer point P (which is at infinity when P.w = 0). + A face with normal exactly perpendicular to the observer vector + may be classified as either a front or a back face arbitrarily. + */ + static void identifyBackfaces( + const Array<Vector3>& vertexArray, + const Array<Face>& faceArray, + const Vector4& P, + Array<bool>& backface); + + /** A faster version of identifyBackfaces for the case where + face normals have already been computed */ + static void identifyBackfaces( + const Array<Vector3>& vertexArray, + const Array<Face>& faceArray, + const Vector4& P, + Array<bool>& backface, + const Array<Vector3>& faceNormals); + + /** + Welds nearby and colocated elements of the <I>oldVertexArray</I> together so that + <I>newVertexArray</I> contains no vertices within <I>radius</I> of one another. + Every vertex in newVertexPositions also appears in oldVertexPositions. + This is useful for downsampling meshes and welding cracks created by artist errors + or numerical imprecision. + + The two integer arrays map indices back and forth between the arrays according to: + <PRE> + oldVertexArray[toOld[ni]] == newVertexArray[ni] + oldVertexArray[oi] == newVertexArray[toNew[ni]] + </PRE> + + Note that newVertexPositions is never longer than oldVertexPositions + and is shorter when vertices are welded. + + Welding with a large radius will effectively compute a lower level of detail for + the mesh. + + The welding method runs in roughly linear time in the length of oldVertexArray-- + a uniform spatial grid is used to achieve nearly constant time vertex collapses + for uniformly distributed vertices. + + It is sometimes desirable to keep the original vertex ordering but + identify the unique vertices. The following code computes + array canonical s.t. canonical[v] = first occurance of + a vertex near oldVertexPositions[v] in oldVertexPositions. + + <PRE> + Array<int> canonical(oldVertexPositions.size()), toNew, toOld; + computeWeld(oldVertexPositions, Array<Vector3>(), toNew, toOld, radius); + for (int v = 0; v < canonical.size(); ++v) { + canonical[v] = toOld[toNew[v]]; + } + </PRE> + + See also G3D::MeshAlg::weldAdjacency. + + @cite The method is that described as the 'Grouper' in Baum, Mann, Smith, and Winget, + Making Radiosity Usable: Automatic Preprocessing and Meshing Techniques for + the Generation of Accurate Radiosity Solutions, Computer Graphics vol 25, no 4, July 1991. + + @deprecated Use weld. + */ + static void computeWeld( + const Array<Vector3>& oldVertexPositions, + Array<Vector3>& newVertexPositions, + Array<int>& toNew, + Array<int>& toOld, + double radius = G3D::fuzzyEpsilon); + + /** + Modifies the face, edge, and vertex arrays in place so that + colocated (within radius) vertices are treated as identical. + Note that the vertexArray and corresponding geometry will + contain elements that are no longer used. In the vertexArray, + these elements are initialized to MeshAlg::Vertex() but not + removed (because removal would change the indexing). + + This is a good preprocessing step for algorithms that are only + concerned with the shape of a mesh (e.g. cartoon rendering, fur, shadows) + and not the indexing of the vertices. + + Use this method when you have already computed adjacency information + and want to collapse colocated vertices within that data without + disturbing the actual mesh vertices or indexing scheme. + + If you have not computed adjacency already, use MeshAlg::computeWeld + instead and compute adjacency information after welding. + + @deprecated Use weld. + + @param faceArray Mutated in place. Size is maintained (degenerate + faces are <b>not</B> removed). + @param edgeArray Mutated in place. May shrink if boundary edges + are welded together. + @param vertexArray Mutated in place. Size is maintained (duplicate + vertices contain no adjacency info). + */ + static void weldAdjacency( + const Array<Vector3>& originalGeometry, + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array<Vertex>& vertexArray, + double radius = G3D::fuzzyEpsilon); + + + /** + Counts the number of edges (in an edge array returned from + MeshAlg::computeAdjacency) that have only one adjacent face. + */ + static int countBoundaryEdges(const Array<Edge>& edgeArray); + + + /** + Generates an array of integers from start to start + n - 1 that have run numbers + in series then omit the next skip before the next run. Useful for turning + a triangle list into an indexed face set. + + Example: + <PRE> + createIndexArray(10, x); + // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + createIndexArray(5, x, 2); + // x = [2, 3, 4, 5, 6, 7] + + createIndexArray(6, x, 0, 2, 1); + // x = [0, 1, 3, 4, 6, 7] + </PRE> + */ + static void createIndexArray( + int n, + Array<int>& array, + int start = 0, + int run = 1, + int skip = 0); + + /** + Computes a conservative, near-optimal axis aligned bounding box and sphere. + + @cite The bounding sphere uses the method from J. Ritter. An effcient bounding sphere. In Andrew S. Glassner, editor, Graphics Gems. Academic Press, Boston, MA, 1990. + + */ + static void computeBounds(const Array<Vector3>& vertex, class Box& box, class Sphere& sphere); + + /** Computes bounds for a subset of the vertices. It is ok if vertices appear more than once in the index array. */ + static void computeBounds(const Array<Vector3>& vertex, const Array<int>& index, class Box& box, class Sphere& sphere); + + /** + Mutates geometry, texCoord, and indexArray so that the output has collocated vertices collapsed (welded). + + @param vertices Input and output + @param textureCoords Input and output + @param normals Output only + @param indices Input and output. This is an array of trilist indices. + @param oldToNewIndex Output argument + @param normalSmoothingAngle Varies from 0 (flat shading) to toRadians(180) for extremely smooth shading. Default is toRadians(70) + */ + static void weld( + Array<Vector3>& vertices, + Array<Vector2>& textureCoords, + Array<Vector3>& normals, + Array<Array<int>*>& indices, + float normalSmoothingAngle = toRadians(70.0f), + float vertexWeldRadius = 0.0001f, + float textureWeldRadius = 0.0001f, + float normalWeldRadius = 0.01f); + + inline static void weld( + Array<Vector3>& vertices, + Array<Vector2>& textureCoords, + Array<Vector3>& normals, + Array<int>& indices, + float normalSmoothingAngle = toRadians(70.0f), + float vertexWeldRadius = 0.0002f, + float textureWeldRadius = 0.00001f, + float normalWeldRadius = 0.00001f) { + + Array<Array<int>*> meta; + meta.append(&indices); + weld(vertices, textureCoords, normals, meta, normalSmoothingAngle, vertexWeldRadius, textureWeldRadius, normalWeldRadius); + } + + /** + In debug mode, asserts that the adjacency references between the + face, edge, and vertex array are consistent. + */ + static void debugCheckConsistency( + const Array<Face>& faceArray, + const Array<Edge>& edgeArray, + const Array<Vertex>& vertexArray); + + /** + Generates a unit square in the X-Z plane composed of a grid of wCells x hCells + squares and then transforms it by xform. + + @param vertex Output vertices + @param texCoord Output texture coordinates + @param index Output triangle list indices + @param textureScale Lower-right texture coordinate + @param spaceCentered If true, the coordinates generated are centered at the origin before the transformation. + @param twoSided If true, matching top and bottom planes are generated. + */ + static void generateGrid( + Array<Vector3>& vertex, + Array<Vector2>& texCoord, + Array<int>& index, + int wCells = 10, + int hCells = 10, + const Vector2& textureScale = Vector2(1,1), + bool spaceCentered = true, + bool twoSided = true, + const CoordinateFrame& xform = CoordinateFrame()); + + /** Converts quadlist (QUADS), + triangle fan (TRIANGLE_FAN), + tristrip(TRIANGLE_STRIP), and quadstrip (QUAD_STRIP) indices into + triangle list (TRIANGLES) indices and appends them to outIndices. */ + template<class IndexType> + static void toIndexedTriList( + const Array<IndexType>& inIndices, + MeshAlg::Primitive inType, + Array<IndexType>& outIndices) { + + debugAssert( + inType == MeshAlg::TRIANGLE_STRIP || + inType == MeshAlg::TRIANGLE_FAN || + inType == MeshAlg::QUADS || + inType == MeshAlg::QUAD_STRIP); + + const int inSize = inIndices.size(); + + switch(inType) { + case MeshAlg::TRIANGLE_FAN: + { + debugAssert(inSize >= 3); + + int N = outIndices.size(); + outIndices.resize(N + (inSize - 2) * 3); + + for (IndexType i = 1, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) { + outIndices[outIndex] = inIndices[0]; + outIndices[outIndex + 1] = inIndices[i]; + outIndices[outIndex + 2] = inIndices[i + 1]; + } + + break; + } + + case MeshAlg::TRIANGLE_STRIP: + { + debugAssert(inSize >= 3); + + int N = outIndices.size(); + outIndices.resize(N + (inSize - 2) * 3); + + bool atEven = false; + for (IndexType i = 0, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) { + if (atEven) { + outIndices[outIndex] = inIndices[i + 1]; + outIndices[outIndex + 1] = inIndices[i]; + outIndices[outIndex + 2] = inIndices[i + 2]; + atEven = false; + } else { + outIndices[outIndex] = inIndices[i]; + outIndices[outIndex + 1] = inIndices[i + 1]; + outIndices[outIndex + 2] = inIndices[i + 2]; + atEven = true; + } + } + + break; + } + + case MeshAlg::QUADS: + { + debugAssert(inIndices.size() >= 4); + + int N = outIndices.size(); + outIndices.resize(N + (inSize / 4) * 3); + + for (IndexType i = 0, outIndex = N; i <= (inSize - 4); i += 4, outIndex += 6) { + outIndices[outIndex] = inIndices[i]; + outIndices[outIndex + 1] = inIndices[i + 1]; + outIndices[outIndex + 2] = inIndices[i + 3]; + outIndices[outIndex + 3] = inIndices[i + 1]; + outIndices[outIndex + 4] = inIndices[i + 2]; + outIndices[outIndex + 5] = inIndices[i + 3]; + } + + break; + } + + case MeshAlg::QUAD_STRIP: + { + debugAssert(inIndices.size() >= 4); + + int N = outIndices.size(); + outIndices.resize(N + (inSize - 2) * 3); + + for (IndexType i = 0, outIndex = N; i <= (inSize - 2); i += 2, outIndex += 6) { + outIndices[outIndex] = inIndices[i]; + outIndices[outIndex + 1] = inIndices[i + 1]; + outIndices[outIndex + 2] = inIndices[i + 2]; + outIndices[outIndex + 3] = inIndices[i + 2]; + outIndices[outIndex + 4] = inIndices[i + 1]; + outIndices[outIndex + 5] = inIndices[i + 3]; + } + break; + } + default: + alwaysAssertM(false, "Illegal argument"); + } + } + +protected: + + /** + Helper for computeAdjacency. If a directed edge with index e already + exists from i0 to i1 then e is returned. If a directed edge with index e + already exists from i1 to i0, ~e is returned (the complement) and + edgeArray[e] is set to f. Otherwise, a new edge is created from i0 to i1 + with first face index f and its index is returned. + + @param vertexArray Vertex positions to use when deciding colocation. + + @param area Area of face f. When multiple edges of the same direction + are found between the same vertices (usually because of degenerate edges) + the face with larger area is kept in the edge table. + */ + static int findEdgeIndex( + const Array<Vector3>& vertexArray, + Array<Edge>& geometricEdgeArray, + int i0, int i1, int f, double area); +}; +} +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h b/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h new file mode 100644 index 00000000000..3256c0de823 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h @@ -0,0 +1,82 @@ +/** + @file MeshBuilder.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-02-27 + @edited 2004-10-04 + */ +#ifndef G3D_MESHBUILDER_H +#define G3D_MESHBUILDER_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Vector3.h" +#include "G3D/Triangle.h" + +namespace G3D { + +/** + Allows creation of optimized watertight meshes from unoptimized polygon soups. + See also G3D::MeshAlg for algorithms that operate on the output. + */ +class MeshBuilder { +public: + + /** + Set setWeldRadius to AUTO_WELD to weld vertices closer than 1/2 + the smallest edge length in a model. + */ + enum {AUTO_WELD = -100}; + +private: + /** Indices of vertices in <B>or near</B> a grid cell. */ + typedef Array<int> List; + + std::string name; + + /** + All of the triangles, as a long triangle list. + */ + Array<Vector3> triList; + + void centerTriList(); + void computeBounds(Vector3& min, Vector3& max); + + bool _twoSided; + + /** Collapse radius */ + double close; + +public: + + inline MeshBuilder(bool twoSided = false) : _twoSided(twoSided), close(AUTO_WELD) {} + + /** Writes the model to the arrays, which can then be used with + G3D::IFSModel::save and G3D::MeshAlg */ + void commit(std::string& name, Array<int>& indexArray, Array<Vector3>& vertexArray); + + /** + Adds a new triangle to the model. (Counter clockwise) + */ + void addTriangle(const Vector3& a, const Vector3& b, const Vector3& c); + + /** + Adds two new triangles to the model. (Counter clockwise) + */ + void addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d); + + void addTriangle(const Triangle& t); + + void setName(const std::string& n); + + /** Vertices within this distance are considered identical. + Use AUTO_WELD (the default) to have the distance be a function of the model size.*/ + void setWeldRadius(double r) { + close = r; + } +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h b/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h new file mode 100644 index 00000000000..8ed20a06690 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h @@ -0,0 +1,132 @@ +#ifndef G3D_NETADDRESS_H +#define G3D_NETADDRESS_H + +#include "G3D/platform.h" +#include "G3D/Table.h" + +/** These control the version of Winsock used by G3D. + Version 2.0 is standard for G3D 6.09 and later. + Version 1.1 is standard for G3D 6.08 and earlier. + */ +#define G3D_WINSOCK_MAJOR_VERSION 2 +#define G3D_WINSOCK_MINOR_VERSION 0 + +#ifdef G3D_WIN32 +# if (G3D_WINSOCK_MAJOR_VERSION == 2) +# include <winsock2.h> +# elif (G3D_WINSOCK_MAJOR_VERSION == 1) +# include <winsock.h> +# endif +#else +# include <sys/types.h> +# include <sys/socket.h> +# include <netinet/in.h> +# ifndef SOCKADDR_IN +# define SOCKADDR_IN struct sockaddr_in +# endif +# ifndef SOCKET +# define SOCKET int +# endif +#endif + +#include "G3D/g3dmath.h" + +namespace G3D { + +class NetAddress { +private: + friend class NetworkDevice; + friend class LightweightConduit; + friend class ReliableConduit; + + /** Host byte order */ + void init(uint32 host, uint16 port); + void init(const std::string& hostname, uint16 port); + NetAddress(const SOCKADDR_IN& a); + NetAddress(const struct in_addr& addr, uint16 port = 0); + + SOCKADDR_IN addr; + +public: + /** + In host byte order + */ + NetAddress(uint32 host, uint16 port = 0); + + /** + @param port Specified in host byte order (i.e., don't worry about endian issues) + */ + NetAddress(const std::string& hostname, uint16 port); + + /** + @param hostnameAndPort in the form "hostname:port" or "ip:port" + */ + NetAddress(const std::string& hostnameAndPort); + + /** + @deprecated Use G3D::NetworkDevice::broadcastAddressArray() + + @brief Creates a UDP broadcast address for use with a + G3D::LightweightConduit. + + UDP broadcast allows one machine to send a packet to all machines + on the same local network. The IP portion of the address is + 0xFFFFFFFF, which indicates "broadcast" to the underlying socket + API. This feature is not available with the connection-based TCP + protocol abstracted by G3D::ReliableConduit; use multisend + instead. + */ + static NetAddress broadcastAddress(uint16 port); + + NetAddress(); + + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** @brief Returns true if this is not an illegal address. */ + bool ok() const; + + /** @brief Returns a value in host format (i.e., don't worry about + endian issues) */ + inline uint32 ip() const { + return ntohl(addr.sin_addr.s_addr); + //return ntohl(addr.sin_addr.S_un.S_addr); + } + + inline uint16 port() const { + return ntohs(addr.sin_port); + } + + std::string ipString() const; + std::string toString() const; + +}; + +std::ostream& operator<<(std::ostream& os, const NetAddress&); + +} // namespace G3D + +template <> struct HashTrait<G3D::NetAddress> { + static size_t hashCode(const G3D::NetAddress& key) { + return static_cast<size_t>(key.ip() + (static_cast<G3D::uint32>(key.port()) << 16)); + } +}; + +namespace G3D { + +/** + Two addresses may point to the same computer but be != because + they have different IP's. + */ +inline bool operator==(const NetAddress& a, const NetAddress& b) { + return (a.ip() == b.ip()) && (a.port() == b.port()); +} + + +inline bool operator!=(const NetAddress& a, const NetAddress& b) { + return !(a == b); +} + +} // namespace G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h b/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h new file mode 100644 index 00000000000..faffa56d690 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h @@ -0,0 +1,738 @@ +/** + @file NetworkDevice.h + + These classes abstract networking from the socket level to a + serialized messaging style that is more appropriate for games. The + performance has been tuned for sending many small messages. The + message protocol contains a header that prevents them from being used + with raw UDP/TCP (e.g. connecting to an HTTP server). + + LightweightConduit and ReliableConduits have different interfaces + because they have different semantics. You would never want to + interchange them without rewriting the surrounding code. + + NetworkDevice creates conduits because they need access to a global + log pointer and because I don't want non-reference counted conduits + being created. + + Be careful with threads and reference counting. The reference + counters are not threadsafe, and are also not updated correctly if a + thread is explicitly killed. Since the conduits will be passed by + const XConduitRef& most of the time this doesn't appear as a major + problem. With non-blocking conduits, you should need few threads + anyway. + + LightweightConduits preceed each message with a 4-byte host order + unsigned integer that is the message type. This does not appear in + the message serialization/deserialization. + + ReliableConduits preceed each message with two 4-byte host order + unsigned integers. The first is the message type and the second + indicates the length of the rest of the data. The size does not + include the size of the header itself. The minimum message is 9 + bytes:a 4-byte type, a 4-byte header equal to "1", and one byte of data. + + @maintainer Morgan McGuire, morgan@graphics3d.com + @created 2002-11-22 + @edited 2006-11-25 + */ + +#ifndef G3D_NETWORKDEVICE_H +#define G3D_NETWORKDEVICE_H + +#include "G3D/platform.h" +#include "G3D/NetAddress.h" + +#include <string> +#include <iostream> +#include "G3D/g3dmath.h" + +#include "G3D/ReferenceCount.h" +#include "G3D/Array.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +class TextOutput; + +class Conduit : public ReferenceCountedObject { +protected: + friend class NetworkDevice; + friend class NetListener; + + uint64 mSent; + uint64 mReceived; + uint64 bSent; + uint64 bReceived; + + SOCKET sock; + + /** + Used for serialization. One per socket + to make this threadsafe. + */ + BinaryOutput binaryOutput; + + Conduit(); + +public: + + virtual ~Conduit(); + uint64 bytesSent() const; + uint64 messagesSent() const; + uint64 bytesReceived() const; + uint64 messagesReceived() const; + + /** + If true, receive will return true. + */ + virtual bool messageWaiting(); + + /** + Returns the type of the waiting message (i.e. the type supplied + with send). The return value is zero when there is no message + waiting. + + One way to use this is to have a Table mapping message types to + pre-allocated subclasses so receiving looks like: + + <PRE> + // My base class for messages. + class Message { + virtual void serialize(BinaryOutput&) const; + virtual void deserialize(BinaryInput&); + virtual void process() = 0; + }; + + Message* m = table[conduit->waitingMessageType()]; + conduit->receive(m); + m->process(); + </PRE> + + Another is to simply switch on the message type: + + <pre> + switch (conduit->waitingMessageType()) { + case 0: + // No message + break; + + case ENTITY_SPAWN_MSG: + { + EntitySpawnMsg m; + condiut->receive(m); + spawnEntity(m.id, m.position, m.modelID); + } + break; + ... + } + </pre> + */ + virtual uint32 waitingMessageType() = 0; + + /** Returns true if the connection is ok. */ + bool ok() const; +}; + +typedef ReferenceCountedPointer<class ReliableConduit> ReliableConduitRef; + +#ifdef __GNUC__ +// Workaround for a known bug in gcc 4.x where htonl produces +// a spurrious warning. +// http://gcc.gnu.org/ml/gcc-bugs/2005-10/msg03270.html +uint32 gcchtonl(uint32); +#endif + +// Messaging and stream APIs must be supported on a single class because +// sometimes an application will switch modes on a single socket. For +// example, when transferring 3D level geometry during handshaking with +// a game server. +/** + A conduit that guarantees messages will arrive, intact and in order. + Create on the client using NetworkDevice::createReliableConduit and + on the server using NetListener::waitForConnection. Set the reference + counted pointer to NULL to disconnect. + + To construct a ReliableConduit: + <OL> + <LI> Create a G3D::NetworkDevice (if you are using G3D::GApp, it creates + one for you) on the client and on the server. + <LI> On the server, create a G3D::NetListener using + G3D::NetworkDevice::createListener + <LI> On the server, invoke G3D::NetListener::waitForConnection. + <LI> On the client, call G3D::NetworkDevice::createReliableConduit. + You will need the server's G3D::NetAddress. Consider using + G3D::DiscoveryClient to find it via broadcasting. + </OL> + + */ +class ReliableConduit : public Conduit { +private: + friend class NetworkDevice; + friend class NetListener; + + enum State {RECEIVING, HOLDING, NO_MESSAGE} state; + + NetAddress addr; + + /** + Type of the incoming message. + */ + uint32 messageType; + + /** + Total size of the incoming message (read from the header). + */ + uint32 messageSize; + + /** Shared buffer for receiving messages. */ + void* receiveBuffer; + + /** Total size of the receiveBuffer. */ + size_t receiveBufferTotalSize; + + /** Size occupied by the current message... so far. This will be + equal to messageSize when the whole message has arrived. + */ + size_t receiveBufferUsedSize; + + ReliableConduit(const NetAddress& addr); + + ReliableConduit(const SOCKET& sock, + const NetAddress& addr); + + template<typename T> static void serializeMessage + (uint32 t, const T& m, BinaryOutput& b) { + + b.writeUInt32(t); + + // Reserve space for the 4 byte size header + b.writeUInt32(0); + + size_t L = b.length(); + m.serialize(b); + if ((size_t)b.length() == L) { + // No data was created by serialization. + // We need to send at least one byte because receive assumes that + // a zero length message is an error. + b.writeUInt8(0xFF); + } + + uint32 len = b.size() - 8; + + // We send the length first to tell recv how much data to read. + // Here we abuse BinaryOutput a bit and write directly into + // its buffer, violating the abstraction. + // Note that we write to the second set of 4 bytes, which is + // the size field. + uint32* lenPtr = ((uint32*)b.getCArray()) + 1; + #if defined(__GNUC__) + *lenPtr = gcchtonl(len); + #else + *lenPtr = htonl(len); + #endif + } + + + void sendBuffer(const BinaryOutput& b); + + /** Accumulates whatever part of the message (not the header) is + still waiting on the socket into the receiveBuffer during + state = RECEIVING mode. Closes the socket if anything goes + wrong. When receiveBufferUsedSize == messageSize, the entire + message has arrived. */ + void receiveIntoBuffer(); + + /** Receives the messageType and messageSize from the socket. */ + void receiveHeader(); + +public: + + /** + Client invokes this to connect to a server. The call blocks until the + conduit is opened. The conduit will not be ok() if it fails. + */ + static ReliableConduitRef create(const NetAddress& address); + + /** Closes the socket. */ + ~ReliableConduit(); + + + // The message is actually copied from the socket to an internal buffer during + // this call. Receive only deserializes. + virtual bool messageWaiting(); + + /** + Serializes the message and schedules it to be sent as soon as possible, + and then returns immediately. The message can be any <B>class</B> with + a serialize and deserialize method. On the receiving side, + use G3D::ReliableConduit::waitingMessageType() to detect the incoming + message and then invoke G3D::ReliableConduit::receive(msg) where msg + is of the same class as the message that was sent. + + The actual data sent across the network is preceeded by the + message type and the size of the serialized message as a 32-bit + integer. The size is sent because TCP is a stream protocol and + doesn't have a concept of discrete messages. + */ + template<typename T> inline void send(uint32 type, const T& message) { + binaryOutput.reset(); + serializeMessage(type, message, binaryOutput); + sendBuffer(binaryOutput); + } + + /** Sends an empty message with the given type. Useful for sending + commands that have no parameters. */ + void send(uint32 type); + + /** Send the same message to a number of conduits. Useful for sending + data from a server to many clients (only serializes once). */ + template<typename T> + inline static void multisend( + const Array<ReliableConduitRef>& array, + uint32 type, + const T& m) { + + if (array.size() > 0) { + array[0]->binaryOutput.reset(); + serializeMessage(type, m, array[0]->binaryOutput); + + for (int i = 0; i < array.size(); ++i) { + array[i]->sendBuffer(array[0]->binaryOutput); + } + } + } + + virtual uint32 waitingMessageType(); + + /** + If a message is waiting, deserializes the waiting message into + message and returns true, otherwise returns false. You can + determine the type of the message (and therefore, the class + of message) using G3D::ReliableConduit::waitingMessageType(). + */ + template<typename T> inline bool receive(T& message) { + if (! messageWaiting()) { + return false; + } + + debugAssert(state == HOLDING); + // Deserialize + BinaryInput b((uint8*)receiveBuffer, receiveBufferUsedSize, G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY); + message.deserialize(b); + + // Don't let anyone read this message again. We leave the buffer + // allocated for the next caller, however. + receiveBufferUsedSize = 0; + state = NO_MESSAGE; + messageType = 0; + messageSize = 0; + + // Potentially read the next message. + messageWaiting(); + + return true; + } + + /** Removes the current message from the queue. */ + inline void receive() { + if (! messageWaiting()) { + return; + } + receiveBufferUsedSize = 0; + state = NO_MESSAGE; + messageType = 0; + messageSize = 0; + + // Potentially read the next message. + messageWaiting(); + } + + NetAddress address() const; +}; + + +typedef ReferenceCountedPointer<class LightweightConduit> LightweightConduitRef; + +/** + Provides fast but unreliable transfer of messages. On a LAN, + LightweightConduit will probably never drop messages but you + <I>might</I> get your messages out of order. On an internet + connection it might drop messages altogether. Messages are never + corrupted, however. LightweightConduit requires a little less setup + and overhead than ReliableConduit. ReliableConduit guarantees + message delivery and order but requires a persistent connection. + + To set up a LightweightConduit (assuming you have already made + subclasses of G3D::NetMessage based on your application's + pcommunication protocol): + +[Server Side] +<OL> +<LI> Call LightweightConduit::create(port, true, false), +where port is the port on which you will receive messages. + +<LI> Poll LightweightConduit::messageWaiting from your main loop. When +it is true (or, equivalently, when LightweightConduit::waitingMessageType +is non-zero) there is an incoming message. + +<LI> To read the incoming message, call LightweightConduit::receive with +the appropriate class type, which mist have a deserialize method. +LightweightConduit::waitingMessageType tells you what class is +needed (you make up your own message constants for your program; numbers +under 1000 are reserved for G3D's internal use). + +<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let +it go out of scope and the conduit cleans itself up automatically. +</OL> + +[Client Side] +<OL> +<LI> Call G3D::LightweightConduit::create(). If you will +broadcast to all servers on a LAN, set the third optional argument to +true (the default is false for no broadcast). You can also set up the +receive port as if it was a server to send and receive from a single +LightweightConduit. + +<LI> To send, call G3D::LightweightConduit::send with the target address +and a pointer to an instance of the message you want to send. + +<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let +it go out of scope and the conduit cleans itself up automatically. + +</OL> + */ +class LightweightConduit : public Conduit { +private: + friend class NetworkDevice; + + /** + True when waitingForMessageType has read the message + from the network into messageType/messageStream. + */ + bool alreadyReadMessage; + + /** + Origin of the received message. + */ + NetAddress messageSender; + + /** + The type of the last message received. + */ + uint32 messageType; + + /** + The message received (the type has already been read off). + */ + Array<uint8> messageBuffer; + + LightweightConduit(uint16 receivePort, bool enableReceive, bool enableBroadcast); + + void sendBuffer(const NetAddress& a, BinaryOutput& b); + + /** Maximum transmission unit (packet size in bytes) for this socket. + May vary between sockets. */ + int MTU; + + + template<typename T> + void serializeMessage( + uint32 type, + const T& m, + BinaryOutput& b) const { + + debugAssert(type != 0); + b.writeUInt32(type); + m.serialize(b); + b.writeUInt32(1); + + debugAssertM(b.size() < MTU, + format("This LightweightConduit is limited to messages of " + "%d bytes (Ethernet hardware limit; this is the " + "'UDP MTU')", maxMessageSize())); + + if (b.size() >= MTU) { + throw LightweightConduit::PacketSizeException( + format("This LightweightConduit is limited to messages of " + "%d bytes (Ethernet hardware limit; this is the " + "'UDP MTU')", maxMessageSize()), + b.size() - 4, // Don't count the type header + maxMessageSize()); + } + } + +public: + + static LightweightConduitRef create(uint16 receivePort, bool enableReceive, bool enableBroadcast); + + class PacketSizeException { + public: + std::string message; + int serializedPacketSize; + int maxMessageSize; + + inline PacketSizeException(const std::string& m, int s, int b) : + message(m), + serializedPacketSize(s), + maxMessageSize(b) {} + }; + + /** Closes the socket. */ + ~LightweightConduit(); + + /** The maximum length of a message that can be sent + (G3D places a small header at the front of each UDP packet; + this is already taken into account by the value returned). + */ + inline int maxMessageSize() const { + return MTU - 4; + } + + + template<typename T> inline void send(const NetAddress& a, uint32 type, const T& msg) { + binaryOutput.reset(); + serializeMessage(type, msg, binaryOutput); + sendBuffer(a, binaryOutput); + } + + /** Send the same message to multiple addresses (only serializes once). + Useful when server needs to send to a known list of addresses + (unlike direct UDP broadcast to all addresses on the subnet) */ + template<typename T> inline void send(const Array<NetAddress>& a, uint32 type, const T& m) { + binaryOutput.reset(); + serializeMessage(type, m, binaryOutput); + + for (int i = 0; i < a.size(); ++i) { + sendBuffer(a[i], binaryOutput); + } + } + + bool receive(NetAddress& sender); + + template<typename T> inline bool receive(NetAddress& sender, T& message) { + bool r = receive(sender); + if (r) { + BinaryInput b((messageBuffer.getCArray() + 4), + messageBuffer.size() - 4, + G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY); + message.deserialize(b); + } + + return r; + } + + inline bool receive() { + static NetAddress ignore; + return receive(ignore); + } + + virtual uint32 waitingMessageType(); + + + virtual bool messageWaiting(); +}; + +/////////////////////////////////////////////////////////////////////////////// + +typedef ReferenceCountedPointer<class NetListener> NetListenerRef; + +/** + Runs on the server listening for clients trying to make reliable connections. + */ +class NetListener : public ReferenceCountedObject { +private: + + friend class NetworkDevice; + + SOCKET sock; + + /** Port is in host byte order. */ + NetListener(uint16 port); + +public: + + static NetListenerRef create(const uint16 port); + + ~NetListener(); + + /** Block until a connection is received. Returns NULL if + something went wrong. */ + ReliableConduitRef waitForConnection(); + + /** True if a client is waiting (i.e. waitForConnection will + return immediately). */ + bool clientWaiting() const; + + bool ok() const; +}; + + +/////////////////////////////////////////////////////////////////////////////// + +/** + @brief Abstraction of network (socket) functionality. + + An abstraction over sockets that provides a message-based network + infrastructure optimized for sending many small (~500 bytes) messages. + All functions always return immediately. + + Create only one NetworkDevice per process (a WinSock restriction). + + NetworkDevice is technically not thread safe. However, as long as + you use different conduits on different threads (or lock conduits + before sending), you will encounter no problems sharing the single + NetworkDevice across multiple threads. That is, do not invoke the same + Conduit's send or receive method on two threads at once. + + This assumes that the underlying WinSock/BSD sockets implementation + is thread safe. That is not guaranteed, but in practice seems + to always be true (see + http://tangentsoft.net/wskfaq/intermediate.html#threadsafety) + + <hr> + + IP networks use "network byte order" (big-endian) for + communicating integers. "Host byte order" is the endian-ness of + the local machine (typically little-endian; see + System::endian). The C functions htonl() and ntohl() convert 32-bit + values between these formats. G3D only ever exposes host byte order, + so programmers rarely need to be aware of the distinction. + + */ +class NetworkDevice { +public: + + /** @brief Description of an ethernet or wireless ethernet adapter.*/ + class EthernetAdapter { + public: + /** Reverse-DNS of the ip address.*/ + std::string hostname; + + /** Name of the adapter */ + std::string name; + + /** IP address in host byte order.*/ + uint32 ip; + + /** Subnet mask in host byte order.*/ + uint32 subnet; + + /** UDP broadcast address in host byte order.*/ + uint32 broadcast; + + /** MAC (hardware) address, if known */ + uint8 mac[6]; + + EthernetAdapter(); + + /** Produces a text description of this adapter */ + void describe(TextOutput& t) const; + }; + +private: + + friend class Conduit; + friend class LightweightConduit; + friend class ReliableConduit; + friend class NetListener; + + bool initialized; + + Array<EthernetAdapter> m_adapterArray; + + /** Broadcast addresses available on this machine, + extracted from m_adapterArray.*/ + Array<uint32> m_broadcastAddresses; + + /** Utility method. */ + void closesocket(SOCKET& sock) const; + + /** Utility method. Returns true on success.*/ + bool bind(SOCKET sock, const NetAddress& addr) const; + + /** The global instance */ + static NetworkDevice* s_instance; + + NetworkDevice(); + + bool init(); + + void _cleanup(); + + /** Called from init to update m_adapterArray and + m_broadcastAddresses. */ + void addAdapter(const EthernetAdapter& a); + +public: + + /** Prints an IP address to a string. + @param ip In host byte order.*/ + static std::string formatIP(uint32 ip); + + /** Prints a MAC address to a string. */ + static std::string formatMAC(const uint8 mac[6]); + + ~NetworkDevice(); + + /** Returns the available ethernet adapters for the current + machine that are online. Does not include the loopback adapter + for localhost.*/ + inline const Array<EthernetAdapter>& adapterArray() const { + return m_adapterArray; + } + + /** Returns the (unique) IP addresses for UDP broadcasting + extracted from adapterArray(). All are in host byte order. */ + inline const Array<uint32>& broadcastAddressArray() const { + return m_broadcastAddresses; + } + + /** + Returns NULL if there was a problem initializing the network. + */ + static NetworkDevice* instance(); + + /** + Shuts down the network device (destroying the global instance). + */ + static void cleanup(); + + /** + Prints a human-readable description of this machine + to the text output stream. + */ + void describeSystem( + TextOutput& t); + + void describeSystem( + std::string& s); + + /** Returns the name (or one of the names) of this computer */ + std::string localHostName() const; + + /** There is often more than one address for the local host. This + returns all of them. + @deprecated Use adapterArray() + */ + void localHostAddresses(Array<NetAddress>& array) const; +}; + + +#ifdef __GNUC__ +inline uint32 gcchtonl(uint32 x) { + // This pragma fools gcc into surpressing all error messages, + // including the bogus one that it creates for htonl +# pragma GCC system_header + return htonl(x); +} +#endif + +} // G3D namespace + +#ifndef _WIN32 +#undef SOCKADDR_IN +#undef SOCKET +#endif + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h b/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h new file mode 100644 index 00000000000..44eedb6846e --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h @@ -0,0 +1,74 @@ +/** + @file PhysicsFrame.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-07-08 + @edited 2006-01-10 +*/ + +#ifndef G3D_PHYSICSFRAME_H +#define G3D_PHYSICSFRAME_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Matrix3.h" +#include "G3D/Quat.h" +#include "G3D/CoordinateFrame.h" +#include <math.h> +#include <string> + + +namespace G3D { + +/** + An RT transformation using a quaternion; suitable for + physics integration. + + This interface is in "Beta" and will change in the next release. + */ +class PhysicsFrame { +public: + + Quat rotation; + + /** + Takes object space points to world space. + */ + Vector3 translation; + + /** + Initializes to the identity frame. + */ + PhysicsFrame(); + + /** + Purely translational force + */ + PhysicsFrame(const Vector3& translation) : translation(translation) {} + + PhysicsFrame(const CoordinateFrame& coordinateFrame); + + /** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/ + PhysicsFrame operator*(const PhysicsFrame& other) const; + + virtual ~PhysicsFrame() {} + + CoordinateFrame toCoordinateFrame() const; + + /** + Linear interpolation (spherical linear for the rotations). + */ + PhysicsFrame lerp( + const PhysicsFrame& other, + float alpha) const; + + void deserialize(class BinaryInput& b); + + void serialize(class BinaryOutput& b) const; + +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Plane.h b/externals/g3dlite/G3D.lib/include/G3D/Plane.h new file mode 100644 index 00000000000..1f0b15e0fe4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Plane.h @@ -0,0 +1,161 @@ +/** + @file Plane.h + + Plane class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-06-02 + @edited 2004-07-18 +*/ + +#ifndef G3D_PLANE_H +#define G3D_PLANE_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/debugAssert.h" + +namespace G3D { + +/** + An infinite 2D plane in 3D space. + */ +class Plane { +private: + + /** normal.Dot(x,y,z) = distance */ + Vector3 _normal; + float _distance; + + /** + Assumes the normal has unit length. + */ + Plane(const Vector3& n, float d) : _normal(n), _distance(d) { + } + +public: + + Plane() : _normal(Vector3::unitY()), _distance(0) { + } + + /** + Constructs a plane from three points. + */ + Plane( + const Vector3& point0, + const Vector3& point1, + const Vector3& point2); + + /** + Constructs a plane from three points, where at most two are + at infinity (w = 0, not xyz = inf). + */ + Plane( + Vector4 point0, + Vector4 point1, + Vector4 point2); + + /** + The normal will be unitized. + */ + Plane( + const Vector3& __normal, + const Vector3& point); + + static Plane fromEquation(float a, float b, float c, float d); + + Plane(class BinaryInput& b); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + virtual ~Plane() {} + + /** + Returns true if point is on the side the normal points to or + is in the plane. + */ + inline bool halfSpaceContains(Vector3 point) const { + // Clamp to a finite range for testing + point = point.clamp(Vector3::minFinite(), Vector3::maxFinite()); + + // We can get away with putting values *at* the limits of the float32 range into + // a dot product, since the dot product is carried out on float64. + return _normal.dot(point) >= _distance; + } + + /** + Returns true if point is on the side the normal points to or + is in the plane. + */ + inline bool halfSpaceContains(const Vector4& point) const { + if (point.w == 0) { + return _normal.dot(point.xyz()) > 0; + } else { + return halfSpaceContains(point.xyz() / point.w); + } + } + + /** + Returns true if point is on the side the normal points to or + is in the plane. Only call on finite points. Faster than halfSpaceContains. + */ + inline bool halfSpaceContainsFinite(const Vector3& point) const { + debugAssert(point.isFinite()); + return _normal.dot(point) >= _distance; + } + + /** + Returns true if the point is nearly in the plane. + */ + inline bool fuzzyContains(const Vector3 &point) const { + return fuzzyEq(point.dot(_normal), _distance); + } + + inline const Vector3& normal() const { + return _normal; + } + + /** + Returns distance from point to plane. Distance is negative if point is behind (not in plane in direction opposite normal) the plane. + */ + inline float distance(const Vector3& x) const { + return (_normal.dot(x) - _distance); + } + + inline Vector3 closestPoint(const Vector3& x) const { + return x + (_normal * (-distance(x))); + } + + /** Returns normal * distance from origin */ + Vector3 center() const { + return _normal * _distance; + } + + /** + Inverts the facing direction of the plane so the new normal + is the inverse of the old normal. + */ + void flip(); + + /** + Returns the equation in the form: + + <CODE>normal.Dot(Vector3(<I>x</I>, <I>y</I>, <I>z</I>)) + d = 0</CODE> + */ + void getEquation(Vector3 &normal, double& d) const; + void getEquation(Vector3 &normal, float& d) const; + + /** + ax + by + cz + d = 0 + */ + void getEquation(double& a, double& b, double& c, double& d) const; + void getEquation(float& a, float& b, float& c, float& d) const; + + std::string toString() const; +}; + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h new file mode 100644 index 00000000000..c8d527a45c2 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h @@ -0,0 +1,1207 @@ +/** + @file PointAABSPTree.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-11 + @edited 2008-11-02 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + + */ + +#ifndef X_PointKDTree_H +#define X_PointKDTree_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Table.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/CollisionDetection.h" +#include "G3D/GCamera.h" +#include "G3D/PositionTrait.h" +#include <algorithm> + + +/////////////////////////////////////////////////////// + +/** @deprecated */ +inline void getPosition(const G3D::Vector3& v, G3D::Vector3& p) { + p = v; +} + +/** @deprecated */ +inline void getPosition(const G3D::Vector4& v, G3D::Vector3& p) { + p = v.xyz(); +} + +/** @deprecated */ +inline void getPosition(const G3D::Vector2& v, G3D::Vector3& p) { + p.x = v.x; + p.y = v.y; + p.z = 0; +} + +namespace G3D { + +/** + A set data structure that supports spatial queries using an axis-aligned + BSP tree for speed. + + PointKDTree allows you to quickly find points in 3D that lie within + a box or sphere. For large sets of objects it is much faster + than testing each object for a collision. See also G3D::KDTree; this class + is optimized for point sets, e.g.,for use in photon mapping and mesh processing. + + <B>Template Parameters</B> + + <br> + + <br>The template parameter <I>T</I> must be one for which + the following functions are overloaded: + + <pre> + T::T(); <I>(public constructor of no arguments)</I> + + template<> struct PositionTrait<class T> { + static void getPosition(const T& v, G3D::Vector3& p);}; + + template <> struct HashTrait<class T> { + static size_t hashCode(const T& key);}; + + template<> struct EqualsTrait<class T> { + static bool equals(const T& a, const T& b); }; + </pre> + + <p> + + G3D provides these for the Vector2, Vector3, and Vector4 classes. + If you use a custom class, or a pointer to a custom class, you will need + to define those functions. + + <B>Moving %Set Members</B> + <DT>It is important that objects do not move without updating the + PointKDTree. If the position of an object is about + to change, PointKDTree::remove it before they change and + PointKDTree::insert it again afterward. For objects + where the hashCode and == operator are invariant with respect + to the 3D position, + you can use the PointKDTree::update method as a shortcut to + insert/remove an object in one step after it has moved. + + + Note: Do not mutate any value once it has been inserted into PointKDTree. Values + are copied interally. All PointKDTree iterators convert to pointers to constant + values to reinforce this. + + If you want to mutate the objects you intend to store in a PointKDTree + simply insert <I>pointers</I> to your objects instead of the objects + themselves, and ensure that the above operations are defined. (And + actually, because values are copied, if your values are large you may + want to insert pointers anyway, to save space and make the balance + operation faster.) + + <B>Dimensions</B> + Although designed as a 3D-data structure, you can use the PointKDTree + for data distributed along 2 or 1 axes by simply returning bounds + that are always zero along one or more dimensions. + +*/ +template<class T, + class PositionFunc = PositionTrait<T>, + class HashFunc = HashTrait<T>, + class EqualsFunc = EqualsTrait<T> > +class PointKDTree { +protected: +#define TreeType PointKDTree<T, PositionFunc, HashFunc, EqualsFunc> + + // Unlike the KDTree, the PointKDTree assumes that T elements are + // small and keeps the handle and cached position together instead of + // placing them in separate bounds arrays. Also note that a copy of T + // is kept in the member table and that there is no indirection. + class Handle { + private: + Vector3 m_position; + + public: + T value; + + inline Handle() {} + inline Handle(const T& v) : value(v) { + PositionFunc::getPosition(v, m_position); + } + + /** Used by makeNode to create fake handles for partitioning. */ + void setPosition(const Vector3& v) { + m_position = v; + } + + inline const Vector3& position() const { + return m_position; + } + }; + + /** Returns the bounds of the sub array. Used by makeNode. */ + static AABox computeBounds( + const Array<Handle>& point) { + + if (point.size() == 0) { + return AABox(Vector3::inf(), Vector3::inf()); + } + + AABox bounds(point[0].position()); + + for (int p = 0; p < point.size(); ++p) { + bounds.merge(point[p].position()); + } + + return bounds; + } + + class Node { + public: + + /** Spatial bounds on all values at this node and its children, based purely on + the parent's splitting planes. May be infinite */ + AABox splitBounds; + + Vector3::Axis splitAxis; + + /** Location along the specified axis */ + float splitLocation; + + /** child[0] contains all values strictly + smaller than splitLocation along splitAxis. + + child[1] contains all values strictly + larger. + + Both may be NULL if there are not enough + values to bother recursing. + */ + Node* child[2]; + + /** Values if this is a leaf node). */ + Array<Handle> valueArray; + + /** Creates node with NULL children */ + Node() { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + splitBounds = AABox(-Vector3::inf(), Vector3::inf()); + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** + Doesn't clone children. + */ + Node(const Node& other) : valueArray(other.valueArray) { + splitAxis = other.splitAxis; + splitLocation = other.splitLocation; + splitBounds = other.splitBounds; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** Copies the specified subarray of pt into point, NULLs the children. + Assumes a second pass will set splitBounds. */ + Node(const Array<Handle>& pt) { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + valueArray = pt; + } + + + /** Deletes the children (but not the values) */ + ~Node() { + for (int i = 0; i < 2; ++i) { + delete child[i]; + } + } + + + /** Returns true if this node is a leaf (no children) */ + inline bool isLeaf() const { + return (child[0] == NULL) && (child[1] == NULL); + } + + + /** + Recursively appends all handles and children's handles + to the array. + */ + void getHandles(Array<Handle>& handleArray) const { + handleArray.append(valueArray); + for (int i = 0; i < 2; ++i) { + if (child[i] != NULL) { + child[i]->getHandles(handleArray); + } + } + } + + + void verifyNode(const Vector3& lo, const Vector3& hi) { + // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n", + // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z); + + debugAssert(lo == splitBounds.low()); + debugAssert(hi == splitBounds.high()); + + for (int i = 0; i < valueArray.length(); ++i) { + const Vector3& b = valueArray[i].position(); + debugAssert(splitBounds.contains(b)); + } + + if (child[0] || child[1]) { + debugAssert(lo[splitAxis] < splitLocation); + debugAssert(hi[splitAxis] > splitLocation); + } + + Vector3 newLo = lo; + newLo[splitAxis] = splitLocation; + Vector3 newHi = hi; + newHi[splitAxis] = splitLocation; + + if (child[0] != NULL) { + child[0]->verifyNode(lo, newHi); + } + + if (child[1] != NULL) { + child[1]->verifyNode(newLo, hi); + } + } + + + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + static void serializeStructure(const Node* n, BinaryOutput& bo) { + if (n == NULL) { + bo.writeUInt8(0); + } else { + bo.writeUInt8(1); + n->splitBounds.serialize(bo); + serialize(n->splitAxis, bo); + bo.writeFloat32(n->splitLocation); + for (int c = 0; c < 2; ++c) { + serializeStructure(n->child[c], bo); + } + } + } + + /** Clears the member table */ + static Node* deserializeStructure(BinaryInput& bi) { + if (bi.readUInt8() == 0) { + return NULL; + } else { + Node* n = new Node(); + n->splitBounds.deserialize(bi); + deserialize(n->splitAxis, bi); + n->splitLocation = bi.readFloat32(); + for (int c = 0; c < 2; ++c) { + n->child[c] = deserializeStructure(bi); + } + } + } + + /** Returns the deepest node that completely contains bounds. */ + Node* findDeepestContainingNode(const Vector3& point) { + + // See which side of the splitting plane the bounds are on + if (point[splitAxis] < splitLocation) { + // Point is on the low side. Recurse into the child + // if it exists. + if (child[0] != NULL) { + return child[0]->findDeepestContainingNode(point); + } + } else if (point[splitAxis] > splitLocation) { + // Point is on the high side, recurse into the child + // if it exists. + if (child[1] != NULL) { + return child[1]->findDeepestContainingNode(point); + } + } + + // There was no containing child, so this node is the + // deepest containing node. + return this; + } + + /** Appends all members that intersect the box. + If useSphere is true, members are tested against the sphere instead. */ + void getIntersectingMembers( + const AABox& sphereBounds, + const Sphere& sphere, + Array<T>& members) const { + + // Test all values at this node. Extract the + // underlying C array for speed + const int N = valueArray.size(); + const Handle* handleArray = valueArray.getCArray(); + + const float r2 = square(sphere.radius); + + // Copy the sphere center so that it is on the stack near the radius + const Vector3 center = sphere.center; + for (int v = 0; v < N; ++v) { + if ((center - handleArray[v].position()).squaredLength() <= r2) { + members.append(handleArray[v].value); + } + } + + // If the left child overlaps the box, recurse into it + if (child[0] && (sphereBounds.low()[splitAxis] < splitLocation)) { + child[0]->getIntersectingMembers(sphereBounds, sphere, members); + } + + // If the right child overlaps the box, recurse into it + if (child[1] && (sphereBounds.high()[splitAxis] > splitLocation)) { + child[1]->getIntersectingMembers(sphereBounds, sphere, members); + } + } + + /** Appends all members that intersect the box. + If useSphere is true, members are tested against the sphere instead. + + Implemented using both box and sphere tests to simplify the implementation + of a future beginSphereInteresection iterator using the same underlying + BoxIterator class. + */ + void getIntersectingMembers( + const AABox& box, + const Sphere& sphere, + Array<T>& members, + bool useSphere) const { + + // Test all values at this node + for (int v = 0; v < valueArray.size(); ++v) { + if ((useSphere && sphere.contains(valueArray[v].position())) || + (! useSphere && box.contains(valueArray[v].position()))) { + members.append(valueArray[v].value); + } + } + + // If the left child overlaps the box, recurse into it + if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) { + child[0]->getIntersectingMembers(box, sphere, members, useSphere); + } + + // If the right child overlaps the box, recurse into it + if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) { + child[1]->getIntersectingMembers(box, sphere, members, useSphere); + } + } + + /** + Recurse through the tree, assigning splitBounds fields. + */ + void assignSplitBounds(const AABox& myBounds) { + splitBounds = myBounds; + +# ifdef G3D_DEBUG + if (child[0] || child[1]) { + debugAssert(splitBounds.high()[splitAxis] > splitLocation); + debugAssert(splitBounds.low()[splitAxis] < splitLocation); + } +# endif + + AABox childBounds[2]; + myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]); + + for (int c = 0; c < 2; ++c) { + if (child[c]) { + child[c]->assignSplitBounds(childBounds[c]); + } + } + } + }; + + class AxisComparator { + private: + Vector3::Axis sortAxis; + + public: + + AxisComparator(Vector3::Axis s) : sortAxis(s) {} + + inline int operator()(const Handle& A, const Handle& B) const { + if (A.position()[sortAxis] > B.position()[sortAxis]) { + return -1; + } else if (A.position()[sortAxis] < B.position()[sortAxis]) { + return 1; + } else { + return 0; + } + } + }; + + /** + Recursively subdivides the subarray. + + The source array will be cleared after it is used + + Call assignSplitBounds() on the root node after making a tree. + */ + Node* makeNode( + Array<Handle>& source, + Array<Handle>& temp, + int valuesPerNode, + int numMeanSplits) { + + Node* node = NULL; + + if (source.size() <= valuesPerNode) { + // Make a new leaf node + node = new Node(source); + + // Set the pointers in the memberTable + for (int i = 0; i < source.size(); ++i) { + memberTable.set(source[i].value, node); + } + + } else { + // Make a new internal node + node = new Node(); + + const AABox bounds = computeBounds(source); + const Vector3 extent = bounds.high() - bounds.low(); + + Vector3::Axis splitAxis = extent.primaryAxis(); + + float splitLocation; + + Array<Handle> lt, gt; + + if (numMeanSplits <= 0) { + source.medianPartition(lt, node->valueArray, gt, temp, AxisComparator(splitAxis)); + splitLocation = node->valueArray[0].position()[splitAxis]; + + if ((node->valueArray.size() > source.size() / 2) && + (source.size() > 10)) { + // Our median split put an awful lot of points on the splitting plane. Try a mean + // split instead + numMeanSplits = 1; + } + } + + if (numMeanSplits > 0) { + // Compute the mean along the axis + + splitLocation = (bounds.high()[splitAxis] + + bounds.low()[splitAxis]) / 2.0; + + Handle splitHandle; + Vector3 v; + v[splitAxis] = splitLocation; + splitHandle.setPosition(v); + + source.partition(splitHandle, lt, node->valueArray, gt, AxisComparator(splitAxis)); + } + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + for (int i = 0; i < lt.size(); ++i) { + const Vector3& v = lt[i].position(); + debugAssert(v[splitAxis] < splitLocation); + } + for (int i = 0; i < gt.size(); ++i) { + debugAssert(gt[i].position()[splitAxis] > splitLocation); + } + for (int i = 0; i < node->valueArray.size(); ++i) { + debugAssert(node->valueArray[i].position()[splitAxis] == splitLocation); + } +# endif + + node->splitAxis = splitAxis; + node->splitLocation = splitLocation; + + // Throw away the source array to save memory + source.fastClear(); + + if (lt.size() > 0) { + node->child[0] = makeNode(lt, temp, valuesPerNode, numMeanSplits - 1); + } + + if (gt.size() > 0) { + node->child[1] = makeNode(gt, temp, valuesPerNode, numMeanSplits - 1); + } + + // Add the values stored at this interior node to the member table + for(int i = 0; i < node->valueArray.size(); ++i) { + memberTable.set(node->valueArray[i].value, node); + } + + } + + return node; + } + + /** + Recursively clone the passed in node tree, setting + pointers for members in the memberTable as appropriate. + called by the assignment operator. + */ + Node* cloneTree(Node* src) { + Node* dst = new Node(*src); + + // Make back pointers + for (int i = 0; i < dst->valueArray.size(); ++i) { + memberTable.set(dst->valueArray[i].value, dst); + } + + // Clone children + for (int i = 0; i < 2; ++i) { + if (src->child[i] != NULL) { + dst->child[i] = cloneTree(src->child[i]); + } + } + + return dst; + } + + /** Maps members to the node containing them */ + typedef Table<T, Node*, HashFunc, EqualsFunc> MemberTable; + MemberTable memberTable; + + Node* root; + +public: + + /** To construct a balanced tree, insert the elements and then call + PointKDTree::balance(). */ + PointKDTree() : root(NULL) {} + + + PointKDTree(const PointKDTree& src) : root(NULL) { + *this = src; + } + + + PointKDTree& operator=(const PointKDTree& src) { + delete root; + // Clone tree takes care of filling out the memberTable. + root = cloneTree(src.root); + return *this; + } + + + ~PointKDTree() { + clear(); + } + + /** + Throws out all elements of the set and erases the structure of the tree. + */ + void clear() { + memberTable.clear(); + delete root; + root = NULL; + } + + /** Removes all elements of the set while maintaining the structure of the tree */ + void clearData() { + memberTable.clear(); + Array<Node*> stack; + stack.push(root); + while (stack.size() > 0) { + Node* node = stack.pop(); + node->valueArray.fastClear(); + + for (int i = 0; i < 2; ++i) { + if (node->child[i] != NULL) { + stack.push(node->child[i]); + } + } + } + } + + + int size() const { + return memberTable.size(); + } + + /** + Inserts an object into the set if it is not + already present. O(log n) time. Does not + cause the tree to be balanced. + */ + void insert(const T& value) { + if (contains(value)) { + // Already in the set + return; + } + + Handle h(value); + + if (root == NULL) { + // This is the first node; create a root node + root = new Node(); + } + + Node* node = root->findDeepestContainingNode(h.position()); + + // Insert into the node + node->valueArray.append(h); + + // Insert into the node table + memberTable.set(value, node); + } + + /** Inserts each elements in the array in turn. If the tree + begins empty (no structure and no elements), this is faster + than inserting each element in turn. You still need to balance + the tree at the end.*/ + void insert(const Array<T>& valueArray) { + // Pre-size the member table to avoid multiple allocations + memberTable.setSizeHint(valueArray.size() + size()); + + if (root == NULL) { + // Optimized case for an empty tree; don't bother + // searching or reallocating the root node's valueArray + // as we incrementally insert. + root = new Node(); + root->valueArray.resize(valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + // Insert in opposite order so that we have the exact same + // data structure as if we inserted each (i.e., order is reversed + // from array). + root->valueArray[valueArray.size() - i - 1] = Handle(valueArray[i]); + memberTable.set(valueArray[i], root); + } + } else { + // Insert at appropriate tree depth. + for (int i = 0; i < valueArray.size(); ++i) { + insert(valueArray[i]); + } + } + } + + + /** + Returns true if this object is in the set, otherwise + returns false. O(1) time. + */ + bool contains(const T& value) { + return memberTable.containsKey(value); + } + + + /** + Removes an object from the set in O(1) time. + It is an error to remove members that are not already + present. May unbalance the tree. + + Removing an element never causes a node (split plane) to be removed... + nodes are only changed when the tree is rebalanced. This behavior + is desirable because it allows the split planes to be serialized, + and then deserialized into an empty tree which can be repopulated. + */ + void remove(const T& value) { + debugAssertM(contains(value), + "Tried to remove an element from a " + "PointKDTree that was not present"); + + Array<Handle>& list = memberTable[value]->valueArray; + + // Find the element and remove it + for (int i = list.length() - 1; i >= 0; --i) { + if (list[i].value == value) { + list.fastRemove(i); + break; + } + } + memberTable.remove(value); + } + + + /** + If the element is in the set, it is removed. + The element is then inserted. + + This is useful when the == and hashCode methods + on <I>T</I> are independent of the bounds. In + that case, you may call update(v) to insert an + element for the first time and call update(v) + again every time it moves to keep the tree + up to date. + */ + void update(const T& value) { + if (contains(value)) { + remove(value); + } + insert(value); + } + + + /** + Rebalances the tree (slow). Call when objects + have moved substantially from their original positions + (which unbalances the tree and causes the spatial + queries to be slow). + + @param valuesPerNode Maximum number of elements to put at + a node. + + @param numMeanSplits numMeanSplits = 0 gives a + fully axis aligned BSP-tree, where the balance operation attempts to balance + the tree so that every splitting plane has an equal number of left + and right children (i.e. it is a <B>median</B> split along that axis). + This tends to maximize average performance; all querries will return in the same amount of time. + + You can override this behavior by + setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT + creates a full oct-tree, which tends to optimize peak performance (some areas of the scene will terminate after few recursive splits) at the expense of + peak performance. + */ + void balance(int valuesPerNode = 40, int numMeanSplits = 3) { + if (root == NULL) { + // Tree is empty + return; + } + + Array<Handle> handleArray; + root->getHandles(handleArray); + + // Delete the old tree + clear(); + + Array<Handle> temp; + root = makeNode(handleArray, temp, valuesPerNode, numMeanSplits); + temp.fastClear(); + + // Walk the tree, assigning splitBounds. We start with unbounded + // space. + root->assignSplitBounds(AABox::maxFinite()); + +# ifdef _DEBUG + root->verifyNode(Vector3::minFinite(), Vector3::maxFinite()); +# endif + } + +private: + + /** + Returns the elements + + @param parentMask The mask that this node returned from culledBy. + */ + static void getIntersectingMembers( + const Array<Plane>& plane, + Array<T>& members, + Node* node, + uint32 parentMask) { + + int dummy; + + if (parentMask == 0) { + // None of these planes can cull anything + for (int v = node->valueArray.size() - 1; v >= 0; --v) { + members.append(node->valueArray[v].value); + } + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c]) { + getIntersectingMembers(plane, members, node->child[c], 0); + } + } + } else { + + if (node->valueArray.size() > 0) { + // This is a leaf; check the points + debugAssertM(node->child[0] == NULL, "Malformed Point tree"); + debugAssertM(node->child[1] == NULL, "Malformed Point tree"); + + // Test values at this node against remaining planes + for (int p = 0; p < plane.size(); ++p) { + if ((parentMask >> p) & 1 != 0) { + // Test against this plane + const Plane& curPlane = plane[p]; + for (int v = node->valueArray.size() - 1; v >= 0; --v) { + if (curPlane.halfSpaceContains(node->valueArray[v].position())) { + members.append(node->valueArray[v].value); + } + } + } + } + } else { + + uint32 childMask = 0xFFFFFF; + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c] && + ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) { + // This node was not culled + getIntersectingMembers(plane, members, node->child[c], childMask); + } + } + } + } + } + +public: + + /** + Returns all members inside the set of planes. + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const { + if (root == NULL) { + return; + } + + getIntersectingMembers(plane, members, root, 0xFFFFFF); + } + + /** + Typically used to find all visible + objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects + <B>not<B> culled by frustum. + + Example: + <PRE> + Array<Object*> visible; + tree.getIntersectingMembers(camera.frustum(), visible); + // ... Draw all objects in the visible array. + </PRE> + @param members The results are appended to this array. + */ + void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const { + Array<Plane> plane; + + for (int i = 0; i < frustum.faceArray.size(); ++i) { + plane.append(frustum.faceArray[i].plane); + } + + getIntersectingMembers(plane, members); + } + + /** + C++ STL style iterator variable. See beginBoxIntersection(). + The iterator overloads the -> (dereference) operator, so this + acts like a pointer to the current member. + */ + // This iterator turns Node::getIntersectingMembers into a + // coroutine. It first translates that method from recursive to + // stack based, then captures the system state (analogous to a Scheme + // continuation) after each element is appended to the member array, + // and allowing the computation to be restarted. + class BoxIntersectionIterator { + private: + friend class TreeType; + + /** True if this is the "end" iterator instance */ + bool isEnd; + + /** The box that we're testing against. */ + AABox box; + + /** Node that we're currently looking at. Undefined if isEnd + is true. */ + Node* node; + + /** Nodes waiting to be processed */ + // We could use backpointers within the tree and careful + // state management to avoid ever storing the stack-- but + // it is much easier this way and only inefficient if the + // caller uses post increment (which they shouldn't!). + Array<Node*> stack; + + /** The next index of current->valueArray to return. + Undefined when isEnd is true.*/ + int nextValueArrayIndex; + + BoxIntersectionIterator() : isEnd(true) {} + + BoxIntersectionIterator(const AABox& b, const Node* root) : + isEnd(root == NULL), box(b), + node(const_cast<Node*>(root)), nextValueArrayIndex(-1) { + + // We intentionally start at the "-1" index of the current + // node so we can use the preincrement operator to move + // ourselves to element 0 instead of repeating all of the + // code from the preincrement method. Note that this might + // cause us to become the "end" instance. + ++(*this); + } + + public: + + inline bool operator!=(const BoxIntersectionIterator& other) const { + return ! (*this == other); + } + + bool operator==(const BoxIntersectionIterator& other) const { + if (isEnd) { + return other.isEnd; + } else if (other.isEnd) { + return false; + } else { + // Two non-end iterators; see if they match. This is kind of + // silly; users shouldn't call == on iterators in general unless + // one of them is the end iterator. + if ((box != other.box) || (node != other.node) || + (nextValueArrayIndex != other.nextValueArrayIndex) || + (stack.length() != other.stack.length())) { + return false; + } + + // See if the stacks are the same + for (int i = 0; i < stack.length(); ++i) { + if (stack[i] != other.stack[i]) { + return false; + } + } + + // We failed to find a difference; they must be the same + return true; + } + } + + /** + Pre increment. + */ + BoxIntersectionIterator& operator++() { + ++nextValueArrayIndex; + + bool foundIntersection = false; + while (! isEnd && ! foundIntersection) { + + // Search for the next node if we've exhausted this one + while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) { + // If we entered this loop, then the iterator has exhausted the elements at + // node (possibly because it just switched to a child node with no members). + // This loop continues until it finds a node with members or reaches + // the end of the whole intersection search. + + // If the right child overlaps the box, push it onto the stack for + // processing. + if ((node->child[1] != NULL) && + (box.high()[node->splitAxis] > node->splitLocation)) { + stack.push(node->child[1]); + } + + // If the left child overlaps the box, push it onto the stack for + // processing. + if ((node->child[0] != NULL) && + (box.low()[node->splitAxis] < node->splitLocation)) { + stack.push(node->child[0]); + } + + if (stack.length() > 0) { + // Go on to the next node (which may be either one of the ones we + // just pushed, or one from farther back the tree). + node = stack.pop(); + nextValueArrayIndex = 0; + } else { + // That was the last node; we're done iterating + isEnd = true; + } + } + + // Search for the next intersection at this node until we run out of children + while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) { + if (box.intersects(node->valueArray[nextValueArrayIndex].bounds)) { + foundIntersection = true; + } else { + ++nextValueArrayIndex; + // If we exhaust this node, we'll loop around the master loop + // to find a new node. + } + } + } + + return *this; + } + + /** + Post increment (much slower than preincrement!). + */ + BoxIntersectionIterator operator++(int) { + BoxIntersectionIterator old = *this; + ++this; + return old; + } + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + const T& operator*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return node->valueArray[nextValueArrayIndex].value; + } + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + T const * operator->() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex].value); + } + + /** Overloaded cast operator so the iterator can masquerade as a pointer + to a member */ + operator T*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex].value); + } + }; + + + /** + Iterates through the members that intersect the box + */ + BoxIntersectionIterator beginBoxIntersection(const AABox& box) const { + return BoxIntersectionIterator(box, root); + } + + BoxIntersectionIterator endBoxIntersection() const { + // The "end" iterator instance + return BoxIntersectionIterator(); + } + + /** + Appends all members whose bounds intersect the box. + See also PointKDTree::beginBoxIntersection. + */ + void getIntersectingMembers(const AABox& box, Array<T>& members) const { + if (root == NULL) { + return; + } + root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false); + } + + + /** + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const { + if (root == NULL) { + return; + } + + AABox box; + sphere.getBounds(box); + root->getIntersectingMembers(box, sphere, members); + + } + + + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + void serializeStructure(BinaryOutput& bo) const { + Node::serializeStructure(root, bo); + } + + /** Clears the member table */ + void deserializeStructure(BinaryInput& bi) { + clear(); + root = Node::deserializeStructure(bi); + } + + /** + Returns an array of all members of the set. See also PointKDTree::begin. + */ + void getMembers(Array<T>& members) const { + memberTable.getKeys(members); + } + + + /** + C++ STL style iterator variable. See begin(). + Overloads the -> (dereference) operator, so this acts like a pointer + to the current member. + */ + class Iterator { + private: + friend class TreeType; + + // Note: this is a Table iterator, we are currently defining + // Set iterator + typename MemberTable::Iterator it; + + Iterator(const typename MemberTable::Iterator& it) : it(it) {} + + public: + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + bool operator==(const Iterator& other) const { + return it == other.it; + } + + /** + Pre increment. + */ + Iterator& operator++() { + ++it; + return *this; + } + + /** + Post increment (slower than preincrement). + */ + Iterator operator++(int) { + Iterator old = *this; + ++(*this); + return old; + } + + const T& operator*() const { + return it->key; + } + + T* operator->() const { + return &(it->key); + } + + operator T*() const { + return &(it->key); + } + }; + + + /** + C++ STL style iterator method. Returns the first member. + Use preincrement (++entry) to get to the next element (iteration + order is arbitrary). + Do not modify the set while iterating. + */ + Iterator begin() const { + return Iterator(memberTable.begin()); + } + + + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + Iterator end() const { + return Iterator(memberTable.end()); + } +#undef TreeType +}; + +#define PointAABSPTree PointKDTree + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h b/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h new file mode 100644 index 00000000000..8e9726e82da --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h @@ -0,0 +1,894 @@ +/** + @file PointHashGrid.h + + @maintainer Morgan McGuire, morgan@cs.williams.edu + @created 2008-07-01 + @edited 2008-11-02 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. +*/ +#ifndef G3D_POINTHASHGRID_H +#define G3D_POINTHASHGRID_H + +#include "G3D/platform.h" +#include "G3D/EqualsTrait.h" +#include "G3D/HashTrait.h" +#include "G3D/Vector3.h" +#include "G3D/Vector3int32.h" +#include "G3D/Array.h" +#include "G3D/Table.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" + +namespace G3D { + +/** + Storage of data in a sparse 3D grid of point-based data. The + space cost for <I>n</I> elements is O(<I>n</I>). For data with + approximately uniform density (with respect to the radius hint), + the time cost of searching for neighbors is O(1). + + <i>Value</i> must be supported by a G3D::PositionTrait, + G3D::EqualsTrait, and G3D::HashFunc. overrides are provided for + common G3D classes like G3D::Vector3. +*/ +template<class Value, + class PosFunc = PositionTrait<Value>, + class EqualsFunc = EqualsTrait<Value>, + class HashFunc = HashTrait<Vector3int32> > +class PointHashGrid { +private: + +#define ThisType PointHashGrid<Value, PosFunc, EqualsFunc, HashFunc> + + /** A value annotated with precomputed position and hash code.*/ + class Entry { + public: + Vector3 position; + Value value; + }; + + /** One cell of the grid. */ + typedef Array<Entry> Cell; + typedef Table<Vector3int32, Cell, HashFunc> CellTable; + + /** The cube of +/-1 along each dimension. Initialized by initOffsetArray.*/ + Vector3int32 m_offsetArray[3*3*3]; + + /** Incremented every time the data structure is mutated. + Used by the iterators to determine if the data structure + has changed since iteration began. */ + int m_epoch; + + /** Extent of a cell along one dimension. */ + float m_cellWidth; + + /** Conservative bounds; the actual data may be smaller. */ + AABox m_bounds; + + /** Number of elements. */ + int m_size; + + /** Non-empty cells indexed by grid position. Actual 3D position is + <code>position * m_cellWidth</code>*/ + CellTable m_data; + + + /** Intentionally unimplemented: prevent copy construction. */ + PointHashGrid(const ThisType&); + + + /** Intentionally unimplemented: prevent assignment. */ + PointHashGrid& operator=(const ThisType&); + + + /** Locate the cell and index within that cell containing v. Called by + remove() and contains(). */ + bool find( + const Value& v, + Vector3int32& foundCellCoord, + Cell*& foundCell, + int& index) { + + Vector3 pos; + PosFunc::getPosition(v, pos); + + Vector3int32 cellCoord; + getCellCoord(pos, cellCoord); + for (int i = 0; i < 27; ++i) { + Vector3int32 c = cellCoord + m_offsetArray[i]; + Cell* cell = m_data.getPointer(c); + if (cell != NULL) { + // The cell exists + for (int j = 0; j < cell->size(); ++j) { + if (EqualsFunc::equals((*cell)[j].value, v)) { + foundCell = cell; + index = j; + foundCellCoord = c; + return true; + } + } + } + } + + // Not found + return false; + } + + /** Given a real-space position, returns the cell coord + containing it.*/ + void getCellCoord(const Vector3& pos, Vector3int32& cellCoord) const { + for (int a = 0; a < 3; ++a) { + cellCoord[a] = iFloor(pos[a] / m_cellWidth); + } + } + + /** Initializes m_offsetArray. */ + void initOffsetArray() { + int i = 0; + Vector3int32 d; + for (d.x = -1; d.x <= +1; ++d.x) { + for (d.y = -1; d.y <= +1; ++d.y) { + for (d.z = -1; d.z <= +1; ++d.z) { + m_offsetArray[i] = d; + ++i; + } + } + } + + // Put (0, 0, 0) first, so that contains() is most likely to find + // the value quickly. + i = (1 * 3 + 1) * 3 + 1; + debugAssert(m_offsetArray[i] == Vector3int32(0,0,0)); + Vector3int32 temp = m_offsetArray[0]; + m_offsetArray[0] = m_offsetArray[i]; + m_offsetArray[i] = temp; + } + +public: + + /** + @param radiusHint the radius that will typically be used with + beginSphereIntersection and beginBoxIntersection. If two <i>Value</i>s are equal, + their positions must be within this radius as well. + */ + PointHashGrid(float radiusHint) : m_size(0) { + initOffsetArray(); + + debugAssertM(radiusHint > 0, "Cell radius must be positive"); + m_cellWidth = radiusHint; + } + + /** + If radiusHint is negative, it is automatically chosen to put + about 5 values in each grid cell (which means about 27 * 5 + values for each beginIntersection call). + */ + PointHashGrid(const Array<Value>& init, float radiusHint = -1.0f) : m_size(0) { + initOffsetArray(); + + Vector3 lo(Vector3::inf()); + Vector3 hi(-lo); + + // Compute bounds + Array<Entry> entry(init.size()); + for (int i = 0; i < entry.size(); ++i) { + const Value& value = init[i]; + Vector3 pos = m_posFunc(value); + + entry[i].value = value; + entry[i].hashCode = m_hashFunc(value); + entry[i].position = pos; + + lo = lo.min(pos); + hi = hi.max(pos); + } + + m_bounds = AABox(lo, hi); + + if (radiusHint <= 0) { + // Compute a good cell width based on the bounds. + // + // N numPerCell + // ----- = --------- + // volume r^3 + + float numPerCell = 5; + radiusHint = + (float)pow(numPerCell * m_bounds.volume() / init.size(), 1.0 / 3.0); + + if (radiusHint == 0) { + // Volume must have been zero because all points were colocated. + radiusHint = 0.1f; + } + } + + insert(init); + } + + /** Returns the number of elements. */ + inline int size() const { + return m_size; + } + + /** Returns a conservative bounding box around the contents. This is + conservative because it is not updated when elements are removed. */ + const AABox& conservativeBoxBounds() const { + return m_bounds; + } + + /** Insert @a v at position @a p given by <code>getPosition(v, p)</code>. + Multiple elements that are equal may be inserted; all copies will be + in the data structure. */ + void insert(const Value& v) { + Vector3 pos; + PosFunc::getPosition(v, pos); + Vector3int32 cellCoord; + getCellCoord(pos, cellCoord); + + // See if the cell already exists + Cell* cell = m_data.getPointer(cellCoord); + + if (cell == NULL) { + // The cell did not exist; create it + m_data.set(cellCoord, Cell()); + cell = m_data.getPointer(cellCoord); + } + debugAssert(cell != NULL); + + Entry& entry = cell->next(); + entry.value = v; + entry.position = pos; + + // Update the bounds + if (size() == 0) { + m_bounds = AABox(pos); + } else { + m_bounds.merge(pos); + } + + ++m_size; + ++m_epoch; + } + + + /** Inserts all elements of the array. */ + void insert(const Array<Value>& v) { + for (int i = 0; i < v.size(); ++i) { + insert(v[i]); + } + } + + + /** If there are multiple copies of an element, you must + delete them multiple times. + + @param shrinkAsNeeded If <b>true</b>, deallocate underlying data + structures as they are emptied. False increases performace at + the cost of memory overhead for dynamic structures. + + @return true if the element was found. + */ + bool remove(const Value& v, bool shrinkIfNecessary = true) { + Cell* cell = NULL; + int index = 0; + Vector3int32 cellCoord; + + if (find(v, cellCoord, cell, index)) { + cell->fastRemove(index, shrinkIfNecessary); + --m_size; + ++m_epoch; + + if ((cell->size() == 0) && shrinkIfNecessary) { + // Remove the cell itself + + // Drop our pointer, which is about to dangle + cell = NULL; + bool success = m_data.remove(cellCoord); + debugAssertM(success, "Data structure corrupt: " + "tried to remove a cell that doesn't exist."); + } + + return true; + + } else { + return false; + } + } + + /** Removes all elements of @v. */ + void remove(const Array<Value>& v, bool shrink = true) { + for (int i = 0; i < v.size(); ++i) { + remove(v[i], shrink); + } + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + class Iterator { + private: + friend class ThisType; + + bool m_isEnd; + + const ThisType* m_grid; + + typename CellTable::Iterator m_tableIterator; + + /** Index within m_tableIterator->value of the current value. */ + int m_arrayIndex; + + const int m_epoch; + + /** End iterator. Note that the m_tableIterator is initialized to the end iterator + of a temporary value! This is ok because we'll never look at the value of the + m_tableIterator, since we're initializing the "end" Iterator.*/ + Iterator() : m_isEnd(true), m_grid(NULL), m_tableIterator(CellTable().end()), + m_arrayIndex(0), m_epoch(0) {} + + Iterator(const ThisType* grid) : + m_isEnd(false), + m_grid(grid), + m_tableIterator( grid->m_data.begin() ), + m_arrayIndex(0), + m_epoch(grid->m_epoch) { } + + private: + + const Value& value() const { + debugAssert(! m_isEnd); + debugAssertM(m_tableIterator->value.size() > m_arrayIndex, + "No more elements"); + return m_tableIterator->value[m_arrayIndex].value; + } + + public: + + inline bool operator!=(const Iterator& other) const { + if (other.m_isEnd && m_isEnd) { + return false; + } else { + return (m_isEnd != other.m_isEnd) || + (m_tableIterator != other.m_tableIterator) || + (m_arrayIndex != other.m_arrayIndex); + } + } + + bool operator==(const Iterator& other) const { + return !(*this != other); + } + + /** Preincrement */ + Iterator& operator++() { + debugAssert(! m_isEnd); + debugAssertM(m_epoch == m_grid->m_epoch, + "It is illegal to mutate the HashGrid " + "while iterating through it."); + + ++m_arrayIndex; + + if (m_arrayIndex >= m_tableIterator->value.size()) { + // Move on to the next cell + ++m_tableIterator; + m_arrayIndex = 0; + + // Check to see if we're at the end + m_isEnd = (m_tableIterator == m_grid->m_data.end()); + } + + return *this; + } + + /** Post increment (slower) */ + Iterator operator++(int) { + debugAssert(! m_isEnd); + Iterator old = *this; + ++(*this); + return old; + } + + const Value& operator*() const { return value(); } + const Value* operator->() const { return &value(); } + operator Value*() const { return &value(); } + }; // Iterator + + + /** Iterate through all members. It is an error to mutate the HashGrid + while iterating through it. Each member can be accessed by "dereferencing" + the iterator: + + <pre> + for (Grid::Iterator i = grid.begin(); i != grid.end(), ++i) { + const Value& = *i; + ... + } + </pre> + */ + Iterator begin() const { + return Iterator(this); + } + + const Iterator& end() const { + static const Iterator it; + return it; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + // Forward declaration required by older gcc versions for friend declaration in BoxIterator + class SphereIterator; + class BoxIterator { + private: + friend class ThisType; + friend class SphereIterator; + + bool m_isEnd; + + const ThisType* m_grid; + + /** Lower bound on the boxes covered, inclusive. */ + Vector3int32 m_lo; + + /** Upper bound on the boxes covered, inclusive.*/ + Vector3int32 m_hi; + + /** If true, test values against m_box before returning them.*/ + bool m_exact; + + /** The underlying box in 3D space */ + AABox m_box; + + /** The iterator winds through the 3D grid between m_lo and (m_lo + m_extent) in + Z,Y,X-major order. This is the index keeping track of how + far it has come */ + Vector3int32 m_current; + + /** The current cell. */ + Cell* m_cell; + + /** Index within m_cell of the current value */ + int m_arrayIndex; + + const int m_epoch; + + + /** Called from advance() */ + void advanceCell() { + do { + ++m_current.x; + if (m_current.x > m_hi.x) { + m_current.x = m_lo.x; + ++m_current.y; + if (m_current.y > m_hi.y) { + m_current.y = m_lo.y; + ++m_current.z; + if (m_current.z > m_hi.z) { + m_isEnd = true; + return; + } + } + } + + // Pick up the new cell + m_cell = m_grid->m_data.getPointer(m_current); + // Keep advancing if the cell does not exist + } while ((m_cell == NULL) || (m_cell->size() == 0)); + } + + /** Advance to the next value */ + void advance() { + debugAssert(! m_isEnd); + + do { + ++m_arrayIndex; + bool inConstructor = (m_cell == NULL); + if (inConstructor || m_arrayIndex >= m_cell->size()) { + advanceCell(); + m_arrayIndex = 0; + + if (m_isEnd) { + // Ran out of values + return; + } + debugAssert(m_cell != NULL); + } + + // Advance until we have a value that can be returned, either + // because we don't care about exactness or because it is + // guaranteed to be within the box. + } while (m_exact && ! m_box.contains(position())); + } + + + /** End iterator */ + BoxIterator() : m_isEnd(true), m_grid(NULL), m_exact(true), m_current(0,0,0), m_cell(NULL), m_arrayIndex(0), m_epoch(0) {} + + /** Begin iterator */ + BoxIterator(const ThisType* grid, bool exact, const AABox& box) : + m_isEnd(false), + m_grid(grid), + m_exact(exact), + m_box(box), + m_current(-1, 0 ,0), + m_cell(NULL), + m_arrayIndex(0), + m_epoch(grid->m_epoch) { + + m_grid->getCellCoord(box.low(), m_lo); + m_grid->getCellCoord(box.high(), m_hi); + + // Get to the first value + m_current = m_lo; + // Back up one so that advancing takes us to the first + --m_current.x; + advance(); + } + + const Value& value() const { + debugAssert(! m_isEnd); + return (*m_cell)[m_arrayIndex].value; + } + + /** Used by SphereIterator::advance() */ + const Vector3& position() const { + debugAssert(! m_isEnd); + return (*m_cell)[m_arrayIndex].position; + } + + public: + + inline bool operator!=(const BoxIterator& other) const { + if (other.m_isEnd && m_isEnd) { + return false; + } else { + return (m_isEnd != other.m_isEnd) || + (m_cell != other.m_cell) || + (m_arrayIndex != other.m_arrayIndex); + } + } + + bool operator==(const BoxIterator& other) const { + return !(*this != other); + } + + /** Preincrement */ + BoxIterator& operator++() { + debugAssert(! m_isEnd); + debugAssertM(m_epoch == m_grid->m_epoch, + "It is illegal to mutate the HashGrid " + "while iterating through it."); + + advance(); + + return *this; + } + + /** Post increment (slower) */ + BoxIterator operator++(int) { + Iterator old = *this; + ++(*this); + return old; + } + + const Value& operator*() const { return value(); } + const Value* operator->() const { return &value(); } + operator Value*() const { return &value(); } + + bool hasMore() const { + return ! m_isEnd; + } + }; // BoxIterator + + /** + Finds all values whose positions are within @a box. It is an error to + mutate the PointHashGrid while iterating through it. + + @param exact If false, the iterator will execute more quickly but will likely return some + values that lie outside the box. Set exact = false if you are going to test the + results against the yourself box anyway. + */ + BoxIterator beginBoxIntersection(const AABox& box, bool exact = true) const { + return BoxIterator(this, exact, box); + } + + const BoxIterator& endBoxIntersection() const { + static const BoxIterator it; + return it; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + class SphereIterator { + private: + + friend class ThisType; + + bool m_isEnd; + Sphere m_sphere; + BoxIterator m_boxIterator; + + SphereIterator() : m_isEnd(true) {} + + void advance() { + if (! m_boxIterator.hasMore()) { + m_isEnd = true; + return; + } + + while (! m_sphere.contains(m_boxIterator.position())) { + ++m_boxIterator; + + if (! m_boxIterator.hasMore()) { + m_isEnd = true; + return; + } + } + } + + static AABox getBoundingBox(const Sphere& s) { + AABox box; + s.getBounds(box); + return box; + } + + SphereIterator(const ThisType* grid, const Sphere& sphere) : + m_isEnd(false), + m_sphere(sphere), + m_boxIterator(grid, false, getBoundingBox(sphere)) { + + // Find the first element that is actually in the sphere, + // not just the box. + advance(); + } + + const Value& value() const { + return *m_boxIterator; + } + + // TODO: if the sphere is very big compared to radius, check each + // cell's box to see if the cell itself is actually inside the sphere + // before iterating through it, since there may be many boxes outside the sphere. + + public: + + inline bool operator!=(const SphereIterator& other) const { + if (other.m_isEnd && m_isEnd) { + return false; + } else { + return + (m_isEnd != other.m_isEnd) || + (m_sphere != other.m_sphere) || + (m_boxIterator != other.m_boxIterator); + } + } + + bool operator==(const SphereIterator& other) const { + return !(*this != other); + } + + + + /** Preincrement */ + SphereIterator& operator++() { + debugAssert(! m_isEnd); + + ++m_boxIterator; + advance(); + + return *this; + } + + /** Post increment (slower) */ + SphereIterator operator++(int) { + Iterator old = *this; + ++(*this); + return old; + } + + const Value& operator*() const { return value(); } + const Value* operator->() const { return &value(); } + operator Value*() const { return &value(); } + + bool hasMore() const { + return ! m_isEnd; + } + }; // SphereIterator + + /** + Finds all values whose positions are within @a sphere. It is an error + to mutate the HashGrid while iterating through it. + */ + SphereIterator beginSphereIntersection(const Sphere& sphere) const { + return SphereIterator(this, sphere); + } + + const SphereIterator& endSphereIntersection() const { + static const SphereIterator it; + return it; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + /** + Dereference to access the bounds() and size() [element count] of the underlying + cell objet. + + Example: + <pre> + for(PointHashGrid<Vector3>::CellIterator iter = grid.beginCells(); iter != grid.endCells(); ++iter) { + entriesFound += iter->size(); + } + */ + class CellIterator { + private: + friend class ThisType; + + bool m_isEnd; + const ThisType* m_grid; + typename CellTable::Iterator m_tableIterator; + const int m_epoch; + + + Cell& cell() { + return m_tableIterator->value; + } + + public: + + class CellObject { + friend class CellIterator; + private: + const CellIterator* m_parent; + + CellObject() : m_parent(NULL) {} + + public: + + /** Returns the bounds on this cell */ + AABox bounds() const { + const Vector3int32& k = m_parent->m_tableIterator->key; + return AABox(Vector3(k) * m_parent->m_cellWidth, + Vector3(k + Vector3int32(1, 1, 1)) * m_parent->m_cellWidth); + } + + /** Number of elements inside this cell */ + int size() const { + debugAssert(! m_parent->m_isEnd); + return m_parent->m_tableIterator->value.size(); + } + }; + + private: + /** Used to make the indirection work.*/ + CellObject m_indirection; + + /** End iterator. Note that the m_tableIterator is initialized to the end iterator + of a temporary value! This is ok because we'll never look at the value of the + m_tableIterator, since we're initializing the "end" Iterator.*/ + CellIterator() : + m_isEnd(true), + m_grid(NULL), + m_tableIterator( CellTable().end() ), + m_epoch(0) {} + + CellIterator(const ThisType* grid) : + m_isEnd(false), + m_grid(grid), + m_tableIterator( grid->m_data.begin()), + m_epoch(grid->m_epoch) { + m_indirection.m_parent = this; + m_isEnd = ! m_tableIterator.hasMore(); + } + + public: + + const CellObject& operator*() const { return m_indirection; } + const CellObject* operator->() const { return &m_indirection; } + operator CellObject*() const { return &m_indirection; } + + inline bool operator!=(const CellIterator& other) const { + // != is called more often than == during iteration + return !( + (m_isEnd && other.m_isEnd) || + ((m_isEnd == other.m_isEnd) && + (m_tableIterator != other.m_tableIterator))); + } + + bool operator==(const CellIterator& other) const { + return !(*this != other); + } + + /** Preincrement */ + CellIterator& operator++() { + debugAssertM(m_epoch == m_grid->m_epoch, + "It is illegal to mutate the HashGrid while " + "iterating through it."); + ++m_tableIterator; + m_isEnd = ! m_tableIterator.hasMore(); + return *this; + } + + /** Post increment (slower) */ + CellIterator operator++(int) { + Iterator old = *this; + ++(*this); + return old; + } + + bool hasMore() const { + return ! m_isEnd; + } + }; // CellIterator + + /** Iterates through the non-empty cells. This is intended primarily for + debugging and visualizing the data structure.*/ + CellIterator beginCells() const { + return CellIterator(this); + } + + const CellIterator& endCells() const { + static const CellIterator it; + return it; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + /** Returns true if there is a value that is exactly equal to @a. This will + check all neighboring cells to avoid roundoff error at cell boundaries. + */ + bool contains(const Value& v) const { + Cell* cell = NULL; + int index = 0; + Vector3int32 cellCoord; + return const_cast<ThisType*>(this)->find(v, cellCoord, cell, index); + } + + /** Calls delete on all of the values, which are assumed to be pointers. + This is a helper to avoid requiring you to iterate through the data + structure, removing and deleting each one. Clears the PointHashGrid at the + end. + + Using objects (instead of pointers) or reference counted pointers is + recommended over using pointers and this deleteAll method.*/ + void deleteAll() { + for (Iterator it = begin(); it.hasMore(); ++it) { + delete *it; + } + clear(); + } + + /** Removes all data. + @param shrink If true, underlying structures are deallocated as + they are freed.*/ + void clear(bool shrink = true) { + m_size = 0; + m_bounds = AABox(); + if (! shrink) { + // Remove all data + for (CellIterator it = beginCells(); it.hasMore(); ++it) { + it.cell().clear(true); + } + } else { + m_data.clear(); + } + ++m_epoch; + } + + int debugGetDeepestBucketSize() const { + return m_data.debugGetDeepestBucketSize(); + } + + float debugGetAverageBucketSize() const { + return m_data.debugGetAverageBucketSize(); + } +#undef ThisType +}; + +} // G3D +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Pointer.h b/externals/g3dlite/G3D.lib/include/G3D/Pointer.h new file mode 100644 index 00000000000..2b80187bae5 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Pointer.h @@ -0,0 +1,275 @@ +/** + @file Pointer.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2007-05-16 + @edited 2007-06-22 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ +#ifndef G3D_POINTER_H +#define G3D_POINTER_H + +#include "G3D/debugAssert.h" +#include "G3D/ReferenceCount.h" + +namespace G3D { + +/** + Acts like a pointer to a value of type ValueType (i.e., + ValueType*), but can operate through accessor methods as well as on + a value in memory. This is useful for implementing scripting + languages and other applications that need to connect existing APIs + by reference. + + Because the accessors require values to be passed by value (instead of by reference) + this is primarily useful for objects whose memory size is small. + + <pre> + class Foo { + public: + void setEnabled(bool b); + bool getEnabled() const; + }; + + Foo f; + bool b; + + Pointer<bool> p1(&b); + Pointer<bool> p2(&f, &Foo::getEnabled, &Foo::setEnabled); + + *p1 = true; + *p2 = false; + *p2 = *p1; \/\/ Value assignment + p2 = p1; \/\/ Pointer aliasing + + \/\/ Or, equivalently: + p1.setValue(true); + p2.setValue(false); + + p2.setValue(p1.getValue()); + p2 = p1; + </pre> + + <i>Note:</i> Because of the way that dereference is implemented, you cannot pass <code>*p</code> through a function + that takes varargs (...), e.g., <code>printf("%d", *p)</code> will produce a compile-time error. Instead use + <code>printf("%d",(bool)*p)</code> or <code>printf("%d", p.getValue())</code>. + + */ +template<class ValueType> +class Pointer { +private: + + class Interface { + public: + virtual ~Interface() {}; + virtual void set(ValueType b) = 0; + virtual ValueType get() const = 0; + virtual Interface* clone() const = 0; + }; + + class Memory : public Interface { + private: + + ValueType* value; + + public: + + Memory(ValueType* value) : value(value) { + debugAssert(value != NULL); + } + + virtual void set(ValueType v) { + *value = v; + } + + virtual ValueType get() const { + return *value; + } + + virtual Interface* clone() const { + return new Memory(value); + } + }; + + template<class T, typename GetMethod, typename SetMethod> + class Accessor : public Interface { + private: + + T* object; + GetMethod getMethod; + SetMethod setMethod; + + public: + + Accessor(T* object, + GetMethod getMethod, + SetMethod setMethod) : object(object), getMethod(getMethod), setMethod(setMethod) { + debugAssert(object != NULL); + } + + virtual void set(ValueType v) { + (object->*setMethod)(v); + } + + virtual ValueType get() const { + return (object->*getMethod)(); + } + + virtual Interface* clone() const { + return new Accessor(object, getMethod, setMethod); + } + }; + + + template<class T, typename GetMethod, typename SetMethod> + class RefAccessor : public Interface { + private: + + ReferenceCountedPointer<T> object; + GetMethod getMethod; + SetMethod setMethod; + + public: + + RefAccessor( + const ReferenceCountedPointer<T>& object, + GetMethod getMethod, + SetMethod setMethod) : object(object), getMethod(getMethod), setMethod(setMethod) { + + debugAssert(object != NULL); + } + + virtual void set(ValueType v) { + (object.pointer()->*setMethod)(v); + } + + virtual ValueType get() const { + return (object.pointer()->*getMethod)(); + } + + virtual Interface* clone() const { + return new RefAccessor(object, getMethod, setMethod); + } + }; + + + Interface* interface; + +public: + + Pointer() : interface(NULL) {}; + + /** Allows implicit cast from real pointer */ + Pointer(ValueType* v) : interface(new Memory(v)) {} + + // Assignment + inline Pointer& operator=(const Pointer& r) { + delete interface; + if (r.interface != NULL) { + interface = r.interface->clone(); + } else { + interface = NULL; + } + return this[0]; + } + + Pointer(const Pointer& p) : interface(NULL) { + this[0] = p; + } + + template<class Class> + Pointer(const ReferenceCountedPointer<Class>& object, + ValueType (Class::*getMethod)() const, + void (Class::*setMethod)(ValueType)) : + interface(new RefAccessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(const ReferenceCountedPointer<Class>& object, + const ValueType& (Class::*getMethod)() const, + void (Class::*setMethod)(ValueType)) : + interface(new RefAccessor<Class, const ValueType& (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(const ReferenceCountedPointer<Class>& object, + ValueType (Class::*getMethod)() const, + void (Class::*setMethod)(const ValueType&)) : + interface(new RefAccessor<Class, ValueType (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(const ReferenceCountedPointer<Class>& object, + const ValueType& (Class::*getMethod)() const, + void (Class::*setMethod)(const ValueType&)) : + interface(new RefAccessor<Class, const ValueType& (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(Class* object, + const ValueType& (Class::*getMethod)() const, + void (Class::*setMethod)(const ValueType&)) : + interface(new Accessor<Class, const ValueType& (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(Class* object, + ValueType (Class::*getMethod)() const, + void (Class::*setMethod)(const ValueType&)) : + interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(Class* object, + const ValueType& (Class::*getMethod)() const, + void (Class::*setMethod)(ValueType)) : + interface(new Accessor<Class, const ValueType& (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {} + + template<class Class> + Pointer(Class* object, + ValueType (Class::*getMethod)() const, + void (Class::*setMethod)(ValueType)) : + interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {} + + ~Pointer() { + delete interface; + } + + inline const ValueType getValue() const { + debugAssert(interface != NULL); + return interface->get(); + } + + inline void setValue(const ValueType& v) { + debugAssert(interface != NULL); + interface->set(v); + } + + class IndirectValue { + private: + + friend class Pointer; + Pointer* pointer; + IndirectValue(Pointer* p) : pointer(p) {} + + public: + + void operator=(const ValueType& v) { + pointer->setValue(v); + } + + operator ValueType() const { + return pointer->getValue(); + } + + }; + + inline IndirectValue operator*() { + return IndirectValue(this); + } + + inline const ValueType operator*() const { + return getValue(); + } +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h b/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h new file mode 100644 index 00000000000..67a4f64138a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h @@ -0,0 +1,7 @@ +#ifndef G3D_POSITIONTRAIT_H +#define G3D_POSITIONTRAIT_H + +template<typename Value> +struct PositionTrait{}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Quat.h b/externals/g3dlite/G3D.lib/include/G3D/Quat.h new file mode 100644 index 00000000000..d4b892623d8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Quat.h @@ -0,0 +1,725 @@ +/** + @file Quat.h + + Quaternion + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-01-23 + @edited 2006-05-10 + */ + +#ifndef G3D_QUAT_H +#define G3D_QUAT_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" +#include "G3D/Matrix3.h" +#include <string> + +namespace G3D { + +/** + Unit quaternions are used in computer graphics to represent + rotation about an axis. Any 3x3 rotation matrix can + be stored as a quaternion. + + A quaternion represents the sum of a real scalar and + an imaginary vector: ix + jy + kz + w. A unit quaternion + representing a rotation by A about axis v has the form + [sin(A/2)*v, cos(A/2)]. For a unit quaternion, q.conj() == q.inverse() + is a rotation by -A about v. -q is the same rotation as q + (negate both the axis and angle). + + A non-unit quaterion q represents the same rotation as + q.unitize() (Dam98 pg 28). + + Although quaternion-vector operations (eg. Quat + Vector3) are + well defined, they are not supported by this class because + they typically are bugs when they appear in code. + + Do not subclass. + + <B>BETA API -- subject to change</B> + @cite Erik B. Dam, Martin Koch, Martin Lillholm, Quaternions, Interpolation and Animation. Technical Report DIKU-TR-98/5, Department of Computer Science, University of Copenhagen, Denmark. 1998. + */ +class Quat { +private: + // Hidden operators + bool operator<(const Quat&) const; + bool operator>(const Quat&) const; + bool operator<=(const Quat&) const; + bool operator>=(const Quat&) const; + +public: + + /** + q = [sin(angle / 2) * axis, cos(angle / 2)] + + In Watt & Watt's notation, s = w, v = (x, y, z) + In the Real-Time Rendering notation, u = (x, y, z), w = w + */ + float x, y, z, w; + + /** + Initializes to a zero degree rotation. + */ + inline Quat() : x(0), y(0), z(0), w(1) {} + + Quat( + const Matrix3& rot); + + inline Quat(float _x, float _y, float _z, float _w) : + x(_x), y(_y), z(_z), w(_w) {} + + /** Defaults to a pure vector quaternion */ + inline Quat(const Vector3& v, float _w = 0) : x(v.x), y(v.y), z(v.z), w(_w) { + } + + /** + The real part of the quaternion. + */ + inline const float& real() const { + return w; + } + + inline float& real() { + return w; + } + + /** Note: two quats can represent the Quat::sameRotation and not be equal. */ + bool fuzzyEq(const Quat& q) { + return G3D::fuzzyEq(x, q.x) && G3D::fuzzyEq(y, q.y) && G3D::fuzzyEq(z, q.z) && G3D::fuzzyEq(w, q.w); + } + + /** True if these quaternions represent the same rotation (note that every rotation is + represented by two values; q and -q). + */ + bool sameRotation(const Quat& q) { + return fuzzyEq(q) || fuzzyEq(-q); + } + + inline Quat operator-() const { + return Quat(-x, -y, -z, -w); + } + + /** + Returns the imaginary part (x, y, z) + */ + inline const Vector3& imag() const { + return *(reinterpret_cast<const Vector3*>(this)); + } + + inline Vector3& imag() { + return *(reinterpret_cast<Vector3*>(this)); + } + + /** q = [sin(angle/2)*axis, cos(angle/2)] */ + static Quat fromAxisAngleRotation( + const Vector3& axis, + float angle); + + /** Returns the axis and angle of rotation represented + by this quaternion (i.e. q = [sin(angle/2)*axis, cos(angle/2)]) */ + void toAxisAngleRotation( + Vector3& axis, + double& angle) const; + + void toAxisAngleRotation( + Vector3& axis, + float& angle) const { + double d; + toAxisAngleRotation(axis, d); + angle = (float)d; + } + + Matrix3 toRotationMatrix() const; + + void toRotationMatrix( + Matrix3& rot) const; + + /** + Spherical linear interpolation: linear interpolation along the + shortest (3D) great-circle route between two quaternions. + + Note: Correct rotations are expected between 0 and PI in the right order. + + @cite Based on Game Physics -- David Eberly pg 538-540 + @param threshold Critical angle between between rotations at which + the algorithm switches to normalized lerp, which is more + numerically stable in those situations. 0.0 will always slerp. + */ + Quat slerp( + const Quat& other, + float alpha, + float threshold = 0.05f) const; + + /** Normalized linear interpolation of quaternion components. */ + Quat nlerp(const Quat& other, float alpha) const; + + /** + Negates the imaginary part. + */ + inline Quat conj() const { + return Quat(-x, -y, -z, w); + } + + inline float sum() const { + return x + y + z + w; + } + + inline float average() const { + return sum() / 4.0f; + } + + inline Quat operator*(float s) const { + return Quat(x * s, y * s, z * s, w * s); + } + + inline Quat& operator*=(float s) { + x *= s; + y *= s; + z *= s; + w *= s; + return *this; + } + + /** @cite Based on Watt & Watt, page 360 */ + friend Quat operator* (float s, const Quat& q); + + inline Quat operator/(float s) const { + return Quat(x / s, y / s, z / s, w / s); + } + + inline float dot(const Quat& other) const { + return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.w); + } + + /** Note that q<SUP>-1</SUP> = q.conj() for a unit quaternion. + @cite Dam99 page 13 */ + inline Quat inverse() const { + return conj() / dot(*this); + } + + Quat operator-(const Quat& other) const; + + Quat operator+(const Quat& other) const; + + /** + Quaternion multiplication (composition of rotations). + Note that this does not commute. + */ + Quat operator*(const Quat& other) const; + + /* (*this) * other.inverse() */ + Quat operator/(const Quat& other) const { + return (*this) * other.inverse(); + } + + + /** Is the magnitude nearly 1.0? */ + inline bool isUnit(float tolerance = 1e-5) const { + return abs(dot(*this) - 1.0f) < tolerance; + } + + + inline float magnitude() const { + return sqrtf(dot(*this)); + } + + inline Quat log() const { + if ((x == 0) && (y == 0) && (z == 0)) { + if (w > 0) { + return Quat(0, 0, 0, ::logf(w)); + } else if (w < 0) { + // Log of a negative number. Multivalued, any number of the form + // (PI * v, ln(-q.w)) + return Quat((float)pi(), 0, 0, ::logf(-w)); + } else { + // log of zero! + return Quat((float)nan(), (float)nan(), (float)nan(), (float)nan()); + } + } else { + // Partly imaginary. + float imagLen = sqrtf(x * x + y * y + z * z); + float len = sqrtf(imagLen * imagLen + w * w); + float theta = atan2f(imagLen, (float)w); + float t = theta / imagLen; + return Quat(t * x, t * y, t * z, ::logf(len)); + } + } + /** log q = [Av, 0] where q = [sin(A) * v, cos(A)]. + Only for unit quaternions + debugAssertM(isUnit(), "Log only defined for unit quaternions"); + // Solve for A in q = [sin(A)*v, cos(A)] + Vector3 u(x, y, z); + double len = u.magnitude(); + + if (len == 0.0) { + return + } + double A = atan2((double)w, len); + Vector3 v = u / len; + + return Quat(v * A, 0); + } + */ + + /** exp q = [sin(A) * v, cos(A)] where q = [Av, 0]. + Only defined for pure-vector quaternions */ + inline Quat exp() const { + debugAssertM(w == 0, "exp only defined for vector quaternions"); + Vector3 u(x, y, z); + float A = u.magnitude(); + Vector3 v = u / A; + return Quat(sinf(A) * v, cosf(A)); + } + + + /** + Raise this quaternion to a power. For a rotation, this is + the effect of rotating x times as much as the original + quaterion. + + Note that q.pow(a).pow(b) == q.pow(a + b) + @cite Dam98 pg 21 + */ + inline Quat pow(float x) const { + return (log() * x).exp(); + } + + inline void unitize() { + float mag2 = dot(*this); + if (! G3D::fuzzyEq(mag2, 1.0f)) { + *this *= rsq(mag2); + } + } + + /** + Returns a unit quaterion obtained by dividing through by + the magnitude. + */ + inline Quat toUnit() const { + Quat x = *this; + x.unitize(); + return x; + } + + /** + The linear algebra 2-norm, sqrt(q dot q). This matches + the value used in Dam's 1998 tech report but differs from the + n(q) value used in Eberly's 1999 paper, which is the square of the + norm. + */ + inline float norm() const { + return magnitude(); + } + + // access quaternion as q[0] = q.x, q[1] = q.y, q[2] = q.z, q[3] = q.w + // + // WARNING. These member functions rely on + // (1) Quat not having virtual functions + // (2) the data packed in a 4*sizeof(float) memory block + const float& operator[] (int i) const; + float& operator[] (int i); + + /** Generate uniform random unit quaternion (i.e. random "direction") + @cite From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III. + */ + static Quat unitRandom(); + + void deserialize(class BinaryInput& b); + void serialize(class BinaryOutput& b) const; + + // 2-char swizzles + + Vector2 xx() const; + Vector2 yx() const; + Vector2 zx() const; + Vector2 wx() const; + Vector2 xy() const; + Vector2 yy() const; + Vector2 zy() const; + Vector2 wy() const; + Vector2 xz() const; + Vector2 yz() const; + Vector2 zz() const; + Vector2 wz() const; + Vector2 xw() const; + Vector2 yw() const; + Vector2 zw() const; + Vector2 ww() const; + + // 3-char swizzles + + Vector3 xxx() const; + Vector3 yxx() const; + Vector3 zxx() const; + Vector3 wxx() const; + Vector3 xyx() const; + Vector3 yyx() const; + Vector3 zyx() const; + Vector3 wyx() const; + Vector3 xzx() const; + Vector3 yzx() const; + Vector3 zzx() const; + Vector3 wzx() const; + Vector3 xwx() const; + Vector3 ywx() const; + Vector3 zwx() const; + Vector3 wwx() const; + Vector3 xxy() const; + Vector3 yxy() const; + Vector3 zxy() const; + Vector3 wxy() const; + Vector3 xyy() const; + Vector3 yyy() const; + Vector3 zyy() const; + Vector3 wyy() const; + Vector3 xzy() const; + Vector3 yzy() const; + Vector3 zzy() const; + Vector3 wzy() const; + Vector3 xwy() const; + Vector3 ywy() const; + Vector3 zwy() const; + Vector3 wwy() const; + Vector3 xxz() const; + Vector3 yxz() const; + Vector3 zxz() const; + Vector3 wxz() const; + Vector3 xyz() const; + Vector3 yyz() const; + Vector3 zyz() const; + Vector3 wyz() const; + Vector3 xzz() const; + Vector3 yzz() const; + Vector3 zzz() const; + Vector3 wzz() const; + Vector3 xwz() const; + Vector3 ywz() const; + Vector3 zwz() const; + Vector3 wwz() const; + Vector3 xxw() const; + Vector3 yxw() const; + Vector3 zxw() const; + Vector3 wxw() const; + Vector3 xyw() const; + Vector3 yyw() const; + Vector3 zyw() const; + Vector3 wyw() const; + Vector3 xzw() const; + Vector3 yzw() const; + Vector3 zzw() const; + Vector3 wzw() const; + Vector3 xww() const; + Vector3 yww() const; + Vector3 zww() const; + Vector3 www() const; + + // 4-char swizzles + + Vector4 xxxx() const; + Vector4 yxxx() const; + Vector4 zxxx() const; + Vector4 wxxx() const; + Vector4 xyxx() const; + Vector4 yyxx() const; + Vector4 zyxx() const; + Vector4 wyxx() const; + Vector4 xzxx() const; + Vector4 yzxx() const; + Vector4 zzxx() const; + Vector4 wzxx() const; + Vector4 xwxx() const; + Vector4 ywxx() const; + Vector4 zwxx() const; + Vector4 wwxx() const; + Vector4 xxyx() const; + Vector4 yxyx() const; + Vector4 zxyx() const; + Vector4 wxyx() const; + Vector4 xyyx() const; + Vector4 yyyx() const; + Vector4 zyyx() const; + Vector4 wyyx() const; + Vector4 xzyx() const; + Vector4 yzyx() const; + Vector4 zzyx() const; + Vector4 wzyx() const; + Vector4 xwyx() const; + Vector4 ywyx() const; + Vector4 zwyx() const; + Vector4 wwyx() const; + Vector4 xxzx() const; + Vector4 yxzx() const; + Vector4 zxzx() const; + Vector4 wxzx() const; + Vector4 xyzx() const; + Vector4 yyzx() const; + Vector4 zyzx() const; + Vector4 wyzx() const; + Vector4 xzzx() const; + Vector4 yzzx() const; + Vector4 zzzx() const; + Vector4 wzzx() const; + Vector4 xwzx() const; + Vector4 ywzx() const; + Vector4 zwzx() const; + Vector4 wwzx() const; + Vector4 xxwx() const; + Vector4 yxwx() const; + Vector4 zxwx() const; + Vector4 wxwx() const; + Vector4 xywx() const; + Vector4 yywx() const; + Vector4 zywx() const; + Vector4 wywx() const; + Vector4 xzwx() const; + Vector4 yzwx() const; + Vector4 zzwx() const; + Vector4 wzwx() const; + Vector4 xwwx() const; + Vector4 ywwx() const; + Vector4 zwwx() const; + Vector4 wwwx() const; + Vector4 xxxy() const; + Vector4 yxxy() const; + Vector4 zxxy() const; + Vector4 wxxy() const; + Vector4 xyxy() const; + Vector4 yyxy() const; + Vector4 zyxy() const; + Vector4 wyxy() const; + Vector4 xzxy() const; + Vector4 yzxy() const; + Vector4 zzxy() const; + Vector4 wzxy() const; + Vector4 xwxy() const; + Vector4 ywxy() const; + Vector4 zwxy() const; + Vector4 wwxy() const; + Vector4 xxyy() const; + Vector4 yxyy() const; + Vector4 zxyy() const; + Vector4 wxyy() const; + Vector4 xyyy() const; + Vector4 yyyy() const; + Vector4 zyyy() const; + Vector4 wyyy() const; + Vector4 xzyy() const; + Vector4 yzyy() const; + Vector4 zzyy() const; + Vector4 wzyy() const; + Vector4 xwyy() const; + Vector4 ywyy() const; + Vector4 zwyy() const; + Vector4 wwyy() const; + Vector4 xxzy() const; + Vector4 yxzy() const; + Vector4 zxzy() const; + Vector4 wxzy() const; + Vector4 xyzy() const; + Vector4 yyzy() const; + Vector4 zyzy() const; + Vector4 wyzy() const; + Vector4 xzzy() const; + Vector4 yzzy() const; + Vector4 zzzy() const; + Vector4 wzzy() const; + Vector4 xwzy() const; + Vector4 ywzy() const; + Vector4 zwzy() const; + Vector4 wwzy() const; + Vector4 xxwy() const; + Vector4 yxwy() const; + Vector4 zxwy() const; + Vector4 wxwy() const; + Vector4 xywy() const; + Vector4 yywy() const; + Vector4 zywy() const; + Vector4 wywy() const; + Vector4 xzwy() const; + Vector4 yzwy() const; + Vector4 zzwy() const; + Vector4 wzwy() const; + Vector4 xwwy() const; + Vector4 ywwy() const; + Vector4 zwwy() const; + Vector4 wwwy() const; + Vector4 xxxz() const; + Vector4 yxxz() const; + Vector4 zxxz() const; + Vector4 wxxz() const; + Vector4 xyxz() const; + Vector4 yyxz() const; + Vector4 zyxz() const; + Vector4 wyxz() const; + Vector4 xzxz() const; + Vector4 yzxz() const; + Vector4 zzxz() const; + Vector4 wzxz() const; + Vector4 xwxz() const; + Vector4 ywxz() const; + Vector4 zwxz() const; + Vector4 wwxz() const; + Vector4 xxyz() const; + Vector4 yxyz() const; + Vector4 zxyz() const; + Vector4 wxyz() const; + Vector4 xyyz() const; + Vector4 yyyz() const; + Vector4 zyyz() const; + Vector4 wyyz() const; + Vector4 xzyz() const; + Vector4 yzyz() const; + Vector4 zzyz() const; + Vector4 wzyz() const; + Vector4 xwyz() const; + Vector4 ywyz() const; + Vector4 zwyz() const; + Vector4 wwyz() const; + Vector4 xxzz() const; + Vector4 yxzz() const; + Vector4 zxzz() const; + Vector4 wxzz() const; + Vector4 xyzz() const; + Vector4 yyzz() const; + Vector4 zyzz() const; + Vector4 wyzz() const; + Vector4 xzzz() const; + Vector4 yzzz() const; + Vector4 zzzz() const; + Vector4 wzzz() const; + Vector4 xwzz() const; + Vector4 ywzz() const; + Vector4 zwzz() const; + Vector4 wwzz() const; + Vector4 xxwz() const; + Vector4 yxwz() const; + Vector4 zxwz() const; + Vector4 wxwz() const; + Vector4 xywz() const; + Vector4 yywz() const; + Vector4 zywz() const; + Vector4 wywz() const; + Vector4 xzwz() const; + Vector4 yzwz() const; + Vector4 zzwz() const; + Vector4 wzwz() const; + Vector4 xwwz() const; + Vector4 ywwz() const; + Vector4 zwwz() const; + Vector4 wwwz() const; + Vector4 xxxw() const; + Vector4 yxxw() const; + Vector4 zxxw() const; + Vector4 wxxw() const; + Vector4 xyxw() const; + Vector4 yyxw() const; + Vector4 zyxw() const; + Vector4 wyxw() const; + Vector4 xzxw() const; + Vector4 yzxw() const; + Vector4 zzxw() const; + Vector4 wzxw() const; + Vector4 xwxw() const; + Vector4 ywxw() const; + Vector4 zwxw() const; + Vector4 wwxw() const; + Vector4 xxyw() const; + Vector4 yxyw() const; + Vector4 zxyw() const; + Vector4 wxyw() const; + Vector4 xyyw() const; + Vector4 yyyw() const; + Vector4 zyyw() const; + Vector4 wyyw() const; + Vector4 xzyw() const; + Vector4 yzyw() const; + Vector4 zzyw() const; + Vector4 wzyw() const; + Vector4 xwyw() const; + Vector4 ywyw() const; + Vector4 zwyw() const; + Vector4 wwyw() const; + Vector4 xxzw() const; + Vector4 yxzw() const; + Vector4 zxzw() const; + Vector4 wxzw() const; + Vector4 xyzw() const; + Vector4 yyzw() const; + Vector4 zyzw() const; + Vector4 wyzw() const; + Vector4 xzzw() const; + Vector4 yzzw() const; + Vector4 zzzw() const; + Vector4 wzzw() const; + Vector4 xwzw() const; + Vector4 ywzw() const; + Vector4 zwzw() const; + Vector4 wwzw() const; + Vector4 xxww() const; + Vector4 yxww() const; + Vector4 zxww() const; + Vector4 wxww() const; + Vector4 xyww() const; + Vector4 yyww() const; + Vector4 zyww() const; + Vector4 wyww() const; + Vector4 xzww() const; + Vector4 yzww() const; + Vector4 zzww() const; + Vector4 wzww() const; + Vector4 xwww() const; + Vector4 ywww() const; + Vector4 zwww() const; + Vector4 wwww() const; +}; + +inline Quat exp(const Quat& q) { + return q.exp(); +} + +inline Quat log(const Quat& q) { + return q.log(); +} + +inline G3D::Quat operator*(double s, const G3D::Quat& q) { + return q * (float)s; +} + +inline G3D::Quat operator*(float s, const G3D::Quat& q) { + return q * s; +} + +inline float& Quat::operator[] (int i) { + debugAssert(i >= 0); + debugAssert(i < 4); + return ((float*)this)[i]; +} + +inline const float& Quat::operator[] (int i) const { + debugAssert(i >= 0); + debugAssert(i < 4); + return ((float*)this)[i]; +} + +inline Quat Quat::operator-(const Quat& other) const { + return Quat(x - other.x, y - other.y, z - other.z, w - other.w); +} + +inline Quat Quat::operator+(const Quat& other) const { + return Quat(x + other.x, y + other.y, z + other.z, w + other.w); +} + +} // Namespace G3D + +// Outside the namespace to avoid overloading confusion for C++ +inline G3D::Quat pow(const G3D::Quat& q, double x) { + return q.pow((float)x); +} + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Queue.h b/externals/g3dlite/G3D.lib/include/G3D/Queue.h new file mode 100644 index 00000000000..b64eced820a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Queue.h @@ -0,0 +1,355 @@ +/** + @file Queue.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2002-07-09 + @edited 2005-08-20 + */ + +#ifndef G3D_QUEUE_H +#define G3D_QUEUE_H + +#include "G3D/platform.h" +#include "G3D/System.h" +#include "G3D/debug.h" + +namespace G3D { + +/** + Locate the indices of the break between of the two + sections of the circular queue. These are used to + construct two for loops that iterate over the whole + sequence without using the modulo operator. + + [0 ... secondEnd) [head .... firstEnd) + */ +#define FIND_ENDS \ + int firstEnd = head + num;\ + int secondEnd = 0;\ + if (firstEnd > numAllocated) {\ + secondEnd = firstEnd - numAllocated;\ + firstEnd = numAllocated;\ + } + + +/** + Dynamic queue that uses a circular buffer for performance. + + Faster than std::deque for objects with constructors. + */ +template <class T> +class Queue { +private: + // + // |<---- num ---->| + // [ | | | | | | | | | | | | | ] + // ^ + // | + // head + // + // + + /** + Only num elements are initialized. + */ + T* data; + + /** + Index of the next element to be dequeue-d in data. + */ + int head; + + /** + Number of elements (including head) that are visible and initialized. + */ + int num; + + /** + Size of data array in elements. + */ + int numAllocated; + + /** If a clear was needed, assumes it already occured */ + void _copy(const Queue& other) { + debugAssert(data == NULL); + data = (T*)System::malloc(sizeof(T) * other.numAllocated); + debugAssert(data); + head = other.head; + num = other.num; + numAllocated = other.numAllocated; + + FIND_ENDS; + + for (int i = head; i < firstEnd; ++i) { + new (data + i)T(other.data[i]); + } + + for (int i = 0; i < secondEnd; ++i) { + new (data + i)T(other.data[i]); + } + } + + + /** + Computes a data array index from a queue position. The queue position + may be negative. + */ + inline int index(int i) const { + return (head + i + numAllocated) % numAllocated; + } + + /** + Allocates newSize elements and repacks the array. + */ + void repackAndRealloc(int newSize) { + // TODO: shrink queue + T* old = data; + data = (T*)System::malloc(newSize * sizeof(T)); + debugAssert(data != NULL); + + FIND_ENDS; + + int j = 0; + for (int i = head; i < firstEnd; ++i, ++j) { + new (data + j)T(old[i]); + (old + i)->~T(); + } + + for (int i = 0; i < secondEnd; ++i, ++j) { + new (data + j)T(old[i]); + (old + i)->~T(); + } + + head = 0; + System::free(old); + numAllocated = newSize; + } + + /** + Ensure that there is at least one element between + the tail and head, wrapping around in the circular + buffer. + */ + inline void reserveSpace() { + if (num == numAllocated) { + repackAndRealloc(numAllocated * 3 + 20); + } + } + +public: + + Queue() : + data(NULL), + head(0), + num(0), + numAllocated(0) { + } + + + /** + Copy constructor + */ + Queue(const Queue& other) : data(NULL) { + _copy(other); + } + + + /** + Destructor does not delete() the objects if T is a pointer type + (e.g. T = int*) instead, it deletes the pointers themselves and + leaves the objects. Call deleteAll if you want to dealocate + the objects referenced. + */ + virtual ~Queue() { + clear(); + } + + /** + Insert a new element into the front of the queue + (a traditional queue only uses pushBack). + */ + inline void pushFront(const T& e) { + reserveSpace(); + + // Get the index of head-1 + int i = index(-1); + + // Call the constructor on the newly exposed element. + new (data + i)T(e); + + // Reassign the head to point to this index + head = i; + ++num; + } + + /** + Insert a new element at the end of the queue. + */ + inline void pushBack(const T& e) { + reserveSpace(); + + // Get the index of 1+tail + int i = index(num); + + // Initialize that element + new (data + i)T(e); + ++num; + } + + /** + pushBack + */ + inline void enqueue(const T& e) { + pushBack(e); + } + + + /** + Remove the last element from the queue. The queue will never + shrink in size. (A typical queue only uses popFront). + */ + inline T popBack() { + int tail = index(num - 1); + T result(data[tail]); + + // Call the destructor + (data + tail)->~T(); + --num; + + return result; + } + + /** + Remove the next element from the head of the queue. The queue will never + shrink in size. */ + inline T popFront() { + T result(data[head]); + // Call the destructor + (data + head)->~T(); + head = (head + 1) % numAllocated; + --num; + return result; + } + + + /** + popFront + */ + inline T dequeue() { + return popFront(); + } + + /** + Removes all elements (invoking their destructors). + + @param freeStorage If false, the underlying array is not deallocated + (allowing fast push in the future), however, the size of the Queue + is reported as zero. + + */ + void clear(bool freeStorage = true) { + + FIND_ENDS; + + // Invoke the destructors on the elements + int i; + for (i = head; i < firstEnd; ++i) { + (data + i)->~T(); + } + + for (i = 0; i < secondEnd; ++i) { + (data + i)->~T(); + } + + num = 0; + head = 0; + if (freeStorage) { + numAllocated = 0; + System::free(data); + data = NULL; + } + } + + /** Clear without freeing the underlying array. */ + void fastClear() { + clear(false); + } + + /** + Assignment operator. + */ + Queue& operator=(const Queue& other) { + clear(); + _copy(other); + return *this; + } + + /** + Number of elements in the queue. + */ + inline int size() const { + return num; + } + + /** + Number of elements in the queue. + */ + inline int length() const { + return size(); + } + + /** + Performs bounds checks in debug mode + */ + inline T& operator[](int n) { + debugAssert((n >= 0) && (n < num)); + return data[index(n)]; + } + + /** + Performs bounds checks in debug mode + */ + inline const T& operator[](int n) const { + debugAssert((n >= 0) && (n < num)); + return data[index(n)]; + } + + + /** + Returns true if the given element is in the queue. + */ + bool contains(const T& e) const { + for (int i = 0; i < size(); ++i) { + if ((*this)[i] == e) { + return true; + } + } + + return false; + } + + /** + Calls delete on all objects[0...size-1] + and sets the queue size to zero. + */ + void deleteAll() { + FIND_ENDS; + + int i; + for (i = 0; i < secondEnd; ++i) { + delete data[i]; + } + + for (i = head; i < firstEnd; ++i) { + delete data[i]; + } + clear(); + } +}; + +#undef FIND_ENDS + +}; // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Ray.h b/externals/g3dlite/G3D.lib/include/G3D/Ray.h new file mode 100644 index 00000000000..ab43c82933a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Ray.h @@ -0,0 +1,329 @@ +/** + @file Ray.h + + Ray class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-07-12 + @edited 2006-02-21 + */ + +#ifndef G3D_RAY_H +#define G3D_RAY_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Triangle.h" + +namespace G3D { + +/** + A 3D Ray. + */ +class Ray { +private: + Ray(const Vector3& origin, const Vector3& direction) { + this->origin = origin; + this->direction = direction; + } + +public: + Vector3 origin; + + /** + Not unit length + */ + Vector3 direction; + + Ray() : origin(Vector3::zero()), direction(Vector3::zero()) {} + + Ray(class BinaryInput& b); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** + Creates a Ray from a origin and a (nonzero) direction. + */ + static Ray fromOriginAndDirection(const Vector3& point, const Vector3& direction) { + return Ray(point, direction); + } + + Ray unit() const { + return Ray(origin, direction.unit()); + } + + /** + Returns the closest point on the Ray to point. + */ + Vector3 closestPoint(const Vector3& point) const { + float t = direction.dot(point - this->origin); + if (t < 0) { + return this->origin; + } else { + return this->origin + direction * t; + } + } + + /** + Returns the closest distance between point and the Ray + */ + float distance(const Vector3& point) const { + return (closestPoint(point) - point).magnitude(); + } + + /** + Returns the point where the Ray and plane intersect. If there + is no intersection, returns a point at infinity. + + Planes are considered one-sided, so the ray will not intersect + a plane where the normal faces in the traveling direction. + */ + Vector3 intersection(const class Plane& plane) const; + + /** + Returns the distance until intersection with the (solid) sphere. + Will be 0 if inside the sphere, inf if there is no intersection. + + The ray direction is <B>not</B> normalized. If the ray direction + has unit length, the distance from the origin to intersection + is equal to the time. If the direction does not have unit length, + the distance = time * direction.length(). + + See also the G3D::CollisionDetection "movingPoint" methods, + which give more information about the intersection. + */ + float intersectionTime(const class Sphere& sphere) const; + + float intersectionTime(const class Plane& plane) const; + + float intersectionTime(const class Box& box) const; + + float intersectionTime(const class AABox& box) const; + + /** + The three extra arguments are the weights of vertices 0, 1, and 2 + at the intersection point; they are useful for texture mapping + and interpolated normals. + */ + float intersectionTime( + const Vector3& v0, const Vector3& v1, const Vector3& v2, + const Vector3& edge01, const Vector3& edge02, + double& w0, double& w1, double& w2) const; + + /** + Ray-triangle intersection for a 1-sided triangle. Fastest version. + @cite http://www.acm.org/jgt/papers/MollerTrumbore97/ + http://www.graphics.cornell.edu/pubs/1997/MT97.html + */ + inline float intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + const Vector3& edge01, + const Vector3& edge02) const; + + + inline float intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2) const { + + return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0); + } + + + inline float intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + double& w0, + double& w1, + double& w2) const { + + return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0, w0, w1, w2); + } + + /* One-sided triangle + */ + inline float intersectionTime(const Triangle& triangle) const { + return intersectionTime( + triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), + triangle.edge01(), triangle.edge02()); + } + + inline float intersectionTime( + const Triangle& triangle, + double& w0, + double& w1, + double& w2) const { + return intersectionTime(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), + triangle.edge01(), triangle.edge02(), w0, w1, w2); + } + + /** Refracts about the normal + using G3D::Vector3::refractionDirection + and bumps the ray slightly from the newOrigin. */ + Ray refract( + const Vector3& newOrigin, + const Vector3& normal, + float iInside, + float iOutside) const; + + /** Reflects about the normal + using G3D::Vector3::reflectionDirection + and bumps the ray slightly from + the newOrigin. */ + Ray reflect( + const Vector3& newOrigin, + const Vector3& normal) const; +}; + + +#define EPSILON 0.000001 +#define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + +#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + +#define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +inline float Ray::intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + const Vector3& edge1, + const Vector3& edge2) const { + + (void)vert1; + (void)vert2; + + // Barycenteric coords + float u, v; + + float tvec[3], pvec[3], qvec[3]; + + // begin calculating determinant - also used to calculate U parameter + CROSS(pvec, direction, edge2); + + // if determinant is near zero, ray lies in plane of triangle + const float det = DOT(edge1, pvec); + + if (det < EPSILON) { + return (float)inf(); + } + + // calculate distance from vert0 to ray origin + SUB(tvec, origin, vert0); + + // calculate U parameter and test bounds + u = DOT(tvec, pvec); + if ((u < 0.0f) || (u > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + // prepare to test V parameter + CROSS(qvec, tvec, edge1); + + // calculate V parameter and test bounds + v = DOT(direction, qvec); + if ((v < 0.0f) || (u + v > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + + // Case where we don't need correct (u, v): + const float t = DOT(edge2, qvec); + + if (t >= 0.0f) { + // Note that det must be positive + return t / det; + } else { + // We had to travel backwards in time to intersect + return (float)inf(); + } +} + + +inline float Ray::intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + const Vector3& edge1, + const Vector3& edge2, + double& w0, + double& w1, + double& w2) const { + + (void)vert1; + (void)vert2; + + // Barycenteric coords + float u, v; + + float tvec[3], pvec[3], qvec[3]; + + // begin calculating determinant - also used to calculate U parameter + CROSS(pvec, direction, edge2); + + // if determinant is near zero, ray lies in plane of triangle + const float det = DOT(edge1, pvec); + + if (det < EPSILON) { + return (float)inf(); + } + + // calculate distance from vert0 to ray origin + SUB(tvec, origin, vert0); + + // calculate U parameter and test bounds + u = DOT(tvec, pvec); + if ((u < 0.0f) || (u > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + // prepare to test V parameter + CROSS(qvec, tvec, edge1); + + // calculate V parameter and test bounds + v = DOT(direction, qvec); + if ((v < 0.0f) || (u + v > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + float t = DOT(edge2, qvec); + + if (t >= 0) { + const float inv_det = 1.0f / det; + t *= inv_det; + u *= inv_det; + v *= inv_det; + + w0 = (1.0f - u - v); + w1 = u; + w2 = v; + + return t; + } else { + // We had to travel backwards in time to intersect + return (float)inf(); + } +} + +#undef EPSILON +#undef CROSS +#undef DOT +#undef SUB + +}// namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h b/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h new file mode 100644 index 00000000000..a6c0b7cd0cb --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h @@ -0,0 +1,391 @@ +/** + @file Rect2D.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-11-13 + @created 2008-11-16 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_RECT2D_H +#define G3D_RECT2D_H + +// Linux defines this as a macro +#ifdef border +#undef border +#endif + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Vector2.h" + +namespace G3D { + +/** + If you are using this class for pixel rectangles, keep in mind that the last + pixel you can draw to is at x0() + width() - 1. + */ +class Rect2D { +private: + Vector2 min, max; + + /** + Returns true if the whole polygon is clipped. + @param p Value of the point + @param axis Index [0 or 1] of the axis to clip along? + @param clipGreater Are we clipping greater than or less than the line? + @param inPoly Polygon being clipped + @param outPoly The clipped polygon + */ + template<class T> + static bool clipSide2D( + const float p, bool clipGreater, int axis, + const Array<T>& inPoly, Array<T>& outPoly) { + + outPoly.clear(); + int i0 = -1; + + Vector2 pt1; + bool c1 = true; + + float negate = clipGreater ? -1 : 1; + + // Find a point that is not clipped + for (i0 = 0; (i0 < inPoly.length()) && c1; ++i0) { + pt1 = inPoly[i0]; + c1 = (negate * pt1[axis]) < (negate * p); + } + + // We incremented i0 one time to many + --i0; + + if (c1) { + // We could not find an unclipped point + return true; + } + + outPoly.append(pt1); + + // for each point in inPoly, + // if the point is outside the side and the previous one was also outside, continue + // if the point is outside the side and the previous one was inside, cut the line + // if the point is inside the side and the previous one was also inside, append the points + // if the point is inside the side and the previous one was outside, cut the line + for (int i = 1; i <= inPoly.length(); ++i) { + T pt2 = inPoly[(i + i0) % inPoly.length()]; + bool c2 = (negate * pt2[axis]) < (negate * p); + + if (c1 ^ c2) { + + if (!c1 && c2 && (i > 1)) { + // Unclipped to clipped trasition and not the first iteration + outPoly.append(pt1); + } + + // only one point is clipped, find where the line crosses the clipping plane + + + float alpha; + if (pt2[axis] == pt1[axis]) { + alpha = 0; + } else { + alpha = (p - pt1[axis]) / (pt2[axis] - pt1[axis]); + } + outPoly.append(pt1.lerp(pt2, alpha)); + } else if (! (c1 || c2) && (i != 1)) { + // neither point is clipped (don't do this the first time + // because we appended the first pt before the loop) + outPoly.append(pt1); + } + + pt1 = pt2; + c1 = c2; + } + + return false; + } + +public: + + inline Rect2D() : min(0, 0), max(0, 0) {} + + /** Creates a rectangle at 0,0 with the given width and height*/ + inline Rect2D(const Vector2& wh) : min(0, 0), max(wh.x, wh.y) {} + + /** Computes a rectangle that contains both @a a and @a b. + Note that even if @a or @b has zero area, its origin will be included.*/ + inline Rect2D(const Rect2D& a, const Rect2D& b) { + min = a.min.min(b.min); + max = a.max.max(b.max); + } + + /** @brief Uniformly random point on the interior */ + inline Vector2 randomPoint() const { + return Vector2(uniformRandom(0, max.x - min.x) + min.x, + uniformRandom(0, max.y - min.y) + min.y); + } + + inline float width() const { + return max.x - min.x; + } + + inline float height() const { + return max.y - min.y; + } + + inline float x0() const { + return min.x; + } + + inline float x1() const { + return max.x; + } + + inline float y0() const { + return min.y; + } + + inline float y1() const { + return max.y; + } + + /** Min, min corner */ + inline Vector2 x0y0() const { + return min; + } + + inline Vector2 x1y0() const { + return Vector2(max.x, min.y); + } + + inline Vector2 x0y1() const { + return Vector2(min.x, max.y); + } + + /** Max,max corner */ + inline Vector2 x1y1() const { + return max; + } + + /** Width and height */ + inline Vector2 wh() const { + return max - min; + } + + inline Vector2 center() const { + return (max + min) * 0.5; + } + + inline static Rect2D xyxy(float x0, float y0, float x1, float y1) { + Rect2D r; + + r.min.x = G3D::min(x0, x1); + r.min.y = G3D::min(y0, y1); + r.max.x = G3D::max(x0, x1); + r.max.y = G3D::max(y0, y1); + + return r; + } + + Rect2D lerp(const Rect2D& other, float alpha) const { + Rect2D out; + + out.min = min.lerp(other.min, alpha); + out.max = max.lerp(other.max, alpha); + + return out; + } + + inline static Rect2D xyxy(const Vector2& v0, const Vector2& v1) { + Rect2D r; + + r.min = v0.min(v1); + r.max = v0.max(v1); + + return r; + } + + inline static Rect2D xywh(float x, float y, float w, float h) { + return xyxy(x, y, x + w, y + h); + } + + inline static Rect2D xywh(const Vector2& v, const Vector2& w) { + return xyxy(v.x, v.y, v.x + w.x, v.y + w.y); + } + + inline bool contains(const Vector2& v) const { + return (v.x >= min.x) && (v.y >= min.y) && (v.x <= max.x) && (v.y <= max.y); + } + + inline bool contains(const Rect2D& r) const { + return (min.x <= r.min.x) && (min.y <= r.min.y) && + (max.x >= r.max.x) && (max.y >= r.max.y); + } + + /** True if there is non-zero area to the intersection between @a this and @a r. + Note that two rectangles that are adjacent do not intersect because there is + zero area to the overlap, even though one of them "contains" the corners of the other.*/ + inline bool intersects(const Rect2D& r) const { + return (min.x < r.max.x) && (min.y < r.max.y) && + (max.x > r.min.x) && (max.y > r.min.y); + } + + /** Like intersection, but counts the adjacent case as touching. */ + inline bool intersectsOrTouches(const Rect2D& r) const { + return (min.x <= r.max.x) && (min.y <= r.max.y) && + (max.x >= r.min.x) && (max.y >= r.min.y); + } + + inline Rect2D operator*(float s) const { + return xyxy(min.x * s, min.y * s, max.x * s, max.y * s); + } + + inline Rect2D operator/(float s) const { + return xyxy(min / s, max / s); + } + + inline Rect2D operator/(const Vector2& s) const { + return xyxy(min / s, max / s); + } + + inline Rect2D operator+(const Vector2& v) const { + return xyxy(min + v, max + v); + } + + inline Rect2D operator-(const Vector2& v) const { + return xyxy(min - v, max - v); + } + + inline bool operator==(const Rect2D& other) const { + return (min == other.min) && (max == other.max); + } + + inline bool operator!=(const Rect2D& other) const { + return (min != other.min) || (max != other.max); + } + + /** Returns the corners in the order: (min,min), (max,min), (max,max), (min,max). */ + inline Vector2 corner(int i) const { + debugAssert(i >= 0 && i < 4); + switch (i & 3) { + case 0: + return Vector2(min.x, min.y); + case 1: + return Vector2(max.x, min.y); + case 2: + return Vector2(max.x, max.y); + case 3: + return Vector2(min.x, max.y); + default: + // Should never get here + return Vector2(0, 0); + } + } + + + /** @deprecated + @sa expand() */ + inline Rect2D border(float delta) const { + return Rect2D::xywh(x0() + delta, + y0() + delta, + width() - 2.0f * delta, + height() - 2.0f * delta); + } + + /** Returns a new Rect2D that is bigger/smaller by the specified amount + (negative is shrink.) */ + inline Rect2D expand(float delta) const { + float newX = x0() - delta; + float newY = y0() - delta; + float newW = width() + 2.0f * delta; + float newH = height() + 2.0f * delta; + + if (newW < 0.0f) { + newX = (x0() + width()) / 2.0f; + newW = 0.0f; + } + + if (newH < 0.0f) { + newY = (y0() + height()) / 2.0f; + newH = 0.0f; + } + return Rect2D::xywh(newX, newY, newW, newH); + } + + /** + Clips so that the rightmost point of the outPoly is at rect.x1 (e.g. a 800x600 window produces + rightmost point 799, not 800). The results are suitable for pixel rendering if iRounded. + Templated so that it will work for Vector2,3,4 (the z and w components are interpolated linearly). + The template parameter must define T.lerp and contain x and y components. + + If the entire polygon is clipped by a single side, the result will be empty. + The result might also have zero area but not be empty. + */ + template<class T> + void clip(const Array<T>& inPoly, Array<T>& outPoly) const { + + const bool greaterThan = true; + const bool lessThan = false; + const int X = 0; + const int Y = 1; + + Array<T> temp; + + bool entirelyClipped = + clipSide2D(x0(), lessThan, X, inPoly, temp) || + clipSide2D(x1(), greaterThan, X, temp, outPoly) || + clipSide2D(y0(), lessThan, Y, outPoly, temp) || + clipSide2D(y1(), greaterThan, Y, temp, outPoly); + + if (entirelyClipped) { + outPoly.clear(); + } + } + + + /** Returns the largest, centered Rect2D that can fit inside this + while maintaining the aspect ratio of x:y. Convenient for + displaying images in odd-shaped windows. + */ + Rect2D largestCenteredSubRect(float ww, float hh) const { + float textureAspect = hh / ww; + float viewAspect = height() / width(); + + if (viewAspect > textureAspect) { + // The view is too tall + float h = width() * textureAspect; + float y = (height() - h) / 2; + return Rect2D::xywh(0, y, width(), h) + corner(0); + } else { + // The view is too wide + float w = height() / textureAspect; + float x = (width() - w) / 2; + return Rect2D::xywh(x, 0, w, height()) + corner(0); + } + } + + /** + Returns the overlap region between the two rectangles. This may have zero area + if they do not intersect. See the two-Rect2D constructor for a way to compute + a union-like rectangle. + */ + Rect2D intersect(const Rect2D& other) const { + if (intersects(other)) { + return Rect2D::xyxy(min.max(other.min), max.min(other.max)); + }else{ + return Rect2D::xywh(0, 0, 0, 0); + } + } + + float area() const { + return width() * height(); + } +}; + +typedef Rect2D AABox2D; +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h b/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h new file mode 100644 index 00000000000..f55a22be0e1 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h @@ -0,0 +1,597 @@ +/** + @file ReferenceCount.h + + Reference Counting Garbage Collector for C++ + + @maintainer Morgan McGuire, matrix@graphics3d.com + @cite Adapted and extended from Justin Miller's "RGC" class that appeared in BYTE magazine. + @cite See also http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm + + @created 2001-10-23 + @edited 2008-09-25 +*/ + +#ifndef G3D_RGC_H +#define G3D_RGC_H + +#include "G3D/platform.h" +#include "G3D/debug.h" +#include "G3D/AtomicInt32.h" + +namespace G3D { + +/** Base class for WeakReferenceCountedPointer */ +class _WeakPtr { +public: + inline virtual ~_WeakPtr() {} + +protected: + friend class ReferenceCountedObject; + + /** Called by ReferenceCountedObject to tell a weak pointer that its underlying object was collected. */ + virtual void objectCollected() = 0; +}; + +/** Used internally by ReferenceCountedObject */ +class _WeakPtrLinkedList { +public: + _WeakPtr* weakPtr; + _WeakPtrLinkedList* next; + + inline _WeakPtrLinkedList() : weakPtr(NULL), next(NULL) {} + + /** Inserts this node into the head of the list that previously had n as its head. */ + inline _WeakPtrLinkedList(_WeakPtr* p, _WeakPtrLinkedList* n) : weakPtr(p), next(n) {} +}; + +/** + Objects that are reference counted inherit from this. Subclasses + <B>must</B> have a public destructor (the default destructor is fine) + and <B>publicly</B> inherit ReferenceCountedObject. + + Multiple inheritance from a reference counted object is dangerous-- use + at your own risk. + + ReferenceCountedPointer and ReferenceCountedObject are threadsafe. + You can create and drop references on multiple threads without + violating integrity. WeakReferenceCountedPointer is <i>not</i> + threadsafe. Introducing a weak pointer destroys all thread safety, + even for strong pointers to the same object (this is inherent in the + design of the class; we cannot fix it without slowing down the + performance of reference counted objects.) + + <B>Usage Example</B> + + <PRE> + +class Foo : public G3D::ReferenceCountedObject { +public: + int x; +}; + +class Bar : public Foo {}; + +typedef G3D::ReferenceCountedPointer<Foo> FooRef; +typedef G3D::WeakReferenceCountedPointer<Foo> WeakFooRef; +typedef G3D::ReferenceCountedPointer<Bar> BarRef; + + +int main(int argc, char *argv[]) { + + WeakFooRef x; + + { + FooRef a = new Foo(); + + // Reference count == 1 + + x = a; + // Weak references do not increase count + + { + FooRef b = a; + // Reference count == 2 + } + + // Reference count == 1 + } + // No more strong references; object automatically deleted. + // x is set to NULL automatically. + + // Example of using dynamic cast on reference counted objects + BarRef b = new Bar(); + + // No cast needed to go down the heirarchy. + FooRef f = b; + + // We can't cast the reference object because it is a class. + // Instead we must extract the pointer and cast that: + b = dynamic_cast<Bar*>(&*f); + + return 0; +} +</PRE> + +@deprecated To be replaced by boost::shared_ptr in 7.0 + */ +class ReferenceCountedObject { +public: + + /** + The long name is to keep this from accidentally conflicting with + a subclass's variable name. Do not use or explicitly manipulate + this value--its type may change in the future and is not part + of the supported API. + */ + AtomicInt32 ReferenceCountedObject_refCount; + + /** + Linked list of all weak pointers that reference this (some may be + on the stack!). Do not use or explicitly manipulate this value. + */ + _WeakPtrLinkedList* ReferenceCountedObject_weakPointer; + +protected: + + ReferenceCountedObject() : + ReferenceCountedObject_refCount(0), + ReferenceCountedObject_weakPointer(0) { + + debugAssertM(isValidHeapPointer(this), + "Reference counted objects must be allocated on the heap."); + } + +public: + + /** Automatically called immediately before the object is deleted. + This is not called from the destructor because it needs to be invoked + before the subclass destructor. + */ + void ReferenceCountedObject_zeroWeakPointers() { + // Tell all of my weak pointers that I'm gone. + + _WeakPtrLinkedList* node = ReferenceCountedObject_weakPointer; + + while (node != 0) { + + // Notify the weak pointer that it is going away + node->weakPtr->objectCollected(); + + // Free the node and advance + _WeakPtrLinkedList* tmp = node; + node = node->next; + delete tmp; + } + } + + virtual ~ReferenceCountedObject() {} + + + /** + Note: copies will initially start out with 0 + references and 0 weak references like any other object. + */ + ReferenceCountedObject(const ReferenceCountedObject& notUsed) : + ReferenceCountedObject_refCount(0), + ReferenceCountedObject_weakPointer(0) { + (void)notUsed; + debugAssertM(G3D::isValidHeapPointer(this), + "Reference counted objects must be allocated on the heap."); + } + + ReferenceCountedObject& operator=(const ReferenceCountedObject& other) { + (void)other; + // Nothing changes when I am assigned; the reference count on + // both objects is the same (although my super-class probably + // changes). + return *this; + } +}; + + + +/** + Use ReferenceCountedPointer<T> in place of T* in your program. + T must subclass ReferenceCountedObject. +@deprecated To be replaced by boost::shared_ptr in 7.0 + */ +template <class T> +class ReferenceCountedPointer { +private: + + T* m_pointer; + +public: + typedef T element_type; + + inline T* pointer() const { + return m_pointer; + } + +private: + + /** Nulls out the pointer and drops a reference. If the reference + count hits zero. */ + void zeroPointer() { + if (m_pointer != NULL) { + + debugAssert(G3D::isValidHeapPointer(m_pointer)); + debugAssertM(m_pointer->ReferenceCountedObject_refCount.value() > 0, + "Dangling reference detected."); + + // Only delete if this instance caused the count to hit + // exactly zero. If there is a race condition, the value + // may be zero after decrement returns, but only one of + // the instances will get a zero return value. + if (m_pointer->ReferenceCountedObject_refCount.decrement() == 0) { + // We held the last reference, so delete the object. + // This test is threadsafe because there is no way for + // the reference count to increase after the last + // reference was dropped (assuming the application does + // not voilate the class abstraction). + //debugPrintf(" delete 0x%x\n", m_pointer); + + // We must zero the weak pointers *before* deletion in case there + // are cycles of weak references. + // Note that since there are no strong references at this point, + // it is perfectly fair to zero the weak pointers anyway. + m_pointer->ReferenceCountedObject_zeroWeakPointers(); + delete m_pointer; + } + + m_pointer = NULL; + } + } + + /** Non-atomic (except for the referencec increment). Can only be + called in contexts like the copy constructor or initial + constructor where it is known that the reference count will + not hit zero on some other thread. */ + void setPointer(T* x) { + if (x != m_pointer) { + zeroPointer(); + + if (x != NULL) { + debugAssert(G3D::isValidHeapPointer(x)); + + m_pointer = x; + + // Note that the ref count can be zero if this is the + // first pointer to it + debugAssertM(m_pointer->ReferenceCountedObject_refCount.value() >= 0, + "Negative reference count detected."); + m_pointer->ReferenceCountedObject_refCount.increment(); + } + } + } + +public: + + inline ReferenceCountedPointer() : m_pointer(NULL) {} + + /** + Allow silent cast <i>to</i> the base class. + + <pre> + SubRef s = new Sub(); + BaseRef b = s; + </pre> + + i.e., compile-time subtyping rule + RCP<<I>T</I>> <: RCP<<I>S</I>> if <I>T</I> <: <I>S</I> + */ + template <class S> + inline ReferenceCountedPointer(const ReferenceCountedPointer<S>& p) : + m_pointer(NULL) { + setPointer(p.pointer()); + } + +# if (! defined(MSC_VER) || (MSC_VER >= 1300)) + /** + Explicit cast to a subclass. Acts like dynamic cast; the result will be NULL if + the cast cannot succeed. Not supported on VC6. + <pre> + SubRef s = new Sub(); + BaseRef b = s; + s = b.downcast<Sub>(); // Note that the template argument is the object type, not the pointer type. + </pre> + */ + template <class S> + ReferenceCountedPointer<S> downcast() { + return ReferenceCountedPointer<S>(dynamic_cast<S*>(m_pointer)); + } + + template <class S> + const ReferenceCountedPointer<S> downcast() const { + return ReferenceCountedPointer<S>(dynamic_cast<const S*>(m_pointer)); + } +# endif + + // We need an explicit version of the copy constructor as well or + // the default copy constructor will be used. + inline ReferenceCountedPointer(const ReferenceCountedPointer<T>& p) : m_pointer(NULL) { + setPointer(p.m_pointer); + } + + /** Allows construction from a raw pointer. That object will thereafter be + reference counted -- do not call delete on it. */ + inline ReferenceCountedPointer(T* p) : m_pointer(NULL) { + setPointer(p); + } + + inline ~ReferenceCountedPointer() { + zeroPointer(); + } + + inline size_t hashCode() const { + return reinterpret_cast<size_t>(m_pointer);; + } + + inline const ReferenceCountedPointer<T>& operator=(const ReferenceCountedPointer<T>& p) { + setPointer(p.m_pointer); + return *this; + } + + inline ReferenceCountedPointer<T>& operator=(T* p) { + setPointer(p); + return *this; + } + + inline bool operator==(const ReferenceCountedPointer<T>& y) const { + return (m_pointer == y.m_pointer); + } + + inline bool operator!=(const ReferenceCountedPointer<T>& y) const { + return (m_pointer != y.m_pointer); + } + + bool operator < (const ReferenceCountedPointer<T>& y) const { + return (m_pointer < y.m_pointer); + } + + bool operator > (const ReferenceCountedPointer<T>& y) const { + return (m_pointer > y.m_pointer); + } + + bool operator <= (const ReferenceCountedPointer<T>& y) const { + return (m_pointer <= y.m_pointer); + } + + bool operator >= (const ReferenceCountedPointer<T>& y) const { + return (m_pointer >= y.m_pointer); + } + + inline T& operator*() const { + debugAssertM(m_pointer != NULL, "Dereferenced a NULL ReferenceCountedPointer"); + return (*m_pointer); + } + + inline T* operator->() const { + debugAssertM(m_pointer != NULL, "Dereferenced a NULL ReferenceCountedPointer"); + return m_pointer; + } + + inline bool isNull() const { + return (m_pointer == NULL); + } + + inline bool notNull() const { + return (m_pointer != NULL); + } + + // TODO: distinguish between last strong and last any pointer + /** + Returns true if this is the last reference to an object. + Useful for flushing memoization caches-- a cache that holds the last + reference is unnecessarily keeping an object alive. + + <b>Not threadsafe.</b> + + @deprecated Use WeakReferenceCountedPointer for caches + */ + inline int isLastReference() const { + return (m_pointer->ReferenceCountedObject_refCount.value() == 1); + } +}; + + +/** + A weak pointer allows the object it references to be garbage collected. + Weak pointers are commonly used in caches, where it is important to hold + a pointer to an object without keeping that object alive solely for the + cache's benefit (i.e., the object can be collected as soon as all + pointers to it <B>outside</B> the cache are gone). They are also convenient + for adding back-pointers in tree and list structures. + + Weak pointers may become NULL at any point (when their target is collected). + Therefore the only way to reference the target is to convert to a strong + pointer and then check that it is not NULL. + +@deprecated To be replaced by boost::weak_ptr in 7.0 + */ +template <class T> +class WeakReferenceCountedPointer : public _WeakPtr { +private: + + /** NULL if the object has been collected. */ + T* pointer; + +public: + /** + Creates a strong pointer, which prevents the object from being + garbage collected. The strong pointer may be NULL, which means + that the underlying. + */ + // There is intentionally no way to check if the + // WeakReferenceCountedPointer has a null reference without + // creating a strong pointer since there is no safe way to use + // that information-- the pointer could be collected by a + // subsequent statement. + ReferenceCountedPointer<T> createStrongPtr() const { + // TODO: What if the object's destructor is called while we + // are in this method? + return ReferenceCountedPointer<T>(pointer); + } + + +private: + + /** + Thread issues: safe because this is only called when another + object is guaranteed to keep p alive for the duration of this + call. + */ + void setPointer(T* p) { + // TODO: must prevent the object from being collected while in + // this method + + zeroPointer(); + pointer = p; + + if (pointer != 0) { + // TODO: threadsafe: must update the list atomically + + // Add myself to the head of my target's list of weak pointers + _WeakPtrLinkedList* head = + new _WeakPtrLinkedList + (this, + pointer->ReferenceCountedObject_weakPointer); + + pointer->ReferenceCountedObject_weakPointer = head; + } else { + + } + } + + + /** + Removes this from its target's list of weak pointers. Called + when the weak pointer goes out of scope. + + Thread issues: depends on the thread safety of createStrongPtr. + */ + void zeroPointer() { + // Grab a strong reference to prevent the object from being collected while we + // are traversing its list. + ReferenceCountedPointer<T> strong = createStrongPtr(); + + // If the following test fails then the object was collected before we + // reached it. + if (strong.notNull()) { + debugAssertM(pointer->ReferenceCountedObject_weakPointer != NULL, + "Weak pointer exists without a backpointer from the object."); + + // Remove myself from my target's list of weak pointers + _WeakPtrLinkedList** node = &pointer->ReferenceCountedObject_weakPointer; + while ((*node)->weakPtr != this) { + node = &((*node)->next); + debugAssertM(*node != NULL, + "Weak pointer exists without a backpointer from the object (2)."); + } + + // Node must now point at the node for me. Remove node and + // close the linked list behind it. + _WeakPtrLinkedList* temp = *node; + *node = temp->next; + + // Now delete the node corresponding to me + delete temp; + } + + pointer = NULL; + } + +public: + + WeakReferenceCountedPointer() : pointer(0) {} + + /** + Allow compile time subtyping rule + RCP<<I>T</I>> <: RCP<<I>S</I>> if <I>T</I> <: <I>S</I> + */ + template <class S> + inline WeakReferenceCountedPointer(const WeakReferenceCountedPointer<S>& p) : pointer(0) { + // Threadsafe: the object cannot be collected while the other pointer exists. + setPointer(p.pointer); + } + + template <class S> + inline WeakReferenceCountedPointer(const ReferenceCountedPointer<S>& p) : pointer(0) { + // Threadsafe: the object cannot be collected while the other + // pointer exists. + setPointer(p.pointer()); + } + + // Gets called a *lot* when weak pointers are on the stack + WeakReferenceCountedPointer( + const WeakReferenceCountedPointer<T>& weakPtr) : pointer(0) { + setPointer(weakPtr.pointer); + } + + WeakReferenceCountedPointer( + const ReferenceCountedPointer<T>& strongPtr) : pointer(0) { + setPointer(strongPtr.pointer()); + } + + ~WeakReferenceCountedPointer() { + zeroPointer(); + } + + WeakReferenceCountedPointer<T>& operator=(const WeakReferenceCountedPointer<T>& other) { + // Threadsafe: the object cannot be collected while the other pointer exists. + + // I now point at other's target + setPointer(other.pointer); + + return *this; + } + + WeakReferenceCountedPointer<T>& operator=(const ReferenceCountedPointer<T>& other) { + + // Threadsafe: the object cannot be collected while the other pointer exists. + + // I now point at other's target + setPointer(other.pointer()); + + return *this; + } + + bool operator==(const WeakReferenceCountedPointer<T>& other) const { + return pointer == other.pointer; + } + + bool operator!=(const WeakReferenceCountedPointer<T>& other) const { + return pointer != other.pointer; + } + + bool operator < (const WeakReferenceCountedPointer<T>& y) const { + return (pointer < y.pointer); + } + + bool operator > (const WeakReferenceCountedPointer<T>& y) const { + return (pointer > y.pointer); + } + + bool operator <= (const WeakReferenceCountedPointer<T>& y) const { + return (pointer <= y.pointer); + } + + bool operator >= (const ReferenceCountedPointer<T>& y) const { + return (pointer >= y.pointer); + } + +protected: + + /** Invoked by the destructor on ReferenceCountedPointer. */ + virtual void objectCollected() { + debugAssertM(pointer != NULL, + "Removed a weak pointer twice."); + pointer = NULL; + } + +}; + +} // namespace + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h b/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h new file mode 100644 index 00000000000..4b47be5f4bd --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h @@ -0,0 +1,97 @@ +/** + @file RegistryUtil.h + + @created 2006-04-06 + @edited 2006-04-06 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. +*/ + +#ifndef G3D_REGISTRYUTIL_H +#define G3D_REGISTRYUTIL_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" + +// This file is only used on Windows +#ifdef G3D_WIN32 + +#include <string> + +namespace G3D { + +/** + Provides generalized Windows registry querying. + + All key names are one string in the format: + "[base key]\[sub-keys]" + + A value must now be provided for every query. + An empty value string will use the (Default) value. + + [base key] can be any of the following: + HKEY_CLASSES_ROOT + HKEY_CURRENT_CONFIG + HKEY_CURRENT_USER + HKEY_LOCAL_MACHINE + HKEY_PERFORMANCE_DATA + HKEY_PERFORMANCE_NLSTEXT + HKEY_PERFORMANCE_TEXT + HKEY_USERS + + valueExists() should be used to validate a key+value before reading or writing + to ensure that a debug assert or false return is for a different error during + reads and writes. + + All read and write calls will assert when a key will not open for reasons other + that it does not exist. All read and write calls will assert when the value cannot + be read or written for any reason. +*/ +class RegistryUtil { + +public: + /** returns true if the key exists and the current user has permission to read */ + static bool keyExists(const std::string& key); + + /** returns true if the key exists and the current user has permission to read */ + static bool valueExists(const std::string& key, const std::string& value); + + /** returns false if the key could not be read for any reason. */ + static bool readInt32(const std::string& key, const std::string& value, int32& data); + + /** + Reads an arbitrary amount of data from a binary registry key. + returns false if the key could not be read for any reason. + + @beta + @param data pointer to the output buffer of sufficient size. Pass NULL as data in order to have available data size returned in dataSize. + @param dataSize size of the output buffer. When NULL is passed for data, contains the size of available data on successful return. + */ + static bool readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize); + + /** returns false if the key could not be read for any reason. */ + static bool readString(const std::string& key, const std::string& value, std::string& data); + + /** returns false if the key could not be written for any reason. */ + static bool writeInt32(const std::string& key, const std::string& value, int32 data); + + /** + Writes an arbitrary amount of data to a binary registry key. + returns false if the key could not be written for any reason. + + @param data pointer to the input buffer + @param dataSize size of the input buffer that should be written + */ + static bool writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize); + + /** returns false if the key could not be written for any reason. */ + static bool writeString(const std::string& key, const std::string& value, const std::string& data); + +}; + +} // namespace G3D + +#endif // G3D_WIN32 + +#endif // G3D_REGISTRYTUIL_H diff --git a/externals/g3dlite/G3D.lib/include/G3D/Set.h b/externals/g3dlite/G3D.lib/include/G3D/Set.h new file mode 100644 index 00000000000..629d802cdd4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Set.h @@ -0,0 +1,160 @@ +/** + @file Set.h + + Hash set + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-12-09 + @edited 2006-02-20 + */ + +#ifndef G3D_SET_H +#define G3D_SET_H + +#include "G3D/platform.h" +#include "G3D/Table.h" +#include <assert.h> +#include <string> + +namespace G3D { + +/** + An unordered data structure that has at most one of each element. + Provides O(1) time insert, remove, and member test (contains). + + Set uses G3D::Table internally, which means that the template type T + must define a hashCode and operator== function. See G3D::Table for + a discussion of these functions. + */ +// There is not copy constructor or assignment operator defined because +// the default ones are correct for Set. +template<class T> +class Set { + + /** + If an object is a member, it is contained in + this table. + */ + Table<T, bool> memberTable; + +public: + + virtual ~Set() {} + + int size() const { + return (int)memberTable.size(); + } + + bool contains(const T& member) const { + return memberTable.containsKey(member); + } + + /** + Inserts into the table if not already present. + */ + void insert(const T& member) { + memberTable.set(member, true); + } + + /** + It is an error to remove members that are not already + present. + */ + void remove(const T& member) { + memberTable.remove(member); + } + + Array<T> getMembers() const { + return memberTable.getKeys(); + } + + void getMembers(Array<T>& keyArray) const { + memberTable.getKeys(keyArray); + } + + void clear() { + memberTable.clear(); + } + + void deleteAll() { + getMembers().deleteAll(); + clear(); + } + + /** + C++ STL style iterator variable. See begin(). + */ + class Iterator { + private: + friend class Set<T>; + + // Note: this is a Table iterator, we are currently defining + // Set iterator + typename Table<T, bool>::Iterator it; + + Iterator(const typename Table<T, bool>::Iterator& it) : it(it) {} + + public: + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + bool operator==(const Iterator& other) const { + return it == other.it; + } + + /** + Pre increment. + */ + Iterator& operator++() { + ++it; + return *this; + } + + /** + Post increment (slower than preincrement). + */ + Iterator operator++(int) { + Iterator old = *this; + ++(*this); + return old; + } + + const T& operator*() const { + return it->key; + } + + T* operator->() const { + return &(it->key); + } + + operator T*() const { + return &(it->key); + } + }; + + + /** + C++ STL style iterator method. Returns the first member. + Use preincrement (++entry) to get to the next element. + Do not modify the set while iterating. + */ + Iterator begin() const { + return Iterator(memberTable.begin()); + } + + + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + const Iterator end() const { + return Iterator(memberTable.end()); + } +}; + +} + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/Sphere.h b/externals/g3dlite/G3D.lib/include/G3D/Sphere.h new file mode 100644 index 00000000000..7d4c412185d --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Sphere.h @@ -0,0 +1,143 @@ +/** + @file Sphere.h + + Sphere class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-06-02 + @edited 2008-10-07 + */ + +#ifndef G3D_SPHERE_H +#define G3D_SPHERE_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Array.h" +#include "G3D/Sphere.h" + +namespace G3D { + +/** + Sphere. + */ +class Sphere { +private: + + static int32 dummy; + +public: + Vector3 center; + float radius; + + Sphere() { + center = Vector3::zero(); + radius = 0; + } + + Sphere(class BinaryInput& b); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + Sphere( + const Vector3& center, + float radius) { + + this->center = center; + this->radius = radius; + } + + virtual ~Sphere() {} + + bool operator==(const Sphere& other) const { + return (center == other.center) && (radius == other.radius); + } + + bool operator!=(const Sphere& other) const { + return !((center == other.center) && (radius == other.radius)); + } + + /** + Returns true if point is less than or equal to radius away from + the center. + */ + bool contains(const Vector3& point) const; + + /** + @deprecated Use culledBy(Array<Plane>&) + */ + bool culledBy( + const class Plane* plane, + int numPlanes, + int32& cullingPlaneIndex, + const uint32 testMask, + uint32& childMask) const; + + /** + @deprecated Use culledBy(Array<Plane>&) + */ + bool culledBy( + const class Plane* plane, + int numPlanes, + int32& cullingPlaneIndex = dummy, + const uint32 testMask = 0xFFFFFFFF) const; + + /** + See AABox::culledBy + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex, + const uint32 testMask, + uint32& childMask) const; + + /** + Conservative culling test that does not produce a mask for children. + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex = dummy, + const uint32 testMask = 0xFFFFFFFF) const; + + virtual std::string toString() const; + + float volume() const; + + float area() const; + + /** + Uniformly distributed on the surface. + */ + Vector3 randomSurfacePoint() const; + + /** + Uniformly distributed on the interior (includes surface) + */ + Vector3 randomInteriorPoint() const; + + void getBounds(class AABox& out) const; + + bool intersects(const Sphere& other) const; + + /** Translates the sphere */ + Sphere operator+(const Vector3& v) const { + return Sphere(center + v, radius); + } + + /** Translates the sphere */ + Sphere operator-(const Vector3& v) const { + return Sphere(center - v, radius); + } +}; + +} + +template <> struct HashTrait<G3D::Sphere> { + static size_t hashCode(const G3D::Sphere& key) { + return static_cast<size_t>(key.center.hashCode() + (key.radius * 13)); + } +}; + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Spline.h b/externals/g3dlite/G3D.lib/include/G3D/Spline.h new file mode 100644 index 00000000000..af9b08230b8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Spline.h @@ -0,0 +1,367 @@ +/** + @file Spline.h + + @author Morgan McGuire, morgan@cs.williams.edu + */ + +#ifndef G3D_SPLINE_H +#define G3D_SPLINE_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/g3dmath.h" +#include "G3D/Matrix4.h" +#include "G3D/Vector4.h" + +namespace G3D { + +/** Common implementation code for all G3D::Spline template parameters */ +class SplineBase { +public: + + /** Times at which control points occur. Must have the same + number of elements as Spline::control. */ + Array<float> time; + + /** If cyclic, then the control points will be assumed to wrap around. + If not cyclic, then the tangents at the ends of the spline + point to the final control points.*/ + bool cyclic; + + /** For a cyclic spline, this is the time elapsed between the last + control point and the first. If less than or equal to zero this is + assumed to be: + + (time[0] - time[1] + . + time[time.size() - 1] - time[time.size() - 2]) / 2. + */ + float finalInterval; + + SplineBase() : cyclic(true), finalInterval(-1) {} + + virtual ~SplineBase() {} + + /** See specification for Spline::finalInterval; this handles the + non-positive case. Returns 0 if not cyclic. */ + float getFinalInterval() const; + + /** Returns the amount of time covered by this spline in one + period. For a cyclic spline, this contains the final + interval.*/ + float duration() const; + + /** Computes the derivative spline basis from the control point version. */ + static Matrix4 computeBasis(); + +protected: + + /** Assumes that t0 <= s < tn. called by computeIndex. */ + void computeIndexInBounds(float s, int& i, float& u) const; + +public: + + /** + Given a time @a s, finds @a i and 0 <= @a u < 1 such that + @s = time[@a i] * @a u + time[@a i + 1] * (1 - @a u). Note that + @i may be outside the bounds of the time and control arrays; + use getControl to handle wraparound and extrapolation issues. + + This function takes expected O(1) time for control points with + uniform time sampled control points or for uniformly + distributed random time samples, but may take O( log time.size() ) time + in the worst case. + + Called from evaluate(). + */ + void computeIndex(float s, int& i, float& u) const; +}; + + +/** + Smooth parameteric curve implemented using a piecewise 3rd-order + Catmull-Rom spline curve. The spline is considered infinite and may + either continue linearly from the specified control points or cycle + through them. Control points are spaced uniformly in time at unit + intervals by default, but irregular spacing may be explicitly + specified. + + The dimension of the spline can be set by varying the Control + template parameter. For a 1D function, use Spline<float>. For a + curve in the plane, Spline<Vector2>. Note that <i>any</i> template + parameter that supports operator+(Control) and operator*(float) can + be used; you can make splines out of G3D::Vector4, G3D::Matrix3, or + your own classes. + + To provide shortest-path interpolation, subclass G3D::Spline and + override ensureShortestPath(). To provide normalization of + interpolated points (e.g., projecting Quats onto the unit + hypersphere) override correct(). + + See Real Time Rendering, 2nd edition, ch 12 for a general discussion + of splines and their properties. + + @sa G3D::UprightSpline, G3D::QuatSpline + */ +template<typename Control> +class Spline : public SplineBase { +protected: + /** The additive identity control point. */ + Control zero; + +public: + + /** Control points. Must have the same number of elements as + Spline::time.*/ + Array<Control> control; + + Spline() { + static Control x; + // Hide the fact from C++ that we are using an + // uninitialized variable here by pointer arithmetic. + // This is ok because any type that is a legal control + // point also supports multiplication by float. + zero = *(&x) * 0.0f; + } + + /** Appends a control point at a specific time that must be + greater than that of the previous point. */ + void append(float t, const Control& c) { + debugAssertM((time.size() == 0) || (t > time.last()), + "Control points must have monotonically increasing times."); + time.append(t); + control.append(c); + debugAssert(control.size() == time.size()); + } + + + /** Appends control point spaced in time based on the previous + control point, or spaced at unit intervals if this is the + first control point. */ + void append(const Control& c) { + switch (time.size()) { + case 0: + append(0, c); + break; + + case 1: + if (time[0] == 0) { + append(1, c); + } else { + append(time[0], c); + } + break; + + default: + append(2 * time[time.size() - 1] - time[time.size() - 2], c); + } + debugAssert(control.size() == time.size()); + } + + /** Erases all control points and times, but retains the state of + cyclic and finalInterval. + */ + void clear() { + control.clear(); + time.clear(); + } + + + /** Number of control points */ + int size() const { + debugAssert(time.size() == control.size()); + return control.size(); + } + + + /** Returns the requested control point and time sample based on + array index. If the array index is out of bounds, wraps (for + a cyclic spline) or linearly extrapolates (for a non-cyclic + spline), assuming time intervals follow the first or last + sample recorded. + + Calls correct() on the control point if it was extrapolated. + + Returns 0 if there are no control points. + + @sa Spline::control and Spline::time for the underlying + control point array; Spline::computeIndex to find the index + given a time. + */ + void getControl(int i, float& t, Control& c) const { + int N = control.size(); + if (N == 0) { + c = zero; + t = 0; + } else if (cyclic) { + c = control[iWrap(i, N)]; + + if (i < 0) { + // Wrapped around bottom + + // Number of times we wrapped around the cyclic array + int wraps = (N + 1 - i) / N; + int j = (i + wraps * N) % N; + t = time[j] - wraps * duration(); + + } else if (i < N) { + + t = time[i]; + + } else { + // Wrapped around top + + // Number of times we wrapped around the cyclic array + int wraps = i / N; + int j = i % N; + t = time[j] + wraps * duration(); + } + + } else if (i < 0) { + // Are there enough points to extrapolate? + if (N >= 2) { + // Step away from control point 0 + float dt = time[1] - time[0]; + + // Extrapolate (note; i is negative) + c = control[1] * float(i) + control[0] * float(1 - i); + correct(c); + t = dt * i + time[0]; + + } else { + // Just clamp + c = control[0]; + + // Only 1 time; assume 1s intervals + t = time[0] + i; + } + + } else if (i >= N) { + if (N >= 2) { + float dt = time[N - 1] - time[N - 2]; + + // Extrapolate + c = control[N - 1] * float(i - N + 2) + control[N - 2] * -float(i - N + 1); + correct(c); + t = time[N - 1] + dt * (i - N + 1); + + } else { + // Return the last, clamping + c = control.last(); + // Only 1 time; assume 1s intervals + t = time[0] + i; + } + } else { + // In bounds + c = control[i]; + t = time[i]; + } + } + +protected: + + /** Returns a series of N control points and times, fixing + boundary issues. The indices may be assumed to be treated + cyclically. */ + void getControls(int i, float* T, Control* A, int N) const { + for (int j = 0; j < N; ++j) { + getControl(i + j, T[j], A[j]); + } + ensureShortestPath(A, N); + } + + /** + Mutates the array of N control points. It is useful to override this + method by one that wraps the values if they are angles or quaternions + for which "shortest path" interpolation is significant. + */ + virtual void ensureShortestPath(Control* A, int N) const {} + + /** Normalize or otherwise adjust this interpolated Control. */ + virtual void correct(Control& A) const {} + +public: + + + /** + Return the position at time s. The spline is defined outside + of the time samples by extrapolation or cycling. + */ + Control evaluate(float s) const { + debugAssertM(control.size() == time.size(), "Corrupt spline: wrong number of control points."); + + /* + @cite http://www.gamedev.net/reference/articles/article1497.asp + Derivation of basis matrix follows. + + Given control points with positions p[i] at times t[i], 0 <= i <= 3, find the position + at time t[1] <= s <= t[2]. + + Let u = s - t[0] + Let U = [u^0 u^1 u^2 u^3] = [1 u u^2 u^3] + Let dt0 = t[0] - t[-1] + Let dt1 = t[1] - t[0] + Let dt2 = t[2] - t[1] + */ + + // Index of the first control point (i.e., the u = 0 point) + int i = 0; + // Fractional part of the time + float u = 0; + + computeIndex(s, i, u); + + Control p[4]; + float t[4]; + getControls(i - 1, t, p, 4); + float dt0 = t[1] - t[0]; + float dt1 = t[2] - t[1]; + float dt2 = t[3] - t[2]; + + static const Matrix4 basis = computeBasis(); + + // Powers of u + Vector4 uvec((float)(u*u*u), (float)(u*u), (float)u, 1.0f); + + // Compute the weights on each of the control points. + const Vector4& weights = uvec * basis; + + // Compute the weighted sum of the neighboring control points. + Control sum; + + const Control& p0 = p[0]; + const Control& p1 = p[1]; + const Control& p2 = p[2]; + const Control& p3 = p[3]; + + const Control& dp0 = p1 + (p0*-1.0f); + const Control& dp1 = p2 + (p1*-1.0f); + const Control& dp2 = p3 + (p2*-1.0f); + + // The factor of 1/2 from averaging two time intervals is + // already factored into the basis + + // tan1 = (dp0 / dt0 + dp1 / dt1) * ((dt0 + dt1) * 0.5); + // The last term normalizes for unequal time intervals + float x = (dt0 + dt1) * 0.5f; + float n0 = x / dt0; + float n1 = x / dt1; + float n2 = x / dt2; + const Control& dp1n1 = dp1 * n1; + const Control& tan1 = dp0 * n0 + dp1n1; + const Control& tan2 = dp1n1 + dp2 * n2; + + sum = + tan1 * weights[0]+ + p1 * weights[1] + + p2 * weights[2] + + tan2 * weights[3]; + + + correct(sum); + return sum; + } +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h b/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h new file mode 100644 index 00000000000..aee3b0f69c9 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h @@ -0,0 +1,108 @@ +/** + @file Stopwatch.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2005-10-05 + @edited 2005-10-05 + + Copyright 2000-2003, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_STOPWATCH_H +#define G3D_STOPWATCH_H + +#include "G3D/platform.h" +#include "G3D/Queue.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/g3dmath.h" + +namespace G3D { + +/** + Utility class for profiling duration and frame rates. + */ +class Stopwatch { +private: + /** True between tick and tock */ + bool inBetween; + + /** The initial cycle count. */ + uint64 cycleStart; + + /** The time at which tick was called. */ + RealTime timeStart; + + /** The time at which the previous tock was called, -1 if never. */ + RealTime lastTockTime; + + RealTime lastDuration; + int64 lastCycleCount; + + /** Frames per second. */ + double m_fps; + + /** Weighted fps */ + double emwaFPS; + double m_smoothFPS; + + /** Weighted duration */ + RealTime emwaDuration; + + /** The overhead for calling into the class. */ + int64 cycleOverhead; + + /** Called from the constructor. */ + void computeOverhead(); + +public: + + Stopwatch(); + + /** Returns the number of times that tick was called per wall-clock second; + e.g. frames-per-second. */ + double FPS() const { + return m_fps; + } + + /** Amount of time between the most recent tick and tock calls. 0 if tick has + never been called. */ + RealTime elapsedTime() const { + return lastDuration; + } + + /** Time-smoothed value that is stable to the nearest 1%. + This is useful if you are displaying elapsed time in real-time + and want a stable number.*/ + RealTime smoothElapsedTime() const { + return emwaDuration; + } + + /** Time-smoothed value of fps that is stable to the nearest integer for fps > 10 and + to the first decimal place for fps <= 10. + This is useful if you + are displaying the frame rate in real-time and want a stable (readable) number.*/ + double smoothFPS() const { + return m_smoothFPS; + } + + /** The elapsed cycle time between tick and tock. An attempt is made to factor out all + tick/tock overhead, so that back-to-back calls should return zero. + Unreliable on non-x86 platforms.*/ + uint64 elapsedCycles() const { + return lastCycleCount; + } + + /** Call at the beginning of the period that you want timed. */ + void tick(); + + /** Call at the end of the period that you want timed. */ + void tock(); +}; + + +} + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/System.h b/externals/g3dlite/G3D.lib/include/G3D/System.h new file mode 100644 index 00000000000..3755dc5e36f --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/System.h @@ -0,0 +1,390 @@ +/** + @file System.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm + @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1 + @cite Michael Herf http://www.stereopsis.com/memcpy.html + + @created 2003-01-25 + @edited 2008-10-14 + */ + +#ifndef G3D_SYSTEM_H +#define G3D_SYSTEM_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/BinaryFormat.h" +#include <string> + +#ifdef G3D_OSX +# include <CoreServices/CoreServices.h> +#endif + +namespace G3D { + +/** + Routine used by the demos to find the data. Searches in + ../data, ../../data, etc. up to 5 levels back. Checks + common locations like c:\libraries\g3d-<version>\data + and some hard-coded paths on the Brown University file + system. + */ +std::string demoFindData(bool errorIfNotFound = true); + +/** G3D, SDL, and IJG libraries require license documentation + to be distributed with your program. This generates the + string that must appear in your documentation. + <B>Your program can be commercial, closed-source</B> under + any license you want.*/ +std::string license(); + +/** +The order in which the bytes of an integer are stored on a machine. Intel/AMD chips tend to be G3D_LITTLE_ENDIAN, Mac PPC's and Suns are G3D_BIG_ENDIAN. However, this is primarily used to specify the byte order of file formats, which are fixed. +*/ +enum G3DEndian { + G3D_BIG_ENDIAN, G3D_LITTLE_ENDIAN +}; + +/** + OS and processor abstraction. The first time any method is called the processor + will be analyzed. Future calls are then fast. + + Timing function overview: + System::getCycleCount + - actual cycle count + + System::getTick + - High-resolution time in seconds since program started + + System::getLocalTime + - High-resolution time in seconds since Jan 1, 1970 + (because it is stored in a double, this may be less + accurate than getTick) + + */ +class System { +public: + + /** Called automatically by the other System routines.*/ + static void init(); + + /** */ + static bool hasMMX(); + + /** */ + static bool hasCPUID(); + + /** */ + static bool hasSSE(); + + /** */ + static bool hasSSE2(); + + /** */ + static bool hasSSE3(); + + /** */ + static bool has3DNow(); + + + /** */ + static bool hasRDTSC(); + + static const std::string& cpuVendor(); + + /** e.g. "Windows", "GNU/Linux" */ + static const std::string& operatingSystem(); + + /** */ + static const std::string& cpuArchitecture(); + + /** + Returns the endianness of this machine. + */ + static G3DEndian machineEndian(); + + /** + Returns the current date as a string in the form YYYY-MM-DD + */ + static std::string currentDateString(); + + /** + Guarantees that the start of the array is aligned to the + specified number of bytes. + */ + static void* alignedMalloc(size_t bytes, size_t alignment); + + /** + Uses pooled storage to optimize small allocations (1 byte to 5 kilobytes). + Can be 10x to 100x faster than calling ::malloc or new. + + The result must be freed with free. + + Threadsafe on Win32. + + @sa calloc realloc OutOfMemoryCallback free + */ + static void* malloc(size_t bytes); + + static void* calloc(size_t n, size_t x); + + /** + @param size Size of memory that the system was trying to allocate + @param recoverable If true, the system will attempt to allocate again + if the callback returns true. If false, malloc is going to return + NULL and this invocation is just to notify the application. + @return Return true to force malloc to attempt allocation again if the + error was recoverable. + */ + typedef bool (*OutOfMemoryCallback)(size_t size, bool recoverable); + + /** + When System::malloc fails to allocate memory because the system is + out of memory, it invokes this handler (if it is not NULL). + The argument to the callback is the amount of memory that malloc + was trying to allocate when it ran out. If the callback returns + true, System::malloc will attempt to allocate the memory again. + If the callback returns false, then System::malloc will return NULL. + + You can use outOfMemoryCallback to free data structures or to + register the failure. + */ + static OutOfMemoryCallback outOfMemoryCallback; + + /** + Version of realloc that works with System::malloc. + */ + static void* realloc(void* block, size_t bytes); + + /** Returns a string describing how well System::malloc is using its internal pooled storage. + "heap" memory was slow to allocate; the other data sizes are comparatively fast.*/ + static std::string mallocPerformance(); + static void resetMallocPerformanceCounters(); + + /** + Returns a string describing the current usage of the buffer pools used for + optimizing System::malloc. + */ + static std::string mallocStatus(); + + /** + Free data allocated with System::malloc. + + Threadsafe on Win32. + */ + static void free(void* p); + + /** + Frees memory allocated with alignedMalloc. + */ + static void alignedFree(void* ptr); + + /** An implementation of memcpy that may be up to 2x as fast as the C library + one on some processors. Guaranteed to have the same behavior as memcpy + in all cases. */ + static void memcpy(void* dst, const void* src, size_t numBytes); + + /** An implementation of memset that may be up to 2x as fast as the C library + one on some processors. Guaranteed to have the same behavior as memset + in all cases. */ + static void memset(void* dst, uint8 value, size_t numBytes); + + /** + Returns the fully qualified filename for the currently running executable. + + This is more reliable than arg[0], which may be intentionally set + to an incorrect value by a calling program, relative to a now + non-current directory, or obfuscated by sym-links. + + @cite Linux version written by Nicolai Haehnle <prefect_@gmx.net>, http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-getexename&forum=cotd&id=-1 + */ + static std::string currentProgramFilename(); + + /** Name of this program. Note that you can mutate this string to set your app name explicitly.*/ + static std::string& appName(); + + /** G3D Version string */ + static const std::string& version(); + + /** + Either Debug or Release, depending on whether _DEBUG was defined at compile-time for the library. + */ + static const std::string& build(); + + /** + Causes the current thread to yield for the specified duration + and consume almost no CPU. + The sleep will be extremely precise; it uses System::time() + to calibrate the exact yeild time. + */ + static void sleep(RealTime t); + + /** + Clears the console. + Console programs only. + */ + static void consoleClearScreen(); + + /** + Returns true if a key is waiting. + Console programs only. + */ + static bool consoleKeyPressed(); + + /** + Blocks until a key is read (use consoleKeyPressed to determine if + a key is waiting to be read) then returns the character code for + that key. + */ + static int consoleReadKey(); + + /** + The actual time (measured in seconds since + Jan 1 1970 midnight). + + Adjusted for local timezone and daylight savings + time. This is as accurate and fast as getCycleCount(). + */ + static RealTime time(); + + /** + To count the number of cycles a given operation takes: + + <PRE> + unsigned long count; + System::beginCycleCount(count); + ... + System::endCycleCount(count); + // count now contains the cycle count for the intervening operation. + + */ + static void beginCycleCount(uint64& cycleCount); + static void endCycleCount(uint64& cycleCount); + + static uint64 getCycleCount(); + + /** Set an environment variable for the current process */ + static void setEnv(const std::string& name, const std::string& value); + + /** Get an environment variable for the current process. Returns NULL if the variable doesn't exist. */ + static const char* getEnv(const std::string& name); + + /** + Prints a human-readable description of this machine + to the text output stream. Either argument may be NULL. + */ + static void describeSystem( + class TextOutput& t); + + static void describeSystem( + std::string& s); + + /** Returns the speed of processor 0 in MHz. + Always returns 0 on linux.*/ + static int cpuSpeedMHz(); + + + /** On Win32, returns the clipboard text contents. Does nothing on other + platforms (yet) */ + static std::string getClipboardText(); + + /** Copies the text to the clipboard on Win32. */ + static void setClipboardText(const std::string& s); + + /** + Tries to locate the resource by looking in related directories. + If found, returns the full path to the resource, otherwise + returns the empty string. + */ + static std::string findDataFile(const std::string& full, bool errorIfNotFound = true); + + /** + Sets the path that the application is using as its data directory. + Used by findDataDir as an initial search location. GApp sets this + upon constrution. + */ + static void setAppDataDir(const std::string& path); + +private: + /** + (CKO) Note: Not sure why these are specifically needed + for OS X. I made them private though. + */ +# ifdef G3D_OSX + static long m_OSXCPUSpeed; //In Cycles/Second + static double m_secondsPerNS; +# endif +}; + + +#ifdef _MSC_VER + inline uint64 System::getCycleCount() { + uint32 timehi, timelo; + + // Use the assembly instruction rdtsc, which gets the current + // cycle count (since the process started) and puts it in edx:eax. + __asm + { + rdtsc; + mov timehi, edx; + mov timelo, eax; + } + + return ((uint64)timehi << 32) + (uint64)timelo; + } + +#elif defined(G3D_LINUX) + + inline uint64 System::getCycleCount() { + uint32 timehi, timelo; + + __asm__ __volatile__ ( + "rdtsc " + : "=a" (timelo), + "=d" (timehi) + : ); + + return ((uint64)timehi << 32) + (uint64)timelo; + } + +#elif defined(G3D_OSX) + + inline uint64 System::getCycleCount() { + //Note: To put off extra processing until the end, this does not + //return the actual clock cycle count. It is a bus cycle count. + //When endCycleCount() is called, it converts the two into a difference + //of clock cycles + + return (uint64) UnsignedWideToUInt64(UpTime()); + //return (uint64) mach_absolute_time(); + } + +#endif + +inline void System::beginCycleCount(uint64& cycleCount) { + cycleCount = getCycleCount(); +} + + +inline void System::endCycleCount(uint64& cycleCount) { + #ifndef G3D_OSX + cycleCount = getCycleCount() - cycleCount; + #else + AbsoluteTime end = UpTime(); + init(); + Nanoseconds diffNS = + AbsoluteDeltaToNanoseconds(end, UInt64ToUnsignedWide(cycleCount)); + cycleCount = + (uint64) ((double) (System::m_OSXCPUSpeed) * + (double) UnsignedWideToUInt64(diffNS) * m_secondsPerNS); + #endif +} + + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Table.h b/externals/g3dlite/G3D.lib/include/G3D/Table.h new file mode 100644 index 00000000000..9ccd4f5c101 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Table.h @@ -0,0 +1,770 @@ +/** + @file Table.h + + Templated hash table class. + + @maintainer Morgan McGuire, morgan@cs.williams.edu + @created 2001-04-22 + @edited 2008-07-01 + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_TABLE_H +#define G3D_TABLE_H + +#include <cstddef> +#include <string> + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/debug.h" +#include "G3D/System.h" +#include "G3D/g3dmath.h" +#include "G3D/EqualsTrait.h" +#include "G3D/HashTrait.h" + +#ifdef _MSC_VER +# pragma warning (push) + // Debug name too long warning +# pragma warning (disable : 4786) +#endif + +namespace G3D { + +/** + An unordered data structure mapping keys to values. + + Key must be a pointer, an int, a std::string or provide overloads for: + + <PRE> + template<> struct HashTrait<class Key> { + static size_t hashCode(const Key& key) { return reinterpret_cast<size_t>( ... ); } + }; + </PRE> + + and one of + + <PRE> + template<> struct EqualsTrait<class Key>{ + static bool equals(const Key& a, const Key& b) { return ... ; } + }; + + + bool operator==(const Key&, const Key&); + </PRE> + + G3D pre-defines HashTrait specializations for common types (like <CODE>int</CODE> and <CODE>std::string</CODE>). + If you use a Table with a different type you must write those functions yourself. For example, + an enum would use: + + <PRE> + template<> struct HashTrait<MyEnum> { + static size_t equals(const MyEnum& key) const { return reinterpret_cast<size_t>( key ); } + }; + </PRE> + + And rely on the default enum operator==. + + + Periodically check that debugGetLoad() is low (> 0.1). When it gets near + 1.0 your hash function is badly designed and maps too many inputs to + the same output. + */ +template<class Key, class Value, class HashFunc = HashTrait<Key>, class EqualsFunc = EqualsTrait<Key> > +class Table { +public: + + /** + The pairs returned by iterator. + */ + class Entry { + public: + Key key; + Value value; + Entry() {} + Entry(const Key& k, const Value& v) : key(k), value(v) {} + }; + +private: + + typedef Table<Key, Value, HashFunc, EqualsFunc> ThisType; + + /** + Linked list nodes used internally by HashTable. + */ + class Node { + public: + Entry entry; + size_t hashCode; + Node* next; + + /** Provide pooled allocation for speed. */ + inline void* operator new (size_t size) { + return System::malloc(size); + } + + inline void operator delete (void* p) { + System::free(p); + } + + Node(const Key& k, const Value& v, size_t h, Node* n) + : entry(k, v), hashCode(h), next(n) { + } + + /** + Clones a whole chain; + */ + Node* clone() { + return new Node(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone()); + } + }; + + void checkIntegrity() const { +# ifdef G3D_DEBUG + debugAssert(bucket == NULL || isValidHeapPointer(bucket)); + for (size_t b = 0; b < numBuckets; ++b) { + Node* node = bucket[b]; + debugAssert(node == NULL || isValidHeapPointer(node)); + while (node != NULL) { + debugAssert(node == NULL || isValidHeapPointer(node)); + node = node->next; + } + } +# endif + } + + /** + Number of elements in the table. + */ + size_t _size; + + /** + Array of Node*. + We don't use Array<Node*> because Table is lower level. + Some elements may be NULL. + */ + Node** bucket; + + /** + Length of the bucket array. + */ + size_t numBuckets; + + /** + Re-hashes for a larger bucket size. + */ + void resize(size_t newSize) { + + // Hang onto the old bucket array + Node** oldBucket = bucket; + + // Allocate a new bucket array with the new size + bucket = (Node**)System::calloc(sizeof(Node*), newSize); + debugAssertM(bucket != NULL, "System::calloc returned NULL. Out of memory."); + // Move each node to its new hash location + for (size_t b = 0; b < numBuckets; ++b) { + Node* node = oldBucket[b]; + + // There is a linked list of nodes at this bucket + while (node != NULL) { + // Hang onto the old next pointer + Node* nextNode = node->next; + + // Insert at the head of the list for bucket[i] + size_t i = node->hashCode % newSize; + node->next = bucket[i]; + bucket[i] = node; + + // Move on to the next node + node = nextNode; + } + + // Drop the old pointer for cleanliness when debugging + oldBucket[b] = NULL; + } + + // Delete the old storage + System::free(oldBucket); + this->numBuckets = newSize; + + checkIntegrity(); + } + + + void copyFrom(const ThisType& h) { + if (&h == this) { + return; + } + + debugAssert(bucket == NULL); + _size = h._size; + numBuckets = h.numBuckets; + bucket = (Node**)System::calloc(sizeof(Node*), numBuckets); + + for (size_t b = 0; b < numBuckets; b++) { + if (h.bucket[b] != NULL) { + bucket[b] = h.bucket[b]->clone(); + } + } + + checkIntegrity(); + } + + /** + Frees the heap structures for the nodes. + */ + void freeMemory() { + checkIntegrity(); + + for (size_t b = 0; b < numBuckets; b++) { + Node* node = bucket[b]; + while (node != NULL) { + Node* next = node->next; + delete node; + node = next; + } + bucket[b] = NULL; + } + System::free(bucket); + bucket = NULL; + numBuckets = 0; + _size = 0; + } + + +public: + + /** + Creates an empty hash table. This causes some heap allocation to occur. + */ + Table() : bucket(NULL) { + numBuckets = 10; + _size = 0; + bucket = (Node**)System::calloc(sizeof(Node*), numBuckets); + checkIntegrity(); + } + + /** + Recommends that the table resize to anticipate at least this number of elements. + */ + void setSizeHint(size_t n) { + size_t s = n * 3; + if (s > numBuckets) { + resize(s); + } + } + + /** + Destroys all of the memory allocated by the table, but does <B>not</B> + call delete on keys or values if they are pointers. If you want to + deallocate things that the table points at, use getKeys() and Array::deleteAll() + to delete them. + */ + virtual ~Table() { + freeMemory(); + } + + Table(const ThisType& h) { + numBuckets = 0; + _size = 0; + bucket = NULL; + this->copyFrom(h); + checkIntegrity(); + } + + + Table& operator=(const ThisType& h) { + // No need to copy if the argument is this + if (this != &h) { + // Free the existing nodes + freeMemory(); + this->copyFrom(h); + checkIntegrity(); + } + return *this; + } + + /** + Returns the length of the deepest bucket. + */ + size_t debugGetDeepestBucketSize() const { + size_t deepest = 0; + + for (size_t b = 0; b < numBuckets; b++) { + size_t count = 0; + Node* node = bucket[b]; + while (node != NULL) { + node = node->next; + ++count; + } + + if (count > deepest) { + deepest = count; + } + } + + return deepest; + } + + /** + Returns the average size of non-empty buckets. + */ + float debugGetAverageBucketSize() const { + size_t num = 0; + size_t count = 0; + + for (size_t b = 0; b < numBuckets; b++) { + Node* node = bucket[b]; + if (node != NULL) { + ++num; + while (node != NULL) { + node = node->next; + ++count; + } + } + } + + return (float)((double)count / num); + } + + /** + A small load (close to zero) means the hash table is acting very + efficiently most of the time. A large load (close to 1) means + the hash table is acting poorly-- all operations will be very slow. + A large load will result from a bad hash function that maps too + many keys to the same code. + */ + double debugGetLoad() const { + return debugGetDeepestBucketSize() / (double)size(); + } + + /** + Returns the number of buckets. + */ + size_t debugGetNumBuckets() const { + return numBuckets; + } + + /** + C++ STL style iterator variable. See begin(). + */ + class Iterator { + private: + friend class Table<Key, Value, HashFunc, EqualsFunc>; + + /** + Bucket index. + */ + size_t index; + + /** + Linked list node. + */ + Node* node; + ThisType* table; + size_t numBuckets; + Node** bucket; + bool isDone; + + /** + Creates the end iterator. + */ + Iterator(const ThisType* table) : table(const_cast<ThisType*>(table)) { + isDone = true; + } + + Iterator(const ThisType* table, size_t numBuckets, Node** bucket) : + table(const_cast<ThisType*>(table)), + numBuckets(numBuckets), + bucket(bucket) { + + if (numBuckets == 0) { + // Empty table + isDone = true; + return; + } + + index = 0; + node = bucket[index]; + isDone = false; + findNext(); + } + + /** + Finds the next element, setting isDone if one can't be found. + Looks at the current element first. + */ + void findNext() { + while (node == NULL) { + index++; + if (index >= numBuckets) { + isDone = true; + break; + } else { + node = bucket[index]; + } + } + } + + public: + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + bool operator==(const Iterator& other) const { + if (other.isDone || isDone) { + // Common case; check against isDone. + return (isDone == other.isDone) && (other.table == table); + } else { + return + (table == other.table) && + (node == other.node) && + (index == other.index); + } + } + + /** + Pre increment. + */ + Iterator& operator++() { + node = node->next; + findNext(); + return *this; + } + + /** + Post increment (slower than preincrement). + */ + Iterator operator++(int) { + Iterator old = *this; + ++(*this); + return old; + } + + const Entry& operator*() const { + return node->entry; + } + + Entry* operator->() const { + return &(node->entry); + } + + operator Entry*() const { + return &(node->entry); + } + + bool hasMore() const { + return ! isDone; + } + }; + + + /** + C++ STL style iterator method. Returns the first Entry, which + contains a key and value. Use preincrement (++entry) to get to + the next element. Do not modify the table while iterating. + */ + Iterator begin() const { + return Iterator(this, numBuckets, bucket); + } + + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + const Iterator end() const { + return Iterator(this); + } + + /** + Removes all elements + */ + void clear() { + freeMemory(); + numBuckets = 10; + _size = 0; + bucket = (Node**)System::calloc(sizeof(Node*), numBuckets); + } + + + /** + Returns the number of keys. + */ + size_t size() const { + return _size; + } + + + /** + If you insert a pointer into the key or value of a table, you are + responsible for deallocating the object eventually. Inserting + key into a table is O(1), but may cause a potentially slow rehashing. + */ + void set(const Key& key, const Value& value) { + size_t code = HashFunc::hashCode(key); + size_t b = code % numBuckets; + + // Go to the bucket + Node* n = bucket[b]; + + // No bucket, so this must be the first + if (n == NULL) { + bucket[b] = new Node(key, value, code, NULL); + ++_size; + return; + } + + size_t bucketLength = 1; + + // Sometimes a bad hash code will cause all elements + // to collide. Detect this case and don't rehash when + // it occurs; nothing good will come from the rehashing. + bool allSameCode = true; + + // Try to find the node + do { + allSameCode = allSameCode && (code == n->hashCode); + + if ((code == n->hashCode) && EqualsFunc::equals(n->entry.key, key)) { + // Replace the existing node. + n->entry.value = value; + return; + } + + n = n->next; + ++bucketLength; + } while (n != NULL); + + const size_t maxBucketLength = 3; + // (Don't bother changing the size of the table if all entries + // have the same hashcode--they'll still collide) + if ((bucketLength > maxBucketLength) && + ! allSameCode && + (numBuckets < _size * 15)) { + + // This bucket was really large; rehash if all elements + // don't have the same hashcode the number of buckets is + // reasonable. + + // Back off the scale factor as the number of buckets gets + // large + float f = 3.0f; + if (numBuckets > 1000000) { + f = 1.5f; + } else if (numBuckets > 100000) { + f = 2.0f; + } + int newSize = iMax((int)(numBuckets * f) + 1, (int)(_size * f)); + resize(newSize); + } + + // Not found; insert at the head. + b = code % numBuckets; + bucket[b] = new Node(key, value, code, bucket[b]); + ++_size; + } + + /** + Removes an element from the table if it is present. + @return true if the element was found and removed, otherwise false + */ + bool remove(const Key& key) { + + size_t code = HashFunc::hashCode(key); + size_t b = code % numBuckets; + + // Go to the bucket + Node* n = bucket[b]; + + if (n == NULL) { + return false; + } + + Node* previous = NULL; + + // Try to find the node + do { + if ((code == n->hashCode) && EqualsFunc::equals(n->entry.key, key)) { + // This is the node; remove it + + // Replace the previous's next pointer + if (previous == NULL) { + bucket[b] = n->next; + } else { + previous->next = n->next; + } + + // Delete the node + delete n; + --_size; + return true; + } + + previous = n; + n = n->next; + } while (n != NULL); + + + return false; + //alwaysAssertM(false, "Tried to remove a key that was not in the table."); + } + + /** + Returns the value associated with key. + @deprecated Use get(key, val) or getPointer(key) + */ + Value& get(const Key& key) const { + + size_t code = HashFunc::hashCode(key); + size_t b = code % numBuckets; + + Node* node = bucket[b]; + + while (node != NULL) { + if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) { + return node->entry.value; + } + node = node->next; + } + + debugAssertM(false, "Key not found"); + // The next line is here just to make + // a compiler warning go away. + return node->entry.value; + } + + + /** Returns a pointer to the element if it exists, or NULL if it does not. + Note that if your value type <i>is</i> a pointer, the return value is + a pointer to a pointer. Do not remove the element while holding this + pointer. + + It is easy to accidentally mis-use this method. Consider making + a Table<Value*> and using get(key, val) instead, which makes you manage + the memory for the values yourself and is less likely to result in + pointer errors. + */ + Value* getPointer(const Key& key) const { + size_t code = HashFunc::hashCode(key); + size_t b = code % numBuckets; + + Node* node = bucket[b]; + + while (node != NULL) { + if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) { + // found key + return &(node->entry.value); + } + node = node->next; + } + + // Failed to find key + return NULL; + } + + /** + If the key is present in the table, val is set to the associated value and returns true. + If the key is not present, returns false. + */ + bool get(const Key& key, Value& val) const { + Value* v = getPointer(key); + if (v != NULL) { + val = *v; + return true; + } else { + return false; + } + } + + /** + Returns true if key is in the table. + */ + bool containsKey(const Key& key) const { + size_t code = HashFunc::hashCode(key); + size_t b = code % numBuckets; + + Node* node = bucket[b]; + + while (node != NULL) { + if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) { + return true; + } + node = node->next; + } while (node != NULL); + + return false; + } + + + /** + Short syntax for get. + */ + inline Value& operator[](const Key &key) const { + return get(key); + } + + + /** + Returns an array of all of the keys in the table. + You can iterate over the keys to get the values. + @deprecated + */ + Array<Key> getKeys() const { + Array<Key> keyArray; + getKeys(keyArray); + return keyArray; + } + + void getKeys(Array<Key>& keyArray) const { + keyArray.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + for (size_t i = 0; i < numBuckets; i++) { + Node* node = bucket[i]; + while (node != NULL) { + keyArray.append(node->entry.key); + node = node->next; + } + } + } + + /** + Calls delete on all of the keys and then clears the table. + */ + void deleteKeys() { + for (size_t i = 0; i < numBuckets; i++) { + Node* node = bucket[i]; + while (node != NULL) { + delete node->entry.key; + node = node->next; + } + } + clear(); + } + + /** + Calls delete on all of the values. This is unsafe-- + do not call unless you know that each value appears + at most once. + + Does not clear the table, so you are left with a table + of NULL pointers. + */ + void deleteValues() { + for (size_t i = 0; i < numBuckets; ++i) { + Node* node = bucket[i]; + while (node != NULL) { + delete node->entry.value; + node->entry.value = NULL; + node = node->next; + } + } + } +}; + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/TextInput.h b/externals/g3dlite/G3D.lib/include/G3D/TextInput.h new file mode 100644 index 00000000000..b6dcad39b8b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/TextInput.h @@ -0,0 +1,693 @@ +/** + @file TextInput.h + + Simple text lexer/tokenizer. + + @maintainer Morgan McGuire, morgan@graphics3d.com + + @cite Based on a lexer written by Aaron Orenstein. + + @created 2002-11-27 + @edited 2006-10-24 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_TEXTINPUT_H +#define G3D_TEXTINPUT_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Set.h" +#include <string> +#include <queue> +#include <ctype.h> +#include <stdio.h> + +namespace G3D { + +/** + For use with TextInput. + */ +class Token { +public: + + /** + More detailed type information than Type. + */ + enum ExtendedType { + DOUBLE_QUOTED_TYPE, + SINGLE_QUOTED_TYPE, + SYMBOL_TYPE, + FLOATING_POINT_TYPE, + INTEGER_TYPE, + BOOLEAN_TYPE, + END_TYPE + }; + + /** + Strings are enclosed in quotes, symbols are not. + */ + enum Type { + STRING = DOUBLE_QUOTED_TYPE, + SYMBOL = SYMBOL_TYPE, + NUMBER = FLOATING_POINT_TYPE, + BOOLEAN = BOOLEAN_TYPE, + END = END_TYPE + }; + +private: + + friend class TextInput; + + /** + Holds the actual value, which might be any type. If a number, it will be + parsed at runtime. + */ + std::string _string; + + bool _bool; + int _line; + int _character; + Type _type; + ExtendedType _extendedType; + +public: + + Token() : + _string(""), + _bool(false), + _line(0), + _character(0), + _type(END), + _extendedType(END_TYPE) {} + + Token(Type t, ExtendedType e, const std::string& s, int L, int c) + : _string(s), _bool(false), _line(L), _character(c), _type(t), _extendedType(e) {} + + Token(Type t, ExtendedType e, const std::string& s, bool b, int L, int c) + : _string(s), _bool(b), _line(L), _character(c), _type(t), _extendedType(e) {} + + Type type() const { + return _type; + } + + ExtendedType extendedType() const { + return _extendedType; + } + + /** + The value of a single or double quote string (not including the quotes), + the name of a symbol, or the exact textual representation of a number as + parsed from the input. + */ + const std::string& string() const { + return _string; + } + + bool boolean() const { + return _bool; + } + + /** + Starting line of the input from which this token was parsed. Starts + at 1. + */ + int line() const { + return _line; + } + + /** + Starting character position in the input line from which this token was + parsed. Starts at 1. + */ + int character() const { + return _character; + } + + /** Return the numeric value for a number type, or zero if this is + not a number type. + */ + double number() const; +}; + + +/** + A simple style tokenizer for reading text files. TextInput handles a + superset of C++,Java, Matlab, and Bash code text including single + line comments, block comments, quoted strings with escape sequences, + and operators. TextInput recognizes several categories of tokens, + which are separated by white space, quotation marks, or the end of a + recognized operator: + + <ul> + <li><CODE>Token::SINGLE_QUOTED_TYPE</CODE> string of characters surrounded by single quotes, e.g., 'x', '\0', 'foo'. + <li><CODE>Token::DOUBLE_QUOTED_TYPE</CODE> string of characters surrounded by double quotes, e.g., "x", "abc\txyz", "b o b". + <li><CODE>Token::SYMBOL_TYPE</CODE> legal C++ operators, keywords, and identifiers. e.g., >=, Foo, _X, class, { + <li><CODE>Token::INTEGER_TYPE</CODE> numbers without decimal places or exponential notation. e.g., 10, 0x17F, 32, 0, -155 + <li><CODE>Token::FLOATING_POINT_TYPE</CODE> numbers with decimal places or exponential notation. e.g., 1e3, -1.2, .4, 0.5 + <li><CODE>Token::BOOLEAN_TYPE</CODE> special symbols like "true" and "false"; the exact details can be configured in TextInput::Settings + </ul> + + <P>The special ".." and "..." tokens are always recognized in + addition to normal C++ operators. Additional tokens can be made + available by changing the Settings. + + Negative numbers are handled specially because of the ambiguity between unary minus and negative numbers-- + see the note on TextInput::read. + + TextInput does not have helper functions for types with non-obvious + formatting, or helpers that would be redundant. Use the serialize + methods instead for parsing specific types like int, Vector3, and + Color3. + + Inside quoted strings escape sequences are converted. Thus the + string token for ["a\nb"] is 'a', followed by a newline, followed by + 'b'. Outside of quoted strings, escape sequences are not converted, + so the token sequence for [a\nb] is symbol 'a', symbol '\', symbol + 'nb' (this matches what a C++ parser would do). The exception is + that a specified TextInput::Settings::otherCommentCharacter preceeded + by a backslash is assumed to be an escaped comment character and is + returned as a symbol token instead of being parsed as a comment + (this is what a LaTex or VRML parser would do). + + <B>Examples</B> + + <PRE> + TextInput ti(TextInput::FROM_STRING, "name = \"Max\", height = 6"); + + Token t; + + t = ti.read(); + debugAssert(t.type == Token::SYMBOL); + debugAssert(t.sval == "name"); + + ti.read(); + debugAssert(t.type == Token::SYMBOL); + debugAssert(t.sval == "="); + + std::string name = ti.read().sval; + ti.read(); + </PRE> + + <PRE> + TextInput ti(TextInput::FROM_STRING, "name = \"Max\", height = 6"); + ti.readSymbols("name", "="); + std::string name = ti.readString(); + ti.readSymbols(",", "height", "="); + double height = ti. readNumber(); + </PRE> + + Assumes that the file is not modified once opened. + */ +class TextInput { +public: + + /** Tokenizer configuration options. */ + class Settings { + public: + /** If true, slash-star marks a multi-line comment. Default + is true. */ + bool cComments; + + /** If true, // begins a single line comment. Default is true. */ + bool cppComments; + + /** If true, \r, \n, \t, \0, \\ and other escape sequences inside + strings are converted to the equivalent C++ escaped character. + If false, backslashes are treated literally. It is convenient to + set to false if reading Windows paths, for example, like + c:\foo\bar. + + Default is true. + */ + bool escapeSequencesInStrings; + + /** If non-NUL, specifies a character that begins single line + comments ('#' and '%' are popular choices). This is independent + of the cppComments flag. If the character appears in text with a + backslash in front of it, it is considered escaped and is not + treated as a comment character. + + Default is '\0'. + */ + char otherCommentCharacter; + + /** Another (optional) 1-comment character. Useful for files that + support multiple comment syntaxes. Default is '\0'. + */ + char otherCommentCharacter2; + + /** If true, "-1" parses as the number -1 instead of the + symbol "-" followed by the number 1. Default is true.*/ + bool signedNumbers; + + /** If true, strings can be marked with single quotes (e.g., + 'aaa'). If false, the quote character is parsed as a + symbol. Default is true. Backquote (`) is always parsed + as a symbol. */ + bool singleQuotedStrings; + + /** If set to a non-empty string, that string will be used in + place of the real file name (or in place of a pseudonym + constructed from the buffer if given FROM_STRING) in + tokens and exceptions. + + Default is empty. + */ + std::string sourceFileName; + + + /** Added to the line number reported by peekLineNumber and in + exceptions. Useful for concatenating files that are + parsed separately. Default is zero. */ + int startingLineNumberOffset; + + /** + Parse -1.#IND00 as the floating point number returned by + nan(), -1.#INF00 as -inf(), and 1.#INF00 as inf(). Note + that the C99 standard specifies that a variety of formats + like "NaN" and "nan" are to be used; these are easier to + parse yourself and not currently supported by readNumber. + + An alternative to specifying msvcSpecials is to read numbers as: + <pre> + Token x = t.read(); + Token y = t.peek(); + if ((x.string() == "-1.") && + (y.string() == "#INF00") && + (y.character() == x.character() + 3) && + (y.line() == x.line()) { + t.read(); + return nan(); + } + // ... similar cases for inf + </pre> + + If the single-comment character was #, the floating point + special format overrides the comment and will be parsed + instead. + + If signedNumbers is false msvcSpecials will not be parsed. + + Default is true. */ + bool msvcSpecials; + + /** + Parse the following set of useful proof symbols: + + => + ::> + <:: + :> + <: + |- + ::= + := + <- + + Default is false. + */ + bool proofSymbols; + + /** + When parsing booleans and msvcSpecials, is case significant? + Default is {true} + */ + bool caseSensitive; + + /** All symbols that will become the 'true' boolean token. See also caseSensitive. + Clear this value to disable parsing of true booleans. + + Default is {true}. + */ + Set<std::string> trueSymbols; + + /** See trueSymbols. Default is {false}*/ + Set<std::string> falseSymbols; + + Settings (); + }; + +private: + + std::deque<Token> stack; + + /** + Characters to be tokenized. + */ + Array<char> buffer; + + /** + Offset of current character (the next character to consumed) in + input buffer. + */ + unsigned int currentCharOffset; + + /** + Line number of next character to be consumed from the input buffer. (1 + indicates first line of input.) + + Note that this is the line number of the @e next character to be + consumed from the input, not the line number of the @e last character + consumed! + */ + unsigned int lineNumber; + + /** + Character number (within the line) of the next character to be consumed + from the input buffer. (1 indicates first character of the line). + + Note that this is the character number of the @e next character to be + consumed from the input, not the character number of the @e last + character consumed! + */ + unsigned int charNumber; + + /** Configuration options. This includes the file name that will be + reported in tokens and exceptions. */ + Settings options; + + void init(); + + /** + Consumes the next character from the input buffer, and returns that + character. Updates lineNumber and charNumber to reflect the location of + the next character in the input buffer. + + Note: you shouldn't be using the return value of this function in most + cases. In general, you should peekInputChar() to get the next + character, determine what to do with it, then consume it with this + function (or with eatAndPeekInputChar()). Given that usage, in most + instances you already know what this function would return! + */ + int eatInputChar(); + + /** + Returns the next character from the input buffer, without consuming any + characters. Can also be used to look deeper into the input buffer. + Does not modify lineNumber or charNumber. + + @param distance Index of the character in the input buffer to peek at, + relative to the next character. Default is 0, for the next character in + the input buffer. + */ + int peekInputChar(unsigned int distance = 0); + + /** + Helper function to consume the next character in the input buffer and + peek at the one following (without consuming it). + */ + inline int eatAndPeekInputChar() { + eatInputChar(); + return peekInputChar(0); + } + + /** + Read the next token, returning an END token if no more input is + available. + */ + Token nextToken(); + + /** + Helper for nextToken. Appends characters to t._string until the end + delimiter is reached. + + When called, the next character in the input buffer should be first the + first character after the opening delimiter character. + */ + void parseQuotedString(unsigned char delimiter, Token& t); + +public: + + class TokenException { + public: + /** Name of file being parsed when exception occurred. */ + std::string sourceFile; + + /** Line number of start of token which caused the exception. 1 is + the first line of the file. Note that you can use + TextInput::Settings::startingLineNumberOffset to shift the effective line + number that is reported. + */ + int line; + + /** Character number in the line of start of token which caused the + exception. 1 is the character in the line. + */ + int character; + + /** Pre-formatted error message */ + std::string message; + + virtual ~TokenException() {} + + protected: + + TokenException( + const std::string& src, + int ln, + int ch); + + }; + + /** While parsing a number of the form 1.#IN?00, ? was + not 'D' or 'F'. */ + class BadMSVCSpecial : public TokenException { + public: + + BadMSVCSpecial( + const std::string& src, + int ln, + int ch); + }; + + /** Thrown by the read methods. */ + class WrongTokenType : public TokenException { + public: + Token::Type expected; + Token::Type actual; + + WrongTokenType( + const std::string& src, + int ln, + int ch, + Token::Type e, + Token::Type a); + }; + + class WrongSymbol : public TokenException { + public: + std::string expected; + std::string actual; + + WrongSymbol( + const std::string& src, + int ln, + int ch, + const std::string& e, + const std::string& a); + }; + + + /** String read from input did not match expected string. */ + class WrongString : public TokenException { + public: + std::string expected; + std::string actual; + + WrongString( + const std::string& src, + int ln, + int ch, + const std::string& e, + const std::string& a); + }; + + TextInput(const std::string& filename, const Settings& settings = Settings()); + + enum FS {FROM_STRING}; + /** Creates input directly from a string. The first argument must be + TextInput::FROM_STRING. + */ + TextInput(FS fs, const std::string& str, const Settings& settings = Settings()); + + /** Returns true while there are tokens remaining. */ + bool hasMore(); + + /** Read the next token (which will be the END token if ! hasMore()). + + Signed numbers can be handled in one of two modes. If the option + TextInput::Settings::signedNumbers is true, + A '+' or '-' immediately before a number is prepended onto that number and + if there is intervening whitespace, it is read as a separate symbol. + + If TextInput::Settings::signedNumbers is false, + read() does not distinguish between a plus or minus symbol next + to a number and a positive/negative number itself. For example, "x - 1" and "x -1" + will be parsed the same way by read(). + + In both cases, readNumber() will contract a leading "-" or "+" onto + a number. + */ + Token read(); + + + /** Read one token (or possibly two) as a number or throws + WrongTokenType, and returns the number. + + If the first token in the input is a number, it is returned directly. + + If TextInput::Settings::signedNumbers is false and the input stream + contains a '+' or '-' symbol token immediately followed by a number + token, both tokens will be consumed and a single token will be + returned by this method. + + WrongTokenType will be thrown if one of the input conditions + described above is not satisfied. When an exception is thrown, no + tokens are consumed. + */ + double readNumber(); + + bool readBoolean(); + + /** Reads a string token or throws WrongTokenType, and returns the token. + + Use this method (rather than readString) if you want the token's + location as well as its value. + + WrongTokenType will be thrown if the next token in the input stream + is not a string. When an exception is thrown, no tokens are + consumed. + */ + Token readStringToken(); + + /** Like readStringToken, but returns the token's string. + + Use this method (rather than readStringToken) if you want the token's + value but don't really care about its location in the input. Use of + readStringToken is encouraged for better error reporting. + */ + std::string readString(); + + /** Reads a specific string token or throws either WrongTokenType or + WrongString. If the next token in the input is a string matching @p + s, it will be consumed. + + Use this method if you want to match a specific string from the + input. In that case, typically error reporting related to the token + is only going to occur because of a mismatch, so no location + information is needed by the caller. + + WrongTokenType will be thrown if the next token in the input stream + is not a string. WrongString will be thrown if the next token in the + input stream is a string but does not match the @p s parameter. When + an exception is thrown, no tokens are consumed. + */ + void readString(const std::string& s); + + + /** Reads a symbol token or throws WrongTokenType, and returns the token. + + Use this method (rather than readSymbol) if you want the token's + location as well as its value. + + WrongTokenType will be thrown if the next token in the input stream + is not a symbol. When an exception is thrown, no tokens are + consumed. + */ + Token readSymbolToken(); + + /** Like readSymbolToken, but returns the token's string. + + Use this method (rather than readSymbolToken) if you want the token's + value but don't really care about its location in the input. Use of + readSymbolToken is encouraged for better error reporting. + */ + std::string readSymbol(); + + /** Reads a specific symbol token or throws either WrongTokenType or + WrongSymbol. If the next token in the input is a symbol matching @p + symbol, it will be consumed. + + Use this method if you want to match a specific symbol from the + input. In that case, typically error reporting related to the token + is only going to occur because of a mismatch, so no location + information is needed by the caller. + + WrongTokenType will be thrown if the next token in the input stream + is not a symbol. WrongSymbol will be thrown if the next token in the + input stream is a symbol but does not match the @p symbol parameter. + When an exception is thrown, no tokens are consumed. + */ + void readSymbol(const std::string& symbol); + + + /** Read a series of two specific symbols. See readSymbol. */ + inline void readSymbols(const std::string& s1, const std::string& s2) { + readSymbol(s1); + readSymbol(s2); + } + + /** Read a series of three specific symbols. See readSymbol. */ + inline void readSymbols( + const std::string& s1, + const std::string& s2, + const std::string& s3) { + readSymbol(s1); + readSymbol(s2); + readSymbol(s3); + } + + /** Read a series of four specific symbols. See readSymbol. */ + inline void readSymbols( + const std::string& s1, + const std::string& s2, + const std::string& s3, + const std::string& s4) { + readSymbol(s1); + readSymbol(s2); + readSymbol(s3); + readSymbol(s4); + } + + /** Return a copy of the next token in the input stream, but don't remove + it from the input stream. + */ + Token peek(); + + /** Returns the line number for the @e next token. See also peek. */ + int peekLineNumber(); + + /** Returns the character number (relative to the line) for the @e next + token in the input stream. See also peek. + */ + int peekCharacterNumber(); + + /** Take a previously read token and push it back at the front of the + input stream. + + Can be used in the case where more than one token of read-ahead is + needed (i.e., when peek doesn't suffice). + */ + void push(const Token& t); + + /** Returns the filename from which this input is drawn, or the first few + characters of the string if created from a string. + If options::filename is non-empty that will replace the + true filename.*/ + const std::string& filename() const; +}; + +void deserialize(bool& b, TextInput& ti); +void deserialize(int& b, TextInput& ti); +void deserialize(uint8& b, TextInput& ti); +void deserialize(double& b, TextInput& ti); +void deserialize(float& b, TextInput& ti); +void deserialize(std::string& b, TextInput& ti); + +} // namespace + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h b/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h new file mode 100644 index 00000000000..6ae7d14fe00 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h @@ -0,0 +1,249 @@ +/** + @file TextOutput.h + + @maintainer Morgan McGuire, morgan@graphics3d.com + @created 2004-06-21 + @edited 2006-10-24 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_TEXTOUTPUT_H +#define G3D_TEXTOUTPUT_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include <string> + +namespace G3D { + +/** + Convenient formatting of ASCII text written to a file. + <P> + + The core writeString, writeNumber, and writeSymbol methods map to TextInput's + methods. Number and Symbol each print an additional space that is used to + separate adjacent tokens. + + TextOutput::printf allows arbitrary text to be conveniently dumped + en-masse. Use [de]serialize(bool, TextOutput) and other overloads to read/write + primitive types in a standardized manner and + + <P> + When a word-wrap line break occurs, all whitespace between words is replaced + with a single newline (the newline may be two characters-- see + G3D::TextOutput::Options::NewlineStyle). Word wrapping occurs against + the number of columns specified by Options::numColumns, <I>minus</I> the current + indent level. + + Indenting adds the specified number of spaces immediately after a newline. + If a newline was followed by spaces in the original string, these are added + to the indent spaces. Indenting <B>will</B> indent blank lines and will leave + indents after the last newline of a file (if the indent level is non-zero at the end). + + <P><B>Serialization/Marshalling</B> + <DT>Text serialization is accomplished using TextOutput by defining the pair of + methods: + + <PRE> + void serialize(TextOutput& to) const; + void deserialize(TextInput& ti); + </PRE> + + See also G3D::TextInput. + + <P> + <B>BETA API</B> + <DT>This API is subject to change in future versions. + */ +class TextOutput { +public: + + class Settings { + public: + /** + WRAP_NONE Word wrapping is disabled + WRAP_WITHOUT_BREAKING Word-wrap, but don't break continuous lines that + are longer than numColumns (default) + WRAP_ALWAYS Wrap even if it means breaking a continuous line or + a quoted string. + + Word wrapping is only allowed at whitespaces ('\n', '\r', '\t', ' '); it + will not occur after commas, punctuation, minus signs, or any other characters + */ + enum WordWrapMode {WRAP_NONE, WRAP_WITHOUT_BREAKING, WRAP_ALWAYS}; + + /** Defaults to WRAP_WITHOUT_BREAKING */ + WordWrapMode wordWrap; + + /** Is word-wrapping allowed to insert newlines inside double quotes? + Default: false */ + bool allowWordWrapInsideDoubleQuotes; + + /** Number of columns for word wrapping. Default: 8 */ + int numColumns; + + /** Number of spaces in each indent. Default: 4 */ + int spacesPerIndent; + + /** Style of newline used by word wrapping and by (optional) conversion. + default: Windows: NEWLINE_WINDOWS, Linux, OS X: NEWLINE_UNIX. + */ + enum NewlineStyle {NEWLINE_WINDOWS, NEWLINE_UNIX}; + + NewlineStyle newlineStyle; + + /** If true, all newlines are converted to NewlineStyle regardless of + how they start out. Default: true. */ + bool convertNewlines; + + /** Used by writeBoolean */ + std::string trueSymbol; + + /** Used by writeBoolean */ + std::string falseSymbol; + + Settings() : + wordWrap(WRAP_WITHOUT_BREAKING), + allowWordWrapInsideDoubleQuotes(false), + numColumns(80), + spacesPerIndent(4), + convertNewlines(true), + trueSymbol("true"), + falseSymbol("false") { + #ifdef G3D_WIN32 + newlineStyle = NEWLINE_WINDOWS; + #else + newlineStyle = NEWLINE_UNIX; + #endif + } + }; + +private: + + /** Used by indentAndAppend to tell when we are writing the + first character of a new line. + + So that push/popIndent work correctly, we cannot indent + immediately after writing a newline. Instead we must + indent on writing the first character <B>after</B> that + newline. + */ + bool startingNewLine; + + /** Number of characters at the end of the buffer since the last newline */ + int currentColumn; + + /** True if we have seen an open " and no close ".*/ + bool inDQuote; + + /** Empty if there is none */ + std::string filename; + + Array<char> data; + + Settings option; + + /** Number of indents to prepend before each line. Always set using setIndentLevel.*/ + int indentLevel; + + void setIndentLevel(int i); + + /** Actual number of spaces to indent. */ + int indentSpaces; + + /** the newline character(s) */ + std::string newline; + + void setOptions(const Settings& _opt); + + /** Converts to the desired newlines. Called from vprintf */ + void convertNewlines(const std::string& in, std::string& out); + + /** Called from vprintf */ + void wordWrapIndentAppend(const std::string& str); + + /** Appends the character to data, indenting whenever a newline is encountered. + Called from wordWrapIndentAppend */ + void indentAppend(char c); + +public: + + explicit TextOutput(const std::string& filename, const Settings& options = Settings()); + + /** Constructs a text output that can later be commited to a string instead of a file.*/ + explicit TextOutput(const Settings& options = Settings()); + + /** Commit to the filename specified on the constructor. + <B>Not</B> called from the destructor; you must call + it yourself. + @param flush If true (default) the file is ready for reading when the method returns, otherwise + the method returns immediately and writes the file in the background.*/ + void commit(bool flush = true); + + /** Commits to this string */ + void commitString(std::string& string); + + /** Increase indent level by 1 */ + void pushIndent(); + + void popIndent(); + + /** Produces a new string that contains the output */ + std::string commitString(); + + /** Writes a quoted string. Special characters in the string (e.g., \, \t, \n) are escaped so that + TextInput will produce the identical string on reading.*/ + void writeString(const std::string& string); + + void writeBoolean(bool b); + + void writeNumber(double n); + + void writeNumber(int n); + + void writeNewline(); + void writeNewlines(int numLines); + + /** The symbol is written without quotes. Symbols are required to begin with a + letter or underscore and contain only letters, underscores, and numbers + or be a C++ symbol (e.g. "{", "(", "++", etc.) + so that they may be properly parsed by TextInput::readSymbol. Symbols are + printed with a trailing space.*/ + void writeSymbol(const std::string& string); + + /** Convenient idiom for writing multiple symbols in a row, e.g. + writeSymbols("name", "="); The empty symbols are not written. + */ + void writeSymbols( + const std::string& a, + const std::string& b = "", + const std::string& c = "", + const std::string& d = "", + const std::string& e = "", + const std::string& f = ""); + + /** Normal printf conventions. Note that the output will be reformatted + for word-wrapping and newlines */ + void __cdecl printf(const char* fmt, ...) + G3D_CHECK_PRINTF_METHOD_ARGS; + + // Can't pass by reference because that confuses va_start + void __cdecl printf(const std::string fmt, ...); + void __cdecl vprintf(const char* fmt, va_list argPtr) + G3D_CHECK_VPRINTF_METHOD_ARGS; +}; + +// Primitive serializers +void serialize(const bool& b, TextOutput& to); +void serialize(const int& b, TextOutput& to); +void serialize(const uint8& b, TextOutput& to); +void serialize(const double& b, TextOutput& to); +void serialize(const float& b, TextOutput& to); +void serialize(const std::string& b, TextOutput& to); +void serialize(const char* b, TextOutput& to); + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h b/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h new file mode 100644 index 00000000000..59c57b062ae --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h @@ -0,0 +1,79 @@ +#ifndef G3D_THREADSET_H +#define G3D_THREADSET_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/ReferenceCount.h" +#include "G3D/GThread.h" + +namespace G3D { + +/** Manages a set of threads. All methods are threadsafe except for + the iterator begin/end. + + @beta*/ +class ThreadSet : public ReferenceCountedObject { +public: + /** Intended to allow future use with a template parameter.*/ + typedef GThread Thread; + + typedef ReferenceCountedPointer<Thread> ThreadRef; + typedef ReferenceCountedPointer<ThreadSet> Ref; + typedef Array<ThreadRef>::Iterator Iterator; + typedef Array<ThreadRef>::ConstIterator ConstIterator; + +private: + + /** Protects m_thread */ + GMutex m_lock; + + /** Threads in the set */ + Array<ThreadRef> m_thread; + +public: + + /** Total number of threads (some of which may be completed). */ + int size() const; + + /** Number of threads that have been started */ + int numStarted() const; + + /** Start all threads that are not currently started */ + void start() const; + + /** Terminate all threads that are currently started */ + void terminate() const; + + /** Waits until all started threads have completed. */ + void waitForCompletion() const; + + /** Remove all (not stopping them) */ + void clear(); + + /** Removes completed threads and returns the new size.*/ + int removeCompleted(); + + /** Inserts a new thread, if it is not already present, and + returns the new number of threads.*/ + int insert(const ThreadRef& t); + + /** Removes a thread. Returns true if the thread was present and + removed. */ + bool remove(const ThreadRef& t); + + bool contains(const ThreadRef& t) const; + + /** It is an error to mutate the ThreadSet while iterating through it. */ + Iterator begin(); + + Iterator end(); + + ConstIterator begin() const; + + ConstIterator end() const; +}; + + +} // namespace G3D + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Triangle.h b/externals/g3dlite/G3D.lib/include/G3D/Triangle.h new file mode 100644 index 00000000000..8b67acf4624 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Triangle.h @@ -0,0 +1,143 @@ +/** + @file Triangle.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-04-05 + @edited 2008-10-06 + + @cite Random point method by Greg Turk, Generating random points in triangles. In A. S. Glassner, ed., Graphics Gems, pp. 24-28. Academic Press, 1990 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_TRIANGLE_H +#define G3D_TRIANGLE_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" +#include "G3D/Plane.h" +#include "G3D/BoundsTrait.h" +#include "G3D/debugAssert.h" +#include <string> + +namespace G3D { + +/** + A generic triangle representation. This should not be used + as the underlying triangle for creating models; it is intended + for providing fast property queries but requires a lot of + storage and is mostly immutable. + */ +class Triangle { +private: + friend class CollisionDetection; + friend class Ray; + + Vector3 _vertex[3]; + + /** edgeDirection[i] is the normalized vector v[i+1] - v[i] */ + Vector3 edgeDirection[3]; + float edgeMagnitude[3]; + Plane _plane; + Vector3::Axis _primaryAxis; + + /** vertex[1] - vertex[0] */ + Vector3 _edge01; + + /** vertex[2] - vertex[0] */ + Vector3 _edge02; + + float _area; + + void init(const Vector3& v0, const Vector3& v1, const Vector3& v2); + +public: + + Triangle(class BinaryInput& b); + void serialize(class BinaryOutput& b); + void deserialize(class BinaryInput& b); + + Triangle(); + + Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2); + + ~Triangle(); + + /** 0, 1, or 2 */ + inline const Vector3& vertex(int n) const { + debugAssert((n >= 0) && (n < 3)); + return _vertex[n]; + } + + /** vertex[1] - vertex[0] */ + inline const Vector3& edge01() const { + return _edge01; + } + + /** vertex[2] - vertex[0] */ + inline const Vector3& edge02() const { + return _edge02; + } + + float area() const; + + Vector3::Axis primaryAxis() const { + return _primaryAxis; + } + + const Vector3& normal() const; + + /** Barycenter */ + Vector3 center() const; + + const Plane& plane() const; + + /** Returns a random point in the triangle. */ + Vector3 randomPoint() const; + + inline void getRandomSurfacePoint(Vector3& P, + Vector3& N = Vector3::dummy) const { + P = randomPoint(); + N = normal(); + } + /** + For two triangles to be equal they must have + the same vertices <I>in the same order</I>. + That is, vertex[0] == vertex[0], etc. + */ + inline bool operator==(const Triangle& other) const { + for (int i = 0; i < 3; ++i) { + if (_vertex[i] != other._vertex[i]) { + return false; + } + } + + return true; + } + + inline size_t hashCode() const { + return + _vertex[0].hashCode() + + (_vertex[1].hashCode() >> 2) + + (_vertex[2].hashCode() >> 3); + } + + void getBounds(class AABox&) const; + +}; + +} // namespace G3D + +template <> struct HashTrait<G3D::Triangle> { + static size_t hashCode(const G3D::Triangle& key) { return key.hashCode(); } +}; + + +template<> struct BoundsTrait<class G3D::Triangle> { + static void getBounds(const G3D::Triangle& t, G3D::AABox& out) { t.getBounds(out); } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h b/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h new file mode 100644 index 00000000000..52db3080b80 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h @@ -0,0 +1,83 @@ +/** + @file UprightFrame.h + + @author Morgan McGuire, morgan@cs.williams.edu + */ + +#ifndef G3D_UPRIGHTFRAME_H +#define G3D_UPRIGHTFRAME_H + +#include "G3D/platform.h" +#include "G3D/Spline.h" +#include "G3D/Vector3.h" +#include "G3D/CoordinateFrame.h" + +namespace G3D { + +/** + Coordinate frame expressed in Euler angles. + Unlike a G3D::Quat, UprightFrame always keeps the reference frame from rolling about its own z axis. + Particularly useful for cameras. + + @sa G3D::CoordinateFrame, G3D::Matrix4, G3D::PhysicsFrame, G3D::UprightSpline, G3D::UprightSplineManipulator + */ +class UprightFrame { +public: + + Vector3 translation; + + /** -pi/2 < pitch < pi/2 in radians about the X-axis */ + float pitch; + + /** In radians about the Y-axis */ + float yaw; + + inline UprightFrame(const Vector3& t = Vector3::zero(), float p = 0, float y = 0) + : translation(t), pitch(p), yaw(y) {} + + UprightFrame(const CoordinateFrame& cframe); + + CoordinateFrame toCoordinateFrame() const; + + /** Supports implicit cast to CoordinateFrame */ + inline operator CoordinateFrame() const { + return toCoordinateFrame(); + } + + /** Required for use with spline */ + UprightFrame operator+(const UprightFrame& other) const; + + /** Required for use with spline */ + UprightFrame operator*(const float k) const; + + /** + Unwraps the yaw values in the elements of the array such that + they still represent the same angles but strictly increase/decrease + without wrapping about zero. For use with Spline<UprightFrame> + */ + static void unwrapYaw(UprightFrame* a, int N); + + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); +}; + +/** Shortest-path linear velocity spline for camera positions. Always keeps the camera from rolling. +@sa G3D::UprightSplineManipulator, G3D::UprightFrame +*/ +class UprightSpline : public Spline<UprightFrame> { +protected: + + virtual void ensureShortestPath(UprightFrame* A, int N) const { + UprightFrame::unwrapYaw(A, N); + } + +public: + + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + +}; + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector2.h b/externals/g3dlite/G3D.lib/include/G3D/Vector2.h new file mode 100644 index 00000000000..b610a1a3500 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector2.h @@ -0,0 +1,457 @@ +/** + @file Vector2.h + + 2D vector class + + @maintainer Morgan McGuire, morgan@cs.williams.edu + + @created 2001-06-02 + @edited 2008-11-30 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. +*/ + +#ifndef G3D_VECTOR2_H +#define G3D_VECTOR2_H + +#include <string> + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Table.h" +#include "G3D/HashTrait.h" +#include "G3D/Vector2int16.h" + +namespace G3D { + +class Vector2; +class Vector3; +class Vector4; + +/** + Do not subclass-- this implementation makes assumptions about the + memory layout. + */ +class Vector2 { +private: + // Hidden operators + bool operator<(const Vector2&) const; + bool operator>(const Vector2&) const; + bool operator<=(const Vector2&) const; + bool operator>=(const Vector2&) const; + +public: + float x; + float y; + + /** Creates the zero vector */ + Vector2(); + Vector2(class TextInput& t); + Vector2(class BinaryInput& b); + Vector2(float x, float y); + Vector2(float coordinate[2]); + Vector2(double coordinate[2]); + Vector2(const Vector2& other); + Vector2(const class Vector2int16& other); + + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + void serialize(class TextOutput& t) const; + void deserialize(class TextInput& t); + + float& operator[](int i); + const float& operator[](int i) const; + operator float*(); + operator const float*() const; + + // assignment and comparison + Vector2& operator=(const Vector2& other); + bool operator==(const Vector2& other) const; + bool operator!=(const Vector2& other) const; + size_t hashCode() const; + bool fuzzyEq(const Vector2& other) const; + bool fuzzyNe(const Vector2& other) const; + + /** Returns true if this vector has finite length */ + bool isFinite() const; + + /** Returns true if this vector has length == 0 */ + bool isZero() const; + + /** Returns true if this vector has length == 1 */ + bool isUnit() const; + + // arithmetic operations + Vector2 operator+(const Vector2& v) const; + Vector2 operator-(const Vector2& v) const; + Vector2 operator*(float s) const; + + /** Array (pointwise) multiplication */ + Vector2 operator*(const Vector2& v) const; + + /** Array division */ + Vector2 operator/(const Vector2& v) const; + Vector2 operator/(float s) const; + + /** Unary minus */ + Vector2 operator-() const; + + /** x + y */ + inline float sum() const { + return x + y; + } + + /** + Linear interpolation + */ + inline Vector2 lerp(const Vector2& v, float alpha) const { + return (*this) + (v - *this) * alpha; + } + + inline Vector2 clamp(const Vector2& low, const Vector2& high) const { + return Vector2( + G3D::clamp(x, low.x, high.x), + G3D::clamp(y, low.y, high.y)); + } + + inline Vector2 clamp(float low, float high) const { + return Vector2( + (float)G3D::clamp(x, low, high), + (float)G3D::clamp(y, low, high)); + } + + // arithmetic updates + Vector2& operator+=(const Vector2&); + Vector2& operator-=(const Vector2&); + Vector2& operator*=(float); + Vector2& operator/=(float); + Vector2& operator*=(const Vector2&); + Vector2& operator/=(const Vector2&); + + // vector operations + + /** */ + float length() const; + + /** Returns a unit-length vector */ + Vector2 direction() const; + + /** + Potentially less accurate but faster than direction(). + Only works if System::hasSSE is true. + */ + Vector2 fastDirection() const { + return direction(); + } + + float squaredLength() const; + float dot(const Vector2& s) const; + + /** + Make this vector have unit length and return the old length. + If the vector length was less than tolerance, do not normalize. + */ + float unitize(float fTolerance = 1e-06); + + Vector2 min(const Vector2& v) const; + Vector2 max(const Vector2& v) const; + + /** Uniformly distributed random vector on the unit sphere */ + static Vector2 random(); + + // Special values. + // Intentionally not inlined: see Matrix3::identity() for details. + static const Vector2& zero(); + inline static const Vector2& one() { static const Vector2 v(1, 1); return v; } + static const Vector2& unitX(); + static const Vector2& unitY(); + static const Vector2& inf(); + static const Vector2& nan(); + /** smallest (most negative) representable vector */ + static const Vector2& minFinite(); + /** Largest representable vector */ + static const Vector2& maxFinite(); + + std::string toString() const; + + // 2-char swizzles + + Vector2 xx() const; + Vector2 yx() const; + Vector2 xy() const; + Vector2 yy() const; + + // 3-char swizzles + + Vector3 xxx() const; + Vector3 yxx() const; + Vector3 xyx() const; + Vector3 yyx() const; + Vector3 xxy() const; + Vector3 yxy() const; + Vector3 xyy() const; + Vector3 yyy() const; + + // 4-char swizzles + + Vector4 xxxx() const; + Vector4 yxxx() const; + Vector4 xyxx() const; + Vector4 yyxx() const; + Vector4 xxyx() const; + Vector4 yxyx() const; + Vector4 xyyx() const; + Vector4 yyyx() const; + Vector4 xxxy() const; + Vector4 yxxy() const; + Vector4 xyxy() const; + Vector4 yyxy() const; + Vector4 xxyy() const; + Vector4 yxyy() const; + Vector4 xyyy() const; + Vector4 yyyy() const; + +}; + +inline Vector2 operator*(double s, const Vector2& v) { + return v * (float)s; +} + +inline Vector2 operator*(float s, const Vector2& v) { + return v * s; +} + +inline Vector2 operator*(int s, const Vector2& v) { + return v * (float)s; +} + + +inline Vector2::Vector2 () : x(0.0f), y(0.0f) { +} + + +inline Vector2::Vector2(float _x, float _y) : x(_x), y(_y) { +} + + +inline Vector2::Vector2 (float afCoordinate[2]) { + x = afCoordinate[0]; + y = afCoordinate[1]; +} + + + +inline Vector2::Vector2 (double afCoordinate[2]) { + x = (float)afCoordinate[0]; + y = (float)afCoordinate[1]; +} + + +inline Vector2::Vector2 (const Vector2& rkVector) { + x = rkVector.x; + y = rkVector.y; +} + + +inline Vector2::Vector2 (const Vector2int16& v) : x(v.x), y(v.y) { +} + + +inline float& Vector2::operator[] (int i) { + return ((float*)this)[i]; +} + + +inline const float& Vector2::operator[] (int i) const { + return ((float*)this)[i]; +} + + +inline Vector2::operator float* () { + return (float*)this; +} + +inline Vector2::operator const float* () const { + return (float*)this; +} + + +inline Vector2& Vector2::operator= (const Vector2& rkVector) { + x = rkVector.x; + y = rkVector.y; + return *this; +} + + +inline bool Vector2::operator== (const Vector2& rkVector) const { + return ( x == rkVector.x && y == rkVector.y); +} + + +inline bool Vector2::operator!= (const Vector2& rkVector) const { + return ( x != rkVector.x || y != rkVector.y); +} + + +inline Vector2 Vector2::operator+ (const Vector2& rkVector) const { + return Vector2(x + rkVector.x, y + rkVector.y); +} + + +inline Vector2 Vector2::operator- (const Vector2& rkVector) const { + return Vector2(x - rkVector.x, y - rkVector.y); +} + + +inline Vector2 Vector2::operator* (float fScalar) const { + return Vector2(fScalar*x, fScalar*y); +} + + + +inline Vector2 Vector2::operator- () const { + return Vector2( -x, -y); +} + + + +inline Vector2& Vector2::operator+= (const Vector2& rkVector) { + x += rkVector.x; + y += rkVector.y; + return *this; +} + + + +inline Vector2& Vector2::operator-= (const Vector2& rkVector) { + x -= rkVector.x; + y -= rkVector.y; + return *this; +} + + + +inline Vector2& Vector2::operator*= (float fScalar) { + x *= fScalar; + y *= fScalar; + return *this; +} + + + + +inline Vector2& Vector2::operator*= (const Vector2& rkVector) { + x *= rkVector.x; + y *= rkVector.y; + return *this; +} + + + +inline Vector2& Vector2::operator/= (const Vector2& rkVector) { + x /= rkVector.x; + y /= rkVector.y; + return *this; +} + + +inline Vector2 Vector2::operator* (const Vector2& rkVector) const { + return Vector2(x * rkVector.x, y * rkVector.y); +} + + + +inline Vector2 Vector2::operator/ (const Vector2& rkVector) const { + return Vector2(x / rkVector.x, y / rkVector.y); +} + + +inline float Vector2::squaredLength () const { + return x*x + y*y; +} + + +inline float Vector2::length () const { + return sqrtf(x*x + y*y); +} + + +inline Vector2 Vector2::direction () const { + float lenSquared = x * x + y * y; + + if (lenSquared != 1.0f) { + return *this / sqrtf(lenSquared); + } else { + return *this; + } +} + + + +inline float Vector2::dot (const Vector2& rkVector) const { + return x*rkVector.x + y*rkVector.y; +} + + + +inline Vector2 Vector2::min(const Vector2 &v) const { + return Vector2(G3D::min(v.x, x), G3D::min(v.y, y)); +} + + + +inline Vector2 Vector2::max(const Vector2 &v) const { + return Vector2(G3D::max(v.x, x), G3D::max(v.y, y)); +} + + + +inline bool Vector2::fuzzyEq(const Vector2& other) const { + return G3D::fuzzyEq((*this - other).squaredLength(), 0); +} + + + +inline bool Vector2::fuzzyNe(const Vector2& other) const { + return G3D::fuzzyNe((*this - other).squaredLength(), 0); +} + + + +inline bool Vector2::isFinite() const { + return G3D::isFinite(x) && G3D::isFinite(y); +} + + + +inline bool Vector2::isZero() const { + return (x == 0.0f) && (y == 0.0f); +} + + + +inline bool Vector2::isUnit() const { + return squaredLength() == 1.0f; +} + +} // namespace G3D + +template <> +struct HashTrait<G3D::Vector2> { + static size_t hashCode(const G3D::Vector2& key) { + return key.hashCode(); + } +}; + + +// Intentionally outside namespace to avoid operator overloading confusion +inline G3D::Vector2 operator*(double s, const G3D::Vector2& v) { + return v * (float)s; +} +inline G3D::Vector2 operator*(int s, const G3D::Vector2& v) { + return v * (float)s; +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h b/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h new file mode 100644 index 00000000000..b7149ad6c90 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h @@ -0,0 +1,137 @@ +/** + @file Vector2int16.h + + @maintainer Morgan McGuire, matrix@brown.edu + + @created 2003-08-09 + @edited 2004-01-03 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef VECTOR2INT16_H +#define VECTOR2INT16_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/HashTrait.h" + +namespace G3D { + +/** + A Vector2 that packs its fields into uint16s. + */ +#ifdef G3D_WIN32 + // Switch to tight alignment + #pragma pack(push, 2) +#endif + +class Vector2int16 { +private: + // Hidden operators + bool operator<(const Vector2int16&) const; + bool operator>(const Vector2int16&) const; + bool operator<=(const Vector2int16&) const; + bool operator>=(const Vector2int16&) const; + +public: + G3D::int16 x; + G3D::int16 y; + + Vector2int16() : x(0), y(0) {} + Vector2int16(G3D::int16 _x, G3D::int16 _y) : x(_x), y(_y){} + Vector2int16(const class Vector2& v); + Vector2int16(class BinaryInput& bi); + + inline G3D::int16& operator[] (int i) { + debugAssert(((unsigned int)i) <= 1); + return ((G3D::int16*)this)[i]; + } + + inline const G3D::int16& operator[] (int i) const { + debugAssert(((unsigned int)i) <= 1); + return ((G3D::int16*)this)[i]; + } + + inline Vector2int16 operator+(const Vector2int16& other) const { + return Vector2int16(x + other.x, y + other.y); + } + + inline Vector2int16 operator-(const Vector2int16& other) const { + return Vector2int16(x - other.x, y - other.y); + } + + inline Vector2int16 operator*(const Vector2int16& other) const { + return Vector2int16(x * other.x, y * other.y); + } + + inline Vector2int16 operator*(const int s) const { + return Vector2int16(x * s, y * s); + } + + inline Vector2int16& operator+=(const Vector2int16& other) { + x += other.x; + y += other.y; + return *this; + } + + /** Shifts both x and y */ + inline Vector2int16 operator>>(const int s) const { + return Vector2int16(x >> s, y >> s); + } + + /** Shifts both x and y */ + inline Vector2int16 operator<<(const int s) const { + return Vector2int16(x << s, y << s); + } + + inline Vector2int16& operator-=(const Vector2int16& other) { + x -= other.x; + y -= other.y; + return *this; + } + + inline Vector2int16& operator*=(const Vector2int16& other) { + x *= other.x; + y *= other.y; + return *this; + } + + Vector2int16 clamp(const Vector2int16& lo, const Vector2int16& hi); + + inline bool operator== (const Vector2int16& rkVector) const { + return ((int32*)this)[0] == ((int32*)&rkVector)[0]; + } + + inline bool operator!= (const Vector2int16& rkVector) const { + return ((int32*)this)[0] != ((int32*)&rkVector)[0]; + } + + Vector2int16 max(const Vector2int16& v) const { + return Vector2int16(iMax(x, v.x), iMax(y, v.y)); + } + + Vector2int16 min(const Vector2int16& v) const { + return Vector2int16(iMin(x, v.x), iMin(y, v.y)); + } + + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); +} +#if defined(G3D_LINUX) || defined(G3D_OSX) + __attribute((aligned(1))) +#endif +; + +#ifdef G3D_WIN32 + #pragma pack(pop) +#endif + +} + +template<> struct HashTrait<G3D::Vector2int16> { + static size_t hashCode(const G3D::Vector2int16& key) { return static_cast<size_t>(key.x + ((int)key.y << 16)); } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3.h new file mode 100644 index 00000000000..d37638a229d --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3.h @@ -0,0 +1,761 @@ +/** + @file Vector3.h + + 3D vector class + + @maintainer Morgan McGuire, morgan@cs.williams.edu + + @created 2001-06-02 + @edited 2008-11-01 + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_VECTOR3_H +#define G3D_VECTOR3_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector2.h" +#include "G3D/Table.h" +#include "G3D/HashTrait.h" +#include "G3D/PositionTrait.h" +#include "G3D/Vector2.h" +#include <iostream> +#include <string> + +//---------------------------------------------------------------------------- +#ifdef SSE + // If you receive an error on this line, it is because you do not have the file + // xmmintrin.h needed for MMX & SSE extensions. Download and install + // + // http://download.microsoft.com/download/vstudio60ent/SP5/Wideband-Full/WIN98Me/EN-US/vs6sp5.exe + // and + // http://download.microsoft.com/download/vb60ent/Update/6/W9X2KXP/EN-US/vcpp5.exe + // + // to get this file. +# include <xmmintrin.h> +#endif + +namespace G3D { + +class Vector2; +class Vector3; +class Vector4; +class Vector4int8; + +/** + <B>Swizzles</B> + Vector classes have swizzle operators, e.g. <CODE>v.xy()</CODE>, that + allow selection of arbitrary sub-fields. These cannot be used as write + masks. Examples + + <PRE> +Vector3 v(1, 2, 3); +Vector3 j; +Vector2 b; + +b = v.xz(); +j = b.xx(); +</PRE> + + + <B>Warning</B> + + Do not subclass-- this implementation makes assumptions about the + memory layout. + */ +class Vector3 { +private: + /** + Reflect this vector about the (not necessarily unit) normal. + Note that if used for a collision or ray reflection you + must negate the resulting vector to get a direction pointing + <I>away</I> from the collision. + + <PRE> + V' N V + + r ^ -, + \ | / + \|/ + </PRE> + + See also Vector3::reflectionDirection + */ + Vector3 reflectAbout(const Vector3& normal) const; + + // Hidden operators + bool operator<(const Vector3&) const; + bool operator>(const Vector3&) const; + bool operator<=(const Vector3&) const; + bool operator>=(const Vector3&) const; + +public: + // construction + Vector3(); + + /** Divides by 127 */ + Vector3(const Vector4int8&); + explicit Vector3(class BinaryInput& b); + Vector3(float _x, float _y, float _z); + explicit Vector3(const class Vector2& v, float _z); + explicit Vector3(float coordinate[3]); + explicit Vector3(double coordinate[3]); + Vector3(const Vector3& rkVector); + Vector3(const class Vector3int16& v); + explicit Vector3(class TextInput& t); + + /** Format is three float32's */ + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + /** Format is "(%f, %f, %f)" */ + void serialize(class TextOutput& t) const; + void deserialize(class TextInput& t); + + // coordinates + float x, y, z; + + // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z + // + // WARNING. These member functions rely on + // (1) Vector3 not having virtual functions + // (2) the data packed in a 3*sizeof(float) memory block + const float& operator[] (int i) const; + float& operator[] (int i); + + inline operator float* () { + return (float*)this; + } + + operator const float* () const { + return (float*)this; + } + + enum Axis {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, DETECT_AXIS=-1}; + + /** + Returns the largest dimension. Particularly convenient for determining + which plane to project a triangle onto for point-in-polygon tests. + */ + Axis primaryAxis() const; + + // assignment and comparison + Vector3& operator= (const Vector3& rkVector); + bool operator== (const Vector3& rkVector) const; + bool operator!= (const Vector3& rkVector) const; + size_t hashCode() const; + bool fuzzyEq(const Vector3& other) const; + bool fuzzyNe(const Vector3& other) const; + + /** Returns true if this vector has finite length. */ + bool isFinite() const; + + /** Returns true if this vector has length ~= 0 */ + bool isZero() const; + + /** Returns true if this vector has length ~= 1 */ + bool isUnit() const; + + // arithmetic operations + Vector3 operator+ (const Vector3& v) const; + Vector3 operator- (const Vector3& v) const; + Vector3 operator* (float s) const; + Vector3 operator/ (float s) const; + Vector3 operator* (const Vector3& v) const; + Vector3 operator/ (const Vector3& v) const; + Vector3 operator- () const; + + // arithmetic updates + Vector3& operator+= (const Vector3& v); + Vector3& operator-= (const Vector3& v); + Vector3& operator*= (float s); + Vector3& operator/= (float s); + Vector3& operator*= (const Vector3& v); + Vector3& operator/= (const Vector3& v); + + /** Same as magnitude */ + float length() const; + + float magnitude() const; + + /** + The result is a nan vector if the length is almost zero. + */ + Vector3 direction() const; + + /** + Potentially less accurate but faster than direction(). + Only works if System::hasSSE is true. + */ + Vector3 fastDirection() const; + + + /** + See also G3D::Ray::reflect. + The length is 1. + <PRE> + V' N V + + r ^ / + \ | / + \|'- + </PRE> + */ + Vector3 reflectionDirection(const Vector3& normal) const; + + + /** + Returns Vector3::zero() if the length is nearly zero, otherwise + returns a unit vector. + */ + inline Vector3 directionOrZero() const { + float mag = magnitude(); + if (G3D::fuzzyEq(mag, 0.0f)) { + return Vector3::zero(); + } else if (G3D::fuzzyEq(mag, 1.0f)) { + return *this; + } else { + return *this * (1.0f / mag); + } + } + + /** + Returns the direction of a refracted ray, + where iExit is the index of refraction for the + previous material and iEnter is the index of refraction + for the new material. Like Vector3::reflectionDirection, + the result has length 1 and is + pointed <I>away</I> from the intersection. + + Returns Vector3::zero() in the case of total internal refraction. + + @param iOutside The index of refraction (eta) outside + (on the <I>positive</I> normal side) of the surface. + + @param iInside The index of refraction (eta) inside + (on the <I>negative</I> normal side) of the surface. + + See also G3D::Ray::refract. + <PRE> + N V + + ^ / + | / + |'- + __-- + V'<-- + </PRE> + */ + Vector3 refractionDirection( + const Vector3& normal, + float iInside, + float iOutside) const; + + /** Synonym for direction */ + inline Vector3 unit() const { + return direction(); + } + + /** Returns a normalized vector. May be computed with lower + precision than unit */ + inline Vector3 fastUnit() const { + return fastDirection(); + } + + /** Same as squaredMagnitude */ + float squaredLength() const; + + float squaredMagnitude () const; + + float dot(const Vector3& rkVector) const; + + float unitize(float tolerance = 1e-06); + + /** Cross product. Note that two cross products in a row + can be computed more cheaply: v1 x (v2 x v3) = (v1 dot v3) v2 - (v1 dot v2) v3. + */ + Vector3 cross(const Vector3& rkVector) const; + Vector3 unitCross (const Vector3& rkVector) const; + + /** + Returns a matrix such that v.cross() * w = v.cross(w). + <PRE> + [ 0 -v.z v.y ] + [ v.z 0 -v.x ] + [ -v.y v.x 0 ] + </PRE> + */ + class Matrix3 cross() const; + + Vector3 min(const Vector3 &v) const; + Vector3 max(const Vector3 &v) const; + + /** Smallest element */ + inline float min() const { + return G3D::min(G3D::min(x, y), z); + } + + /** Largest element */ + inline float max() const { + return G3D::max(G3D::max(x, y), z); + } + + std::string toString() const; + + inline Vector3 clamp(const Vector3& low, const Vector3& high) const { + return Vector3( + G3D::clamp(x, low.x, high.x), + G3D::clamp(y, low.y, high.y), + G3D::clamp(z, low.z, high.z)); + } + + inline Vector3 clamp(float low, float high) const { + return Vector3( + G3D::clamp(x, low, high), + G3D::clamp(y, low, high), + G3D::clamp(z, low, high)); + } + + /** + Linear interpolation + */ + inline Vector3 lerp(const Vector3& v, float alpha) const { + return (*this) + (v - *this) * alpha; + } + + /** Gram-Schmidt orthonormalization. */ + static void orthonormalize (Vector3 akVector[3]); + + /** Random unit vector, uniformly distributed */ + static Vector3 random(); + + /** Random unit vector, distributed + so that the probability of V is proportional + to max(V dot Normal, 0). + + @cite Henrik Wann Jensen, Realistic Image Synthesis using Photon Mapping eqn 2.24 + */ + static Vector3 cosRandom(const Vector3& normal); + + + /** + Random vector distributed over the hemisphere about normal. + */ + static Vector3 hemiRandom(const Vector3& normal); + + // Input W must be initialize to a nonzero vector, output is {U,V,W} + // an orthonormal basis. A hint is provided about whether or not W + // is already unit length. + static void generateOrthonormalBasis (Vector3& rkU, Vector3& rkV, + Vector3& rkW, bool bUnitLengthW = true); + + inline float sum() const { + return x + y + z; + } + + inline float average() const { + return sum() / 3.0f; + } + + // Special values. + inline static const Vector3& zero() { static Vector3 v(0, 0, 0); return v; } + inline static const Vector3& one() { static Vector3 v(1, 1, 1); return v; } + inline static const Vector3& unitX() { static Vector3 v(1, 0, 0); return v; } + inline static const Vector3& unitY() { static Vector3 v(0, 1, 0); return v; } + inline static const Vector3& unitZ() { static Vector3 v(0, 0, 1); return v; } + inline static const Vector3& inf() { static Vector3 v((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); return v; } + inline static const Vector3& nan() { static Vector3 v((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan()); return v; } + /** Smallest (most negative) representable vector */ + inline static const Vector3& minFinite(){ static Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; } + /** Largest representable vector */ + inline static const Vector3& maxFinite(){ static Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; } + + // 2-char swizzles + + Vector2 xx() const; + Vector2 yx() const; + Vector2 zx() const; + Vector2 xy() const; + Vector2 yy() const; + Vector2 zy() const; + Vector2 xz() const; + Vector2 yz() const; + Vector2 zz() const; + + // 3-char swizzles + + Vector3 xxx() const; + Vector3 yxx() const; + Vector3 zxx() const; + Vector3 xyx() const; + Vector3 yyx() const; + Vector3 zyx() const; + Vector3 xzx() const; + Vector3 yzx() const; + Vector3 zzx() const; + Vector3 xxy() const; + Vector3 yxy() const; + Vector3 zxy() const; + Vector3 xyy() const; + Vector3 yyy() const; + Vector3 zyy() const; + Vector3 xzy() const; + Vector3 yzy() const; + Vector3 zzy() const; + Vector3 xxz() const; + Vector3 yxz() const; + Vector3 zxz() const; + Vector3 xyz() const; + Vector3 yyz() const; + Vector3 zyz() const; + Vector3 xzz() const; + Vector3 yzz() const; + Vector3 zzz() const; + + // 4-char swizzles + + Vector4 xxxx() const; + Vector4 yxxx() const; + Vector4 zxxx() const; + Vector4 xyxx() const; + Vector4 yyxx() const; + Vector4 zyxx() const; + Vector4 xzxx() const; + Vector4 yzxx() const; + Vector4 zzxx() const; + Vector4 xxyx() const; + Vector4 yxyx() const; + Vector4 zxyx() const; + Vector4 xyyx() const; + Vector4 yyyx() const; + Vector4 zyyx() const; + Vector4 xzyx() const; + Vector4 yzyx() const; + Vector4 zzyx() const; + Vector4 xxzx() const; + Vector4 yxzx() const; + Vector4 zxzx() const; + Vector4 xyzx() const; + Vector4 yyzx() const; + Vector4 zyzx() const; + Vector4 xzzx() const; + Vector4 yzzx() const; + Vector4 zzzx() const; + Vector4 xxxy() const; + Vector4 yxxy() const; + Vector4 zxxy() const; + Vector4 xyxy() const; + Vector4 yyxy() const; + Vector4 zyxy() const; + Vector4 xzxy() const; + Vector4 yzxy() const; + Vector4 zzxy() const; + Vector4 xxyy() const; + Vector4 yxyy() const; + Vector4 zxyy() const; + Vector4 xyyy() const; + Vector4 yyyy() const; + Vector4 zyyy() const; + Vector4 xzyy() const; + Vector4 yzyy() const; + Vector4 zzyy() const; + Vector4 xxzy() const; + Vector4 yxzy() const; + Vector4 zxzy() const; + Vector4 xyzy() const; + Vector4 yyzy() const; + Vector4 zyzy() const; + Vector4 xzzy() const; + Vector4 yzzy() const; + Vector4 zzzy() const; + Vector4 xxxz() const; + Vector4 yxxz() const; + Vector4 zxxz() const; + Vector4 xyxz() const; + Vector4 yyxz() const; + Vector4 zyxz() const; + Vector4 xzxz() const; + Vector4 yzxz() const; + Vector4 zzxz() const; + Vector4 xxyz() const; + Vector4 yxyz() const; + Vector4 zxyz() const; + Vector4 xyyz() const; + Vector4 yyyz() const; + Vector4 zyyz() const; + Vector4 xzyz() const; + Vector4 yzyz() const; + Vector4 zzyz() const; + Vector4 xxzz() const; + Vector4 yxzz() const; + Vector4 zxzz() const; + Vector4 xyzz() const; + Vector4 yyzz() const; + Vector4 zyzz() const; + Vector4 xzzz() const; + Vector4 yzzz() const; + Vector4 zzzz() const; + + /** A value that can be passed to ignore a parameter. Never look at the result of dummy. */ + static Vector3 dummy; +}; + +inline G3D::Vector3 operator*(float s, const G3D::Vector3& v) { + return v * s; +} + +inline G3D::Vector3 operator*(double s, const G3D::Vector3& v) { + return v * (float)s; +} + +inline G3D::Vector3 operator*(int s, const G3D::Vector3& v) { + return v * (float)s; +} + +std::ostream& operator<<(std::ostream& os, const Vector3&); + + +void serialize(const Vector3::Axis& a, class BinaryOutput& bo); +void deserialize(Vector3::Axis& a, class BinaryInput& bo); + + +//---------------------------------------------------------------------------- +inline Vector3::Vector3() : x(0.0f), y(0.0f), z(0.0f) { +} + +//---------------------------------------------------------------------------- + +inline Vector3::Vector3 (float fX, float fY, float fZ) : x(fX), y(fY), z(fZ) { +} + +//---------------------------------------------------------------------------- +inline Vector3::Vector3 (float V[3]) : x(V[0]), y(V[1]), z(V[2]){ +} +//---------------------------------------------------------------------------- +inline Vector3::Vector3 (double V[3]) : x((float)V[0]), y((float)V[1]), z((float)V[2]){ +} + +//---------------------------------------------------------------------------- +inline Vector3::Vector3 (const Vector3& V) : x(V.x), y(V.y), z(V.z) { +} + +//---------------------------------------------------------------------------- + +//inline Vector3::Vector3 (const __m128& m) { + // Cast from SSE packed floats +// *this = *(Vector3*)&m; +//} + +//---------------------------------------------------------------------------- +inline const float& Vector3::operator[] (int i) const { + return ((float*)this)[i]; +} + +inline float& Vector3::operator[] (int i) { + return ((float*)this)[i]; +} + + +//---------------------------------------------------------------------------- +inline Vector3& Vector3::operator= (const Vector3& rkVector) { + x = rkVector.x; + y = rkVector.y; + z = rkVector.z; + return *this; +} + +//---------------------------------------------------------------------------- + +inline bool Vector3::fuzzyEq(const Vector3& other) const { + return G3D::fuzzyEq((*this - other).squaredMagnitude(), 0); +} + +//---------------------------------------------------------------------------- + +inline bool Vector3::fuzzyNe(const Vector3& other) const { + return G3D::fuzzyNe((*this - other).squaredMagnitude(), 0); +} + +//---------------------------------------------------------------------------- + +inline bool Vector3::isFinite() const { + return G3D::isFinite(x) && G3D::isFinite(y) && G3D::isFinite(z); +} + +//---------------------------------------------------------------------------- +inline bool Vector3::operator== (const Vector3& rkVector) const { + return ( x == rkVector.x && y == rkVector.y && z == rkVector.z ); +} + +//---------------------------------------------------------------------------- +inline bool Vector3::operator!= (const Vector3& rkVector) const { + return ( x != rkVector.x || y != rkVector.y || z != rkVector.z ); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::operator+ (const Vector3& rkVector) const { + return Vector3(x + rkVector.x, y + rkVector.y, z + rkVector.z); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::operator- (const Vector3& rkVector) const { + return Vector3(x - rkVector.x, y - rkVector.y, z - rkVector.z); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::operator* (const Vector3& rkVector) const { + return Vector3(x * rkVector.x, y * rkVector.y, z * rkVector.z); +} + +inline Vector3 Vector3::operator*(float f) const { + return Vector3(x * f, y * f, z * f); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::operator/ (const Vector3& rkVector) const { + return Vector3(x / rkVector.x, y / rkVector.y, z / rkVector.z); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::operator- () const { + return Vector3(-x, -y, -z); +} + +//---------------------------------------------------------------------------- +inline Vector3& Vector3::operator+= (const Vector3& rkVector) { + x += rkVector.x; + y += rkVector.y; + z += rkVector.z; + return *this; +} + +//---------------------------------------------------------------------------- +inline Vector3& Vector3::operator-= (const Vector3& rkVector) { + x -= rkVector.x; + y -= rkVector.y; + z -= rkVector.z; + return *this; +} + +//---------------------------------------------------------------------------- +inline Vector3& Vector3::operator*= (float fScalar) { + x *= fScalar; + y *= fScalar; + z *= fScalar; + return *this; +} + +//---------------------------------------------------------------------------- +inline Vector3& Vector3::operator*= (const Vector3& rkVector) { + x *= rkVector.x; + y *= rkVector.y; + z *= rkVector.z; + return *this; +} + +//---------------------------------------------------------------------------- +inline Vector3& Vector3::operator/= (const Vector3& rkVector) { + x /= rkVector.x; + y /= rkVector.y; + z /= rkVector.z; + return *this; +} + +//---------------------------------------------------------------------------- +inline float Vector3::squaredMagnitude () const { + return x*x + y*y + z*z; +} + +//---------------------------------------------------------------------------- +inline float Vector3::squaredLength () const { + return squaredMagnitude(); +} + +//---------------------------------------------------------------------------- +inline float Vector3::magnitude() const { + return sqrtf(x*x + y*y + z*z); +} + +//---------------------------------------------------------------------------- +inline float Vector3::length() const { + return magnitude(); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::direction () const { + float lenSquared = squaredMagnitude(); + float invSqrt = 1.0f / sqrtf(lenSquared); + return Vector3(x * invSqrt, y * invSqrt, z * invSqrt); +} + +//---------------------------------------------------------------------------- + +inline Vector3 Vector3::fastDirection () const { + float lenSquared = x * x + y * y + z * z; + float invSqrt = rsq(lenSquared); + return Vector3(x * invSqrt, y * invSqrt, z * invSqrt); +} + +//---------------------------------------------------------------------------- +inline float Vector3::dot (const Vector3& rkVector) const { + return x*rkVector.x + y*rkVector.y + z*rkVector.z; +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::cross (const Vector3& rkVector) const { + return Vector3(y*rkVector.z - z*rkVector.y, z*rkVector.x - x*rkVector.z, + x*rkVector.y - y*rkVector.x); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::unitCross (const Vector3& rkVector) const { + Vector3 kCross(y*rkVector.z - z*rkVector.y, z*rkVector.x - x*rkVector.z, + x*rkVector.y - y*rkVector.x); + kCross.unitize(); + return kCross; +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::min(const Vector3 &v) const { + return Vector3(G3D::min(v.x, x), G3D::min(v.y, y), G3D::min(v.z, z)); +} + +//---------------------------------------------------------------------------- +inline Vector3 Vector3::max(const Vector3 &v) const { + return Vector3(G3D::max(v.x, x), G3D::max(v.y, y), G3D::max(v.z, z)); +} + +//---------------------------------------------------------------------------- +inline bool Vector3::isZero() const { + return G3D::fuzzyEq(squaredMagnitude(), 0.0f); +} + +//---------------------------------------------------------------------------- + +inline bool Vector3::isUnit() const { + return G3D::fuzzyEq(squaredMagnitude(), 1.0f); +} + +} // namespace G3D + + +template <> +struct HashTrait<G3D::Vector3> { + static size_t hashCode(const G3D::Vector3& key) { + return key.hashCode(); + } +}; + + +template<> struct PositionTrait<class G3D::Vector2> { + static void getPosition(const G3D::Vector2& v, G3D::Vector3& p) { p = G3D::Vector3(v, 0); } +}; + +template<> struct PositionTrait<class G3D::Vector3> { + static void getPosition(const G3D::Vector3& v, G3D::Vector3& p) { p = v; } +}; + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h new file mode 100644 index 00000000000..f3f30a5bab4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h @@ -0,0 +1,130 @@ +/** + @file Vector3int16.h + + @maintainer Morgan McGuire, matrix@brown.edu + + @created 2003-04-07 + @edited 2003-06-24 + Copyright 2000-2004, Morgan McGuire. + All rights reserved. + */ + +#ifndef VECTOR3INT16_H +#define VECTOR3INT16_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/HashTrait.h" + +namespace G3D { + +/** + A Vector3 that packs its fields into uint16s. + */ +#ifdef G3D_WIN32 + // Switch to tight alignment + #pragma pack(push, 2) +#endif + +class Vector3int16 { +private: + // Hidden operators + bool operator<(const Vector3int16&) const; + bool operator>(const Vector3int16&) const; + bool operator<=(const Vector3int16&) const; + bool operator>=(const Vector3int16&) const; + +public: + G3D::int16 x; + G3D::int16 y; + G3D::int16 z; + + Vector3int16() : x(0), y(0), z(0) {} + Vector3int16(G3D::int16 _x, G3D::int16 _y, G3D::int16 _z) : x(_x), y(_y), z(_z) {} + Vector3int16(const class Vector3& v); + Vector3int16(class BinaryInput& bi); + + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); + + inline G3D::int16& operator[] (int i) { + debugAssert(i <= 2); + return ((G3D::int16*)this)[i]; + } + + inline const G3D::int16& operator[] (int i) const { + debugAssert(i <= 2); + return ((G3D::int16*)this)[i]; + } + + inline Vector3int16 operator+(const Vector3int16& other) const { + return Vector3int16(x + other.x, y + other.y, z + other.z); + } + + inline Vector3int16 operator-(const Vector3int16& other) const { + return Vector3int16(x - other.x, y - other.y, z - other.z); + } + + inline Vector3int16 operator*(const Vector3int16& other) const { + return Vector3int16(x * other.x, y * other.y, z * other.z); + } + + inline Vector3int16 operator*(const int s) const { + return Vector3int16(x * s, y * s, z * s); + } + + inline Vector3int16& operator+=(const Vector3int16& other) { + x += other.x; + y += other.y; + z += other.y; + return *this; + } + + inline Vector3int16& operator-=(const Vector3int16& other) { + x -= other.x; + y -= other.y; + z -= other.z; + return *this; + } + + inline Vector3int16& operator*=(const Vector3int16& other) { + x *= other.x; + y *= other.y; + z *= other.z; + return *this; + } + + inline bool operator== (const Vector3int16& rkVector) const { + return ( x == rkVector.x && y == rkVector.y && z == rkVector.z ); + } + + inline bool operator!= (const Vector3int16& rkVector) const { + return ( x != rkVector.x || y != rkVector.y || z != rkVector.z ); + } + + Vector3int16 max(const Vector3int16& v) const { + return Vector3int16(iMax(x, v.x), iMax(y, v.y), iMax(z, v.z)); + } + + Vector3int16 min(const Vector3int16& v) const { + return Vector3int16(iMin(x, v.x), iMin(y, v.y), iMin(z, v.z)); + } + + std::string toString() const; +} +#if defined(G3D_LINUX) || defined(G3D_OSX) + __attribute((aligned(1))) +#endif +; + +#ifdef G3D_WIN32 + #pragma pack(pop) +#endif + +} + +template <> struct HashTrait<G3D::Vector3int16> { + static size_t hashCode(const G3D::Vector3int16& key) { return static_cast<size_t>(key.x + ((int)key.y << 5) + ((int)key.z << 10)); } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h new file mode 100644 index 00000000000..01c8581b47a --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h @@ -0,0 +1,138 @@ +/** + @file Vector3int32.h + + @maintainer Morgan McGuire, matrix@brown.edu + + @created 2008-07-01 + @edited 2008-07-01 + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef VECTOR3INT32_H +#define VECTOR3INT32_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/HashTrait.h" + +namespace G3D { + +/** + A Vector3 that packs its fields into uint32s. + */ +#ifdef G3D_WIN32 + // Switch to tight alignment + #pragma pack(push, 4) +#endif + +class Vector3int32 { +private: + // Hidden operators + bool operator<(const Vector3int32&) const; + bool operator>(const Vector3int32&) const; + bool operator<=(const Vector3int32&) const; + bool operator>=(const Vector3int32&) const; + +public: + G3D::int32 x; + G3D::int32 y; + G3D::int32 z; + + Vector3int32() : x(0), y(0), z(0) {} + Vector3int32(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {} + Vector3int32(const class Vector3int16& v); + Vector3int32(const class Vector3& v); + Vector3int32(class BinaryInput& bi); + + void serialize(class BinaryOutput& bo) const; + void deserialize(class BinaryInput& bi); + + inline G3D::int32& operator[] (int i) { + debugAssert(i <= 2); + return ((G3D::int32*)this)[i]; + } + + inline const G3D::int32& operator[] (int i) const { + debugAssert(i <= 2); + return ((G3D::int32*)this)[i]; + } + + inline Vector3int32 operator+(const Vector3int32& other) const { + return Vector3int32(x + other.x, y + other.y, z + other.z); + } + + inline Vector3int32 operator-(const Vector3int32& other) const { + return Vector3int32(x - other.x, y - other.y, z - other.z); + } + + inline Vector3int32 operator*(const Vector3int32& other) const { + return Vector3int32(x * other.x, y * other.y, z * other.z); + } + + inline Vector3int32 operator*(const int s) const { + return Vector3int32(x * s, y * s, z * s); + } + + inline Vector3int32& operator+=(const Vector3int32& other) { + x += other.x; + y += other.y; + z += other.y; + return *this; + } + + inline Vector3int32& operator-=(const Vector3int32& other) { + x -= other.x; + y -= other.y; + z -= other.z; + return *this; + } + + inline Vector3int32& operator*=(const Vector3int32& other) { + x *= other.x; + y *= other.y; + z *= other.z; + return *this; + } + + inline bool operator== (const Vector3int32& rkVector) const { + return ( x == rkVector.x && y == rkVector.y && z == rkVector.z ); + } + + inline bool operator!= (const Vector3int32& rkVector) const { + return ( x != rkVector.x || y != rkVector.y || z != rkVector.z ); + } + + Vector3int32 max(const Vector3int32& v) const { + return Vector3int32(iMax(x, v.x), iMax(y, v.y), iMax(z, v.z)); + } + + Vector3int32 min(const Vector3int32& v) const { + return Vector3int32(iMin(x, v.x), iMin(y, v.y), iMin(z, v.z)); + } + + std::string toString() const; +} +#if defined(G3D_LINUX) || defined(G3D_OSX) + __attribute((aligned(1))) +#endif +; + +#ifdef G3D_WIN32 + #pragma pack(pop) +#endif + +} + +template <> struct HashTrait<G3D::Vector3int32> { + static size_t hashCode(const G3D::Vector3int32& key) { + // Mask for the top bit of a uint32 + const G3D::uint32 top = (1 << 31); + // Mask for the bottom 10 bits of a uint32 + const G3D::uint32 bot = 0x000003FF; + return static_cast<size_t>(((key.x & top) | ((key.y & top) >> 1) | ((key.z & top) >> 2)) | + (((key.x & bot) << 19) ^ ((key.y & bot) << 10) ^ (key.z & bot))); + } +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector4.h b/externals/g3dlite/G3D.lib/include/G3D/Vector4.h new file mode 100644 index 00000000000..d60ff76ea8b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector4.h @@ -0,0 +1,717 @@ +/** + @file Vector4.h + + Homogeneous vector class. + + @maintainer Morgan McGuire, morgan@cs.williams.edu + + @created 2002-07-09 + @edited 2008-11-01 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_VECTOR4_H +#define G3D_VECTOR4_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3.h" +#include "G3D/Vector2.h" +#include "G3D/Table.h" +#include "G3D/HashTrait.h" +#include "G3D/PositionTrait.h" +#include <string> + +namespace G3D { + +class Vector2; +class Vector3; +class Vector4; +class Vector4int8; + +/** + Do not subclass-- this implementation makes assumptions about the + memory layout. + */ +class Vector4 { +private: + // Hidden operators + bool operator<(const Vector4&) const; + bool operator>(const Vector4&) const; + bool operator<=(const Vector4&) const; + bool operator>=(const Vector4&) const; + +public: + // construction + Vector4(); + Vector4(float fX, float fY, float fZ, float fW); + Vector4(float afCoordinate[4]); + Vector4(const Vector4& rkVector); + Vector4(const class Color4& c); + Vector4(const Vector3& rkVector, float fW); + Vector4(const Vector2& v1, const Vector2& v2); + Vector4(const Vector2& v1, float fz, float fw); + + /** Divides by 127 when converting */ + Vector4(const Vector4int8&); + + Vector4(class BinaryInput& b); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + // coordinates + float x, y, z, w; + + // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z, etc. + // + // WARNING. These member functions rely on + // (1) Vector4 not having virtual functions + // (2) the data packed in a 4*sizeof(float) memory block + float& operator[] (int i); + const float& operator[] (int i) const; + operator float* (); + operator const float* () const; + + // assignment and comparison + Vector4& operator= (const Vector4& rkVector); + bool operator== (const Vector4& rkVector) const; + bool operator!= (const Vector4& rkVector) const; + + inline void set(float _x, float _y, float _z, float _w) { + x = _x; + y = _y; + z = _z; + w = _w; + } + + inline void set(const Vector3& v, float _w) { + x = v.x; + y = v.y; + z = v.z; + w = _w; + } + + inline void set(const Vector2& v, float _z, float _w) { + x = v.x; + y = v.y; + z = _z; + w = _w; + } + + size_t hashCode() const; + bool fuzzyEq(const Vector4& other) const; + bool fuzzyNe(const Vector4& other) const; + + inline static const Vector4& inf() { static Vector4 v((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); return v; } + inline static const Vector4& nan() { static Vector4 v((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan()); return v; } + + /** sqrt(this->dot(*this)) */ + float length() const; + float squaredLength() const; + + inline float sum() const { + return x + y + z + w; + } + + /** Returns true if this vector has finite length */ + bool isFinite() const; + + /** Returns true if this vector has length == 0 */ + bool isZero() const; + + /** Returns true if this vector has length == 1 */ + bool isUnit() const; + + // arithmetic operations + Vector4 operator+ (const Vector4& rkVector) const; + Vector4 operator- (const Vector4& rkVector) const; + + inline Vector4 operator*(const Vector4& rkVector) const { + return Vector4(x * rkVector.x, y * rkVector.y, z * rkVector.z, w * rkVector.w); + } + + inline Vector4 operator/(const Vector4& rkVector) const { + return Vector4(x / rkVector.x, y / rkVector.y, z / rkVector.z, w / rkVector.w); + } + + Vector4 operator*(const class Matrix4& M) const; + + Vector4 operator* (float fScalar) const; + Vector4 operator/ (float fScalar) const; + Vector4 operator- () const; + friend Vector4 operator* (float, const Vector4& rkVector); + + // arithmetic updates + Vector4& operator+= (const Vector4& rkVector); + Vector4& operator-= (const Vector4& rkVector); + Vector4& operator*= (float fScalar); + Vector4& operator/= (float fScalar); + + inline Vector4 clamp(const Vector4& low, const Vector4& high) const { + return Vector4( + G3D::clamp(x, low.x, high.x), + G3D::clamp(y, low.y, high.y), + G3D::clamp(z, low.z, high.z), + G3D::clamp(w, low.w, high.w)); + } + + inline Vector4 clamp(float low, float high) const { + return Vector4( + G3D::clamp(x, low, high), + G3D::clamp(y, low, high), + G3D::clamp(z, low, high), + G3D::clamp(w, low, high)); + } + + float dot (const Vector4& rkVector) const; + + Vector4 min(const Vector4& v) const; + Vector4 max(const Vector4& v) const; + + std::string toString() const; + + /** + Linear interpolation + */ + Vector4 lerp(const Vector4& v, float alpha) const; + + // 2-char swizzles + + Vector2 xx() const; + Vector2 yx() const; + Vector2 zx() const; + Vector2 wx() const; + Vector2 xy() const; + Vector2 yy() const; + Vector2 zy() const; + Vector2 wy() const; + Vector2 xz() const; + Vector2 yz() const; + Vector2 zz() const; + Vector2 wz() const; + Vector2 xw() const; + Vector2 yw() const; + Vector2 zw() const; + Vector2 ww() const; + + // 3-char swizzles + + Vector3 xxx() const; + Vector3 yxx() const; + Vector3 zxx() const; + Vector3 wxx() const; + Vector3 xyx() const; + Vector3 yyx() const; + Vector3 zyx() const; + Vector3 wyx() const; + Vector3 xzx() const; + Vector3 yzx() const; + Vector3 zzx() const; + Vector3 wzx() const; + Vector3 xwx() const; + Vector3 ywx() const; + Vector3 zwx() const; + Vector3 wwx() const; + Vector3 xxy() const; + Vector3 yxy() const; + Vector3 zxy() const; + Vector3 wxy() const; + Vector3 xyy() const; + Vector3 yyy() const; + Vector3 zyy() const; + Vector3 wyy() const; + Vector3 xzy() const; + Vector3 yzy() const; + Vector3 zzy() const; + Vector3 wzy() const; + Vector3 xwy() const; + Vector3 ywy() const; + Vector3 zwy() const; + Vector3 wwy() const; + Vector3 xxz() const; + Vector3 yxz() const; + Vector3 zxz() const; + Vector3 wxz() const; + Vector3 xyz() const; + Vector3 yyz() const; + Vector3 zyz() const; + Vector3 wyz() const; + Vector3 xzz() const; + Vector3 yzz() const; + Vector3 zzz() const; + Vector3 wzz() const; + Vector3 xwz() const; + Vector3 ywz() const; + Vector3 zwz() const; + Vector3 wwz() const; + Vector3 xxw() const; + Vector3 yxw() const; + Vector3 zxw() const; + Vector3 wxw() const; + Vector3 xyw() const; + Vector3 yyw() const; + Vector3 zyw() const; + Vector3 wyw() const; + Vector3 xzw() const; + Vector3 yzw() const; + Vector3 zzw() const; + Vector3 wzw() const; + Vector3 xww() const; + Vector3 yww() const; + Vector3 zww() const; + Vector3 www() const; + + // 4-char swizzles + + Vector4 xxxx() const; + Vector4 yxxx() const; + Vector4 zxxx() const; + Vector4 wxxx() const; + Vector4 xyxx() const; + Vector4 yyxx() const; + Vector4 zyxx() const; + Vector4 wyxx() const; + Vector4 xzxx() const; + Vector4 yzxx() const; + Vector4 zzxx() const; + Vector4 wzxx() const; + Vector4 xwxx() const; + Vector4 ywxx() const; + Vector4 zwxx() const; + Vector4 wwxx() const; + Vector4 xxyx() const; + Vector4 yxyx() const; + Vector4 zxyx() const; + Vector4 wxyx() const; + Vector4 xyyx() const; + Vector4 yyyx() const; + Vector4 zyyx() const; + Vector4 wyyx() const; + Vector4 xzyx() const; + Vector4 yzyx() const; + Vector4 zzyx() const; + Vector4 wzyx() const; + Vector4 xwyx() const; + Vector4 ywyx() const; + Vector4 zwyx() const; + Vector4 wwyx() const; + Vector4 xxzx() const; + Vector4 yxzx() const; + Vector4 zxzx() const; + Vector4 wxzx() const; + Vector4 xyzx() const; + Vector4 yyzx() const; + Vector4 zyzx() const; + Vector4 wyzx() const; + Vector4 xzzx() const; + Vector4 yzzx() const; + Vector4 zzzx() const; + Vector4 wzzx() const; + Vector4 xwzx() const; + Vector4 ywzx() const; + Vector4 zwzx() const; + Vector4 wwzx() const; + Vector4 xxwx() const; + Vector4 yxwx() const; + Vector4 zxwx() const; + Vector4 wxwx() const; + Vector4 xywx() const; + Vector4 yywx() const; + Vector4 zywx() const; + Vector4 wywx() const; + Vector4 xzwx() const; + Vector4 yzwx() const; + Vector4 zzwx() const; + Vector4 wzwx() const; + Vector4 xwwx() const; + Vector4 ywwx() const; + Vector4 zwwx() const; + Vector4 wwwx() const; + Vector4 xxxy() const; + Vector4 yxxy() const; + Vector4 zxxy() const; + Vector4 wxxy() const; + Vector4 xyxy() const; + Vector4 yyxy() const; + Vector4 zyxy() const; + Vector4 wyxy() const; + Vector4 xzxy() const; + Vector4 yzxy() const; + Vector4 zzxy() const; + Vector4 wzxy() const; + Vector4 xwxy() const; + Vector4 ywxy() const; + Vector4 zwxy() const; + Vector4 wwxy() const; + Vector4 xxyy() const; + Vector4 yxyy() const; + Vector4 zxyy() const; + Vector4 wxyy() const; + Vector4 xyyy() const; + Vector4 yyyy() const; + Vector4 zyyy() const; + Vector4 wyyy() const; + Vector4 xzyy() const; + Vector4 yzyy() const; + Vector4 zzyy() const; + Vector4 wzyy() const; + Vector4 xwyy() const; + Vector4 ywyy() const; + Vector4 zwyy() const; + Vector4 wwyy() const; + Vector4 xxzy() const; + Vector4 yxzy() const; + Vector4 zxzy() const; + Vector4 wxzy() const; + Vector4 xyzy() const; + Vector4 yyzy() const; + Vector4 zyzy() const; + Vector4 wyzy() const; + Vector4 xzzy() const; + Vector4 yzzy() const; + Vector4 zzzy() const; + Vector4 wzzy() const; + Vector4 xwzy() const; + Vector4 ywzy() const; + Vector4 zwzy() const; + Vector4 wwzy() const; + Vector4 xxwy() const; + Vector4 yxwy() const; + Vector4 zxwy() const; + Vector4 wxwy() const; + Vector4 xywy() const; + Vector4 yywy() const; + Vector4 zywy() const; + Vector4 wywy() const; + Vector4 xzwy() const; + Vector4 yzwy() const; + Vector4 zzwy() const; + Vector4 wzwy() const; + Vector4 xwwy() const; + Vector4 ywwy() const; + Vector4 zwwy() const; + Vector4 wwwy() const; + Vector4 xxxz() const; + Vector4 yxxz() const; + Vector4 zxxz() const; + Vector4 wxxz() const; + Vector4 xyxz() const; + Vector4 yyxz() const; + Vector4 zyxz() const; + Vector4 wyxz() const; + Vector4 xzxz() const; + Vector4 yzxz() const; + Vector4 zzxz() const; + Vector4 wzxz() const; + Vector4 xwxz() const; + Vector4 ywxz() const; + Vector4 zwxz() const; + Vector4 wwxz() const; + Vector4 xxyz() const; + Vector4 yxyz() const; + Vector4 zxyz() const; + Vector4 wxyz() const; + Vector4 xyyz() const; + Vector4 yyyz() const; + Vector4 zyyz() const; + Vector4 wyyz() const; + Vector4 xzyz() const; + Vector4 yzyz() const; + Vector4 zzyz() const; + Vector4 wzyz() const; + Vector4 xwyz() const; + Vector4 ywyz() const; + Vector4 zwyz() const; + Vector4 wwyz() const; + Vector4 xxzz() const; + Vector4 yxzz() const; + Vector4 zxzz() const; + Vector4 wxzz() const; + Vector4 xyzz() const; + Vector4 yyzz() const; + Vector4 zyzz() const; + Vector4 wyzz() const; + Vector4 xzzz() const; + Vector4 yzzz() const; + Vector4 zzzz() const; + Vector4 wzzz() const; + Vector4 xwzz() const; + Vector4 ywzz() const; + Vector4 zwzz() const; + Vector4 wwzz() const; + Vector4 xxwz() const; + Vector4 yxwz() const; + Vector4 zxwz() const; + Vector4 wxwz() const; + Vector4 xywz() const; + Vector4 yywz() const; + Vector4 zywz() const; + Vector4 wywz() const; + Vector4 xzwz() const; + Vector4 yzwz() const; + Vector4 zzwz() const; + Vector4 wzwz() const; + Vector4 xwwz() const; + Vector4 ywwz() const; + Vector4 zwwz() const; + Vector4 wwwz() const; + Vector4 xxxw() const; + Vector4 yxxw() const; + Vector4 zxxw() const; + Vector4 wxxw() const; + Vector4 xyxw() const; + Vector4 yyxw() const; + Vector4 zyxw() const; + Vector4 wyxw() const; + Vector4 xzxw() const; + Vector4 yzxw() const; + Vector4 zzxw() const; + Vector4 wzxw() const; + Vector4 xwxw() const; + Vector4 ywxw() const; + Vector4 zwxw() const; + Vector4 wwxw() const; + Vector4 xxyw() const; + Vector4 yxyw() const; + Vector4 zxyw() const; + Vector4 wxyw() const; + Vector4 xyyw() const; + Vector4 yyyw() const; + Vector4 zyyw() const; + Vector4 wyyw() const; + Vector4 xzyw() const; + Vector4 yzyw() const; + Vector4 zzyw() const; + Vector4 wzyw() const; + Vector4 xwyw() const; + Vector4 ywyw() const; + Vector4 zwyw() const; + Vector4 wwyw() const; + Vector4 xxzw() const; + Vector4 yxzw() const; + Vector4 zxzw() const; + Vector4 wxzw() const; + Vector4 xyzw() const; + Vector4 yyzw() const; + Vector4 zyzw() const; + Vector4 wyzw() const; + Vector4 xzzw() const; + Vector4 yzzw() const; + Vector4 zzzw() const; + Vector4 wzzw() const; + Vector4 xwzw() const; + Vector4 ywzw() const; + Vector4 zwzw() const; + Vector4 wwzw() const; + Vector4 xxww() const; + Vector4 yxww() const; + Vector4 zxww() const; + Vector4 wxww() const; + Vector4 xyww() const; + Vector4 yyww() const; + Vector4 zyww() const; + Vector4 wyww() const; + Vector4 xzww() const; + Vector4 yzww() const; + Vector4 zzww() const; + Vector4 wzww() const; + Vector4 xwww() const; + Vector4 ywww() const; + Vector4 zwww() const; + Vector4 wwww() const; + +}; + + +//---------------------------------------------------------------------------- +inline Vector4::Vector4() { + x = y = z = w = 0; +} + +//---------------------------------------------------------------------------- + +inline Vector4::Vector4 (float fX, float fY, float fZ, float fW) { + x = fX; + y = fY; + z = fZ; + w = fW; +} + +//---------------------------------------------------------------------------- +inline Vector4::Vector4 (float afCoordinate[4]) { + x = afCoordinate[0]; + y = afCoordinate[1]; + z = afCoordinate[2]; + w = afCoordinate[3]; +} + +//---------------------------------------------------------------------------- +inline Vector4::Vector4(const Vector4& rkVector) { + x = rkVector.x; + y = rkVector.y; + z = rkVector.z; + w = rkVector.w; +} +//---------------------------------------------------------------------------- +inline Vector4::Vector4(const Vector3& rkVector, float fW) { + x = rkVector.x; + y = rkVector.y; + z = rkVector.z; + w = fW; +} + +//---------------------------------------------------------------------------- +inline float& Vector4::operator[] (int i) { + return ((float*)this)[i]; +} + +//---------------------------------------------------------------------------- +inline const float& Vector4::operator[] (int i) const { + return ((float*)this)[i]; +} + +//---------------------------------------------------------------------------- +inline Vector4::operator float* () { + return (float*)this; +} + +inline Vector4::operator const float* () const { + return (float*)this; +} + +//---------------------------------------------------------------------------- +inline Vector4& Vector4::operator= (const Vector4& rkVector) { + x = rkVector.x; + y = rkVector.y; + z = rkVector.z; + w = rkVector.w; + return *this; +} + +//---------------------------------------------------------------------------- +inline bool Vector4::operator== (const Vector4& rkVector) const { + return ( (x == rkVector.x) && (y == rkVector.y) && (z == rkVector.z) && (w == rkVector.w)); +} + +//---------------------------------------------------------------------------- +inline bool Vector4::operator!= (const Vector4& rkVector) const { + return ( x != rkVector.x || y != rkVector.y || z != rkVector.z || w != rkVector.w); +} + +//---------------------------------------------------------------------------- +inline Vector4 Vector4::operator+ (const Vector4& rkVector) const { + return Vector4(x + rkVector.x, y + rkVector.y, z + rkVector.z, w + rkVector.w); +} + +//---------------------------------------------------------------------------- +inline Vector4 Vector4::operator- (const Vector4& rkVector) const { + return Vector4(x - rkVector.x, y - rkVector.y, z - rkVector.z, w - rkVector.w); +} + +//---------------------------------------------------------------------------- +inline Vector4 Vector4::operator* (float fScalar) const { + return Vector4(fScalar*x, fScalar*y, fScalar*z, fScalar*w); +} + +//---------------------------------------------------------------------------- +inline Vector4 Vector4::operator- () const { + return Vector4( -x, -y, -z, -w); +} + +//---------------------------------------------------------------------------- +inline Vector4& Vector4::operator+= (const Vector4& rkVector) { + x += rkVector.x; + y += rkVector.y; + z += rkVector.z; + w += rkVector.w; + return *this; +} + +//---------------------------------------------------------------------------- +inline Vector4& Vector4::operator-= (const Vector4& rkVector) { + x -= rkVector.x; + y -= rkVector.y; + z -= rkVector.z; + w -= rkVector.w; + return *this; +} + +//---------------------------------------------------------------------------- + +inline Vector4 Vector4::lerp(const Vector4& v, float alpha) const { + return (*this) + (v - *this) * alpha; +} + + +//---------------------------------------------------------------------------- +inline Vector4& Vector4::operator*= (float fScalar) { + x *= fScalar; + y *= fScalar; + z *= fScalar; + w *= fScalar; + return *this; +} + + +//---------------------------------------------------------------------------- +inline float Vector4::dot(const Vector4& rkVector) const { + return x*rkVector.x + y*rkVector.y + z*rkVector.z + w*rkVector.w; +} + +//---------------------------------------------------------------------------- +inline Vector4 Vector4::min(const Vector4 &v) const { + return Vector4(G3D::min(v.x, x), G3D::min(v.y, y), G3D::min(v.z, z), G3D::min(v.w, w)); +} + +//---------------------------------------------------------------------------- +inline Vector4 Vector4::max(const Vector4 &v) const { + return Vector4(G3D::max(v.x, x), G3D::max(v.y, y), G3D::max(v.z, z), G3D::max(v.w, w)); +} + +//---------------------------------------------------------------------------- +inline bool Vector4::isZero() const { + return (x == 0.0f) && (y == 0.0f) && (z == 0.0f) && (w == 0.0f); +} + +//---------------------------------------------------------------------------- + +inline bool Vector4::isFinite() const { + return G3D::isFinite(x) && G3D::isFinite(y) && G3D::isFinite(z) && G3D::isFinite(w); +} + +//---------------------------------------------------------------------------- + +inline bool Vector4::isUnit() const { + return squaredLength() == 1.0; +} + +//---------------------------------------------------------------------------- + +inline float Vector4::length() const { + return sqrtf(squaredLength()); +} + +//---------------------------------------------------------------------------- + +inline float Vector4::squaredLength() const { + return x * x + y * y + z * z + w * w; +} + +} + +template <> struct HashTrait<G3D::Vector4> { + static size_t hashCode(const G3D::Vector4& key) { return key.hashCode(); } +}; + + +template<> struct PositionTrait<class G3D::Vector4> { + static void getPosition(const G3D::Vector4& v, G3D::Vector3& p) { p = v.xyz(); } +}; + +inline G3D::Vector4 operator* (float s, const G3D::Vector4& v) { + return v * s; +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h b/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h new file mode 100644 index 00000000000..8476a2d008b --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h @@ -0,0 +1,113 @@ +/** + @file Vector4int8.h + + Homogeneous vector class. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-02-09 + @edited 2007-02-09 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_VECTOR4INT8_H +#define G3D_VECTOR4INT8_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" + +namespace G3D { + +class Vector3; +class Vector4; + +/** + Homogeneous vector stored efficiently in four signed int8s. + + */ +class Vector4int8 { +private: + // Hidden operators + bool operator<(const Vector4int8&) const; + bool operator>(const Vector4int8&) const; + bool operator<=(const Vector4int8&) const; + bool operator>=(const Vector4int8&) const; + + + /** For fast operations, treat this packed data structure as + an int32 */ + inline uint32& asInt32() { + return *reinterpret_cast<uint32*>(this); + } + + inline const uint32& asInt32() const { + return *reinterpret_cast<const uint32*>(this); + } + +public: + // construction + inline Vector4int8() : x(0), y(0), z(0), w(0) {} + + /** Multiplies the source by 127 and clamps to (-128, 127) when converting */ + Vector4int8(const Vector4& source); + + /** Multiplies the source by 127 and clamps to (-128, 127) when converting */ + Vector4int8(const Vector3& source, int8 w); + + inline Vector4int8(int8 x, int8 y, int8 z, int8 w) : x(x), y(y), z(z), w(w) {} + + Vector4int8(class BinaryInput& b); + void serialize(class BinaryOutput& b) const; + void deserialize(class BinaryInput& b); + + // coordinates + int8 x, y, z, w; + + inline operator int8* () { + return reinterpret_cast<int8*>(this); + } + + inline operator const int8* () const { + return reinterpret_cast<const int8*>(this); + } + + // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z, etc. + // + // WARNING. These member functions rely on + // (1) Vector4int8 not having virtual functions + // (2) the data packed in a 4*sizeof(int8) memory block + inline int8& operator[] (int i) { + debugAssert(i >= 0 && i <= 4); + return ((int8*)this)[i]; + } + + const int8& operator[] (int i) const { + debugAssert(i >= 0 && i <= 4); + return ((const int8*)this)[i]; + } + + // assignment and comparison + Vector4int8& operator= (const Vector4int8& other) { + asInt32() = other.asInt32(); + return *this; + } + + inline bool operator== (const Vector4int8& other) const { + return asInt32() == other.asInt32(); + } + + inline bool operator!= (const Vector4int8& other) const { + return ! (*this == other); + } + + inline unsigned int hashCode() const { + return asInt32(); + } +}; + +} // namespace G3D + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h b/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h new file mode 100644 index 00000000000..87988aaf5cb --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h @@ -0,0 +1,90 @@ +/** + @file WeakCache.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2007-05-16 + @edited 2007-05-16 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ +#ifndef G3D_WEAKCACHE_H +#define G3D_WEAKCACHE_H + +#include "G3D/ReferenceCount.h" +#include "G3D/Table.h" + +namespace G3D { + +/** + A cache that does not prevent its members from being garbage collected. + Useful to avoid loading or computing an expression twice. Useful + for memoization and dynamic programming. + + Maintains a table of weak pointers. Weak pointers do not prevent + an object from being garbage collected. If the object is garbage + collected, the cache removes its reference. + + There are no "contains" or "iterate" methods because elements can be + flushed from the cache at any time if they are garbage collected. + + Example: + <pre> + WeakCache<std::string, TextureRef> textureCache; + + TextureRef loadTexture(std::string s) { + TextureRef t = textureCache[s]; + + if (t.isNull()) { + t = Texture::fromFile(s); + textureCache.set(s, t); + } + + return t; + } + + + </pre> + */ +template<class Key, class ValueRef> +class WeakCache { + typedef WeakReferenceCountedPointer<typename ValueRef::element_type> ValueWeakRef; + +private: + + Table<Key, ValueWeakRef> table; + +public: + /** + Returns NULL if the object is not in the cache + */ + ValueRef operator[](const Key& k) { + if (table.containsKey(k)) { + ValueWeakRef w = table[k]; + ValueRef s = w.createStrongPtr(); + if (s.isNull()) { + // This object has been collected; clean out its key + table.remove(k); + } + return s; + } else { + return NULL; + } + } + + void set(const Key& k, ValueRef v) { + table.set(k, v); + } + + /** Removes k from the cache or does nothing if it is not currently in the cache.*/ + void remove(const Key& k) { + if (table.containsKey(k)) { + table.remove(k); + } + } +}; + +} +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h b/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h new file mode 100644 index 00000000000..f97e68d6910 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h @@ -0,0 +1,79 @@ +/** + @file WrapMode.h + + @maintainer Morgan McGuire, graphics3d.com + + @created 2007-04-17 + @edited 2007-04-17 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_WRAPMODE_H +#define G3D_WRAPMODE_H + +#include "G3D/platform.h" +#include "G3D/enumclass.h" + +#ifdef IGNORE +# undef IGNORE +#endif +#ifdef ZERO +# undef ZERO +#endif +#ifdef ERROR +# undef ERROR +#endif + +namespace G3D { + +/** + Describes the behavior of G3D::Texture, G3D::Map2D, G3D::Image3, etc. when accessing an out-of-bounds + pixel. Not all classes support all modes. + + Refer to these as scoped enums, e.g., <code>WrapMode m = WrapMode::CLAMP;</code>. + + WrapMode::IGNORE silently discards attempts to write to out + of bounds locations and returns an undefined value for reading + from out of bounds locations. + + WrapMode::ERROR generates an error when the + pixel indices are out of bounds + + WrapMode::CLAMP makes out of bounds pixels equal to the last in-range pixel along that dimension. + + WrapMode::TILE computes out of bounds pixels modulo the dimension + + WrapMode::ZERO treats out of bounds values as the zero value, which varies in definition + according to the class used. For example, with a G3D::Texture, ZERO = Color4(0,0,0,0). + + Uses the "Intelligent Enum" design pattern + http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4001/ + */ +class WrapMode { +public: + /** Don't use this enum; use WrapMode instances instead. */ + enum Value { + CLAMP, + TILE, + ZERO, + IGNORE, + ERROR + }; + +private: + + Value value; + +public: + + G3D_DECLARE_ENUM_CLASS_METHODS(WrapMode); + +}; + +} // namespace G3D + +G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::WrapMode); + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/debug.h b/externals/g3dlite/G3D.lib/include/G3D/debug.h new file mode 100644 index 00000000000..a96babc6fa0 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/debug.h @@ -0,0 +1,66 @@ +/** + @file debug.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-08-26 + @edited 2006-02-16 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. +*/ + +#ifndef G3D_DEBUG_H +#define G3D_DEBUG_H + +#include "G3D/platform.h" +#ifdef _MSC_VER + #include <crtdbg.h> +#endif + +#include "G3D/debugPrintf.h" +#include "G3D/debugAssert.h" + +namespace G3D { + +#ifdef _MSC_VER + // Turn off 64-bit warnings +# pragma warning(push) +# pragma warning( disable : 4312) +# pragma warning( disable : 4267) +# pragma warning( disable : 4311) +#endif + + +/** + Useful for debugging purposes. + */ +inline bool isValidHeapPointer(const void* x) { + #ifdef _MSC_VER + return + (x != (void*)0xcccccccc) && (x != (void*)0xdeadbeef) && (x != (void*)0xfeeefeee); + #else + return x != NULL; + #endif +} + +/** + Returns true if the pointer is likely to be + a valid pointer (instead of an arbitrary number). + Useful for debugging purposes. + */ +inline bool isValidPointer(const void* x) { + #ifdef _MSC_VER + return x != ((void*)0xcccccccc) && (x != (void*)0xdeadbeef) && (x != (void*)0xfeeefeee); + #else + return x != NULL; + #endif +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h b/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h new file mode 100644 index 00000000000..f9b5bea4e9f --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h @@ -0,0 +1,236 @@ +/** + @file debugAssert.h + + debugAssert(expression); + debugAssertM(expression, message); + + @cite + John Robbins, Microsoft Systems Journal Bugslayer Column, Feb 1999. + <A HREF="http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm"> + http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm</A> + + @cite + Douglas Cox, An assert() Replacement, Code of The Day, flipcode, Sept 19, 2000 + <A HREF="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1"> + http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1</A> + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-08-26 + @edited 2006-01-12 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_DEBUGASSERT_H +#define G3D_DEBUGASSERT_H + +#include <string> +#include "G3D/platform.h" + +#include <cstdlib> + +#ifdef _MSC_VER +// conditional expression is constant +# pragma warning (disable : 4127) +#endif + +#ifdef G3D_LINUX + // Needed so we can define a global display + // pointer for debugAssert. + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <X11/Xatom.h> +#endif + +#ifdef G3D_OSX + // Need this for DebugStr() + #import <CoreServices/CoreServices.h> +#endif + + +/** + @def debugBreak() + + Break at the current location (i.e. don't push a procedure stack frame + before breaking). + */ + +/** + @def debugAssert(exp) + Breaks if the expression is false. If G3D_DEBUG_NOGUI is defined, prompts at + the console, otherwise pops up a dialog. The user may then break (debug), + ignore, ignore always, or halt the program. + + The assertion is also posted to the clipboard under Win32. + */ + +/** + @def debugAssertM(exp, msg) + Breaks if the expression is false and displays a message. If G3D_DEBUG_NOGUI + is defined, prompts at the console, otherwise pops up a dialog. The user may + then break (debug), ignore, ignore always, or halt the program. + + The assertion is also posted to the clipboard under Win32. + */ + +namespace G3D { +typedef bool (*AssertionHook)( + const char* _expression, + const std::string& message, + const char* filename, + int lineNumber, + bool& ignoreAlways, + bool useGuiPrompt); + +/** + Allows customization of the global function invoked when a debugAssert fails. + The initial value is G3D::_internal::_handleDebugAssert_. G3D will invoke + rawBreak if the hook returns true. If NULL, assertions are not handled. +*/ +void setAssertionHook(AssertionHook hook); + +AssertionHook assertionHook(); + +/** + Called by alwaysAssertM in case of failure in release mode. If returns + true then the program exits with -1 (you can replace this with your own + version that throws an exception or has other failure modes). + */ +void setFailureHook(AssertionHook hook); +AssertionHook failureHook(); + +namespace _internal { + extern AssertionHook _debugHook; + extern AssertionHook _failureHook; +} // internal +} // G3D + +/** + @def __debugPromptShowDialog__ + @internal + */ + +#ifdef G3D_DEBUG + +# if defined(_MSC_VER) +# define rawBreak() ::DebugBreak(); +# elif defined(__i386__) + // gcc on intel +# define rawBreak() __asm__ __volatile__ ( "int $3" ); +# else + // some other gcc +# define rawBreak() ::abort() +# endif +// old mac code: +//# define rawBreak() DebugStr((const unsigned char*)("\nG3D: Invoking breakpoint in debugger.")); /* XCode must be set to break on Debugger()/DebugStr() */ + + +# define debugBreak() G3D::_internal::_releaseInputGrab_(); rawBreak(); G3D::_internal::_restoreInputGrab_(); +# define debugAssert(exp) debugAssertM(exp, "Debug assertion failure") + + #ifdef G3D_DEBUG_NOGUI + #define __debugPromptShowDialog__ false + #else + #define __debugPromptShowDialog__ true + #endif + + #define debugAssertM(exp, message) do { \ + static bool __debugAssertIgnoreAlways__ = false; \ + if (!__debugAssertIgnoreAlways__ && !(exp)) { \ + G3D::_internal::_releaseInputGrab_(); \ + if ((G3D::_internal::_debugHook != NULL) && \ + G3D::_internal::_debugHook((const char*)(#exp), message, __FILE__, __LINE__, __debugAssertIgnoreAlways__, __debugPromptShowDialog__)) { \ + rawBreak(); \ + } \ + G3D::_internal::_restoreInputGrab_(); \ + } \ + } while (0) + + #define alwaysAssertM debugAssertM + +#else // Release + #ifdef G3D_DEBUG_NOGUI + #define __debugPromptShowDialog__ false + #else + #define __debugPromptShowDialog__ true + #endif + + // In the release build, just define away assertions. + #define rawBreak() do {} while (0) + #define debugAssert(exp) do {} while (0) + #define debugAssertM(exp, message) do {} while (0) + #define debugBreak() do {} while (0) + + // But keep the 'always' assertions + #define alwaysAssertM(exp, message) { \ + static bool __alwaysAssertIgnoreAlways__ = false; \ + if (!__alwaysAssertIgnoreAlways__ && !(exp)) { \ + G3D::_internal::_releaseInputGrab_(); \ + if ((G3D::_internal::_failureHook != NULL) && \ + G3D::_internal::_failureHook(#exp, message, __FILE__, __LINE__, __alwaysAssertIgnoreAlways__, __debugPromptShowDialog__)) { \ + ::exit(-1); \ + } \ + G3D::_internal::_restoreInputGrab_(); \ + } \ + } + +#endif // if debug + + + +namespace G3D { namespace _internal { + +#ifdef G3D_LINUX + /** + A pointer to the X11 display. Initially NULL. If set to a + non-null value (e.g. by SDLWindow), debugAssert attempts to use + this display to release the mouse/input grab when an assertion + fails. + */ + extern Display* x11Display; + + /** + A pointer to the X11 window. Initially NULL. If set to a + non-null value (e.g. by SDLWindow), debugAssert attempts to use + this window to release the mouse/input grab when an assertion + fails. + */ + extern Window x11Window; +#endif + +/** + Pops up an assertion dialog or prints an assertion + + ignoreAlways - return result of pressing the ignore button. + useGuiPrompt - if true, shows a dialog + */ +bool _handleDebugAssert_( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + bool& ignoreAlways, + bool useGuiPrompt); + +bool _handleErrorCheck_( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + bool& ignoreAlways, + bool useGuiPrompt); + +/** Attempts to give the user back their mouse and keyboard if they + were locked to the current window. + @internal*/ +void _releaseInputGrab_(); + +/** Attempts to restore the state before _releaseInputGrab_. + @internal*/ +void _restoreInputGrab_(); + +}; }; // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h b/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h new file mode 100644 index 00000000000..bc3315c8a6d --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h @@ -0,0 +1,62 @@ +/** + @file debugPrintf.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-08-26 + @edited 2007-07-20 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_DEBUGPRINTF_H +#define G3D_DEBUGPRINTF_H + +#include "G3D/platform.h" +#include <stdio.h> +#include <cstdarg> +#include "G3D/format.h" +#include <string> + +namespace G3D { + +typedef void (*ConsolePrintHook)(const std::string&); + +namespace _internal { + extern ConsolePrintHook _consolePrintHook; +} + +/** Called by consolePrintf after the log and terminal have been written to. + Used by GConsole to intercept printing routines.*/ +void setConsolePrintHook(ConsolePrintHook h); + +ConsolePrintHook consolePrintHook(); + +/** + Sends output to the log and to the last GConsole instantiated. + + Guarantees that the output has been flushed by the time the routine + returns. + @sa G3D::logPrintf, G3D::screenPrintf + @return The string that was printed + */ +std::string __cdecl consolePrintf(const char* fmt ...) G3D_CHECK_PRINTF_ARGS; +std::string consolePrint(const std::string&); + +/** + Under visual studio, appears in the VS debug pane. + On unix-based operating systems the output is sent to stderr. + + Also sends output to the console (G3D::consolePrintf) if there is a consolePrintHook, + and log (G3D::logPrintf), and flushes before returning. + + @return The string that was printed +*/ +std::string __cdecl debugPrintf(const char* fmt ...) G3D_CHECK_PRINTF_ARGS; +std::string debugPrint(const std::string&); + +} // namespace G3D + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/enumclass.h b/externals/g3dlite/G3D.lib/include/G3D/enumclass.h new file mode 100644 index 00000000000..7e7bc3f6ac3 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/enumclass.h @@ -0,0 +1,141 @@ +/** + @file G3D/enumclass.h + + @maintainer Morgan McGuire, morgan@graphics3d.com + @created 2007-01-27 + @edited 2007-07-20 +*/ +#ifndef G3D_ENUMCLASS_H +#define G3D_ENUMCLASS_H + +#include "G3D/Table.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +/** + Creates a series of methods that turn a class into a scoped enumeration. + Uses the "Intelligent Enum" design pattern + http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4001/ + + Enum classes are initialized to their zero value by default. + + See GLG3D/GKey.h for an example. + */ +#define G3D_DECLARE_ENUM_CLASS_METHODS(Classname)\ + inline Classname(char v) : value((Value)v) {}\ +\ + inline Classname() : value((Value)0) {}\ +\ + inline Classname(const Value v) : value(v) {}\ +\ + explicit inline Classname(int v) : value((Value)v) {}\ +\ + /** Support cast back to the Value type, which is needed to allow implicit assignment inside unions. */\ + /*inline operator Value() const { + return value; + }*/\ +\ + inline operator int() const {\ + return (int)value;\ + }\ +\ + inline bool operator== (const Classname other) const {\ + return value == other.value;\ + }\ +\ + inline bool operator== (const Classname::Value other) const {\ + return value == other;\ + }\ +\ + inline bool operator!= (const Classname other) const {\ + return value != other.value;\ + }\ +\ + inline bool operator!= (const Classname::Value other) const {\ + return value != other;\ + }\ +\ + inline bool operator< (const Classname other) const {\ + return value < other.value;\ + }\ +\ + inline bool operator> (const Classname other) const {\ + return value > other.value;\ + }\ +\ + inline bool operator>= (const Classname other) const {\ + return value >= other.value;\ + }\ +\ + inline bool operator<= (const Classname other) const {\ + return value <= other.value;\ + }\ +\ + inline bool operator< (const Value other) const {\ + return value < other;\ + }\ +\ + inline bool operator> (const Value other) const {\ + return value > other;\ + }\ +\ + inline bool operator<= (const Value other) const {\ + return value <= other;\ + }\ +\ + inline bool operator>= (const Value other) const {\ + return value >= other;\ + }\ +\ + inline Classname& operator-- () {\ + value = (Value)((int)value - 1);\ + return *this;\ + }\ +\ + inline Classname& operator++ () {\ + value = (Value)((int)value + 1);\ + return *this;\ + }\ +\ + inline Classname& operator+= (const int x) {\ + value = (Value)((int)value + x);\ + return *this;\ + }\ +\ + inline Classname& operator-= (const int x) {\ + value = (Value)((int)value - x);\ + return *this;\ + }\ +\ + inline Classname operator+ (const int x) const {\ + return Classname((int)value + x);\ + }\ +\ + inline Classname operator- (const int x) const {\ + return Classname((int)value - x);\ + }\ +\ + inline unsigned int hashCode() const {\ + return (unsigned int)value;\ + }\ +\ + void serialize(BinaryOutput& b) const {\ + b.writeInt32(value);\ + }\ +\ + void deserialize(BinaryInput& b) {\ + value = (Value)b.readInt32();\ + } + +#define G3D_DECLARE_ENUM_CLASS_HASHCODE(Classname)\ +template <> struct HashTrait<Classname::Value> \ +{ \ + static size_t hashCode(Classname::Value key) { return static_cast<size_t>(key); } \ +}; \ + \ +template <> struct HashTrait<Classname> \ +{ \ + static size_t hashCode(Classname key) { return static_cast<size_t>(key.hashCode()); } \ +}; + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/fileutils.h b/externals/g3dlite/G3D.lib/include/G3D/fileutils.h new file mode 100644 index 00000000000..ead20720870 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/fileutils.h @@ -0,0 +1,246 @@ +/** + @file fileutils.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @author 2002-06-06 + @edited 2007-01-18 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_FILEUTILS_H +#define G3D_FILEUTILS_H + +#include "G3D/platform.h" +#include <string> +#include <stdio.h> +#include "G3D/Array.h" +#include "G3D/Set.h" +#include "G3D/g3dmath.h" + +#ifdef G3D_WIN32 +// For chdir, mkdir, etc. +# include <direct.h> +#endif + +namespace G3D { + + namespace _internal { + extern Set<std::string> currentFilesUsed; + } + +/** Returns all the files used by G3D and GLG3D during the current execution. */ +Array<std::string> filesUsed(); + +std::string readWholeFile( + const std::string& filename); + + +/** Reads from a zip file and decompresses the desired contents + into memory. Does not support recursive zip calls (i.e. a .zip + stored within another .zip) + + @param file the path, of the format C:\...\something.zip\...\desiredfile.ext + @param data a pointer to the memory where the file will be stored + @param length the size of the file decompressed to memory */ +void zipRead(const std::string& file, + void*& data, + size_t& length); + + +/** Closes the contents of a zip file that had been decompressed to + memory. Must be called in tandem with zipRead() to avoid memory + leaks. + + @param data the pointer to the decompressed file in memory */ +void zipClose(void* data); + + +/** + @param flush If true (default), the file is ready for reading as soon + as the function returns. If false, the function returns immediately and + writes the file in the background. + */ +void writeWholeFile( + const std::string& filename, + const std::string& str, + bool flush = true); + +/** + Creates the directory (which may optionally end in a /) + and any parents needed to reach it. + */ +void createDirectory( + const std::string& dir); + +/** + Fully qualifies a filename. The filename may contain wildcards, + in which case the wildcards will be preserved in the returned value. + */ +std::string resolveFilename(const std::string& filename); + +/** + Appends all files matching filespec to the files array. The names + will not contain paths unless includePath == true. These may be + relative to the current directory unless the filespec is fully qualified + (can be done with resolveFilename). + Wildcards can only appear to the right of the last slash in filespec. + Works with .zip files used as paths, if filespec is passed in the form + C:\...\something.zip\* Does not work recursively with zipfiles (a + .zip within a .zip will not work) + */ +void getFiles( + const std::string& filespec, + Array<std::string>& files, + bool includePath = false); + +/** + Appends all directories matching filespec to the files array. The names + will not contain paths unless includePath == true. These may be + relative to the current directory unless the filespec is fully qualified + (can be done with resolveFilename). + Does not append special directories "." or "..". + Works with .zip files used as paths, if filespec is passed in the form + C:\...\something.zip\* Does not work recursively with zipfiles (a + .zip within a .zip will not work) + */ +void getDirs( + const std::string& filespec, + Array<std::string>& files, + bool includePath = false); + + +/** Returns true if the specified path exists and is a directory */ +bool isDirectory(const std::string& filespec); + + +/** Returns true if the specified filename exists and is a zipfile */ +bool isZipfile(const std::string& filename); + + +/** Returns the length of the file. If + filename specifies a path that contains a zipfile, but the + contents within are specified correctly, returns the + uncompressed size of the requested file. Returns -1 if + the file does not exist. + + @param filename the path to test, may contain .zip +*/ +int64 fileLength(const std::string& filename); + +/** + Copies the file + */ +void copyFile( + const std::string& source, + const std::string& dest); + +/** Returns a temporary file that is open for read/write access. This + tries harder than the ANSI tmpfile, so it may succeed when that fails. */ +FILE* createTempFile(); + +/** + Returns true if the given file (or directory) exists. + + @param filename the path to test. must not end in a trailing slash. + @param lookInZipfiles if the path does not exist, calls zipfileExists() + */ +bool fileExists( + const std::string& filename, + const bool lookInZipfiles = true); + +/** + Returns true if the given file (or directory) exists + within a zipfile. Called if fileExists initially + returns false and the lookInZipfiles flag has been set. + Must not end in a trailing slash. Does not work for recursive + zipfiles (.zips within another .zip) + + @param filename the path to test + @param outZipfile the path to the .zip file + @param outInternalFile the path (within the .zip) where the desired file is located, if valid + + */ +bool zipfileExists( + const std::string& filename, + std::string& outZipfile, + std::string& outInternalFile + ); + +bool zipfileExists(const std::string& filename); + +/** + Parses a filename into four useful pieces. + + Examples: + + c:\a\b\d.e + root = "c:\" + path = "a" "b" + base = "d" + ext = "e" + + /a/b/d.e + root = "/" + path = "a" "b" + base = "d" + ext = "e" + + /a/b + root = "/" + path = "a" + base = "b" + ext = "e" + + */ +void parseFilename( + const std::string& filename, + std::string& drive, + Array<std::string>& path, + std::string& base, + std::string& ext); + + +/** + Returns the part of the filename that includes the base and ext from + parseFilename (i.e. everything to the right of the path). + */ +std::string filenameBaseExt(const std::string& filename); + +/** + Returns the extension on a filename. + */ +std::string filenameExt(const std::string& filename); + + +/** Returns the portion of a filename to the left of the last period + and to the right of the last slash or colon. + */ +std::string filenameBase(const std::string& filename); + +/** Creates a unique filename base in the current directory using the + specified prefix.*/ +std::string generateFilenameBase(const std::string& prefix = ""); + +/** + Returns the drive (if Win32) and path from a filename, including + a slash if there was one. + <CODE>filenamePath(f) + filenameBaseExt(f) == f</CODE> + */ +std::string filenamePath(const std::string& filename); + +/** Returns true if '*' or '?' appears in the string */ +bool filenameContainsWildcards(const std::string& filename); + +/** Returns true if dst does not exist or src is newer than dst. Works on both files and directories. */ +bool fileIsNewer(const std::string& src, const std::string& dst); + +/** Appends file onto dirname, ensuring a / if needed. */ +std::string pathConcat(const std::string& dirname, const std::string& file); + +} // namespace + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/filter.h b/externals/g3dlite/G3D.lib/include/G3D/filter.h new file mode 100644 index 00000000000..74a32ad01ea --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/filter.h @@ -0,0 +1,29 @@ +/** + @file G3D/filter.h + + @author Morgan McGuire, matrix@graphics3d.com + @created 2007-03-01 + @edited 2007-03-01 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ +#ifndef G3D_FILTER_H +#define G3D_FILTER_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/g3dmath.h" + +namespace G3D { +/** + Generates a set of 1D gaussian filter coefficients of size N. The coefficients + are centered on element (N-1)/2 and have standard deviation given by std. The coefficients + are normalized such that the sum across coeff is 1.0. + + Matches the results returned by Matlab <code>fspecial('gaussian', [1, N], std)</code> + */ +void gaussian1D(Array<float>& coeff, int N = 5, float std = 0.5f); +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/format.h b/externals/g3dlite/G3D.lib/include/G3D/format.h new file mode 100644 index 00000000000..f993a74038f --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/format.h @@ -0,0 +1,44 @@ +/** + @file format.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @author 2000-09-09 + @edited 2005-11-03 + + Copyright 2000-2005, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_FORMAT_H +#define G3D_FORMAT_H + +#include "G3D/platform.h" +#include <string> +#include <stdio.h> +#include <cstdarg> + +namespace G3D { + +/** + Produces a string from arguments of the style of printf. This avoids + problems with buffer overflows when using sprintf and makes it easy + to use the result functionally. This function is fast when the resulting + string is under 160 characters (not including terminator) and slower + when the string is longer. + */ +std::string __cdecl format( + const char* fmt + ...) G3D_CHECK_PRINTF_ARGS; + +/** + Like format, but can be called with the argument list from a ... function. + */ +std::string vformat( + const char* fmt, + va_list argPtr) G3D_CHECK_VPRINTF_ARGS; + + +} // namespace + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h b/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h new file mode 100644 index 00000000000..0a4f01f11c6 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h @@ -0,0 +1,810 @@ +/** + @file g3dmath.h + + Math util class. + + @maintainer Morgan McGuire, matrix@graphics3d.com + @cite highestBit by Jukka Liimatta + + @created 2001-06-02 + @edited 2006-01-16 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3DMATH_H +#define G3DMATH_H + +#ifdef _MSC_VER +// Disable conditional expression is constant, which occurs incorrectly on inlined functions +# pragma warning (push) +# pragma warning (disable : 4127) +// disable: "C++ exception handler used" +# pragma warning (disable : 4530) +#endif + +#include "G3D/platform.h" +#include <ctype.h> +#include <float.h> +#include <limits> +#include <stdlib.h> + +#ifdef _MSC_VER + // Visual Studio is missing inttypes.h +# ifndef PRId64 +# define PRId64 "I64d" +# endif +#else +#include <inttypes.h> +#endif + +/*These defines enable functionality introduced with the 1999 ISO C +**standard. They must be defined before the inclusion of math.h to +**engage them. If optimisation is enabled, these functions will be +**inlined. With optimisation switched off, you have to link in the +**maths library using -lm. +*/ + +#define _ISOC9X_SOURCE1 +#define _ISOC99_SOURCE1 +#define __USE_ISOC9X1 +#define __USE_ISOC991 + +#include <math.h> + +#include "G3D/debug.h" + +#undef min +#undef max + +namespace G3D { + +#ifdef _MSC_VER + +/** + Win32 implementation of the C99 fast rounding routines. + + @cite routines are + Copyright (C) 2001 Erik de Castro Lopo <erikd AT mega-nerd DOT com> + + Permission to use, copy, modify, distribute, and sell this file for any + purpose is hereby granted without fee, provided that the above copyright + and this permission notice appear in all copies. No representations are + made about the suitability of this software for any purpose. It is + provided "as is" without express or implied warranty. +*/ + +__inline long int lrint (double flt) { + int intgr; + + _asm { + fld flt + fistp intgr + }; + + return intgr; +} + +__inline long int lrintf(float flt) { + int intgr; + + _asm { + fld flt + fistp intgr + }; + + return intgr; +} +#endif + + + +const double fuzzyEpsilon = 0.00001; + +/** Returns a reference to a static double. + This value should not be tested against directly, instead + G3D::isNan() and G3D::isFinite() will return reliable results. */ +inline const double& inf() { + + // double is a standard type and should have infinity + static const double i = std::numeric_limits<double>::infinity(); + return i; +} + +/** Returns a reference to a static double. + This value should not be tested against directly, instead + G3D::isNan() and G3D::isFinite() will return reliable results. */ +inline const double& nan() { + + // double is a standard type and should have quiet NaN + static const double n = std::numeric_limits<double>::quiet_NaN(); + return n; +} + +/** Returns a reference to a static double. Use instead of G3D_PI. */ +inline const double& pi() { + static const double p = 3.1415926535898; + return p; +} + +/** Returns a reference to a static double. */ +inline const double& halfPi() { + static const double p = pi() / 2.0; + return p; +} + +/** Returns a reference to a static double. */ +inline const double& twoPi() { + static const double p = pi() * 2.0;; + return p; +} + +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; + +#ifdef _MSC_EXTENSIONS + typedef __int64 int64; + typedef unsigned __int64 uint64; +#elif ! defined(_MSC_VER) + typedef int64_t int64; + typedef uint64_t uint64; +#else + typedef long long int64; + typedef unsigned long long uint64; +#endif + +typedef float float32; +typedef double float64; + +int iAbs(int iValue); +int iCeil(double fValue); + +/** + Clamps the value to the range [low, hi] (inclusive) + */ +int iClamp(int val, int low, int hi); +int16 iClamp(int16 val, int16 low, int16 hi); +double clamp(double val, double low, double hi); +float clamp(float val, float low, float hi); + +/** + Returns a + (b - a) * f; + */ +inline double lerp(double a, double b, double f) { + return a + (b - a) * f; +} + +inline float lerp(float a, float b, float f) { + return a + (b - a) * f; +} + +/** + Wraps the value to the range [0, hi) (exclusive + on the high end). This is like the clock arithmetic + produced by % (modulo) except the result is guaranteed + to be positive. + */ +int iWrap(int val, int hi); + +int iFloor(double fValue); + +int iSign(int iValue); +int iSign(double fValue); + +inline int iSign(float f) { + return iSign((double)f); +} + + +/** + Fast round to integer using the lrint routine. + Typically 6x faster than casting to integer. + */ +inline int iRound(double fValue) { + return lrint(fValue); +} + +/** + Fast round to integer using the lrint routine. + Typically 6x faster than casting to integer. + */ +inline int iRound(float f) { + return lrintf(f); +} + +/** + Returns a random number uniformly at random between low and hi + (inclusive). + */ +int iRandom(int low, int hi); + +double abs (double fValue); +double aCos (double fValue); +double aSin (double fValue); +double aTan (double fValue); +double aTan2 (double fY, double fX); +double sign (double fValue); +double square (double fValue); + +/** + Returns true if the argument is a finite real number. + */ +bool isFinite(double x); + +/** + Returns true if the argument is NaN (not a number). + You can't use x == nan to test this because all + comparisons against nan return false. + */ +bool isNaN(double x); + +/** + Computes x % 3. + */ +int iMod3(int x); + +/** + Uniform random number between low and hi, inclusive. [low, hi] + */ +float uniformRandom(float low = 0.0f, float hi = 1.0f); + +/** + Normally distributed random number. + */ +float gaussRandom(float mean = 0.0f, float stdev = 1.0f); + +template <class T> +inline T min(const T& x, const T& y) { + return std::min<T>(x, y); +} + +template <class T> +inline T min(const T& x, const T& y, const T& z) { + return std::min<T>(std::min<T>(x, y), z); +} + +template <class T> +inline T min(const T& x, const T& y, const T& z, const T& w) { + return std::min<T>(std::min<T>(x, y), std::min<T>(z, w)); +} + +template <class T> +inline T max(const T& x, const T& y) { + return std::max<T>(x, y); +} + +template <class T> +inline T max(const T& x, const T& y, const T& z) { + return std::max<T>(std::max<T>(x, y), z); +} + +template <class T> +inline T max(const T& x, const T& y, const T& z, const T& w) { + return std::max<T>(std::max<T>(x, y), std::max<T>(z, w)); +} + +int iMin(int x, int y); +int iMax(int x, int y); + +double square(double x); +double sumSquares(double x, double y); +double sumSquares(double x, double y, double z); +double distance(double x, double y); +double distance(double x, double y, double z); + +/** + Returnes the 0-based index of the highest 1 bit from + the left. -1 means the number was 0. + + @cite Based on code by jukka@liimatta.org + */ +int highestBit(uint32 x); + +/** + Note that fuzzyEq(a, b) && fuzzyEq(b, c) does not imply + fuzzyEq(a, c), although that will be the case on some + occasions. + */ +bool fuzzyEq(double a, double b); + +/** True if a is definitely not equal to b. + Guaranteed false if a == b. + Possibly false when a != b.*/ +bool fuzzyNe(double a, double b); + +/** Is a strictly greater than b? (Guaranteed false if a <= b). + (Possibly false if a > b) */ +bool fuzzyGt(double a, double b); + +/** Is a near or greater than b? */ +bool fuzzyGe(double a, double b); + +/** Is a strictly less than b? (Guaranteed false if a >= b)*/ +bool fuzzyLt(double a, double b); + +/** Is a near or less than b? */ +bool fuzzyLe(double a, double b); + +/** + Computes 1 / sqrt(x). + */ +inline float rsq(float x) { + return 1.0f / sqrtf(x); +} + +/** + Uses SSE to implement rsq. + @cite Nick nicolas@capens.net + */ +inline float SSErsq(float x) { + + #if defined(SSE) && defined(G3D_WIN32) + __asm { + movss xmm0, x + rsqrtss xmm0, xmm0 + movss x, xmm0 + } + return x; + #else + return 1.0f / sqrt(x); + #endif +} + +/** + Return the next power of 2 higher than the input + If the input is already a power of 2, the output will be the same + as the input. + */ +int ceilPow2(unsigned int in); + +/** Returns 2^x */ +inline int pow2(unsigned int x) { + return 1 << x; +} + + +/** Log base 2 */ +inline float log2(float x) { + return ::logf(x) / ::logf(2.0f); +} + +/** Log base 2 */ +inline double log2(double x) { + return ::log(x) / ::log(2.0); +} + +/** Log base 2 */ +inline double log2(int x) { + return log2((double)x); +} + + +/** + * True if num is a power of two. + */ +bool isPow2(int num); + +bool isOdd(int num); +bool isEven(int num); + +double toRadians(double deg); +double toDegrees(double rad); + +/** + Returns true if x is not exactly equal to 0.0f. + */ +inline bool any(float x) { + return x != 0; +} + +/** + Returns true if x is not exactly equal to 0.0f. + */ +inline bool all(float x) { + return x != 0; +} + +/** + v / v (for DirectX/Cg support) + */ +inline float normalize(float v) { + return v / v; +} + +/** + a * b (for DirectX/Cg support) + */ +inline float dot(float a, float b) { + return a * b; +} + + +/** + a * b (for DirectX/Cg support) + */ +inline float mul(float a, float b) { + return a * b; +} + +/** + 2^x + */ +inline double exp2(double x) { + return pow(2.0, x); +} + +inline double rsqrt(double x) { + return 1.0 / sqrt(x); +} + + +/** + sin(x)/x + */ +inline double sinc(double x) { + double r = sin(x) / x; + + if (isNaN(r)) { + return 1.0; + } else { + return r; + } +} + +/** + Computes a floating point modulo; the result is t wrapped to the range [lo, hi). + */ +inline float wrap(float t, float lo, float hi) { + if ((t >= lo) && (t < hi)) { + return t; + } + + debugAssert(hi > lo); + + float interval = hi - lo; + + return t - interval * iFloor((t - lo) / interval); +} + + +inline double wrap(double t, double lo, double hi) { + if ((t >= lo) && (t < hi)) { + return t; + } + + debugAssert(hi > lo); + + double interval = hi - lo; + + return t - interval * iFloor((t - lo) / interval); +} + +inline double wrap(double t, double hi) { + return wrap(t, 0.0, hi); +} + + +inline bool isNaN(double x) { + bool b1 = (x < 0.0); + bool b2 = (x >= 0.0); + bool b3 = !(b1 || b2); + return b3; +} + +inline bool isFinite(double x) { + return ! isNaN(x) && (x < G3D::inf()) && (x > -G3D::inf()); +} + +//---------------------------------------------------------------------------- +inline int iAbs (int iValue) { + return ( iValue >= 0 ? iValue : -iValue ); +} + +//---------------------------------------------------------------------------- +inline int iCeil (double fValue) { + return int(::ceil(fValue)); +} + +//---------------------------------------------------------------------------- + +inline int iClamp(int val, int low, int hi) { + debugAssert(low <= hi); + if (val <= low) { + return low; + } else if (val >= hi) { + return hi; + } else { + return val; + } +} + +//---------------------------------------------------------------------------- + +inline int16 iClamp(int16 val, int16 low, int16 hi) { + debugAssert(low <= hi); + if (val <= low) { + return low; + } else if (val >= hi) { + return hi; + } else { + return val; + } +} + +//---------------------------------------------------------------------------- + +inline double clamp(double val, double low, double hi) { + debugAssert(low <= hi); + if (val <= low) { + return low; + } else if (val >= hi) { + return hi; + } else { + return val; + } +} + +inline float clamp(float val, float low, float hi) { + debugAssert(low <= hi); + if (val <= low) { + return low; + } else if (val >= hi) { + return hi; + } else { + return val; + } +} +//---------------------------------------------------------------------------- + +inline int iWrap(int val, int hi) { + if (val < 0) { + return ((val % hi) + hi) % hi; + } else { + return val % hi; + } +} + +//---------------------------------------------------------------------------- +inline int iFloor (double fValue) { + return int(::floor(fValue)); +} + +//---------------------------------------------------------------------------- +inline int iSign (int iValue) { + return ( iValue > 0 ? + 1 : ( iValue < 0 ? -1 : 0 ) ); +} + +inline int iSign (double fValue) { + return ( fValue > 0.0 ? + 1 : ( fValue < 0.0 ? -1 : 0 ) ); +} + +//---------------------------------------------------------------------------- +inline double abs (double fValue) { + return double(::fabs(fValue)); +} + +//---------------------------------------------------------------------------- +inline double aCos (double fValue) { + if ( -1.0 < fValue ) { + if ( fValue < 1.0 ) + return double(::acos(fValue)); + else + return 0.0; + } else { + return pi(); + } +} + +//---------------------------------------------------------------------------- +inline double aSin (double fValue) { + if ( -1.0 < fValue ) { + if ( fValue < 1.0 ) { + return double(::asin(fValue)); + } else { + return -halfPi(); + } + } else { + return halfPi(); + } +} + +//---------------------------------------------------------------------------- +inline double aTan (double fValue) { + return double(::atan(fValue)); +} + +//---------------------------------------------------------------------------- +inline double aTan2 (double fY, double fX) { + return double(::atan2(fY, fX)); +} + +//---------------------------------------------------------------------------- +inline double sign (double fValue) { + if (fValue > 0.0) { + return 1.0; + } + + if (fValue < 0.0) { + return -1.0; + } + + return 0.0; +} + +inline float sign (float fValue) { + if (fValue > 0.0f) { + return 1.0f; + } + + if (fValue < 0.0f) { + return -1.0f; + } + + return 0.0f; +} + + +inline float uniformRandom(float low, float hi) { + return (hi - low) * float(::rand()) / float(RAND_MAX) + low; +} + +//---------------------------------------------------------------------------- +inline double square(double x) { + return x * x; +} + +//---------------------------------------------------------------------------- +inline double sumSquares(double x, double y) { + return x*x + y*y; +} + +//---------------------------------------------------------------------------- +inline double sumSquares(double x, double y, double z) { + return x*x + y*y + z*z; +} + +//---------------------------------------------------------------------------- +inline double distance(double x, double y) { + return sqrt(sumSquares(x, y)); +} + +//---------------------------------------------------------------------------- +inline double distance(double x, double y, double z) { + return sqrt(sumSquares(x, y, z)); +} + +//---------------------------------------------------------------------------- + +/** @deprecated use G3D::min */ +inline int iMin(int x, int y) { + return (x >= y) ? y : x; +} + +//---------------------------------------------------------------------------- +/** @deprecated use G3D::min */ +inline int iMax(int x, int y) { + return (x >= y) ? x : y; +} + +//---------------------------------------------------------------------------- +inline int ceilPow2(unsigned int in) { + in -= 1; + + in |= in >> 16; + in |= in >> 8; + in |= in >> 4; + in |= in >> 2; + in |= in >> 1; + + return in + 1; +} + +inline bool isPow2(int num) { + return ((num & -num) == num); +} + +inline bool isOdd(int num) { + return (num & 1) == 1; +} + +inline bool isEven(int num) { + return (num & 1) == 0; +} + +inline double toRadians(double deg) { + return deg * pi() / 180.0; +} + +inline double toDegrees(double rad) { + return rad * 180.0 / pi(); +} + +inline float toRadians(float deg) { + return deg * (float)pi() / 180.0f; +} + +inline float toDegrees(float rad) { + return rad * 180.0f / (float)pi(); +} + +inline float toRadians(int deg) { + return deg * (float)pi() / 180.0f; +} + +inline float toDegrees(int rad) { + return rad * 180.0f / (float)pi(); +} +/** + Computes an appropriate epsilon for comparing a and b. + */ +inline double eps(double a, double b) { + // For a and b to be nearly equal, they must have nearly + // the same magnitude. This means that we can ignore b + // since it either has the same magnitude or the comparison + // will fail anyway. + (void)b; + const double aa = abs(a) + 1; + if (aa == inf()) { + return fuzzyEpsilon; + } else { + return fuzzyEpsilon * aa; + } +} + +inline bool fuzzyEq(double a, double b) { + return (a == b) || (abs(a - b) <= eps(a, b)); +} + +inline bool fuzzyNe(double a, double b) { + return ! fuzzyEq(a, b); +} + +inline bool fuzzyGt(double a, double b) { + return a > b + eps(a, b); +} + +inline bool fuzzyGe(double a, double b) { + return a > b - eps(a, b); +} + +inline bool fuzzyLt(double a, double b) { + return a < b - eps(a, b); +} + +inline bool fuzzyLe(double a, double b) { + return a < b + eps(a, b); +} + +inline int iMod3(int x) { + return x % 3; +} + +/** + Given a 32-bit integer, returns the integer with the bytes in the opposite order. + */ +inline uint32 flipEndian32(const uint32 x) { + return (x << 24) | ((x & 0xFF00) << 8) | + ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); +} + +/** + Given a 16-bit integer, returns the integer with the bytes in the opposite order. + */ +inline uint16 flipEndian16(const uint16 x) { + return (x << 8) | ((x & 0xFF00) >> 8); +} + + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/platform.h b/externals/g3dlite/G3D.lib/include/G3D/platform.h new file mode 100644 index 00000000000..be9ec4d5123 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/platform.h @@ -0,0 +1,256 @@ +/** + @file platform.h + + #defines for platform specific issues. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-06-09 + @edited 2007-07-30 + */ + +#ifndef G3D_PLATFORM_H +#define G3D_PLATFORM_H + +/** + The version number of G3D in the form: MmmBB -> + version M.mm [beta BB] + */ +#define G3D_VER 70100 + +#if defined(G3D_RELEASEDEBUG) +# define G3D_DEBUGRELEASE +#endif + +#if defined(G3D_DEBUGRELEASE) && defined(_DEBUG) +# undef _DEBUG +#endif + +#if !defined(G3D_DEBUG) && (defined(_DEBUG) || defined(G3D_DEBUGRELEASE)) +# define G3D_DEBUG +#endif + +#ifdef _MSC_VER + #define G3D_WIN32 +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + #define G3D_FREEBSD + #define G3D_LINUX +#elif defined(__linux__) + #define G3D_LINUX +#elif defined(__APPLE__) + #define G3D_OSX + + // Prevent OS X fp.h header from being included; it defines + // pi as a constant, which creates a conflict with G3D +#define __FP__ +#else + #error Unknown platform +#endif + + +// Default to compiling with SSE, but if you want to compile +// without installing SP5.0 and the Processor Pack on Windows, compile with NO_SSE +// defined (can be passed to the compiler command line with /D "NO_SSE") +#if !defined(NO_SSE) + #define SSE +#endif + +// On g++, recognize cases where the -msse2 flag was not specified +#if defined(SSE) && defined(__GNUC__) && ! defined (__SSE__) +# undef SSE +#endif + + +// Verify that the supported compilers are being used and that this is a known +// processor. + +#ifdef G3D_LINUX +# ifndef __GNUC__ +# error G3D only supports the gcc compiler on Linux. +# endif +#endif + +#ifdef G3D_OSX +# ifndef __GNUC__ +# error G3D only supports the gcc compiler on OS X. +# endif + +# if defined(__i386__) +# define G3D_OSX_INTEL +# elif defined(__PPC__) +# define G3D_OSX_PPC +# else +# define G3D_OSX_UNKNOWN +# endif + +#endif + + +#ifdef _MSC_VER +// Microsoft Visual C++ 8.0 ("Express") = 1400 +// Microsoft Visual C++ 7.1 ("2003") _MSC_VER = 1310 +// Microsoft Visual C++ 7.0 ("2002") _MSC_VER = 1300 +// Microsoft Visual C++ 6.0 _MSC_VER = 1200 +// Microsoft Visual C++ 5.0 _MSC_VER = 1100 + +// Turn off warnings about deprecated C routines (TODO: revisit) +# pragma warning (disable : 4996) + +// Turn off "conditional expression is constant" warning; MSVC generates this +// for debug assertions in inlined methods. +# pragma warning (disable : 4127) + +# define G3D_DEPRECATED __declspec(deprecated) + +// Prevent Winsock conflicts by hiding the winsock API +# ifndef _WINSOCKAPI_ +# define _G3D_INTERNAL_HIDE_WINSOCK_ +# define _WINSOCKAPI_ +# endif + +// Disable 'name too long for browse information' warning +# pragma warning (disable : 4786) +// TODO: remove +# pragma warning (disable : 4244) + +# define ZLIB_WINAPI + +# define restrict + +# define G3D_CHECK_PRINTF_ARGS +# define G3D_CHECK_VPRINTF_ARGS +# define G3D_CHECK_PRINTF_METHOD_ARGS +# define G3D_CHECK_VPRINTF_METHOD_ARGS + + // On MSVC, we need to link against the multithreaded DLL version of + // the C++ runtime because that is what SDL and ZLIB are compiled + // against. This is not the default for MSVC, so we set the following + // defines to force correct linking. + // + // For documentation on compiler options, see: + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_.2f.md.2c_2f.ml.2c_2f.mt.2c_2f.ld.asp + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HTML/_core_Compiler_Reference.asp + // + + // DLL runtime + #ifndef _DLL + #define _DLL + #endif + + // Multithreaded runtime + #ifndef _MT + #define _MT 1 + #endif + + // Ensure that we aren't forced into the static lib + #ifdef _STATIC_CPPLIB + #undef _STATIC_CPPLIB + #endif + + #ifdef _DEBUG + #pragma comment (linker, "/NODEFAULTLIB:LIBCMTD.LIB") + #pragma comment (linker, "/NODEFAULTLIB:LIBCPMTD.LIB") + #pragma comment (linker, "/NODEFAULTLIB:LIBCPD.LIB") + #pragma comment (linker, "/DEFAULTLIB:MSVCPRTD.LIB") + #pragma comment(linker, "/NODEFAULTLIB:LIBCD.LIB") + #pragma comment(linker, "/DEFAULTLIB:MSVCRTD.LIB") + #else + #pragma comment(linker, "/NODEFAULTLIB:LIBC.LIB") + #pragma comment(linker, "/DEFAULTLIB:MSVCRT.LIB") + #pragma comment (linker, "/NODEFAULTLIB:LIBCMT.LIB") + #pragma comment (linker, "/NODEFAULTLIB:LIBCPMT.LIB") + #pragma comment(linker, "/NODEFAULTLIB:LIBCP.LIB") + #pragma comment (linker, "/DEFAULTLIB:MSVCPRT.LIB") + #endif + + // Now set up external linking + +# ifdef _DEBUG + // zlib was linked against the release MSVCRT; force + // the debug version. +# pragma comment(linker, "/NODEFAULTLIB:MSVCRT.LIB") +# endif + + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif + + +# define NOMINMAX 1 +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +# endif +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +# undef NOMINMAX + +# ifdef _G3D_INTERNAL_HIDE_WINSOCK_ +# undef _G3D_INTERNAL_HIDE_WINSOCK_ +# undef _WINSOCKAPI_ +# endif + + +# define G3D_START_AT_MAIN()\ +int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw);\ +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {\ + return G3D_WinMain(hInst, hPrev, szCmdLine, sw);\ +} + +#else + +# define G3D_START_AT_MAIN() + +#endif // win32 + +#ifdef __GNUC__ + +# include <stdint.h> + +# if __STDC_VERSION__ < 199901 +# define restrict __restrict__ +# endif + +# define G3D_DEPRECATED __attribute__((__deprecated__)) + +// setup function calling conventions +# if defined(__i386__) && ! defined(__x86_64__) + +# ifndef __cdecl +# define __cdecl __attribute__((cdecl)) +# endif + +# ifndef __stdcall +# define __stdcall __attribute__((stdcall)) +# endif + +# elif defined(__x86_64__) || defined(__powerpc__) + +# ifndef __cdecl +# define __cdecl +# endif + +# ifndef __stdcall +# define __stdcall +# endif +# endif // calling conventions + +# define G3D_CHECK_PRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 3))) +# define G3D_CHECK_VPRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 0))) +# define G3D_CHECK_PRINTF_ARGS __attribute__((__format__(__printf__, 1, 2))) +# define G3D_CHECK_VPRINTF_ARGS __attribute__((__format__(__printf__, 1, 0))) +#endif + + +/** + @def STR(expression) + + Creates a string from the expression. Frequently used with G3D::Shader + to express shading programs inline. + + <CODE>STR(this becomes a string)<PRE> evaluates the same as <CODE>"this becomes a string"</CODE> + */ +#define STR(x) #x + +// Header guard +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/prompt.h b/externals/g3dlite/G3D.lib/include/G3D/prompt.h new file mode 100644 index 00000000000..e25b955ab28 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/prompt.h @@ -0,0 +1,67 @@ +/** + @file prompt.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + @cite Windows GUI code by Max McGuire + + @created 2001-08-26 + @edited 2006-08-13 + */ + +#ifndef G3D_PROMPT_H +#define G3D_PROMPT_H + +#include "platform.h" +#include <string> + +namespace G3D { + +/** + Prints a prompt to stdout and waits for user input. The return value is + the number of the user's choice (the first is 0, if there are no + choices, returns 0). + + @param useGui Under Win32, use a GUI, not stdout prompt. + @param windowTitle The title for the prompt window + @param promptx The text string to prompt the user with + @param choice An array of strings that are the choices the user may make + @param numChoices The length of choice. + + @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com + @cite Font setting code by Kurt Miller, kurt@flipcode.com + */ +int prompt( + const char* windowTitle, + const char* promptx, + const char** choice, + int numChoices, + bool useGui); + +/** + Prints a prompt and waits for user input. The return value is + the number of the user's choice (the first is 0, if there are no + choices, returns 0). + <P>Uses GUI under Win32, stdout prompt otherwise. + */ +inline int prompt( + const char* windowTitle, + const char* promptx, + const char** choice, + int numChoices) { + + return prompt(windowTitle, promptx, choice, numChoices, true); +} + + +/** + Displays a GUI prompt with "Ok" as the only choice. + */ +void msgBox( + const std::string& message, + const std::string& title = "Message"); + + +}; // namespace + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/serialize.h b/externals/g3dlite/G3D.lib/include/G3D/serialize.h new file mode 100644 index 00000000000..2382c0ee0fd --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/serialize.h @@ -0,0 +1,30 @@ +#ifndef G3D_SERIALIZE_H +#define G3D_SERIALIZE_H + +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Array.h" + +namespace G3D { + + +template<typename T> +void serialize(const Array<T>& array, BinaryOutput& b) { + b.writeInt32(array.size()); + for (int i = 0; i < array.size(); ++i) { + serialize(array[i], b); + } +} + +template<typename T> +void deserialize(Array<T>& array, BinaryInput& b) { + int N = b.readInt32(); + array.resize(N); + for (int i = 0; i < array.size(); ++i) { + deserialize(array[i], b); + } +} + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h b/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h new file mode 100644 index 00000000000..a9daad9b578 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h @@ -0,0 +1,118 @@ +/** + @file spline.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-07-25 + @edited 2007-05-05 + */ + +#ifndef G3D_SPLINEFUNC_H +#define G3D_SPLINEFUNC_H + +namespace G3D { + +#include "G3D/platform.h" +#include "G3D/debug.h" +#include "G3D/Array.h" +#include "G3D/g3dmath.h" + +/** + Interpolates a property according to a piecewise linear spline. This provides + C0 continuity but the derivatives are not smooth. + <P> + Example: + <CODE> + const double times[] = {MIDNIGHT, SUNRISE - HOUR, SUNRISE, SUNRISE + sunRiseAndSetTime / 4, SUNRISE + sunRiseAndSetTime, SUNSET - sunRiseAndSetTime, SUNSET - sunRiseAndSetTime / 2, SUNSET, SUNSET + HOUR/2, DAY}; + const Color3 color[] = {Color3(0, .0, .1), Color3(0, .0, .1), Color3::black(), Color3::black(), Color3::white() * .25, Color3::white() * .25, Color3(.5, .2, .2), Color3(.05, .05, .1), Color3(0, .0, .1), Color3(0, .0, .1)}; + ambient = linearSpline(time, times, color, 10); + </CODE> + + See also G3D::Spline + + @param x The spline is a function of x; this is the sample to choose. + @param controlX controlX[i], controlY[i] is a control points. It is assumed + that controlX are strictly increasing. XType must support + the "<" operator and a subtraction operator that returns + a number. + @param controlY YType must support multiplication and addition. + @param numControl The number of control points. + */ +template<class XType, class YType> +YType linearSpline(double x, const XType* controlX, const YType* controlY, int numControl) { + debugAssert(numControl >= 1); + + // Off the beginning + if ((numControl == 1) || (x < controlX[0])) { + return controlY[0]; + } + + for (int i = 1; i < numControl; ++i) { + if (x < controlX[i]) { + const double alpha = (double)(controlX[i] - x) / (controlX[i] - controlX[i - 1]); + return controlY[i] * (1 - alpha) + controlY[i - 1] * alpha; + } + } + + // Off the end + return controlY[numControl - 1]; +} + + + /** See also G3D::Spline*/ +template<class YType> YType cyclicCatmullRomSpline( + double t, + const YType* controlY, + int numPoints) { + + debugAssert(numPoints >= 3); + + t = wrap(t, numPoints); + + // Find the indices of adjacent control points + int i = iFloor(t); + + // Compute the distance from the control point + t = t - i; + + // Shift back one point for correct indexing + i += numPoints - 1; + + // Pick up four control points + const YType& P0 = controlY[(i + 0) % numPoints]; + const YType& P1 = controlY[(i + 1) % numPoints]; + const YType& P2 = controlY[(i + 2) % numPoints]; + const YType& P3 = controlY[(i + 3) % numPoints]; + + return 0.5 * ((2 * P1) + + (-P0 + P2) * t + + (2*P0 - 5*P1 + 4*P2 - P3) * t*t + + (-P0 + 3*P1- 3*P2 + P3) * t*t*t); +} + +/** + A cubic spline with regularly spaced + control points. The spline interpolates + the control points. The spline + will wrap from the last point back to the first. + + The t parameter is on the range [0, controlY.size()], + where integers correspond to control points exactly. + + See also G3D::Spline + + @cite http://www.mvps.org/directx/articles/catmull/ +*/ +template<class YType> YType cyclicCatmullRomSpline( + double t, + const Array<YType>& controlY) { + + int numPoints = controlY.size(); + return cyclicCatmullRomSpline(t, controlY.getCArray(), numPoints); +} + +} + +#endif + + diff --git a/externals/g3dlite/G3D.lib/include/G3D/stringutils.h b/externals/g3dlite/G3D.lib/include/G3D/stringutils.h new file mode 100644 index 00000000000..20a2ff6e795 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/stringutils.h @@ -0,0 +1,130 @@ +/** + @file stringutils.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @author 2000-09-09 + @edited 2008-08-05 + */ + +#ifndef G3D_STRINGUTILS_H +#define G3D_STRINGUTILS_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include <cstring> + +namespace G3D { + +extern const char* NEWLINE; + +/** + Returns true if the test string begins with the pattern string. + */ +bool beginsWith( + const std::string& test, + const std::string& pattern); + +/** + Returns true if the test string ends with the pattern string. + */ +bool endsWith( + const std::string& test, + const std::string& pattern); + +/** + Produces a new string that is the input string + wrapped at a certain number of columns (where + the line is broken at the latest space before the + column limit.) Platform specific NEWLINEs + are inserted to wrap. + */ +std::string wordWrap( + const std::string& input, + int numCols); + +/** + A comparison function for passing to Array::sort. + */ +int stringCompare( + const std::string& s1, + const std::string& s2); + +int stringPtrCompare( + const std::string* s1, + const std::string* s2); + +/** + Returns a new string that is an uppercase version of x. + */ +std::string toUpper( + const std::string& x); + +std::string toLower( + const std::string& x); + +/** + Splits x at each occurance of splitChar. + */ +G3D::Array<std::string> stringSplit( + const std::string& x, + char splitChar); + +/** + joinChar is not inserted at the beginning or end, just in between + elements. + */ +std::string stringJoin( + const G3D::Array<std::string>& a, + char joinChar); + +std::string stringJoin( + const G3D::Array<std::string>& a, + const std::string& joinStr); + +/** + Strips whitespace from both ends of the string. + */ +std::string trimWhitespace( + const std::string& s); + +/** These standard C functions are renamed for clarity/naming + conventions and to return bool, not int. + */ +inline bool isWhiteSpace(const unsigned char c) { + return isspace(c) != 0; +} + +/** These standard C functions are renamed for clarity/naming + conventions and to return bool, not int. + */ +inline bool isNewline(const unsigned char c) { + return (c == '\n') || (c == '\r'); +} + +/** These standard C functions are renamed for clarity/naming + conventions and to return bool, not int. + */ +inline bool isDigit(const unsigned char c) { + return isdigit(c) != 0; +} + +/** These standard C functions are renamed for clarity/naming + conventions and to return bool, not int. + */ +inline bool isLetter(const unsigned char c) { + return isalpha(c) != 0; +} + +inline bool isSlash(const unsigned char c) { + return (c == '\\') || (c == '/'); +} + +inline bool isQuote(const unsigned char c) { + return (c == '\'') || (c == '\"'); +} + +}; // namespace + +#endif + diff --git a/externals/g3dlite/G3D.lib/include/G3D/uint128.h b/externals/g3dlite/G3D.lib/include/G3D/uint128.h new file mode 100644 index 00000000000..3e970cb2a7e --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/uint128.h @@ -0,0 +1,51 @@ +/** + @file uint128.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + @author Kyle Whitson + + @created 2008-07-17 + @edited 2008-07-17 + */ + +#ifndef G3D_UINT128_H +#define G3D_UINT128_H + +#include "G3D/g3dmath.h" + +namespace G3D { + +/** Limited functionality 128-bit unsigned integer. This is primarily to support FNV hashing and other + cryptography applications. See the GMP library for high-precision C++ math support. */ +class uint128 { +public: + + G3D::uint64 hi; + G3D::uint64 lo; + + uint128(const uint64& lo); + + uint128(const uint64& hi, const uint64& lo); + + uint128& operator+=(const uint128& x); + + uint128& operator*=(const uint128& x); + + uint128& operator^=(const uint128& x); + + uint128& operator&=(const uint128& x); + + uint128& operator|=(const uint128& x); + + bool operator==(const uint128& x); + + uint128& operator>>=(const int x); + + uint128& operator<<=(const int x); + + uint128 operator&(const uint128& x); + +}; +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h b/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h new file mode 100644 index 00000000000..aedc731e7f8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h @@ -0,0 +1,235 @@ +/** + @file vectorMath.h + + Function aliases for popular vector methods. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created: 2001-06-02 + @edited: 2004-02-02 + Copyright 2000-2004, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_VECTORMATH_H +#define G3D_VECTORMATH_H + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/Matrix3.h" +#include "G3D/Matrix4.h" +#include "G3D/Color1.h" +#include "G3D/Color3.h" +#include "G3D/Color4.h" + + +namespace G3D { + + +inline Matrix4 mul(const Matrix4& a, const Matrix4& b) { + return a * b; +} + +inline Vector4 mul(const Matrix4& m, const Vector4& v) { + return m * v; +} + +inline Vector3 mul(const Matrix3& m, const Vector3& v) { + return m * v; +} + +inline Matrix3 mul(const Matrix3& a, const Matrix3& b) { + return a * b; +} + +inline float dot(const Vector2& a, const Vector2& b) { + return a.dot(b); +} + +inline float dot(const Vector3& a, const Vector3& b) { + return a.dot(b); +} + +inline float dot(const Vector4& a, const Vector4& b) { + return a.dot(b); +} + +inline Vector2 normalize(const Vector2& v) { + return v / v.length(); +} + +inline Vector3 normalize(const Vector3& v) { + return v / v.magnitude(); +} + +inline Vector4 normalize(const Vector4& v) { + return v / v.length(); +} + +inline Vector2 abs(const Vector2& v) { + return Vector2(::fabsf(v.x), ::fabsf(v.y)); +} + +inline Vector3 abs(const Vector3& v) { + return Vector3(::fabsf(v.x), ::fabsf(v.y), ::fabsf(v.z)); +} + +inline Vector4 abs(const Vector4& v) { + return Vector4(::fabsf(v.x), ::fabsf(v.y), ::fabsf(v.z), ::fabsf(v.w)); +} + +inline bool all(const Vector2& v) { + return (v.x != 0) && (v.y != 0); +} + +inline bool all(const Vector3& v) { + return (v.x != 0) && (v.y != 0) && (v.z != 0); +} + +inline bool all(const Vector4& v) { + return (v.x != 0) && (v.y != 0) && (v.z != 0) && (v.w != 0); +} + +inline bool any(const Vector2& v) { + return (v.x != 0) || (v.y != 0); +} + +inline bool any(const Vector3& v) { + return (v.x != 0) || (v.y != 0) || (v.z != 0); +} + +inline bool any(const Vector4& v) { + return (v.x != 0) || (v.y != 0) || (v.z != 0) || (v.w != 0); +} + +inline Vector2 clamp(const Vector2& v, const Vector2& a, const Vector2& b) { + return v.clamp(a, b); +} + +inline Vector3 clamp(const Vector3& v, const Vector3& a, const Vector3& b) { + return v.clamp(a, b); +} + +inline Vector4 clamp(const Vector4& v, const Vector4& a, const Vector4& b) { + return v.clamp(a, b); +} + +inline Vector2 lerp(const Vector2& v1, const Vector2& v2, float f) { + return v1.lerp(v2, f); +} + +inline Vector3 lerp(const Vector3& v1, const Vector3& v2, float f) { + return v1.lerp(v2, f); +} + +inline Vector4 lerp(const Vector4& v1, const Vector4& v2, float f) { + return v1.lerp(v2, f); +} + +inline Color1 lerp(const Color1& v1, const Color1& v2, float f) { + return v1.lerp(v2, f); +} + +inline Color3 lerp(const Color3& v1, const Color3& v2, float f) { + return v1.lerp(v2, f); +} + +inline Color4 lerp(const Color4& v1, const Color4& v2, float f) { + return v1.lerp(v2, f); +} + +inline Vector3 cross(const Vector3& v1, const Vector3& v2) { + return v1.cross(v2); +} + +inline double determinant(const Matrix3& m) { + return m.determinant(); +} + +inline double determinant(const Matrix4& m) { + return m.determinant(); +} + +inline Vector2 min(const Vector2& v1, const Vector2& v2) { + return v1.min(v2); +} + +inline Vector3 min(const Vector3& v1, const Vector3& v2) { + return v1.min(v2); +} + +inline Vector4 min(const Vector4& v1, const Vector4& v2) { + return v1.min(v2); +} + +inline Color3 min(const Color3& v1, const Color3& v2) { + return v1.min(v2); +} + +inline Color4 min(const Color4& v1, const Color4& v2) { + return v1.min(v2); +} + +inline Vector2 max(const Vector2& v1, const Vector2& v2) { + return v1.max(v2); +} + +inline Vector3 max(const Vector3& v1, const Vector3& v2) { + return v1.max(v2); +} + +inline Vector4 max(const Vector4& v1, const Vector4& v2) { + return v1.max(v2); +} + +inline Color3 max(const Color3& v1, const Color3& v2) { + return v1.max(v2); +} + +inline Color4 max(const Color4& v1, const Color4& v2) { + return v1.max(v2); +} + +inline Vector2 sign(const Vector2& v) { + return Vector2((float)sign(v.x), (float)sign(v.y)); +} + +inline Vector3 sign(const Vector3& v) { + return Vector3((float)sign(v.x), (float)sign(v.y), (float)sign(v.z)); +} + +inline Vector4 sign(const Vector4& v) { + return Vector4((float)sign(v.x), (float)sign(v.y), (float)sign(v.z), (float)sign(v.w)); +} + +inline float length(float v) { + return ::fabsf(v); +} + +inline float length(const Vector2& v) { + return v.length(); +} + +inline float length(const Vector3& v) { + return v.magnitude(); +} + +inline float length(const Vector4& v) { + return v.length(); +} + +/** + Computes the log of each component. Useful for + inverting the monitor gamma function or simulating + perceptual response. + */ +inline Color3 log(const Color3& c) { + return Color3(::logf(c.r), ::logf(c.g), ::logf(c.b)); +} + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/source/AABox.cpp b/externals/g3dlite/G3D.lib/source/AABox.cpp new file mode 100644 index 00000000000..9e871aa8af7 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/AABox.cpp @@ -0,0 +1,342 @@ +/** + @file AABox.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-10 + @edited 2006-01-11 +*/ + +#include "G3D/platform.h" +#include "G3D/AABox.h" +#include "G3D/Box.h" +#include "G3D/Plane.h" +#include "G3D/Sphere.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + + +namespace G3D { + + +void AABox::serialize(class BinaryOutput& b) const { + b.writeVector3(lo); + b.writeVector3(hi); +} + + +void AABox::deserialize(class BinaryInput& b) { + lo = b.readVector3(); + hi = b.readVector3(); +} + + +void AABox::split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const { + // Low, medium, and high along the chosen axis + float L = G3D::min(location, lo[axis]); + float M = G3D::min(G3D::max(location, lo[axis]), hi[axis]); + float H = G3D::max(location, hi[axis]); + + // Copy over this box. + high = low = *this; + + // Now move the split points along the special axis + low.lo[axis] = L; + low.hi[axis] = M; + high.lo[axis] = M; + high.hi[axis] = H; +} + + +Vector3 AABox::randomSurfacePoint() const { + Vector3 extent = hi - lo; + float aXY = extent.x * extent.y; + float aYZ = extent.y * extent.z; + float aZX = extent.z * extent.x; + + float r = (float)uniformRandom(0.0f, aXY + aYZ + aZX); + + // Choose evenly between positive and negative face planes + float d = ((float)uniformRandom(0, 1) < 0.5f) ? 0.0f : 1.0f; + + // The probability of choosing a given face is proportional to + // its area. + if (r < aXY) { + return + lo + + Vector3( + (float)uniformRandom(0.0f, extent.x), + (float)uniformRandom(0.0f, extent.y), + d * extent.z); + } else if (r < aYZ) { + return + lo + + Vector3( + d * extent.x, + (float)uniformRandom(0, extent.y), + (float)uniformRandom(0, extent.z)); + } else { + return + lo + + Vector3( + (float)uniformRandom(0, extent.x), + d * extent.y, + (float)uniformRandom(0, extent.z)); + } +} + + +Vector3 AABox::randomInteriorPoint() const { + return Vector3( + (float)uniformRandom(lo.x, hi.x), + (float)uniformRandom(lo.y, hi.y), + (float)uniformRandom(lo.z, hi.z)); +} + + +bool AABox::intersects(const AABox& other) const { + // Must be overlap along all three axes. + // Try to find a separating axis. + + for (int a = 0; a < 3; ++a) { + + // |--------| + // |------| + + if ((lo[a] > other.hi[a]) || + (hi[a] < other.lo[a])) { + return false; + } + } + + return true; +} + +int AABox::dummy = 0; + + +bool AABox::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask, + uint32& childMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + childMask = 0; + + const bool finite = + (abs(lo.x) < G3D::inf()) && + (abs(hi.x) < G3D::inf()) && + (abs(lo.y) < G3D::inf()) && + (abs(hi.y) < G3D::inf()) && + (abs(lo.z) < G3D::inf()) && + (abs(hi.z) < G3D::inf()); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + Vector3 corner; + + int numContained = 0; + int v = 0; + + // We can early-out only if we have found one point on each + // side of the plane (i.e. if we are straddling). That + // occurs when (numContained < v) && (numContained > 0) + for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) { + // Unrolling these 3 if's into a switch decreases performance + // by about 2x + corner.x = (v & 1) ? hi.x : lo.x; + corner.y = (v & 2) ? hi.y : lo.y; + corner.z = (v & 4) ? hi.z : lo.z; + + if (finite) { // this branch is highly predictable + if (plane[p].halfSpaceContainsFinite(corner)) { + ++numContained; + } + } else { + if (plane[p].halfSpaceContains(corner)) { + ++numContained; + } + } + } + + if (numContained == 0) { + // Plane p culled the box + cullingPlane = p; + + // The caller should not recurse into the children, + // since the parent is culled. If they do recurse, + // make them only test against this one plane, which + // will immediately cull the volume. + childMask = 1 << p; + return true; + + } else if (numContained < v) { + // The bounding volume straddled the plane; we have + // to keep testing against this plane + childMask |= (1 << p); + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool AABox::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + const bool finite = + (abs(lo.x) < G3D::inf()) && + (abs(hi.x) < G3D::inf()) && + (abs(lo.y) < G3D::inf()) && + (abs(hi.y) < G3D::inf()) && + (abs(lo.z) < G3D::inf()) && + (abs(hi.z) < G3D::inf()); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + bool culled = true; + Vector3 corner; + + int v; + + // Assume this plane culls all points. See if there is a point + // not culled by the plane... early out when at least one point + // is in the positive half space. + for (v = 0; (v < 8) && culled; ++v) { + + // Unrolling these 3 if's into a switch decreases performance + // by about 2x + corner.x = (v & 1) ? hi.x : lo.x; + corner.y = (v & 2) ? hi.y : lo.y; + corner.z = (v & 4) ? hi.z : lo.z; + + if (finite) { // this branch is highly predictable + culled = ! plane[p].halfSpaceContainsFinite(corner); + } else { + culled = ! plane[p].halfSpaceContains(corner); + } + } + + if (culled) { + // Plane p culled the box + cullingPlane = p; + + return true; + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool AABox::intersects(const class Sphere& sphere) const { + double d = 0; + + //find the square of the distance + //from the sphere to the box + for (int i = 0; i < 3; ++i) { + if (sphere.center[i] < lo[i]) { + d += square(sphere.center[i] - lo[i]); + } else if (sphere.center[i] > hi[i]) { + d += square(sphere.center[i] - hi[i]); + } + } + + return d <= square(sphere.radius); +} + +Vector3 AABox::corner(int index) const { + + // default constructor inits all components to 0 + Vector3 v; + + switch (index) + { + case 0: + v.x = lo.x; + v.y = lo.y; + v.z = hi.z; + break; + + case 1: + v.x = hi.x; + v.y = lo.y; + v.z = hi.z; + break; + + case 2: + v.x = hi.x; + v.y = hi.y; + v.z = hi.z; + break; + + case 3: + v.x = lo.x; + v.y = hi.y; + v.z = hi.z; + break; + + case 4: + v.x = lo.x; + v.y = lo.y; + v.z = lo.z; + break; + + case 5: + v.x = hi.x; + v.y = lo.y; + v.z = lo.z; + break; + + case 6: + v.x = hi.x; + v.y = hi.y; + v.z = lo.z; + break; + + case 7: + v.x = lo.x; + v.y = hi.y; + v.z = lo.z; + break; + + default: + debugAssertM(false, "Invalid corner index"); + break; + } + + return v; +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/AnyVal.cpp b/externals/g3dlite/G3D.lib/source/AnyVal.cpp new file mode 100644 index 00000000000..45fb41df534 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/AnyVal.cpp @@ -0,0 +1,1381 @@ +/** + @file AnyVal.cpp + @author Morgan McGuire + @maintainer Morgan McGuire + @created 2006-06-11 + @edited 2008-07-14 + */ + +#include "G3D/AnyVal.h" +#include "G3D/Array.h" +#include "G3D/stringutils.h" +#include "G3D/Table.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/Color1.h" +#include "G3D/Color3.h" +#include "G3D/Color4.h" +#include "G3D/Matrix2.h" +#include "G3D/Matrix3.h" +#include "G3D/Matrix4.h" +#include "G3D/Rect2D.h" +#include "G3D/AABox.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Quat.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +AnyVal AnyVal::fromFile(const std::string& filename) { + TextInput t(filename); + return AnyVal(t); +} + + +void AnyVal::load(const std::string& filename) { + *this = fromFile(filename); +} + + +void AnyVal::save(const std::string& filename) const { + TextOutput t(filename); + serialize(t); + t.commit(); +} + + +AnyVal::AnyVal() : m_type(NIL), m_value(NULL), m_referenceCount(NULL) { +} + + +AnyVal::AnyVal(bool b) : m_type(BOOLEAN), m_value(new bool(b)), m_referenceCount(NULL) { +} + + +AnyVal::AnyVal(G3D::TextInput& t) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) { + deserialize(t); +} + + +/*AnyVal::AnyVal(G3D::BinaryInput& b) { + deserialize(b); +} +*/ + +AnyVal::AnyVal(double v) : m_type(NUMBER), m_referenceCount(NULL) { + m_value = new double(v); +} + + +AnyVal::AnyVal(int v) : m_type(NUMBER), m_referenceCount(NULL) { + m_value = new double(v); +} + + +AnyVal::AnyVal(const Rect2D& v) : m_type(RECT2D), m_referenceCount(NULL) { + m_value = new Rect2D(v); +} + + +AnyVal::AnyVal(const AABox& v) : m_type(AABOX), m_referenceCount(NULL) { + m_value = new AABox(v); +} + + +AnyVal::AnyVal(const Vector2& v) : m_type(VECTOR2), m_referenceCount(NULL) { + m_value = new Vector2(v); +} + + +AnyVal::AnyVal(const Vector3& v) : m_type(VECTOR3), m_referenceCount(NULL) { + m_value = new Vector3(v); +} + + +AnyVal::AnyVal(const Vector4& v) : m_type(VECTOR4), m_referenceCount(NULL) { + m_value = new Vector4(v); +} + + +AnyVal::AnyVal(const Color1& v) : m_type(COLOR1), m_referenceCount(NULL) { + m_value = new Color1(v); +} + + +AnyVal::AnyVal(const Color3& v) : m_type(COLOR3), m_referenceCount(NULL) { + m_value = new Color3(v); +} + + +AnyVal::AnyVal(const Color4& v) : m_type(COLOR4), m_referenceCount(NULL) { + m_value = new Color4(v); +} + + +AnyVal::AnyVal(const std::string& v) : m_type(STRING), m_referenceCount(NULL) { + m_value = new std::string(v); +} + + +AnyVal::AnyVal(const char* v) : m_type(STRING), m_referenceCount(NULL) { + m_value = new std::string(v); +} + + +AnyVal::AnyVal(const Quat& v) : m_type(QUAT), m_referenceCount(NULL) { + m_value = new Quat(v); +} + + +AnyVal::AnyVal(const CoordinateFrame& v) : m_type(COORDINATEFRAME), m_referenceCount(NULL) { + m_value = new CoordinateFrame(v); +} + + +AnyVal::AnyVal(const Matrix2& v) : m_type(MATRIX2), m_referenceCount(NULL) { + m_value = new Matrix2(v); +} + +AnyVal::AnyVal(const Matrix3& v) : m_type(MATRIX3), m_referenceCount(NULL) { + m_value = new Matrix3(v); +} + + +AnyVal::AnyVal(const Matrix4& v) : m_type(MATRIX4), m_referenceCount(NULL) { + m_value = new Matrix4(v); +} + + +AnyVal::AnyVal(const AnyVal& c) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) { + *this = c; +} + + +AnyVal::AnyVal(Type arrayOrTable) : m_type(NIL), m_value(NULL), m_referenceCount(new int(1)) { + // TODO: make AnyVal::createArray() + switch (arrayOrTable) { + case ARRAY: + m_type = ARRAY; + m_value = new Array<AnyVal>(); + break; + + case TABLE: + m_type = TABLE; + m_value = new Table<std::string, AnyVal>(); + break; + + default: + debugAssertM(false, "Cannot construct AnyVal from constants except ARRAY or TABLE."); + } +} + + +AnyVal::~AnyVal() { + deleteValue(); +} + + +void AnyVal::deleteValue() { + if (m_referenceCount) { + --(*m_referenceCount); + if (*m_referenceCount <= 0) { + delete m_referenceCount; + m_referenceCount = NULL; + // Pass through and delete the real object now + } else { + // Someone else is holding a reference, so we can't delete + // the object. + m_referenceCount = NULL; + return; + } + } + + switch (m_type) { + case NIL: + // Nothing to do + break; + + case NUMBER: + delete (double*)m_value; + break; + + case BOOLEAN: + delete (bool*)m_value; + break; + + case STRING: + delete (std::string*)m_value; + break; + + case RECT2D: + delete (Rect2D*)m_value; + break; + + case AABOX: + delete (AABox*)m_value; + break; + + case VECTOR2: + delete (Vector2*)m_value; + break; + + case VECTOR3: + delete (Vector3*)m_value; + break; + + case VECTOR4: + delete (Vector4*)m_value; + break; + + case MATRIX2: + delete (Matrix2*)m_value; + break; + + case MATRIX3: + delete (Matrix3*)m_value; + break; + + case MATRIX4: + delete (Matrix4*)m_value; + break; + + case QUAT: + delete (Quat*)m_value; + break; + + case COORDINATEFRAME: + delete (CoordinateFrame*)m_value; + break; + + case COLOR1: + delete (Color1*)m_value; + break; + + case COLOR3: + delete (Color3*)m_value; + break; + + case COLOR4: + delete (Color4*)m_value; + break; + + case ARRAY: + delete (Array<AnyVal>*)m_value; + break; + + case TABLE: + delete (Table<std::string, AnyVal>*)m_value; + break; + + default: + debugAssertM(false, "Internal error: no destructor for this type."); + } + + m_value = NULL; +} + + +AnyVal& AnyVal::operator=(const AnyVal& v) { + deleteValue(); + + m_type = v.m_type; + + m_referenceCount = v.m_referenceCount; + + if (isSharedType()) { + ++(*m_referenceCount); + m_value = v.m_value; + } else { + m_value = v.copyValue(); + } + + return *this; +} + + +void* AnyVal::copyValue() const { + switch (m_type) { + case NIL: + return NULL; + + case NUMBER: + return new double(*(double*)m_value); + + case BOOLEAN: + return new bool(*(bool*)m_value); + + case STRING: + return new std::string(*(std::string*)m_value); + + case RECT2D: + return new Rect2D(*(Rect2D*)m_value); + + case AABOX: + return new AABox(*(AABox*)m_value); + + case VECTOR2: + return new Vector2(*(Vector2*)m_value); + + case VECTOR3: + return new Vector3(*(Vector3*)m_value); + + case VECTOR4: + return new Vector4(*(Vector4*)m_value); + + case MATRIX2: + return new Matrix2(*(Matrix2*)m_value); + + case MATRIX3: + return new Matrix3(*(Matrix3*)m_value); + + case MATRIX4: + return new Matrix4(*(Matrix4*)m_value); + + case QUAT: + return new Quat(*(Quat*)m_value); + + case COORDINATEFRAME: + return new CoordinateFrame(*(CoordinateFrame*)m_value); + + case COLOR1: + return new Color1(*(Color1*)m_value); + + case COLOR3: + return new Color3(*(Color3*)m_value); + + case COLOR4: + return new Color4(*(Color4*)m_value); + + case ARRAY: + return new Array<AnyVal>(*(Array<AnyVal>*)m_value); + + case TABLE: + return new Table<std::string, AnyVal>(*(Table<std::string, AnyVal>*)m_value); + + default: + debugAssertM(false, "Internal error: no assignment operator for this type."); + return NULL; + } +} + +AnyVal::Type AnyVal::type() const { + return m_type; +} + + +static bool legalIdentifier(const std::string& s) { + if (s.size() == 0) { + return false; + } + + if (! isLetter(s[0]) || (s[0] == '_')) { + return false; + } + + bool ok = true; + + for (unsigned int i = 1; i < s.size(); ++i) { + ok &= isDigit(s[i]) || isLetter(s[i]) || (s[i] == '_'); + } + + return ok; +} + + +void AnyVal::serialize(G3D::TextOutput& t) const { + switch (m_type) { + case NIL: + t.writeSymbol("Nil"); + break; + + case NUMBER: + t.printf("%g", *(double*)m_value); + break; + + case BOOLEAN: + t.writeBoolean(*(bool*)m_value); + break; + + case STRING: + t.writeString(*(std::string*)m_value); + break; + + case RECT2D: + t.printf("R(%g, %g, %g, %g)", ((Rect2D*)m_value)->x0(), ((Rect2D*)m_value)->y0(), + ((Rect2D*)m_value)->width(), ((Rect2D*)m_value)->height()); + break; + + case AABOX: + t.printf("AAB(V3(%g, %g, %g), V3(%g, %g, %g))", + aabox().low().x, + aabox().low().y, + aabox().low().z, + aabox().high().x, + aabox().high().y, + aabox().high().z); + break; + + case VECTOR2: + t.printf("V2(%g, %g)", ((Vector2*)m_value)->x, ((Vector2*)m_value)->y); + break; + + case VECTOR3: + t.printf("V3(%g, %g, %g)", ((Vector3*)m_value)->x, ((Vector3*)m_value)->y, ((Vector3*)m_value)->z); + break; + + case VECTOR4: + t.printf("V4(%g, %g, %g, %g)", ((Vector4*)m_value)->x, ((Vector4*)m_value)->y, ((Vector4*)m_value)->z, ((Vector4*)m_value)->w); + break; + + case MATRIX2: + { + const Matrix2& m = *(Matrix2*)m_value; + t.printf("M2(\n"); + t.pushIndent(); + t.printf("%10.5f, %10.5f,\n%10.5f, %10.5f)", + m[0][0], m[0][1], + m[1][0], m[1][1]); + t.popIndent(); + } + break; + + case MATRIX3: + { + const Matrix3& m = *(Matrix3*)m_value; + t.printf("M3(\n"); + t.pushIndent(); + t.printf("%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f)", + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + t.popIndent(); + } + break; + + case MATRIX4: + { + const Matrix4& m = *(Matrix4*)m_value; + t.printf("M4(\n"); + t.pushIndent(); + t.printf( + "%10.5f, %10.5f, %10.5f, %10.5f,\n" + "%10.5f, %10.5f, %10.5f, %10.5f,\n" + "%10.5f, %10.5f, %10.5f, %10.5f,\n" + "%10.5f, %10.5f, %10.5f, %10.5f)", + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3]); + t.popIndent(); + } + break; + + case QUAT: + t.printf("Q(%g, %g, %g, %g)", ((Quat*)m_value)->x, ((Quat*)m_value)->y, ((Quat*)m_value)->z, ((Quat*)m_value)->w); + break; + + case COORDINATEFRAME: + { + const CoordinateFrame& c = *(CoordinateFrame*)m_value; + float x,y,z,yaw,pitch,roll; + c.getXYZYPRDegrees(x,y,z,yaw,pitch,roll); + t.printf("CF(V3(%g,%g,%g), %g, %g, %g)", x, y, z, yaw, pitch, roll); + /* + t.pushIndent(); + t.printf( + "CF(\n%10.5f, %10.5f, %10.5f, %10.5f,\n" + "%10.5f, %10.5f, %10.5f, %10.5f,\n" + "%10.5f, %10.5f, %10.5f, %10.5f)", + c.rotation[0][0], c.rotation[0][1], c.rotation[0][2], c.translation.x, + c.rotation[1][0], c.rotation[1][1], c.rotation[1][2], c.translation.y, + c.rotation[2][0], c.rotation[2][1], c.rotation[2][2], c.translation.z); + t.popIndent(); + */ + } + break; + + case COLOR1: + t.printf("C1(%g)", ((Color1*)m_value)->value); + break; + + case COLOR3: + t.printf("C3(%g, %g, %g)", ((Color3*)m_value)->r, ((Color3*)m_value)->g, ((Color3*)m_value)->b); + break; + + case COLOR4: + t.printf("C4(%g, %g, %g, %g)", ((Color4*)m_value)->r, ((Color4*)m_value)->g, ((Color4*)m_value)->b, ((Color4*)m_value)->a); + break; + + case ARRAY: + { + const Array<AnyVal>& a = *(Array<AnyVal>*)m_value; + t.printf("[\n"); + t.pushIndent(); + for (int i = 0; i < a.size(); ++i) { + a[i].serialize(t); + if (i != a.size() - 1) { + t.printf(", \n"); + } + } + t.printf("]"); + t.popIndent(); + } + break; + + case TABLE: + { + const Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value; + t.printf("{\n"); + t.pushIndent(); + Table<std::string, AnyVal>::Iterator i = a.begin(); + const Table<std::string, AnyVal>::Iterator end = a.end(); + while (i != end) { + // Quote names that are not legal C++ identifiers + if (! legalIdentifier(i->key)) { + t.printf("'%s' ", i->key.c_str()); + } else { + t.writeSymbol(i->key); + } + t.printf("= "); + + i->value.serialize(t); + + if (i != end) { + t.printf("\n"); + } + ++i; + } + t.popIndent(); + t.printf("}"); + } + break; + + default: + debugAssertM(false, "Internal error: no serialize method for this type."); + } +} + + +std::string AnyVal::toString() const { + TextOutput t; + serialize(t); + std::string s; + t.commitString(s); + return s; +} + +/* +void AnyVal::serialize(G3D::BinaryOutput& t) const { + alwaysAssertM(false, "TODO"); +} +*/ + +void AnyVal::deserialize(G3D::TextInput& t) { + deleteValue(); + m_type = NIL; + m_value = NULL; + + if (! t.hasMore()) { + return; + } + + switch (t.peek().type()) { + case Token::END: + // should never get here because of the hasMore check above + return; + break; + + case Token::NUMBER: + m_type = NUMBER; + m_value = new double(t.readNumber()); + break; + + case Token::STRING: + m_type = STRING; + m_value = new std::string(t.readString()); + break; + + case Token::BOOLEAN: + m_type = BOOLEAN; + m_value = new bool(t.readBoolean()); + break; + + case Token::SYMBOL: + { + std::string s = t.readSymbol(); + if (s == "NIL") { + break; + + } else if (s == "true") { + + m_type = BOOLEAN; + m_value = new bool(true); + + } else if (s == "false") { + + m_type = BOOLEAN; + m_value = new bool(false); + + } else if (s == "R") { + + m_type = RECT2D; + t.readSymbol("("); + float x,y,w,h; + x = (float)t.readNumber(); + t.readSymbol(","); + y = (float)t.readNumber(); + t.readSymbol(","); + w = (float)t.readNumber(); + t.readSymbol(","); + h = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Rect2D(Rect2D::xywh(x, y, w, h)); + + } else if (s == "AAB") { + + m_type = AABOX; + Vector3 v[2]; + t.readSymbol("("); + for (int i = 0; i < 2; ++i) { + t.readSymbols("V3", "("); + v[i].x = (float)t.readNumber(); + t.readSymbol(","); + v[i].y = (float)t.readNumber(); + t.readSymbol(","); + v[i].z = (float)t.readNumber(); + t.readSymbol(","); + if (i == 0) { + t.readSymbol(","); + } + } + t.readSymbol(")"); + m_value = new AABox(v[0], v[1]); + + } else if (s == "V2") { + + t.readSymbol("("); + Vector2 v; + v.x = (float)t.readNumber(); + t.readSymbol(","); + v.y = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Vector2(v); + m_type = VECTOR2; + + } else if (s == "V3") { + + t.readSymbol("("); + Vector3 v; + v.x = (float)t.readNumber(); + t.readSymbol(","); + v.y = (float)t.readNumber(); + t.readSymbol(","); + v.z = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Vector3(v); + m_type = VECTOR3; + + } else if (s == "V4") { + + t.readSymbol("("); + Vector4 v; + v.x = (float)t.readNumber(); + t.readSymbol(","); + v.y = (float)t.readNumber(); + t.readSymbol(","); + v.z = (float)t.readNumber(); + t.readSymbol(","); + v.w = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Vector4(v); + m_type = VECTOR4; + + } else if (s == "M2") { + + t.readSymbol("("); + Matrix2 m; + for (int r = 0; r < 2; ++r) { + for (int c = 0; c < 2; ++c) { + m[r][c] = (float)t.readNumber(); + if ((c != 1) || (r != 1)) { + t.readSymbol(","); + } + } + } + t.readSymbol(")"); + m_value = new Matrix2(m); + m_type = MATRIX2; + + } else if (s == "M3") { + + t.readSymbol("("); + Matrix3 m; + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + m[r][c] = (float)t.readNumber(); + if ((c != 2) || (r != 2)) { + t.readSymbol(","); + } + } + } + t.readSymbol(")"); + m_value = new Matrix3(m); + m_type = MATRIX3; + + } else if (s == "M4") { + + t.readSymbol("("); + Matrix4 m; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + m[r][c] = (float)t.readNumber(); + if ((c != 3) || (r != 3)) { + t.readSymbol(","); + } + } + } + t.readSymbol(")"); + m_value = new Matrix4(m); + m_type = MATRIX4; + + } else if (s == "Q") { + + t.readSymbol("("); + Quat q; + q.x = (float)t.readNumber(); + t.readSymbol(","); + q.y = (float)t.readNumber(); + t.readSymbol(","); + q.z = (float)t.readNumber(); + t.readSymbol(","); + q.w = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Quat(q); + m_type = QUAT; + + } else if (s == "CF") { + + t.readSymbol("("); + CoordinateFrame m; + if (t.peek().type() == Token::SYMBOL) { + // Angle format + float x, y, z, yaw, roll, pitch; + t.readSymbols("V3", "("); + x = (float)t.readNumber(); + t.readSymbol(","); + y = (float)t.readNumber(); + t.readSymbol(","); + z = (float)t.readNumber(); + t.readSymbols(")", ","); + yaw = (float)t.readNumber(); + t.readSymbol(","); + pitch = (float)t.readNumber(); + roll = 0; + if (t.peek().string() == ",") { + t.readSymbol(","); + roll = (float)t.readNumber(); + } + m = CoordinateFrame::fromXYZYPRDegrees(x, y, z, yaw, pitch, roll); + } else { + // Matrix format + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + m.rotation[r][c] = (float)t.readNumber(); + } + m.translation[r] = (float)t.readNumber(); + if (r != 2) { + t.readSymbol(","); + } + } + } + t.readSymbol(")"); + m_value = new CoordinateFrame(m); + m_type = COORDINATEFRAME; + + } else if (s == "C1") { + + t.readSymbol("("); + float v = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Color1(v); + m_type = COLOR1; + + } else if (s == "C3") { + + t.readSymbol("("); + Color3 c; + c.r = (float)t.readNumber(); + t.readSymbol(","); + c.g = (float)t.readNumber(); + t.readSymbol(","); + c.b = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Color3(c); + m_type = COLOR3; + + } else if (s == "C4") { + + t.readSymbol("("); + Color4 c; + c.r = (float)t.readNumber(); + t.readSymbol(","); + c.g = (float)t.readNumber(); + t.readSymbol(","); + c.b = (float)t.readNumber(); + t.readSymbol(","); + c.a = (float)t.readNumber(); + t.readSymbol(")"); + m_value = new Color4(c); + m_type = COLOR4; + + } else if (s == "[") { + + // Array + m_type = ARRAY; + m_value = new Array<AnyVal>(); + m_referenceCount = new int(1); + Array<AnyVal>& a = *(Array<AnyVal>*)m_value; + + Token peek = t.peek(); + while ((peek.type() != Token::SYMBOL) || (peek.string() != "]")) { + // Avoid copying large objects + a.next().deserialize(t); + + peek = t.peek(); + if (peek.type() != Token::SYMBOL) { + throw CorruptText("Expected ',' or ']'", peek); + } else if (peek.string() == ",") { + t.readSymbol(","); + } else if (peek.string() != "]") { + throw CorruptText("Missing ']'", peek); + } + } + t.readSymbol("]"); + + } else if (s == "{") { + + // Table + m_type = TABLE; + m_value = new Table<std::string, AnyVal>(); + m_referenceCount = new int(1); + Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value; + + Token peek = t.peek(); + while ((peek.type() != Token::SYMBOL) || (peek.string() != "}")) { + + std::string key; + // Get the name + if (peek.type() == Token::SYMBOL) { + key = t.readSymbol(); + } else if (peek.extendedType() == Token::SINGLE_QUOTED_TYPE) { + key = t.readString(); + } else { + throw CorruptText("Expected name inside table", peek); + } + + t.readSymbol("="); + + // Avoid copying large values + a.set(key, AnyVal()); + a[key].deserialize(t); + + peek = t.peek(); + if ((peek.type() != Token::SYMBOL) && (peek.extendedType() == Token::SINGLE_QUOTED_TYPE)) { + throw CorruptText("Missing expected name or '}'", peek); + } + } + t.readSymbol("}"); + + } else { + throw CorruptText("Invalid value type.", t.peek()); + } // dispatch on symbol type + } // scope + break; + } +} + +/* +void AnyVal::deserialize(G3D::BinaryInput& t) { + alwaysAssertM(false, "TODO"); +} +*/ + + +AnyVal& AnyVal::operator[](const char* key) { + return this->operator[]((std::string)key); +} + + +const AnyVal& AnyVal::operator[](const char* key) const { + return this->operator[]((std::string)key); +} + + +AnyVal& AnyVal::operator[](const std::string& key) { + if (m_type != TABLE) { + throw WrongType(TABLE, m_type); + } + + makeMutable(); + + Table<std::string, AnyVal>& t = *(Table<std::string, AnyVal>*)m_value; + + if (! t.containsKey(key)) { + t.set(key, AnyVal()); + } + + return t[key]; +} + + +const AnyVal& AnyVal::operator[](const std::string& key) const { + if (m_type != TABLE) { + throw WrongType(TABLE, m_type); + } + + const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value; + + if (! t.containsKey(key)) { + throw KeyNotFound(key); + } + + return t[key]; +} + + +void AnyVal::append(const AnyVal& v) { + if (m_type != ARRAY) { + throw WrongType(ARRAY, m_type); + } + makeMutable(); + + Array<AnyVal>& a = *(Array<AnyVal>*)m_value; + a.append(v); +} + + +void AnyVal::getKeys(Array<std::string>& keys) const { + if (m_type != TABLE) { + throw WrongType(TABLE, m_type); + } + + const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value; + t.getKeys(keys); +} + + +int AnyVal::size() const { + switch (m_type) { + case TABLE: + { + const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value; + return t.size(); + } + + case ARRAY: + { + const Array<AnyVal>& a = *(Array<AnyVal>*)m_value; + return a.size(); + } + + default: + throw WrongType(ARRAY, m_type); + } +} + + +AnyVal& AnyVal::operator[](int i) { + if (m_type != ARRAY) { + throw WrongType(ARRAY, m_type); + } + makeMutable(); + + Array<AnyVal>& a = *(Array<AnyVal>*)m_value; + + if (i < 0) { + throw IndexOutOfBounds(i, a.size()); + } + + if (a.size() <= i) { + a.resize(i + 1); + } + + return a[i]; +} + + +const AnyVal& AnyVal::operator[](int i) const { + if (m_type != ARRAY) { + throw WrongType(ARRAY, m_type); + } + + const Array<AnyVal>& a = *(Array<AnyVal>*)m_value; + + if (a.size() <= i || i < 0) { + throw IndexOutOfBounds(i, a.size()); + } + + return a[i]; +} + + +void AnyVal::makeMutable() { + if (*m_referenceCount > 1) { + // This is a shared instance + --(*m_referenceCount); + m_referenceCount = new int(1); + m_value = copyValue(); + } +} + +bool AnyVal::boolean() const { + if (m_type != BOOLEAN) { + throw WrongType(BOOLEAN, m_type); + } + + return *(bool*)m_value; +} + + +bool AnyVal::boolean(bool defaultVal) const { + if (m_type != BOOLEAN) { + return defaultVal; + } + + return *(bool*)m_value; +} + + +const std::string& AnyVal::string() const { + if (m_type != STRING) { + throw WrongType(STRING, m_type); + } + + return *(std::string*)m_value; +} + + +const std::string& AnyVal::string(const std::string& defaultVal) const { + if (m_type != STRING) { + return defaultVal; + } else { + return *(std::string*)m_value; + } +} + + +double AnyVal::number() const { + if (m_type != NUMBER) { + throw WrongType(NUMBER, m_type); + } + + return *(double*)m_value; +} + + +double AnyVal::number(double defaultVal) const { + if (m_type != NUMBER) { + return defaultVal; + } else { + return *(double*)m_value; + } +} + + +const Rect2D& AnyVal::rect2D() const { + if (m_type != RECT2D) { + throw WrongType(RECT2D, m_type); + } + + return *(Rect2D*)m_value; +} + + +const Rect2D& AnyVal::rect2D(const Rect2D& defaultVal) const { + if (m_type != RECT2D) { + return defaultVal; + } else { + return *(Rect2D*)m_value; + } +} + + +const AABox& AnyVal::aabox() const { + if (m_type != AABOX) { + throw WrongType(AABOX, m_type); + } + + return *(AABox*)m_value; +} + + +const AABox& AnyVal::aabox(const AABox& defaultVal) const { + if (m_type != AABOX) { + return defaultVal; + } else { + return *(AABox*)m_value; + } +} + + +const Color1& AnyVal::color1() const { + if (m_type != COLOR1) { + throw WrongType(COLOR1, m_type); + } + + return *(Color1*)m_value; +} + + +const Color1& AnyVal::color1(const Color1& defaultVal) const { + if (m_type != COLOR1) { + return defaultVal; + } else { + return *(Color1*)m_value; + } +} + + +const Color3& AnyVal::color3() const { + if (m_type != COLOR3) { + throw WrongType(COLOR3, m_type); + } + + return *(Color3*)m_value; +} + + +const Color3& AnyVal::color3(const Color3& defaultVal) const { + if (m_type != COLOR3) { + return defaultVal; + } else { + return *(Color3*)m_value; + } +} + + +const Color4& AnyVal::color4() const { + if (m_type != COLOR4) { + throw WrongType(COLOR4, m_type); + } + + return *(Color4*)m_value; +} + + +const Color4& AnyVal::color4(const Color4& defaultVal) const { + if (m_type != COLOR4) { + return defaultVal; + } else { + return *(Color4*)m_value; + } +} + + +const Vector2& AnyVal::vector2() const { + if (m_type != VECTOR2) { + throw WrongType(VECTOR2, m_type); + } + + return *(Vector2*)m_value; +} + + +const Vector2& AnyVal::vector2(const Vector2& defaultVal) const { + if (m_type != VECTOR2) { + return defaultVal; + } else { + return *(Vector2*)m_value; + } +} + + +const Vector3& AnyVal::vector3() const { + if (m_type != VECTOR3) { + throw WrongType(VECTOR3, m_type); + } + + return *(Vector3*)m_value; +} + + +const Vector3& AnyVal::vector3(const Vector3& defaultVal) const { + if (m_type != VECTOR3) { + return defaultVal; + } else { + return *(Vector3*)m_value; + } +} + + +const Vector4& AnyVal::vector4() const { + if (m_type != VECTOR4) { + throw WrongType(VECTOR4, m_type); + } + + return *(Vector4*)m_value; +} + + +const Vector4& AnyVal::vector4(const Vector4& defaultVal) const { + if (m_type != VECTOR4) { + return defaultVal; + } else { + return *(Vector4*)m_value; + } +} + + +const CoordinateFrame& AnyVal::coordinateFrame() const { + if (m_type != COORDINATEFRAME) { + throw WrongType(COORDINATEFRAME, m_type); + } + + return *(CoordinateFrame*)m_value; +} + + +const CoordinateFrame& AnyVal::coordinateFrame(const CoordinateFrame& defaultVal) const { + if (m_type != COORDINATEFRAME) { + return defaultVal; + } else { + return *(CoordinateFrame*)m_value; + } +} + +const Matrix2& AnyVal::matrix2(const Matrix2& defaultVal) const { + if (m_type != MATRIX2) { + return defaultVal; + } else { + return *(Matrix2*)m_value; + } +} + + +const Matrix2& AnyVal::matrix2() const { + if (m_type != MATRIX2) { + throw WrongType(MATRIX2, m_type); + } + + return *(Matrix2*)m_value; +} + + +const Matrix3& AnyVal::matrix3(const Matrix3& defaultVal) const { + if (m_type != MATRIX3) { + return defaultVal; + } else { + return *(Matrix3*)m_value; + } +} + + +const Matrix3& AnyVal::matrix3() const { + if (m_type != MATRIX3) { + throw WrongType(MATRIX3, m_type); + } + + return *(Matrix3*)m_value; +} + + +const Matrix4& AnyVal::matrix4(const Matrix4& defaultVal) const { + if (m_type != MATRIX4) { + return defaultVal; + } else { + return *(Matrix4*)m_value; + } +} + + +const Matrix4& AnyVal::matrix4() const { + if (m_type != MATRIX4) { + throw WrongType(MATRIX4, m_type); + } + + return *(Matrix4*)m_value; +} + + +const Quat& AnyVal::quat(const Quat& defaultVal) const { + if (m_type != QUAT) { + return defaultVal; + } else { + return *(Quat*)m_value; + } +} + + +const Quat& AnyVal::quat() const { + if (m_type != QUAT) { + throw WrongType(QUAT, m_type); + } + + return *(Quat*)m_value; +} + + +const AnyVal& AnyVal::get(const std::string& key, const AnyVal& defaultVal) const { + if (m_type != TABLE) { + return defaultVal; + } + + const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value; + + if (t.containsKey(key)) { + return t[key]; + } else { + return defaultVal; + } +} + + +const AnyVal& AnyVal::get(const std::string& key) const { + if (m_type != TABLE) { + throw WrongType(TABLE, m_type); + } + + const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value; + + if (t.containsKey(key)) { + return t[key]; + } else { + throw KeyNotFound(key); + } +} + + +const AnyVal& AnyVal::get(int i, const AnyVal& defaultVal) const { + if (m_type != ARRAY) { + return defaultVal; + } + + const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value; + + if ((i >= 0) && (i < a.size())) { + return a[i]; + } else { + return defaultVal; + } +} + + +const AnyVal& AnyVal::get(int i) const { + if (m_type != ARRAY) { + throw WrongType(ARRAY, m_type); + } + + const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value; + + if ((i >= 0) && (i < a.size())) { + return a[i]; + } else { + throw IndexOutOfBounds(i, a.size()); + } +} + +} diff --git a/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp b/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp new file mode 100644 index 00000000000..a30ec0644ea --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp @@ -0,0 +1,81 @@ +/** + @file BinaryFormat.cpp + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2005-06-10 + @edited 2005-06-10 + */ + +#include "G3D/BinaryFormat.h" + +namespace G3D { + +int32 byteSize(BinaryFormat f) { + switch (f) { + case BOOL8_BINFMT: + case UINT8_BINFMT: + case INT8_BINFMT: + return 1; + + case UINT16_BINFMT: + case INT16_BINFMT: + return 2; + + case FLOAT16_BINFMT: + return 2; + + case UINT32_BINFMT: + case INT32_BINFMT: + case FLOAT32_BINFMT: + return 4; + + case FLOAT64_BINFMT: + case UINT64_BINFMT: + case INT64_BINFMT: + return 8; + + case INT128_BINFMT: + case UINT128_BINFMT: + return 16; + + case VECTOR2_BINFMT: + return 2 * 4; + + case VECTOR2INT16_BINFMT: + return 2 * 2; + + case VECTOR3_BINFMT: + return 3 * 4; + + case VECTOR3INT16_BINFMT: + return 3 * 2; + + case VECTOR4_BINFMT: + return 4 * 4; + + case VECTOR4INT16_BINFMT: + return 4 * 4; + + case COLOR3_BINFMT: + return 3 * 4; + + case COLOR3UINT8_BINFMT: + return 3 * 1; + + case COLOR3INT16_BINFMT: + return 3 * 2; + + case COLOR4_BINFMT: + return 4 * 4; + + case COLOR4UINT8_BINFMT: + return 4 * 1; + + case COLOR4INT16_BINFMT: + return 4 * 2; + + default: + return -1; + } +} +} diff --git a/externals/g3dlite/G3D.lib/source/BinaryInput.cpp b/externals/g3dlite/G3D.lib/source/BinaryInput.cpp new file mode 100644 index 00000000000..65a9976fe04 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/BinaryInput.cpp @@ -0,0 +1,568 @@ +/** + @file BinaryInput.cpp + + @author Morgan McGuire, graphics3d.com + Copyright 2001-2007, Morgan McGuire. All rights reserved. + + @created 2001-08-09 + @edited 2005-02-24 + + + <PRE> + { + BinaryOutput b("c:/tmp/test.b", BinaryOutput::LITTLE_ENDIAN); + + float f = 3.1415926; + int i = 1027221; + std::string s = "Hello World!"; + + b.writeFloat32(f); + b.writeInt32(i); + b.writeString(s); + b.commit(); + + + BinaryInput in("c:/tmp/test.b", BinaryInput::LITTLE_ENDIAN); + + debugAssert(f == in.readFloat32()); + int ii = in.readInt32(); + debugAssert(i == ii); + debugAssert(s == in.readString()); + } + </PRE> + */ + +#include "G3D/platform.h" +#include "G3D/BinaryInput.h" +#include "G3D/Array.h" +#include "G3D/fileutils.h" +#include "G3D/Log.h" +#include <zlib.h> + +#include <cstring> + +namespace G3D { + +void BinaryInput::readBool8(std::vector<bool>& out, int64 n) { + out.resize((int)n); + // std::vector optimizes bool in a way that prevents fast reading + for (int64 i = 0; i < n ; ++i) { + out[i] = readBool8(); + } +} + + +void BinaryInput::readBool8(Array<bool>& out, int64 n) { + out.resize(n); + readBool8(out.begin(), n); +} + + +#define IMPLEMENT_READER(ucase, lcase)\ +void BinaryInput::read##ucase(std::vector<lcase>& out, int64 n) {\ + out.resize(n);\ + read##ucase(&out[0], n);\ +}\ +\ +\ +void BinaryInput::read##ucase(Array<lcase>& out, int64 n) {\ + out.resize(n);\ + read##ucase(out.begin(), n);\ +} + + +IMPLEMENT_READER(UInt8, uint8) +IMPLEMENT_READER(Int8, int8) +IMPLEMENT_READER(UInt16, uint16) +IMPLEMENT_READER(Int16, int16) +IMPLEMENT_READER(UInt32, uint32) +IMPLEMENT_READER(Int32, int32) +IMPLEMENT_READER(UInt64, uint64) +IMPLEMENT_READER(Int64, int64) +IMPLEMENT_READER(Float32, float32) +IMPLEMENT_READER(Float64, float64) + +#undef IMPLEMENT_READER + +// Data structures that are one byte per element can be +// directly copied, regardles of endian-ness. +#define IMPLEMENT_READER(ucase, lcase)\ +void BinaryInput::read##ucase(lcase* out, int64 n) {\ + if (sizeof(lcase) == 1) {\ + readBytes(out, n);\ + } else {\ + for (int64 i = 0; i < n ; ++i) {\ + out[i] = read##ucase();\ + }\ + }\ +} + +IMPLEMENT_READER(Bool8, bool) +IMPLEMENT_READER(UInt8, uint8) +IMPLEMENT_READER(Int8, int8) + +#undef IMPLEMENT_READER + + +#define IMPLEMENT_READER(ucase, lcase)\ +void BinaryInput::read##ucase(lcase* out, int64 n) {\ + if (m_swapBytes) {\ + for (int64 i = 0; i < n; ++i) {\ + out[i] = read##ucase();\ + }\ + } else {\ + readBytes(out, sizeof(lcase) * n);\ + }\ +} + + +IMPLEMENT_READER(UInt16, uint16) +IMPLEMENT_READER(Int16, int16) +IMPLEMENT_READER(UInt32, uint32) +IMPLEMENT_READER(Int32, int32) +IMPLEMENT_READER(UInt64, uint64) +IMPLEMENT_READER(Int64, int64) +IMPLEMENT_READER(Float32, float32) +IMPLEMENT_READER(Float64, float64) + +#undef IMPLEMENT_READER + +void BinaryInput::loadIntoMemory(int64 startPosition, int64 minLength) { + // Load the next section of the file + debugAssertM(m_filename != "<memory>", "Read past end of file."); + + int64 absPos = m_alreadyRead + m_pos; + + if (m_bufferLength < minLength) { + // The current buffer isn't big enough to hold the chunk we want to read. + // This happens if there was little memory available during the initial constructor + // read but more memory has since been freed. + m_bufferLength = minLength; + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::realloc(m_buffer, m_bufferLength); + if (m_buffer == NULL) { + throw "Tried to read a larger memory chunk than could fit in memory. (2)"; + } + } + + m_alreadyRead = startPosition; + +# ifdef G3D_WIN32 + FILE* file = fopen(m_filename.c_str(), "rb"); + debugAssert(file); + int ret = fseek(file, (off_t)m_alreadyRead, SEEK_SET); + debugAssert(ret == 0); + size_t toRead = (size_t)G3D::min(m_bufferLength, m_length - m_alreadyRead); + ret = fread(m_buffer, 1, toRead, file); + debugAssert(ret == toRead); + fclose(file); + file = NULL; + +# else + FILE* file = fopen(m_filename.c_str(), "rb"); + debugAssert(file); + int ret = fseeko(file, (off_t)m_alreadyRead, SEEK_SET); + debugAssert(ret == 0); + size_t toRead = (size_t)G3D::min<int64>(m_bufferLength, m_length - m_alreadyRead); + ret = fread(m_buffer, 1, toRead, file); + debugAssert((size_t)ret == (size_t)toRead); + fclose(file); + file = NULL; +# endif + + m_pos = absPos - m_alreadyRead; + debugAssert(m_pos >= 0); +} + + + +const bool BinaryInput::NO_COPY = false; + +static bool needSwapBytes(G3DEndian fileEndian) { + return (fileEndian != System::machineEndian()); +} + + +/** Helper used by the constructors for decompression */ +static uint32 readUInt32(const uint8* data, bool swapBytes) { + if (swapBytes) { + uint8 out[4]; + out[0] = data[3]; + out[1] = data[2]; + out[2] = data[1]; + out[3] = data[0]; + return *((uint32*)out); + } else { + return *((uint32*)data); + } +} + + +void BinaryInput::setEndian(G3DEndian e) { + m_fileEndian = e; + m_swapBytes = needSwapBytes(m_fileEndian); +} + + +BinaryInput::BinaryInput( + const uint8* data, + int64 dataLen, + G3DEndian dataEndian, + bool compressed, + bool copyMemory) : + m_filename("<memory>"), + m_bitPos(0), + m_bitString(0), + m_beginEndBits(0), + m_alreadyRead(0), + m_bufferLength(0), + m_pos(0) { + + m_freeBuffer = copyMemory || compressed; + + setEndian(dataEndian); + + if (compressed) { + // Read the decompressed size from the first 4 bytes + m_length = G3D::readUInt32(data, m_swapBytes); + + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::alignedMalloc(m_length, 16); + + unsigned long L = m_length; + // Decompress with zlib + int64 result = uncompress(m_buffer, (unsigned long*)&L, data + 4, dataLen - 4); + m_length = L; + m_bufferLength = L; + debugAssert(result == Z_OK); (void)result; + + } else { + m_length = dataLen; + m_bufferLength = m_length; + if (! copyMemory) { + debugAssert(!m_freeBuffer); + m_buffer = const_cast<uint8*>(data); + } else { + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::alignedMalloc(m_length, 16); + System::memcpy(m_buffer, data, dataLen); + } + } +} + + +BinaryInput::BinaryInput( + const std::string& filename, + G3DEndian fileEndian, + bool compressed) : + m_filename(filename), + m_bitPos(0), + m_bitString(0), + m_beginEndBits(0), + m_alreadyRead(0), + m_length(0), + m_bufferLength(0), + m_buffer(NULL), + m_pos(0), + m_freeBuffer(true) { + + setEndian(fileEndian); + + // Update global file tracker + _internal::currentFilesUsed.insert(m_filename); + + + if (! fileExists(m_filename, false)) { + std::string zipfile; + std::string internalfile; + if (zipfileExists(m_filename, zipfile, internalfile)) { + // Load from zipfile + void* v; + size_t s; + zipRead(filename, v, s); + m_buffer = reinterpret_cast<uint8*>(v); + m_bufferLength = m_length = s; + if (compressed) { + decompress(); + } + m_freeBuffer = true; + } else { + Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str()); + } + return; + } + + // Figure out how big the file is and verify that it exists. + m_length = fileLength(m_filename); + + // Read the file into memory + FILE* file = fopen(m_filename.c_str(), "rb"); + + if (! file || (m_length == -1)) { + throw format("File not found: \"%s\"", m_filename.c_str()); + return; + } + + if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) { + // Read only a subset of the file so we don't consume + // all available memory. + m_bufferLength = INITIAL_BUFFER_LENGTH; + } else { + // Either the length is fine or the file is compressed + // and requires us to read the whole thing for zlib. + m_bufferLength = m_length; + } + + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16); + if (m_buffer == NULL) { + if (compressed) { + throw "Not enough memory to load compressed file. (1)"; + } + + // Try to allocate a small array; not much memory is available. + // Give up if we can't allocate even 1k. + while ((m_buffer == NULL) && (m_bufferLength > 1024)) { + m_bufferLength /= 2; + m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16); + } + } + debugAssert(m_buffer); + + fread(m_buffer, m_bufferLength, sizeof(int8), file); + fclose(file); + file = NULL; + + if (compressed) { + if (m_bufferLength != m_length) { + throw "Not enough memory to load compressed file. (2)"; + } + + decompress(); + } +} + +void BinaryInput::decompress() { + // Decompress + // Use the existing buffer as the source, allocate + // a new buffer to use as the destination. + + int64 tempLength = m_length; + m_length = G3D::readUInt32(m_buffer, m_swapBytes); + + // The file couldn't have better than 500:1 compression + alwaysAssertM(m_length < m_bufferLength * 500, "Compressed file header is corrupted"); + + uint8* tempBuffer = m_buffer; + m_buffer = (uint8*)System::alignedMalloc(m_length, 16); + + debugAssert(m_buffer); + debugAssert(isValidHeapPointer(tempBuffer)); + debugAssert(isValidHeapPointer(m_buffer)); + + unsigned long L = m_length; + int64 result = uncompress(m_buffer, &L, tempBuffer + 4, tempLength - 4); + m_length = L; + m_bufferLength = m_length; + + debugAssertM(result == Z_OK, "BinaryInput/zlib detected corruption in " + m_filename); + (void)result; + + System::alignedFree(tempBuffer); +} + + +void BinaryInput::readBytes(void* bytes, int64 n) { + prepareToRead(n); + debugAssert(isValidPointer(bytes)); + + memcpy(bytes, m_buffer + m_pos, n); + m_pos += n; +} + + +BinaryInput::~BinaryInput() { + + if (m_freeBuffer) { + System::alignedFree(m_buffer); + } + m_buffer = NULL; +} + + +uint64 BinaryInput::readUInt64() { + prepareToRead(8); + uint8 out[8]; + + if (m_swapBytes) { + out[0] = m_buffer[m_pos + 7]; + out[1] = m_buffer[m_pos + 6]; + out[2] = m_buffer[m_pos + 5]; + out[3] = m_buffer[m_pos + 4]; + out[4] = m_buffer[m_pos + 3]; + out[5] = m_buffer[m_pos + 2]; + out[6] = m_buffer[m_pos + 1]; + out[7] = m_buffer[m_pos + 0]; + } else { + *(uint64*)out = *(uint64*)(m_buffer + m_pos); + } + + m_pos += 8; + return *(uint64*)out; +} + + +std::string BinaryInput::readString(int64 n) { + prepareToRead(n); + debugAssertM((m_pos + n) <= m_length, "Read past end of file"); + + char *s = (char*)System::alignedMalloc(n + 1, 16); + assert(s != NULL); + + memcpy(s, m_buffer + m_pos, n); + // There may not be a null, so make sure + // we add one. + s[n] = '\0'; + + std::string out = s; + System::alignedFree(s); + s = NULL; + + m_pos += n; + + return out; + +} + + +std::string BinaryInput::readString() { + int64 n = 0; + + if ((m_pos + m_alreadyRead + n) < (m_length - 1)) { + prepareToRead(1); + } + + if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) && + (m_buffer[m_pos + n] != '\0')) { + + ++n; + while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) && + (m_buffer[m_pos + n] != '\0')) { + + prepareToRead(1); + ++n; + } + } + + // Consume NULL + ++n; + + return readString(n); +} + + +std::string BinaryInput::readStringEven() { + std::string x = readString(); + if (hasMore() && (G3D::isOdd(x.length() + 1))) { + skip(1); + } + return x; +} + + +std::string BinaryInput::readString32() { + int len = readUInt32(); + return readString(len); +} + + +Vector4 BinaryInput::readVector4() { + float x = readFloat32(); + float y = readFloat32(); + float z = readFloat32(); + float w = readFloat32(); + return Vector4(x, y, z, w); +} + + +Vector3 BinaryInput::readVector3() { + float x = readFloat32(); + float y = readFloat32(); + float z = readFloat32(); + return Vector3(x, y, z); +} + + +Vector2 BinaryInput::readVector2() { + float x = readFloat32(); + float y = readFloat32(); + return Vector2(x, y); +} + + +Color4 BinaryInput::readColor4() { + float r = readFloat32(); + float g = readFloat32(); + float b = readFloat32(); + float a = readFloat32(); + return Color4(r, g, b, a); +} + + +Color3 BinaryInput::readColor3() { + float r = readFloat32(); + float g = readFloat32(); + float b = readFloat32(); + return Color3(r, g, b); +} + + +void BinaryInput::beginBits() { + debugAssert(m_beginEndBits == 0); + m_beginEndBits = 1; + m_bitPos = 0; + + debugAssertM(hasMore(), "Can't call beginBits when at the end of a file"); + m_bitString = readUInt8(); +} + + +uint32 BinaryInput::readBits(int numBits) { + debugAssert(m_beginEndBits == 1); + + uint32 out = 0; + + const int total = numBits; + while (numBits > 0) { + if (m_bitPos > 7) { + // Consume a new byte for reading. We do this at the beginning + // of the loop so that we don't try to read past the end of the file. + m_bitPos = 0; + m_bitString = readUInt8(); + } + + // Slide the lowest bit of the bitString into + // the correct position. + out |= (m_bitString & 1) << (total - numBits); + + // Shift over to the next bit + m_bitString = m_bitString >> 1; + ++m_bitPos; + --numBits; + } + + return out; +} + + +void BinaryInput::endBits() { + debugAssert(m_beginEndBits == 1); + if (m_bitPos == 0) { + // Put back the last byte we read + --m_pos; + } + m_beginEndBits = 0; + m_bitPos = 0; +} + +} diff --git a/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp b/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp new file mode 100644 index 00000000000..8fcc30e548f --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp @@ -0,0 +1,518 @@ +/** + @file BinaryOutput.cpp + + @author Morgan McGuire, graphics3d.com + Copyright 2002-2007, Morgan McGuire, All rights reserved. + + @created 2002-02-20 + @edited 2008-01-07 + */ + +#include "G3D/platform.h" +#include "G3D/BinaryOutput.h" +#include "G3D/fileutils.h" +#include "G3D/stringutils.h" +#include "G3D/Array.h" +#include <zlib.h> + +#include <cstring> + +// Largest memory buffer that the system will use for writing to +// disk. After this (or if the system runs out of memory) +// chunks of the file will be dumped to disk. +// +// Currently 400 MB +#define MAX_BINARYOUTPUT_BUFFER_SIZE 400000000 + +namespace G3D { + +void BinaryOutput::writeBool8(const std::vector<bool>& out, int n) { + for (int i = 0; i < n; ++i) { + writeBool8(out[i]); + } +} + + +void BinaryOutput::writeBool8(const Array<bool>& out, int n) { + writeBool8(out.getCArray(), n); +} + +#define IMPLEMENT_WRITER(ucase, lcase)\ +void BinaryOutput::write##ucase(const std::vector<lcase>& out, int n) {\ + write##ucase(&out[0], n);\ +}\ +\ +\ +void BinaryOutput::write##ucase(const Array<lcase>& out, int n) {\ + write##ucase(out.getCArray(), n);\ +} + + +IMPLEMENT_WRITER(UInt8, uint8) +IMPLEMENT_WRITER(Int8, int8) +IMPLEMENT_WRITER(UInt16, uint16) +IMPLEMENT_WRITER(Int16, int16) +IMPLEMENT_WRITER(UInt32, uint32) +IMPLEMENT_WRITER(Int32, int32) +IMPLEMENT_WRITER(UInt64, uint64) +IMPLEMENT_WRITER(Int64, int64) +IMPLEMENT_WRITER(Float32, float32) +IMPLEMENT_WRITER(Float64, float64) + +#undef IMPLEMENT_WRITER + +// Data structures that are one byte per element can be +// directly copied, regardles of endian-ness. +#define IMPLEMENT_WRITER(ucase, lcase)\ +void BinaryOutput::write##ucase(const lcase* out, int n) {\ + if (sizeof(lcase) == 1) {\ + writeBytes((void*)out, n);\ + } else {\ + for (int i = 0; i < n ; ++i) {\ + write##ucase(out[i]);\ + }\ + }\ +} + +IMPLEMENT_WRITER(Bool8, bool) +IMPLEMENT_WRITER(UInt8, uint8) +IMPLEMENT_WRITER(Int8, int8) + +#undef IMPLEMENT_WRITER + + +#define IMPLEMENT_WRITER(ucase, lcase)\ +void BinaryOutput::write##ucase(const lcase* out, int n) {\ + if (m_swapBytes) {\ + for (int i = 0; i < n; ++i) {\ + write##ucase(out[i]);\ + }\ + } else {\ + writeBytes((const void*)out, sizeof(lcase) * n);\ + }\ +} + + +IMPLEMENT_WRITER(UInt16, uint16) +IMPLEMENT_WRITER(Int16, int16) +IMPLEMENT_WRITER(UInt32, uint32) +IMPLEMENT_WRITER(Int32, int32) +IMPLEMENT_WRITER(UInt64, uint64) +IMPLEMENT_WRITER(Int64, int64) +IMPLEMENT_WRITER(Float32, float32) +IMPLEMENT_WRITER(Float64, float64) + +#undef IMPLEMENT_WRITER + + +void BinaryOutput::reallocBuffer(size_t bytes, size_t oldBufferLen) { + //debugPrintf("reallocBuffer(%d, %d)\n", bytes, oldBufferLen); + + size_t newBufferLen = (int)(m_bufferLen * 1.5) + 100; + uint8* newBuffer = NULL; + + if ((m_filename == "<memory>") || (newBufferLen < MAX_BINARYOUTPUT_BUFFER_SIZE)) { + // We're either writing to memory (in which case we *have* to try and allocate) + // or we've been asked to allocate a reasonable size buffer. + + //debugPrintf(" realloc(%d)\n", newBufferLen); + newBuffer = (uint8*)System::realloc(m_buffer, newBufferLen); + if (newBuffer != NULL) { + m_maxBufferLen = newBufferLen; + } + } + + if ((newBuffer == NULL) && (bytes > 0)) { + // Realloc failed; we're probably out of memory. Back out + // the entire call and try to dump some data to disk. + m_bufferLen = oldBufferLen; + reserveBytesWhenOutOfMemory(bytes); + } else { + m_buffer = newBuffer; + debugAssert(isValidHeapPointer(m_buffer)); + } +} + + +void BinaryOutput::reserveBytesWhenOutOfMemory(size_t bytes) { + if (m_filename == "<memory>") { + throw "Out of memory while writing to memory in BinaryOutput (no RAM left)."; + } else if ((int)bytes > (int)m_maxBufferLen) { + throw "Out of memory while writing to disk in BinaryOutput (could not create a large enough buffer)."; + } else { + + // Dump the contents to disk. In order to enable seeking backwards, + // we keep the last 10 MB in memory. + int writeBytes = m_bufferLen - 10 * 1024 * 1024; + + if (writeBytes < m_bufferLen / 3) { + // We're going to write less than 1/3 of the file; + // give up and just write the whole thing. + writeBytes = m_bufferLen; + } + debugAssert(writeBytes > 0); + + //debugPrintf("Writing %d bytes to disk\n", writeBytes); + + const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb"; + FILE* file = fopen(m_filename.c_str(), mode); + debugAssert(file); + + size_t count = fwrite(m_buffer, 1, writeBytes, file); + debugAssert((int)count == writeBytes); (void)count; + + fclose(file); + file = NULL; + + // Record that we saved this data. + m_alreadyWritten += writeBytes; + m_bufferLen -= writeBytes; + m_pos -= writeBytes; + + debugAssert(m_bufferLen < m_maxBufferLen); + debugAssert(m_bufferLen >= 0); + debugAssert(m_pos >= 0); + debugAssert(m_pos <= m_bufferLen); + + // Shift the unwritten data back appropriately in the buffer. + debugAssert(isValidHeapPointer(m_buffer)); + System::memcpy(m_buffer, m_buffer + writeBytes, m_bufferLen); + debugAssert(isValidHeapPointer(m_buffer)); + + // *now* we allocate bytes (there should presumably be enough + // space in the buffer; if not, we'll come back through this + // code and dump the last 10MB to disk as well. Note that the + // bytes > maxBufferLen case above would already have triggered + // if this call couldn't succeed. + reserveBytes(bytes); + } +} + + +BinaryOutput::BinaryOutput() { + m_alreadyWritten = 0; + m_swapBytes = false; + m_pos = 0; + m_filename = "<memory>"; + m_buffer = NULL; + m_bufferLen = 0; + m_maxBufferLen = 0; + m_beginEndBits = 0; + m_bitString = 0; + m_bitPos = 0; + m_ok = true; + m_committed = false; +} + + +BinaryOutput::BinaryOutput( + const std::string& filename, + G3DEndian fileEndian) { + + m_pos = 0; + m_alreadyWritten = 0; + setEndian(fileEndian); + m_filename = filename; + m_buffer = NULL; + m_bufferLen = 0; + m_maxBufferLen = 0; + m_beginEndBits = 0; + m_bitString = 0; + m_bitPos = 0; + m_committed = false; + + m_ok = true; + /** Verify ability to write to disk */ + commit(false); + m_committed = false; +} + + +void BinaryOutput::reset() { + debugAssert(m_beginEndBits == 0); + alwaysAssertM(m_filename == "<memory>", + "Can only reset a BinaryOutput that writes to memory."); + + // Do not reallocate, just clear the size of the buffer. + m_pos = 0; + m_alreadyWritten = 0; + m_bufferLen = 0; + m_beginEndBits = 0; + m_bitString = 0; + m_bitPos = 0; + m_committed = false; +} + + +BinaryOutput::~BinaryOutput() { + debugAssert((m_buffer == NULL) || isValidHeapPointer(m_buffer)); + System::free(m_buffer); + m_buffer = NULL; + m_bufferLen = 0; + m_maxBufferLen = 0; +} + + +void BinaryOutput::setEndian(G3DEndian fileEndian) { + m_fileEndian = fileEndian; + m_swapBytes = (fileEndian != System::machineEndian()); +} + + +bool BinaryOutput::ok() const { + return m_ok; +} + + +void BinaryOutput::compress() { + if (m_alreadyWritten > 0) { + throw "Cannot compress huge files (part of this file has already been written to disk)."; + } + + // Old buffer size + int L = m_bufferLen; + uint8* convert = (uint8*)&L; + + // Zlib requires the output buffer to be this big + unsigned long newSize = iCeil(m_bufferLen * 1.01) + 12; + uint8* temp = (uint8*)System::malloc(newSize); + int result = compress2(temp, &newSize, m_buffer, m_bufferLen, 9); + + debugAssert(result == Z_OK); (void)result; + + // Write the header + if (m_swapBytes) { + m_buffer[0] = convert[3]; + m_buffer[1] = convert[2]; + m_buffer[2] = convert[1]; + m_buffer[3] = convert[0]; + } else { + m_buffer[0] = convert[0]; + m_buffer[1] = convert[1]; + m_buffer[2] = convert[2]; + m_buffer[3] = convert[3]; + } + + // Write the data + if ((int64)newSize + 4 > (int64)m_maxBufferLen) { + m_maxBufferLen = newSize + 4; + m_buffer = (uint8*)System::realloc(m_buffer, m_maxBufferLen); + } + m_bufferLen = newSize + 4; + System::memcpy(m_buffer + 4, temp, newSize); + m_pos = m_bufferLen; + + System::free(temp); +} + + +void BinaryOutput::commit(bool flush) { + debugAssertM(! m_committed, "Cannot commit twice"); + m_committed = true; + debugAssertM(m_beginEndBits == 0, "Missing endBits before commit"); + + // Make sure the directory exists. + std::string root, base, ext, path; + Array<std::string> pathArray; + parseFilename(m_filename, root, pathArray, base, ext); + + path = root + stringJoin(pathArray, '/'); + if (! fileExists(path, false)) { + createDirectory(path); + } + + const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb"; + + FILE* file = fopen(m_filename.c_str(), mode); + + m_ok = (file != NULL) && m_ok; + + if (m_ok) { + debugAssertM(file, std::string("Could not open '") + m_filename + "'"); + + m_alreadyWritten += m_bufferLen; + + fwrite(m_buffer, m_bufferLen, 1, file); + if (flush) { + fflush(file); + } + fclose(file); + file = NULL; + } +} + + +void BinaryOutput::commit( + uint8* out) { + debugAssertM(! m_committed, "Cannot commit twice"); + m_committed = true; + + System::memcpy(out, m_buffer, m_bufferLen); +} + + +void BinaryOutput::writeUInt16(uint16 u) { + reserveBytes(2); + + uint8* convert = (uint8*)&u; + + if (m_swapBytes) { + m_buffer[m_pos] = convert[1]; + m_buffer[m_pos + 1] = convert[0]; + } else { + *(uint16*)(m_buffer + m_pos) = u; + } + + m_pos += 2; +} + + +void BinaryOutput::writeUInt32(uint32 u) { + reserveBytes(4); + + uint8* convert = (uint8*)&u; + + debugAssert(m_beginEndBits == 0); + + if (m_swapBytes) { + m_buffer[m_pos] = convert[3]; + m_buffer[m_pos + 1] = convert[2]; + m_buffer[m_pos + 2] = convert[1]; + m_buffer[m_pos + 3] = convert[0]; + } else { + *(uint32*)(m_buffer + m_pos) = u; + } + + m_pos += 4; +} + + +void BinaryOutput::writeUInt64(uint64 u) { + reserveBytes(8); + + uint8* convert = (uint8*)&u; + + if (m_swapBytes) { + m_buffer[m_pos] = convert[7]; + m_buffer[m_pos + 1] = convert[6]; + m_buffer[m_pos + 2] = convert[5]; + m_buffer[m_pos + 3] = convert[4]; + m_buffer[m_pos + 4] = convert[3]; + m_buffer[m_pos + 5] = convert[2]; + m_buffer[m_pos + 6] = convert[1]; + m_buffer[m_pos + 7] = convert[0]; + } else { + *(uint64*)(m_buffer + m_pos) = u; + } + + m_pos += 8; +} + + +void BinaryOutput::writeString(const char* s) { + // +1 is because strlen doesn't count the null + int len = strlen(s) + 1; + + debugAssert(m_beginEndBits == 0); + reserveBytes(len); + System::memcpy(m_buffer + m_pos, s, len); + m_pos += len; +} + + +void BinaryOutput::writeStringEven(const char* s) { + // +1 is because strlen doesn't count the null + int len = strlen(s) + 1; + + reserveBytes(len); + System::memcpy(m_buffer + m_pos, s, len); + m_pos += len; + + // Pad with another NULL + if ((len % 2) == 1) { + writeUInt8(0); + } +} + + +void BinaryOutput::writeString32(const char* s) { + writeUInt32(strlen(s) + 1); + writeString(s); +} + + +void BinaryOutput::writeVector4(const Vector4& v) { + writeFloat32(v.x); + writeFloat32(v.y); + writeFloat32(v.z); + writeFloat32(v.w); +} + + +void BinaryOutput::writeVector3(const Vector3& v) { + writeFloat32(v.x); + writeFloat32(v.y); + writeFloat32(v.z); +} + + +void BinaryOutput::writeVector2(const Vector2& v) { + writeFloat32(v.x); + writeFloat32(v.y); +} + + +void BinaryOutput::writeColor4(const Color4& v) { + writeFloat32(v.r); + writeFloat32(v.g); + writeFloat32(v.b); + writeFloat32(v.a); +} + + +void BinaryOutput::writeColor3(const Color3& v) { + writeFloat32(v.r); + writeFloat32(v.g); + writeFloat32(v.b); +} + + +void BinaryOutput::beginBits() { + debugAssertM(m_beginEndBits == 0, "Already in beginBits...endBits"); + m_bitString = 0x00; + m_bitPos = 0; + m_beginEndBits = 1; +} + + +void BinaryOutput::writeBits(uint32 value, int numBits) { + + while (numBits > 0) { + // Extract the current bit of value and + // insert it into the current byte + m_bitString |= (value & 1) << m_bitPos; + ++m_bitPos; + value = value >> 1; + --numBits; + + if (m_bitPos > 7) { + // We've reached the end of this byte + writeUInt8(m_bitString); + m_bitString = 0x00; + m_bitPos = 0; + } + } +} + + +void BinaryOutput::endBits() { + debugAssertM(m_beginEndBits == 1, "Not in beginBits...endBits"); + if (m_bitPos > 0) { + writeUInt8(m_bitString); + } + m_bitString = 0; + m_bitPos = 0; + m_beginEndBits = 0; +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Box.cpp b/externals/g3dlite/G3D.lib/source/Box.cpp new file mode 100644 index 00000000000..4b6c56388ec --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Box.cpp @@ -0,0 +1,393 @@ +/** + @file Box.cpp + Box class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-06-02 + @edited 2006-02-05 +*/ + +#include "G3D/Box.h" +#include "G3D/debug.h" +#include "G3D/Plane.h" +#include "G3D/AABox.h" +#include "G3D/CoordinateFrame.h" + +namespace G3D { + +/** + Sets a field on four vertices. Used by the constructor. + */ +#define setMany(i0, i1, i2, i3, field, extreme) \ + _corner[i0].field = _corner[i1].field = \ + _corner[i2].field = _corner[i3].field = \ + (extreme).field + +Box::Box() { +} + + +Box::Box(const AABox& b) { + init(b.low(), b.high()); +} + +Box::Box(class BinaryInput& b) { + deserialize(b); +} + + +void Box::serialize(class BinaryOutput& b) const { + int i; + for (i = 0; i < 8; ++i) { + _corner[i].serialize(b); + } + + // Other state can be reconstructed +} + + +void Box::deserialize(class BinaryInput& b) { + int i; + + _center = Vector3::zero(); + for (i = 0; i < 8; ++i) { + _corner[i].deserialize(b); + _center += _corner[i]; + } + + _center = _center / 8; + + // Reconstruct other state from the corners + _axis[0] = _corner[5] - _corner[4]; + _axis[1] = _corner[7] - _corner[4]; + _axis[2] = _corner[0] - _corner[4]; + + for (i = 0; i < 3; ++i) { + _extent[i] = _axis[i].magnitude(); + _axis[i] /= _extent[i]; + } + + _volume = _extent.x * _extent.y * _extent.z; + + _area = 2 * + (_extent.x * _extent.y + + _extent.y * _extent.z + + _extent.z * _extent.x); +} + + +Box::Box( + const Vector3& min, + const Vector3& max) { + + init(min.min(max), min.max(max)); + +} + +void Box::init( + const Vector3& min, + const Vector3& max) { + + debugAssert( + (min.x <= max.x) && + (min.y <= max.y) && + (min.z <= max.z)); + + setMany(0, 1, 2, 3, z, max); + setMany(4, 5, 6, 7, z, min); + + setMany(1, 2, 5, 6, x, max); + setMany(0, 3, 4, 7, x, min); + + setMany(3, 2, 6, 7, y, max); + setMany(0, 1, 5, 4, y, min); + + _extent = max - min; + + _axis[0] = Vector3::unitX(); + _axis[1] = Vector3::unitY(); + _axis[2] = Vector3::unitZ(); + + if (_extent.isFinite()) { + _volume = _extent.x * _extent.y * _extent.z; + } else { + _volume = G3D::inf(); + } + + debugAssert(! isNaN(_extent.x)); + + _area = 2 * + (_extent.x * _extent.y + + _extent.y * _extent.z + + _extent.z * _extent.x); + + _center = (max + min) / 2; + + // If the extent is infinite along an axis, make the center zero to avoid NaNs + for (int i = 0; i < 3; ++i) { + if (! G3D::isFinite(_extent[i])) { + _center[i] = 0.0f; + } + } +} + + +float Box::volume() const { + return _volume; +} + + +float Box::area() const { + return _area; +} + + +void Box::getLocalFrame(CoordinateFrame& frame) const { + + frame.rotation = Matrix3( + _axis[0][0], _axis[1][0], _axis[2][0], + _axis[0][1], _axis[1][1], _axis[2][1], + _axis[0][2], _axis[1][2], _axis[2][2]); + + frame.translation = _center; +} + + +CoordinateFrame Box::localFrame() const { + CoordinateFrame out; + getLocalFrame(out); + return out; +} + + +void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3& v3) const { + switch (f) { + case 0: + v0 = _corner[0]; v1 = _corner[1]; v2 = _corner[2]; v3 = _corner[3]; + break; + + case 1: + v0 = _corner[1]; v1 = _corner[5]; v2 = _corner[6]; v3 = _corner[2]; + break; + + case 2: + v0 = _corner[7]; v1 = _corner[6]; v2 = _corner[5]; v3 = _corner[4]; + break; + + case 3: + v0 = _corner[2]; v1 = _corner[6]; v2 = _corner[7]; v3 = _corner[3]; + break; + + case 4: + v0 = _corner[3]; v1 = _corner[7]; v2 = _corner[4]; v3 = _corner[0]; + break; + + case 5: + v0 = _corner[1]; v1 = _corner[0]; v2 = _corner[4]; v3 = _corner[5]; + break; + + default: + debugAssert((f >= 0) && (f < 6)); + } +} + + + +int Box::dummy = 0; + +bool Box::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask, + uint32& childMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + childMask = 0; + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + Vector3 corner; + + int numContained = 0; + int v = 0; + + // We can early-out only if we have found one point on each + // side of the plane (i.e. if we are straddling). That + // occurs when (numContained < v) && (numContained > 0) + for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) { + if (plane[p].halfSpaceContains(_corner[v])) { + ++numContained; + } + } + + if (numContained == 0) { + // Plane p culled the box + cullingPlane = p; + + // The caller should not recurse into the children, + // since the parent is culled. If they do recurse, + // make them only test against this one plane, which + // will immediately cull the volume. + childMask = 1 << p; + return true; + + } else if (numContained < v) { + // The bounding volume straddled the plane; we have + // to keep testing against this plane + childMask |= (1 << p); + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool Box::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + bool culled = true; + + int v; + + // Assume this plane culls all points. See if there is a point + // not culled by the plane... early out when at least one point + // is in the positive half space. + for (v = 0; (v < 8) && culled; ++v) { + culled = ! plane[p].halfSpaceContains(corner(v)); + } + + if (culled) { + // Plane p culled the box + cullingPlane = p; + + return true; + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool Box::contains( + const Vector3& point) const { + + // Form axes from three edges, transform the point into that + // space, and perform 3 interval tests + + Vector3 u = _corner[4] - _corner[0]; + Vector3 v = _corner[3] - _corner[0]; + Vector3 w = _corner[1] - _corner[0]; + + Matrix3 M = Matrix3(u.x, v.x, w.x, + u.y, v.y, w.y, + u.z, v.z, w.z); + + // M^-1 * (point - _corner[0]) = point in unit cube's object space + // compute the inverse of M + Vector3 osPoint = M.inverse() * (point - _corner[0]); + + return + (osPoint.x >= 0) && + (osPoint.y >= 0) && + (osPoint.z >= 0) && + (osPoint.x <= 1) && + (osPoint.y <= 1) && + (osPoint.z <= 1); +} + +#undef setMany + + +void Box::getRandomSurfacePoint(Vector3& P, Vector3& N) const { + float aXY = _extent.x * _extent.y; + float aYZ = _extent.y * _extent.z; + float aZX = _extent.z * _extent.x; + + float r = (float)uniformRandom(0, aXY + aYZ + aZX); + + // Choose evenly between positive and negative face planes + float d = (uniformRandom(0, 1) < 0.5f) ? -1.0f : 1.0f; + + // The probability of choosing a given face is proportional to + // its area. + if (r < aXY) { + P = _axis[0] * (float)uniformRandom(-0.5, 0.5) * _extent.x + + _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y + + _center + _axis[2] * d * _extent.z * 0.5f; + N = _axis[2] * d; + } else if (r < aYZ) { + P = _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y + + _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z + + _center + _axis[0] * d * _extent.x * 0.5f; + N = _axis[0] * d; + } else { + P = _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z + + _axis[0] *(float) uniformRandom(-0.5, 0.5) * _extent.x + + _center + _axis[1] * d * _extent.y * 0.5f; + N = _axis[1] * d; + } +} + + +Vector3 Box::randomInteriorPoint() const { + Vector3 sum = _center; + + for (int a = 0; a < 3; ++a) { + sum += _axis[a] * (float)uniformRandom(-0.5, 0.5) * _extent[a]; + } + + return sum; +} + +Box Box::inf() { + return Box(-Vector3::inf(), Vector3::inf()); +} + +void Box::getBounds(class AABox& aabb) const { + + Vector3 lo = _corner[0]; + Vector3 hi = lo; + + for (int v = 1; v < 8; ++v) { + const Vector3& C = _corner[v]; + lo = lo.min(C); + hi = hi.max(C); + } + + aabb = AABox(lo, hi); +} + + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/Capsule.cpp b/externals/g3dlite/G3D.lib/source/Capsule.cpp new file mode 100644 index 00000000000..fbcb56fe97b --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Capsule.cpp @@ -0,0 +1,179 @@ +/** + @file Capsule.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-07 + @edited 2005-08-18 + + Copyright 2000-2005, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/Capsule.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/LineSegment.h" +#include "G3D/Sphere.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Line.h" +#include "G3D/AABox.h" + +namespace G3D { + +Capsule::Capsule(class BinaryInput& b) { + deserialize(b); +} + + +Capsule::Capsule() { +} + + +Capsule::Capsule(const Vector3& _p1, const Vector3& _p2, float _r) + : p1(_p1), p2(_p2), _radius(_r) { +} + + +void Capsule::serialize(class BinaryOutput& b) const { + p1.serialize(b); + p2.serialize(b); + b.writeFloat64(_radius); +} + + +void Capsule::deserialize(class BinaryInput& b) { + p1.deserialize(b); + p2.deserialize(b); + _radius = b.readFloat64(); +} + + +Line Capsule::axis() const { + return Line::fromTwoPoints(p1, p2); +} + + +float Capsule::volume() const { + return + // Sphere volume + pow(_radius, 3) * pi() * 4 / 3 + + + // Cylinder volume + pow(_radius, 2) * (p1 - p2).magnitude(); +} + + +float Capsule::area() const { + + return + // Sphere area + pow(_radius, 2) * 4 * pi() + + + // Cylinder area + twoPi() * _radius * (p1 - p2).magnitude(); +} + + +void Capsule::getBounds(AABox& out) const { + Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * _radius); + Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * _radius); + + out = AABox(min, max); +} + + +bool Capsule::contains(const Vector3& p) const { + return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(radius()); +} + + +void Capsule::getRandomSurfacePoint(Vector3& p, Vector3& N) const { + float h = height(); + float r = radius(); + + // Create a random point on a standard capsule and then rotate to the global frame. + + // Relative areas + float capRelArea = sqrt(r) / 2.0f; + float sideRelArea = r * h; + + float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea); + + if (r1 < capRelArea * 2) { + + // Select a point uniformly at random on a sphere + N = Sphere(Vector3::zero(), 1).randomSurfacePoint(); + p = N * r; + p.y += sign(p.y) * h / 2.0f; + } else { + // Side + float a = uniformRandom(0, (float)twoPi()); + N.x = cos(a); + N.y = 0; + N.z = sin(a); + p.x = N.x * r; + p.z = N.y * r; + p.y = uniformRandom(-h / 2.0f, h / 2.0f); + } + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + p = cframe.pointToWorldSpace(p); + N = cframe.normalToWorldSpace(N); +} + + +void Capsule::getReferenceFrame(CoordinateFrame& cframe) const { + cframe.translation = center(); + + Vector3 Y = (p1 - p2).direction(); + Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX(); + Vector3 Z = X.cross(Y).direction(); + X = Y.cross(Z); + cframe.rotation.setColumn(0, X); + cframe.rotation.setColumn(1, Y); + cframe.rotation.setColumn(2, Z); +} + + +Vector3 Capsule::randomInteriorPoint() const { + float h = height(); + float r = radius(); + + // Create a random point in a standard capsule and then rotate to the global frame. + + Vector3 p; + + float hemiVolume = pi() * (r*r*r) * 4 / 6.0; + float cylVolume = pi() * square(r) * h; + + float r1 = uniformRandom(0, 2.0 * hemiVolume + cylVolume); + + if (r1 < 2.0 * hemiVolume) { + + p = Sphere(Vector3::zero(), r).randomInteriorPoint(); + + p.y += sign(p.y) * h / 2.0f; + + } else { + + // Select a point uniformly at random on a disk + float a = uniformRandom(0, (float)twoPi()); + float r2 = sqrt(uniformRandom(0, 1)) * r; + + p = Vector3(cos(a) * r2, + uniformRandom(-h / 2.0f, h / 2.0f), + sin(a) * r2); + } + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + return cframe.pointToWorldSpace(p); +} + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp b/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp new file mode 100644 index 00000000000..16650bc1a96 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp @@ -0,0 +1,2152 @@ +/** + @file CollisionDetection.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @cite Bounce direction based on Paul Nettle's ftp://ftp.3dmaileffects.com/pub/FluidStudios/CollisionDetection/Fluid_Studios_Generic_Collision_Detection_for_Games_Using_Ellipsoids.pdf and comments by Max McGuire. Ray-sphere code by Eric Haines. + + @created 2001-11-24 + @edited 2008-10-10 + */ + +#include "G3D/CoordinateFrame.h" +#include "G3D/platform.h" +#include "G3D/CollisionDetection.h" +#include "G3D/debugAssert.h" +#include "G3D/vectorMath.h" +#include "G3D/Capsule.h" +#include "G3D/Plane.h" +#include "G3D/Line.h" +#include "G3D/LineSegment.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/Triangle.h" +#include "G3D/Vector3.h" +#include "G3D/AABox.h" + +namespace G3D { + +bool CollisionDetection::ignoreBool; +Vector3 CollisionDetection::ignore; +Array<Vector3> CollisionDetection::ignoreArray; + + + +Vector3 CollisionDetection::separatingAxisForSolidBoxSolidBox( + const int separatingAxisIndex, + const Box & box1, + const Box & box2) { + debugAssert(separatingAxisIndex >= 0); + debugAssert(separatingAxisIndex < 15); + Vector3 axis; + if (separatingAxisIndex < 3) { + axis = box1.axis(separatingAxisIndex); + } else if (separatingAxisIndex < 6) { + axis = box2.axis(separatingAxisIndex - 3); + } else { + int box1Index = (separatingAxisIndex - 6) / 3; + int box2Index = (separatingAxisIndex - 6) % 3; + axis = cross(box1.axis(box1Index), box2.axis(box2Index)); + } + return axis; +} + +#ifdef _MSC_VER +# pragma warning (push) +# pragma warning (disable : 4244) +#endif + +float CollisionDetection::projectedDistanceForSolidBoxSolidBox( + const int separatingAxisIndex, + const Vector3 & a, + const Vector3 & b, + const Vector3 & D, + const double* c, + const double* ca, + const double* ad, + const double* bd) +{ + (void)D; + + float R0 = 0.0f; + float R1 = 0.0f; + float R = 0.0f; + switch (separatingAxisIndex) { + case 0: + // A0 + R0 = a[0]; + R1 = b[0] * ca[0] + b[1] * ca[1] + b[2] * ca[2]; + R = fabs(ad[0]); + break; + case 1: + // A1 + R0 = a[1]; + R1 = b[0] * ca[3] + b[1] * ca[4] + b[2] * ca[5]; + R = fabs(ad[1]); + break; + case 2: + // A2 + R0 = a[2]; + R1 = b[0] * ca[6] + b[1] * ca[7] + b[2] * ca[8]; + R = fabs(ad[2]); + break; + case 3: + // B0 + R0 = a[0] * ca[0] + a[1] * ca[3] + a[2] * ca[6]; + R1 = b[0]; + R = fabs(bd[0]); + break; + case 4: + // B1 + R0 = a[0] * ca[1] + a[1] * ca[4] + a[2] * ca[7]; + R1 = b[1]; + R = fabs(bd[1]); + break; + case 5: + // B2 + R0 = a[0] * ca[2] + a[1] * ca[5] + a[2] * ca[8]; + R1 = b[2]; + R = fabs(bd[2]); + break; + case 6: + // A0 x B0 + R0 = a[1] * ca[6] + a[2] * ca[3]; + R1 = b[1] * ca[2] + b[2] * ca[1]; + R = fabs(c[3] * ad[2] - c[6] * ad[1]); + break; + case 7: + // A0 x B1 + R0 = a[1] * ca[7] + a[2] * ca[4]; + R1 = b[0] * ca[2] + b[2] * ca[0]; + R = fabs(c[4] * ad[2] - c[7] * ad[1]); + break; + case 8: + // A0 x B2 + R0 = a[1] * ca[8] + a[2] * ca[5]; + R1 = b[0] * ca[1] + b[1] * ca[0]; + R = fabs(c[5] * ad[2] - c[8] * ad[1]); + break; + case 9: + // A1 x B0 + R0 = a[0] * ca[6] + a[2] * ca[0]; + R1 = b[1] * ca[5] + b[2] * ca[4]; + R = fabs(c[6] * ad[0] - c[0] * ad[2]); + break; + case 10: + // A1 x B1 + R0 = a[0] * ca[7] + a[2] * ca[1]; + R1 = b[0] * ca[5] + b[2] * ca[3]; + R = fabs(c[7] * ad[0] - c[1] * ad[2]); + break; + case 11: + // A1 x B2 + R0 = a[0] * ca[8] + a[2] * ca[2]; + R1 = b[0] * ca[4] + b[1] * ca[3]; + R = fabs(c[8] * ad[0] - c[2] * ad[2]); + break; + case 12: + // A2 x B0 + R0 = a[0] * ca[3] + a[1] * ca[0]; + R1 = b[1] * ca[8] + b[2] * ca[7]; + R = fabs(c[0] * ad[1] - c[3] * ad[0]); + break; + case 13: + // A2 x B1 + R0 = a[0] * ca[4] + a[1] * ca[1]; + R1 = b[0] * ca[8] + b[2] * ca[6]; + R = fabs(c[1] * ad[1] - c[4] * ad[0]); + break; + case 14: + // A2 x B2 + R0 = a[0] * ca[5] + a[1] * ca[2]; + R1 = b[0] * ca[7] + b[1] * ca[6]; + R = fabs(c[2] * ad[1] - c[5] * ad[0]); + break; + default: + debugAssertM(false, "fell through switch statement"); + } + + return (R - (R0 + R1)); +} + + +bool CollisionDetection::parallelAxisForSolidBoxSolidBox( + const double* ca, + const double epsilon, + int & axis1, + int & axis2) { + const double parallelDot = 1.0 - epsilon; + for (int i = 0; i < 9; i++) { + if (ca[i] >= parallelDot) { + axis1 = i / 3; + axis2 = i % 3; + return true; + } + } + return false; +} + + + + +void CollisionDetection::fillSolidBoxSolidBoxInfo( + const Box & box1, + const Box & box2, + Vector3 & a, + Vector3 & b, + Vector3 & D, + double* c, + double* ca, + double* ad, + double* bd) { + // length between center and each side of box1 and box2 + a = box1.extent() * 0.5; + b = box2.extent() * 0.5; + + // difference between centers of box1 and box2 + D = box2.center() - box1.center(); + + // store the value of all possible dot products between the + // axes of box1 and box2, c_{row, col} in the Eberly paper + // corresponds to c[row * 3 + col] for this 9 element array. + // + // c[] holds signed values, ca[] hold absolute values + for (int i = 0; i < 9; i++) { + c[i] = dot(box1.axis(i / 3), box2.axis(i % 3)); + ca[i] = fabs(c[i]); + } + + // store all possible dot products between the axes of box1 and D, + // as well as the axes of box2 and D + for (int i = 0; i < 3; i++) { + ad[i] = dot(box1.axis(i), D); + bd[i] = dot(box2.axis(i), D); + } +} + + + +bool CollisionDetection::conservativeBoxBoxTest( + const Vector3 & a, const Vector3 & b, const Vector3 & D) { + // do a quick bounding sphere test because it is relatively + // cheap, (three dot products, two sqrts, and a few others) + double boxRadius1 = a.magnitude(); + double boxRadius2 = b.magnitude(); + return (D.squaredMagnitude() < square(boxRadius1 + boxRadius2)); +} + + + + +bool CollisionDetection::fixedSolidBoxIntersectsFixedSolidBox( + const Box& box1, + const Box& box2, + const int lastSeparatingAxis) { + // for explanations of the variable please refer to the + // paper and fillSolidBoxSolidBoxInfo() + Vector3 a; + Vector3 b; + Vector3 D; + double c[9]; + double ca[9]; + double ad[3]; + double bd[3]; + + fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd); + + int dummy1, dummy2; + bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001, + dummy1, dummy2); + + // check the separating axis from the last time step + if (lastSeparatingAxis != -1 && + (lastSeparatingAxis < 6 || !parallelAxes)) { + double projectedDistance = projectedDistanceForSolidBoxSolidBox( + lastSeparatingAxis, a, b, D, c, ca, ad, bd); + + // the separating axis from the last time step is still + // valid, the boxes do not intersect + if (projectedDistance > 0.0) { + return false; + } + } + + // test if the boxes can be separated by a plane normal to + // any of the three axes of box1, any of the three axes of box2, + // or any of the 9 possible cross products of axes from box1 + // and box2 + for (int i = 0; i < 15; i++) { + // do not need to check edge-edge cases if any two of + // the axes are parallel + if (parallelAxes && i == 6) { + return true; + } + + double projectedDistance = + projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd); + + // found a separating axis, the boxes do not intersect + if (projectedDistance > 0.0) { + return false; + } + } + + return true; +} + + + +void CollisionDetection::closestPointsBetweenLineAndLine( + const Line & line1, + const Line & line2, + Vector3 & closest1, + Vector3 & closest2) { + // TODO make accessors for Line that don't make a copy of data + Vector3 P0 = line1.point(); + Vector3 u = line1.direction(); + Vector3 Q0 = line2.point(); + Vector3 v = line2.direction(); + Vector3 w0 = P0 - Q0; + + // a = 1.0, c = 1.0 + double b = dot(u, v); + double d = dot(u, w0); + double e = dot(v, w0); + double D = 1.0 - b * b; + double sc, tc; + + static const double epsilon = 0.00001; + + if (D < epsilon) { + // lines are parallel, choose P0 as one point, find the point + // on line2 that is closest to P0 + sc = 0.0; + tc = (b > 1.0) ? (d / b) : (e / 1.0); + } else { + // lines are not parallel + sc = (b * e - 1.0 * d) / D; + tc = (1.0 * e - b * d) / D; + } + + closest1 = P0 + (sc * u); + closest2 = Q0 + (tc * v); +} + + + +float CollisionDetection::penetrationDepthForFixedBoxFixedBox( + const Box& box1, + const Box& box2, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals, + const int lastSeparatingAxis) { + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + Vector3 a; + Vector3 b; + Vector3 D; + double c[9]; + double ca[9]; + double ad[3]; + double bd[3]; + + debugAssert(lastSeparatingAxis >= -1); + debugAssert(lastSeparatingAxis < 15); + + fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd); + + int axis1, axis2; + bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001, + axis1, axis2); + + + // check the separating axis from the last time step + if (lastSeparatingAxis != -1 && + (lastSeparatingAxis < 6 || !parallelAxes)) { + float projectedDistance = projectedDistanceForSolidBoxSolidBox( + lastSeparatingAxis, a, b, D, c, ca, ad, bd); + + // the separating axis from the last time step is still + // valid, the boxes do not intersect + if (projectedDistance > 0.0) { + return -projectedDistance; + } + } + + // test if the boxes can be separated by a plane normal to + // any of the three axes of box1, any of the three axes of box2, + // (test 9 possible cross products later) + float penetration = -(float)G3D::inf(); + int penetrationAxisIndex = -1; + + for (int i = 0; i < 6; i++) { + float projectedDistance = + projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd); + + // found a separating axis, the boxes do not intersect + if (projectedDistance > 0.0) { + return -projectedDistance; + } + + // keep track of the axis that is least violated + if (projectedDistance > penetration) { + penetration = projectedDistance; + penetrationAxisIndex = i; + } + } + + + // for each edge-edge case we have to adjust the magnitude of + // penetration since we did not include the dot(L, L) denominator + // that can be smaller than 1.0 for the edge-edge cases. + if (!parallelAxes) { + double edgeDistances[9]; + + // run through edge-edge cases to see if we can find a separating axis + for (int i = 6; i < 15; i++) { + float projectedDistance = + projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd); + + // found a separating axis, the boxes do not intersect, + // correct magnitude and return projected distance + if (projectedDistance > 0.0) { + Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2); + projectedDistance /= dot(L, L); + return -projectedDistance; + } + + edgeDistances[i - 6] = projectedDistance; + } + + // no separating axis found, the boxes do intersect, + // correct the magnitudes of the projectedDistance values + for (int i = 6; i < 15; i++) { + // find the negative penetration value with the smallest magnitude, + // the adjustment done for the edge-edge cases only increases + // magnitude by dividing by a number smaller than 1 and greater than 0 + float projectedDistance = (float)edgeDistances[i - 6]; + if (projectedDistance > penetration) { + Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2); + projectedDistance /= dot(L, L); + if (projectedDistance > penetration) { + penetration = projectedDistance; + penetrationAxisIndex = i; + } + } + } + } + + // get final separating axis vector + Vector3 L = separatingAxisForSolidBoxSolidBox(penetrationAxisIndex, + box1, box2); + + // set L to be the normal that faces away from box1 + if (dot(L, D) < 0) { + L = -L; + } + + Vector3 contactPoint; + + if (penetrationAxisIndex < 6) { + // vertex to face collision, find deepest colliding vertex + const Box* vertexBox; + const Box* faceBox; + Vector3 faceNormal = L; + + // L will be the outward facing normal for the faceBox + if (penetrationAxisIndex < 3) { + faceBox = & box1; + vertexBox = & box2; + if (dot(L, D) < 0) { + faceNormal = -L; + } + } else { + faceBox = & box2; + vertexBox = & box1; + if (dot(L, D) > 0) { + faceNormal = -L; + } + } + + // find the vertex that is farthest away in the direction + // face normal direction + int deepestPointIndex = 0; + float deepestPointDot = dot(faceNormal, vertexBox->corner(0)); + for (int i = 1; i < 8; i++) { + float dotProduct = dot(faceNormal, vertexBox->corner(i)); + if (dotProduct < deepestPointDot) { + deepestPointDot = dotProduct; + deepestPointIndex = i; + } + } + + // return the point half way between the deepest point and the + // contacting face + contactPoint = vertexBox->corner(deepestPointIndex) + + (-penetration * 0.5 * faceNormal); + } else { + // edge-edge case, find the two ege lines + int edge1 = (penetrationAxisIndex - 6) / 3; + int edge2 = (penetrationAxisIndex - 6) % 3; + Vector3 linePoint1 = box1.center(); + Vector3 linePoint2 = box2.center(); + Vector3 lineDir1; + Vector3 lineDir2; + + // find edge line by finding the edge axis, and the + // other two axes that are closest to the other box + for (int i = 0; i < 3; i++ ) { + if (i == edge1) { + lineDir1 = box1.axis(i); + } else { + Vector3 axis = box1.axis(i); + if (dot(axis, L) < 0) { + axis = -axis; + } + linePoint1 += axis * a[i]; + } + + if (i == edge2) { + lineDir2 = box2.axis(i); + } else { + Vector3 axis = box2.axis(i); + if (dot(axis, L) > 0) { + axis = -axis; + } + linePoint2 += axis * b[i]; + } + } + + // make lines from the two closest edges, and find + // the points that on each line that are closest to the other + Line line1 = Line::fromPointAndDirection(linePoint1, lineDir1); + Line line2 = Line::fromPointAndDirection(linePoint2, lineDir2); + Vector3 closest1; + Vector3 closest2; + + closestPointsBetweenLineAndLine(line1, line2, closest1, closest2); + + // take the average of the two closest edge points for the final + // contact point + contactPoint = (closest1 + closest2) * 0.5; + } + + contactPoints.push(contactPoint); + contactNormals.push(L); + + return -penetration; + +} + + + + +float CollisionDetection::penetrationDepthForFixedSphereFixedBox( + const Sphere& sphere, + const Box& box, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + // In its local coordinate frame, the box measures + // 2 * halfExtent[a] along dimesion a. + Vector3 halfExtent(box.extent(0), box.extent(1), box.extent(2)); + halfExtent *= 0.5f; + + CoordinateFrame boxFrame; + box.getLocalFrame(boxFrame); + + // Transform the sphere to the box's coordinate frame. + Vector3 center = boxFrame.pointToObjectSpace(sphere.center); + + // Find the square of the distance from the sphere to the box + + + // Distance along each axis from the closest side of the box + // to the sphere center. Negative values are *inside* the box. + Vector3 distOutsideBox; + + // Divide space up into the 27 regions corresponding + // to {+|-|0}X, {+|-|0}Y, {+|-|0}Z and classify the + // sphere center into one of them. + Vector3 centerRegion; + + // In the edge collision case, the edge is between vertices + // (constant + variable) and (constant - variable). + Vector3 constant, variable; + + int numNonZero = 0; + + // Iterate over axes + for (int a = 0; a < 3; ++a) { + // For each (box side), see which direction the sphere + // is outside the box (positive or negative). Add the + // square of that distance to the total distance from + // the box. + + float distanceFromLow = -halfExtent[a] - center[a]; + float distanceFromHigh = center[a] - halfExtent[a]; + + if (fabsf(distanceFromLow) < fabsf(distanceFromHigh)) { + distOutsideBox[a] = distanceFromLow; + } else { + distOutsideBox[a] = distanceFromHigh; + } + + if (distanceFromLow < 0.0) { + if (distanceFromHigh < 0.0) { + // Inside the box + centerRegion[a] = 0.0; + variable[a] = 1.0; + } else { + // Off the high side + centerRegion[a] = 1.0; + constant[a] = halfExtent[a]; + ++numNonZero; + } + } else if (distanceFromHigh < 0.0) { + // Off the low side + centerRegion[a] = -1.0; + constant[a] = -halfExtent[a]; + ++numNonZero; + } else { + debugAssertM(false, + "distanceFromLow and distanceFromHigh cannot both be positive"); + } + } + + // Squared distance between the outside of the box and the + // sphere center. + float d2 = Vector3::zero().max(distOutsideBox).squaredMagnitude(); + + if (d2 > square(sphere.radius)) { + // There is no penetration because the distance is greater + // than the radius of the sphere. This is the common case + // and we quickly exit. + return -1; + } + + // We know there is some penetration but need to classify it. + // + // Examine the region that contains the center of the sphere. If + // there is exactly one non-zero axis, the collision is with a + // plane. If there are exactly two non-zero axes, the collision + // is with an edge. If all three axes are non-zero, the collision is + // with a vertex. If there are no non-zero axes, the center is inside + // the box. + + double depth = -1; + switch (numNonZero) { + case 3: // Vertex collision + // The collision point is the vertex at constant, the normal + // is the vector from there to the sphere center. + contactNormals.append(boxFrame.normalToWorldSpace(constant - center)); + contactPoints.append(boxFrame.pointToWorldSpace(constant)); + depth = sphere.radius - sqrt(d2); + break; + + case 2: // Edge collision + { + // TODO: unwrapping the edge constructor and closest point + // code will probably make it faster. + + // Determine the edge + Line line = Line::fromPointAndDirection(constant, variable); + + // Penetration depth: + depth = sphere.radius - sqrt(d2); + + // The contact point is the closes point to the sphere on the line + Vector3 X = line.closestPoint(center); + contactNormals.append(boxFrame.normalToWorldSpace(X - center).direction()); + contactPoints.append(boxFrame.pointToWorldSpace(X)); + } + break; + + case 1: // Plane collision + { + // The plane normal is the centerRegion vector, + // so the sphere normal is the negative. Take + // it to world space from box-space. + + // Center region doesn't need to be normalized because + // it is known to contain only one non-zero value + // and that value is +/- 1. + Vector3 N = boxFrame.normalToWorldSpace(-centerRegion); + contactNormals.append(N); + + // Penetration depth: + depth = sphere.radius - sqrtf(d2); + + // Compute the contact point from the penetration depth + contactPoints.append(sphere.center + N * (sphere.radius - depth)); + } + break; + + case 0: // Volume collision + + // The sphere center is inside the box. This is an easy case + // to handle. Note that all axes of distOutsideBox must + // be negative. + + // Arbitratily choose the sphere center as a contact point + contactPoints.append(sphere.center); + + // Find the least-negative penetration axis. + // + // We could have computed this during the loop over the axes, + // but since volume collisions are rare (they only occur with + // large time steps), this case will seldom be executed and + // should not be optimized at the expense of the others. + if (distOutsideBox.x > distOutsideBox.y) { + if (distOutsideBox.x > distOutsideBox.z) { + // Smallest penetration on x-axis + // Chose normal based on which side we're closest to. + // Keep in mind that this is a normal to the sphere, + // so it is the inverse of the box normal. + if (center.x > 0) { + contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitX())); + } else { + contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitX())); + } + depth = -distOutsideBox.x; + } else { + // Smallest penetration on z-axis + goto ZAXIS; + } + } else if (distOutsideBox.y > distOutsideBox.z) { + // Smallest penetration on y-axis + // Chose normal based on which side we're closest to. + // Keep in mind that this is a normal to the sphere, + // so it is the inverse of the box normal. + if (center.y > 0) { + contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitY())); + } else { + contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitY())); + } + depth = -distOutsideBox.y; + } else { + // Smallest on z-axis +ZAXIS: + // Chose normal based on which side we're closest to. + // Keep in mind that this is a normal to the sphere, + // so it is the inverse of the box normal. + if (center.z > 0) { + contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitZ())); + } else { + contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitZ())); + } + depth = -distOutsideBox.z; + } + break; + + default: + debugAssertM(false, "Fell through switch"); + break; + } + + return depth; +} + + +float CollisionDetection::penetrationDepthForFixedSphereFixedSphere( + const Sphere& sphereA, + const Sphere& sphereB, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + Vector3 axis = sphereB.center - sphereA.center; + double radius = sphereA.radius + sphereB.radius; + double mag = axis.magnitude(); + axis /= mag; + double depth = -(mag - radius); + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + if (depth >= 0) { + contactPoints.append(sphereA.center + axis * (sphereA.radius - depth / 2)); + contactNormals.append(axis); + } + + return depth; +} + + +float CollisionDetection::penetrationDepthForFixedSphereFixedPlane( + const Sphere& sphereA, + const Plane& planeB, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + Vector3 N; + double d; + + planeB.getEquation(N, d); + + double depth = -(sphereA.center.dot(N) + d - sphereA.radius); + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + if (depth >= 0) { + contactPoints.append(N * (depth - sphereA.radius) + sphereA.center); + contactNormals.append(N); + } + + return depth; +} + + +float CollisionDetection::penetrationDepthForFixedBoxFixedPlane( + const Box& box, + const Plane& plane, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + Vector3 N; + double d; + + plane.getEquation(N, d); + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + float lowest = (float)inf(); + for (int i = 0; i < 8; ++i) { + const Vector3 vertex = box.corner(i); + + float x = vertex.dot(N) + (float)d; + + if (x <= 0) { + // All vertices below the plane should be contact points. + contactPoints.append(vertex); + contactNormals.append(-N); + } + + lowest = min(lowest, x); + } + + // Depth should be a positive number + return -lowest; +} + + +float CollisionDetection::collisionTimeForMovingPointFixedPlane( + const Vector3& point, + const Vector3& velocity, + const Plane& plane, + Vector3& location, + Vector3& outNormal) { + + // Solve for the time at which normal.dot(point + velocity) + d == 0. + double d; + Vector3 normal; + plane.getEquation(normal, d); + + float vdotN = velocity.dot(normal); + float pdotN = point.dot(normal); + + if (fuzzyEq(pdotN + d, 0)) { + // The point is *in* the plane. + location = point; + outNormal = normal; + return 0; + } + + if (vdotN >= 0) { + // no collision will occur + location = Vector3::inf(); + return (float)inf(); + } + + float t = -(pdotN + d) / vdotN; + if (t < 0) { + location = Vector3::inf(); + return (float)inf(); + } else { + location = point + velocity * t; + outNormal = normal; + return t; + } +} + + +float CollisionDetection::collisionTimeForMovingPointFixedSphere( + const Vector3& point, + const Vector3& velocity, + const Sphere& sphere, + Vector3& location, + Vector3& outNormal) { + + double speed = velocity.magnitude(); + Vector3 direction = velocity / speed; + + Vector3 L = sphere.center - point; + double d = L.dot(direction); + + double L2 = L.dot(L); + double R2 = sphere.radius * sphere.radius; + double D2 = d * d; + + if ((d < 0) && (L2 > R2)) { + location = Vector3::inf(); + return inf(); + } + + double M2 = L2 - D2; + + if (M2 > R2) { + location = Vector3::inf(); + return inf(); + } + + double q = sqrt(R2 - M2); + double time; + + if (L2 > R2) { + time = d - q; + } else { + time = d + q; + } + + time /= speed; + + location = point + velocity * time; + outNormal = (location - sphere.center).direction(); + + return time; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedSphere( + const Sphere& movingSphere, + const Vector3& velocity, + const Sphere& fixedSphere, + Vector3& location, + Vector3& outNormal) { + + double time = collisionTimeForMovingPointFixedSphere(movingSphere.center, velocity, Sphere(fixedSphere.center, fixedSphere.radius + movingSphere.radius), location, outNormal); + + if (time < inf()) { + // Location is now the center of the moving sphere at the collision time. + // Adjust for the size of the moving sphere. Two spheres always collide + // along a line between their centers. + location += (location - fixedSphere.center) * movingSphere.radius / fixedSphere.radius; + } + + return time; +} + + +/* +float CollisionDetection::collisionTimeForMovingPointFixedTriangle( + const Vector3& point, + const Vector3& velocity, + const Triangle& triangle, + Vector3& outLocation, + Vector3& outNormal) { + + double time = collisionTimeForMovingPointFixedPlane(point, velocity, triangle.plane(), outLocation, outNormal); + + if (time == inf()) { + // No collision with the plane of the triangle. + return inf(); + } + + if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), outLocation, triangle.primaryAxis())) { + // Collision occured inside the triangle + return time; + } else { + // Missed the triangle + outLocation = Vector3::inf(); + return inf(); + } +}*/ + +/* +float CollisionDetection::collisionTimeForMovingPointFixedTriangle( + const Vector3& orig, + const Vector3& dir, + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2) { + + // Barycenteric coords + double u, v; + #define EPSILON 0.000001 + #define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + + #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + + #define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + + double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + + // find vectors for two edges sharing vert0 + SUB(edge1, vert1, vert0); + SUB(edge2, vert2, vert0); + + // begin calculating determinant - also used to calculate U parameter + CROSS(pvec, dir, edge2); + + // if determinant is near zero, ray lies in plane of triangle + const double det = DOT(edge1, pvec); + + if (det < EPSILON) { + return inf(); + } + + // calculate distance from vert0 to ray origin + SUB(tvec, orig, vert0); + + // calculate U parameter and test bounds + u = DOT(tvec, pvec); + if ((u < 0.0) || (u > det)) { + // Hit the plane outside the triangle + return inf(); + } + + // prepare to test V parameter + CROSS(qvec, tvec, edge1); + + // calculate V parameter and test bounds + v = DOT(dir, qvec); + if ((v < 0.0) || (u + v > det)) { + // Hit the plane outside the triangle + return inf(); + } + + // calculate t, scale parameters, ray intersects triangle + // If we want u,v, we can compute this + // double t = DOT(edge2, qvec); + //const double inv_det = 1.0 / det; + //t *= inv_det; + //u *= inv_det; + //v *= inv_det; + // return t; + + // Case where we don't need correct (u, v): + + const double t = DOT(edge2, qvec); + + if (t >= 0) { + // Note that det must be positive + return t / det; + } else { + // We had to travel backwards in time to intersect + return inf(); + } + + #undef EPSILON + #undef CROSS + #undef DOT + #undef SUB +} +*/ + +float CollisionDetection::collisionTimeForMovingPointFixedBox( + const Vector3& point, + const Vector3& velocity, + const Box& box, + Vector3& location, + Vector3& outNormal) { + + double bestTime; + + Vector3 normal; + Vector3 v[4]; + + // Prime the loop + int f = 0; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + bestTime = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], location, normal); + outNormal = normal; + + // Check other faces + for (f = 1; f < 6; ++f) { + Vector3 pos; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + float time = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], pos, normal); + if (time < bestTime) { + bestTime = time; + outNormal = normal; + location = pos; + } + } + + return bestTime; +} + + +float CollisionDetection::collisionTimeForMovingPointFixedAABox( + const Vector3& origin, + const Vector3& dir, + const AABox& box, + Vector3& location, + bool& Inside, + Vector3& normal) { + + if (collisionLocationForMovingPointFixedAABox(origin, dir, box, location, Inside, normal)) { + return (location - origin).magnitude(); + } else { + return (float)inf(); + } +} + + +bool CollisionDetection::collisionLocationForMovingPointFixedAABox( + const Vector3& origin, + const Vector3& dir, + const AABox& box, + Vector3& location, + bool& Inside, + Vector3& normal) { + + // Integer representation of a floating-point value. + #define IR(x) ((uint32&)x) + + Inside = true; + const Vector3& MinB = box.low(); + const Vector3& MaxB = box.high(); + Vector3 MaxT(-1.0f, -1.0f, -1.0f); + + // Find candidate planes. + for (int i = 0; i < 3; ++i) { + if (origin[i] < MinB[i]) { + location[i] = MinB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if (IR(dir[i])) { + MaxT[i] = (MinB[i] - origin[i]) / dir[i]; + } + } else if (origin[i] > MaxB[i]) { + location[i] = MaxB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if (IR(dir[i])) { + MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; + } + } + } + + if (Inside) { + // Ray origin inside bounding box + location = origin; + return false; + } + + // Get largest of the maxT's for final choice of intersection + int WhichPlane = 0; + if (MaxT[1] > MaxT[WhichPlane]) { + WhichPlane = 1; + } + + if (MaxT[2] > MaxT[WhichPlane]) { + WhichPlane = 2; + } + + // Check final candidate actually inside box + if (IR(MaxT[WhichPlane]) & 0x80000000) { + // Miss the box + return false; + } + + for (int i = 0; i < 3; ++i) { + if (i != WhichPlane) { + location[i] = origin[i] + MaxT[WhichPlane] * dir[i]; + if ((location[i] < MinB[i]) || + (location[i] > MaxB[i])) { + // On this plane we're outside the box extents, so + // we miss the box + return false; + } + } + } + + // Choose the normal to be the plane normal facing into the ray + normal = Vector3::zero(); + normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0; + + return true; + + #undef IR +} + + + +float CollisionDetection::collisionTimeForMovingPointFixedRectangle( + const Vector3& point, + const Vector3& velocity, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + Vector3& location, + Vector3& outNormal) { + + Plane plane = Plane(v0, v1, v2); + + float time = collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal); + + if (time == inf()) { + // No collision is ever going to happen + return time; + } + + if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) { + // The intersection point is inside the rectangle; that is the location where + // the point hits the rectangle. + return time; + } else { + return inf(); + } +} + +/** Used by findRayCapsuleIntersection. + @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp */ +static int findRayCapsuleIntersectionAux( + const Vector3& rkOrigin, + const Vector3& rkDirection, + const Capsule& rkCapsule, + double afT[2]) { + + Vector3 capsuleDirection = rkCapsule.point(1) - rkCapsule.point(0); + + // set up quadratic Q(t) = a*t^2 + 2*b*t + c + Vector3 kU, kV, kW = capsuleDirection; + float fWLength = kW.unitize(); + Vector3::generateOrthonormalBasis(kU, kV, kW); + Vector3 kD(kU.dot(rkDirection), kV.dot(rkDirection), kW.dot(rkDirection)); + float fDLength = kD.unitize(); + + float fEpsilon = 1e-6f; + + float fInvDLength = 1.0f/fDLength; + Vector3 kDiff = rkOrigin - rkCapsule.point(0); + Vector3 kP(kU.dot(kDiff),kV.dot(kDiff),kW.dot(kDiff)); + float fRadiusSqr = square(rkCapsule.radius()); + + float fInv, fA, fB, fC, fDiscr, fRoot, fT, fTmp; + + // Is the velocity parallel to the capsule direction? (or zero) + if ((abs(kD.z) >= 1.0f - fEpsilon) || (fDLength < fEpsilon)) { + + float fAxisDir = rkDirection.dot(capsuleDirection); + + fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y; + if ((fAxisDir < 0) && (fDiscr >= 0.0f)) { + // Velocity anti-parallel to the capsule direction + fRoot = sqrt(fDiscr); + afT[0] = (kP.z + fRoot)*fInvDLength; + afT[1] = -(fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } else if ((fAxisDir > 0) && (fDiscr >= 0.0f)) { + // Velocity parallel to the capsule direction + fRoot = sqrt(fDiscr); + afT[0] = -(kP.z + fRoot)*fInvDLength; + afT[1] = (fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } else { + // sphere heading wrong direction, or no velocity at all + return 0; + } + } + + // test intersection with infinite cylinder + fA = kD.x*kD.x + kD.y*kD.y; + fB = kP.x*kD.x + kP.y*kD.y; + fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr; + fDiscr = fB*fB - fA*fC; + if (fDiscr < 0.0f) { + // line does not intersect infinite cylinder + return 0; + } + + int iQuantity = 0; + + if (fDiscr > 0.0f) { + // line intersects infinite cylinder in two places + fRoot = sqrt(fDiscr); + fInv = 1.0f/fA; + fT = (-fB - fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + if ((0.0f <= fTmp) && (fTmp <= fWLength)) { + afT[iQuantity] = fT * fInvDLength; + iQuantity++; + } + + fT = (-fB + fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + + if ((0.0f <= fTmp) && (fTmp <= fWLength)) { + afT[iQuantity++] = fT*fInvDLength; + } + + if (iQuantity == 2) { + // line intersects capsule wall in two places + return 2; + } + } else { + // line is tangent to infinite cylinder + fT = -fB/fA; + fTmp = kP.z + fT*kD.z; + if ((0.0f <= fTmp) && (fTmp <= fWLength)) { + afT[0] = fT*fInvDLength; + return 1; + } + } + + // test intersection with bottom hemisphere + // fA = 1 + fB += kP.z*kD.z; + fC += kP.z*kP.z; + fDiscr = fB*fB - fC; + if (fDiscr > 0.0f) { + fRoot = sqrt(fDiscr); + fT = -fB - fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp <= 0.0f) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp <= 0.0f) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } else if (fDiscr == 0.0f) { + fT = -fB; + fTmp = kP.z + fT*kD.z; + if (fTmp <= 0.0f) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } + + // test intersection with top hemisphere + // fA = 1 + fB -= kD.z*fWLength; + fC += fWLength*(fWLength - 2.0f*kP.z); + + fDiscr = fB*fB - fC; + if (fDiscr > 0.0f) { + fRoot = sqrt(fDiscr); + fT = -fB - fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp >= fWLength) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp >= fWLength) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } else if (fDiscr == 0.0f) { + fT = -fB; + fTmp = kP.z + fT*kD.z; + if (fTmp >= fWLength) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } + + return iQuantity; +} + + +/** Used by collisionTimeForMovingPointFixedCapsule. + @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp + + @param rkRay The ray + @param rkCapsule The capsule + @param riQuantity The number of intersections found + @param akPoint The intersections found + @return True if there is at least one intersection + */ +static bool findRayCapsuleIntersection( + const Ray& rkRay, + const Capsule& rkCapsule, + int& riQuantity, + Vector3 akPoint[2]) { + + double afT[2]; + riQuantity = findRayCapsuleIntersectionAux(rkRay.origin, rkRay.direction, rkCapsule, afT); + + // Only return intersections that occur in the future + int iClipQuantity = 0; + int i; + for (i = 0; i < riQuantity; i++) { + if (afT[i] >= 0.0f) { + akPoint[iClipQuantity] = rkRay.origin + afT[i] * rkRay.direction; + iClipQuantity++; + } + } + + riQuantity = iClipQuantity; + return (riQuantity > 0); +} + +float CollisionDetection::collisionTimeForMovingPointFixedCapsule( + const Vector3& _point, + const Vector3& velocity, + const Capsule& capsule, + Vector3& location, + Vector3& outNormal) { + + float timeScale = velocity.magnitude(); + + if (timeScale == 0.0f) { + timeScale = 1; + } + + Vector3 direction = velocity / timeScale; + int numIntersections; + Vector3 intersection[2]; + findRayCapsuleIntersection(Ray::fromOriginAndDirection(_point, direction), capsule, numIntersections, intersection); + + if (numIntersections == 2) { + // A collision can only occur if there are two intersections. If there is one + // intersection, that one is exiting the capsule. + + // Find the entering intersection (the first one that occurs). + float d0 = (intersection[0] - _point).squaredMagnitude(); + float d1 = (intersection[1] - _point).squaredMagnitude(); + + // Compute the surface normal (if we aren't ignoring the result) + if (&outNormal != &ignore) { + Vector3 p2 = LineSegment::fromTwoPoints(capsule.point(0), capsule.point(1)).closestPoint(_point); + outNormal = (_point - p2).direction(); + } + + if (d0 > d1) { + location = intersection[1]; + return sqrt(d1) / timeScale; + } else { + location = intersection[0]; + return sqrt(d0) / timeScale; + } + } else { + // No entering intersection discovered; return no intersection. + location = Vector3::inf(); + return inf(); + } +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedPlane( + const Sphere& sphere, + const Vector3& velocity, + const Plane& plane, + Vector3& location, + Vector3& outNormal) { + + if (sphere.radius == 0) { + // Optimization for zero radius sphere + return collisionTimeForMovingPointFixedPlane(sphere.center, velocity, plane, location, outNormal); + } + + // The collision point on the sphere will be the point at + // center - (radius * normal). Collisions only occur when + // the sphere is travelling into the plane. + + double d; + plane.getEquation(outNormal, d); + + double vdotN = velocity.dot(outNormal); + + if (fuzzyGt(vdotN, 0)) { + // No collision when the sphere is moving towards a backface. + location = Vector3::inf(); + return (float)inf(); + } + + float cdotN = sphere.center.dot(outNormal); + + // Distance from the center to the plane + float distance = cdotN + (float)d; + + // Where is the collision on the sphere? + Vector3 point = sphere.center - (sphere.radius * outNormal); + + if (fuzzyLe(G3D::abs(distance), sphere.radius)) { + // Already interpenetrating + location = sphere.center - distance * outNormal; + return 0; + } else { + return collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal); + } + +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedTriangle( + const class Sphere& sphere, + const Vector3& velocity, + const Triangle& triangle, + Vector3& outLocation, + float b[3]) { + + Vector3 dummy; + float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, triangle.plane(), + outLocation, dummy); + + if (time == inf()) { + // No collision is ever going to happen + return time; + } + + // We will hit the plane of the triangle at *time*. See if + // the intersection point actually is within the triangle. + + if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), + outLocation, b, triangle.primaryAxis())) { + + // The intersection point is inside the triangle; that is the location where + // the sphere hits the triangle. + +# ifdef G3D_DEBUG + { + // Internal consistency checks + debugAssertM(b[0] >= 0.0 && b[0] <= 1.0f, "Intersection is outside triangle."); + debugAssertM(b[1] >= 0.0 && b[1] <= 1.0f, "Intersection is outside triangle."); + debugAssertM(b[2] >= 0.0 && b[2] <= 1.0f, "Intersection is outside triangle."); + Vector3 blend = + b[0] * triangle.vertex(0) + + b[1] * triangle.vertex(1) + + b[2] * triangle.vertex(2); + debugAssertM(blend.fuzzyEq(outLocation), "Barycentric coords don't match intersection."); + // Call again so that we can debug the problem + // isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), + // outLocation, b, triangle.primaryAxis()); + } +# endif + + return time; + } + + // The collision (if it exists) is with a point on the triangle perimeter. + // Switch over to moving the triangle towards a fixed sphere and see at what time + // they will hit. + + // Closest point on the triangle to the sphere intersection with the plane. + int edgeIndex; + const Vector3& point = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection, + triangle.edgeMagnitude, outLocation, edgeIndex); + + float t = 0; + if (! sphere.contains(point)) { + // The point is outside the sphere--see when it will hit + t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy); + } + + if (t < inf()) { + outLocation = point; + // Compute Barycentric coords + + // Index of the next vertex + static const int next[] = {1, 2, 0}; + + // Project along the edge in question. + // Avoid sqrt by taking advantage of the existing edgeDirection unit vector. + b[next[edgeIndex]] = (outLocation - triangle._vertex[edgeIndex]).dot + (triangle.edgeDirection[edgeIndex]) / triangle.edgeMagnitude[edgeIndex]; + + b[edgeIndex] = 1.0f - b[next[edgeIndex]]; + + b[next[next[edgeIndex]]] = 0.0f; + +# ifdef G3D_DEBUG + { + // Internal consistency checks + for (int i = 0; i < 3; ++i) { + debugAssertM(fuzzyGe(b[i], 0.0f) && fuzzyLe(b[i], 1.0f), "Intersection is outside triangle."); + } + Vector3 blend = + b[0] * triangle.vertex(0) + + b[1] * triangle.vertex(1) + + b[2] * triangle.vertex(2); + debugAssertM(blend.fuzzyEq(outLocation), + format("Barycentric coords don't match intersection. %s != %s", + blend.toString().c_str(), + outLocation.toString().c_str())); + + // Call again so that we can debug the problem + collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy); + } +# endif + + // Due to tiny roundoffs, these values might be slightly out of bounds. + // Ensure that they are legal. Note that the above debugging code + // verifies that we are not clamping truly illegal values. + for (int i = 0; i < 3; ++i) { + b[i] = clamp(b[i], 0.0f, 1.0f); + } + } + + // The collision occured at the point, if it occured. The normal + // was the plane normal, computed above. + + return t; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedRectangle( + const Sphere& sphere, + const Vector3& velocity, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + Vector3& location, + Vector3& outNormal) { + + Plane plane(v0, v1, v2); + + float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, plane, location, outNormal); + + if (time == inf()) { + // No collision is ever going to happen + return time; + } + + if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) { + // The intersection point is inside the rectangle; that is the location where + // the sphere hits the rectangle. + return time; + } + + // Switch over to moving the rectangle towards a fixed sphere and see at what time + // they will hit. + + Vector3 point = closestPointToRectanglePerimeter(v0, v1, v2, v3, sphere.center); + + Vector3 dummy; + double t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, location, dummy); + + // Normal is the plane normal, location is the original location of the point. + location = point; + + return t; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedBox( + const Sphere& sphere, + const Vector3& velocity, + const Box& box, + Vector3& location, + Vector3& outNormal) { + + if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) { + // TODO: Compute more useful location and normal? + location = sphere.center; + outNormal = Vector3::zero(); + return 0; + } + + float bestTime; + + Vector3 v[4]; + int f = 0; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + bestTime = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], location, outNormal); + + for (f = 1; f < 6; ++f) { + Vector3 pos, normal; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + float time = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], pos, normal); + if (time < bestTime) { + bestTime = time; + location = pos; + outNormal = normal; + } + } + + return bestTime; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedCapsule( + const Sphere& sphere, + const Vector3& velocity, + const Capsule& capsule, + Vector3& location, + Vector3& outNormal) { + + (void)outNormal; + + Capsule _capsule(capsule.point(0), capsule.point(1), capsule.radius() + sphere.radius); + + Vector3 normal; + double time = collisionTimeForMovingPointFixedCapsule(sphere.center, velocity, _capsule, location, normal); + + if (time < inf()) { + // Location is now the position of the center of the sphere at the time of collision. + // We have to adjust the collision location for the size of the sphere. + location -= sphere.radius * normal; + } + + return time; +} + + +Vector3 CollisionDetection::bounceDirection( + const Sphere& sphere, + const Vector3& velocity, + const float collisionTime, + const Vector3& collisionLocation, + const Vector3& collisionNormal) { + + // Location when the collision occurs + Vector3 sphereLocation = sphere.center + velocity * collisionTime; + + Vector3 normal = (sphereLocation - collisionLocation); + if (fuzzyEq(normal.squaredMagnitude(), 0)) { + normal = collisionNormal; + } else { + normal.unitize(); + } + + Vector3 direction = velocity.direction(); + + // Reflect direction about the normal + return direction - 2.0 * normal * normal.dot(direction); +} + + +Vector3 CollisionDetection::slideDirection( + const Sphere& sphere, + const Vector3& velocity, + const float collisionTime, + const Vector3& collisionLocation) { + + Vector3 sphereLocation = sphere.center + velocity * collisionTime; + Vector3 normal = (sphereLocation - collisionLocation).direction(); + Vector3 direction = velocity.direction(); + + // subtract off the part in the direction away from the normal. + return direction - normal * normal.dot(direction); +} + + +Vector3 CollisionDetection::closestPointOnLineSegment( + const Vector3& v0, + const Vector3& v1, + const Vector3& point) { + + const Vector3& edge = (v1 - v0); + float edgeLength = edge.magnitude(); + + if (edgeLength == 0) { + // The line segment is a point + return v0; + } + + return closestPointOnLineSegment(v0, v1, edge / edgeLength, edgeLength, point); +} + + +Vector3 CollisionDetection::closestPointOnLineSegment( + const Vector3& v0, + const Vector3& v1, + const Vector3& edgeDirection, + const float edgeLength, + const Vector3& point) { + + debugAssert((v1 - v0).direction().fuzzyEq(edgeDirection)); + debugAssert(fuzzyEq((v1 - v0).magnitude(), edgeLength)); + + // Vector towards the point + const Vector3& c = point - v0; + + // Projected onto the edge itself + float t = edgeDirection.dot(c); + + if (t <= 0) { + // Before the start + return v0; + } else if (t >= edgeLength) { + // After the end + return v1; + } else { + // At distance t along the edge + return v0 + edgeDirection * t; + } +} + + +Vector3 CollisionDetection::closestPointOnTrianglePerimeter( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& point) { + + Vector3 v[3] = {v0, v1, v2}; + Vector3 edgeDirection[3] = {(v1 - v0), (v2 - v1), (v0 - v2)}; + float edgeLength[3]; + + for (int i = 0; i < 3; ++i) { + edgeLength[i] = edgeDirection[i].magnitude(); + edgeDirection[i] /= edgeLength[i]; + } + + int edgeIndex; + return closestPointOnTrianglePerimeter(v, edgeDirection, edgeLength, point, edgeIndex); +} + + +Vector3 CollisionDetection::closestPointOnTrianglePerimeter( + const Vector3 v[3], + const Vector3 edgeDirection[3], + const float edgeLength[3], + const Vector3& point, + int& edgeIndex) { + + // Closest point on segment from v[i] to v[i + 1] + Vector3 r[3]; + + // Distance squared from r[i] to point + float d[3]; + + // Index of the next point + static const int next[] = {1, 2, 0}; + + for (int i = 0; i < 3; ++i) { + r[i] = closestPointOnLineSegment(v[i], v[next[i]], edgeDirection[i], edgeLength[i], point); + d[i] = (r[i] - point).squaredMagnitude(); + } + + if (d[0] < d[1]) { + if (d[0] < d[2]) { + // Between v0 and v1 + edgeIndex = 0; + } else { + // Between v2 and v0 + edgeIndex = 2; + } + } else { + if (d[1] < d[2]) { + // Between v1 and v2 + edgeIndex = 1; + } else { + // Between v2 and v0 + edgeIndex = 2; + } + } + +# ifdef G3D_DEBUG + { + Vector3 diff = r[edgeIndex] - v[edgeIndex]; + debugAssertM(fuzzyEq(diff.direction().dot(edgeDirection[edgeIndex]), 1.0f) || + diff.fuzzyEq(Vector3::zero()), "Point not on correct triangle edge"); + float frac = diff.dot(edgeDirection[edgeIndex])/edgeLength[edgeIndex]; + debugAssertM(frac >= -0.000001, "Point off low side of edge."); + debugAssertM(frac <= 1.000001, "Point off high side of edge."); + } +# endif + + return r[edgeIndex]; +} + + +bool CollisionDetection::isPointInsideTriangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& normal, + const Vector3& point, + float b[3], + Vector3::Axis primaryAxis) { + + if (primaryAxis == Vector3::DETECT_AXIS) { + primaryAxis = normal.primaryAxis(); + } + + // Check that the point is within the triangle using a Barycentric + // coordinate test on a two dimensional plane. + int i, j; + + switch (primaryAxis) { + case Vector3::X_AXIS: + i = Vector3::Y_AXIS; + j = Vector3::Z_AXIS; + break; + + case Vector3::Y_AXIS: + i = Vector3::Z_AXIS; + j = Vector3::X_AXIS; + break; + + case Vector3::Z_AXIS: + i = Vector3::X_AXIS; + j = Vector3::Y_AXIS; + break; + + default: + // This case is here to supress a warning on Linux + i = j = 0; + debugAssertM(false, "Should not get here."); + break; + } + + // See if all barycentric coordinates are non-negative + + // 2D area via cross product +# define AREA2(d, e, f) (((e)[i] - (d)[i]) * ((f)[j] - (d)[j]) - ((f)[i] - (d)[i]) * ((e)[j] - (d)[j])) + + // Area of the polygon + float area = AREA2(v0, v1, v2); + if (area == 0) { + // This triangle has zero area, so the point must not + // be in it unless the triangle point is the test point. + return (v0 == point); + } + + debugAssert(area != 0); + + float invArea = 1.0f / area; + + // (avoid normalization until absolutely necessary) + b[0] = AREA2(point, v1, v2) * invArea; + + if ((b[0] < 0.0f) || (b[0] > 1.0f)) { + return false; + } + + b[1] = AREA2(v0, point, v2) * invArea; + if ((b[1] < 0.0f) || (b[1] > 1.0f)) { + return false; + } + + b[2] = 1.0f - b[0] - b[1]; + +# undef AREA2 + + return (b[2] >= 0.0f) && (b[2] <= 1.0f); +} + + +bool CollisionDetection::isPointInsideRectangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& normal, + const Vector3& point) { + + return isPointInsideTriangle(v0, v1, v2, normal, point) || + isPointInsideTriangle(v2, v3, v0, normal, point); +} + + +Vector3 CollisionDetection::closestPointToRectanglePerimeter( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& point) { + + Vector3 r0 = closestPointOnLineSegment(v0, v1, point); + Vector3 r1 = closestPointOnLineSegment(v1, v2, point); + Vector3 r2 = closestPointOnLineSegment(v2, v3, point); + Vector3 r3 = closestPointOnLineSegment(v3, v0, point); + + double d0 = (r0 - point).squaredMagnitude(); + double d1 = (r1 - point).squaredMagnitude(); + double d2 = (r2 - point).squaredMagnitude(); + double d3 = (r3 - point).squaredMagnitude(); + + if (d0 < d1) { + if (d0 < d2) { + if (d0 < d3) { + return r0; + } else { + return r3; + } + } else { + if (d2 < d3) { + return r2; + } else { + return r3; + } + } + } else { + if (d1 < d2) { + if (d1 < d3) { + return r1; + } else { + return r3; + } + } else { + if (d2 < d3) { + return r2; + } else { + return r3; + } + } + } +} + + +Vector3 CollisionDetection::closestPointToRectangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& point) { + + Plane plane(v0, v1, v2); + + // Project the point into the plane + double a, b, c, d; + plane.getEquation(a, b, c, d); + + double distance = a*point.x + b*point.y + c*point.z + d; + Vector3 planePoint = point - distance * plane.normal(); + + if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), planePoint)) { + return planePoint; + } else { + return closestPointToRectanglePerimeter(v0, v1, v2, v3, planePoint); + } +} + + +bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidSphere( + const Sphere& sphere1, + const Sphere& sphere2) { + + return (sphere1.center - sphere2.center).squaredMagnitude() < square(sphere1.radius + sphere2.radius); +} + + +bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidBox( + const Sphere& sphere, + const Box& box) { + + // If the center of the sphere is within the box, the whole + // sphere is within the box. + if (box.contains(sphere.center)) { + return true; + } + + float r2 = square(sphere.radius); + + // Find the closest point on the surface of the box to the sphere. If + // this point is within the sphere's radius, they intersect. + int f; + for (f = 0; f < 6; ++f) { + Vector3 v0, v1, v2, v3; + box.getFaceCorners(f, v0, v1, v2, v3); + if ((closestPointToRectangle(v0, v1, v2, v3, sphere.center) - sphere.center).squaredMagnitude() <= r2) { + return true; + } + } + + return false; +} + + +bool CollisionDetection::movingSpherePassesThroughFixedBox( + const Sphere& sphere, + const Vector3& velocity, + const Box& box, + double timeLimit) { + + // If they intersect originally, they definitely pass through each other. + if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) { + return true; + } + + // See if the sphere hits the box during the time period. + Vector3 dummy1, dummy2; + + return (collisionTimeForMovingSphereFixedBox(sphere, velocity, box, dummy1, dummy2) < timeLimit); +} + + +bool CollisionDetection::movingSpherePassesThroughFixedSphere( + const Sphere& sphere, + const Vector3& velocity, + const Sphere& fixedSphere, + double timeLimit) { + + if (fixedSolidSphereIntersectsFixedSolidSphere(sphere, fixedSphere)) { + return true; + } + + // Extend the fixed sphere by the radius of the moving sphere + Sphere bigFixed(fixedSphere.center, fixedSphere.radius + sphere.radius); + Vector3 dummy1, dummy2; + + // If the sphere collides with the other sphere during the time limit, it passes through + return (collisionTimeForMovingPointFixedSphere(sphere.center, velocity, bigFixed, dummy1, dummy2) < timeLimit); +} + + + +bool CollisionDetection::fixedSolidSphereIntersectsFixedTriangle( + const Sphere& sphere, + const Triangle& triangle) { + + // How far is the sphere from the plane of the triangle + const Plane& plane = triangle.plane(); + + // Does the closest point to the sphere center lie within the triangle? + Vector3 v = plane.closestPoint(sphere.center); + + // Is the closest point to the plane within the sphere? + if ((v - sphere.center).squaredLength() <= square(sphere.radius)) { + // Is it also within the triangle? + float b[3]; + if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), + v, b, triangle.primaryAxis())){ + // The closest point is inside the triangle + return true; + } + } + + // ignored + int edgeIndex; + + v = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection, triangle.edgeMagnitude, sphere.center, edgeIndex); + + // Is the closest point within the sphere? + return ((v - sphere.center).squaredLength() <= square(sphere.radius)); +} + + +} // namespace +#ifdef _MSC_VER +#pragma warning (pop) +#endif diff --git a/externals/g3dlite/G3D.lib/source/Color1.cpp b/externals/g3dlite/G3D.lib/source/Color1.cpp new file mode 100644 index 00000000000..7e058a27d1a --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Color1.cpp @@ -0,0 +1,40 @@ +/** + @file Color1.cpp + + Color class. + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-30 + @edited 2007-01-30 + */ + +#include "G3D/platform.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Color1::Color1(BinaryInput& bi) { + deserialize(bi); +} + + +void Color1::deserialize(BinaryInput& bi) { + value = bi.readFloat32(); +} + + +void Color1::serialize(BinaryOutput& bo) const { + bo.writeFloat32(value); +} + + +Color1::Color1(const class Color1uint8& other) { + value = other.value / 255.0f; +} + +} // namespace G3D + diff --git a/externals/g3dlite/G3D.lib/source/Color1uint8.cpp b/externals/g3dlite/G3D.lib/source/Color1uint8.cpp new file mode 100644 index 00000000000..0fd00693d4c --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Color1uint8.cpp @@ -0,0 +1,38 @@ +/** + @file Color1uint8.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-30 + @edited 2007-01-30 + */ + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color1.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Color1uint8::Color1uint8(const class Color1& c) : value(iClamp(iFloor(c.value * 256), 0, 255)) { +} + + +Color1uint8::Color1uint8(class BinaryInput& bi) { + deserialize(bi); +} + + +void Color1uint8::serialize(class BinaryOutput& bo) const { + bo.writeUInt8(value); +} + + +void Color1uint8::deserialize(class BinaryInput& bi) { + value = bi.readUInt8(); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/Color3.cpp b/externals/g3dlite/G3D.lib/source/Color3.cpp new file mode 100644 index 00000000000..8183d8a0f62 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Color3.cpp @@ -0,0 +1,321 @@ +/** + @file Color3.cpp + + Color class. + + @author Morgan McGuire, matrix@graphics3d.com + @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com + + + @created 2001-06-02 + @edited 2006-01-13 + */ + +#include "G3D/platform.h" +#include <stdlib.h> +#include "G3D/Color3.h" +#include "G3D/Vector3.h" +#include "G3D/format.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Color3uint8.h" + +namespace G3D { + +const Color3& Color3::red() { + static Color3 c(1.0f, 0.0f, 0.0f); + return c; +} + + +const Color3& Color3::green() { + static Color3 c(0.0f, 1.0f, 0.0f); + return c; +} + + +const Color3& Color3::blue() { + static Color3 c(0.0f, 0.0f, 1.0f); + return c; +} + + +const Color3& Color3::purple() { + static Color3 c(0.7f, 0.0f, 1.0f); + return c; +} + + +const Color3& Color3::cyan() { + static Color3 c(0.0f, 0.7f, 1.0f); + return c; +} + + +const Color3& Color3::yellow() { + static Color3 c(1.0f, 1.0f, 0.0f); + return c; +} + + +const Color3& Color3::brown() { + static Color3 c(0.5f, 0.5f, 0.0f); + return c; +} + + +const Color3& Color3::orange() { + static Color3 c(1.0f, 0.5f, 0.0f); + return c; +} + + +const Color3& Color3::black() { + static Color3 c(0.0f, 0.0f, 0.0f); + return c; +} + +const Color3& Color3::zero() { + static Color3 c(0.0f, 0.0f, 0.0f); + return c; +} + + +const Color3& Color3::one() { + static Color3 c(1.0f, 1.0f, 1.0f); + return c; +} + + +const Color3& Color3::gray() { + static Color3 c(0.7f, 0.7f, 0.7f); + return c; +} + + +const Color3& Color3::white() { + static Color3 c(1, 1, 1); + return c; +} + + +Color3::Color3(BinaryInput& bi) { + deserialize(bi); +} + + +void Color3::deserialize(BinaryInput& bi) { + r = bi.readFloat32(); + g = bi.readFloat32(); + b = bi.readFloat32(); +} + + +void Color3::serialize(BinaryOutput& bo) const { + bo.writeFloat32(r); + bo.writeFloat32(g); + bo.writeFloat32(b); +} + + +const Color3& Color3::wheelRandom() { + static const Color3 colorArray[8] = + {Color3::blue(), Color3::red(), Color3::green(), + Color3::orange(), Color3::yellow(), + Color3::cyan(), Color3::purple(), Color3::brown()}; + + return colorArray[iRandom(0, 7)]; +} + + +size_t Color3::hashCode() const { + unsigned int rhash = (*(int*)(void*)(&r)); + unsigned int ghash = (*(int*)(void*)(&g)); + unsigned int bhash = (*(int*)(void*)(&b)); + + return rhash + (ghash * 37) + (bhash * 101); +} + + +Color3::Color3(const Vector3& v) { + r = v.x; + g = v.y; + b = v.z; +} + + +Color3::Color3(const class Color3uint8& other) { + r = other.r / 255.0f; + g = other.g / 255.0f; + b = other.b / 255.0f; +} + + +Color3 Color3::fromARGB(uint32 x) { + return Color3((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF)) / 255.0f; +} + +//---------------------------------------------------------------------------- + + +Color3 Color3::random() { + return Color3(uniformRandom(), + uniformRandom(), + uniformRandom()).direction(); +} + +//---------------------------------------------------------------------------- +Color3 Color3::operator/ (float fScalar) const { + Color3 kQuot; + + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + kQuot.r = fInvScalar * r; + kQuot.g = fInvScalar * g; + kQuot.b = fInvScalar * b; + return kQuot; + + } else { + + return Color3((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); + } +} + +//---------------------------------------------------------------------------- +Color3& Color3::operator/= (float fScalar) { + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + r *= fInvScalar; + g *= fInvScalar; + b *= fInvScalar; + } else { + r = (float)G3D::inf(); + g = (float)G3D::inf(); + b = (float)G3D::inf(); + } + + return *this; +} + +//---------------------------------------------------------------------------- +float Color3::unitize (float fTolerance) { + float fLength = length(); + + if ( fLength > fTolerance ) { + float fInvLength = 1.0f / fLength; + r *= fInvLength; + g *= fInvLength; + b *= fInvLength; + } else { + fLength = 0.0f; + } + + return fLength; +} + +//---------------------------------------------------------------------------- +Color3 Color3::fromHSV(const Vector3& _hsv) { + debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f) + && (_hsv.y <= 1.0f && _hsv.y >= 0.0f) + && ( _hsv.z <= 1.0f && _hsv.z >= 0.0f), "H,S,V must be between [0,1]"); + const int i = G3D::iFloor(6.0*_hsv.x); + const float f = 6.0f * _hsv.x - i; + const float m = _hsv.z * (1.0f - (_hsv.y)); + const float n = _hsv.z * (1.0f - (_hsv.y * f)); + const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f))); + switch(i) { + case 0: + return Color3(_hsv.z, k, m); + + case 1: + return Color3(n, _hsv.z, m); + + case 2: + return Color3(m, _hsv.z, k); + + case 3: + return Color3(m, n, _hsv.z); + + case 4: + return Color3(k, m, _hsv.z); + + case 5: + return Color3(_hsv.z, m, n); + + default: + debugAssertM(false, "fell through switch.."); + } + return Color3::black(); +} + + +Vector3 Color3::toHSV(const Color3& _rgb) { + debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f) + && (_rgb.g <= 1.0f && _rgb.g >= 0.0f) + && (_rgb.b <= 1.0f && _rgb.b >= 0.0f), "R,G,B must be between [0,1]"); + Vector3 hsv = Vector3::zero(); + hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b); + if (G3D::fuzzyEq(hsv.z, 0.0f)) { + return hsv; + } + + const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b); + hsv.y = (hsv.z - x) / hsv.z; + + if (G3D::fuzzyEq(hsv.y, 0.0f)) { + return hsv; + } + + Vector3 rgbN; + rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x); + rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x); + rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x); + + if (_rgb.r == hsv.z) { // note from the max we know that it exactly equals one of the three. + hsv.x = (_rgb.g == x)? 5.0f + rgbN.z : 1.0f - rgbN.y; + } else if (_rgb.g == hsv.z) { + hsv.x = (_rgb.b == x)? 1.0f + rgbN.x : 3.0f - rgbN.z; + } else { + hsv.x = (_rgb.r == x)? 3.0f + rgbN.y : 5.0f - rgbN.x; + } + + hsv.x /= 6.0f; + + return hsv; +} + +Color3 Color3::jetColorMap(const float& val) { + debugAssertM(val <= 1.0f && val >= 0.0f , "value should be in [0,1]"); + + //truncated triangles where sides have slope 4 + Color3 jet; + + jet.r = G3D::min(4.0f * val - 1.5f,-4.0f * val + 4.5f) ; + jet.g = G3D::min(4.0f * val - 0.5f,-4.0f * val + 3.5f) ; + jet.b = G3D::min(4.0f * val + 0.5f,-4.0f * val + 2.5f) ; + + + jet.r = G3D::clamp(jet.r, 0.0f, 1.0f); + jet.g = G3D::clamp(jet.g, 0.0f, 1.0f); + jet.b = G3D::clamp(jet.b, 0.0f, 1.0f); + + return jet; +} + + + + + +std::string Color3::toString() const { + return G3D::format("(%g, %g, %g)", r, g, b); +} + +//---------------------------------------------------------------------------- + +Color3 Color3::rainbowColorMap(float hue) { + return fromHSV(Vector3(hue, 1.0f, 1.0f)); +} + + +}; // namespace + diff --git a/externals/g3dlite/G3D.lib/source/Color3uint8.cpp b/externals/g3dlite/G3D.lib/source/Color3uint8.cpp new file mode 100644 index 00000000000..837bf1b2c8b --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Color3uint8.cpp @@ -0,0 +1,45 @@ +/** + @file Color3uint8.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2003-04-07 + @edited 2006-01-07 + */ + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color3.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Color3uint8::Color3uint8(const class Color3& c) { + r = iMin(255, iFloor(c.r * 256)); + g = iMin(255, iFloor(c.g * 256)); + b = iMin(255, iFloor(c.b * 256)); +} + + +Color3uint8::Color3uint8(class BinaryInput& bi) { + deserialize(bi); +} + + +void Color3uint8::serialize(class BinaryOutput& bo) const { + bo.writeUInt8(r); + bo.writeUInt8(g); + bo.writeUInt8(b); +} + + +void Color3uint8::deserialize(class BinaryInput& bi) { + r = bi.readUInt8(); + g = bi.readUInt8(); + b = bi.readUInt8(); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/Color4.cpp b/externals/g3dlite/G3D.lib/source/Color4.cpp new file mode 100644 index 00000000000..ed2e91291a1 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Color4.cpp @@ -0,0 +1,140 @@ +/** + @file Color4.cpp + + Color class. + + @author Morgan McGuire, matrix@graphics3d.com + @cite Portions by Laura Wollstadt, graphics3d.com + @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com + + + @created 2002-06-25 + @edited 2006-01-10 + */ + +#include <stdlib.h> +#include "G3D/Color4.h" +#include "G3D/Color4uint8.h" +#include "G3D/Vector4.h" +#include "G3D/format.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +const Color4& Color4::zero() { + static Color4 c(0.0f, 0.0f, 0.0f, 0.0f); + return c; +} + + +const Color4& Color4::inf() { + static Color4 c((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); + return c; +} + + +const Color4& Color4::clear() { + return Color4::zero(); +} + + +Color4::Color4(const Vector4& v) { + r = v.x; + g = v.y; + b = v.z; + a = v.w; +} + + +Color4::Color4(const Color4uint8& c) : r(c.r), g(c.g), b(c.b), a(c.a) { + *this /= 255.0f; +} + +size_t Color4::hashCode() const { + unsigned int rhash = (*(int*)(void*)(&r)); + unsigned int ghash = (*(int*)(void*)(&g)); + unsigned int bhash = (*(int*)(void*)(&b)); + unsigned int ahash = (*(int*)(void*)(&a)); + + return rhash + (ghash * 37) + (bhash * 101) + (ahash * 241); +} + +Color4 Color4::fromARGB(uint32 x) { + return Color4( + (float)((x >> 16) & 0xFF), + (float)((x >> 8) & 0xFF), + (float)(x & 0xFF), + (float)((x >> 24) & 0xFF)) / 255.0; +} + + +Color4::Color4(BinaryInput& bi) { + deserialize(bi); +} + + +void Color4::deserialize(BinaryInput& bi) { + r = bi.readFloat32(); + g = bi.readFloat32(); + b = bi.readFloat32(); + a = bi.readFloat32(); +} + + +void Color4::serialize(BinaryOutput& bo) const { + bo.writeFloat32(r); + bo.writeFloat32(g); + bo.writeFloat32(b); + bo.writeFloat32(a); +} + + +//---------------------------------------------------------------------------- + +Color4 Color4::operator/ (float fScalar) const { + Color4 kQuot; + + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + kQuot.r = fInvScalar * r; + kQuot.g = fInvScalar * g; + kQuot.b = fInvScalar * b; + kQuot.a = fInvScalar * a; + return kQuot; + + } else { + + return Color4::inf(); + } +} + +//---------------------------------------------------------------------------- + +Color4& Color4::operator/= (float fScalar) { + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + r *= fInvScalar; + g *= fInvScalar; + b *= fInvScalar; + a *= fInvScalar; + } else { + r = (float)G3D::inf(); + g = (float)G3D::inf(); + b = (float)G3D::inf(); + a = (float)G3D::inf(); + } + + return *this; +} + +//---------------------------------------------------------------------------- + +std::string Color4::toString() const { + return G3D::format("(%g, %g, %g, %g)", r, g, b, a); +} + +//---------------------------------------------------------------------------- + +}; // namespace + diff --git a/externals/g3dlite/G3D.lib/source/Color4uint8.cpp b/externals/g3dlite/G3D.lib/source/Color4uint8.cpp new file mode 100644 index 00000000000..8c8636a742e --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Color4uint8.cpp @@ -0,0 +1,47 @@ +/** + @file Color4uint8.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2003-04-07 + @edited 2006-01-07 + */ +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Color4uint8.h" +#include "G3D/Color4.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Color4uint8::Color4uint8(const class Color4& c) { + r = iMin(255, iFloor(c.r * 256)); + g = iMin(255, iFloor(c.g * 256)); + b = iMin(255, iFloor(c.b * 256)); + a = iMin(255, iFloor(c.a * 256)); +} + + +Color4uint8::Color4uint8(class BinaryInput& bi) { + deserialize(bi); +} + + +void Color4uint8::serialize(class BinaryOutput& bo) const { + bo.writeUInt8(r); + bo.writeUInt8(g); + bo.writeUInt8(b); + bo.writeUInt8(a); +} + + +void Color4uint8::deserialize(class BinaryInput& bi) { + r = bi.readUInt8(); + g = bi.readUInt8(); + b = bi.readUInt8(); + a = bi.readUInt8(); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/Cone.cpp b/externals/g3dlite/G3D.lib/source/Cone.cpp new file mode 100644 index 00000000000..99b29b5b0af --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Cone.cpp @@ -0,0 +1,79 @@ +/** + @file Cone.cpp + + Cone class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-07-09 + @edited 2006-01-29 +*/ + +#include "G3D/platform.h" +#include "G3D/Cone.h" +#include "G3D/Line.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" + +namespace G3D { + +Cone::Cone(const Vector3 &tip, const Vector3 &direction, float angle) { + this->tip = tip; + this->direction = direction.direction(); + this->angle = angle; + + debugAssert(angle >= 0); + debugAssert(angle <= pi()); +} + +/** + Forms the smallest cone that contains the box. Undefined if + the tip is inside or on the box. + */ +Cone::Cone(const Vector3& tip, const Box& box) { + this->tip = tip; + this->direction = (box.center() - tip).direction(); + + // Find the biggest angle + float smallestDotProduct = direction.dot((box.corner(0) - tip).direction()); + + for (int i = 1; i < 8; ++i) { + float dp = direction.dot((box.corner(i) - tip).direction()); + + debugAssert(dp > 0); + + if (dp < smallestDotProduct) { + smallestDotProduct = dp; + } + } + + angle = acosf(smallestDotProduct); +} + + +bool Cone::intersects(const Sphere& b) const { + // If the bounding sphere contains the tip, then + // they definitely touch. + if (b.contains(this->tip)) { + return true; + } + + // Move the tip backwards, effectively making the cone bigger + // to account for the radius of the sphere. + + Vector3 tip = this->tip - direction * b.radius / sinf(angle); + + return Cone(tip, direction, angle).contains(b.center); +} + + +bool Cone::contains(const Vector3& v) const { + + Vector3 d = (v - tip).direction(); + + float x = d.dot(direction); + + return (x > 0) && (x >= cosf(angle)); +} + +}; // namespace diff --git a/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp b/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp new file mode 100644 index 00000000000..76dbe21a7c4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp @@ -0,0 +1,449 @@ +/** + @file ConvexPolyhedron.cpp + + @author Morgan McGuire, morgan@graphics3d.com + + @created 2001-11-11 + @edited 2006-01-10 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/platform.h" +#include "G3D/ConvexPolyhedron.h" +#include "G3D/debug.h" + +namespace G3D { + +ConvexPolygon::ConvexPolygon(const Array<Vector3>& __vertex) : _vertex(__vertex) { + // Intentionally empty +} + + +bool ConvexPolygon::isEmpty() const { + return (_vertex.length() == 0) || (getArea() <= fuzzyEpsilon); +} + + +float ConvexPolygon::getArea() const { + + if (_vertex.length() < 3) { + return 0; + } + + float sum = 0; + + int length = _vertex.length(); + // Split into triangle fan, compute individual area + for (int v = 2; v < length; v++) { + int i0 = 0; + int i1 = v - 1; + int i2 = v; + + sum += (_vertex[i1] - _vertex[i0]).cross(_vertex[i2] - _vertex[i0]).magnitude() / 2; + } + + return sum; +} + +void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below) { + DirectedEdge edge; + cut(plane, above, below, edge); +} + +void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge &newEdge) { + above._vertex.resize(0); + below._vertex.resize(0); + + if (isEmpty()) { + //debugPrintf("Empty\n"); + return; + } + + int v = 0; + int length = _vertex.length(); + + + Vector3 polyNormal = normal(); + Vector3 planeNormal= plane.normal(); + + // See if the polygon is *in* the plane. + if (planeNormal.fuzzyEq(polyNormal) || planeNormal.fuzzyEq(-polyNormal)) { + // Polygon is parallel to the plane. It must be either above, + // below, or in the plane. + + double a, b, c, d; + Vector3 pt = _vertex[0]; + + plane.getEquation(a,b,c,d); + float r = (float)(a * pt.x + b * pt.y + c * pt.z + d); + + if (fuzzyGe(r, 0)) { + // The polygon is entirely in the plane. + //debugPrintf("Entirely above\n"); + above = *this; + return; + } else { + //debugPrintf("Entirely below (1)\n"); + below = *this; + return; + } + } + + + // Number of edges crossing the plane. Used for + // debug assertions. + int count = 0; + + // True when the last _vertex we looked at was above the plane + bool lastAbove = plane.halfSpaceContains(_vertex[v]); + + if (lastAbove) { + above._vertex.append(_vertex[v]); + } else { + below._vertex.append(_vertex[v]); + } + + for (v = 1; v < length; v++) { + bool isAbove = plane.halfSpaceContains(_vertex[v]); + + if (lastAbove ^ isAbove) { + // Switched sides. + // Create an interpolated point that lies + // in the plane, between the two points. + Line line = Line::fromTwoPoints(_vertex[v - 1], _vertex[v]); + Vector3 interp = line.intersection(plane); + + if (! interp.isFinite()) { + + // Since the polygon is not in the plane (we checked above), + // it must be the case that this edge (and only this edge) + // is in the plane. This only happens when the polygon is + // entirely below the plane except for one edge. This edge + // forms a degenerate polygon, so just treat the whole polygon + // as below the plane. + below = *this; + above._vertex.resize(0); + //debugPrintf("Entirely below\n"); + return; + } + + above._vertex.append(interp); + below._vertex.append(interp); + if (lastAbove) { + newEdge.stop = interp; + } else { + newEdge.start = interp; + } + count++; + } + + lastAbove = isAbove; + if (lastAbove) { + above._vertex.append(_vertex[v]); + } else { + below._vertex.append(_vertex[v]); + } + } + + // Loop back to the first point, seeing if an interpolated point is + // needed. + bool isAbove = plane.halfSpaceContains(_vertex[0]); + if (lastAbove ^ isAbove) { + Line line = Line::fromTwoPoints(_vertex[length - 1], _vertex[0]); + Vector3 interp = line.intersection(plane); + if (! interp.isFinite()) { + // Since the polygon is not in the plane (we checked above), + // it must be the case that this edge (and only this edge) + // is in the plane. This only happens when the polygon is + // entirely below the plane except for one edge. This edge + // forms a degenerate polygon, so just treat the whole polygon + // as below the plane. + below = *this; + above._vertex.resize(0); + //debugPrintf("Entirely below\n"); + return; + } + + above._vertex.append(interp); + below._vertex.append(interp); + debugAssertM(count < 2, "Convex polygons may only intersect planes at two edges."); + if (lastAbove) { + newEdge.stop = interp; + } else { + newEdge.start = interp; + } + count++; + } + + debugAssertM((count == 2) || (count == 0), "Convex polygons may only intersect planes at two edges."); +} + +ConvexPolygon ConvexPolygon::inverse() const { + ConvexPolygon result; + int length = _vertex.length(); + result._vertex.resize(length); + + for (int v = 0; v < length; v++) { + result._vertex[v] = _vertex[length - v - 1]; + } + + return result; +} + +void ConvexPolygon::removeDuplicateVertices(){ + // Any valid polygon should have 3 or more vertices, but why take chances? + if(_vertex.size() >= 2){ + + // Remove duplicate vertices. + for(int i=0;i<_vertex.size()-1;++i){ + if(_vertex[i].fuzzyEq(_vertex[i+1])){ + _vertex.remove(i+1); + --i; // Don't move forward. + } + } + + // Check the last vertex against the first. + if(_vertex[_vertex.size()-1].fuzzyEq(_vertex[0])){ + _vertex.pop(); + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +ConvexPolyhedron::ConvexPolyhedron(const Array<ConvexPolygon>& _face) : face(_face) { + // Intentionally empty +} + +float ConvexPolyhedron::getVolume() const { + + if (face.length() < 4) { + return 0; + } + + // The volume of any pyramid is 1/3 * h * base area. + // Discussion at: http://nrich.maths.org/mathsf/journalf/oct01/art1/ + + float sum = 0; + + // Choose the first _vertex of the first face as the origin. + // This lets us skip one face, too, and avoids negative heights. + Vector3 v0 = face[0]._vertex[0]; + for (int f = 1; f < face.length(); f++) { + const ConvexPolygon& poly = face[f]; + + float height = (poly._vertex[0] - v0).dot(poly.normal()); + float base = poly.getArea(); + + sum += height * base; + } + + return sum / 3; +} + +bool ConvexPolyhedron::isEmpty() const { + return (face.length() == 0) || (getVolume() <= fuzzyEpsilon); +} + +void ConvexPolyhedron::cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below) { + above.face.resize(0); + below.face.resize(0); + + Array<DirectedEdge> edge; + + int f; + + // See if the plane cuts this polyhedron at all. Detect when + // the polyhedron is entirely to one side or the other. + //{ + int numAbove = 0, numIn = 0, numBelow = 0; + bool ruledOut = false; + double d; + Vector3 abc; + plane.getEquation(abc, d); + + // This number has to be fairly large to prevent precision problems down + // the road. + const float eps = 0.005f; + for (f = face.length() - 1; (f >= 0) && (!ruledOut); f--) { + const ConvexPolygon& poly = face[f]; + for (int v = poly._vertex.length() - 1; (v >= 0) && (!ruledOut); v--) { + double r = abc.dot(poly._vertex[v]) + d; + if (r > eps) { + numAbove++; + } else if (r < -eps) { + numBelow++; + } else { + numIn++; + } + + ruledOut = (numAbove != 0) && (numBelow !=0); + } + } + + if (numBelow == 0) { + above = *this; + return; + } else if (numAbove == 0) { + below = *this; + return; + } + //} + + // Clip each polygon, collecting split edges. + for (f = face.length() - 1; f >= 0; f--) { + ConvexPolygon a, b; + DirectedEdge e; + face[f].cut(plane, a, b, e); + + bool aEmpty = a.isEmpty(); + bool bEmpty = b.isEmpty(); + + //debugPrintf("\n"); + if (! aEmpty) { + //debugPrintf(" Above %f\n", a.getArea()); + above.face.append(a); + } + + if (! bEmpty) { + //debugPrintf(" Below %f\n", b.getArea()); + below.face.append(b); + } + + if (! aEmpty && ! bEmpty) { + //debugPrintf(" == Split\n"); + edge.append(e); + } else { + // Might be the case that the polygon is entirely on + // one side of the plane yet there is an edge we need + // because it touches the plane. + // + // Extract the non-empty _vertex list and examine it. + // If we find exactly one edge in the plane, add that edge. + const Array<Vector3>& _vertex = (aEmpty ? b._vertex : a._vertex); + int L = _vertex.length(); + int count = 0; + for (int v = 0; v < L; v++) { + if (plane.fuzzyContains(_vertex[v]) && plane.fuzzyContains(_vertex[(v + 1) % L])) { + e.start = _vertex[v]; + e.stop = _vertex[(v + 1) % L]; + count++; + } + } + + if (count == 1) { + edge.append(e); + } + } + } + + if (above.face.length() == 1) { + // Only one face above means that this entire + // polyhedron is below the plane. Move that face over. + below.face.append(above.face[0]); + above.face.resize(0); + } else if (below.face.length() == 1) { + // This shouldn't happen, but it arises in practice + // from numerical imprecision. + above.face.append(below.face[0]); + below.face.resize(0); + } + + if ((above.face.length() > 0) && (below.face.length() > 0)) { + // The polyhedron was actually cut; create a cap polygon + ConvexPolygon cap; + + // Collect the final polgyon by sorting the edges + int numVertices = edge.length(); +/*debugPrintf("\n"); +for (int xx=0; xx < numVertices; xx++) { + std::string s1 = edge[xx].start.toString(); + std::string s2 = edge[xx].stop.toString(); + debugPrintf("%s -> %s\n", s1.c_str(), s2.c_str()); +} +*/ + + // Need at least three points to make a polygon + debugAssert(numVertices >= 3); + + Vector3 last_vertex = edge.last().stop; + cap._vertex.append(last_vertex); + + // Search for the next _vertex. Because of accumulating + // numerical error, we have to find the closest match, not + // just the one we expect. + for (int v = numVertices - 1; v >= 0; v--) { + // matching edge index + int index = 0; + int num = edge.length(); + double distance = (edge[index].start - last_vertex).squaredMagnitude(); + for (int e = 1; e < num; e++) { + double d = (edge[e].start - last_vertex).squaredMagnitude(); + + if (d < distance) { + // This is the new closest one + index = e; + distance = d; + } + } + + // Don't tolerate ridiculous error. + debugAssertM(distance < 0.02, "Edge missing while closing polygon."); + + last_vertex = edge[index].stop; + cap._vertex.append(last_vertex); + } + + //debugPrintf("\n"); + //debugPrintf("Cap (both) %f\n", cap.getArea()); + above.face.append(cap); + below.face.append(cap.inverse()); + } + + // Make sure we put enough faces on each polyhedra + debugAssert((above.face.length() == 0) || (above.face.length() >= 4)); + debugAssert((below.face.length() == 0) || (below.face.length() >= 4)); +} + +/////////////////////////////////////////////// + +ConvexPolygon2D::ConvexPolygon2D(const Array<Vector2>& pts, bool reverse) : m_vertex(pts) { + if (reverse) { + m_vertex.reverse(); + } +} + + +bool ConvexPolygon2D::contains(const Vector2& p, bool reverse) const { + // Compute the signed area of each polygon from p to an edge. + // If the area is non-negative for all polygons then p is inside + // the polygon. (To adapt this algorithm for a concave polygon, + // the *sum* of the areas must be non-negative). + + float r = reverse ? -1 : 1; + + for (int i0 = 0; i0 < m_vertex.size(); ++i0) { + int i1 = (i0 + 1) % m_vertex.size(); + const Vector2& v0 = m_vertex[i0]; + const Vector2& v1 = m_vertex[i1]; + + Vector2 e0 = v0 - p; + Vector2 e1 = v1 - p; + + // Area = (1/2) cross product, negated to be ccw in + // a 2D space; we neglect the 1/2 + float area = -(e0.x * e1.y - e0.y * e1.x); + + if (area * r < 0) { + return false; + } + } + + return true; +} + + +} + diff --git a/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp b/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp new file mode 100644 index 00000000000..b6e94fe5857 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp @@ -0,0 +1,381 @@ +/** + @file CoordinateFrame.cpp + + Coordinate frame class + + @maintainer Morgan McGuire, morgan@cs.williams.edu + + @created 2001-06-02 + @edited 2008-07-13 +*/ + +#include "G3D/platform.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Quat.h" +#include "G3D/Matrix4.h" +#include "G3D/Box.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" +#include "G3D/Triangle.h" +#include "G3D/Ray.h" +#include "G3D/Capsule.h" +#include "G3D/Cylinder.h" +#include "G3D/UprightFrame.h" + +namespace G3D { + +CoordinateFrame::CoordinateFrame(const class UprightFrame& f) { + *this = f.toCoordinateFrame(); +} + + +CoordinateFrame CoordinateFrame::fromXYZYPRRadians(float x, float y, float z, float yaw, + float pitch, float roll) { + Matrix3 rotation = Matrix3::fromAxisAngle(Vector3::unitY(), yaw); + + rotation = Matrix3::fromAxisAngle(rotation.column(0), pitch) * rotation; + rotation = Matrix3::fromAxisAngle(rotation.column(2), roll) * rotation; + + const Vector3 translation(x, y, z); + + return CoordinateFrame(rotation, translation); +} + + +void CoordinateFrame::getXYZYPRRadians(float& x, float& y, float& z, + float& yaw, float& pitch, float& roll) const { + x = translation.x; + y = translation.y; + z = translation.z; + + const Vector3& look = lookVector(); + + if (abs(look.y) > 0.99f) { + // Looking nearly straight up or down + + yaw = G3D::pi() + atan2(look.x, look.z); + pitch = asin(look.y); + roll = 0.0f; + + } else { + + // Yaw cannot be affected by others, so pull it first + yaw = G3D::pi() + atan2(look.x, look.z); + + // Pitch is the elevation of the yaw vector + pitch = asin(look.y); + + Vector3 actualRight = rightVector(); + Vector3 expectedRight = look.cross(Vector3::unitY()); + + roll = 0;//acos(actualRight.dot(expectedRight)); TODO + } +} + + +void CoordinateFrame::getXYZYPRDegrees(float& x, float& y, float& z, + float& yaw, float& pitch, float& roll) const { + getXYZYPRRadians(x, y, z, yaw, pitch, roll); + yaw = toDegrees(yaw); + pitch = toDegrees(pitch); + roll = toDegrees(roll); +} + + +CoordinateFrame CoordinateFrame::fromXYZYPRDegrees(float x, float y, float z, + float yaw, float pitch, float roll) { + return fromXYZYPRRadians(x, y, z, toRadians(yaw), toRadians(pitch), toRadians(roll)); +} + + +Ray CoordinateFrame::lookRay() const { + return Ray::fromOriginAndDirection(translation, lookVector()); +} + + +bool CoordinateFrame::fuzzyEq(const CoordinateFrame& other) const { + + for (int c = 0; c < 3; ++c) { + for (int r = 0; r < 3; ++r) { + if (! G3D::fuzzyEq(other.rotation[r][c], rotation[r][c])) { + return false; + } + } + if (! G3D::fuzzyEq(translation[c], other.translation[c])) { + return false; + } + } + + return true; +} + + +bool CoordinateFrame::fuzzyIsIdentity() const { + const Matrix3& I = Matrix3::identity(); + + for (int c = 0; c < 3; ++c) { + for (int r = 0; r < 3; ++r) { + if (fuzzyNe(I[r][c], rotation[r][c])) { + return false; + } + } + if (fuzzyNe(translation[c], 0)) { + return false; + } + } + + return true; +} + + +bool CoordinateFrame::isIdentity() const { + return + (translation == Vector3::zero()) && + (rotation == Matrix3::identity()); +} + + +Matrix4 CoordinateFrame::toMatrix4() const { + return Matrix4(*this); +} + + +std::string CoordinateFrame::toXML() const { + return G3D::format( + "<COORDINATEFRAME>\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf\n</COORDINATEFRAME>\n", + rotation[0][0], rotation[0][1], rotation[0][2], translation.x, + rotation[1][0], rotation[1][1], rotation[1][2], translation.y, + rotation[2][0], rotation[2][1], rotation[2][2], translation.z, + 0.0, 0.0, 0.0, 1.0); +} + + +Plane CoordinateFrame::toObjectSpace(const Plane& p) const { + Vector3 N, P; + double d; + p.getEquation(N, d); + P = N * (float)d; + P = pointToObjectSpace(P); + N = normalToObjectSpace(N); + return Plane(N, P); +} + + +Plane CoordinateFrame::toWorldSpace(const Plane& p) const { + Vector3 N, P; + double d; + p.getEquation(N, d); + P = N * (float)d; + P = pointToWorldSpace(P); + N = normalToWorldSpace(N); + return Plane(N, P); +} + + +Triangle CoordinateFrame::toObjectSpace(const Triangle& t) const { + return Triangle(pointToObjectSpace(t.vertex(0)), + pointToObjectSpace(t.vertex(1)), + pointToObjectSpace(t.vertex(2))); +} + + +Triangle CoordinateFrame::toWorldSpace(const Triangle& t) const { + return Triangle(pointToWorldSpace(t.vertex(0)), + pointToWorldSpace(t.vertex(1)), + pointToWorldSpace(t.vertex(2))); +} + + +Cylinder CoordinateFrame::toWorldSpace(const Cylinder& c) const { + return Cylinder( + pointToWorldSpace(c.point(0)), + pointToWorldSpace(c.point(1)), + c.radius()); +} + + +Capsule CoordinateFrame::toWorldSpace(const Capsule& c) const { + return Capsule( + pointToWorldSpace(c.point(0)), + pointToWorldSpace(c.point(1)), + c.radius()); +} + + +Box CoordinateFrame::toWorldSpace(const AABox& b) const { + return toWorldSpace(Box(b)); +} + + +Box CoordinateFrame::toWorldSpace(const Box& b) const { + Box out(b); + + for (int i = 0; i < 8; ++i) { + out._corner[i] = pointToWorldSpace(out._corner[i]); + debugAssert(! isNaN(out._corner[i].x)); + } + + for (int i = 0; i < 3; ++i) { + out._axis[i] = vectorToWorldSpace(out._axis[i]); + } + + out._center = pointToWorldSpace(out._center); + + return out; +} + + +Box CoordinateFrame::toObjectSpace(const Box &b) const { + return inverse().toWorldSpace(b); +} + + +Box CoordinateFrame::toObjectSpace(const AABox& b) const { + return toObjectSpace(Box(b)); +} + + +CoordinateFrame::CoordinateFrame(class BinaryInput& b) : rotation(Matrix3::zero()) { + deserialize(b); +} + + +void CoordinateFrame::deserialize(class BinaryInput& b) { + rotation.deserialize(b); + translation.deserialize(b); +} + + +void CoordinateFrame::serialize(class BinaryOutput& b) const { + rotation.serialize(b); + translation.serialize(b); +} + + +Sphere CoordinateFrame::toWorldSpace(const Sphere &b) const { + return Sphere(pointToWorldSpace(b.center), b.radius); +} + + +Sphere CoordinateFrame::toObjectSpace(const Sphere &b) const { + return Sphere(pointToObjectSpace(b.center), b.radius); +} + + +Ray CoordinateFrame::toWorldSpace(const Ray& r) const { + return Ray::fromOriginAndDirection(pointToWorldSpace(r.origin), vectorToWorldSpace(r.direction)); +} + + +Ray CoordinateFrame::toObjectSpace(const Ray& r) const { + return Ray::fromOriginAndDirection(pointToObjectSpace(r.origin), vectorToObjectSpace(r.direction)); +} + + +void CoordinateFrame::lookAt(const Vector3 &target) { + lookAt(target, Vector3::unitY()); +} + + +void CoordinateFrame::lookAt( + const Vector3& target, + Vector3 up) { + + up = up.direction(); + + Vector3 look = (target - translation).direction(); + if (fabs(look.dot(up)) > .99f) { + up = Vector3::unitX(); + if (fabs(look.dot(up)) > .99f) { + up = Vector3::unitY(); + } + } + + up -= look * look.dot(up); + up.unitize(); + + Vector3 z = -look; + Vector3 x = -z.cross(up); + x.unitize(); + + Vector3 y = z.cross(x); + + rotation.setColumn(0, x); + rotation.setColumn(1, y); + rotation.setColumn(2, z); +} + + +CoordinateFrame CoordinateFrame::lerp( + const CoordinateFrame& other, + float alpha) const { + + if (alpha == 1.0f) { + return other; + } else if (alpha == 0.0f) { + return *this; + } else { + Quat q1 = Quat(this->rotation); + Quat q2 = Quat(other.rotation); + + return CoordinateFrame( + q1.slerp(q2, alpha).toRotationMatrix(), + this->translation * (1 - alpha) + other.translation * alpha); + } +} + + +void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = pointToWorldSpace(v[i]); + } +} + + +void CoordinateFrame::normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = normalToWorldSpace(v[i]); + } +} + + +void CoordinateFrame::vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = vectorToWorldSpace(v[i]); + } +} + + +void CoordinateFrame::pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = pointToObjectSpace(v[i]); + } +} + + +void CoordinateFrame::normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = normalToObjectSpace(v[i]); + } +} + + +void CoordinateFrame::vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = vectorToObjectSpace(v[i]); + } +} + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/Crypto.cpp b/externals/g3dlite/G3D.lib/source/Crypto.cpp new file mode 100644 index 00000000000..09aee2c265d --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Crypto.cpp @@ -0,0 +1,70 @@ +/** + @file Crypto.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + + @created 2006-03-28 + @edited 2006-04-06 + */ + +#include "G3D/platform.h" +#include "G3D/Crypto.h" +#include "G3D/g3dmath.h" +#include <zlib.h> + +namespace G3D { + + +int Crypto::smallPrime(int n) { + debugAssert(n < numSmallPrimes() && n >= 0); + + // From: + // http://primes.utm.edu/lists/small/1000.txt + + static const int table[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, + 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, + 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, + 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, + 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, + 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, + 1993, 1997, 1999}; + + return table[n]; +} + + +int Crypto::numSmallPrimes() { + return 303; +} + +uint32 Crypto::crc32(const void* byte, size_t numBytes) { + return ::crc32(::crc32(0, Z_NULL, 0), static_cast<const Bytef *>(byte), numBytes); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp b/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp new file mode 100644 index 00000000000..4aa24c39f9c --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp @@ -0,0 +1,471 @@ +/** + @file Crypto_md5.cpp + + @author Morgan McGuire, matrix@graphics3d.com + Copyright 2006-2007, Morgan McGuire. All rights reserved. + + @created 2006-03-28 + @edited 2006-04-06 + */ + +#include "G3D/platform.h" +#include "G3D/Crypto.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +#include <cstring> + +namespace G3D { + + +MD5Hash::MD5Hash(class BinaryInput& b) { + deserialize(b); +} + + +void MD5Hash::deserialize(class BinaryInput& b) { + b.readBytes(value, 16); +} + + +void MD5Hash::serialize(class BinaryOutput& b) const { + b.writeBytes(value, 16); +} + + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +static void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +static void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +#ifdef __cplusplus +} +#endif + + + +MD5Hash Crypto::md5(const void* data, size_t n) { + md5_state_t state; + md5_init(&state); + md5_append(&state, (const uint8*)data, (int)n); + + MD5Hash h; + md5_finish(&state, &(h[0])); + return h; +} + +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + + +#if defined(G3D_LINUX) || defined(G3D_OSX) +# if defined(G3D_OSX_PPC) +# include <ppc/endian.h> +# elif defined(G3D_OSX_INTEL) +# include <i386/endian.h> +# elif defined(__linux__) +# include <endian.h> +# elif defined(__FreeBSD__) +# include <sys/endian.h> +# endif +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Cylinder.cpp b/externals/g3dlite/G3D.lib/source/Cylinder.cpp new file mode 100644 index 00000000000..bdc7d56be23 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Cylinder.cpp @@ -0,0 +1,176 @@ +/** + @file Cylinder.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-07 + @edited 2006-02-18 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/platform.h" +#include "G3D/Cylinder.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/LineSegment.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Line.h" +#include "G3D/AABox.h" + +namespace G3D { + +Cylinder::Cylinder(class BinaryInput& b) { + deserialize(b); +} + + +Cylinder::Cylinder() { +} + + +Cylinder::Cylinder(const Vector3& _p1, const Vector3& _p2, float _r) + : p1(_p1), p2(_p2), mRadius(_r) { +} + + +void Cylinder::serialize(class BinaryOutput& b) const { + p1.serialize(b); + p2.serialize(b); + b.writeFloat64(mRadius); +} + + +void Cylinder::deserialize(class BinaryInput& b) { + p1.deserialize(b); + p2.deserialize(b); + mRadius = b.readFloat64(); +} + + +Line Cylinder::axis() const { + return Line::fromTwoPoints(p1, p2); +} + + + +float Cylinder::radius() const { + return mRadius; +} + + +float Cylinder::volume() const { + return + (float)pi() * square(mRadius) * (p1 - p2).magnitude(); +} + + +float Cylinder::area() const { + return + // Sides + (twoPi() * mRadius) * height() + + + // Caps + twoPi() * square(mRadius); +} + +void Cylinder::getBounds(AABox& out) const { + Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * mRadius); + Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * mRadius); + out = AABox(min, max); +} + +bool Cylinder::contains(const Vector3& p) const { + return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(mRadius); +} + + +void Cylinder::getReferenceFrame(CoordinateFrame& cframe) const { + cframe.translation = center(); + + Vector3 Y = (p1 - p2).direction(); + Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX(); + Vector3 Z = X.cross(Y).direction(); + X = Y.cross(Z); + cframe.rotation.setColumn(0, X); + cframe.rotation.setColumn(1, Y); + cframe.rotation.setColumn(2, Z); +} + + +void Cylinder::getRandomSurfacePoint(Vector3& p, Vector3& N) const { + float h = height(); + float r = radius(); + + // Create a random point on a standard cylinder and then rotate to the global frame. + + // Relative areas (factor of 2PI already taken out) + float capRelArea = square(r) / 2.0f; + float sideRelArea = r * h; + + float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea); + + if (r1 < capRelArea * 2) { + + // Select a point uniformly at random on a disk + // @cite http://mathworld.wolfram.com/DiskPointPicking.html + float a = uniformRandom(0, (float)twoPi()); + float r2 = sqrt(uniformRandom(0, 1)) * r; + p.x = cos(a) * r2; + p.z = sin(a) * r2; + + N.x = 0; + N.z = 0; + if (r1 < capRelArea) { + // Top + p.y = h / 2.0f; + N.y = 1; + } else { + // Bottom + p.y = -h / 2.0f; + N.y = -1; + } + } else { + // Side + float a = uniformRandom(0, (float)twoPi()); + N.x = cos(a); + N.y = 0; + N.z = sin(a); + p.x = N.x * r; + p.z = N.y * r; + p.y = uniformRandom(-h / 2.0f, h / 2.0f); + } + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + p = cframe.pointToWorldSpace(p); + N = cframe.normalToWorldSpace(N); +} + + +Vector3 Cylinder::randomInteriorPoint() const { + float h = height(); + float r = radius(); + + // Create a random point in a standard cylinder and then rotate to the global frame. + + // Select a point uniformly at random on a disk + // @cite http://mathworld.wolfram.com/DiskPointPicking.html + float a = uniformRandom(0, (float)twoPi()); + float r2 = sqrt(uniformRandom(0, 1)) * r; + + Vector3 p( cos(a) * r2, + uniformRandom(-h / 2.0f, h / 2.0f), + sin(a) * r2); + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + return cframe.pointToWorldSpace(p); +} + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/Discovery.cpp b/externals/g3dlite/G3D.lib/source/Discovery.cpp new file mode 100644 index 00000000000..2c27c5bf2b9 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Discovery.cpp @@ -0,0 +1,170 @@ +/** + @file Discovery.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-06-26 + @edited 2005-02-24 + */ + +#include "G3D/Discovery.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +#include <cstring> + +namespace G3D { + +/////////////////////////////////////////////////////////////////////////////////////////// + +void DiscoveryAdvertisement::serialize(BinaryOutput& b) const { + address.serialize(b); +} + +void DiscoveryAdvertisement::deserialize(BinaryInput& b) { + address.deserialize(b); + lastUpdateTime = System::time(); +} + + +/////////////////////////////////////////////////////////////////////////////////////////// + +void DiscoveryServerAddressMessage::serialize(BinaryOutput& b) const { + b.writeString(G3D_DISCOVERY_PROTOCOL_NAME); + b.writeInt32(G3D_DISCOVERY_PROTOCOL_VERSION); + b.writeString(settings->appProtocolName); + b.writeInt32(settings->appProtocolVersion); + + // Send addresses + b.writeInt32(address.size()); + for (int i = 0; i < address.size(); ++i) { + address[i].serialize(b); + } +} + + +void DiscoveryServerAddressMessage::deserialize(BinaryInput& b) { + address.clear(); + correctProtocol = false; + serverProtocolVersion[0] = 0; + serverProtocolVersion[1] = 0; + + // Verify that we are on the same protocol + if (b.readString(strlen(G3D_DISCOVERY_PROTOCOL_NAME) + 1) != G3D_DISCOVERY_PROTOCOL_NAME) { + return; + } + + serverProtocolVersion[0] = b.readInt32(); + if (serverProtocolVersion[0] != G3D_DISCOVERY_PROTOCOL_VERSION) { + return; + } + + if (b.readString() != settings->appProtocolName) { + return; + } + + serverProtocolVersion[1] = b.readInt32(); + if (serverProtocolVersion[1] != settings->appProtocolVersion) { + return; + } + + correctProtocol = true; + + address.resize(b.readInt32()); + for (int i = 0; i < address.size(); ++i) { + address[i].deserialize(b); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +void DiscoveryServer::sendAnnouncement() const { + NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], settings->serverBroadcastPort); + + net->send(broadcast, SERVER_BROADCAST_MESSAGE, addressMessage); + + const_cast<DiscoveryServer*>(this)->lastBroadcast = System::time(); +} + + +void DiscoveryServer::sendShutDown() const { + NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], settings->serverBroadcastPort); + ShutdownMessage s; + net->send(broadcast, SERVER_SHUTDOWN_MESSAGE, s); +} + + +bool DiscoveryServer::ok() const { + return listener->ok() && net->ok(); +} + + +void DiscoveryServer::init( + const DiscoverySettings* _settings, + DiscoveryAdvertisement* _advertisement) { + + Discovery::init(_settings); + + advertisement = _advertisement; + addressMessage.settings = settings; + NetworkDevice::instance()->localHostAddresses(addressMessage.address); + + // Set the port number + for (int i = 0; i < addressMessage.address.size(); ++i) { + addressMessage.address[i] = + NetAddress(addressMessage.address[i].ip(), + settings->serverAdvertisementPort); + } + + net = LightweightConduit::create(settings->clientBroadcastPort, true, true); + + listener = NetListener::create(settings->serverAdvertisementPort); + + // Send initial announcement + sendAnnouncement(); +} + + +void DiscoveryServer::doNetwork() { + const RealTime UNSOLICITED_BROADCAST_PERIOD = 60; + + // Check for client broadcast requests + + if (net->messageWaiting()) { + // Some client broadcast that it is looking for servers. + // Respond by sending out our announcement to everyone + // (avoids having to figure out if the message return address + // is correct). + NetAddress dummy; + net->receive(dummy); + sendAnnouncement(); + } else if (System::time() > lastBroadcast + UNSOLICITED_BROADCAST_PERIOD) { + sendAnnouncement(); + } + + // Handle incoming connections + + if (listener->clientWaiting()) { + // Respond to this client + ReliableConduitRef client = listener->waitForConnection(); + client->send(SERVER_BROADCAST_MESSAGE, *advertisement); + } +} + + +void DiscoveryServer::cleanup() { + sendShutDown(); +} + +////////////////////////////////////////////////////////////////////////////////// + +std::string IncompatibleServerDescription::toString() const { + return std::string("Incompatible server at ") + address.toString() + + format(", version %d.%d", protocolVersion[0], protocolVersion[1]); +} + +////////////////////////////////////////////////////////////////////////////////// + + +} + diff --git a/externals/g3dlite/G3D.lib/source/GCamera.cpp b/externals/g3dlite/G3D.lib/source/GCamera.cpp new file mode 100644 index 00000000000..e70188377e6 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GCamera.cpp @@ -0,0 +1,399 @@ +/** + @file GCamera.cpp + + @author Morgan McGuire, matrix@graphics3d.com + @author Jeff Marsceill, 08jcm@williams.edu + + @created 2005-07-20 + @edited 2007-07-24 +*/ +#include "G3D/GCamera.h" +#include "G3D/platform.h" +#include "G3D/Rect2D.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Ray.h" +#include "G3D/Matrix4.h" + +namespace G3D { + +GCamera::GCamera() { + setNearPlaneZ(-0.1f); + setFarPlaneZ(-(float)inf()); + setFieldOfView((float)toRadians(55.0f), VERTICAL); +} + + +GCamera::~GCamera() { +} + +void GCamera::getCoordinateFrame(CoordinateFrame& c) const { + c = m_cframe; +} + +void GCamera::setCoordinateFrame(const CoordinateFrame& c) { + m_cframe = c; +} + +void GCamera::setFieldOfView(float angle, FOVDirection dir) { + debugAssert((angle < pi()) && (angle > 0)); + + m_fieldOfView = angle; + m_direction = dir; +} + +float GCamera::imagePlaneDepth() const{ + return -m_nearPlaneZ; +} + +float GCamera::viewportWidth(const Rect2D& viewport) const { + // Compute the side of a square at the near plane based on our field of view + float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f); + + if (m_direction == VERTICAL) { + s *= viewport.width() / viewport.height(); + } + + return s; +} + +float GCamera::viewportHeight(const Rect2D& viewport) const { + // Compute the side of a square at the near plane based on our field of view + float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f); + + if (m_direction == HORIZONTAL) { + s *= viewport.height() / viewport.width(); + } + + return s; +} + +Ray GCamera::worldRay(float x, float y, const Rect2D& viewport) const { + + int screenWidth = iFloor(viewport.width()); + int screenHeight = iFloor(viewport.height()); + + Ray out; + out.origin = m_cframe.translation; + + float cx = screenWidth / 2.0f; + float cy = screenHeight / 2.0f; + + out.direction = Vector3( (x - cx) * viewportWidth(viewport) / screenWidth, + -(y - cy) * viewportHeight(viewport) / screenHeight, + (m_nearPlaneZ) ); + + out.direction = m_cframe.vectorToWorldSpace(out.direction); + + // Normalize the direction (we didn't do it before) + out.direction = out.direction.direction(); + + return out; +} + +/** +This is the matrix that a RenderDevice (or OpenGL) uses as the projection matrix. +@sa RenderDevice::setProjectionAndCameraMatrix, RenderDevice::setProjectionMatrix, Matrix4::perspectiveProjection +*/ +void GCamera::getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const{ + + float screenWidth = viewport.width(); + float screenHeight = viewport.height(); + + float r, l, t, b, n, f, x, y; + + if(m_direction == VERTICAL){ + y = -m_nearPlaneZ * tan(m_fieldOfView / 2); + x = y * (screenWidth / screenHeight); + } + else{ //m_direction == HORIZONTAL + x = -m_nearPlaneZ * tan(m_fieldOfView / 2); + y = x * (screenHeight / screenWidth); + } + + n = -m_nearPlaneZ; + f = -m_farPlaneZ; + r = x; + l = -x; + t = y; + b = -y; + + P = Matrix4::perspectiveProjection(l, r, b, t, n, f); +} + +Vector3 GCamera::projectUnit(const Vector3& point, const Rect2D& viewport) const { + Matrix4 M; + getProjectUnitMatrix(viewport, M); + + Vector4 cameraSpacePoint(coordinateFrame().pointToObjectSpace(point), 1.0f); + const Vector4& screenSpacePoint = M * cameraSpacePoint; + + return Vector3(screenSpacePoint.xyz() / screenSpacePoint.w); +} + +Vector3 GCamera::project(const Vector3& point, + const Rect2D& viewport) const { + + // Find the point in the homogeneous cube + const Vector3& cube = projectUnit(point, viewport); + + return convertFromUnitToNormal(cube, viewport); +} + +Vector3 GCamera::unprojectUnit(const Vector3& v, const Rect2D& viewport) const { + + const Vector3& projectedPoint = convertFromUnitToNormal(v, viewport); + + return unproject(projectedPoint, viewport); +} + + +Vector3 GCamera::unproject(const Vector3& v, const Rect2D& viewport) const { + + const float n = m_nearPlaneZ; + const float f = m_farPlaneZ; + + float z; + + if (-f >= inf()) { + // Infinite far plane + z = 1.0f / (((-1.0f / n) * v.z) + 1.0f / n); + } else { + z = 1.0f / ((((1.0f / f) - (1.0f / n)) * v.z) + 1.0f / n); + } + + const Ray& ray = worldRay(v.x, v.y, viewport); + + // Find out where the ray reaches the specified depth. + const Vector3& out = ray.origin + ray.direction * -z / (ray.direction.dot(m_cframe.lookVector())); + + return out; +} + +float GCamera::worldToScreenSpaceArea(float area, float z, const Rect2D& viewport) const { + if (z >= 0) { + return (float)inf(); + } + return area * (float)square(imagePlaneDepth() / z); +} + + +void GCamera::getClipPlanes( + const Rect2D& viewport, + Array<Plane>& clip) const { + + Frustum fr; + frustum(viewport, fr); + clip.resize(fr.faceArray.size(), DONT_SHRINK_UNDERLYING_ARRAY); + for (int f = 0; f < clip.size(); ++f) { + clip[f] = fr.faceArray[f].plane; + } +} + +GCamera::Frustum GCamera::frustum(const Rect2D& viewport) const { + Frustum f; + frustum(viewport, f); + return f; +} + +void GCamera::frustum(const Rect2D& viewport, Frustum& fr) const { + + // The volume is the convex hull of the vertices definining the view + // frustum and the light source point at infinity. + + const float x = viewportWidth(viewport) / 2; + const float y = viewportHeight(viewport) / 2; + const float z = m_nearPlaneZ; + const float w = z / -m_farPlaneZ; + float fovx; + + fovx = m_fieldOfView; + if (m_direction == VERTICAL) { + fovx *= x / y; + } + + // Near face (ccw from UR) + fr.vertexPos.append( + Vector4( x, y, z, 1), + Vector4(-x, y, z, 1), + Vector4(-x, -y, z, 1), + Vector4( x, -y, z, 1)); + + // Far face (ccw from UR, from origin) + fr.vertexPos.append( + Vector4( x, y, z, w), + Vector4(-x, y, z, w), + Vector4(-x, -y, z, w), + Vector4( x, -y, z, w)); + + Frustum::Face face; + + // Near plane (wind backwards so normal faces into frustum) + // Recall that nearPlane, farPlane are positive numbers, so + // we need to negate them to produce actual z values. + face.plane = Plane(Vector3(0,0,-1), Vector3(0,0,m_nearPlaneZ)); + face.vertexIndex[0] = 3; + face.vertexIndex[1] = 2; + face.vertexIndex[2] = 1; + face.vertexIndex[3] = 0; + fr.faceArray.append(face); + + // Right plane + face.plane = Plane(Vector3(-cosf(fovx/2), 0, -sinf(fovx/2)), Vector3::zero()); + face.vertexIndex[0] = 0; + face.vertexIndex[1] = 4; + face.vertexIndex[2] = 7; + face.vertexIndex[3] = 3; + fr.faceArray.append(face); + + // Left plane + face.plane = Plane(Vector3(-fr.faceArray.last().plane.normal().x, 0, fr.faceArray.last().plane.normal().z), Vector3::zero()); + face.vertexIndex[0] = 5; + face.vertexIndex[1] = 1; + face.vertexIndex[2] = 2; + face.vertexIndex[3] = 6; + fr.faceArray.append(face); + + // Top plane + face.plane = Plane(Vector3(0, -cosf(m_fieldOfView/2.0f), -sinf(m_fieldOfView/2.0f)), Vector3::zero()); + face.vertexIndex[0] = 1; + face.vertexIndex[1] = 5; + face.vertexIndex[2] = 4; + face.vertexIndex[3] = 0; + fr.faceArray.append(face); + + // Bottom plane + face.plane = Plane(Vector3(0, -fr.faceArray.last().plane.normal().y, fr.faceArray.last().plane.normal().z), Vector3::zero()); + face.vertexIndex[0] = 2; + face.vertexIndex[1] = 3; + face.vertexIndex[2] = 7; + face.vertexIndex[3] = 6; + fr.faceArray.append(face); + + // Far plane + if (-m_farPlaneZ < inf()) { + face.plane = Plane(Vector3(0, 0, 1), Vector3(0, 0, m_farPlaneZ)); + face.vertexIndex[0] = 4; + face.vertexIndex[1] = 5; + face.vertexIndex[2] = 6; + face.vertexIndex[3] = 7; + fr.faceArray.append(face); + } + + // Transform vertices to world space + for (int v = 0; v < fr.vertexPos.size(); ++v) { + fr.vertexPos[v] = m_cframe.toWorldSpace(fr.vertexPos[v]); + } + + // Transform planes to world space + for (int p = 0; p < fr.faceArray.size(); ++p) { + // Since there is no scale factor, we don't have to + // worry about the inverse transpose of the normal. + Vector3 normal; + float d; + + fr.faceArray[p].plane.getEquation(normal, d); + + Vector3 newNormal = m_cframe.rotation * normal; + + if (isFinite(d)) { + d = (newNormal * -d + m_cframe.translation).dot(newNormal); + fr.faceArray[p].plane = Plane(newNormal, newNormal * d); + } else { + // When d is infinite, we can't multiply 0's by it without + // generating NaNs. + fr.faceArray[p].plane = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d); + } + } +} + +void GCamera::getNearViewportCorners( + const Rect2D& viewport, + Vector3& outUR, + Vector3& outUL, + Vector3& outLL, + Vector3& outLR) const { + + // Must be kept in sync with getFrustum() + const float w = viewportWidth(viewport) / 2.0f; + const float h = viewportHeight(viewport) / 2.0f; + const float z = nearPlaneZ(); + + // Compute the points + outUR = Vector3( w, h, z); + outUL = Vector3(-w, h, z); + outLL = Vector3(-w, -h, z); + outLR = Vector3( w, -h, z); + + // Take to world space + outUR = m_cframe.pointToWorldSpace(outUR); + outUL = m_cframe.pointToWorldSpace(outUL); + outLR = m_cframe.pointToWorldSpace(outLR); + outLL = m_cframe.pointToWorldSpace(outLL); +} + +void GCamera::getFarViewportCorners( + const Rect2D& viewport, + Vector3& outUR, + Vector3& outUL, + Vector3& outLL, + Vector3& outLR) const { + + // Must be kept in sync with getFrustum() + const float w = viewportWidth(viewport) * m_farPlaneZ / m_nearPlaneZ; + const float h = viewportHeight(viewport) * m_farPlaneZ / m_nearPlaneZ; + const float z = m_farPlaneZ; + + // Compute the points + outUR = Vector3( w, h, z); + outUL = Vector3(-w, h, z); + outLL = Vector3(-w, -h, z); + outLR = Vector3( w, -h, z); + + // Take to world space + outUR = m_cframe.pointToWorldSpace(outUR); + outUL = m_cframe.pointToWorldSpace(outUL); + outLR = m_cframe.pointToWorldSpace(outLR); + outLL = m_cframe.pointToWorldSpace(outLL); +} + + + +void GCamera::setPosition(const Vector3& t) { + m_cframe.translation = t; +} + + +void GCamera::lookAt(const Vector3& position, const Vector3& up) { + m_cframe.lookAt(position, up); +} + + +void GCamera::serialize(BinaryOutput& bo) const { + bo.writeFloat32(m_fieldOfView); + bo.writeFloat32(imagePlaneDepth()); + debugAssert(nearPlaneZ() < 0.0f); + bo.writeFloat32(nearPlaneZ()); + debugAssert(farPlaneZ() < 0.0f); + bo.writeFloat32(farPlaneZ()); + m_cframe.serialize(bo); + bo.writeInt8(m_direction); +} + + +void GCamera::deserialize(BinaryInput& bi) { + m_fieldOfView = bi.readFloat32(); + m_nearPlaneZ = bi.readFloat32(); + debugAssert(m_nearPlaneZ < 0.0f); + m_farPlaneZ = bi.readFloat32(); + debugAssert(m_farPlaneZ < 0.0f); + m_cframe.deserialize(bi); + m_direction = (FOVDirection)bi.readInt8(); +} + + +Vector3 GCamera::convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const{ + return (in + Vector3(1,1,1)) * 0.5 * Vector3(viewport.width(), -viewport.height(), 1) + + Vector3(viewport.x0(), viewport.y1(), 0); +} +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/GImage.cpp b/externals/g3dlite/G3D.lib/source/GImage.cpp new file mode 100644 index 00000000000..c7abf982553 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage.cpp @@ -0,0 +1,1065 @@ +/** + @file GImage.cpp + @author Morgan McGuire, morgan@graphics3d.com + Copyright 2002-2007, Morgan McGuire + + @created 2002-05-27 + @edited 2006-10-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/debug.h" +#include "G3D/stringutils.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Log.h" +#include <png.h> +#include <sys/stat.h> +#include <assert.h> +#include <sys/types.h> + +////////////////////////////////////////////////////////////////////////////////////////////// + +namespace G3D { + +void GImage::RGBtoRGBA( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 0] = in[i3 + 0]; + out[i4 + 1] = in[i3 + 1]; + out[i4 + 2] = in[i3 + 2]; + out[i4 + 3] = 255; + } +} + + +void GImage::RGBAtoRGB( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i3 + 0] = in[i4 + 0]; + out[i3 + 1] = in[i4 + 1]; + out[i3 + 2] = in[i4 + 2]; + } +} + + +void GImage::RGBtoBGRA( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 2] = in[i3 + 0]; + out[i4 + 1] = in[i3 + 1]; + out[i4 + 0] = in[i3 + 2]; + out[i4 + 3] = 255; + } +} + + + +void GImage::RGBtoBGR( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + + int r = in[i3 + 0]; + int g = in[i3 + 1]; + int b = in[i3 + 2]; + + out[i3 + 2] = r; + out[i3 + 1] = g; + out[i3 + 0] = b; + } +} + + +void GImage::RGBxRGBtoRGBA( + const uint8* colorRGB, + const uint8* alphaRGB, + uint8* out, + int numPixels) { + + for (int i = numPixels - 1; i >= 0; --i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 0] = colorRGB[i3 + 0]; + out[i4 + 1] = colorRGB[i3 + 1]; + out[i4 + 2] = colorRGB[i3 + 2]; + out[i4 + 3] = alphaRGB[i3 + 0]; + } +} + + +void GImage::RGBtoARGB( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 0] = 255; + out[i4 + 1] = in[i3 + 0]; + out[i4 + 2] = in[i3 + 1]; + out[i4 + 3] = in[i3 + 2]; + } +} + + +void GImage::flipRGBVertical( + const uint8* in, + uint8* out, + int width, + int height) { + + + // Allocate a temp row so the operation + // is still safe if in == out + uint8* temp = (uint8*)System::malloc(width * 3); + alwaysAssertM(temp != NULL, "Out of memory"); + + int oneRow = width * 3; + int N = height / 2; + + // if height is an odd value, don't swap odd middle row + for (int i = 0; i < N; ++i) { + int topOff = i * oneRow; + int botOff = (height - i - 1) * oneRow; + System::memcpy(temp, in + topOff, oneRow); + System::memcpy(out + topOff, in + botOff, oneRow); + System::memcpy(out + botOff, temp, oneRow); + } + + System::free(temp); +} + + +void GImage::flipRGBAVertical( + const uint8* in, + uint8* out, + int width, + int height) { + + + // Allocate a temp row so the operation + // is still safe if in == out + uint8* temp = (uint8*)System::malloc(width * 4); + alwaysAssertM(temp != NULL, "Out of memory"); + + int oneRow = width * 4; + + // if height is an odd value, don't swap odd middle row + for (int i = 0; i < height / 2; ++i) { + int topOff = i * oneRow; + int botOff = (height - i - 1) * oneRow; + System::memcpy(temp, in + topOff, oneRow); + System::memcpy(out + topOff, in + botOff, oneRow); + System::memcpy(out + botOff, temp, oneRow); + } + + System::free(temp); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void GImage::decode( + BinaryInput& input, + Format format) { + + switch (format) { + case PPM_ASCII: + decodePPMASCII(input); + break; + + case PPM: + decodePPM(input); + break; + + case PNG: + decodePNG(input); + break; + + case JPEG: + decodeJPEG(input); + break; + + case TGA: + decodeTGA(input); + break; + + case BMP: + decodeBMP(input); + break; + + case ICO: + decodeICO(input); + break; + + case PCX: + decodePCX(input); + break; + + default: + debugAssert(false); + } + + debugAssert(width >= 0); + debugAssert(height >= 0); + debugAssert(channels == 1 || channels == 3 || channels == 4); + debugAssert(_byte != NULL); +} + + +void GImage::decodePCX( + BinaryInput& input) { + + uint8 manufacturer = input.readUInt8(); + uint8 version = input.readUInt8(); + uint8 encoding = input.readUInt8(); + uint8 bitsPerPixel = input.readUInt8(); + + uint16 xmin = input.readUInt16(); + uint16 ymin = input.readUInt16(); + uint16 xmax = input.readUInt16(); + uint16 ymax = input.readUInt16(); + + uint16 horizDPI = input.readUInt16(); + uint16 vertDPI = input.readUInt16(); + + Color3uint8 colorMap[16]; + input.readBytes(colorMap, 48); + + input.skip(1); + + uint8 planes = input.readUInt8(); + uint16 bytesPerLine = input.readUInt16(); + uint16 paletteType = input.readUInt16(); + input.skip(4 + 54); + + (void)bytesPerLine; + + width = xmax - xmin + 1; + height = ymax - ymin + 1; + channels = 3; + + if ((manufacturer != 0x0A) || (encoding != 0x01)) { + throw GImage::Error("PCX file is corrupted", input.getFilename()); + } + + (void)version; + (void)vertDPI; + (void)horizDPI; + + if ((bitsPerPixel != 8) || ((planes != 1) && (planes != 3))) { + throw GImage::Error("Only 8-bit paletted and 24-bit PCX files supported.", input.getFilename()); + } + + // Prepare the pointer object for the pixel data + _byte = (uint8*)System::malloc(width * height * 3); + + if ((paletteType == 1) && (planes == 3)) { + + Color3uint8* pixel = pixel3(); + + // Iterate over each scan line + for (int row = 0; row < height; ++row) { + // Read each scan line once per plane + for (int plane = 0; plane < planes; ++plane) { + int p = row * width; + int p1 = p + width; + while (p < p1) { + uint8 value = input.readUInt8(); + int length = 1; + + if (value >= 192) { + // This is the length, not the value. Mask off + // the two high bits and read the true index. + length = value & 0x3F; + value = input.readUInt8(); + } + + // Set the whole run + for (int i = length - 1; i >= 0; --i, ++p) { + debugAssert(p < width * height); + pixel[p][plane] = value; + } + } + } + } + + } else if (planes == 1) { + + Color3uint8 palette[256]; + + int imageBeginning = input.getPosition(); + int paletteBeginning = input.getLength() - 769; + + input.setPosition(paletteBeginning); + + uint8 dummy = input.readUInt8(); + + if (dummy != 12) { + Log::common()->println("\n*********************"); + Log::common()->printf("Warning: Corrupted PCX file (palette marker byte was missing) \"%s\"\nLoading anyway\n\n", input.getFilename().c_str()); + } + + input.readBytes(palette, sizeof(palette)); + input.setPosition(imageBeginning); + + Color3uint8* pixel = pixel3(); + + // The palette indices are run length encoded. + int p = 0; + while (p < width * height) { + uint8 index = input.readUInt8(); + uint8 length = 1; + + if (index >= 192) { + // This is the length, not the index. Mask off + // the two high bits and read the true index. + length = index & 0x3F; + index = input.readUInt8(); + } + + Color3uint8 color = palette[index]; + + // Set the whole run + for (int i = length - 1; i >= 0; --i, ++p) { + if (p > width * height) { + break; + } + pixel[p] = color; + } + + } + + } else { + throw GImage::Error("Unsupported PCX file type.", input.getFilename()); + } +} + + +GImage::Format GImage::resolveFormat(const std::string& filename) { + BinaryInput b(filename, G3D_LITTLE_ENDIAN); + if (b.size() <= 0) { + throw Error("File not found.", filename); + } + + return resolveFormat(filename, b.getCArray(), b.size(), AUTODETECT); +} + + +GImage::Format GImage::resolveFormat( + const std::string& filename, + const uint8* data, + int dataLen, + Format maybeFormat) { + + // Return the provided format if it is specified. + if (maybeFormat != AUTODETECT) { + return maybeFormat; + } + + std::string extension; + + // Try to detect from the filename's extension + if (filename.size() >= 5) { + int n = iMax(filename.size() - 1, 5); + // Search backwards for the "." + for (int i = 1; i <= n; ++i) { + if (filename[filename.size() - i] == '.') { + // Upper case + extension = toUpper(filename.substr(filename.size() - i + 1)); + break; + } + } + } + + if (extension == "PPM") { + // There are two PPM formats; we handle them differently + if (dataLen > 3) { + if (!memcmp(data, "P6", 2)) { + return PPM; + } else { + return PPM_ASCII; + } + } + } + + Format tmp = stringToFormat(extension); + if ((tmp != AUTODETECT) && (tmp != UNKNOWN)) { + return tmp; + } + + // Try and autodetect from the file itself by looking at the first + // character. + + // We can't look at the character if it is null. + debugAssert(data != NULL); + + if ((dataLen > 3) && (!memcmp(data, "P3", 2) || !memcmp(data, "P2", 2) || !memcmp(data, "P1", 2))) { + return PPM_ASCII; + } + + if ((dataLen > 3) && !memcmp(data, "P6", 2)) { + return PPM; + } + + if (dataLen > 8) { + if (!png_sig_cmp((png_bytep)data, 0, 8)) + return PNG; + } + + if ((dataLen > 0) && (data[0] == 'B')) { + return BMP; + } + + if (dataLen > 10) { + if ((dataLen > 11) && (data[0] == 0xFF) && + (memcmp(&data[6], "JFIF", 4) == 0)) { + return JPEG; + } + } + + if (dataLen > 40) { + if (memcmp(&data[dataLen - 18], "TRUEVISION-XFILE", 16) == 0) { + return TGA; + } + } + + if ((dataLen > 4) && (data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 1)) { + return ICO; + } + + if ((dataLen > 0) && (data[0] == 10)) { + return PCX; + } + + return UNKNOWN; +} + + +GImage::GImage( + const std::string& filename, + Format format) : + _byte(NULL), + width(0), + height(0), + channels(0){ + + load(filename, format); +} + + +void GImage::load( + const std::string& filename, + Format format) { + + clear(); + + try { + BinaryInput b(filename, G3D_LITTLE_ENDIAN); + if (b.size() <= 0) { + throw Error("File not found.", filename); + } + + alwaysAssertM(this != NULL, "Corrupt GImage"); + decode(b, resolveFormat(filename, b.getCArray(), b.size(), format)); + } catch (const std::string& error) { + throw Error(error, filename); + } +} + + +GImage::GImage( + const uint8* data, + int length, + Format format) { + + BinaryInput b(data, length, G3D_LITTLE_ENDIAN); + // It is safe to cast away the const because we + // know we don't corrupt the data. + + decode(b, resolveFormat("", data, length, format)); +} + + +GImage::GImage( + int width, + int height, + int channels) { + + _byte = NULL; + resize(width, height, channels); +} + + +void GImage::resize( + int width, + int height, + int channels) { + debugAssert(width >= 0); + debugAssert(height >= 0); + debugAssert(channels >= 1); + + clear(); + + this->width = width; + this->height = height; + this->channels = channels; + size_t sz = width * height * channels; + + _byte = (uint8*)System::calloc(sz, sizeof(uint8)); + debugAssert(isValidHeapPointer(_byte)); +} + + +void GImage::_copy( + const GImage& other) { + + clear(); + + width = other.width; + height = other.height; + channels = other.channels; + int s = width * height * channels * sizeof(uint8); + _byte = (uint8*)System::malloc(s); + debugAssert(isValidHeapPointer(_byte)); + memcpy(_byte, other._byte, s); +} + + +GImage::GImage( + const GImage& other) : _byte(NULL) { + + _copy(other); +} + + +GImage::~GImage() { + clear(); +} + + +void GImage::clear() { + width = 0; + height = 0; + System::free(_byte); + _byte = NULL; +} + + +GImage& GImage::operator=(const GImage& other) { + _copy(other); + return *this; +} + + +bool GImage::copySubImage( + GImage & dest, const GImage & src, + int srcX, int srcY, int srcWidth, int srcHeight) { + if ((src.width < srcX + srcWidth) || + (src.height < srcY + srcHeight) || + (srcY < 0) || + (srcX < 0)) { + + return false; + } + + dest.resize(srcWidth, srcHeight, src.channels); + + bool ret; + ret = pasteSubImage(dest, src, 0, 0, srcX, srcY, srcWidth, srcHeight); + debugAssert(ret); + + return true; +} + + +bool GImage::pasteSubImage( + GImage & dest, const GImage & src, + int destX, int destY, + int srcX, int srcY, int srcWidth, int srcHeight) { + if ((src.width < srcX + srcWidth) || + (src.height < srcY + srcHeight) || + (dest.width < destX + srcWidth) || + (dest.height < destY + srcHeight) || + (srcY < 0) || + (srcX < 0) || + (destY < 0) || + (destX < 0) || + (src.channels != dest.channels)) { + + return false; + } + + for (int i = 0; i < srcHeight; i++) { + const uint8* srcRow = src.byte() + + ((i + srcY) * src.width + srcX) * src.channels; + uint8* destRow = dest.byte() + + ((i + destY) * dest.width + destX) * dest.channels; + memcpy(destRow, srcRow, srcWidth * src.channels); + } + + return true; +} + + +bool GImage::supportedFormat( + const std::string& format) { + + return (stringToFormat(format) != UNKNOWN); +} + + +GImage::Format GImage::stringToFormat( + const std::string& format) { + + std::string extension = toUpper(format); + + if ((extension == "JPG") || (extension == "JPEG")) { + return JPEG; + } else if (extension == "TGA") { + return TGA; + } else if (extension == "BMP") { + return BMP; + } else if (extension == "PCX") { + return PCX; + } else if (extension == "ICO") { + return ICO; + } else if (extension == "PNG") { + return PNG; + } else if (extension == "PPM") { + return PPM; + } else { + return UNKNOWN; + } +} + + +void GImage::save( + const std::string& filename, + Format format) const { + + BinaryOutput b(filename, G3D_LITTLE_ENDIAN); + encode(resolveFormat(filename, NULL, 0, format), b); + b.commit(false); +} + + +void GImage::encode( + Format format, + uint8*& outData, + int& outLength) const { + + BinaryOutput out; + + encode(format, out); + + outData = (uint8*)System::malloc(out.size()); + debugAssert(outData); + outLength = out.size(); + + out.commit(outData); +} + + +void GImage::encode( + Format format, + BinaryOutput& out) const { + + switch (format) { + case PPM_ASCII: + encodePPMASCII(out); + break; + + case PPM: + encodePPM(out); + break; + + case PNG: + encodePNG(out); + break; + + case JPEG: + encodeJPEG(out); + break; + + case BMP: + encodeBMP(out); + break; + + case TGA: + encodeTGA(out); + break; + + default: + debugAssert(false); + } +} + +void GImage::insertRedAsAlpha(const GImage& alpha, GImage& output) const { + debugAssert(alpha.width == width); + debugAssert(alpha.height == height); + + // make sure output GImage is valid + if (output.width != width || output.height != height || output.channels != 4) { + output.resize(width, height, 4); + } + + for (int i = 0; i < width * height; ++i) { + output.byte()[i * 4 + 0] = byte()[i * channels + 0]; + output.byte()[i * 4 + 1] = byte()[i * channels + 1]; + output.byte()[i * 4 + 2] = byte()[i * channels + 2]; + output.byte()[i * 4 + 3] = alpha.byte()[i * alpha.channels]; + } +} + +GImage GImage::insertRedAsAlpha(const GImage& alpha) const { + debugAssert(alpha.width == width); + debugAssert(alpha.height == height); + + GImage out(width, height, 4); + + insertRedAsAlpha(alpha, out); + + return out; +} + + +void GImage::stripAlpha(GImage& output) const { + + if (output.width != width || output.height != height || output.channels != 3) + { + output.resize(width, height, 3); + } + + for (int i = 0; i < width * height; ++i) { + output.byte()[i * 3 + 0] = byte()[i * channels + 0]; + output.byte()[i * 3 + 1] = byte()[i * channels + 1]; + output.byte()[i * 3 + 2] = byte()[i * channels + 2]; + } +} + +GImage GImage::stripAlpha() const { + GImage out(width, height, 3); + + stripAlpha(out); + + return out; +} + + +int GImage::sizeInMemory() const { + return sizeof(GImage) + width * height * channels; +} + + +void GImage::computeNormalMap( + const GImage& bump, + GImage& normal, + float whiteHeightInPixels, + bool lowPassBump, + bool scaleHeightByNz) { + computeNormalMap(bump.width, bump.height, bump.channels, bump.byte(), normal, whiteHeightInPixels, lowPassBump, scaleHeightByNz); +} + +void GImage::computeNormalMap( + int width, + int height, + int channels, + const uint8* src, + GImage& normal, + float whiteHeightInPixels, + bool lowPassBump, + bool scaleHeightByNz) { + + if (whiteHeightInPixels == -1.0f) { + // Default setting scales so that a gradient ramp + // over the whole image becomes a 45-degree angle + + // Account for potentially non-square aspect ratios + whiteHeightInPixels = max(width, height); + } + + debugAssert(whiteHeightInPixels >= 0); + + const int w = width; + const int h = height; + const int stride = channels; + + normal.resize(w, h, 4); + + const uint8* const B = src; + Color4uint8* const N = normal.pixel4(); + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + // Index into normal map pixel + int i = x + y * w; + + // Index into bump map *byte* + int j = stride * i; + + Vector3 delta; + + // Get a value from B (with wrapping lookup) relative to (x, y) + // and divide by 255 + #define height(DX, DY) ((int)B[(((DX + x + w) % w) + \ + ((DY + y + h) % h) * w) * stride]) + + + // Sobel filter to compute the normal. + // + // Y Filter (X filter is the transpose) + // [ -1 -2 -1 ] + // [ 0 0 0 ] + // [ 1 2 1 ] + + // Write the Y value directly into the x-component so we don't have + // to explicitly compute a cross product at the end. + delta.y = -(height(-1, -1) * 1 + height( 0, -1) * 2 + height( 1, -1) * 1 + + height(-1, 1) * -1 + height( 0, 1) * -2 + height( 1, 1) * -1); + + delta.x = -(height(-1, -1) * -1 + height( 1, -1) * 1 + + height(-1, 0) * -2 + height( 1, 0) * 2 + + height(-1, 1) * -1 + height( 1, 1) * 1); + + // The scale of each filter row is 4, the filter width is two pixels, + // and the "normal" range is 0-255. + delta.z = 4 * 2 * (whiteHeightInPixels / 255.0f); + + // Delta is now scaled in pixels; normalize + delta = delta.direction(); + + // Copy over the bump value into the alpha channel. + float H = B[j] / 255.0; + + if (lowPassBump) { + H = (height(-1, -1) + height( 0, -1) + height(1, -1) + + height(-1, 0) + height( 0, 0) + height(1, 0) + + height(-1, 1) + height( 0, 1) + height(1, 1)) / (255.0f * 9.0f); + } + #undef height + + if (scaleHeightByNz) { + // delta.z can't possibly be negative, so we avoid actually + // computing the absolute value. + H *= delta.z; + } + + N[i].a = iRound(H * 255.0); + + // Pack into byte range + delta = delta * 127.5 + Vector3(127.5, 127.5, 127.5); + N[i].r = iClamp(iRound(delta.x), 0, 255); + N[i].g = iClamp(iRound(delta.y), 0, 255); + N[i].b = iClamp(iRound(delta.z), 0, 255); + } + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void GImage::convertToL8() { + switch(channels) { + case 1: + return; + + case 3: + { + // Average + Color3uint8* src = (Color3uint8*)_byte; + _byte = NULL; + resize(width, height, 1); + for (int i = width * height - 1; i >= 0; --i) { + const Color3uint8 s = src[i]; + uint8& d = _byte[i]; + d = ((int)s.r + (int)s.g + (int)s.b) / 3; + } + System::free(src); + } + break; + + case 4: + { + // Average + Color4uint8* src = (Color4uint8*)_byte; + _byte = NULL; + resize(width, height, 1); + for (int i = width * height - 1; i >= 0; --i) { + const Color4uint8 s = src[i]; + uint8& d = _byte[i]; + d = ((int)s.r + (int)s.g + (int)s.b) / 3; + } + System::free(src); + } + return; + + default: + alwaysAssertM(false, "Bad number of channels in input image"); + } +} + + +void GImage::convertToRGBA() { + switch(channels) { + case 1: + { + // Spread + uint8* old = _byte; + _byte = NULL; + resize(width, height, 4); + for (int i = width * height - 1; i >= 0; --i) { + const uint8 s = old[i]; + Color4uint8& d = ((Color4uint8*)_byte)[i]; + d.r = d.g = d.b = s; + d.a = 255; + } + System::free(_byte); + } + break; + + case 3: + { + // Add alpha + Color3uint8* old = (Color3uint8*)_byte; + _byte = NULL; + resize(width, height, 4); + for (int i = width * height - 1; i >= 0; --i) { + const Color3uint8 s = old[i]; + Color4uint8& d = ((Color4uint8*)_byte)[i]; + d.r = s.r; + d.g = s.g; + d.b = s.b; + d.a = 255; + } + System::free(old); + } + break; + + case 4: + // Already RGBA + return; + + default: + alwaysAssertM(false, "Bad number of channels in input image"); + } +} + + +void GImage::convertToRGB() { + switch(channels) { + case 1: + { + // Spread + uint8* old = _byte; + _byte = NULL; + resize(width, height, 3); + for (int i = width * height - 1; i >= 0; --i) { + const uint8 s = old[i]; + Color3uint8& d = ((Color3uint8*)_byte)[i]; + d.r = d.g = d.b = s; + } + System::free(old); + } + break; + + case 3: + return; + + case 4: + // Strip alpha + { + Color4uint8* old = (Color4uint8*)_byte; + _byte = NULL; + resize(width, height, 3); + for (int i = width * height - 1; i >= 0; --i) { + const Color4uint8 s = old[i]; + Color3uint8& d = ((Color3uint8*)_byte)[i]; + d.r = s.r; + d.g = s.g; + d.b = s.b; + } + System::free(old); + } + break; + + default: + alwaysAssertM(false, "Bad number of channels in input image"); + } +} + + +void GImage::R8G8B8_to_Y8U8V8(int width, int height, const uint8* _in, uint8* _out) { + const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in); + Color3uint8* out = reinterpret_cast<Color3uint8*>(_out); + + Color3uint8 p; + for (int i = width * height - 1; i >= 0; --i) { + p.r = iClamp(iRound(in->r * 0.229 + in->g * 0.587 + in->b * 0.114), 0, 255); + p.g = iClamp(iRound(in->r * -0.147 + in->g * -0.289 + in->b * 0.436) + 127, 0, 255); + p.b = iClamp(iRound(in->r * 0.615 + in->g * -0.515 + in->b * -0.100) + 127, 0, 255); + *out = p; + ++in; + ++out; + } +} + + + +void GImage::Y8U8V8_to_R8G8B8(int width, int height, const uint8* _in, uint8* _out) { + const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in); + Color3uint8* out = reinterpret_cast<Color3uint8*>(_out); + + Color3uint8 p; + for (int i = width * height - 1; i >= 0; --i) { + p.r = iClamp(iRound(in->r * 1.0753 + (in->b - 127) * 1.2256), 0, 255); + p.g = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * -0.3946 + (in->b - 127) * -0.4947), 0, 255); + p.b = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * 2.0320 + (in->b - 127) * 0.0853), 0, 255); + *out = p; + ++in; + ++out; + } +} + + +void GImage::makeCheckerboard(GImage& im, int checkerSize, const Color4uint8& A, const Color4uint8& B) { + for (int y = 0; y < im.height; ++y) { + for (int x = 0; x < im.width; ++x) { + bool checker = isOdd((x / checkerSize) + (y / checkerSize)); + const Color4uint8& color = checker ? A : B; + for (int c = 0; c < im.channels; ++c) { + uint8* v = im.byte() + (x + y * im.width) * im.channels + c; + *v = color[c]; + } + } + } +} + +} + diff --git a/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp b/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp new file mode 100644 index 00000000000..16499af15f6 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp @@ -0,0 +1,298 @@ +/** + @file GImage_bayer.cpp + @author Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2006-05-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" + +namespace G3D { + +void GImage::BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int width, int height, const uint8* in, uint8* out) { + debugAssert(in != out); + + int halfHeight = height / 2; + int halfWidth = width / 2; + + int dst_off = 0; + for (int y = 0; y < halfHeight; ++y) { + for (int x = 0; x < halfWidth; ++x) { + // GBRG + int src_off = x*2 + y*2*width; + out[dst_off] = in[src_off+width]; // red + out[dst_off+1] = ((int)in[src_off] + (int)in[src_off+width+1])/2; // green + out[dst_off+2] = in[src_off+1]; // blue + + dst_off = dst_off + 3; + } + } +} + + +void GImage::Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out) { + // Undo quarter-size Bayer as best we can. This code isn't very efficient, but it + // also isn't used very frequently. + + debugAssert(out != in); + + int outWidth = 2 * inWidth; + int outHeight = 2 * inHeight; + + for (int y = 0; y < outHeight; ++y) { + for (int x = 0; x < outWidth; ++x) { + const Color3uint8* inp = ((const Color3uint8*)in) + ((x/2) + (y/2)* inWidth); + uint8* outp = out + x + y * outWidth; + + if (isEven(y)) { + // GB row + if (isEven(x)) { + // Green + *outp = inp->g; + } else { + // Blue + *outp = inp->b; + } + } else { + // RG row + if (isEven(x)) { + // Red + *outp = inp->r; + } else { + // Green + *outp = inp->g; + } + } + } + } +} + + +/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */ +static uint8 applyFilter( + const uint8* I, + int x, + int y, + int w, + int h, + const float filter[5][5]) { + + debugAssert(isEven(w)); + debugAssert(isEven(h)); + + float sum = 0.0f; + float denom = 0.0f; + + for (int dy = 0; dy < 5; ++dy) { + int offset = ((y + dy + h - 2) % h) * w; + + for (int dx = 0; dx < 5; ++dx) { + float f = filter[dy][dx]; + sum += f * I[((x + dx + w - 2) % w) + offset]; + denom += f; + } + } + + return (uint8)iClamp(iRound(sum / denom), 0, 255); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// +// Bayer conversions +// + +// There are two kinds of rows (GR and BG). +// In each row, there are two kinds of pixels (G/R, B/G). +// We express the four kinds of INPUT pixels as: +// GRG, GRG, BGB, BGG +// +// There are three kinds of OUTPUT pixels: R, G, B. +// Thus there are nominally 12 different I/O combinations, +// but several are impulses because needed output at that +// location *is* the input (e.g., G_GRG and G_BGG). +// +// The following 5x5 row-major filters are named as output_input. + +// Green +static const float G_GRR[5][5] = +{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, +{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, +{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, +{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, +{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; + +static const float G_BGB[5][5] = +{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, +{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, +{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, +{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, +{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; + +// Red +//(the caption in the paper is wrong for this case: +// "R row B column really means R row G column" +static const float R_GRG[5][5] = +{{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, +{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, +{ -1.0f, 4.0f, 5.0f, 4.0f, -1.0f}, +{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, +{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}}; + +static const float R_BGG[5][5] = +{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, +{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, +{ 0.5f, 0.0f, 5.0f, 0.0f, 0.5f}, +{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, +{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; + +static const float R_BGB[5][5] = +{{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}, +{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, +{-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f}, +{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, +{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}}; + + +// Blue +//(the caption in the paper is wrong for this case: +// "B row R column really means B row G column") +#define B_BGG R_GRG +#define B_GRG R_BGG +#define B_GRR R_BGB + + +void GImage::BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { + debugAssert(in != _out); + + Color3uint8* out = (Color3uint8*)_out; + + for (int y = 0; y < h; ++y) { + + // Row beginning in the input array. + int offset = y * w; + + // RG row + for (int x = 0; x < w; ++x, ++out) { + // R pixel + { + out->r = in[x + offset]; + out->g = applyFilter(in, x, y, w, h, G_GRR); + out->b = applyFilter(in, x, y, w, h, B_GRR); + } + ++x; ++out; + + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_GRG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_GRG); + } + } + + ++y; + offset += w; + + // GB row + for (int x = 0; x < w; ++x, ++out) { + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_BGG); + } + ++x; ++out; + + // B pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGB); + out->g = applyFilter(in, x, y, w, h, G_BGB); + out->b = in[x + offset]; + } + } + } +} + +static void swapRedAndBlue(int N, Color3uint8* out) { + for (int i = N - 1; i >= 0; --i) { + uint8 tmp = out[i].r; + out[i].r = out[i].b; + out[i].b = tmp; + } +} + +void GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { + // Run the equivalent function for red + BAYER_G8B8_R8G8_to_R8G8B8_MHC(w, h, in, _out); + + // Now swap red and blue + swapRedAndBlue(w * h, (Color3uint8*)_out); +} + + +void GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { + // Run the equivalent function for red + BAYER_R8G8_G8B8_to_R8G8B8_MHC(w, h, in, _out); + + // Now swap red and blue + swapRedAndBlue(w * h, (Color3uint8*)_out); +} + + +void GImage::BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { + + debugAssert(in != _out); + + Color3uint8* out = (Color3uint8*)_out; + + for (int y = 0; y < h; ++y) { + + // Row beginning in the input array. + int offset = y * w; + + // GB row + for (int x = 0; x < w; ++x, ++out) { + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_BGG); + } + ++x; ++out; + + // B pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGB); + out->g = applyFilter(in, x, y, w, h, G_BGB); + out->b = in[x + offset]; + } + } + + ++y; + offset += w; + + // RG row + for (int x = 0; x < w; ++x, ++out) { + // R pixel + { + out->r = in[x + offset]; + out->g = applyFilter(in, x, y, w, h, G_GRR); + out->b = applyFilter(in, x, y, w, h, B_GRR); + } + ++x; ++out; + + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_GRG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_GRG); + } + } + } + +} + +#undef B_BGG +#undef B_GRG +#undef B_GRR + +} diff --git a/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp b/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp new file mode 100644 index 00000000000..598ba730d0b --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp @@ -0,0 +1,716 @@ +/** + @file GImage_bmp.cpp + @author Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2006-05-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Log.h" + +namespace G3D { + +#ifndef G3D_WIN32 +/** + This is used by the Windows bitmap I/O. + */ +static const int BI_RGB = 0; +#endif + +void GImage::encodeBMP( + BinaryOutput& out) const { + + debugAssert(channels == 1 || channels == 3); + out.setEndian(G3D_LITTLE_ENDIAN); + + uint8 red; + uint8 green; + uint8 blue; + int pixelBufferSize = width * height * 3; + int fileHeaderSize = 14; + int infoHeaderSize = 40; + int BMScanWidth; + int BMPadding; + + // First write the BITMAPFILEHEADER + // + // WORD bfType; + // DWORD bfSize; + // WORD bfReserved1; + // WORD bfReserved2; + // DWORD bfOffBits; + + // Type + out.writeUInt8('B'); + out.writeUInt8('M'); + + // File size + out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize); + + // Two reserved fields set to zero + out.writeUInt16(0); + out.writeUInt16(0); + + // The offset, in bytes, from the BITMAPFILEHEADER structure + // to the bitmap bits. + out.writeUInt32(infoHeaderSize + fileHeaderSize); + + // Now the BITMAPINFOHEADER + // + // DWORD biSize; + // LONG biWidth; + // LONG biHeight; + // WORD biPlanes; + // WORD biBitCount + // DWORD biCompression; + // DWORD biSizeImage; + // LONG biXPelsPerMeter; + // LONG biYPelsPerMeter; + // DWORD biClrUsed; + // DWORD biClrImportant; + + // Size of the info header + out.writeUInt32(infoHeaderSize); + + // Width and height of the image + out.writeUInt32(width); + out.writeUInt32(height); + + // Planes ("must be set to 1") + out.writeUInt16(1); + + // BitCount and CompressionType + out.writeUInt16(24); + out.writeUInt32(BI_RGB); + + // Image size ("may be zero for BI_RGB bitmaps") + out.writeUInt32(0); + + // biXPelsPerMeter + out.writeUInt32(0); + // biYPelsPerMeter + out.writeUInt32(0); + + // biClrUsed + out.writeUInt32(0); + + // biClrImportant + out.writeUInt32(0); + + BMScanWidth = width * 3; + + if (BMScanWidth & 3) { + BMPadding = 4 - (BMScanWidth & 3); + } else { + BMPadding = 0; + } + + int hStart = height - 1; + int hEnd = -1; + int hDir = -1; + int dest; + + // Write the pixel data + for (int h = hStart; h != hEnd; h += hDir) { + dest = channels * h * width; + for (int w = 0; w < width; ++w) { + + if (channels == 3) { + red = _byte[dest]; + green = _byte[dest + 1]; + blue = _byte[dest + 2]; + } else { + red = _byte[dest]; + green = _byte[dest]; + blue = _byte[dest]; + } + + out.writeUInt8(blue); + out.writeUInt8(green); + out.writeUInt8(red); + + dest += channels; + } + + if (BMPadding > 0) { + out.skip(BMPadding); + } + } +} + + +void GImage::decodeBMP( + BinaryInput& input) { + + // The BMP decoding uses these flags. + static const uint16 PICTURE_NONE = 0x0000; + static const uint16 PICTURE_BITMAP = 0x1000; + + // Compression Flags + static const uint16 PICTURE_UNCOMPRESSED = 0x0100; + static const uint16 PICTURE_MONOCHROME = 0x0001; + static const uint16 PICTURE_4BIT = 0x0002; + static const uint16 PICTURE_8BIT = 0x0004; + static const uint16 PICTURE_16BIT = 0x0008; + static const uint16 PICTURE_24BIT = 0x0010; + static const uint16 PICTURE_32BIT = 0x0020; + + (void)PICTURE_16BIT; + (void)PICTURE_32BIT; + + // This is a simple BMP loader that can handle uncompressed BMP files. + // Verify this is a BMP file by looking for the BM tag. + input.reset(); + std::string tag = input.readString(2); + if (tag != "BM") { + throw Error("Not a BMP file", input.getFilename()); + } + + channels = 3; + // Skip to the BITMAPINFOHEADER's width and height + input.skip(16); + + width = input.readUInt32(); + height = input.readUInt32(); + + // Skip to the bit count and compression type + input.skip(2); + + uint16 bitCount = input.readUInt16(); + uint32 compressionType = input.readUInt32(); + + uint8 red; + uint8 green; + uint8 blue; + uint8 blank; + + // Only uncompressed bitmaps are supported by this code + if ((int32)compressionType != BI_RGB) { + throw Error("BMP images must be uncompressed", input.getFilename()); + } + + uint8* palette = NULL; + + // Create the palette if needed + if (bitCount <= 8) { + + // Skip to the palette color count in the header + input.skip(12); + + int numColors = input.readUInt32(); + + palette = (uint8*)System::malloc(numColors * 3); + debugAssert(palette); + + // Skip past the end of the header to the palette info + input.skip(4); + + int c; + for(c = 0; c < numColors * 3; c += 3) { + // Palette information in bitmaps is stored in BGR_ format. + // That means it's blue-green-red-blank, for each entry. + blue = input.readUInt8(); + green = input.readUInt8(); + red = input.readUInt8(); + blank = input.readUInt8(); + + palette[c] = red; + palette[c + 1] = green; + palette[c + 2] = blue; + } + } + + int hStart = 0; + int hEnd = 0; + int hDir = 0; + + if (height < 0) { + height = -height; + hStart = 0; + hEnd = height; + hDir = 1; + } else { + //height = height; + hStart = height - 1; + hEnd = -1; + hDir = -1; + } + + _byte = (uint8*)System::malloc(width * height * 3); + debugAssert(_byte); + + int BMScanWidth; + int BMPadding; + uint8 BMGroup; + uint8 BMPixel8; + int currPixel; + int dest; + int flags = PICTURE_NONE; + + if (bitCount == 1) { + // Note that this file is not necessarily grayscale, since it's possible + // the palette is blue-and-white, or whatever. But of course most image + // programs only write 1-bit images if they're black-and-white. + flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME; + + // For bitmaps, each scanline is dword-aligned. + BMScanWidth = (width + 7) >> 3; + if (BMScanWidth & 3) { + BMScanWidth += 4 - (BMScanWidth & 3); + } + + // Powers of 2 + int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + + for (int h = hStart; h != hEnd; h += hDir) { + + currPixel = 0; + dest = 3 * h * width; + + for (int w = 0; w < BMScanWidth; ++w) { + + BMGroup = input.readUInt8(); + + // Now we read the pixels. Usually there are eight pixels per byte, + // since each pixel is represented by one bit, but if the width + // is not a multiple of eight, the last byte will have some bits + // set, with the others just being extra. Plus there's the + // dword-alignment padding. So we keep checking to see if we've + // already read "width" number of pixels. + for (int i = 7; i >= 0; --i) { + if (currPixel < width) { + int src = 3 * ((BMGroup & pow2[i]) >> i); + + _byte[dest] = palette[src]; + _byte[dest + 1] = palette[src + 1]; + _byte[dest + 2] = palette[src + 2]; + + ++currPixel; + dest += 3; + } + } + } + } + + } else if (bitCount == 4) { + + flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT; + + // For bitmaps, each scanline is dword-aligned. + int BMScanWidth = (width+1) >> 1; + if (BMScanWidth & 3) { + BMScanWidth += 4 - (BMScanWidth & 3); + } + + for (int h = hStart; h != hEnd; h += hDir) { + + currPixel = 0; + dest = 3 * h * width; + + for (int w = 0; w < BMScanWidth; w++) { + + BMGroup = input.readUInt8(); + int src[2]; + src[0] = 3 * ((BMGroup & 0xF0) >> 4); + src[1] = 3 * (BMGroup & 0x0F); + + // Now we read the pixels. Usually there are two pixels per byte, + // since each pixel is represented by four bits, but if the width + // is not a multiple of two, the last byte will have only four bits + // set, with the others just being extra. Plus there's the + // dword-alignment padding. So we keep checking to see if we've + // already read "Width" number of pixels. + + for (int i = 0; i < 2; ++i) { + if (currPixel < width) { + int tsrc = src[i]; + + _byte[dest] = palette[tsrc]; + _byte[dest + 1] = palette[tsrc + 1]; + _byte[dest + 2] = palette[tsrc + 2]; + + ++currPixel; + dest += 3; + } + } + } + } + + } else if (bitCount == 8) { + + flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT; + + // For bitmaps, each scanline is dword-aligned. + BMScanWidth = width; + if (BMScanWidth & 3) { + BMScanWidth += 4 - (BMScanWidth & 3); + } + + for (int h = hStart; h != hEnd; h += hDir) { + + currPixel = 0; + + for (int w = 0; w < BMScanWidth; ++w) { + + BMPixel8 = input.readUInt8(); + + if (currPixel < width) { + dest = 3 * ((h * width) + currPixel); + int src = 3 * BMPixel8; + + _byte[dest] = palette[src]; + _byte[dest + 1] = palette[src + 1]; + _byte[dest + 2] = palette[src + 2]; + + ++currPixel; + } + } + } + + } else if (bitCount == 16) { + + System::free(_byte); + _byte = NULL; + System::free(palette); + palette = NULL; + throw Error("16-bit bitmaps not supported", input.getFilename()); + + } else if (bitCount == 24) { + input.skip(20); + + flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT; + + // For bitmaps, each scanline is dword-aligned. + BMScanWidth = width * 3; + + if (BMScanWidth & 3) { + BMPadding = 4 - (BMScanWidth & 3); + } else { + BMPadding = 0; + } + + for (int h = hStart; h != hEnd; h += hDir) { + dest = 3 * h * width; + for (int w = 0; w < width; ++w) { + + blue = input.readUInt8(); + green = input.readUInt8(); + red = input.readUInt8(); + + _byte[dest] = red; + _byte[dest + 1] = green; + _byte[dest + 2] = blue; + + dest += 3; + } + + if (BMPadding) { + input.skip(2); + } + } + + } else if (bitCount == 32) { + + System::free(_byte); + _byte = NULL; + System::free(palette); + palette = NULL; + throw Error("32 bit bitmaps not supported", input.getFilename()); + + } else { + // We support all possible bit depths, so if the + // code gets here, it's not even a real bitmap. + System::free(_byte); + _byte = NULL; + throw Error("Not a bitmap!", input.getFilename()); + } + + System::free(palette); + palette = NULL; +} + + +void GImage::decodeICO( + BinaryInput& input) { + + // Header + uint16 r = input.readUInt16(); + debugAssert(r == 0); + r = input.readUInt16(); + debugAssert(r == 1); + + // Read the number of icons, although we'll only load the + // first one. + int count = input.readUInt16(); + + channels = 4; + + debugAssert(count > 0); + + const uint8* headerBuffer = input.getCArray() + input.getPosition(); + int maxWidth = 0, maxHeight = 0; + int maxHeaderNum = 0; + for (int currentHeader = 0; currentHeader < count; ++currentHeader) { + + const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16); + int tmpWidth = curHeaderBuffer[0]; + int tmpHeight = curHeaderBuffer[1]; + // Just in case there is a non-square icon, checking area + if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight)) { + maxWidth = tmpWidth; + maxHeight = tmpHeight; + maxHeaderNum = currentHeader; + } + } + + input.skip(maxHeaderNum * 16); + + width = input.readUInt8(); + height = input.readUInt8(); + int numColors = input.readUInt8(); + + _byte = (uint8*)System::malloc(width * height * channels); + debugAssert(_byte); + + // Bit mask for packed bits + int mask = 0; + + int bitsPerPixel = 8; + + switch (numColors) { + case 2: + mask = 0x01; + bitsPerPixel = 1; + break; + + case 16: + mask = 0x0F; + bitsPerPixel = 4; + break; + + case 0: + numColors = 256; + mask = 0xFF; + bitsPerPixel = 8; + break; + default: + throw Error("Unsupported ICO color count.", input.getFilename()); + } + + input.skip(5); + // Skip 'size' unused + input.skip(4); + + int offset = input.readUInt32(); + + // Skip over any other icon descriptions + input.setPosition(offset); + + // Skip over bitmap header; it is redundant + input.skip(40); + + Array<Color4uint8> palette; + palette.resize(numColors, true); + for (int c = 0; c < numColors; ++c) { + palette[c].b = input.readUInt8(); + palette[c].g = input.readUInt8(); + palette[c].r = input.readUInt8(); + palette[c].a = input.readUInt8(); + } + + // The actual image and mask follow + + // The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap + // using the same encoding as BMP files. The AND Bitmap is stored in as + // 1-bit uncompressed Bitmap. + // + // Pixels are stored bottom-up, left-to-right. Pixel lines are padded + // with zeros to end on a 32bit (4byte) boundary. Every line will have the + // same number of bytes. Color indices are zero based, meaning a pixel color + // of 0 represents the first color table entry, a pixel color of 255 (if there + // are that many) represents the 256th entry. +/* + int bitsPerRow = width * bitsPerPixel; + int bytesPerRow = iCeil((double)bitsPerRow / 8); + // Rows are padded to 32-bit boundaries + bytesPerRow += bytesPerRow % 4; + + // Read the XOR values into the color channel + for (int y = height - 1; y >= 0; --y) { + int x = 0; + // Read the row + for (int i = 0; i < bytesPerRow; ++i) { + uint8 byte = input.readUInt8(); + for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) { + int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask; + pixel4(x, y) = colorTable[bit]; + } + } + } +*/ + int hStart = 0; + int hEnd = 0; + int hDir = 0; + + if (height < 0) { + height = -height; + hStart = 0; + hEnd = height; + hDir = 1; + } else { + //height = height; + hStart = height - 1; + hEnd = -1; + hDir = -1; + } + + int BMScanWidth; + uint8 BMGroup; + uint8 BMPixel8; + int currPixel; + int dest; + + if (bitsPerPixel == 1) { + // Note that this file is not necessarily grayscale, since it's possible + // the palette is blue-and-white, or whatever. But of course most image + // programs only write 1-bit images if they're black-and-white. + + // For bitmaps, each scanline is dword-aligned. + BMScanWidth = (width + 7) >> 3; + if (BMScanWidth & 3) { + BMScanWidth += 4 - (BMScanWidth & 3); + } + + // Powers of 2 + int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + + for (int h = hStart; h != hEnd; h += hDir) { + + currPixel = 0; + dest = 3 * h * width; + + for (int w = 0; w < BMScanWidth; ++w) { + + BMGroup = input.readUInt8(); + + // Now we read the pixels. Usually there are eight pixels per byte, + // since each pixel is represented by one bit, but if the width + // is not a multiple of eight, the last byte will have some bits + // set, with the others just being extra. Plus there's the + // dword-alignment padding. So we keep checking to see if we've + // already read "width" number of pixels. + for (int i = 7; i >= 0; --i) { + if (currPixel < width) { + int src = ((BMGroup & pow2[i]) >> i); + + _byte[dest] = palette[src].r; + _byte[dest + 1] = palette[src].g; + _byte[dest + 2] = palette[src].b; + + ++currPixel; + dest += 4; + } + } + } + } + + } else if (bitsPerPixel == 4) { + + // For bitmaps, each scanline is dword-aligned. + int BMScanWidth = (width+1) >> 1; + if (BMScanWidth & 3) { + BMScanWidth += 4 - (BMScanWidth & 3); + } + + for (int h = hStart; h != hEnd; h += hDir) { + + currPixel = 0; + dest = 4 * h * width; + + for (int w = 0; w < BMScanWidth; w++) { + + BMGroup = input.readUInt8(); + int src[2]; + src[0] = ((BMGroup & 0xF0) >> 4); + src[1] = (BMGroup & 0x0F); + + // Now we read the pixels. Usually there are two pixels per byte, + // since each pixel is represented by four bits, but if the width + // is not a multiple of two, the last byte will have only four bits + // set, with the others just being extra. Plus there's the + // dword-alignment padding. So we keep checking to see if we've + // already read "Width" number of pixels. + + for (int i = 0; i < 2; ++i) { + if (currPixel < width) { + int tsrc = src[i]; + + _byte[dest] = palette[tsrc].r; + _byte[dest + 1] = palette[tsrc].g; + _byte[dest + 2] = palette[tsrc].b; + + ++currPixel; + dest += 4; + } + } + } + } + + } else if (bitsPerPixel == 8) { + + // For bitmaps, each scanline is dword-aligned. + BMScanWidth = width; + if (BMScanWidth & 3) { + BMScanWidth += 4 - (BMScanWidth & 3); + } + + for (int h = hStart; h != hEnd; h += hDir) { + + currPixel = 0; + + for (int w = 0; w < BMScanWidth; ++w) { + + BMPixel8 = input.readUInt8(); + + if (currPixel < width) { + dest = 4 * ((h * width) + currPixel); + int src = BMPixel8; + + _byte[dest] = palette[src].r; + _byte[dest + 1] = palette[src].g; + _byte[dest + 2] = palette[src].b; + + ++currPixel; + } + } + } + } + + // Read the mask into the alpha channel + int bitsPerRow = width; + int bytesPerRow = iCeil((double)bitsPerRow / 8); + + // For bitmaps, each scanline is dword-aligned. + //BMScanWidth = (width + 1) >> 1; + if (bytesPerRow & 3) { + bytesPerRow += 4 - (bytesPerRow & 3); + } + + for (int y = height - 1; y >= 0; --y) { + int x = 0; + // Read the row + for (int i = 0; i < bytesPerRow; ++i) { + uint8 byte = input.readUInt8(); + for (int j = 0; (j < 8) && (x < width); ++x, ++j) { + int bit = (byte >> (7 - j)) & 0x01; + pixel4(x, y).a = (1 - bit) * 0xFF; + } + } + } + +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp b/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp new file mode 100644 index 00000000000..fe2812cf3b2 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp @@ -0,0 +1,445 @@ +/** + @file GImage_jpeg.cpp + @author Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2006-10-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +#include <cstring> + +/** + Pick up libjpeg headers locally on Windows, but from the system on all other platforms. +*/ +extern "C" { +#include <jconfig.h> +#include <jpeglib.h> +} + +namespace G3D { + + +const int jpegQuality = 96; + +/** + The IJG library needs special setup for compress/decompressing + from memory. These classes provide them. + + The format of this class is defined by the IJG library; do not + change it. + */ +class memory_destination_mgr { +public: + struct jpeg_destination_mgr pub; + JOCTET* buffer; + int size; + int count; +}; + +typedef memory_destination_mgr* mem_dest_ptr; + +/** + Signature dictated by IJG. + */ +static void init_destination ( + j_compress_ptr cinfo) { + + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = dest->size; + dest->count=0; +} + +/** + Signature dictated by IJG. + */ +static boolean empty_output_buffer ( + j_compress_ptr cinfo) { + + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = dest->size; + + return TRUE; +} + +/** + Signature dictated by IJG. + */ +static void term_destination ( + j_compress_ptr cinfo) { + + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + dest->count = dest->size - dest->pub.free_in_buffer; +} + +/** + Signature dictated by IJG. + */ +static void jpeg_memory_dest ( + j_compress_ptr cinfo, + JOCTET* buffer, + int size) { + + mem_dest_ptr dest; + + if (cinfo->dest == NULL) { + // First time for this JPEG object; call the + // IJG allocator to get space. + cinfo->dest = (struct jpeg_destination_mgr*) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, + JPOOL_PERMANENT, + sizeof(memory_destination_mgr)); + } + + dest = (mem_dest_ptr) cinfo->dest; + dest->size = size; + dest->buffer = buffer; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +#define INPUT_BUF_SIZE 4096 + +/** + Structure dictated by IJG. + */ +class memory_source_mgr { +public: + struct jpeg_source_mgr pub; + int source_size; + unsigned char* source_data; + boolean start_of_data; + JOCTET* buffer; +}; + + +typedef memory_source_mgr* mem_src_ptr; + + +/** + Signature dictated by IJG. + */ +static void init_source( + j_decompress_ptr cinfo) { + + mem_src_ptr src = (mem_src_ptr) cinfo->src; + + src->start_of_data = TRUE; +} + + +/** + Signature dictated by IJG. + */ +static boolean fill_input_buffer( + j_decompress_ptr cinfo) { + + mem_src_ptr src = (mem_src_ptr) cinfo->src; + + size_t bytes_read = 0; + + if (src->source_size > INPUT_BUF_SIZE) + bytes_read = INPUT_BUF_SIZE; + else + bytes_read = src->source_size; + + memcpy (src->buffer, src->source_data, bytes_read); + + src->source_data += bytes_read; + src->source_size -= bytes_read; + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = bytes_read; + src->start_of_data = FALSE; + + + return TRUE; +} + + +/** + Signature dictated by IJG. + */ +static void skip_input_data( + j_decompress_ptr cinfo, + long num_bytes) { + + mem_src_ptr src = (mem_src_ptr)cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + boolean s = fill_input_buffer(cinfo); + debugAssert(s); (void)s; + } + + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/** + Signature dictated by IJG. + */ +static void term_source ( + j_decompress_ptr cinfo) { + (void)cinfo; + // Intentionally empty +} + + +/** + Signature dictated by IJG. + */ +static void jpeg_memory_src ( + j_decompress_ptr cinfo, + JOCTET* buffer, + int size) { + + mem_src_ptr src; + + if (cinfo->src == NULL) { + // First time for this JPEG object + cinfo->src = (struct jpeg_source_mgr*) + (*cinfo->mem->alloc_small)( + (j_common_ptr) cinfo, + JPOOL_PERMANENT, + sizeof(memory_source_mgr)); + + src = (mem_src_ptr)cinfo->src; + + src->buffer = (JOCTET*) + (*cinfo->mem->alloc_small)( + (j_common_ptr) cinfo, + JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET)); + } + + src = (mem_src_ptr)cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + + // use default method + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = term_source; + src->source_data = buffer; + src->source_size = size; + + // forces fill_input_buffer on first read + src->pub.bytes_in_buffer = 0; + + // until buffer loaded + src->pub.next_input_byte = NULL; +} + + +void GImage::encodeJPEG( + BinaryOutput& out) const { + + if (channels != 3) { + // Convert to three channel + GImage tmp = *this; + tmp.convertToRGB(); + tmp.encodeJPEG(out); + return; + } + + debugAssert(channels == 3); + out.setEndian(G3D_LITTLE_ENDIAN); + + // Allocate and initialize a compression object + jpeg_compress_struct cinfo; + jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + // Specify the destination for the compressed data. + // (Overestimate the size) + int buffer_size = width * height * 3 + 200; + JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size); + jpeg_memory_dest(&cinfo, compressed_data, buffer_size); + + + cinfo.image_width = width; + cinfo.image_height = height; + + // # of color components per pixel + cinfo.input_components = 3; + + // colorspace of input image + cinfo.in_color_space = JCS_RGB; + cinfo.input_gamma = 1.0; + + // Set parameters for compression, including image size & colorspace + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, jpegQuality, false); + cinfo.smoothing_factor = 0; + cinfo.optimize_coding = TRUE; +// cinfo.dct_method = JDCT_FLOAT; + cinfo.dct_method = JDCT_ISLOW; + cinfo.jpeg_color_space = JCS_YCbCr; + + // Initialize the compressor + jpeg_start_compress(&cinfo, TRUE); + + // Iterate over all scanlines from top to bottom + // pointer to a single row + JSAMPROW row_pointer[1]; + + // JSAMPLEs per row in image_buffer + int row_stride = cinfo.image_width * 3; + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = &(_byte[cinfo.next_scanline * row_stride]); + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + // Shut down the compressor + jpeg_finish_compress(&cinfo); + + // Figure out how big the result was. + int outLength = ((mem_dest_ptr)cinfo.dest)->count; + + // Release the JPEG compression object + jpeg_destroy_compress(&cinfo); + + // Copy into an appropriately sized output buffer. + out.writeBytes(compressed_data, outLength); + + // Free the conservative buffer. + System::free(compressed_data); + compressed_data = NULL; +} + + + +void GImage::decodeJPEG( + BinaryInput& input) { + + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int loc = 0; + + channels = 3; + // We have to set up the error handler, in case initialization fails. + cinfo.err = jpeg_std_error(&jerr); + + // Initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + // Specify data source (eg, a file, for us, memory) + jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size()); + + // Read the parameters with jpeg_read_header() + jpeg_read_header(&cinfo, TRUE); + + // Set parameters for decompression + // (We do nothing here since the defaults are fine) + + // Start decompressor + jpeg_start_decompress(&cinfo); + + // Get and set the values of interest to this object + this->width = cinfo.output_width; + this->height = cinfo.output_height; + + // Prepare the pointer object for the pixel data + _byte = (uint8*)System::malloc(width * height * 3); + + // JSAMPLEs per row in output buffer + int bpp = cinfo.output_components; + int row_stride = cinfo.output_width * bpp; + + // Make a one-row-high sample array that will go away when done with image + JSAMPARRAY temp = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + // Read data on a scanline by scanline basis + while (cinfo.output_scanline < cinfo.output_height) { + + // We may need to adjust the output based on the + // number of channels it has. + switch (bpp) { + case 1: + // Grayscale; decompress to temp. + jpeg_read_scanlines(&cinfo, temp, 1); + + // Expand to three channels + { + uint8* scan = &(_byte[loc * 3]); + uint8* endScan = scan + (width * 3); + uint8* t = *temp; + + while (scan < endScan) { + uint8 value = t[0]; + + // Spread the value 3x. + scan[0] = value; + scan[1] = value; + scan[2] = value; + + scan += 3; + t += 1; + } + } + break; + + case 3: + // Read directly into the array + { + // Need one extra level of indirection. + uint8* scan = _byte + loc; + JSAMPARRAY ptr = &scan; + jpeg_read_scanlines(&cinfo, ptr, 1); + } + break; + + case 4: + // RGBA; decompress to temp. + jpeg_read_scanlines(&cinfo, temp, 1); + + // Drop the 3rd channel + { + uint8* scan = &(_byte[loc * 3]); + uint8* endScan = scan + width * 3; + uint8* t = *temp; + + while (scan < endScan) { + scan[0] = t[0]; + scan[1] = t[1]; + scan[2] = t[2]; + + scan += 3; + t += 4; + } + } + break; + + default: + throw Error("Unexpected number of channels.", input.getFilename()); + } + + loc += row_stride; + } + + // Finish decompression + jpeg_finish_decompress(&cinfo); + + alwaysAssertM(this, "Corrupt GImage"); + // Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/GImage_png.cpp b/externals/g3dlite/G3D.lib/source/GImage_png.cpp new file mode 100644 index 00000000000..9fd6a743e8e --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage_png.cpp @@ -0,0 +1,245 @@ +/** + @file GImage_png.cpp + @author Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2006-05-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Log.h" +#include <png.h> + +namespace G3D { + + +//libpng required function signature +static void png_read_data( + png_structp png_ptr, + png_bytep data, + png_size_t length) { + + + debugAssert( png_ptr->io_ptr != NULL ); + debugAssert( length >= 0 ); + debugAssert( data != NULL ); + + ((BinaryInput*)png_ptr->io_ptr)->readBytes(data, length); +} + +//libpng required function signature +static void png_write_data(png_structp png_ptr, + png_bytep data, + png_size_t length) { + + debugAssert( png_ptr->io_ptr != NULL ); + debugAssert( data != NULL ); + + ((BinaryOutput*)png_ptr->io_ptr)->writeBytes(data, length); +} + +//libpng required function signature +static void png_flush_data( + png_structp png_ptr) { + (void)png_ptr; + //Do nothing. +} + +//libpng required function signature +static void png_error( + png_structp png_ptr, + png_const_charp error_msg) { + + (void)png_ptr; + debugAssert( error_msg != NULL ); + throw GImage::Error(error_msg, "PNG"); +} + +//libpng required function signature +void png_warning( + png_structp png_ptr, + png_const_charp warning_msg) { + + (void)png_ptr; + debugAssert( warning_msg != NULL ); + Log::common()->println(warning_msg); +} + +void GImage::encodePNG( + BinaryOutput& out) const { + + debugAssert( channels == 1 || channels == 3 || channels == 4 ); + + if (this->height > (int)(PNG_UINT_32_MAX/png_sizeof(png_bytep))) + throw GImage::Error("Unsupported PNG height.", out.getFilename()); + + out.setEndian(G3D_LITTLE_ENDIAN); + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning); + if (!png_ptr) + throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename()); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, &info_ptr); + throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename()); + } + + //setup libpng write handler so can use BinaryOutput + png_set_write_fn(png_ptr, (void*)&out, png_write_data, png_flush_data); + + if (channels == 3) { + png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + else if (channels == 4) { + png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + else if (channels == 1) { + png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + else { + png_destroy_write_struct(&png_ptr, &info_ptr); + throw GImage::Error("Unsupported number of channels for PNG.", out.getFilename()); + } + + png_color_8_struct sig_bit; + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + if (channels == 4) + sig_bit.alpha = 8; + else + sig_bit.alpha = 0; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + + //write the png header + png_write_info(png_ptr, info_ptr); + + png_bytepp row_pointers = new png_bytep[this->height]; + + for (int i=0; i < this->height; ++i) { + row_pointers[i] = (png_bytep)&this->_byte[(this->width * this->channels * i)]; + } + + png_write_image(png_ptr, row_pointers); + + png_write_end(png_ptr, info_ptr); + + delete[] row_pointers; + + png_destroy_write_struct(&png_ptr, &info_ptr); +} + + +void GImage::decodePNG( + BinaryInput& input) { + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning); + if (!png_ptr) + throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename()); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename()); + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename()); + } + + //now that the libpng structures are setup, change the error handlers and read routines + //to use G3D functions so that BinaryInput can be used. + + png_set_read_fn(png_ptr, (png_voidp)&input, png_read_data); + + //read in sequentially so that three copies of the file are not in memory at once + png_read_info(png_ptr, info_ptr); + + png_uint_32 png_width, png_height; + int bit_depth, color_type, interlace_type; + //this will validate the data it extracts from info_ptr + png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + throw GImage::Error("Unsupported PNG color type - PNG_COLOR_TYPE_GRAY_ALPHA.", input.getFilename()); + } + + this->width = static_cast<uint32>(png_width); + this->height = static_cast<uint32>(png_height); + + //swap bytes of 16 bit files to least significant byte first + png_set_swap(png_ptr); + + png_set_strip_16(png_ptr); + + //Expand paletted colors into true RGB triplets + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + + //Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + png_set_gray_1_2_4_to_8(png_ptr); + } + + //Expand paletted or RGB images with transparency to full alpha channels + //so the data will be available as RGBA quartets. + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + } + + // Fix sub-8 bit_depth to 8bit + if (bit_depth < 8) { + png_set_packing(png_ptr); + } + + if ((color_type == PNG_COLOR_TYPE_RGBA) || + ((color_type == PNG_COLOR_TYPE_PALETTE) && (png_ptr->num_trans > 0)) ) { + + this->channels = 4; + this->_byte = (uint8*)System::malloc(width * height * 4); + + } else if ((color_type == PNG_COLOR_TYPE_RGB) || + (color_type == PNG_COLOR_TYPE_PALETTE)) { + + this->channels = 3; + this->_byte = (uint8*)System::malloc(width * height * 3); + + } else if (color_type == PNG_COLOR_TYPE_GRAY) { + + this->channels = 1; + this->_byte = (uint8*)System::malloc(width * height); + + } else { + throw GImage::Error("Unsupported PNG bit-depth or type.", input.getFilename()); + } + + //since we are reading row by row, required to handle interlacing + uint32 number_passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + for (uint32 pass = 0; pass < number_passes; ++pass) { + for (uint32 y = 0; y < (uint32)height; ++y) { + png_bytep rowPointer = &this->_byte[width * this->channels * y]; + png_read_rows(png_ptr, &rowPointer, png_bytepp_NULL, 1); + } + } + +// png_read_image(png_ptr, &_byte); + png_read_end(png_ptr, info_ptr); + + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp b/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp new file mode 100644 index 00000000000..bfe5c8305e8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp @@ -0,0 +1,185 @@ +/** + @file GImage_ppm.cpp + @author Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2006-05-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/Log.h" + +namespace G3D { + +void GImage::encodePPMASCII( + BinaryOutput& out) const { + + debugAssert(channels == 3); + + TextOutput::Settings ppmOptions; + ppmOptions.convertNewlines = false; + ppmOptions.numColumns = 70; + ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING; + TextOutput ppm(ppmOptions); + // Always write out a full-color ppm + ppm.printf("P3\n%d %d\n255\n", width, height); + + const Color3uint8* c = this->pixel3(); + for (uint32 i = 0; i < (uint32)(width * height); ++i) { + ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b, + ((i % ((width * 3) - 1)) == 0) ? + '\n' : ' '); + } + + out.writeString(ppm.commitString()); +} + + +void GImage::encodePPM( + BinaryOutput& out) const { + + // http://netpbm.sourceforge.net/doc/ppm.html + debugAssert(channels == 3); + + std::string header = format("P6 %d %d 255 ", width, height); + + out.writeBytes(header.c_str(), header.size()); + + out.writeBytes(this->pixel3(), width * height * 3); +} + +void GImage::decodePPMASCII( + BinaryInput& input) { + + int ppmWidth; + int ppmHeight; + + double maxColor; + + // Create a TextInput object to parse ascii format + // Mixed binary/ascii formats will require more + + const std::string inputStr = input.readString(); + + TextInput::Settings ppmOptions; + ppmOptions.cppComments = false; + ppmOptions.otherCommentCharacter = '#'; + ppmOptions.signedNumbers = true; + ppmOptions.singleQuotedStrings = false; + + TextInput ppmInput(TextInput::FROM_STRING, inputStr, ppmOptions); + + //Skip first line in header P# + std::string ppmType = ppmInput.readSymbol(); + + ppmWidth = (int)ppmInput.readNumber(); + ppmHeight = (int)ppmInput.readNumber(); + + // Everything but a PBM will have a max color value + if (ppmType != "P2") { + maxColor = ppmInput.readNumber(); + } else { + maxColor = 255; + } + + if ((ppmWidth < 0) || + (ppmHeight < 0) || + (maxColor <= 0)) { + throw GImage::Error("Invalid PPM Header.", input.getFilename()); + } + + // I don't think it's proper to scale values less than 255 + if (maxColor <= 255.0) { + maxColor = 255.0; + } + + this->width = ppmWidth; + this->height = ppmHeight; + this->channels = 3; + // always scale down to 1 byte per channel + this->_byte = (uint8*)System::malloc(width * height * 3); + + // Read in the image data. I am not validating if the values match the maxColor + // requirements. I only scale if needed to fit within the byte available. + for (uint32 i = 0; i < (uint32)(width * height); ++i) { + // read in color and scale to max pixel defined in header + // A max color less than 255 might need to be left alone and not scaled. + Color3uint8& curPixel = *(this->pixel3() + i); + + if (ppmType == "P3") { + curPixel.r = (uint8)(ppmInput.readNumber() * (255.0 / maxColor)); + curPixel.g = (uint8)(ppmInput.readNumber() * (255.0 / maxColor)); + curPixel.b = (uint8)(ppmInput.readNumber() * (255.0 / maxColor)); + } else if (ppmType == "P2") { + uint8 pixel = (uint8)(ppmInput.readNumber() * (255.0 / maxColor)); + curPixel.r = pixel; + curPixel.g = pixel; + curPixel.b = pixel; + } else if (ppmType == "P1") { + int pixel = (uint8)(ppmInput.readNumber() * maxColor); + curPixel.r = pixel; + curPixel.g = pixel; + curPixel.b = pixel; + } + } +} + +/** Consumes whitespace up to and including a number, but not the following character */ +static int scanUInt(BinaryInput& input) { + char c = input.readUInt8(); + while (isWhiteSpace(c)) { + c = input.readUInt8(); + } + + std::string s; + s += c; + c = input.readUInt8(); + while (!isWhiteSpace(c)) { + s += c; + c = input.readUInt8(); + } + + // Back up one to avoid consuming the last character + input.setPosition(input.getPosition() - 1); + + int x; + sscanf(s.c_str(), "%d", &x); + return x; +} + +void GImage::decodePPM( + BinaryInput& input) { + + char head[2]; + int w, h; + + input.readBytes(head, 2); + if (head[0] != 'P' || head[1] != '6') { + throw GImage::Error("Invalid PPM Header.", input.getFilename()); + } + + w = scanUInt(input); + h = scanUInt(input); + + // Skip the max color specifier + scanUInt(input); + + if ((w < 0) || + (h < 0) || + (w > 100000) || + (h > 100000)) { + throw GImage::Error("Invalid PPM size in header.", input.getFilename()); + } + + // Trailing whitespace + input.readUInt8(); + + resize(w, h, 3); + + input.readBytes(_byte, width * height * 3); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/GImage_tga.cpp b/externals/g3dlite/G3D.lib/source/GImage_tga.cpp new file mode 100644 index 00000000000..d84feedecc9 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GImage_tga.cpp @@ -0,0 +1,179 @@ +/** + @file GImage_tga.cpp + @author Morgan McGuire, morgan@graphics3d.com + @created 2002-05-27 + @edited 2006-05-10 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Log.h" + +namespace G3D { + +void GImage::encodeTGA( + BinaryOutput& out) const { + + out.setEndian(G3D_LITTLE_ENDIAN); + + // ID length + out.writeUInt8(0); + + // Color map Type + out.writeUInt8(0); + + // Type + out.writeUInt8(2); + + // Color map + out.skip(5); + + // x, y offsets + out.writeUInt16(0); + out.writeUInt16(0); + + // Width & height + out.writeUInt16(width); + out.writeUInt16(height); + + // Color depth + out.writeUInt8(8 * channels); + + // Image descriptor + if (channels == 3) { + // 0 alpha bits + out.writeUInt8(0); + } + else { + // 8 alpha bits + out.writeUInt8(8); + } + + // Image ID (zero length) + + if (channels == 3) { + // Pixels are upside down in BGR format. + for (int y = height - 1; y >= 0; y--) { + for (int x = 0; x < width; x++) { + uint8* p = &(_byte[3 * (y * width + x)]); + out.writeUInt8(p[2]); + out.writeUInt8(p[1]); + out.writeUInt8(p[0]); + } + } + } else { + // Pixels are upside down in BGRA format. + for (int y = height - 1; y >= 0; y--) { + for (int x = 0; x < width; x++) { + uint8* p = &(_byte[4 * (y * width + x)]); + out.writeUInt8(p[2]); + out.writeUInt8(p[1]); + out.writeUInt8(p[0]); + out.writeUInt8(p[3]); + } + } + } + + // Write "TRUEVISION-XFILE " 18 bytes from the end + // (with null termination) + out.writeString("TRUEVISION-XFILE "); +} + + +void GImage::decodeTGA( + BinaryInput& input) { + + // This is a simple TGA loader that can handle uncompressed + // truecolor TGA files (TGA type 2). + // Verify this is a TGA file by looking for the TRUEVISION tag. + int pos = input.getPosition(); + input.setPosition(input.size() - 18); + std::string tag = input.readString(16); + if (tag != "TRUEVISION-XFILE") { + throw Error("Not a TGA file", input.getFilename()); + } + + input.setPosition(pos); + + int IDLength = input.readUInt8(); + int colorMapType = input.readUInt8(); + int imageType = input.readUInt8(); + + (void)colorMapType; + + // 2 is the type supported by this routine. + if (imageType != 2) { + throw Error("TGA images must be type 2 (Uncompressed truecolor)", input.getFilename()); + } + + // Color map specification + input.skip(5); + + // Image specification + + // Skip x and y offsets + input.skip(4); + + width = input.readInt16(); + height = input.readInt16(); + + int colorDepth = input.readUInt8(); + + if ((colorDepth != 24) && (colorDepth != 32)) { + throw Error("TGA files must be 24 or 32 bit.", input.getFilename()); + } + + if (colorDepth == 32) { + channels = 4; + } else { + channels = 3; + } + + // Image descriptor contains overlay data as well + // as data indicating where the origin is + int imageDescriptor = input.readUInt8(); + (void)imageDescriptor; + + // Image ID + input.skip(IDLength); + + _byte = (uint8*)System::malloc(width * height * channels); + debugAssert(_byte); + + // Pixel data + int x; + int y; + + if (channels == 3) { + for (y = height - 1; y >= 0; y--) { + for (x = 0; x < width; x++) { + int b = input.readUInt8(); + int g = input.readUInt8(); + int r = input.readUInt8(); + + int i = (x + y * width) * 3; + _byte[i + 0] = r; + _byte[i + 1] = g; + _byte[i + 2] = b; + } + } + } else { + for (y = height - 1; y >= 0; y--) { + for (x = 0; x < width; x++) { + int b = input.readUInt8(); + int g = input.readUInt8(); + int r = input.readUInt8(); + int a = input.readUInt8(); + + int i = (x + y * width) * 4; + _byte[i + 0] = r; + _byte[i + 1] = g; + _byte[i + 2] = b; + _byte[i + 3] = a; + } + } + } +} + +} diff --git a/externals/g3dlite/G3D.lib/source/GLight.cpp b/externals/g3dlite/G3D.lib/source/GLight.cpp new file mode 100644 index 00000000000..8acb066ef54 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GLight.cpp @@ -0,0 +1,143 @@ +/** + @file GLight.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-11-12 + @edited 2007-10-22 +*/ + +#include "G3D/GLight.h" +#include "G3D/Sphere.h" + +namespace G3D { + +GLight::GLight() { + position = Vector4(0, 0, 0, 0); + color = Color3::white(); + spotDirection = Vector3(0, 0, -1); + spotCutoff = 180; + enabled = false; + attenuation[0] = 1.0; + attenuation[1] = 0.0; + attenuation[2] = 0.0; + specular = true; + diffuse = true; +} + + +GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d) { + GLight L; + L.position = Vector4(toLight.direction(), 0); + L.color = color; + L.specular = s; + L.diffuse = d; + return L; +} + + +GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) { + GLight L; + L.position = Vector4(pos, 1); + L.color = color; + L.attenuation[0] = constAtt; + L.attenuation[1] = linAtt; + L.attenuation[2] = quadAtt; + L.specular = s; + L.diffuse = d; + return L; +} + + +GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) { + GLight L; + L.position = Vector4(pos, 1.0f); + L.spotDirection = pointDirection.direction(); + debugAssert(cutOffAngleDegrees <= 90); + L.spotCutoff = cutOffAngleDegrees; + L.color = color; + L.attenuation[0] = constAtt; + L.attenuation[1] = linAtt; + L.attenuation[2] = quadAtt; + L.specular = s; + L.diffuse = d; + return L; +} + + +bool GLight::operator==(const GLight& other) const { + return (position == other.position) && + (spotDirection == other.spotDirection) && + (spotCutoff == other.spotCutoff) && + (attenuation[0] == other.attenuation[0]) && + (attenuation[1] == other.attenuation[1]) && + (attenuation[2] == other.attenuation[2]) && + (color == other.color) && + (enabled == other.enabled) && + (specular == other.specular) && + (diffuse == other.diffuse); +} + +bool GLight::operator!=(const GLight& other) const { + return !(*this == other); +} + + +Sphere GLight::effectSphere(float cutoff) const { + if (position.w == 0) { + // Directional light + return Sphere(Vector3::zero(), (float)inf()); + } else { + // Avoid divide by zero + cutoff = max(cutoff, 0.0001f); + float maxIntensity = max(color.r, max(color.g, color.b)); + + float radius = (float)inf(); + + if (attenuation[2] != 0) { + + // Solve I / attenuation.dot(1, r, r^2) < cutoff for r + // + // a[0] + a[1] r + a[2] r^2 > I/cutoff + // + + float a = attenuation[2]; + float b = attenuation[1]; + float c = attenuation[0] - maxIntensity / cutoff; + + float discrim = square(b) - 4 * a * c; + + if (discrim >= 0) { + discrim = sqrt(discrim); + + float r1 = (-b + discrim) / (2 * a); + float r2 = (-b - discrim) / (2 * a); + + if (r1 < 0) { + if (r2 > 0) { + radius = r2; + } + } else if (r2 > 0) { + radius = min(r1, r2); + } else { + radius = r1; + } + } + + } else if (attenuation[1] != 0) { + + // Solve I / attenuation.dot(1, r) < cutoff for r + // + // r * a[1] + a[0] = I / cutoff + // r = (I / cutoff - a[0]) / a[1] + + float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1]; + radius = max(radius, 0.0f); + } + + return Sphere(position.xyz(), radius); + + } +} + +} diff --git a/externals/g3dlite/G3D.lib/source/GThread.cpp b/externals/g3dlite/G3D.lib/source/GThread.cpp new file mode 100644 index 00000000000..090370436c4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GThread.cpp @@ -0,0 +1,203 @@ +/** + @file GThread.cpp + + GThread class. + + @created 2005-09-24 + @edited 2005-10-22 + */ + +#include "G3D/GThread.h" +#include "G3D/System.h" +#include "G3D/debugAssert.h" + + +namespace G3D { + +namespace _internal { + +class BasicThread: public GThread { +public: + BasicThread(const std::string& name, void (*proc)(void*), void* param): + GThread(name), m_wrapperProc(proc), m_param(param) { } +protected: + virtual void threadMain() { + m_wrapperProc(m_param); + } + +private: + void (*m_wrapperProc)(void*); + + void* m_param; +}; + +} // namespace _internal + + +GThread::GThread(const std::string& name): + m_status(STATUS_CREATED), + m_name(name) { + +#ifdef G3D_WIN32 + m_event = NULL; +#endif + + // system-independent clear of handle + System::memset(&m_handle, 0, sizeof(m_handle)); +} + +GThread::~GThread() { +#ifdef _MSC_VER +# pragma warning( push ) +# pragma warning( disable : 4127 ) +#endif + alwaysAssertM(m_status != STATUS_RUNNING, "Deleting thread while running."); +#ifdef _MSC_VER +# pragma warning( pop ) +#endif + +#ifdef G3D_WIN32 + if (m_event) { + ::CloseHandle(m_event); + } +#endif +} + +GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) { + return new _internal::BasicThread(name, proc, param); +} + + +bool GThread::started() const { + return m_status != STATUS_CREATED; +} + +bool GThread::start() { + + debugAssertM(! started(), "Thread has already executed."); + if (started()) { + return false; + } + + m_status = STATUS_STARTED; + +# ifdef G3D_WIN32 + DWORD threadId; + + m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL); + debugAssert(m_event); + + m_handle = ::CreateThread(NULL, 0, &internalThreadProc, this, 0, &threadId); + + if (m_handle == NULL) { + ::CloseHandle(m_event); + m_event = NULL; + } + + return (m_handle != NULL); +# else + if (!pthread_create(&m_handle, NULL, &internalThreadProc, this)) { + return true; + } else { + // system-independent clear of handle + System::memset(&m_handle, 0, sizeof(m_handle)); + + return false; + } +# endif +} + +void GThread::terminate() { + if (m_handle) { +# ifdef G3D_WIN32 + ::TerminateThread(m_handle, 0); +# else + pthread_kill(m_handle, SIGSTOP); +# endif + // system-independent clear of handle + System::memset(&m_handle, 0, sizeof(m_handle)); + } +} + +bool GThread::running() const{ + return (m_status == STATUS_RUNNING); +} + +bool GThread::completed() const { + return (m_status == STATUS_COMPLETED); +} + +void GThread::waitForCompletion() { +# ifdef G3D_WIN32 + debugAssert(m_event); + ::WaitForSingleObject(m_event, INFINITE); +# else + debugAssert(m_handle); + pthread_join(m_handle, NULL); +# endif +} + +#ifdef G3D_WIN32 +DWORD WINAPI GThread::internalThreadProc(LPVOID param) { + GThread* current = reinterpret_cast<GThread*>(param); + debugAssert(current->m_event); + current->m_status = STATUS_RUNNING; + current->threadMain(); + current->m_status = STATUS_COMPLETED; + ::SetEvent(current->m_event); + return 0; +} +#else +void* GThread::internalThreadProc(void* param) { + GThread* current = reinterpret_cast<GThread*>(param); + current->m_status = STATUS_RUNNING; + current->threadMain(); + current->m_status = STATUS_COMPLETED; + return (void*)NULL; +} +#endif + + + +GMutex::GMutex() { +# ifdef G3D_WIN32 + ::InitializeCriticalSection(&m_handle); +# else + pthread_mutex_init(&m_handle, NULL); +# endif +} + +GMutex::~GMutex() { + //TODO: Debug check for locked +# ifdef G3D_WIN32 + ::DeleteCriticalSection(&m_handle); +# else + pthread_mutex_destroy(&m_handle); +# endif +} + +//bool GMutex::tryLock() { +//# ifdef G3D_WIN32 +// return ::TryEnterCriticalSection(&m_handle); +//# else +// return pthread_mutex_trylock(&m_handle); +//# endif +//} + +void GMutex::lock() { +# ifdef G3D_WIN32 + ::EnterCriticalSection(&m_handle); +# else + pthread_mutex_lock(&m_handle); +# endif +} + +void GMutex::unlock() { +# ifdef G3D_WIN32 + ::LeaveCriticalSection(&m_handle); +# else + pthread_mutex_unlock(&m_handle); +# endif +} + +} // namespace G3D diff --git a/externals/g3dlite/G3D.lib/source/GUniqueID.cpp b/externals/g3dlite/G3D.lib/source/GUniqueID.cpp new file mode 100644 index 00000000000..25c757b70e4 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/GUniqueID.cpp @@ -0,0 +1,78 @@ +/** + @file GUniqueID.cpp + @author Morgan McGuire, morgan@cs.williams.edu + */ +#include "G3D/GUniqueID.h" +#include "G3D/BinaryInput.h" +#include "G3D/TextInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/TextOutput.h" +#include "G3D/NetworkDevice.h" + +namespace G3D { + +void GUniqueID::serialize(BinaryOutput& b) const { + b.writeUInt64(id); +} + + +void GUniqueID::deserialize(BinaryInput& b) { + id = b.readUInt64(); +} + +void GUniqueID::serialize(TextOutput& t) const { + t.writeSymbol("("); + t.writeNumber((double)(id >> 32)); + t.writeNumber((double)(id & 0xFFFFFFFF)); + t.writeSymbol(")"); +} + +void GUniqueID::deserialize(TextInput& t) { + t.readSymbol("("); + id = (((uint64)t.readNumber()) << 32) + (uint64)t.readNumber(); + t.readSymbol(")"); +} + + +GUniqueID GUniqueID::create(uint16 tag) { + static uint64 counter = 0; + static uint64 systemID = 0; + + if (systemID == 0) { + // Create a unique ID for this machine/program instance + + // TODO: see ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr) + Array<NetAddress> addr; + NetworkDevice::instance()->localHostAddresses(addr); + if (addr.size() > 0) { + systemID |= addr[0].ip(); + } + + union { + float64 ft; + uint64 ut; + }; + ft = System::time(); + systemID = ut << 22; + systemID ^= ((uint64)iRandom(0, 32768)) << 8; + + systemID &= ~((uint64)1023 << 54); + + // Ensure that the systemID is non-zero (vanishingly small probability) + if (systemID == 0) { + systemID = 1; + } + } + + // No need for modulo; we'll all be dead before this counter + // overflows 54 bits + ++counter; + + GUniqueID i; + + i.id = (((uint64)(tag & 1023)) << 54) | (counter ^ systemID); + + return i; +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Image1.cpp b/externals/g3dlite/G3D.lib/source/Image1.cpp new file mode 100644 index 00000000000..30841d01c6b --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Image1.cpp @@ -0,0 +1,224 @@ +/** + @file Image1.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + + +#include "G3D/Image1.h" +#include "G3D/Image1uint8.h" +#include "G3D/GImage.h" +#include "G3D/Color4.h" +#include "G3D/Color4uint8.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +Image1::Image1(int w, int h, WrapMode wrap) : Map2D<Color1, Color1>(w, h, wrap) { + setAll(Color1(0.0f)); +} + + +Image1::Ref Image1::fromGImage(const GImage& im, WrapMode wrap) { + switch (im.channels) { + case 1: + return fromArray(im.pixel1(), im.width, im.height, wrap); + + case 3: + return fromArray(im.pixel3(), im.width, im.height, wrap); + + case 4: + return fromArray(im.pixel4(), im.width, im.height, wrap); + + default: + debugAssertM(false, "Input GImage must have 1, 3, or 4 channels."); + return NULL; + } +} + + +Image1::Ref Image1::fromImage1uint8(const ReferenceCountedPointer<Image1uint8>& im) { + Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode())); + out->resize(im->width(), im->height()); + + int N = im->width() * im->height(); + const Color1uint8* src = reinterpret_cast<Color1uint8*>(im->getCArray()); + for (int i = 0; i < N; ++i) { + out->data[i] = Color1(src[i]); + } + + return out; +} + + +Image1::Ref Image1::createEmpty(int width, int height, WrapMode wrap) { + return new Type(width, height, wrap); +} + + +Image1::Ref Image1::createEmpty(WrapMode wrap) { + return createEmpty(0, 0, wrap); +} + + +Image1::Ref Image1::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) { + Ref out = createEmpty(wrap); + out->load(filename); + return out; +} + + +void Image1::load(const std::string& filename, GImage::Format fmt) { + copyGImage(GImage(filename, fmt)); + setChanged(true); +} + + +Image1::Ref Image1::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1::Ref Image1::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1::Ref Image1::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1::Ref Image1::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1::Ref Image1::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1::Ref Image1::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + +void Image1::copyGImage(const GImage& im) { + switch (im.channels) { + case 1: + copyArray(im.pixel1(), im.width, im.height); + break; + + case 3: + copyArray(im.pixel3(), im.width, im.height); + break; + + case 4: + copyArray(im.pixel4(), im.width, im.height); + break; + } +} + + +void Image1::copyArray(const Color3uint8* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color1* dst = data.getCArray(); + // Convert int8 -> float + for (int i = 0; i < N; ++i) { + dst[i] = Color1(Color3(src[i]).average()); + } +} + + +void Image1::copyArray(const Color4uint8* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color1* dst = data.getCArray(); + + // Strip alpha and convert + for (int i = 0; i < N; ++i) { + dst[i] = Color1(Color3(src[i].rgb()).average()); + } +} + + +void Image1::copyArray(const Color1* src, int w, int h) { + resize(w, h); + System::memcpy(getCArray(), src, w * h * sizeof(Color1)); +} + + +void Image1::copyArray(const Color4* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color1* dst = data.getCArray(); + + // Strip alpha + for (int i = 0; i < N; ++i) { + dst[i] = Color1(src[i].rgb().average()); + } +} + + +void Image1::copyArray(const Color1uint8* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color1* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i]= Color1(src[i]); + } +} + + +void Image1::copyArray(const Color3* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color1* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color1(src[i].average()); + } +} + + +/** Saves in any of the formats supported by G3D::GImage. */ +void Image1::save(const std::string& filename, GImage::Format fmt) { + GImage im(width(), height(), 1); + + int N = im.width * im.height; + Color1uint8* dst = im.pixel1(); + for (int i = 0; i < N; ++i) { + dst[i] = Color1uint8(data[i]); + } + + im.save(filename, fmt); +} + + +const ImageFormat* Image1::format() const { + return ImageFormat::L32F(); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Image1uint8.cpp b/externals/g3dlite/G3D.lib/source/Image1uint8.cpp new file mode 100644 index 00000000000..c43e7194d7d --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Image1uint8.cpp @@ -0,0 +1,212 @@ +/** + @file Image1uint8.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2008-01-13 +*/ + +#include "G3D/Image1uint8.h" +#include "G3D/Image3uint8.h" +#include "G3D/Image1.h" +#include "G3D/GImage.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color4.h" +#include "G3D/Color4uint8.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +Image1uint8::Image1uint8(int w, int h, WrapMode wrap) : Map2D<Color1uint8, Color1>(w, h, wrap) { + setAll(Color1uint8(0)); +} + + +Image1uint8::Ref Image1uint8::fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im) { + return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode()); +} + + +Image1uint8::Ref Image1uint8::fromGImage(const GImage& im, WrapMode wrap) { + switch (im.channels) { + case 1: + return fromArray(im.pixel1(), im.width, im.height, wrap); + + case 3: + return fromArray(im.pixel3(), im.width, im.height, wrap); + + case 4: + return fromArray(im.pixel4(), im.width, im.height, wrap); + + default: + debugAssertM(false, "Input GImage must have 1, 3, or 4 channels."); + return NULL; + } +} + + +Image1uint8::Ref Image1uint8::fromImage1(const ReferenceCountedPointer<Image1>& im) { + Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode())); + out->copyArray(im->getCArray(), im->width(), im->height()); + + return out; +} + + +Image1uint8::Ref Image1uint8::createEmpty(int width, int height, WrapMode wrap) { + return new Type(width, height, wrap); +} + + +Image1uint8::Ref Image1uint8::createEmpty(WrapMode wrap) { + return createEmpty(0, 0, wrap); +} + + +Image1uint8::Ref Image1uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) { + Ref out = createEmpty(wrap); + out->load(filename); + return out; +} + + +Image1uint8::Ref Image1uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1uint8::Ref Image1uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1uint8::Ref Image1uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1uint8::Ref Image1uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1uint8::Ref Image1uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image1uint8::Ref Image1uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +void Image1uint8::load(const std::string& filename, GImage::Format fmt) { + copyGImage(GImage(filename, fmt)); + setChanged(true); +} + + +void Image1uint8::copyGImage(const GImage& im) { + switch (im.channels) { + case 1: + copyArray(im.pixel1(), im.width, im.height); + break; + + case 3: + copyArray(im.pixel3(), im.width, im.height); + break; + + case 4: + copyArray(im.pixel4(), im.width, im.height); + break; + } +} + + +void Image1uint8::copyArray(const Color3uint8* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color1uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].value = (src[i].r + src[i].g + src[i].b) / 3; + } +} + +void Image1uint8::copyArray(const Color3* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color1uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color1uint8(Color1(src[i].average())); + } +} + + +void Image1uint8::copyArray(const Color1uint8* ptr, int w, int h) { + resize(w, h); + System::memcpy(getCArray(), ptr, w * h); +} + + +void Image1uint8::copyArray(const Color1* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color1uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color1uint8(src[i]); + } +} + + +void Image1uint8::copyArray(const Color4uint8* ptr, int w, int h) { + resize(w, h); + int N = w * h; + + Color1uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].value = (ptr[i].r + ptr[i].g + ptr[i].b) / 3; + } +} + + +void Image1uint8::copyArray(const Color4* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color1uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color1uint8(Color1(src[i].rgb().average())); + } +} + + +/** Saves in any of the formats supported by G3D::GImage. */ +void Image1uint8::save(const std::string& filename, GImage::Format fmt) { + GImage im(width(), height(), 1); + System::memcpy(im.byte(), getCArray(), width() * height()); + im.save(filename, fmt); +} + + +const ImageFormat* Image1uint8::format() const { + return ImageFormat::L8(); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Image3.cpp b/externals/g3dlite/G3D.lib/source/Image3.cpp new file mode 100644 index 00000000000..aa2ac6098dc --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Image3.cpp @@ -0,0 +1,224 @@ +/** + @file Image3.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2007-01-31 +*/ + + +#include "G3D/Image3.h" +#include "G3D/Image3uint8.h" +#include "G3D/GImage.h" +#include "G3D/Color4.h" +#include "G3D/Color4uint8.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +Image3::Image3(int w, int h, WrapMode wrap) : Map2D<Color3, Color3>(w, h, wrap) { + setAll(Color3::black()); +} + + +Image3::Ref Image3::fromGImage(const GImage& im, WrapMode wrap) { + switch (im.channels) { + case 1: + return fromArray(im.pixel1(), im.width, im.height, wrap); + + case 3: + return fromArray(im.pixel3(), im.width, im.height, wrap); + + case 4: + return fromArray(im.pixel4(), im.width, im.height, wrap); + + default: + debugAssertM(false, "Input GImage must have 1, 3, or 4 channels."); + return NULL; + } +} + + +Image3::Ref Image3::fromImage3uint8(const ReferenceCountedPointer<Image3uint8>& im) { + Ref out = createEmpty(im->wrapMode()); + out->resize(im->width(), im->height()); + + int N = im->width() * im->height(); + const Color3uint8* src = reinterpret_cast<Color3uint8*>(im->getCArray()); + for (int i = 0; i < N; ++i) { + out->data[i] = Color3(src[i]); + } + + return out; +} + + +Image3::Ref Image3::createEmpty(int width, int height, WrapMode wrap) { + return new Image3(width, height, wrap); +} + + +Image3::Ref Image3::createEmpty(WrapMode wrap) { + return createEmpty(0, 0, wrap); +} + + +Image3::Ref Image3::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) { + Ref out = createEmpty(wrap); + out->load(filename); + return out; +} + + +void Image3::load(const std::string& filename, GImage::Format fmt) { + copyGImage(GImage(filename, fmt)); + setChanged(true); +} + + +Image3::Ref Image3::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3::Ref Image3::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3::Ref Image3::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3::Ref Image3::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3::Ref Image3::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3::Ref Image3::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + +void Image3::copyGImage(const GImage& im) { + switch (im.channels) { + case 1: + copyArray(im.pixel1(), im.width, im.height); + break; + + case 3: + copyArray(im.pixel3(), im.width, im.height); + break; + + case 4: + copyArray(im.pixel4(), im.width, im.height); + break; + } +} + + +void Image3::copyArray(const Color3uint8* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color3* dst = data.getCArray(); + // Convert int8 -> float + for (int i = 0; i < N; ++i) { + dst[i] = Color3(src[i]); + } +} + + +void Image3::copyArray(const Color4uint8* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color3* dst = data.getCArray(); + + // Strip alpha and convert + for (int i = 0; i < N; ++i) { + dst[i] = Color3(src[i].rgb()); + } +} + + +void Image3::copyArray(const Color3* src, int w, int h) { + resize(w, h); + System::memcpy(getCArray(), src, w * h * sizeof(Color3)); +} + + +void Image3::copyArray(const Color4* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color3* dst = data.getCArray(); + + // Strip alpha + for (int i = 0; i < N; ++i) { + dst[i] = src[i].rgb(); + } +} + + +void Image3::copyArray(const Color1uint8* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color3* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value; + } +} + + +void Image3::copyArray(const Color1* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color3* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = src[i].value; + } +} + + +/** Saves in any of the formats supported by G3D::GImage. */ +void Image3::save(const std::string& filename, GImage::Format fmt) { + GImage im(width(), height(), 3); + + int N = im.width * im.height; + Color3uint8* dst = im.pixel3(); + for (int i = 0; i < N; ++i) { + dst[i] = Color3uint8(data[i]); + } + + im.save(filename, fmt); +} + + +const ImageFormat* Image3::format() const { + return ImageFormat::RGB32F(); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Image3uint8.cpp b/externals/g3dlite/G3D.lib/source/Image3uint8.cpp new file mode 100644 index 00000000000..2de32b6009e --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Image3uint8.cpp @@ -0,0 +1,225 @@ +/** + @file Image3uint8.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2008-01-08 +*/ + +#include "G3D/Image1uint8.h" +#include "G3D/Image3uint8.h" +#include "G3D/Image3.h" +#include "G3D/GImage.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color4.h" +#include "G3D/Color4uint8.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +Image3uint8::Ref Image3uint8::fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im) { + return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode()); +} + + +Image3uint8::Image3uint8(int w, int h, WrapMode wrap) : Map2D<Color3uint8>(w, h, wrap) { + setAll(Color3::black()); +} + + +Image3uint8::Ref Image3uint8::fromGImage(const GImage& im, WrapMode wrap) { + switch (im.channels) { + case 1: + return fromArray(im.pixel1(), im.width, im.height, wrap); + + case 3: + return fromArray(im.pixel3(), im.width, im.height, wrap); + + case 4: + return fromArray(im.pixel4(), im.width, im.height, wrap); + + default: + debugAssertM(false, "Input GImage must have 1, 3, or 4 channels."); + return NULL; + } +} + + +Image3uint8::Ref Image3uint8::fromImage3(const ReferenceCountedPointer<Image3>& im) { + Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode())); + out->copyArray(im->getCArray(), im->width(), im->height()); + + return out; +} + + +Image3uint8::Ref Image3uint8::createEmpty(int width, int height, WrapMode wrap) { + return new Type(width, height, wrap); +} + + +Image3uint8::Ref Image3uint8::createEmpty(WrapMode wrap) { + return createEmpty(0, 0, wrap); +} + + +Image3uint8::Ref Image3uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) { + Ref out = createEmpty(wrap); + out->load(filename); + return out; +} + + +Image3uint8::Ref Image3uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3uint8::Ref Image3uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3uint8::Ref Image3uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3uint8::Ref Image3uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3uint8::Ref Image3uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image3uint8::Ref Image3uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +void Image3uint8::load(const std::string& filename, GImage::Format fmt) { + copyGImage(GImage(filename, fmt)); + setChanged(true); +} + + +void Image3uint8::copyGImage(const GImage& im) { + switch (im.channels) { + case 1: + copyArray(im.pixel1(), im.width, im.height); + break; + + case 3: + copyArray(im.pixel3(), im.width, im.height); + break; + + case 4: + copyArray(im.pixel4(), im.width, im.height); + break; + } +} + + +void Image3uint8::copyArray(const Color1uint8* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color3uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = src[i].value; + } +} + +void Image3uint8::copyArray(const Color1* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color3uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value; + } +} + + +void Image3uint8::copyArray(const Color3uint8* ptr, int w, int h) { + resize(w, h); + System::memcpy(getCArray(), ptr, w * h * 3); +} + + +void Image3uint8::copyArray(const Color3* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color3uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color3uint8(src[i]); + } +} + + +void Image3uint8::copyArray(const Color4uint8* ptr, int w, int h) { + resize(w, h); + + // Copy 3/4 bytes + GImage::RGBAtoRGB((const uint8*)ptr, (uint8*)getCArray(), w * h); +} + + +void Image3uint8::copyArray(const Color4* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color3uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color3uint8(src[i].rgb()); + } +} + + +/** Saves in any of the formats supported by G3D::GImage. */ +void Image3uint8::save(const std::string& filename, GImage::Format fmt) { + GImage im(width(), height(), 3); + System::memcpy(im.byte(), getCArray(), width() * height() * 3); + im.save(filename, fmt); +} + + +ReferenceCountedPointer<class Image1uint8> Image3uint8::getChannel(int c) const { + debugAssert(c >= 0 && c <= 2); + + Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode()); + const Color3uint8* srcArray = getCArray(); + Color1uint8* dstArray = dst->getCArray(); + + const int N = width() * height(); + for (int i = 0; i < N; ++i) { + dstArray[i] = Color1uint8(srcArray[i][c]); + } + + return dst; +} + + +const ImageFormat* Image3uint8::format() const { + return ImageFormat::RGB8(); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Image4.cpp b/externals/g3dlite/G3D.lib/source/Image4.cpp new file mode 100644 index 00000000000..84a1cd650b6 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Image4.cpp @@ -0,0 +1,226 @@ +/** + @file Image4.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2008-07-27 +*/ + + +#include "G3D/Image4.h" +#include "G3D/Image4uint8.h" +#include "G3D/GImage.h" +#include "G3D/Color3.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +Image4::Image4(int w, int h, WrapMode wrap) : Map2D<Color4, Color4>(w, h, wrap) { + setAll(Color4::zero()); +} + + +Image4::Ref Image4::fromGImage(const GImage& im, WrapMode wrap) { + switch (im.channels) { + case 1: + return fromArray(im.pixel1(), im.width, im.height, wrap); + + case 3: + return fromArray(im.pixel3(), im.width, im.height, wrap); + + case 4: + return fromArray(im.pixel4(), im.width, im.height, wrap); + + default: + debugAssertM(false, "Input GImage must have 1, 3, or 4 channels."); + return NULL; + } +} + + +Image4::Ref Image4::fromImage4uint8(const ReferenceCountedPointer<Image4uint8>& im) { + Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode())); + out->resize(im->width(), im->height()); + + int N = im->width() * im->height(); + const Color4uint8* src = reinterpret_cast<Color4uint8*>(im->getCArray()); + for (int i = 0; i < N; ++i) { + out->data[i] = Color4(src[i]); + } + + return out; +} + + +Image4::Ref Image4::createEmpty(int width, int height, WrapMode wrap) { + return new Type(width, height, WrapMode::ERROR); +} + + +Image4::Ref Image4::createEmpty(WrapMode wrap) { + return createEmpty(0, 0, wrap); +} + + +Image4::Ref Image4::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) { + Ref out = createEmpty(wrap); + out->load(filename); + return out; +} + + +void Image4::load(const std::string& filename, GImage::Format fmt) { + copyGImage(GImage(filename, fmt)); + setChanged(true); +} + + +Image4::Ref Image4::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4::Ref Image4::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4::Ref Image4::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4::Ref Image4::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4::Ref Image4::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4::Ref Image4::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +void Image4::copyGImage(const GImage& im) { + switch (im.channels) { + case 1: + copyArray(im.pixel1(), im.width, im.height); + break; + + case 3: + copyArray(im.pixel3(), im.width, im.height); + break; + + case 4: + copyArray(im.pixel4(), im.width, im.height); + break; + } +} + + +void Image4::copyArray(const Color4uint8* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color4* dst = data.getCArray(); + // Convert int8 -> float + for (int i = 0; i < N; ++i) { + dst[i] = Color4(src[i]); + } +} + + +void Image4::copyArray(const Color3uint8* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color4* dst = data.getCArray(); + + // Add alpha and convert + for (int i = 0; i < N; ++i) { + dst[i] = Color4(Color3(src[i]), 1.0f); + } +} + + +void Image4::copyArray(const Color4* src, int w, int h) { + resize(w, h); + System::memcpy(getCArray(), src, w * h * sizeof(Color4)); +} + + +void Image4::copyArray(const Color3* src, int w, int h) { + resize(w, h); + + int N = w * h; + Color4* dst = data.getCArray(); + + // Add alpha + for (int i = 0; i < N; ++i) { + dst[i] = Color4(src[i], 1.0f); + } +} + + +void Image4::copyArray(const Color1uint8* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color4* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value; + dst[i].a = 1.0f; + } +} + + +void Image4::copyArray(const Color1* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color4* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = src[i].value; + dst[i].a = 1.0f; + } +} + + +/** Saves in any of the formats supported by G3D::GImage. */ +void Image4::save(const std::string& filename, GImage::Format fmt) { + GImage im(width(), height(), 4); + + int N = im.width * im.height; + Color4uint8* dst = im.pixel4(); + for (int i = 0; i < N; ++i) { + dst[i] = Color4uint8(data[i]); + } + + im.save(filename, fmt); +} + +const ImageFormat* Image4::format() const { + return ImageFormat::RGBA32F(); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/Image4uint8.cpp b/externals/g3dlite/G3D.lib/source/Image4uint8.cpp new file mode 100644 index 00000000000..dee1cba14ba --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Image4uint8.cpp @@ -0,0 +1,222 @@ +/** + @file Image4uint8.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-01-31 + @edited 2008-07-31 +*/ + +#include "G3D/Image4uint8.h" +#include "G3D/Image4.h" +#include "G3D/Image3uint8.h" +#include "G3D/Image3.h" +#include "G3D/GImage.h" +#include "G3D/Color1.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color4.h" +#include "G3D/Color4uint8.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +Image4uint8::Image4uint8(int w, int h, WrapMode wrap) : Map2D<Color4uint8, Color4>(w, h, wrap) { + setAll(Color4::zero()); +} + + +Image4uint8::Ref Image4uint8::fromGImage(const GImage& im, WrapMode wrap) { + switch (im.channels) { + case 1: + return fromArray(im.pixel1(), im.width, im.height, wrap); + + case 3: + return fromArray(im.pixel3(), im.width, im.height, wrap); + + case 4: + return fromArray(im.pixel4(), im.width, im.height, wrap); + + default: + debugAssertM(false, "Input GImage must have 1, 3, or 4 channels."); + return NULL; + } +} + + +Image4uint8::Ref Image4uint8::fromImage4(const ReferenceCountedPointer<Image4>& im) { + Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode())); + out->copyArray(im->getCArray(), im->width(), im->height()); + + return out; +} + + +Image4uint8::Ref Image4uint8::createEmpty(int width, int height, WrapMode wrap) { + return new Type(width, height, wrap); +} + + +Image4uint8::Ref Image4uint8::createEmpty(WrapMode wrap) { + return createEmpty(0, 0, wrap); +} + + +Image4uint8::Ref Image4uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) { + Ref out = createEmpty(wrap); + out->load(filename); + return out; +} + + +Image4uint8::Ref Image4uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4uint8::Ref Image4uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4uint8::Ref Image4uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4uint8::Ref Image4uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4uint8::Ref Image4uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +Image4uint8::Ref Image4uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) { + Ref out = createEmpty(wrap); + out->copyArray(ptr, w, h); + return out; +} + + +void Image4uint8::load(const std::string& filename, GImage::Format fmt) { + copyGImage(GImage(filename, fmt)); + setChanged(true); +} + + +void Image4uint8::copyGImage(const GImage& im) { + switch (im.channels) { + case 1: + copyArray(im.pixel1(), im.width, im.height); + break; + + case 3: + copyArray(im.pixel3(), im.width, im.height); + break; + + case 4: + copyArray(im.pixel4(), im.width, im.height); + break; + } +} + + +void Image4uint8::copyArray(const Color1uint8* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color4uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = src[i].value; + dst[i].a = 255; + } +} + +void Image4uint8::copyArray(const Color1* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color4uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value; + dst[i].a = 255; + } +} + + +void Image4uint8::copyArray(const Color4uint8* ptr, int w, int h) { + resize(w, h); + System::memcpy(getCArray(), ptr, w * h * 4); +} + + +void Image4uint8::copyArray(const Color4* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color4uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color4uint8(src[i]); + } +} + + +void Image4uint8::copyArray(const Color3uint8* ptr, int w, int h) { + resize(w, h); + + GImage::RGBtoRGBA((const uint8*)ptr, (uint8*)getCArray(), w * h); +} + + +void Image4uint8::copyArray(const Color3* src, int w, int h) { + resize(w, h); + int N = w * h; + + Color4uint8* dst = getCArray(); + for (int i = 0; i < N; ++i) { + dst[i] = Color4uint8(Color4(src[i], 1.0f)); + } +} + + +/** Saves in any of the formats supported by G3D::GImage. */ +void Image4uint8::save(const std::string& filename, GImage::Format fmt) { + GImage im(width(), height(), 4); + System::memcpy(im.byte(), getCArray(), width() * height() * 4); + im.save(filename, fmt); +} + + +ReferenceCountedPointer<class Image1uint8> Image4uint8::getChannel(int c) const { + debugAssert(c >= 0 && c <= 3); + + Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode()); + const Color4uint8* srcArray = getCArray(); + Color1uint8* dstArray = dst->getCArray(); + + const int N = width() * height(); + for (int i = 0; i < N; ++i) { + dstArray[i] = Color1uint8(srcArray[i][c]); + } + + return dst; +} + + +const ImageFormat* Image4uint8::format() const { + return ImageFormat::RGBA8(); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/ImageFormat.cpp b/externals/g3dlite/G3D.lib/source/ImageFormat.cpp new file mode 100644 index 00000000000..3cbf6ad711e --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/ImageFormat.cpp @@ -0,0 +1,440 @@ +/** + @file ImageFormat.cpp + + @maintainer Morgan McGuire, morgan@graphics3d.com + + @created 2003-05-23 + @edited 2006-01-11 + */ + +#include "../../GLG3D.lib/include/GLG3D/glheaders.h" +#include "../../GLG3D.lib/include/GLG3D/glcalls.h" +#include "G3D/ImageFormat.h" + +namespace G3D { + +ImageFormat::ImageFormat( + int _numComponents, + bool _compressed, + int _glFormat, + int _glBaseFormat, + int _luminanceBits, + int _alphaBits, + int _redBits, + int _greenBits, + int _blueBits, + int _depthBits, + int _stencilBits, + int _hardwareBitsPerTexel, + int _packedBitsPerTexel, + int glDataFormat, + bool _opaque, + bool _floatingPoint, + Code _code, + ColorSpace _colorSpace, + BayerPattern _bayerPattern) : + + numComponents(_numComponents), + compressed(_compressed), + code(_code), + colorSpace(_colorSpace), + bayerPattern(_bayerPattern), + openGLFormat(_glFormat), + openGLBaseFormat(_glBaseFormat), + luminanceBits(_luminanceBits), + alphaBits(_alphaBits), + redBits(_redBits), + greenBits(_greenBits), + blueBits(_blueBits), + stencilBits(_stencilBits), + depthBits(_depthBits), + cpuBitsPerPixel(_packedBitsPerTexel), + packedBitsPerTexel(_packedBitsPerTexel), + openGLBitsPerPixel(_hardwareBitsPerTexel), + hardwareBitsPerTexel(_hardwareBitsPerTexel), + openGLDataFormat(glDataFormat), + opaque(_opaque), + floatingPoint(_floatingPoint) { + + debugAssert(_packedBitsPerTexel <= _hardwareBitsPerTexel); +} + +const ImageFormat* ImageFormat::depth(int depthBits) { + + switch (depthBits) { + case 16: + return DEPTH16(); + + case 24: + return DEPTH24(); + + case 32: + return DEPTH32(); + + default: + debugAssertM(false, "Depth must be 16, 24, or 32."); + return DEPTH32(); + } +} + + +const ImageFormat* ImageFormat::stencil(int bits) { + switch (bits) { + case 1: + return STENCIL1(); + + case 4: + return STENCIL4(); + + case 8: + return STENCIL8(); + + case 16: + return STENCIL16(); + + default: + debugAssertM(false, "Stencil must be 1, 4, 8 or 16."); + return STENCIL16(); + } +} + + +std::string ImageFormat::name() const { + + static const std::string nameArray[] = + { + "L8", + "L16", + "L16F", + "L32F", + + "A8", + "A16", + "A16F", + "A32F", + + "LA4", + "LA8", + "LA16", + "LA16F", + "LA32F", + + "RGB5", + "RGB5A1", + "RGB8", + "RGB10", + "RGB10A2", + "RGB16", + "RGB16F", + "RGB32F", + + "ARGB8", + "BGR8", + + "RGBA8", + "RGBA16", + "RGBA16F", + "RGBA32F", + + "BAYER_RGGB8", + "BAYER_GRBG8", + "BAYER_GBRG8", + "BAYER_BGGR8", + "BAYER_RGGB32F", + "BAYER_GRBG32F", + "BAYER_GBRG32F", + "BAYER_BGGR32F", + + "HSV8", + "HSV32F", + + "YUV420_PLANAR", + "YUV422", + "YUV444", + + "RGB_DXT1", + "RGBA_DXT1", + "RGBA_DXT3", + "RGBA_DXT5", + + "DEPTH16", + "DEPTH24", + "DEPTH32", + "DEPTH32F", + + "STENCIL1", + "STENCIL4", + "STENCIL8", + "STENCIL16", + + "DEPTH24_STENCIL8" + }; + + debugAssert(code < CODE_NUM); + return nameArray[code]; +} + + +const ImageFormat* ImageFormat::fromCode(ImageFormat::Code code) { + switch (code) { + case ImageFormat::CODE_L8: + return ImageFormat::L8(); + break; + case ImageFormat::CODE_L16: + return ImageFormat::L16(); + break; + case ImageFormat::CODE_L16F: + return ImageFormat::L16F(); + break; + case ImageFormat::CODE_L32F: + return ImageFormat::L32F(); + break; + + case ImageFormat::CODE_A8: + return ImageFormat::A8(); + break; + case ImageFormat::CODE_A16: + return ImageFormat::A16(); + break; + case ImageFormat::CODE_A16F: + return ImageFormat::A16F(); + break; + case ImageFormat::CODE_A32F: + return ImageFormat::A32F(); + break; + + case ImageFormat::CODE_LA4: + return ImageFormat::LA4(); + break; + case ImageFormat::CODE_LA8: + return ImageFormat::LA8(); + break; + case ImageFormat::CODE_LA16: + return ImageFormat::LA16(); + break; + case ImageFormat::CODE_LA16F: + return ImageFormat::LA16F(); + break; + case ImageFormat::CODE_LA32F: + return ImageFormat::LA32F(); + break; + + case ImageFormat::CODE_RGB5: + return ImageFormat::RGB5(); + break; + case ImageFormat::CODE_RGB5A1: + return ImageFormat::RGB5A1(); + break; + case ImageFormat::CODE_RGB8: + return ImageFormat::RGB8(); + break; + case ImageFormat::CODE_RGB10: + return ImageFormat::RGB10(); + break; + case ImageFormat::CODE_RGB10A2: + return ImageFormat::RGB10A2(); + break; + case ImageFormat::CODE_RGB16: + return ImageFormat::RGB16(); + break; + case ImageFormat::CODE_RGB16F: + return ImageFormat::RGB16F(); + break; + case ImageFormat::CODE_RGB32F: + return ImageFormat::RGB32F(); + break; + + case ImageFormat::CODE_ARGB8: + return NULL; + + case ImageFormat::CODE_BGR8: + return NULL; + + case ImageFormat::CODE_RGBA8: + return ImageFormat::RGBA8(); + break; + case ImageFormat::CODE_RGBA16: + return ImageFormat::RGBA16(); + break; + case ImageFormat::CODE_RGBA16F: + return ImageFormat::RGBA16F(); + break; + case ImageFormat::CODE_RGBA32F: + return ImageFormat::RGBA32F(); + break; + + case ImageFormat::CODE_BAYER_RGGB8: + case ImageFormat::CODE_BAYER_GRBG8: + case ImageFormat::CODE_BAYER_GBRG8: + case ImageFormat::CODE_BAYER_BGGR8: + case ImageFormat::CODE_BAYER_RGGB32F: + case ImageFormat::CODE_BAYER_GRBG32F: + case ImageFormat::CODE_BAYER_GBRG32F: + case ImageFormat::CODE_BAYER_BGGR32F: + + case ImageFormat::CODE_HSV8: + case ImageFormat::CODE_HSV32F: + + case ImageFormat::CODE_RGB_DXT1: + return ImageFormat::RGB_DXT1(); + break; + case ImageFormat::CODE_RGBA_DXT1: + return ImageFormat::RGBA_DXT1(); + break; + case ImageFormat::CODE_RGBA_DXT3: + return ImageFormat::RGBA_DXT3(); + break; + case ImageFormat::CODE_RGBA_DXT5: + return ImageFormat::RGBA_DXT5(); + break; + + case ImageFormat::CODE_DEPTH16: + return ImageFormat::DEPTH16(); + break; + case ImageFormat::CODE_DEPTH24: + return ImageFormat::DEPTH24(); + break; + case ImageFormat::CODE_DEPTH32: + return ImageFormat::DEPTH32(); + break; + case ImageFormat::CODE_DEPTH32F: + return ImageFormat::DEPTH32F(); + break; + + case ImageFormat::CODE_STENCIL1: + return ImageFormat::STENCIL1(); + break; + case ImageFormat::CODE_STENCIL4: + return ImageFormat::STENCIL4(); + break; + case ImageFormat::CODE_STENCIL8: + return ImageFormat::STENCIL8(); + break; + case ImageFormat::CODE_STENCIL16: + return ImageFormat::STENCIL16(); + break; + + case ImageFormat::CODE_DEPTH24_STENCIL8: + return ImageFormat::DEPTH24_STENCIL8(); + break; + + case ImageFormat::CODE_YUV420_PLANAR: + return ImageFormat::YUV420_PLANAR(); + break; + + case ImageFormat::CODE_YUV422: + return ImageFormat::YUV422(); + break; + + case ImageFormat::CODE_YUV444: + return ImageFormat::YUV444(); + break; + + default: + return NULL; + } +} + +// Helper variables for defining texture formats + +// Is floating point format +static const bool FLOAT_FORMAT = true; +static const bool INT_FORMAT = false; + +// Is opaque format (no alpha) +static const bool OPAQUE_FORMAT = true; +static const bool CLEAR_FORMAT = false; + +// Is compressed format (not raw component data) +static const bool COMP_FORMAT = true; +static const bool UNCOMP_FORMAT = false; + +#define DEFINE_TEXTUREFORMAT_METHOD(name, cmpnts, cmprssd, glf, glbf, lb, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs, bp) \ + const ImageFormat* ImageFormat::name() { \ + static const ImageFormat format(cmpnts, cmprssd, glf, glbf, lb, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs, bp); \ + return &format; } + +DEFINE_TEXTUREFORMAT_METHOD(L8, 1, UNCOMP_FORMAT, GL_LUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, CODE_L8, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(L16, 1, UNCOMP_FORMAT, GL_LUMINANCE16, GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16,GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, CODE_L16, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(L16F, 1, UNCOMP_FORMAT, GL_LUMINANCE16F_ARB,GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L16F, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(L32F, 1, UNCOMP_FORMAT, GL_LUMINANCE32F_ARB,GL_LUMINANCE, 32, 0, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L32F, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(A8, 1, UNCOMP_FORMAT, GL_ALPHA8, GL_ALPHA, 0, 8, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_A8, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(A16, 1, UNCOMP_FORMAT, GL_ALPHA16, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_A16, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(A16F, 1, UNCOMP_FORMAT, GL_ALPHA16F_ARB, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A16F, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(A32F, 1, UNCOMP_FORMAT, GL_ALPHA32F_ARB, GL_ALPHA, 0, 32, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A32F, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(LA4, 2, UNCOMP_FORMAT, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, 4, 4, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA4, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(LA8, 2, UNCOMP_FORMAT, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA8, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(LA16, 2, UNCOMP_FORMAT, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_LA16, COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(LA16F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA16F_ARB, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA16F, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(LA32F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA32F_ARB, GL_LUMINANCE_ALPHA, 32, 32, 0, 0, 0, 0, 0, 32*2, 32*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA32F, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(BGR8, 3, UNCOMP_FORMAT, GL_RGB8, GL_BGR, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_BGR8, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB5, 3, UNCOMP_FORMAT, GL_RGB5, GL_RGBA, 0, 0, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB5A1, 4, UNCOMP_FORMAT, GL_RGB5_A1, GL_RGBA, 0, 1, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5A1, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB8, 3, UNCOMP_FORMAT, GL_RGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB10, 3, UNCOMP_FORMAT, GL_RGB10, GL_RGB, 0, 0, 10, 10, 10, 0, 0, 32, 10*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB10A2, 4, UNCOMP_FORMAT, GL_RGB10_A2, GL_RGBA, 0, 2, 10, 10, 10, 0, 0, 32, 32, GL_UNSIGNED_INT_10_10_10_2, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10A2, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB16, 3, UNCOMP_FORMAT, GL_RGB16, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB16, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB16F, 3, UNCOMP_FORMAT, GL_RGB16F_ARB, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB32F, 3, UNCOMP_FORMAT, GL_RGB32F_ARB, GL_RGB, 0, 0, 32, 32, 32, 0, 0, 32*3, 32*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB32F, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA8, 4, UNCOMP_FORMAT, GL_RGBA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 32, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA16, 4, UNCOMP_FORMAT, GL_RGBA16, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA16, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA16F, 4, UNCOMP_FORMAT, GL_RGBA16F_ARB, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA32F, 4, UNCOMP_FORMAT, GL_RGBA32F_ARB, GL_RGBA, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA32F, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB_DXT1, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT1, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT3, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT5, ImageFormat::COLOR_SPACE_RGB); + +DEFINE_TEXTUREFORMAT_METHOD(DEPTH16, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH16, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(DEPTH24, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 24, 32, 24, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(DEPTH32, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 32, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH32, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(DEPTH32F, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 32, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_DEPTH32F, ImageFormat::COLOR_SPACE_NONE); + +// These formats are for use with Renderbuffers only! +DEFINE_TEXTUREFORMAT_METHOD(STENCIL1, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 1, 1, 1, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL1, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(STENCIL4, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 4, 4, 4, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL4, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(STENCIL8, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 8, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL8, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(STENCIL16, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX16_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL16, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(DEPTH24_STENCIL8, 2, UNCOMP_FORMAT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_STENCIL_EXT,0, 0, 0, 0, 0, 24, 8, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24_STENCIL8, ImageFormat::COLOR_SPACE_NONE); + +DEFINE_TEXTUREFORMAT_METHOD(YUV420_PLANAR, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 12, 12, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV420_PLANAR, ImageFormat::COLOR_SPACE_YUV); +DEFINE_TEXTUREFORMAT_METHOD(YUV422, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV422, ImageFormat::COLOR_SPACE_YUV); +DEFINE_TEXTUREFORMAT_METHOD(YUV444, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 24, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV444, ImageFormat::COLOR_SPACE_YUV); + +} diff --git a/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp b/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp new file mode 100644 index 00000000000..9cbc4edcb39 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp @@ -0,0 +1,1305 @@ +#include "G3D/ImageFormat.h" +#include "G3D/Color1uint8.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color4uint8.h" +#include "G3D/Color1.h" +#include "G3D/Color3.h" +#include "G3D/Color4.h" + + +namespace G3D { + +// this is the signature for all conversion routines (same parameters as ImageFormat::convert) +typedef void (*ConvertFunc)(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg); + +// this defines the conversion routines for converting between compatible formats +static const int NUM_CONVERT_IMAGE_FORMATS = 5; +struct ConvertAttributes { + ConvertFunc m_converter; + ImageFormat::Code m_sourceFormats[NUM_CONVERT_IMAGE_FORMATS]; + ImageFormat::Code m_destFormats[NUM_CONVERT_IMAGE_FORMATS]; + bool m_handlesSourcePadding; + bool m_handlesDestPadding; + bool m_handleInvertY; +}; + +// forward declare the converters we can use them below +#define DECLARE_CONVERT_FUNC(name) static void name(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg); + +DECLARE_CONVERT_FUNC(l8_to_rgb8); +DECLARE_CONVERT_FUNC(l32f_to_rgb8); +DECLARE_CONVERT_FUNC(rgb8_to_rgba8); +DECLARE_CONVERT_FUNC(rgb8_to_bgr8); +DECLARE_CONVERT_FUNC(rgb8_to_rgba32f); +DECLARE_CONVERT_FUNC(bgr8_to_rgb8); +DECLARE_CONVERT_FUNC(bgr8_to_rgba8); +DECLARE_CONVERT_FUNC(bgr8_to_rgba32f); +DECLARE_CONVERT_FUNC(rgba8_to_rgb8); +DECLARE_CONVERT_FUNC(rgba8_to_bgr8); +DECLARE_CONVERT_FUNC(rgba8_to_rgba32f); +DECLARE_CONVERT_FUNC(rgb32f_to_rgba32f); +DECLARE_CONVERT_FUNC(rgba32f_to_rgb8); +DECLARE_CONVERT_FUNC(rgba32f_to_rgba8); +DECLARE_CONVERT_FUNC(rgba32f_to_bgr8); +DECLARE_CONVERT_FUNC(rgba32f_to_rgb32f); +DECLARE_CONVERT_FUNC(rgba32f_to_bayer_rggb8); +DECLARE_CONVERT_FUNC(rgba32f_to_bayer_gbrg8); +DECLARE_CONVERT_FUNC(rgba32f_to_bayer_grbg8); +DECLARE_CONVERT_FUNC(rgba32f_to_bayer_bggr8); +DECLARE_CONVERT_FUNC(bayer_rggb8_to_rgba32f); +DECLARE_CONVERT_FUNC(bayer_gbrg8_to_rgba32f); +DECLARE_CONVERT_FUNC(bayer_grbg8_to_rgba32f); +DECLARE_CONVERT_FUNC(bayer_bggr8_to_rgba32f); +DECLARE_CONVERT_FUNC(rgb8_to_yuv420p); +DECLARE_CONVERT_FUNC(rgb8_to_yuv422); +DECLARE_CONVERT_FUNC(rgb8_to_yuv444); +DECLARE_CONVERT_FUNC(yuv420p_to_rgb8); +DECLARE_CONVERT_FUNC(yuv422_to_rgb8); +DECLARE_CONVERT_FUNC(yuv444_to_rgb8); + +// this is the list of mappings between formats and the routines to perform them +static const ConvertAttributes sConvertMappings[] = { + + // RGB -> RGB color space + // L8 -> + {l8_to_rgb8, {ImageFormat::CODE_L8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, + + // L32F -> + {l32f_to_rgb8, {ImageFormat::CODE_L32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, + + // RGB8 -> + {rgb8_to_rgba8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true}, + {rgb8_to_bgr8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true}, + {rgb8_to_rgba32f, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, + + // BGR8 -> + {bgr8_to_rgb8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, + {bgr8_to_rgba8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true}, + {bgr8_to_rgba32f, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, + + // RGBA8 -> + {rgba8_to_rgb8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, + {rgba8_to_bgr8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true}, + {rgba8_to_rgba32f, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, + + // RGB32F -> + {rgb32f_to_rgba32f, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, + + // RGBA32F -> + {rgba32f_to_rgb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, true, true}, + {rgba32f_to_rgba8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, true, true}, + {rgba32f_to_bgr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, true, true}, + {rgba32f_to_rgb32f, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, false, true, true}, + + // RGB -> BAYER color space + {rgba32f_to_bayer_rggb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, false, true, true}, + {rgba32f_to_bayer_gbrg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, false, true, true}, + {rgba32f_to_bayer_grbg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, false, true, true}, + {rgba32f_to_bayer_bggr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, false, true, true}, + + // BAYER -> RGB color space + {bayer_rggb8_to_rgba32f, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, + {bayer_gbrg8_to_rgba32f, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, + {bayer_grbg8_to_rgba32f, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, + {bayer_bggr8_to_rgba32f, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, + + // RGB <-> YUV color space + {rgb8_to_yuv420p, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, false, false, false}, + {rgb8_to_yuv422, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, false, false, false}, + {rgb8_to_yuv444, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, false, false, false}, + {yuv420p_to_rgb8, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false}, + {yuv422_to_rgb8, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false}, + {yuv444_to_rgb8, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false}, +}; + +static ConvertFunc findConverter(TextureFormat::Code sourceCode, TextureFormat::Code destCode, bool needsSourcePadding, bool needsDestPadding, bool needsInvertY) { + int numRoutines = sizeof(sConvertMappings) / sizeof(ConvertAttributes); + for (int routineIndex = 0; routineIndex < numRoutines; ++routineIndex) { + int sourceIndex = 0; + ConvertAttributes routine = sConvertMappings[routineIndex]; + + while (routine.m_sourceFormats[sourceIndex] != ImageFormat::CODE_NONE) { + // check for matching source + if (routine.m_sourceFormats[sourceIndex] == sourceCode) { + int destIndex = 0; + + // now check for matching dest to see if the routine fits + while (routine.m_destFormats[destIndex] != ImageFormat::CODE_NONE) { + + // check if dest format matches and padding + invert rules match + if ((routine.m_destFormats[destIndex] == destCode) && + (!needsSourcePadding || (routine.m_handlesSourcePadding == needsSourcePadding)) && + (!needsDestPadding || (routine.m_handlesDestPadding == needsDestPadding)) && + (!needsInvertY || (routine.m_handleInvertY == needsInvertY))) { + + // found compatible converter + return routine.m_converter; + } + ++destIndex; + } + } + ++sourceIndex; + } + } + + return NULL; +} + +bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false) { + bool conversionAvailable = false; + + // check if a conversion is available + if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) { + conversionAvailable = true; + } else { + ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY); + + conversionAvailable = (directConverter != NULL); + } + + return conversionAvailable; +} + +bool ImageFormat::convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, + const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, + bool invertY, BayerAlgorithm bayerAlg) { + + bool conversionAvailable = false; + + // Handle direct copy of image to same format + if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) { + + System::memcpy(dstBytes[0], srcBytes[0], iCeil(((srcWidth * srcFormat->cpuBitsPerPixel + srcRowPadBits) * srcHeight) / 8.0f)); + conversionAvailable = true; + } else { + // if no direct conversion routine exists, + // then look for conversion to intermediate + // and then from intermediate to dest. + // intermediate format is RGBA32F + ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY); + + // if we have a direct converter, use it, otherwise find intermdiate path + if (directConverter) { + directConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg); + conversionAvailable = true; + } else { + ConvertFunc toInterConverter = findConverter(srcFormat->code, ImageFormat::CODE_RGBA32F, srcRowPadBits > 0, false, false);; + ConvertFunc fromInterConverter = findConverter(ImageFormat::CODE_RGBA32F, dstFormat->code, false, dstRowPadBits > 0, invertY);; + + if (toInterConverter && fromInterConverter) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * ImageFormat::RGBA32F()->cpuBitsPerPixel * 8)); + + toInterConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, tmp, ImageFormat::RGBA32F(), 0, false, bayerAlg); + fromInterConverter(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg); + + System::free(tmp[0]); + + conversionAvailable = true; + } + } + } + + return conversionAvailable; +} + + +// ******************* +// RGB -> RGB color space conversions +// ******************* + +// L8 -> +static void l8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + + dst[i3 + 0] = src[i]; + dst[i3 + 1] = src[i]; + dst[i3 + 2] = src[i]; + } + } +} + +// L32F -> +static void l32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + int srcIndex = 0; + int dstByteOffset = 0; + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const float* src = static_cast<const float*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + srcIndex = srcWidth * (srcHeight - y - 1); + } + + for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) { + Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset); + float s = src[srcIndex]; + + uint8 c = iMin(255, iFloor(s * 256)); + d = Color3uint8(c, c, c); + } + } +} + +// RGB8 -> +static void rgb8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + int i4 = i3 + i; + + dst[i4 + 0] = src[i3 + 0]; + dst[i4 + 1] = src[i3 + 1]; + dst[i4 + 2] = src[i3 + 2]; + dst[i4 + 3] = 255; + } + } +} + +static void rgb8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + dst[i3 + 0] = src[i3 + 2]; + dst[i3 + 1] = src[i3 + 1]; + dst[i3 + 2] = src[i3 + 0]; + } + } +} + +static void rgb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); + + int dstIndex = 0; + int srcByteOffset = 0; + int srcRowPadBytes = srcRowPadBits / 8; + Color4* dst = static_cast<Color4*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + dstIndex = srcWidth * (srcHeight - 1 - y); + } + for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) { + const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset); + dst[dstIndex] = Color4(Color3(s), 1.0f); + } + srcByteOffset += srcRowPadBytes; + } +} + +// BGR8 -> +static void bgr8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + dst[i3 + 0] = src[i3 + 2]; + dst[i3 + 1] = src[i3 + 1]; + dst[i3 + 2] = src[i3 + 0]; + } + } +} + +static void bgr8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + int i4 = i3 + i; + + dst[i4 + 0] = src[i3 + 2]; + dst[i4 + 1] = src[i3 + 1]; + dst[i4 + 2] = src[i3 + 0]; + dst[i4 + 3] = 255; + } + } +} + +static void bgr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); + + int dstIndex = 0; + int srcByteOffset = 0; + int srcRowPadBytes = srcRowPadBits / 8; + Color4* dst = static_cast<Color4*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + dstIndex = srcWidth * (srcHeight - 1 - y); + } + + for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) { + const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset); + dst[dstIndex] = Color4(Color3(s).bgr(), 1.0f); + } + srcByteOffset += srcRowPadBytes; + } +} + +// RGBA8 -> +static void rgba8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + int i4 = i3 + i; + + dst[i3 + 0] = src[i4 + 0]; + dst[i3 + 1] = src[i4 + 1]; + dst[i3 + 2] = src[i4 + 2]; + } + } +} + +static void rgba8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); + int i3 = i * 3; + int i4 = i3 + i; + + dst[i3 + 0] = src[i4 + 2]; + dst[i3 + 1] = src[i4 + 1]; + dst[i3 + 2] = src[i4 + 0]; + } + } +} + +static void rgba8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); + + int dstIndex = 0; + int srcByteOffset = 0; + int srcRowPadBytes = srcRowPadBits / 8; + Color4* dst = static_cast<Color4*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + dstIndex = srcWidth * (srcHeight - 1 - y); + } + + for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 4) { + const Color4uint8& s = *reinterpret_cast<const Color4uint8*>(src + srcByteOffset); + dst[dstIndex] = Color4(s); + } + srcByteOffset += srcRowPadBytes; + } +} + +// RGB32F -> +static void rgb32f_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); + + int dstIndex = 0; + int srcByteOffset = 0; + int srcRowPadBytes = srcRowPadBits / 8; + Color4* dst = static_cast<Color4*>(dstBytes[0]); + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + dstIndex = srcWidth * (srcHeight - 1 - y); + } + + for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3 * sizeof(float)) { + const Color3& s = *reinterpret_cast<const Color3*>(src + srcByteOffset); + dst[dstIndex] = Color4(Color3(s), 1.0f); + } + srcByteOffset += srcRowPadBytes; + } +} + +// RGBA32F -> +static void rgba32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); + + int srcIndex = 0; + int dstByteOffset = 0; + int dstRowPadBytes = dstRowPadBits / 8; + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const Color4* src = static_cast<const Color4*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + srcIndex = srcWidth * (srcHeight - y - 1); + } + + for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) { + Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset); + const Color4& s = src[srcIndex]; + + d = Color3uint8(s.rgb()); + } + dstByteOffset += dstRowPadBytes; + } +} + +static void rgba32f_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); + + int srcIndex = 0; + int dstByteOffset = 0; + int dstRowPadBytes = dstRowPadBits / 8; + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const Color4* src = static_cast<const Color4*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + srcIndex = srcWidth * (srcHeight - 1 - y); + } + for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 4) { + Color4uint8& d = *reinterpret_cast<Color4uint8*>(dst + dstByteOffset); + const Color4& s = src[srcIndex]; + + d = Color4uint8(s); + } + dstByteOffset += dstRowPadBytes; + } +} + +static void rgba32f_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); + + int srcIndex = 0; + int dstByteOffset = 0; + int dstRowPadBytes = dstRowPadBits / 8; + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const Color4* src = static_cast<const Color4*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + srcIndex = srcWidth * (srcHeight - y - 1); + } + + for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) { + Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset); + const Color4& s = src[srcIndex]; + + d = Color3uint8(s.rgb()).bgr(); + } + dstByteOffset += dstRowPadBytes; + } +} + +static void rgba32f_to_rgb32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); + + int srcIndex = 0; + int dstByteOffset = 0; + int dstRowPadBytes = dstRowPadBits / 8; + uint8* dst = static_cast<uint8*>(dstBytes[0]); + const Color4* src = static_cast<const Color4*>(srcBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + if (invertY) { + srcIndex = srcWidth * (srcHeight - 1 - y); + } + for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3 * sizeof(float)) { + Color3& d = *reinterpret_cast<Color3*>(dst + dstByteOffset); + const Color4& s = src[srcIndex]; + d = Color3(s); + } + dstByteOffset += dstRowPadBytes; + } +} + +// ******************* +// RGB <-> YUV color space conversions +// ******************* + +static uint32 blendPixels(uint32 pixel1, uint32 pixel2) { + static const uint32 rbMask = 0x00FF00FF; + static const uint32 agMask = 0xFF00FF00; + + // Compute two color channels at a time. Use >> 1 for fast division by two + // Using alternating color channels prevents overflow + const uint32 rb = ((pixel1 & rbMask) + (pixel2 & rbMask)) >> 1; + + // Shift first to avoid overflow in alpha channel + const uint32 ag = (((pixel1 & agMask) >> 1) + ((pixel2 & agMask) >> 1)); + + return ((rb & rbMask) | (ag & agMask)); +} + +#define PIXEL_RGB8_TO_YUV_Y(r, g, b) static_cast<uint8>(iClamp(((66 * r + 129 * g + 25 * b + 128) >> 8) + 16, 0, 255)) +#define PIXEL_RGB8_TO_YUV_U(r, g, b) static_cast<uint8>(iClamp(((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128, 0, 255)) +#define PIXEL_RGB8_TO_YUV_V(r, g, b) static_cast<uint8>(iClamp(((112 * r - 94 * g - 18 * b + 128) >> 8) + 128, 0, 255)) + +static void rgb8_to_yuv420p(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); + debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two"); + + const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]); + + uint8* dstY = static_cast<uint8*>(dstBytes[0]); + uint8* dstU = static_cast<uint8*>(dstBytes[1]); + uint8* dstV = static_cast<uint8*>(dstBytes[2]); + + for (int y = 0; y < srcHeight; y += 2) { + for (int x = 0; x < srcWidth; x += 2) { + + // convert 4-pixel block at a time + int srcPixelOffset0 = y * srcWidth + x; + int srcPixelOffset1 = srcPixelOffset0 + 1; + int srcPixelOffset2 = srcPixelOffset0 + srcWidth; + int srcPixelOffset3 = srcPixelOffset2 + 1; + + int yIndex = y * srcWidth + x; + + dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset0].r, src[srcPixelOffset0].g, src[srcPixelOffset0].b); + dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset1].r, src[srcPixelOffset1].g, src[srcPixelOffset1].b); + + yIndex += srcWidth; + dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset2].r, src[srcPixelOffset2].g, src[srcPixelOffset2].b); + dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset3].r, src[srcPixelOffset3].g, src[srcPixelOffset3].b); + + uint32 blendedPixel = blendPixels(src[srcPixelOffset0].asUInt32(), src[srcPixelOffset2].asUInt32()); + Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel); + + int uvIndex = y / 2 * srcWidth / 2 + x / 2; + dstU[uvIndex] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); + dstV[uvIndex] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); + } + } +} + +static void rgb8_to_yuv422(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); + debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two"); + + const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]); + + uint8* dst = static_cast<uint8*>(dstBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; x += 2) { + + // convert 2-pixel horizontal block at a time + int srcIndex = y * srcWidth + x; + int dstIndex = srcIndex * 2; + + uint32 blendedPixel = blendPixels(src[srcIndex].asUInt32(), src[srcIndex + 1].asUInt32()); + Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel); + + dst[dstIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex].r, src[srcIndex].g, src[srcIndex].b); + + dst[dstIndex + 1] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); + + dst[dstIndex + 2] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex + 1].r, src[srcIndex + 1].g, src[srcIndex + 1].b); + + dst[dstIndex + 3] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); + + } + } +} + +static void rgb8_to_yuv444(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); + + const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]); + + Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + + // convert 1-pixels at a time + int index = y * srcWidth + x; + uint8 y = PIXEL_RGB8_TO_YUV_Y(src[index].r, src[index].g, src[index].b); + uint8 u = PIXEL_RGB8_TO_YUV_U(src[index].r, src[index].g, src[index].b); + uint8 v = PIXEL_RGB8_TO_YUV_V(src[index].r, src[index].g, src[index].b); + + dst[index].r = y; + dst[index].g = u; + dst[index].b = v; + } + } +} + + +#define PIXEL_YUV_TO_RGB8_R(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 409 * (v - 128) + 128) >> 8, 0, 255)) +#define PIXEL_YUV_TO_RGB8_G(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) - 100 * (u - 128) - 208 * (v - 128) + 128) >> 8, 0, 255)) +#define PIXEL_YUV_TO_RGB8_B(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 516 * (u - 128) + 128) >> 8, 0, 255)) + +static void yuv420p_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); + debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two"); + + const uint8* srcY = static_cast<const uint8*>(srcBytes[0]); + const uint8* srcU = static_cast<const uint8*>(srcBytes[1]); + const uint8* srcV = static_cast<const uint8*>(srcBytes[2]); + + Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; x += 2) { + + // convert to two rgb pixels in a row + Color3uint8* rgb = &dst[y * srcWidth + x]; + + int yOffset = y * srcWidth + x; + int uvOffset = y / 2 * srcWidth / 2 + x / 2; + + rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]); + rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]); + rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]); + + rgb += 1; + rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]); + rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]); + rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]); + } + } +} + +static void yuv422_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); + debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two"); + + const uint8* src = static_cast<const uint8*>(srcBytes[0]); + + Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; x += 2) { + + // convert to two rgb pixels in a row + Color3uint8* rgb = &dst[y * srcWidth + x]; + + int srcIndex = (y * srcWidth + x) * 2; + uint8 y = src[srcIndex]; + uint8 u = src[srcIndex + 1]; + uint8 y2 = src[srcIndex + 2]; + uint8 v = src[srcIndex + 3]; + + rgb->r = PIXEL_YUV_TO_RGB8_R(y, u, v); + rgb->g = PIXEL_YUV_TO_RGB8_G(y, u, v); + rgb->b = PIXEL_YUV_TO_RGB8_B(y, u, v); + + rgb += 1; + rgb->r = PIXEL_YUV_TO_RGB8_R(y2, u, v); + rgb->g = PIXEL_YUV_TO_RGB8_G(y2, u, v); + rgb->b = PIXEL_YUV_TO_RGB8_B(y2, u, v); + } + } +} + +static void yuv444_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); + + const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]); + + Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]); + + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + + // convert to one rgb pixels at a time + int index = y * srcWidth + x; + Color3uint8* rgb = &dst[index]; + + rgb->r = PIXEL_YUV_TO_RGB8_R(src[index].r, src[index].g, src[index].b); + rgb->g = PIXEL_YUV_TO_RGB8_G(src[index].r, src[index].g, src[index].b); + rgb->b = PIXEL_YUV_TO_RGB8_B(src[index].r, src[index].g, src[index].b); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// +// Bayer conversions +// + +// There are two kinds of rows (GR and BG). +// In each row, there are two kinds of pixels (G/R, B/G). +// We express the four kinds of INPUT pixels as: +// GRG, GRG, BGB, BGG +// +// There are three kinds of OUTPUT pixels: R, G, B. +// Thus there are nominally 12 different I/O combinations, +// but several are impulses because needed output at that +// location *is* the input (e.g., G_GRG and G_BGG). +// +// The following 5x5 row-major filters are named as output_input. + +// Green +static const float G_GRR[5][5] = + {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, + { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, + { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; + +static const float G_BGB[5][5] = + {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, + { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, + { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; + +// Red +//(the caption in the paper is wrong for this case: +// "R row B column really means R row G column" +static const float R_GRG[5][5] = + {{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, + { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, + { -1.0f, 4.0f, 5.0f, 4.0f, -1.0f}, + { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, + { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}}; + +static const float R_BGG[5][5] = + {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, + { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, + { 0.5f, 0.0f, 5.0f, 0.0f, 0.5f}, + { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, + { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; + +static const float R_BGB[5][5] = + {{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}, + { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, + {-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f}, + { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, + { 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}}; + + +// Blue +//(the caption in the paper is wrong for this case: +// "B row R column really means B row G column") +#define B_BGG R_GRG +#define B_GRG R_BGG +#define B_GRR R_BGB + +// ===================================================================== +// Helper methods +// ===================================================================== + + +/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */ +static uint8 applyFilter(const uint8* I, + int x, + int y, + int w, + int h, + const float filter[5][5]) { + + debugAssert(isEven(w)); + debugAssert(isEven(h)); + + float sum = 0.0f; + float denom = 0.0f; + + for (int dy = 0; dy < 5; ++dy) { + int offset = ((y + dy + h - 2) % h) * w; + + for (int dx = 0; dx < 5; ++dx) { + float f = filter[dy][dx]; + sum += f * I[((x + dx + w - 2) % w) + offset]; + denom += f; + } + } + + return (uint8)iClamp(iRound(sum / denom), 0, 255); +} + +/** Helper method for Bayer grbg and bggr --> rgb8 */ +static void swapRedAndBlue(int N, Color3uint8* out) { + for (int i = N - 1; i >= 0; --i) { + uint8 tmp = out[i].r; + out[i].r = out[i].b; + out[i].b = tmp; + } +} + +// RGB -> BAYER color space + +// ===================================================================== +// rgb8 --> bayer helpers +// ===================================================================== +static void rgb8_to_bayer_rggb8(const int w, const int h, + const uint8* src, uint8* dst) { + Color3uint8* srcColor = (Color3uint8*)src; + Color1uint8* dstColor = (Color1uint8*)dst; + + // Top row pixels + for (int y = 0; y < h - 1; y += 2) { + int offset = y * w; + + // Top left pixels + for(int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].r); + } + + // Top right pixels + for(int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + } + + // Bottom row pixels + for (int y = 1; y < h - 1; y += 2) { + int offset = y * w; + + // Bottom left pixels + for (int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + + // Bottom right pixels + for (int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].b); + } + } +} + + +static void rgb8_to_bayer_grbg8(const int w, const int h, + const uint8* src, uint8* dst) { + Color3uint8* srcColor = (Color3uint8*)src; + Color1uint8* dstColor = (Color1uint8*)dst; + + // Top row pixels + for (int y = 0; y < h - 1; y += 2) { + int offset = y * w; + + // Top left pixels + for (int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + + // Top right pixels + for (int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].r); + } + } + + // Bottom row pixels + for (int y = 1; y < h - 1; y += 2) { + int offset = y * w; + + // Bottom left pixels + for (int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].b); + } + + // Bottom right pixels + for (int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + } +} + + +static void rgb8_to_bayer_bggr8(const int w, const int h, + const uint8* src, uint8* dst) { + Color3uint8* srcColor = (Color3uint8*)src; + Color1uint8* dstColor = (Color1uint8*)dst; + + // Top row pixels + for (int y = 0; y < h - 1; y += 2) { + int offset = y * w; + + // Top left pixels + for (int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].b); + } + + // Top right pixels + for (int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + } + + // Bottom row pixels + for (int y = 1; y < h - 1; y += 2) { + int offset = y * w; + + // Bottom left pixels + for(int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + + // Bottom right pixels + for(int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].r); + } + } +} + + +static void rgb8_to_bayer_gbrg8(const int w, const int h, + const uint8* src, uint8* dst) { + Color3uint8* srcColor = (Color3uint8*)src; + Color1uint8* dstColor = (Color1uint8*)dst; + + // Top row pixels + for(int y = 0; y < h - 1; y += 2) { + int offset = y * w; + + // Top left pixels + for(int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + + // Top right pixels + for(int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].b); + } + } + + // Bottom row pixels + for(int y = 1; y < h - 1; y += 2) { + int offset = y * w; + + // Bottom left pixels + for(int x = 0; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].r); + } + + // Bottom right pixels + for(int x = 1; x < w - 1; x += 2) { + dstColor[x + offset] = Color1(srcColor[x + offset].g); + } + } +} + +// ===================================================================== +// rgba32f (-->rgb8) --> bayer converter implementations +// ===================================================================== +static void rgba32f_to_bayer_rggb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); + rgb8_to_bayer_rggb8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0])); + + System::free(tmp[0]); +} + +static void rgba32f_to_bayer_gbrg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); + rgb8_to_bayer_grbg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0])); + + System::free(tmp[0]); +} + +static void rgba32f_to_bayer_grbg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); + rgb8_to_bayer_gbrg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0])); + + System::free(tmp[0]); +} + +static void rgba32f_to_bayer_bggr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); + rgb8_to_bayer_bggr8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0])); + + System::free(tmp[0]); +} + +// BAYER -> RGB color space + +// ===================================================================== +// bayer --> rgb8 helpers +// ===================================================================== +static void bayer_rggb8_to_rgb8_mhc(int w, int h, + const uint8* in, uint8* _out) { + debugAssert(in != _out); + + Color3uint8* out = (Color3uint8*)_out; + + for (int y = 0; y < h; ++y) { + + // Row beginning in the input array. + int offset = y * w; + + // RG row + for (int x = 0; x < w; ++x, ++out) { + // R pixel + { + out->r = in[x + offset]; + out->g = applyFilter(in, x, y, w, h, G_GRR); + out->b = applyFilter(in, x, y, w, h, B_GRR); + } + ++x; ++out; + + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_GRG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_GRG); + } + } + + ++y; + offset += w; + + // GB row + for (int x = 0; x < w; ++x, ++out) { + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_BGG); + } + ++x; ++out; + + // B pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGB); + out->g = applyFilter(in, x, y, w, h, G_BGB); + out->b = in[x + offset]; + } + } + } +} + + + +static void bayer_gbrg8_to_rgb8_mhc(int w, int h, + const uint8* in, uint8* _out) { + + debugAssert(in != _out); + + Color3uint8* out = (Color3uint8*)_out; + + for (int y = 0; y < h; ++y) { + + // Row beginning in the input array. + int offset = y * w; + + // GB row + for (int x = 0; x < w; ++x, ++out) { + // G pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGG); + out->g = in[x + offset]; + out->b = applyFilter(in, x, y, w, h, B_BGG); + } + ++x; ++out; + + // B pixel + { + out->r = applyFilter(in, x, y, w, h, R_BGB); + out->g = applyFilter(in, x, y, w, h, G_BGB); + out->b = in[x + offset]; + } + } + } +} + + +static void bayer_grbg8_to_rgb8_mhc(int w, int h, + const uint8* in, uint8* _out) { + // Run the equivalent function for red + bayer_gbrg8_to_rgb8_mhc(w, h, in, _out); + + // Now swap red and blue + swapRedAndBlue(w * h, (Color3uint8*)_out); +} + + +static void bayer_bggr8_to_rgb8_mhc(int w, int h, + const uint8* in, uint8* _out) { + // Run the equivalent function for red + bayer_rggb8_to_rgb8_mhc(w, h, in, _out); + + // Now swap red and blue + swapRedAndBlue(w * h, (Color3uint8*)_out); +} + +// ===================================================================== +// bayer (--> rgb8) --> rgba32f converter implementations +// ===================================================================== +static void bayer_rggb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + bayer_rggb8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0])); + rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); + + System::free(tmp[0]); +} + +static void bayer_gbrg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + bayer_grbg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0])); + rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); + + System::free(tmp[0]); +} + +static void bayer_grbg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + bayer_gbrg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0])); + rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); + + System::free(tmp[0]); +} + +static void bayer_bggr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { + Array<void*> tmp; + tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8))); + + bayer_bggr8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0])); + rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); + + System::free(tmp[0]); +} + + + + + + // TODO: The following region is commented out because so far + // those conversions are not used anywhere else. Until it is + // decided that such conversions are not needed, this region + // remains commented out. + + +// // ===================================================================== +// // bayer --> bgr8 +// // ===================================================================== + +// static void bayer_rggb8_to_bgr8_mhc(int w, int h, +// const uint8* in, uint8* _out) { +// debugAssert(in != _out); + +// Color3uint8* out = (Color3uint8*)_out; + +// for (int y = 0; y < h; ++y) { + +// // Row beginning in the input array. +// int offset = y * w; + +// // RG row +// for (int x = 0; x < w; ++x, ++out) { +// // R pixel +// { +// out->b = in[x + offset]; +// out->g = applyFilter(in, x, y, w, h, G_GRR); +// out->r = applyFilter(in, x, y, w, h, B_GRR); +// } +// ++x; ++out; + +// // G pixel +// { +// out->b = applyFilter(in, x, y, w, h, R_GRG); +// out->g = in[x + offset]; +// out->r = applyFilter(in, x, y, w, h, B_GRG); +// } +// } + +// ++y; +// offset += w; + +// // GB row +// for (int x = 0; x < w; ++x, ++out) { +// // G pixel +// { +// out->b = applyFilter(in, x, y, w, h, R_BGG); +// out->g = in[x + offset]; +// out->r = applyFilter(in, x, y, w, h, B_BGG); +// } +// ++x; ++out; + +// // B pixel +// { +// out->b = applyFilter(in, x, y, w, h, R_BGB); +// out->g = applyFilter(in, x, y, w, h, G_BGB); +// out->r = in[x + offset]; +// } +// } +// } +// } + + +// static void bayer_gbrg8_to_bgr8_mhc(int w, int h, +// const uint8* in, uint8* _out) { + +// debugAssert(in != _out); + +// Color3uint8* out = (Color3uint8*)_out; + +// for (int y = 0; y < h; ++y) { + +// // Row beginning in the input array. +// int offset = y * w; + +// // GB row +// for (int x = 0; x < srcWidth; ++x, ++out) { +// // G pixel +// { +// out->b = applyFilter(in, x, y, w, h, R_BGG); +// out->g = in[x + offset]; +// out->r = applyFilter(in, x, y, w, h, B_BGG); +// } +// ++x; ++out; + +// // B pixel +// { +// out->b = applyFilter(in, x, y, w, h, R_BGB); +// out->g = applyFilter(in, x, y, w, h, G_BGB); +// out->r = in[x + offset]; +// } +// } +// } +// } + +// static void bayer_grbg8_to_bgr8_mhc(int w, int h, +// const uint8* in, uint8* _out) { +// // Run the equivalent function for red +// bayer_gbrg8_to_bgr8_mhc(w, h, in, _out); + +// // Now swap red and blue +// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out); +// } + +// static void bayer_bggr8_to_bgr8_mhc(int w, int h, +// const uint8* in, uint8* _out) { +// // Run the equivalent function for red +// bayer_rggb8_to_bgr8_mhc(w, h, in, _out); + +// // Now swap red and blue +// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out); +// } + + + +/////////////////////////////////////////////////// + +} // namespace G3D diff --git a/externals/g3dlite/G3D.lib/source/Line.cpp b/externals/g3dlite/G3D.lib/source/Line.cpp new file mode 100644 index 00000000000..195ae7197f2 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Line.cpp @@ -0,0 +1,89 @@ +/** + @file Line.cpp + + Line class + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-06-02 + @edited 2006-01-28 + */ + +#include "G3D/Line.h" +#include "G3D/Plane.h" + +namespace G3D { + +Vector3 Line::intersection(const Plane& plane) const { + float d; + Vector3 normal = plane.normal(); + plane.getEquation(normal, d); + float rate = _direction.dot(normal); + + if (rate == 0) { + + return Vector3::inf(); + + } else { + float t = -(d + _point.dot(normal)) / rate; + + return _point + _direction * t; + } +} + + +Line::Line(class BinaryInput& b) { + deserialize(b); +} + + +void Line::serialize(class BinaryOutput& b) const { + _point.serialize(b); + _direction.serialize(b); +} + + +void Line::deserialize(class BinaryInput& b) { + _point.deserialize(b); + _direction.deserialize(b); +} + + +Vector3 Line::closestPoint(const Vector3& pt) const { + float t = _direction.dot(pt - _point); + return _point + _direction * t; +} + + +Vector3 Line::point() const { + return _point; +} + + +Vector3 Line::direction() const { + return _direction; +} + + +Vector3 Line::closestPoint(const Line& B, float& minDist) const { + const Vector3& P1 = _point; + const Vector3& U1 = _direction; + + Vector3 P2 = B.point(); + Vector3 U2 = B.direction(); + + const Vector3& P21 = P2 - P1; + const Vector3& M = U2.cross(U1); + float m2 = M.length(); + + Vector3 R = P21.cross(M) / m2; + + float t1 = R.dot(U2); + + minDist = abs(P21.dot(M)) / sqrt(m2); + + return P1 + t1 * U1; +} + +} + diff --git a/externals/g3dlite/G3D.lib/source/LineSegment.cpp b/externals/g3dlite/G3D.lib/source/LineSegment.cpp new file mode 100644 index 00000000000..44dfcb48bc1 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/LineSegment.cpp @@ -0,0 +1,236 @@ +/** + @file LineSegment.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-08 + @edited 2008-02-02 + */ + +#include "G3D/platform.h" +#include "G3D/LineSegment.h" +#include "G3D/Sphere.h" +#include "G3D/debug.h" + +namespace G3D { + + +Vector3 LineSegment::closestPoint(const Vector3& p) const { + + // The vector from the end of the capsule to the point in question. + Vector3 v(p - _point); + + // Projection of v onto the line segment scaled by + // the length of direction. + float t = direction.dot(v); + + // Avoid some square roots. Derivation: + // t/direction.length() <= direction.length() + // t <= direction.squaredLength() + + if ((t >= 0) && (t <= direction.squaredMagnitude())) { + + // The point falls within the segment. Normalize direction, + // divide t by the length of direction. + return _point + direction * t / direction.squaredMagnitude(); + + } else { + + // The point does not fall within the segment; see which end is closer. + + // Distance from 0, squared + float d0Squared = v.squaredMagnitude(); + + // Distance from 1, squared + float d1Squared = (v - direction).squaredMagnitude(); + + if (d0Squared < d1Squared) { + + // Point 0 is closer + return _point; + + } else { + + // Point 1 is closer + return _point + direction; + + } + } + +} + +Vector3 LineSegment::point(int i) const { + switch (i) { + case 0: + return _point; + + case 1: + return _point + direction; + + default: + debugAssertM(i == 0 || i == 1, "Argument to point must be 0 or 1"); + return _point; + } +} + + +bool LineSegment::intersectsSolidSphere(const class Sphere& s) const { + return distanceSquared(s.center) <= square(s.radius); +} + + +LineSegment::LineSegment(class BinaryInput& b) { + deserialize(b); +} + + +void LineSegment::serialize(class BinaryOutput& b) const { + _point.serialize(b); + direction.serialize(b); +} + + +void LineSegment::deserialize(class BinaryInput& b) { + _point.deserialize(b); + direction.deserialize(b); +} + + +Vector3 LineSegment::randomPoint() const { + return _point + uniformRandom(0, 1) * direction; +} + + +///////////////////////////////////////////////////////////////////////////////////// + +LineSegment2D LineSegment2D::fromTwoPoints(const Vector2& p0, const Vector2& p1) { + LineSegment2D s; + s.m_origin = p0; + s.m_direction = p1 - p0; + s.m_length = s.m_direction.length(); + return s; +} + + +Vector2 LineSegment2D::point(int i) const { + debugAssert(i == 0 || i == 1); + if (i == 0) { + return m_origin; + } else { + return m_direction + m_origin; + } +} + + +Vector2 LineSegment2D::closestPoint(const Vector2& Q) const { + // Two constants that appear in the result + const Vector2 k1(m_origin - Q); + const Vector2& k2 = m_direction; + + if (fuzzyEq(m_length, 0)) { + // This line segment has no length + return m_origin; + } + + // Time [0, 1] at which we hit the closest point travelling from p0 to p1. + // Derivation can be obtained by minimizing the expression + // ||P0 + (P1 - P0)t - Q||. + const float t = -k1.dot(k2) / (m_length * m_length); + + if (t < 0) { + // Clipped to low end point + return m_origin; + } else if (t > 1) { + // Clipped to high end point + return m_origin + m_direction; + } else { + // Subsitute into the line equation to find + // the point on the segment. + return m_origin + k2 * t; + } +} + + +float LineSegment2D::distance(const Vector2& p) const { + Vector2 closest = closestPoint(p); + return (closest - p).length(); +} + + +float LineSegment2D::length() const { + return m_length; +} + + +Vector2 LineSegment2D::intersection(const LineSegment2D& other) const { + + if ((m_origin == other.m_origin) || + (m_origin == other.m_origin + other.m_direction)) { + return m_origin; + } + + if (m_origin + m_direction == other.m_origin) { + return other.m_origin; + } + + // Note: Now that we've checked the endpoints, all other parallel lines can now be assumed + // to not intersect (within numerical precision) + + Vector2 dir1 = m_direction; + Vector2 dir2 = other.m_direction; + Vector2 origin1 = m_origin; + Vector2 origin2 = other.m_origin; + + if (dir1.x == 0) { + // Avoid an upcoming divide by zero + dir1 = dir1.yx(); + dir2 = dir2.yx(); + origin1 = origin1.yx(); + origin2 = origin2.yx(); + } + + // t1 = ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) / m_direction.x + // + // ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) * m_direction.y / m_direction.x = + // (other.m_origin.y - m_origin.y) + other.m_direction.y * t2 + // + // m = m_direction.y / m_direction.x + // d = other.m_origin - m_origin + // + // (d.x + other.m_direction.x * t2) * m = d.y + other.m_direction.y * t2 + // + // d.x * m + other.m_direction.x * m * t2 = d.y + other.m_direction.y * t2 + // + // d.x * m - d.y = (other.m_direction.y - other.m_direction.x * m) * t2 + // + // (d.x * m - d.y) / (other.m_direction.y - other.m_direction.x * m) = t2 + // + + Vector2 d = origin2 - origin1; + float m = dir1.y / dir1.x; + + float t2 = (d.x * m - d.y) / (dir2.y - dir2.x * m); + if (! isFinite(t2)) { + // Parallel lines: no intersection + return Vector2::inf(); + } + + if ((t2 < 0.0f) || (t2 > 1.0f)) { + // Intersection occurs past the end of the line segments + return Vector2::inf(); + } + + float t1 = (d.x + dir2.x * t2) / dir1.x; + if ((t1 < 0.0f) || (t1 > 1.0f)) { + // Intersection occurs past the end of the line segments + return Vector2::inf(); + } + + // Return the intersection point (computed from non-transposed + // variables even if we flipped above) + return m_origin + m_direction * t1; + +} + +} + diff --git a/externals/g3dlite/G3D.lib/source/Log.cpp b/externals/g3dlite/G3D.lib/source/Log.cpp new file mode 100644 index 00000000000..13cea7a31f0 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Log.cpp @@ -0,0 +1,157 @@ +/** + @file Log.cpp + + @maintainer Morgan McGuire, morgan@graphics3d.com + @created 2001-08-04 + @edited 2005-07-01 + */ + +#include "G3D/platform.h" +#include "G3D/Log.h" +#include "G3D/format.h" +#include "G3D/Array.h" +#include "G3D/fileutils.h" +#include <time.h> + +#ifdef G3D_WIN32 + #include <imagehlp.h> +#else + #include <stdarg.h> +#endif + +namespace G3D { + +void logPrintf(const char* fmt, ...) { + va_list arg_list; + va_start(arg_list, fmt); + Log::common()->vprintf(fmt, arg_list); + va_end(arg_list); +} + + +Log* Log::commonLog = NULL; + +Log::Log(const std::string& filename, int stripFromStackBottom) : + stripFromStackBottom(stripFromStackBottom) { + + this->filename = filename; + + logFile = fopen(filename.c_str(), "w"); + + if (logFile == NULL) { + std::string drive, base, ext; + Array<std::string> path; + parseFilename(filename, drive, path, base, ext); + std::string logName = base + ((ext != "") ? ("." + ext) : ""); + + // Write time is greater than 1ms. This may be a network drive.... try another file. + #ifdef G3D_WIN32 + logName = std::string(std::getenv("TEMP")) + logName; + #else + logName = std::string("/tmp/") + logName; + #endif + + logFile = fopen(logName.c_str(), "w"); + } + + // Turn off buffering. + setvbuf(logFile, NULL, _IONBF, 0); + + fprintf(logFile, "Application Log\n"); + time_t t; + time(&t); + fprintf(logFile, "Start: %s\n", ctime(&t)); + fflush(logFile); + + if (commonLog == NULL) { + commonLog = this; + } +} + + +Log::~Log() { + section("Shutdown"); + println("Closing log file"); + + // Make sure we don't leave a dangling pointer + if (Log::commonLog == this) { + Log::commonLog = NULL; + } + + fclose(logFile); +} + + +FILE* Log::getFile() const { + return logFile; +} + +Log* Log::common() { + if (commonLog == NULL) { + commonLog = new Log(); + } + return commonLog; +} + + +std::string Log::getCommonLogFilename() { + return common()->filename; +} + + +void Log::section(const std::string& s) { + fprintf(logFile, "_____________________________________________________\n"); + fprintf(logFile, "\n ### %s ###\n\n", s.c_str()); +} + + +void __cdecl Log::printf(const char* fmt, ...) { + printHeader(); + + va_list arg_list; + va_start(arg_list, fmt); + print(vformat(fmt, arg_list)); + va_end(arg_list); +} + + +void __cdecl Log::vprintf(const char* fmt, va_list argPtr) { + vfprintf(logFile, fmt, argPtr); +} + + +void Log::print(const std::string& s) { + printHeader(); + fprintf(logFile, "%s", s.c_str()); +} + + +void Log::println(const std::string& s) { + printHeader(); + fprintf(logFile, "%s\n", s.c_str()); +} + + +void Log::printHeader() { + time_t t; + if (time(&t) != ((time_t)-1)) { + /* + char buf[32]; + strftime(buf, 32, "[%H:%M:%S]", localtime(&t)); + + Removed because this doesn't work on SDL threads. + + #ifdef _DEBUG + std::string bt = getBacktrace(15, 2, stripFromStackBottom); + fprintf(logFile, "\n %s %s\n\n", buf, bt.c_str()); + #endif + + fprintf(logFile, "\n %s \n", buf); + */ + + } else { + println("[Error getting time]"); + } +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Matrix.cpp b/externals/g3dlite/G3D.lib/source/Matrix.cpp new file mode 100644 index 00000000000..0c6db214543 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Matrix.cpp @@ -0,0 +1,1801 @@ +/** + @file Matrix.cpp + @author Morgan McGuire, matrix@graphics3d.com + */ +#include "G3D/Matrix.h" +#include "G3D/TextOutput.h" + +static inline G3D::Matrix::T negate(G3D::Matrix::T x) { + return -x; +} + +namespace G3D { + +int Matrix::debugNumCopyOps = 0; +int Matrix::debugNumAllocOps = 0; + +void Matrix::serialize(TextOutput& t) const { + t.writeSymbol("%"); + t.writeNumber(rows()); + t.writeSymbol("x"); + t.writeNumber(cols()); + t.pushIndent(); + t.writeNewline(); + + t.writeSymbol("["); + for (int r = 0; r < rows(); ++r) { + for (int c = 0; c < cols(); ++c) { + t.writeNumber(impl->get(r, c)); + if (c < cols() - 1) { + t.writeSymbol(","); + } else { + if (r < rows() - 1) { + t.writeSymbol(";"); + t.writeNewline(); + } + } + } + } + t.writeSymbol("]"); + t.popIndent(); + t.writeNewline(); +} + + +std::string Matrix::toString(const std::string& name) const { + std::string s; + + if (name != "") { + s += format("%s = \n", name.c_str()); + } + + s += "["; + for (int r = 0; r < rows(); ++r) { + for (int c = 0; c < cols(); ++c) { + double v = impl->get(r, c); + + if (::fabs(v) < 0.00001) { + // Don't print "negative zero" + s += format("% 10.04g", 0.0); + } else if (v == iRound(v)) { + // Print integers nicely + s += format("% 10.04g", v); + } else { + s += format("% 10.04f", v); + } + + if (c < cols() - 1) { + s += ","; + } else if (r < rows() - 1) { + s += ";\n "; + } else { + s += "]\n"; + } + } + } + return s; +} + + +#define INPLACE(OP)\ + ImplRef A = impl;\ +\ + if (! A.isLastReference()) {\ + impl = new Impl(A->R, A->C);\ + }\ +\ + A->OP(B, *impl); + +Matrix& Matrix::operator*=(const T& B) { + INPLACE(mul) + return *this; +} + + +Matrix& Matrix::operator-=(const T& B) { + INPLACE(sub) + return *this; +} + + +Matrix& Matrix::operator+=(const T& B) { + INPLACE(add) + return *this; +} + + +Matrix& Matrix::operator/=(const T& B) { + INPLACE(div) + return *this; +} + + +Matrix& Matrix::operator*=(const Matrix& B) { + // We can't optimize this one + *this = *this * B; + return *this; +} + + +Matrix& Matrix::operator-=(const Matrix& _B) { + const Impl& B = *_B.impl; + INPLACE(sub) + return *this; +} + + +Matrix& Matrix::operator+=(const Matrix& _B) { + const Impl& B = *_B.impl; + INPLACE(add) + return *this; +} + + +void Matrix::arrayMulInPlace(const Matrix& _B) { + const Impl& B = *_B.impl; + INPLACE(arrayMul) +} + + +void Matrix::arrayDivInPlace(const Matrix& _B) { + const Impl& B = *_B.impl; + INPLACE(arrayDiv) +} + +#undef INPLACE + +Matrix Matrix::fromDiagonal(const Matrix& d) { + debugAssert((d.rows() == 1) || (d.cols() == 1)); + + int n = d.numElements(); + Matrix D = zero(n, n); + for (int i = 0; i < n; ++i) { + D.set(i, i, d.impl->data[i]); + } + + return D; +} + +void Matrix::set(int r, int c, T v) { + if (! impl.isLastReference()) { + // Copy the data before mutating; this object is shared + impl = new Impl(*impl); + } + impl->set(r, c, v); +} + + +void Matrix::setRow(int r, const Matrix& vec) { + debugAssertM(vec.cols() == cols(), + "A row must be set to a vector of the same size."); + debugAssertM(vec.rows() == 1, + "A row must be set to a row vector."); + + debugAssert(r >= 0); + debugAssert(r < rows()); + + if (! impl.isLastReference()) { + // Copy the data before mutating; this object is shared + impl = new Impl(*impl); + } + impl->setRow(r, vec.impl->data); +} + + +void Matrix::setCol(int c, const Matrix& vec) { + debugAssertM(vec.rows() == rows(), + "A column must be set to a vector of the same size."); + debugAssertM(vec.cols() == 1, + "A column must be set to a column vector."); + + debugAssert(c >= 0); + + debugAssert(c < cols()); + + if (! impl.isLastReference()) { + // Copy the data before mutating; this object is shared + impl = new Impl(*impl); + } + impl->setCol(c, vec.impl->data); +} + + +Matrix::T Matrix::get(int r, int c) const { + return impl->get(r, c); +} + + +Matrix Matrix::row(int r) const { + debugAssert(r >= 0); + debugAssert(r < rows()); + Matrix out(1, cols()); + out.impl->setRow(1, impl->elt[r]); + return out; +} + + +Matrix Matrix::col(int c) const { + debugAssert(c >= 0); + debugAssert(c < cols()); + Matrix out(rows(), 1); + + T* outData = out.impl->data; + // Get a pointer to the first element in the column + const T* inElt = &(impl->elt[0][c]); + int R = rows(); + int C = cols(); + for (int r = 0; r < R; ++r) { + outData[r] = *inElt; + // Skip around to the next row + inElt += C; + } + + return out; +} + + +Matrix Matrix::zero(int R, int C) { + Impl* A = new Impl(R, C); + A->setZero(); + return Matrix(A); +} + + +Matrix Matrix::one(int R, int C) { + Impl* A = new Impl(R, C); + for (int i = R * C - 1; i >= 0; --i) { + A->data[i] = 1.0; + } + return Matrix(A); +} + + +Matrix Matrix::random(int R, int C) { + Impl* A = new Impl(R, C); + for (int i = R * C - 1; i >= 0; --i) { + A->data[i] = G3D::uniformRandom(0.0, 1.0); + } + return Matrix(A); +} + + +Matrix Matrix::identity(int N) { + Impl* m = new Impl(N, N); + m->setZero(); + for (int i = 0; i < N; ++i) { + m->elt[i][i] = 1.0; + } + return Matrix(m); +} + + +// Implement an explicit-output unary method by trampolining to the impl +#define TRAMPOLINE_EXPLICIT_1(method)\ +void Matrix::method(Matrix& out) const {\ + if ((out.impl == impl) && impl.isLastReference()) {\ + impl->method(*out.impl);\ + } else {\ + out = this->method();\ + }\ +} + +TRAMPOLINE_EXPLICIT_1(abs) +TRAMPOLINE_EXPLICIT_1(negate) +TRAMPOLINE_EXPLICIT_1(arrayLog) +TRAMPOLINE_EXPLICIT_1(arrayExp) +TRAMPOLINE_EXPLICIT_1(arrayCos) +TRAMPOLINE_EXPLICIT_1(arraySin) + +void Matrix::mulRow(int r, const T& v) { + debugAssert(r >= 0 && r < rows()); + + if (! impl.isLastReference()) { + impl = new Impl(*impl); + } + + impl->mulRow(r, v); +} + + +void Matrix::transpose(Matrix& out) const { + if ((out.impl == impl) && impl.isLastReference() && (impl->R == impl->C)) { + // In place + impl->transpose(*out.impl); + } else { + out = this->transpose(); + } +} + + +Matrix3 Matrix::toMatrix3() const { + debugAssert(impl->R == 3); + debugAssert(impl->C == 3); + return Matrix3( + impl->get(0,0), impl->get(0,1), impl->get(0,2), + impl->get(1,0), impl->get(1,1), impl->get(1,2), + impl->get(2,0), impl->get(2,1), impl->get(2,2)); +} + + +Matrix4 Matrix::toMatrix4() const { + debugAssert(impl->R == 4); + debugAssert(impl->C == 4); + return Matrix4( + impl->get(0,0), impl->get(0,1), impl->get(0,2), impl->get(0,3), + impl->get(1,0), impl->get(1,1), impl->get(1,2), impl->get(1,3), + impl->get(2,0), impl->get(2,1), impl->get(2,2), impl->get(2,3), + impl->get(3,0), impl->get(3,1), impl->get(3,2), impl->get(3,3)); +} + + +Vector2 Matrix::toVector2() const { + debugAssert(impl->R * impl->C == 2); + if (impl->R > impl->C) { + return Vector2(impl->get(0,0), impl->get(1,0)); + } else { + return Vector2(impl->get(0,0), impl->get(0,1)); + } +} + + +Vector3 Matrix::toVector3() const { + debugAssert(impl->R * impl->C == 3); + if (impl->R > impl->C) { + return Vector3(impl->get(0,0), impl->get(1,0), impl->get(2, 0)); + } else { + return Vector3(impl->get(0,0), impl->get(0,1), impl->get(0, 2)); + } +} + + +Vector4 Matrix::toVector4() const { + debugAssert( + ((impl->R == 4) && (impl->C == 1)) || + ((impl->R == 1) && (impl->C == 4))); + + if (impl->R > impl->C) { + return Vector4(impl->get(0,0), impl->get(1,0), impl->get(2, 0), impl->get(3,0)); + } else { + return Vector4(impl->get(0,0), impl->get(0,1), impl->get(0, 2), impl->get(0,3)); + } +} + + +void Matrix::swapRows(int r0, int r1) { + debugAssert(r0 >= 0 && r0 < rows()); + debugAssert(r1 >= 0 && r1 < rows()); + + if (r0 == r1) { + return; + } + + if (! impl.isLastReference()) { + impl = new Impl(*impl); + } + + impl->swapRows(r0, r1); +} + + +void Matrix::swapAndNegateCols(int c0, int c1) { + debugAssert(c0 >= 0 && c0 < cols()); + debugAssert(c1 >= 0 && c1 < cols()); + + if (c0 == c1) { + return; + } + + if (! impl.isLastReference()) { + impl = new Impl(*impl); + } + + impl->swapAndNegateCols(c0, c1); +} + +Matrix Matrix::subMatrix(int r1, int r2, int c1, int c2) const { + debugAssert(r2>=r1); + debugAssert(c2>=c1); + debugAssert(c2<cols()); + debugAssert(r2<rows()); + debugAssert(r1>=0); + debugAssert(c1>=0); + + Matrix X(r2 - r1 + 1, c2 - c1 + 1); + + for (int r = 0; r < X.rows(); ++r) { + for (int c = 0; c < X.cols(); ++c) { + X.set(r, c, get(r + r1, c + c1)); + } + } + + return X; +} + + +bool Matrix::anyNonZero() const { + return impl->anyNonZero(); +} + + +bool Matrix::allNonZero() const { + return impl->allNonZero(); +} + + +void Matrix::svd(Matrix& U, Array<T>& d, Matrix& V, bool sort) const { + debugAssert(rows() >= cols()); + debugAssertM(&U != &V, "Arguments to SVD must be different matrices"); + debugAssertM(&U != this, "Arguments to SVD must be different matrices"); + debugAssertM(&V != this, "Arguments to SVD must be different matrices"); + + int R = rows(); + int C = cols(); + + // Make sure we don't overwrite a shared matrix + if (! V.impl.isLastReference()) { + V = Matrix::zero(C, C); + } else { + V.impl->setSize(C, C); + } + + if (&U != this || ! impl.isLastReference()) { + // Make a copy of this for in-place SVD + U.impl = new Impl(*impl); + } + + d.resize(C); + const char* ret = svdCore(U.impl->elt, R, C, d.getCArray(), V.impl->elt); + + debugAssertM(ret == NULL, ret); + (void)ret; + + if (sort) { + // Sort the singular values from greatest to least + + Array<SortRank> rank(C); + for (int c = 0; c < C; ++c) { + rank[c].col = c; + rank[c].value = d[c]; + } + + rank.sort(SORT_INCREASING); + + Matrix Uold = U; + Matrix Vold = V; + + U = Matrix(U.rows(), U.cols()); + V = Matrix(V.rows(), V.cols()); + + // Now permute U, d, and V appropriately + for (int c0 = 0; c0 < C; ++c0) { + const int c1 = rank[c0].col; + + d[c0] = rank[c0].value; + U.setCol(c0, Uold.col(c1)); + V.setCol(c0, Vold.col(c1)); + } + + } +} + + +#define COMPARE_SCALAR(OP)\ +Matrix Matrix::operator OP (const T& scalar) const {\ + int R = rows();\ + int C = cols();\ + int N = R * C;\ + Matrix out = Matrix::zero(R, C);\ +\ + const T* raw = impl->data;\ + T* outRaw = out.impl->data;\ + for (int i = 0; i < N; ++i) {\ + outRaw[i] = raw[i] OP scalar;\ + }\ +\ + return out;\ +} + +COMPARE_SCALAR(<) +COMPARE_SCALAR(<=) +COMPARE_SCALAR(>) +COMPARE_SCALAR(>=) +COMPARE_SCALAR(==) +COMPARE_SCALAR(!=) + +#undef COMPARE_SCALAR + +double Matrix::normSquared() const { + int R = rows(); + int C = cols(); + int N = R * C; + + double sum = 0.0; + + const T* raw = impl->data; + for (int i = 0; i < N; ++i) { + sum += square(raw[i]); + } + + return sum; +} + +double Matrix::norm() const { + return sqrt(normSquared()); +} + +/////////////////////////////////////////////////////////// + +Matrix::Impl::Impl(const Matrix3& M) : elt(NULL), data(NULL), R(0), C(0), dataSize(0){ + setSize(3, 3); + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + set(r, c, M[r][c]); + } + } + +} + + +Matrix::Impl::Impl(const Matrix4& M): elt(NULL), data(NULL), R(0), C(0), dataSize(0) { + setSize(4, 4); + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + set(r, c, M[r][c]); + } + } +} + + +void Matrix::Impl::setSize(int newRows, int newCols) { + if ((R == newRows) && (C == newCols)) { + // Nothing to do + return; + } + + int newSize = newRows * newCols; + + R = newRows; C = newCols; + + // Only allocate if we need more space + // or the size difference is ridiculous + if ((newSize > dataSize) || (newSize < dataSize / 4)) { + System::alignedFree(data); + data = (float*)System::alignedMalloc(R * C * sizeof(T), 16); + ++Matrix::debugNumAllocOps; + dataSize = newSize; + } + + // Construct the row pointers + //delete[] elt; + System::free(elt); + elt = (T**)System::malloc(R * sizeof(T*));// new T*[R]; + + for (int r = 0; r < R; ++ r) { + elt[r] = data + r * C; + } +} + + +Matrix::Impl::~Impl() { + //delete[] elt; + System::free(elt); + System::alignedFree(data); +} + + +Matrix::Impl& Matrix::Impl::operator=(const Impl& m) { + setSize(m.R, m.C); + System::memcpy(data, m.data, R * C * sizeof(T)); + ++Matrix::debugNumCopyOps; + return *this; +} + + +void Matrix::Impl::setZero() { + System::memset(data, 0, R * C * sizeof(T)); +} + + +void Matrix::Impl::swapRows(int r0, int r1) { + T* R0 = elt[r0]; + T* R1 = elt[r1]; + + for (int c = 0; c < C; ++c) { + T temp = R0[c]; + R0[c] = R1[c]; + R1[c] = temp; + } +} + + +void Matrix::Impl::swapAndNegateCols(int c0, int c1) { + for (int r = 0; r < R; ++r) { + T* row = elt[r]; + + const T temp = -row[c0]; + row[c0] = -row[c1]; + row[c1] = temp; + } +} + + +void Matrix::Impl::mulRow(int r, const T& v) { + T* row = elt[r]; + + for (int c = 0; c < C; ++c) { + row[c] *= v; + } +} + + +void Matrix::Impl::mul(const Impl& B, Impl& out) const { + const Impl& A = *this; + + debugAssertM( + (this != &out) && (&B != &out), + "Output argument to mul cannot be the same as an input argument."); + + debugAssert(A.C == B.R); + debugAssert(A.R == out.R); + debugAssert(B.C == out.C); + + for (int r = 0; r < out.R; ++r) { + for (int c = 0; c < out.C; ++c) { + T sum = 0.0; + for (int i = 0; i < A.C; ++i) { + sum += A.get(r, i) * B.get(i, c); + } + out.set(r, c, sum); + } + } +} + + +// We're about to define several similar methods, +// so use a macro to share implementations. This +// must be a macro because the difference between +// the macros is the operation in the inner loop. +#define IMPLEMENT_ARRAY_2(method, OP)\ +void Matrix::Impl::method(const Impl& B, Impl& out) const {\ + const Impl& A = *this;\ + \ + debugAssert(A.C == B.C);\ + debugAssert(A.R == B.R);\ + debugAssert(A.C == out.C);\ + debugAssert(A.R == out.R);\ + \ + for (int i = R * C - 1; i >= 0; --i) {\ + out.data[i] = A.data[i] OP B.data[i];\ + }\ +} + + +#define IMPLEMENT_ARRAY_1(method, f)\ +void Matrix::Impl::method(Impl& out) const {\ + const Impl& A = *this;\ + \ + debugAssert(A.C == out.C);\ + debugAssert(A.R == out.R);\ + \ + for (int i = R * C - 1; i >= 0; --i) {\ + out.data[i] = f(A.data[i]);\ + }\ +} + + +#define IMPLEMENT_ARRAY_SCALAR(method, OP)\ +void Matrix::Impl::method(Matrix::T B, Impl& out) const {\ + const Impl& A = *this;\ + \ + debugAssert(A.C == out.C);\ + debugAssert(A.R == out.R);\ + \ + for (int i = R * C - 1; i >= 0; --i) {\ + out.data[i] = A.data[i] OP B;\ + }\ +} + +IMPLEMENT_ARRAY_2(add, +) +IMPLEMENT_ARRAY_2(sub, -) +IMPLEMENT_ARRAY_2(arrayMul, *) +IMPLEMENT_ARRAY_2(arrayDiv, /) + +IMPLEMENT_ARRAY_SCALAR(add, +) +IMPLEMENT_ARRAY_SCALAR(sub, -) +IMPLEMENT_ARRAY_SCALAR(mul, *) +IMPLEMENT_ARRAY_SCALAR(div, /) + +IMPLEMENT_ARRAY_1(abs, ::fabs) +IMPLEMENT_ARRAY_1(negate, ::negate) +IMPLEMENT_ARRAY_1(arrayLog, ::log) +IMPLEMENT_ARRAY_1(arraySqrt, ::sqrt) +IMPLEMENT_ARRAY_1(arrayExp, ::exp) +IMPLEMENT_ARRAY_1(arrayCos, ::cos) +IMPLEMENT_ARRAY_1(arraySin, ::sin) + +#undef IMPLEMENT_ARRAY_SCALAR +#undef IMPLEMENT_ARRAY_1 +#undef IMPLEMENT_ARRAY_2 + +// lsub is special because the argument order is reversed +void Matrix::Impl::lsub(Matrix::T B, Impl& out) const { + const Impl& A = *this; + + debugAssert(A.C == out.C); + debugAssert(A.R == out.R); + + for (int i = R * C - 1; i >= 0; --i) { + out.data[i] = B - A.data[i]; + } +} + + +void Matrix::Impl::inverseViaAdjoint(Impl& out) const { + debugAssert(&out != this); + + // Inverse = adjoint / determinant + + adjoint(out); + + // Don't call the determinant method when we already have an + // adjoint matrix; there's a faster way of computing it: the dot + // product of the first row and the adjoint's first col. + double det = 0.0; + for (int r = R - 1; r >= 0; --r) { + det += elt[0][r] * out.elt[r][0]; + } + + out.div(Matrix::T(det), out); +} + + +void Matrix::Impl::transpose(Impl& out) const { + debugAssert(out.R == C); + debugAssert(out.C == R); + + if (&out == this) { + // Square matrix in place + for (int r = 0; r < R; ++r) { + for (int c = r + 1; c < C; ++c) { + T temp = get(r, c); + out.set(r, c, get(c, r)); + out.set(c, r, temp); + } + } + } else { + for (int r = 0; r < R; ++r) { + for (int c = 0; c < C; ++c) { + out.set(c, r, get(r, c)); + } + } + } +} + + +void Matrix::Impl::adjoint(Impl& out) const { + cofactor(out); + // Transpose is safe to perform in place + out.transpose(out); +} + + +void Matrix::Impl::cofactor(Impl& out) const { + debugAssert(&out != this); + for(int r = 0; r < R; ++r) { + for(int c = 0; c < C; ++c) { + out.set(r, c, cofactor(r, c)); + } + } +} + + +Matrix::T Matrix::Impl::cofactor(int r, int c) const { + // Strang p. 217 + float s = isEven(r + c) ? 1.0f : -1.0f; + + return s * determinant(r, c); +} + + +Matrix::T Matrix::Impl::determinant(int nr, int nc) const { + debugAssert(R > 0); + debugAssert(C > 0); + Impl A(R - 1, C - 1); + withoutRowAndCol(nr, nc, A); + return A.determinant(); +} + + +void Matrix::Impl::setRow(int r, const T* vals) { + debugAssert(r >= 0); + System::memcpy(elt[r], vals, sizeof(T) * C); +} + + +void Matrix::Impl::setCol(int c, const T* vals) { + for (int r = 0; r < R; ++r) { + elt[r][c] = vals[r]; + } +} + + +Matrix::T Matrix::Impl::determinant() const { + + debugAssert(R == C); + + // Compute using cofactors + switch(R) { + case 0: + return 0; + + case 1: + // Determinant of a 1x1 is the element + return elt[0][0]; + + case 2: + // Determinant of a 2x2 is ad-bc + return elt[0][0] * elt[1][1] - elt[0][1] * elt[1][0]; + + case 3: + { + // Determinant of an nxn matrix is the dot product of the first + // row with the first row of cofactors. The base cases of this + // method get called a lot, so we spell out the implementation + // for the 3x3 case. + + double cofactor00 = elt[1][1] * elt[2][2] - elt[1][2] * elt[2][1]; + double cofactor10 = elt[1][2] * elt[2][0] - elt[1][0] * elt[2][2]; + double cofactor20 = elt[1][0] * elt[2][1] - elt[1][1] * elt[2][0]; + + return Matrix::T( + elt[0][0] * cofactor00 + + elt[0][1] * cofactor10 + + elt[0][2] * cofactor20); + } + + default: + { + // Determinant of an n x n matrix is the dot product of the first + // row with the first row of cofactors + T det = 0.0; + + for (int c = 0; c < C; ++c) { + det += elt[0][c] * cofactor(0, c); + } + + return det; + } + } +} + + +void Matrix::Impl::withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const { + debugAssert(out.R == R - 1); + debugAssert(out.C == C - 1); + + for (int r = 0; r < out.R; ++r) { + for (int c = 0; c < out.C; ++c) { + out.elt[r][c] = elt[r + ((r >= excludeRow) ? 1 : 0)][c + ((c >= excludeCol) ? 1 : 0)]; + } + } +} + + +Matrix Matrix::pseudoInverse(float tolerance) const { + if ((cols() == 1) || (rows() == 1)) { + return vectorPseudoInverse(); + } else if ((cols() <= 4) || (rows() <= 4)) { + return partitionPseudoInverse(); + } else { + return svdPseudoInverse(tolerance); + } +} + +/* + Public function for testing purposes only. Use pseudoInverse(), as it contains optimizations for + nonsingular matrices with at least one small (<5) dimension. +*/ +Matrix Matrix::svdPseudoInverse(float tolerance) const { + if (cols() > rows()) { + return transpose().svdPseudoInverse(tolerance).transpose(); + } + + // Matrices from SVD + Matrix U, V; + + // Diagonal elements + Array<T> d; + + svd(U, d, V); + + if (rows() == 1) { + d.resize(1, false); + } + + if (tolerance < 0) { + // TODO: Should be eps(d[0]), which is the largest diagonal + tolerance = G3D::max(rows(), cols()) * 0.0001f; + } + + Matrix X; + + int r = 0; + for (int i = 0; i < d.size(); ++i) { + if (d[i] > tolerance) { + d[i] = Matrix::T(1) / d[i]; + ++r; + } + } + + if (r == 0) { + // There were no non-zero elements + X = zero(cols(), rows()); + } else { + // Use the first r columns + + // Test code (the rest is below) + /* + d.resize(r); + Matrix testU = U.subMatrix(0, U.rows() - 1, 0, r - 1); + Matrix testV = V.subMatrix(0, V.rows() - 1, 0, r - 1); + Matrix testX = testV * Matrix::fromDiagonal(d) * testU.transpose(); + X = testX; + */ + + + // We want to do this: + // + // d.resize(r); + // U = U.subMatrix(0, U.rows() - 1, 0, r - 1); + // X = V * Matrix::fromDiagonal(d) * U.transpose(); + // + // but creating a large diagonal matrix and then + // multiplying by it is wasteful. So we instead + // explicitly perform A = (D * U')' = U * D, and + // then multiply X = V * A'. + + Matrix A = Matrix(U.rows(), r); + + const T* dPtr = d.getCArray(); + for (int i = 0; i < A.rows(); ++i) { + const T* Urow = U.impl->elt[i]; + T* Arow = A.impl->elt[i]; + const int Acols = A.cols(); + for (int j = 0; j < Acols; ++j) { + // A(i,j) = U(i,:) * D(:,j) + // This is non-zero only at j = i because D is diagonal + // A(i,j) = U(i,j) * D(j,j) + Arow[j] = Urow[j] * dPtr[j]; + } + } + + // + // Compute X = V.subMatrix(0, V.rows() - 1, 0, r - 1) * A.transpose() + // + // Avoid the explicit subMatrix call, and by storing A' instead of A, avoid + // both the transpose and the memory incoherence of striding across memory + // in big steps. + + alwaysAssertM(A.cols() == r, + "Internal dimension mismatch during pseudoInverse()"); + alwaysAssertM(V.cols() >= r, + "Internal dimension mismatch during pseudoInverse()"); + + X = Matrix(V.rows(), A.rows()); + T** Xelt = X.impl->elt; + for (int i = 0; i < X.rows(); ++i) { + const T* Vrow = V.impl->elt[i]; + for (int j = 0; j < X.cols(); ++j) { + const T* Arow = A.impl->elt[j]; + T sum = 0; + for (int k = 0; k < r; ++k) { + sum += Vrow[k] * Arow[k]; + } + Xelt[i][j] = sum; + } + } + + /* + // Test that results are the same after optimizations: + Matrix diff = X - testX; + T n = diff.norm(); + debugAssert(n < 0.0001); + */ + } + return X; +} + +// Computes pseudoinverse for a vector +Matrix Matrix::vectorPseudoInverse() const { + // If vector A has nonzero elements: transpose A, then divide each elt. by the squared norm + // If A is zero vector: transpose A + double x = 0.0; + + if (anyNonZero()) { + x = 1.0 / normSquared(); + } + + Matrix A(cols(), rows()); + T** Aelt = A.impl->elt; + for (int r = 0; r < rows(); ++r) { + const T* MyRow = impl->elt[r]; + for (int c = 0; c < cols(); ++c) { + Aelt[c][r] = T(MyRow[c] * x); + } + } + return Matrix(A); +} + + +Matrix Matrix::rowPartPseudoInverse() const{ + int m = rows(); + int n = cols(); + alwaysAssertM((m<=n),"Row-partitioned block matrix pseudoinverse requires R<C"); + + // B = A * A' + Matrix A = *this; + Matrix B = Matrix(m,m); + + T** Aelt = A.impl->elt; + T** Belt = B.impl->elt; + for (int i = 0; i < m; ++i) { + const T* Arow = Aelt[i]; + for (int j = 0; j < m; ++j) { + const T* Brow = Aelt[j]; + T sum = 0; + for (int k = 0; k < n; ++k) { + sum += Arow[k] * Brow[k]; + } + Belt[i][j] = sum; + } + } + + // B has size m x m + switch (m) { + case 2: + return row2PseudoInverse(B); + + case 3: + return row3PseudoInverse(B); + + case 4: + return row4PseudoInverse(B); + + default: + alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!"); + return Matrix(); + } +} + +Matrix Matrix::colPartPseudoInverse() const{ + int m = rows(); + int n = cols(); + alwaysAssertM((m>=n),"Column-partitioned block matrix pseudoinverse requires R>C"); + // TODO: Put each of the individual cases in its own helper function + // TODO: Push the B computation down into the individual cases + // B = A' * A + Matrix A = *this; + Matrix B = Matrix(n, n); + T** Aelt = A.impl->elt; + T** Belt = B.impl->elt; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + T sum = 0; + for (int k = 0; k < m; ++k) { + sum += Aelt[k][i] * Aelt[k][j]; + } + Belt[i][j] = sum; + } + } + + // B has size n x n + switch (n) { + case 2: + return col2PseudoInverse(B); + + case 3: + return col3PseudoInverse(B); + + case 4: + return col4PseudoInverse(B); + + default: + alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!"); + return Matrix(); + } +} + +Matrix Matrix::col2PseudoInverse(const Matrix& B) const { + + Matrix A = *this; + int m = rows(); + int n = cols(); + (void)n; + + // Row-major 2x2 matrix + const float B2[2][2] = + {{B.get(0,0), B.get(0,1)}, + {B.get(1,0), B.get(1,1)}}; + + float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]); + + if (fuzzyEq(det, T(0))) { + + // Matrix was singular; the block matrix pseudo-inverse can't + // handle that, so fall back to the old case + return svdPseudoInverse(); + + } else { + // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html + + // Multiply by Binv * A' + Matrix X(cols(), rows()); + + T** Xelt = X.impl->elt; + T** Aelt = A.impl->elt; + float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det; + float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det; + for (int j = 0; j < m; ++j) { + const T* Arow = Aelt[j]; + float a0 = Arow[0]; + float a1 = Arow[1]; + Xelt[0][j] = binv00 * a0 + binv01 * a1; + Xelt[1][j] = binv10 * a0 + binv11 * a1; + } + return X; + } +} + +Matrix Matrix::col3PseudoInverse(const Matrix& B) const { + Matrix A = *this; + int m = rows(); + int n = cols(); + + Matrix3 B3 = B.toMatrix3(); + if (fuzzyEq(B3.determinant(), (T)0.0)) { + + // Matrix was singular; the block matrix pseudo-inverse can't + // handle that, so fall back to the old case + return svdPseudoInverse(); + + } else { + Matrix3 B3inv = B3.inverse(); + + // Multiply by Binv * A' + Matrix X(cols(), rows()); + + T** Xelt = X.impl->elt; + T** Aelt = A.impl->elt; + for (int i = 0; i < n; ++i) { + T* Xrow = Xelt[i]; + for (int j = 0; j < m; ++j) { + const T* Arow = Aelt[j]; + T sum = 0; + const float* Binvrow = B3inv[i]; + for (int k = 0; k < n; ++k) { + sum += Binvrow[k] * Arow[k]; + } + Xrow[j] = sum; + } + } + return X; + } +} + +Matrix Matrix::col4PseudoInverse(const Matrix& B) const { + Matrix A = *this; + int m = rows(); + int n = cols(); + + Matrix4 B4 = B.toMatrix4(); + if (fuzzyEq(B4.determinant(), (T)0.0)) { + + // Matrix was singular; the block matrix pseudo-inverse can't + // handle that, so fall back to the old case + return svdPseudoInverse(); + + } else { + Matrix4 B4inv = B4.inverse(); + + // Multiply by Binv * A' + Matrix X(cols(), rows()); + + T** Xelt = X.impl->elt; + T** Aelt = A.impl->elt; + for (int i = 0; i < n; ++i) { + T* Xrow = Xelt[i]; + for (int j = 0; j < m; ++j) { + const T* Arow = Aelt[j]; + T sum = 0; + const float* Binvrow = B4inv[i]; + for (int k = 0; k < n; ++k) { + sum += Binvrow[k] * Arow[k]; + } + Xrow[j] = sum; + } + } + return X; + } +} + +Matrix Matrix::row2PseudoInverse(const Matrix& B) const { + + Matrix A = *this; + int m = rows(); + int n = cols(); + (void)m; + + // Row-major 2x2 matrix + const float B2[2][2] = + {{B.get(0,0), B.get(0,1)}, + {B.get(1,0), B.get(1,1)}}; + + float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]); + + if (fuzzyEq(det, T(0))) { + + // Matrix was singular; the block matrix pseudo-inverse can't + // handle that, so fall back to the old case + return svdPseudoInverse(); + + } else { + // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html + + // Multiply by Binv * A' + Matrix X(cols(), rows()); + + T** Xelt = X.impl->elt; + T** Aelt = A.impl->elt; + float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det; + float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det; + for (int j = 0; j < n; ++j) { + Xelt[j][0] = Aelt[0][j] * binv00 + Aelt[1][j] * binv10; + Xelt[j][1] = Aelt[0][j] * binv01 + Aelt[1][j] * binv11; + } + return X; + } +} + +Matrix Matrix::row3PseudoInverse(const Matrix& B) const { + + Matrix A = *this; + int m = rows(); + int n = cols(); + + Matrix3 B3 = B.toMatrix3(); + if (fuzzyEq(B3.determinant(), (T)0.0)) { + + // Matrix was singular; the block matrix pseudo-inverse can't + // handle that, so fall back to the old case + return svdPseudoInverse(); + + } else { + Matrix3 B3inv = B3.inverse(); + + // Multiply by Binv * A' + Matrix X(cols(), rows()); + + T** Xelt = X.impl->elt; + T** Aelt = A.impl->elt; + for (int i = 0; i < n; ++i) { + T* Xrow = Xelt[i]; + for (int j = 0; j < m; ++j) { + T sum = 0; + for (int k = 0; k < m; ++k) { + sum += Aelt[k][i] * B3inv[j][k]; + } + Xrow[j] = sum; + } + } + return X; + } +} + +Matrix Matrix::row4PseudoInverse(const Matrix& B) const { + + Matrix A = *this; + int m = rows(); + int n = cols(); + + Matrix4 B4 = B.toMatrix4(); + if (fuzzyEq(B4.determinant(), (T)0.0)) { + + // Matrix was singular; the block matrix pseudo-inverse can't + // handle that, so fall back to the old case + return svdPseudoInverse(); + + } else { + Matrix4 B4inv = B4.inverse(); + + // Multiply by Binv * A' + Matrix X(cols(), rows()); + + T** Xelt = X.impl->elt; + T** Aelt = A.impl->elt; + for (int i = 0; i < n; ++i) { + T* Xrow = Xelt[i]; + for (int j = 0; j < m; ++j) { + T sum = 0; + for (int k = 0; k < m; ++k) { + sum += Aelt[k][i] * B4inv[j][k]; + } + Xrow[j] = sum; + } + } + return X; + } +} + +// Uses the block matrix pseudoinverse to compute the pseudoinverse of a full-rank mxn matrix with m >= n +// http://en.wikipedia.org/wiki/Block_matrix_pseudoinverse +Matrix Matrix::partitionPseudoInverse() const { + + // Logic: + // A^-1 = (A'A)^-1 A' + // A has few (n) columns, so A'A is small (n x n) and fast to invert + + int m = rows(); + int n = cols(); + + if (m < n) { + // TODO: optimize by pushing through the transpose + //return transpose().partitionPseudoInverse().transpose(); + return rowPartPseudoInverse(); + + } else { + return colPartPseudoInverse(); + } +} + +void Matrix::Impl::inverseInPlaceGaussJordan() { + debugAssertM(R == C, + format( + "Cannot perform Gauss-Jordan inverse on a non-square matrix." + " (Argument was %dx%d)", + R, C)); + + // Exchange to float elements +# define SWAP(x, y) {float temp = x; x = y; y = temp;} + + // The integer arrays pivot, rowIndex, and colIndex are + // used for bookkeeping on the pivoting + static Array<int> colIndex, rowIndex, pivot; + + int col = 0, row = 0; + + colIndex.resize(R); + rowIndex.resize(R); + pivot.resize(R); + + static const int NO_PIVOT = -1; + + // Initialize the pivot array to default values. + for (int i = 0; i < R; ++i) { + pivot[i] = NO_PIVOT; + } + + // This is the main loop over the columns to be reduced + // Loop over the columns. + for (int c = 0; c < R; ++c) { + + // Find the largest element and use that as a pivot + float largestMagnitude = 0.0; + + // This is the outer loop of the search for a pivot element + for (int r = 0; r < R; ++r) { + + // Unless we've already found the pivot, keep going + if (pivot[r] != 0) { + + // Find the largest pivot + for (int k = 0; k < R; ++k) { + if (pivot[k] == NO_PIVOT) { + const float mag = fabs(elt[r][k]); + + if (mag >= largestMagnitude) { + largestMagnitude = mag; + row = r; col = k; + } + } + } + } + } + + pivot[col] += 1; + + // Interchange columns so that the pivot element is on the diagonal (we'll have to undo this + // at the end) + if (row != col) { + for (int k = 0; k < R; ++k) { + SWAP(elt[row][k], elt[col][k]) + } + } + + // The pivot is now at [row, col] + rowIndex[c] = row; + colIndex[c] = col; + + double piv = elt[col][col]; + + debugAssertM(piv != 0.0, "Matrix is singular"); + + // Divide everything by the pivot (avoid computing the division + // multiple times). + const double pivotInverse = 1.0 / piv; + elt[col][col] = 1.0; + + for (int k = 0; k < R; ++k) { + elt[col][k] *= Matrix::T(pivotInverse); + } + + // Reduce all rows + for (int r = 0; r < R; ++r) { + // Skip over the pivot row + if (r != col) { + + double oldValue = elt[r][col]; + elt[r][col] = 0.0; + + for (int k = 0; k < R; ++k) { + elt[r][k] -= Matrix::T(elt[col][k] * oldValue); + } + } + } + } + + + // Put the columns back in the correct locations + for (int i = R - 1; i >= 0; --i) { + if (rowIndex[i] != colIndex[i]) { + for (int k = 0; k < R; ++k) { + SWAP(elt[k][rowIndex[i]], elt[k][colIndex[i]]); + } + } + } + +# undef SWAP +} + + +bool Matrix::Impl::anyNonZero() const { + int N = R * C; + for (int i = 0; i < N; ++i) { + if (data[i] != 0.0) { + return true; + } + } + return false; +} + + +bool Matrix::Impl::allNonZero() const { + int N = R * C; + for (int i = 0; i < N; ++i) { + if (data[i] == 0.0) { + return false; + } + } + return true; +} + + +/** Helper for svdCore */ +static double pythag(double a, double b) { + + double at = fabs(a), bt = fabs(b), ct, result; + + if (at > bt) { + ct = bt / at; + result = at * sqrt(1.0 + square(ct)); + } else if (bt > 0.0) { + ct = at / bt; + result = bt * sqrt(1.0 + square(ct)); + } else { + result = 0.0; + } + + return result; +} + +#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) + +const char* Matrix::svdCore(float** U, int rows, int cols, float* D, float** V) { + const int MAX_ITERATIONS = 30; + + int flag, i, its, j, jj, k, l = 0, nm = 0; + double c, f, h, s, x, y, z; + double anorm = 0.0, g = 0.0, scale = 0.0; + + // Temp row vector + double* rv1; + + debugAssertM(rows >= cols, "Must have more rows than columns"); + + rv1 = (double*)System::alignedMalloc(cols * sizeof(double), 16); + debugAssert(rv1); + + // Householder reduction to bidiagonal form + for (i = 0; i < cols; ++i) { + + // Left-hand reduction + l = i + 1; + rv1[i] = scale * g; + g = s = scale = 0.0; + + if (i < rows) { + + for (k = i; k < rows; ++k) { + scale += fabs((double)U[k][i]); + } + + if (scale) { + for (k = i; k < rows; ++k) { + U[k][i] = (float)((double)U[k][i]/scale); + s += ((double)U[k][i] * (double)U[k][i]); + } + + f = (double)U[i][i]; + + // TODO: what is this 2-arg sign function? + g = -SIGN(sqrt(s), f); + h = f * g - s; + U[i][i] = (float)(f - g); + + if (i != cols - 1) { + for (j = l; j < cols; j++) { + + for (s = 0.0, k = i; k < rows; ++k) { + s += ((double)U[k][i] * (double)U[k][j]); + } + + f = s / h; + for (k = i; k < rows; ++k) { + U[k][j] += (float)(f * (double)U[k][i]); + } + } + } + for (k = i; k < rows; ++k) { + U[k][i] = (float)((double)U[k][i]*scale); + } + } + } + D[i] = (float)(scale * g); + + // right-hand reduction + g = s = scale = 0.0; + if (i < rows && i != cols - 1) { + for (k = l; k < cols; ++k) { + scale += fabs((double)U[i][k]); + } + + if (scale) { + for (k = l; k < cols; ++k) { + U[i][k] = (float)((double)U[i][k]/scale); + s += ((double)U[i][k] * (double)U[i][k]); + } + + f = (double)U[i][l]; + g = -SIGN(sqrt(s), f); + h = f * g - s; + U[i][l] = (float)(f - g); + + for (k = l; k < cols; ++k) { + rv1[k] = (double)U[i][k] / h; + } + + if (i != rows - 1) { + + for (j = l; j < rows; ++j) { + for (s = 0.0, k = l; k < cols; ++k) { + s += ((double)U[j][k] * (double)U[i][k]); + } + + for (k = l; k < cols; ++k) { + U[j][k] += (float)(s * rv1[k]); + } + } + } + + for (k = l; k < cols; ++k) { + U[i][k] = (float)((double)U[i][k]*scale); + } + } + } + + anorm = max(anorm, fabs((double)D[i]) + fabs(rv1[i])); + } + + // accumulate the right-hand transformation + for (i = cols - 1; i >= 0; --i) { + if (i < cols - 1) { + if (g) { + for (j = l; j < cols; j++) { + V[j][i] = (float)(((double)U[i][j] / (double)U[i][l]) / g); + } + + // double division to avoid underflow + for (j = l; j < cols; ++j) { + for (s = 0.0, k = l; k < cols; k++) { + s += ((double)U[i][k] * (double)V[k][j]); + } + + for (k = l; k < cols; ++k) { + V[k][j] += (float)(s * (double)V[k][i]); + } + } + } + + for (j = l; j < cols; ++j) { + V[i][j] = V[j][i] = 0.0; + } + } + + V[i][i] = 1.0; + g = rv1[i]; + l = i; + } + + // accumulate the left-hand transformation + for (i = cols - 1; i >= 0; --i) { + l = i + 1; + g = (double)D[i]; + if (i < cols - 1) { + for (j = l; j < cols; ++j) { + U[i][j] = 0.0; + } + } + + if (g) { + g = 1.0 / g; + if (i != cols - 1) { + for (j = l; j < cols; ++j) { + for (s = 0.0, k = l; k < rows; ++k) { + s += ((double)U[k][i] * (double)U[k][j]); + } + + f = (s / (double)U[i][i]) * g; + + for (k = i; k < rows; ++k) { + U[k][j] += (float)(f * (double)U[k][i]); + } + } + } + + for (j = i; j < rows; ++j) { + U[j][i] = (float)((double)U[j][i]*g); + } + + } else { + for (j = i; j < rows; ++j) { + U[j][i] = 0.0; + } + } + ++U[i][i]; + } + + // diagonalize the bidiagonal form + for (k = cols - 1; k >= 0; --k) { + // loop over singular values + for (its = 0; its < MAX_ITERATIONS; ++its) { + // loop over allowed iterations + flag = 1; + + for (l = k; l >= 0; --l) { + // test for splitting + nm = l - 1; + if (fabs(rv1[l]) + anorm == anorm) { + flag = 0; + break; + } + + if (fabs((double)D[nm]) + anorm == anorm) { + break; + } + } + + if (flag) { + c = 0.0; + s = 1.0; + for (i = l; i <= k; ++i) { + f = s * rv1[i]; + if (fabs(f) + anorm != anorm) { + g = (double)D[i]; + h = pythag(f, g); + D[i] = (float)h; + h = 1.0 / h; + c = g * h; + s = (- f * h); + for (j = 0; j < rows; ++j) { + y = (double)U[j][nm]; + z = (double)U[j][i]; + U[j][nm] = (float)(y * c + z * s); + U[j][i] = (float)(z * c - y * s); + } + } + } + } + + z = (double)D[k]; + if (l == k) { + // convergence + if (z < 0.0) { + // make singular value nonnegative + D[k] = (float)(-z); + + for (j = 0; j < cols; ++j) { + V[j][k] = (-V[j][k]); + } + } + break; + } + + if (its >= MAX_ITERATIONS) { + free(rv1); + rv1 = NULL; + return "Failed to converge."; + } + + // shift from bottom 2 x 2 minor + x = (double)D[l]; + nm = k - 1; + y = (double)D[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y); + g = pythag(f, 1.0); + f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; + + // next QR transformation + c = s = 1.0; + for (j = l; j <= nm; ++j) { + i = j + 1; + g = rv1[i]; + y = (double)D[i]; + h = s * g; + g = c * g; + z = pythag(f, h); + rv1[j] = z; + c = f / z; + s = h / z; + f = x * c + g * s; + g = g * c - x * s; + h = y * s; + y = y * c; + + for (jj = 0; jj < cols; ++jj) { + x = (double)V[jj][j]; + z = (double)V[jj][i]; + V[jj][j] = (float)(x * c + z * s); + V[jj][i] = (float)(z * c - x * s); + } + z = pythag(f, h); + D[j] = (float)z; + if (z) { + z = 1.0 / z; + c = f * z; + s = h * z; + } + f = (c * g) + (s * y); + x = (c * y) - (s * g); + for (jj = 0; jj < rows; jj++) { + y = (double)U[jj][j]; + z = (double)U[jj][i]; + U[jj][j] = (float)(y * c + z * s); + U[jj][i] = (float)(z * c - y * s); + } + } + rv1[l] = 0.0; + rv1[k] = f; + D[k] = (float)x; + } + } + + System::alignedFree(rv1); + rv1 = NULL; + + return NULL; +} + +#undef SIGN + +} diff --git a/externals/g3dlite/G3D.lib/source/Matrix3.cpp b/externals/g3dlite/G3D.lib/source/Matrix3.cpp new file mode 100644 index 00000000000..7fb6648d3f7 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Matrix3.cpp @@ -0,0 +1,1725 @@ +/** + @file Matrix3.cpp + + 3x3 matrix class + + @author Morgan McGuire, graphics3d.com + + @created 2001-06-02 + @edited 2006-04-06 +*/ + +#include "G3D/platform.h" +#include <memory.h> +#include <assert.h> +#include "G3D/Matrix3.h" +#include "G3D/g3dmath.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Quat.h" + +namespace G3D { + +const float Matrix3::EPSILON = 1e-06f; + +const Matrix3& Matrix3::zero() { + static Matrix3 m(0, 0, 0, 0, 0, 0, 0, 0, 0); + return m; +} + +const Matrix3& Matrix3::identity() { + static Matrix3 m(1, 0, 0, 0, 1, 0, 0, 0, 1); + return m; +} + + +const float Matrix3::ms_fSvdEpsilon = 1e-04f; +const int Matrix3::ms_iSvdMaxIterations = 32; + +Matrix3::Matrix3(BinaryInput& b) { + deserialize(b); +} + +bool Matrix3::fuzzyEq(const Matrix3& b) const { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + if (! G3D::fuzzyEq(elt[r][c], b[r][c])) { + return false; + } + } + } + return true; +} + + +bool Matrix3::isOrthonormal() const { + Vector3 X = column(0); + Vector3 Y = column(1); + Vector3 Z = column(2); + + return + (G3D::fuzzyEq(X.dot(Y), 0.0f) && + G3D::fuzzyEq(Y.dot(Z), 0.0f) && + G3D::fuzzyEq(X.dot(Z), 0.0f) && + G3D::fuzzyEq(X.squaredMagnitude(), 1.0f) && + G3D::fuzzyEq(Y.squaredMagnitude(), 1.0f) && + G3D::fuzzyEq(Z.squaredMagnitude(), 1.0f)); +} + +//---------------------------------------------------------------------------- +Matrix3::Matrix3(const Quat& _q) { + // Implementation from Watt and Watt, pg 362 + // See also http://www.flipcode.com/documents/matrfaq.html#Q54 + Quat q = _q; + q.unitize(); + float xx = 2.0f * q.x * q.x; + float xy = 2.0f * q.x * q.y; + float xz = 2.0f * q.x * q.z; + float xw = 2.0f * q.x * q.w; + + float yy = 2.0f * q.y * q.y; + float yz = 2.0f * q.y * q.z; + float yw = 2.0f * q.y * q.w; + + float zz = 2.0f * q.z * q.z; + float zw = 2.0f * q.z * q.w; + + set(1.0f - yy - zz, xy - zw, xz + yw, + xy + zw, 1.0f - xx - zz, yz - xw, + xz - yw, yz + xw, 1.0f - xx - yy); +} + +//---------------------------------------------------------------------------- + +Matrix3::Matrix3 (const float aafEntry[3][3]) { + memcpy(elt, aafEntry, 9*sizeof(float)); +} + +//---------------------------------------------------------------------------- +Matrix3::Matrix3 (const Matrix3& rkMatrix) { + memcpy(elt, rkMatrix.elt, 9*sizeof(float)); +} + +//---------------------------------------------------------------------------- +Matrix3::Matrix3( + float fEntry00, float fEntry01, float fEntry02, + float fEntry10, float fEntry11, float fEntry12, + float fEntry20, float fEntry21, float fEntry22) { + set(fEntry00, fEntry01, fEntry02, + fEntry10, fEntry11, fEntry12, + fEntry20, fEntry21, fEntry22); +} + +void Matrix3::set( + float fEntry00, float fEntry01, float fEntry02, + float fEntry10, float fEntry11, float fEntry12, + float fEntry20, float fEntry21, float fEntry22) { + + elt[0][0] = fEntry00; + elt[0][1] = fEntry01; + elt[0][2] = fEntry02; + elt[1][0] = fEntry10; + elt[1][1] = fEntry11; + elt[1][2] = fEntry12; + elt[2][0] = fEntry20; + elt[2][1] = fEntry21; + elt[2][2] = fEntry22; +} + + +void Matrix3::deserialize(BinaryInput& b) { + int r,c; + for (c = 0; c < 3; ++c) { + for (r = 0; r < 3; ++r) { + elt[r][c] = b.readFloat32(); + } + } +} + + +void Matrix3::serialize(BinaryOutput& b) const { + int r,c; + for (c = 0; c < 3; ++c) { + for (r = 0; r < 3; ++r) { + b.writeFloat32(elt[r][c]); + } + } +} + + +//---------------------------------------------------------------------------- +Vector3 Matrix3::column (int iCol) const { + assert((0 <= iCol) && (iCol < 3)); + return Vector3(elt[0][iCol], elt[1][iCol], + elt[2][iCol]); +} + +const Vector3& Matrix3::row (int iRow) const { + assert((0 <= iRow) && (iRow < 3)); + return *reinterpret_cast<const Vector3*>(elt[iRow]); +} + + +Vector3 Matrix3::getColumn (int iCol) const { + assert((0 <= iCol) && (iCol < 3)); + return Vector3(elt[0][iCol], elt[1][iCol], + elt[2][iCol]); +} + +Vector3 Matrix3::getRow (int iRow) const { + return Vector3(elt[iRow][0], elt[iRow][1], elt[iRow][2]); +} + +void Matrix3::setColumn(int iCol, const Vector3 &vector) { + debugAssert((iCol >= 0) && (iCol < 3)); + elt[0][iCol] = vector.x; + elt[1][iCol] = vector.y; + elt[2][iCol] = vector.z; +} + + +void Matrix3::setRow(int iRow, const Vector3 &vector) { + debugAssert((iRow >= 0) && (iRow < 3)); + elt[iRow][0] = vector.x; + elt[iRow][1] = vector.y; + elt[iRow][2] = vector.z; +} + + +//---------------------------------------------------------------------------- +bool Matrix3::operator== (const Matrix3& rkMatrix) const { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + if ( elt[iRow][iCol] != rkMatrix.elt[iRow][iCol] ) + return false; + } + } + + return true; +} + +//---------------------------------------------------------------------------- +bool Matrix3::operator!= (const Matrix3& rkMatrix) const { + return !operator==(rkMatrix); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator+ (const Matrix3& rkMatrix) const { + Matrix3 kSum; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kSum.elt[iRow][iCol] = elt[iRow][iCol] + + rkMatrix.elt[iRow][iCol]; + } + } + + return kSum; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator- (const Matrix3& rkMatrix) const { + Matrix3 kDiff; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kDiff.elt[iRow][iCol] = elt[iRow][iCol] - + rkMatrix.elt[iRow][iCol]; + } + } + + return kDiff; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator* (const Matrix3& rkMatrix) const { + Matrix3 kProd; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kProd.elt[iRow][iCol] = + elt[iRow][0] * rkMatrix.elt[0][iCol] + + elt[iRow][1] * rkMatrix.elt[1][iCol] + + elt[iRow][2] * rkMatrix.elt[2][iCol]; + } + } + + return kProd; +} + +Matrix3& Matrix3::operator+= (const Matrix3& rkMatrix) { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] = elt[iRow][iCol] + rkMatrix.elt[iRow][iCol]; + } + } + + return *this; +} + +Matrix3& Matrix3::operator-= (const Matrix3& rkMatrix) { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] = elt[iRow][iCol] - rkMatrix.elt[iRow][iCol]; + } + } + + return *this; +} + +Matrix3& Matrix3::operator*= (const Matrix3& rkMatrix) { + Matrix3 mulMat; + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + mulMat.elt[iRow][iCol] = + elt[iRow][0] * rkMatrix.elt[0][iCol] + + elt[iRow][1] * rkMatrix.elt[1][iCol] + + elt[iRow][2] * rkMatrix.elt[2][iCol]; + } + } + + *this = mulMat; + return *this; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator- () const { + Matrix3 kNeg; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kNeg[iRow][iCol] = -elt[iRow][iCol]; + } + } + + return kNeg; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator* (float fScalar) const { + Matrix3 kProd; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kProd[iRow][iCol] = fScalar * elt[iRow][iCol]; + } + } + + return kProd; +} + +//---------------------------------------------------------------------------- +Matrix3 operator* (double fScalar, const Matrix3& rkMatrix) { + Matrix3 kProd; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kProd[iRow][iCol] = fScalar * rkMatrix.elt[iRow][iCol]; + } + } + + return kProd; +} + +Matrix3 operator* (float fScalar, const Matrix3& rkMatrix) { + return (double)fScalar * rkMatrix; +} + + +Matrix3 operator* (int fScalar, const Matrix3& rkMatrix) { + return (double)fScalar * rkMatrix; +} +//---------------------------------------------------------------------------- +Matrix3 Matrix3::transpose () const { + Matrix3 kTranspose; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kTranspose[iRow][iCol] = elt[iCol][iRow]; + } + } + + return kTranspose; +} + +//---------------------------------------------------------------------------- +bool Matrix3::inverse (Matrix3& rkInverse, float fTolerance) const { + // Invert a 3x3 using cofactors. This is about 8 times faster than + // the Numerical Recipes code which uses Gaussian elimination. + + rkInverse[0][0] = elt[1][1] * elt[2][2] - + elt[1][2] * elt[2][1]; + rkInverse[0][1] = elt[0][2] * elt[2][1] - + elt[0][1] * elt[2][2]; + rkInverse[0][2] = elt[0][1] * elt[1][2] - + elt[0][2] * elt[1][1]; + rkInverse[1][0] = elt[1][2] * elt[2][0] - + elt[1][0] * elt[2][2]; + rkInverse[1][1] = elt[0][0] * elt[2][2] - + elt[0][2] * elt[2][0]; + rkInverse[1][2] = elt[0][2] * elt[1][0] - + elt[0][0] * elt[1][2]; + rkInverse[2][0] = elt[1][0] * elt[2][1] - + elt[1][1] * elt[2][0]; + rkInverse[2][1] = elt[0][1] * elt[2][0] - + elt[0][0] * elt[2][1]; + rkInverse[2][2] = elt[0][0] * elt[1][1] - + elt[0][1] * elt[1][0]; + + float fDet = + elt[0][0] * rkInverse[0][0] + + elt[0][1] * rkInverse[1][0] + + elt[0][2] * rkInverse[2][0]; + + if ( G3D::abs(fDet) <= fTolerance ) + return false; + + float fInvDet = 1.0 / fDet; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) + rkInverse[iRow][iCol] *= fInvDet; + } + + return true; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::inverse (float fTolerance) const { + Matrix3 kInverse = Matrix3::zero(); + inverse(kInverse, fTolerance); + return kInverse; +} + +//---------------------------------------------------------------------------- +float Matrix3::determinant () const { + float fCofactor00 = elt[1][1] * elt[2][2] - + elt[1][2] * elt[2][1]; + float fCofactor10 = elt[1][2] * elt[2][0] - + elt[1][0] * elt[2][2]; + float fCofactor20 = elt[1][0] * elt[2][1] - + elt[1][1] * elt[2][0]; + + float fDet = + elt[0][0] * fCofactor00 + + elt[0][1] * fCofactor10 + + elt[0][2] * fCofactor20; + + return fDet; +} + +//---------------------------------------------------------------------------- +void Matrix3::bidiagonalize (Matrix3& kA, Matrix3& kL, + Matrix3& kR) { + float afV[3], afW[3]; + float fLength, fSign, fT1, fInvT1, fT2; + bool bIdentity; + + // map first column to (*,0,0) + fLength = sqrt(kA[0][0] * kA[0][0] + kA[1][0] * kA[1][0] + + kA[2][0] * kA[2][0]); + + if ( fLength > 0.0 ) { + fSign = (kA[0][0] > 0.0 ? 1.0 : -1.0); + fT1 = kA[0][0] + fSign * fLength; + fInvT1 = 1.0 / fT1; + afV[1] = kA[1][0] * fInvT1; + afV[2] = kA[2][0] * fInvT1; + + fT2 = -2.0 / (1.0 + afV[1] * afV[1] + afV[2] * afV[2]); + afW[0] = fT2 * (kA[0][0] + kA[1][0] * afV[1] + kA[2][0] * afV[2]); + afW[1] = fT2 * (kA[0][1] + kA[1][1] * afV[1] + kA[2][1] * afV[2]); + afW[2] = fT2 * (kA[0][2] + kA[1][2] * afV[1] + kA[2][2] * afV[2]); + kA[0][0] += afW[0]; + kA[0][1] += afW[1]; + kA[0][2] += afW[2]; + kA[1][1] += afV[1] * afW[1]; + kA[1][2] += afV[1] * afW[2]; + kA[2][1] += afV[2] * afW[1]; + kA[2][2] += afV[2] * afW[2]; + + kL[0][0] = 1.0 + fT2; + kL[0][1] = kL[1][0] = fT2 * afV[1]; + kL[0][2] = kL[2][0] = fT2 * afV[2]; + kL[1][1] = 1.0 + fT2 * afV[1] * afV[1]; + kL[1][2] = kL[2][1] = fT2 * afV[1] * afV[2]; + kL[2][2] = 1.0 + fT2 * afV[2] * afV[2]; + bIdentity = false; + } else { + kL = Matrix3::identity(); + bIdentity = true; + } + + // map first row to (*,*,0) + fLength = sqrt(kA[0][1] * kA[0][1] + kA[0][2] * kA[0][2]); + + if ( fLength > 0.0 ) { + fSign = (kA[0][1] > 0.0 ? 1.0 : -1.0); + fT1 = kA[0][1] + fSign * fLength; + afV[2] = kA[0][2] / fT1; + + fT2 = -2.0 / (1.0 + afV[2] * afV[2]); + afW[0] = fT2 * (kA[0][1] + kA[0][2] * afV[2]); + afW[1] = fT2 * (kA[1][1] + kA[1][2] * afV[2]); + afW[2] = fT2 * (kA[2][1] + kA[2][2] * afV[2]); + kA[0][1] += afW[0]; + kA[1][1] += afW[1]; + kA[1][2] += afW[1] * afV[2]; + kA[2][1] += afW[2]; + kA[2][2] += afW[2] * afV[2]; + + kR[0][0] = 1.0; + kR[0][1] = kR[1][0] = 0.0; + kR[0][2] = kR[2][0] = 0.0; + kR[1][1] = 1.0 + fT2; + kR[1][2] = kR[2][1] = fT2 * afV[2]; + kR[2][2] = 1.0 + fT2 * afV[2] * afV[2]; + } else { + kR = Matrix3::identity(); + } + + // map second column to (*,*,0) + fLength = sqrt(kA[1][1] * kA[1][1] + kA[2][1] * kA[2][1]); + + if ( fLength > 0.0 ) { + fSign = (kA[1][1] > 0.0 ? 1.0 : -1.0); + fT1 = kA[1][1] + fSign * fLength; + afV[2] = kA[2][1] / fT1; + + fT2 = -2.0 / (1.0 + afV[2] * afV[2]); + afW[1] = fT2 * (kA[1][1] + kA[2][1] * afV[2]); + afW[2] = fT2 * (kA[1][2] + kA[2][2] * afV[2]); + kA[1][1] += afW[1]; + kA[1][2] += afW[2]; + kA[2][2] += afV[2] * afW[2]; + + float fA = 1.0 + fT2; + float fB = fT2 * afV[2]; + float fC = 1.0 + fB * afV[2]; + + if ( bIdentity ) { + kL[0][0] = 1.0; + kL[0][1] = kL[1][0] = 0.0; + kL[0][2] = kL[2][0] = 0.0; + kL[1][1] = fA; + kL[1][2] = kL[2][1] = fB; + kL[2][2] = fC; + } else { + for (int iRow = 0; iRow < 3; iRow++) { + float fTmp0 = kL[iRow][1]; + float fTmp1 = kL[iRow][2]; + kL[iRow][1] = fA * fTmp0 + fB * fTmp1; + kL[iRow][2] = fB * fTmp0 + fC * fTmp1; + } + } + } +} + +//---------------------------------------------------------------------------- +void Matrix3::golubKahanStep (Matrix3& kA, Matrix3& kL, + Matrix3& kR) { + float fT11 = kA[0][1] * kA[0][1] + kA[1][1] * kA[1][1]; + float fT22 = kA[1][2] * kA[1][2] + kA[2][2] * kA[2][2]; + float fT12 = kA[1][1] * kA[1][2]; + float fTrace = fT11 + fT22; + float fDiff = fT11 - fT22; + float fDiscr = sqrt(fDiff * fDiff + 4.0 * fT12 * fT12); + float fRoot1 = 0.5 * (fTrace + fDiscr); + float fRoot2 = 0.5 * (fTrace - fDiscr); + + // adjust right + float fY = kA[0][0] - (G3D::abs(fRoot1 - fT22) <= + G3D::abs(fRoot2 - fT22) ? fRoot1 : fRoot2); + float fZ = kA[0][1]; + float fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + float fSin = fZ * fInvLength; + float fCos = -fY * fInvLength; + + float fTmp0 = kA[0][0]; + float fTmp1 = kA[0][1]; + kA[0][0] = fCos * fTmp0 - fSin * fTmp1; + kA[0][1] = fSin * fTmp0 + fCos * fTmp1; + kA[1][0] = -fSin * kA[1][1]; + kA[1][1] *= fCos; + + int iRow; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[0][iRow]; + fTmp1 = kR[1][iRow]; + kR[0][iRow] = fCos * fTmp0 - fSin * fTmp1; + kR[1][iRow] = fSin * fTmp0 + fCos * fTmp1; + } + + // adjust left + fY = kA[0][0]; + + fZ = kA[1][0]; + + fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + + fSin = fZ * fInvLength; + + fCos = -fY * fInvLength; + + kA[0][0] = fCos * kA[0][0] - fSin * kA[1][0]; + + fTmp0 = kA[0][1]; + + fTmp1 = kA[1][1]; + + kA[0][1] = fCos * fTmp0 - fSin * fTmp1; + + kA[1][1] = fSin * fTmp0 + fCos * fTmp1; + + kA[0][2] = -fSin * kA[1][2]; + + kA[1][2] *= fCos; + + int iCol; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][0]; + fTmp1 = kL[iCol][1]; + kL[iCol][0] = fCos * fTmp0 - fSin * fTmp1; + kL[iCol][1] = fSin * fTmp0 + fCos * fTmp1; + } + + // adjust right + fY = kA[0][1]; + + fZ = kA[0][2]; + + fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + + fSin = fZ * fInvLength; + + fCos = -fY * fInvLength; + + kA[0][1] = fCos * kA[0][1] - fSin * kA[0][2]; + + fTmp0 = kA[1][1]; + + fTmp1 = kA[1][2]; + + kA[1][1] = fCos * fTmp0 - fSin * fTmp1; + + kA[1][2] = fSin * fTmp0 + fCos * fTmp1; + + kA[2][1] = -fSin * kA[2][2]; + + kA[2][2] *= fCos; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[1][iRow]; + fTmp1 = kR[2][iRow]; + kR[1][iRow] = fCos * fTmp0 - fSin * fTmp1; + kR[2][iRow] = fSin * fTmp0 + fCos * fTmp1; + } + + // adjust left + fY = kA[1][1]; + + fZ = kA[2][1]; + + fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + + fSin = fZ * fInvLength; + + fCos = -fY * fInvLength; + + kA[1][1] = fCos * kA[1][1] - fSin * kA[2][1]; + + fTmp0 = kA[1][2]; + + fTmp1 = kA[2][2]; + + kA[1][2] = fCos * fTmp0 - fSin * fTmp1; + + kA[2][2] = fSin * fTmp0 + fCos * fTmp1; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][1]; + fTmp1 = kL[iCol][2]; + kL[iCol][1] = fCos * fTmp0 - fSin * fTmp1; + kL[iCol][2] = fSin * fTmp0 + fCos * fTmp1; + } +} + +//---------------------------------------------------------------------------- +void Matrix3::singularValueDecomposition (Matrix3& kL, Vector3& kS, + Matrix3& kR) const { + int iRow, iCol; + + Matrix3 kA = *this; + bidiagonalize(kA, kL, kR); + + for (int i = 0; i < ms_iSvdMaxIterations; i++) { + float fTmp, fTmp0, fTmp1; + float fSin0, fCos0, fTan0; + float fSin1, fCos1, fTan1; + + bool bTest1 = (G3D::abs(kA[0][1]) <= + ms_fSvdEpsilon * (G3D::abs(kA[0][0]) + G3D::abs(kA[1][1]))); + bool bTest2 = (G3D::abs(kA[1][2]) <= + ms_fSvdEpsilon * (G3D::abs(kA[1][1]) + G3D::abs(kA[2][2]))); + + if ( bTest1 ) { + if ( bTest2 ) { + kS[0] = kA[0][0]; + kS[1] = kA[1][1]; + kS[2] = kA[2][2]; + break; + } else { + // 2x2 closed form factorization + fTmp = (kA[1][1] * kA[1][1] - kA[2][2] * kA[2][2] + + kA[1][2] * kA[1][2]) / (kA[1][2] * kA[2][2]); + fTan0 = 0.5 * (fTmp + sqrt(fTmp * fTmp + 4.0)); + fCos0 = 1.0 / sqrt(1.0 + fTan0 * fTan0); + fSin0 = fTan0 * fCos0; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][1]; + fTmp1 = kL[iCol][2]; + kL[iCol][1] = fCos0 * fTmp0 - fSin0 * fTmp1; + kL[iCol][2] = fSin0 * fTmp0 + fCos0 * fTmp1; + } + + fTan1 = (kA[1][2] - kA[2][2] * fTan0) / kA[1][1]; + fCos1 = 1.0 / sqrt(1.0 + fTan1 * fTan1); + fSin1 = -fTan1 * fCos1; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[1][iRow]; + fTmp1 = kR[2][iRow]; + kR[1][iRow] = fCos1 * fTmp0 - fSin1 * fTmp1; + kR[2][iRow] = fSin1 * fTmp0 + fCos1 * fTmp1; + } + + kS[0] = kA[0][0]; + kS[1] = fCos0 * fCos1 * kA[1][1] - + fSin1 * (fCos0 * kA[1][2] - fSin0 * kA[2][2]); + kS[2] = fSin0 * fSin1 * kA[1][1] + + fCos1 * (fSin0 * kA[1][2] + fCos0 * kA[2][2]); + break; + } + } else { + if ( bTest2 ) { + // 2x2 closed form factorization + fTmp = (kA[0][0] * kA[0][0] + kA[1][1] * kA[1][1] - + kA[0][1] * kA[0][1]) / (kA[0][1] * kA[1][1]); + fTan0 = 0.5 * ( -fTmp + sqrt(fTmp * fTmp + 4.0)); + fCos0 = 1.0 / sqrt(1.0 + fTan0 * fTan0); + fSin0 = fTan0 * fCos0; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][0]; + fTmp1 = kL[iCol][1]; + kL[iCol][0] = fCos0 * fTmp0 - fSin0 * fTmp1; + kL[iCol][1] = fSin0 * fTmp0 + fCos0 * fTmp1; + } + + fTan1 = (kA[0][1] - kA[1][1] * fTan0) / kA[0][0]; + fCos1 = 1.0 / sqrt(1.0 + fTan1 * fTan1); + fSin1 = -fTan1 * fCos1; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[0][iRow]; + fTmp1 = kR[1][iRow]; + kR[0][iRow] = fCos1 * fTmp0 - fSin1 * fTmp1; + kR[1][iRow] = fSin1 * fTmp0 + fCos1 * fTmp1; + } + + kS[0] = fCos0 * fCos1 * kA[0][0] - + fSin1 * (fCos0 * kA[0][1] - fSin0 * kA[1][1]); + kS[1] = fSin0 * fSin1 * kA[0][0] + + fCos1 * (fSin0 * kA[0][1] + fCos0 * kA[1][1]); + kS[2] = kA[2][2]; + break; + } else { + golubKahanStep(kA, kL, kR); + } + } + } + + // positize diagonal + for (iRow = 0; iRow < 3; iRow++) { + if ( kS[iRow] < 0.0 ) { + kS[iRow] = -kS[iRow]; + + for (iCol = 0; iCol < 3; iCol++) + kR[iRow][iCol] = -kR[iRow][iCol]; + } + } +} + +//---------------------------------------------------------------------------- +void Matrix3::singularValueComposition (const Matrix3& kL, + const Vector3& kS, const Matrix3& kR) { + int iRow, iCol; + Matrix3 kTmp; + + // product S*R + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) + kTmp[iRow][iCol] = kS[iRow] * kR[iRow][iCol]; + } + + // product L*S*R + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] = 0.0; + + for (int iMid = 0; iMid < 3; iMid++) + elt[iRow][iCol] += kL[iRow][iMid] * kTmp[iMid][iCol]; + } + } +} + +//---------------------------------------------------------------------------- +void Matrix3::orthonormalize () { + // Algorithm uses Gram-Schmidt orthogonalization. If 'this' matrix is + // M = [m0|m1|m2], then orthonormal output matrix is Q = [q0|q1|q2], + // + // q0 = m0/|m0| + // q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0| + // q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1| + // + // where |V| indicates length of vector V and A*B indicates dot + // product of vectors A and B. + + // compute q0 + float fInvLength = 1.0 / sqrt(elt[0][0] * elt[0][0] + + elt[1][0] * elt[1][0] + + elt[2][0] * elt[2][0]); + + elt[0][0] *= fInvLength; + elt[1][0] *= fInvLength; + elt[2][0] *= fInvLength; + + // compute q1 + float fDot0 = + elt[0][0] * elt[0][1] + + elt[1][0] * elt[1][1] + + elt[2][0] * elt[2][1]; + + elt[0][1] -= fDot0 * elt[0][0]; + elt[1][1] -= fDot0 * elt[1][0]; + elt[2][1] -= fDot0 * elt[2][0]; + + fInvLength = 1.0 / sqrt(elt[0][1] * elt[0][1] + + elt[1][1] * elt[1][1] + + elt[2][1] * elt[2][1]); + + elt[0][1] *= fInvLength; + elt[1][1] *= fInvLength; + elt[2][1] *= fInvLength; + + // compute q2 + float fDot1 = + elt[0][1] * elt[0][2] + + elt[1][1] * elt[1][2] + + elt[2][1] * elt[2][2]; + + fDot0 = + elt[0][0] * elt[0][2] + + elt[1][0] * elt[1][2] + + elt[2][0] * elt[2][2]; + + elt[0][2] -= fDot0 * elt[0][0] + fDot1 * elt[0][1]; + elt[1][2] -= fDot0 * elt[1][0] + fDot1 * elt[1][1]; + elt[2][2] -= fDot0 * elt[2][0] + fDot1 * elt[2][1]; + + fInvLength = 1.0 / sqrt(elt[0][2] * elt[0][2] + + elt[1][2] * elt[1][2] + + elt[2][2] * elt[2][2]); + + elt[0][2] *= fInvLength; + elt[1][2] *= fInvLength; + elt[2][2] *= fInvLength; +} + +//---------------------------------------------------------------------------- +void Matrix3::qDUDecomposition (Matrix3& kQ, + Vector3& kD, Vector3& kU) const { + // Factor M = QR = QDU where Q is orthogonal, D is diagonal, + // and U is upper triangular with ones on its diagonal. Algorithm uses + // Gram-Schmidt orthogonalization (the QR algorithm). + // + // If M = [ m0 | m1 | m2 ] and Q = [ q0 | q1 | q2 ], then + // + // q0 = m0/|m0| + // q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0| + // q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1| + // + // where |V| indicates length of vector V and A*B indicates dot + // product of vectors A and B. The matrix R has entries + // + // r00 = q0*m0 r01 = q0*m1 r02 = q0*m2 + // r10 = 0 r11 = q1*m1 r12 = q1*m2 + // r20 = 0 r21 = 0 r22 = q2*m2 + // + // so D = diag(r00,r11,r22) and U has entries u01 = r01/r00, + // u02 = r02/r00, and u12 = r12/r11. + + // Q = rotation + // D = scaling + // U = shear + + // D stores the three diagonal entries r00, r11, r22 + // U stores the entries U[0] = u01, U[1] = u02, U[2] = u12 + + // build orthogonal matrix Q + float fInvLength = 1.0 / sqrt(elt[0][0] * elt[0][0] + + elt[1][0] * elt[1][0] + + elt[2][0] * elt[2][0]); + kQ[0][0] = elt[0][0] * fInvLength; + kQ[1][0] = elt[1][0] * fInvLength; + kQ[2][0] = elt[2][0] * fInvLength; + + float fDot = kQ[0][0] * elt[0][1] + kQ[1][0] * elt[1][1] + + kQ[2][0] * elt[2][1]; + kQ[0][1] = elt[0][1] - fDot * kQ[0][0]; + kQ[1][1] = elt[1][1] - fDot * kQ[1][0]; + kQ[2][1] = elt[2][1] - fDot * kQ[2][0]; + fInvLength = 1.0 / sqrt(kQ[0][1] * kQ[0][1] + kQ[1][1] * kQ[1][1] + + kQ[2][1] * kQ[2][1]); + kQ[0][1] *= fInvLength; + kQ[1][1] *= fInvLength; + kQ[2][1] *= fInvLength; + + fDot = kQ[0][0] * elt[0][2] + kQ[1][0] * elt[1][2] + + kQ[2][0] * elt[2][2]; + kQ[0][2] = elt[0][2] - fDot * kQ[0][0]; + kQ[1][2] = elt[1][2] - fDot * kQ[1][0]; + kQ[2][2] = elt[2][2] - fDot * kQ[2][0]; + fDot = kQ[0][1] * elt[0][2] + kQ[1][1] * elt[1][2] + + kQ[2][1] * elt[2][2]; + kQ[0][2] -= fDot * kQ[0][1]; + kQ[1][2] -= fDot * kQ[1][1]; + kQ[2][2] -= fDot * kQ[2][1]; + fInvLength = 1.0 / sqrt(kQ[0][2] * kQ[0][2] + kQ[1][2] * kQ[1][2] + + kQ[2][2] * kQ[2][2]); + kQ[0][2] *= fInvLength; + kQ[1][2] *= fInvLength; + kQ[2][2] *= fInvLength; + + // guarantee that orthogonal matrix has determinant 1 (no reflections) + float fDet = kQ[0][0] * kQ[1][1] * kQ[2][2] + kQ[0][1] * kQ[1][2] * kQ[2][0] + + kQ[0][2] * kQ[1][0] * kQ[2][1] - kQ[0][2] * kQ[1][1] * kQ[2][0] - + kQ[0][1] * kQ[1][0] * kQ[2][2] - kQ[0][0] * kQ[1][2] * kQ[2][1]; + + if ( fDet < 0.0 ) { + for (int iRow = 0; iRow < 3; iRow++) + for (int iCol = 0; iCol < 3; iCol++) + kQ[iRow][iCol] = -kQ[iRow][iCol]; + } + + // build "right" matrix R + Matrix3 kR; + + kR[0][0] = kQ[0][0] * elt[0][0] + kQ[1][0] * elt[1][0] + + kQ[2][0] * elt[2][0]; + + kR[0][1] = kQ[0][0] * elt[0][1] + kQ[1][0] * elt[1][1] + + kQ[2][0] * elt[2][1]; + + kR[1][1] = kQ[0][1] * elt[0][1] + kQ[1][1] * elt[1][1] + + kQ[2][1] * elt[2][1]; + + kR[0][2] = kQ[0][0] * elt[0][2] + kQ[1][0] * elt[1][2] + + kQ[2][0] * elt[2][2]; + + kR[1][2] = kQ[0][1] * elt[0][2] + kQ[1][1] * elt[1][2] + + kQ[2][1] * elt[2][2]; + + kR[2][2] = kQ[0][2] * elt[0][2] + kQ[1][2] * elt[1][2] + + kQ[2][2] * elt[2][2]; + + // the scaling component + kD[0] = kR[0][0]; + + kD[1] = kR[1][1]; + + kD[2] = kR[2][2]; + + // the shear component + float fInvD0 = 1.0 / kD[0]; + + kU[0] = kR[0][1] * fInvD0; + + kU[1] = kR[0][2] * fInvD0; + + kU[2] = kR[1][2] / kD[1]; +} + +//---------------------------------------------------------------------------- +float Matrix3::maxCubicRoot (float afCoeff[3]) { + // Spectral norm is for A^T*A, so characteristic polynomial + // P(x) = c[0]+c[1]*x+c[2]*x^2+x^3 has three positive float roots. + // This yields the assertions c[0] < 0 and c[2]*c[2] >= 3*c[1]. + + // quick out for uniform scale (triple root) + const float fOneThird = 1.0f / 3.0f; + const float fEpsilon = 1e-06f; + float fDiscr = afCoeff[2] * afCoeff[2] - 3.0f * afCoeff[1]; + + if ( fDiscr <= fEpsilon ) + return -fOneThird*afCoeff[2]; + + // Compute an upper bound on roots of P(x). This assumes that A^T*A + // has been scaled by its largest entry. + float fX = 1.0f; + + float fPoly = afCoeff[0] + fX * (afCoeff[1] + fX * (afCoeff[2] + fX)); + + if ( fPoly < 0.0f ) { + // uses a matrix norm to find an upper bound on maximum root + fX = G3D::abs(afCoeff[0]); + float fTmp = 1.0 + G3D::abs(afCoeff[1]); + + if ( fTmp > fX ) + fX = fTmp; + + fTmp = 1.0 + G3D::abs(afCoeff[2]); + + if ( fTmp > fX ) + fX = fTmp; + } + + // Newton's method to find root + float fTwoC2 = 2.0f * afCoeff[2]; + + for (int i = 0; i < 16; i++) { + fPoly = afCoeff[0] + fX * (afCoeff[1] + fX * (afCoeff[2] + fX)); + + if ( G3D::abs(fPoly) <= fEpsilon ) + return fX; + + float fDeriv = afCoeff[1] + fX * (fTwoC2 + 3.0f * fX); + + fX -= fPoly / fDeriv; + } + + return fX; +} + +//---------------------------------------------------------------------------- +float Matrix3::spectralNorm () const { + Matrix3 kP; + int iRow, iCol; + float fPmax = 0.0; + + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) { + kP[iRow][iCol] = 0.0; + + for (int iMid = 0; iMid < 3; iMid++) { + kP[iRow][iCol] += + elt[iMid][iRow] * elt[iMid][iCol]; + } + + if ( kP[iRow][iCol] > fPmax ) + fPmax = kP[iRow][iCol]; + } + } + + float fInvPmax = 1.0 / fPmax; + + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) + kP[iRow][iCol] *= fInvPmax; + } + + float afCoeff[3]; + afCoeff[0] = -(kP[0][0] * (kP[1][1] * kP[2][2] - kP[1][2] * kP[2][1]) + + kP[0][1] * (kP[2][0] * kP[1][2] - kP[1][0] * kP[2][2]) + + kP[0][2] * (kP[1][0] * kP[2][1] - kP[2][0] * kP[1][1])); + afCoeff[1] = kP[0][0] * kP[1][1] - kP[0][1] * kP[1][0] + + kP[0][0] * kP[2][2] - kP[0][2] * kP[2][0] + + kP[1][1] * kP[2][2] - kP[1][2] * kP[2][1]; + afCoeff[2] = -(kP[0][0] + kP[1][1] + kP[2][2]); + + float fRoot = maxCubicRoot(afCoeff); + float fNorm = sqrt(fPmax * fRoot); + return fNorm; +} + +//---------------------------------------------------------------------------- +void Matrix3::toAxisAngle (Vector3& rkAxis, float& rfRadians) const { + // + // Let (x,y,z) be the unit-length axis and let A be an angle of rotation. + // The rotation matrix is R = I + sin(A)*P + (1-cos(A))*P^2 (Rodrigues' formula) where + // I is the identity and + // + // +- -+ + // P = | 0 -z +y | + // | +z 0 -x | + // | -y +x 0 | + // +- -+ + // + // If A > 0, R represents a counterclockwise rotation about the axis in + // the sense of looking from the tip of the axis vector towards the + // origin. Some algebra will show that + // + // cos(A) = (trace(R)-1)/2 and R - R^t = 2*sin(A)*P + // + // In the event that A = pi, R-R^t = 0 which prevents us from extracting + // the axis through P. Instead note that R = I+2*P^2 when A = pi, so + // P^2 = (R-I)/2. The diagonal entries of P^2 are x^2-1, y^2-1, and + // z^2-1. We can solve these for axis (x,y,z). Because the angle is pi, + // it does not matter which sign you choose on the square roots. + + float fTrace = elt[0][0] + elt[1][1] + elt[2][2]; + float fCos = 0.5f * (fTrace - 1.0f); + rfRadians = G3D::aCos(fCos); // in [0,PI] + + if ( rfRadians > 0.0 ) { + if ( rfRadians < pi() ) { + rkAxis.x = elt[2][1] - elt[1][2]; + rkAxis.y = elt[0][2] - elt[2][0]; + rkAxis.z = elt[1][0] - elt[0][1]; + rkAxis.unitize(); + } else { + // angle is PI + float fHalfInverse; + + if ( elt[0][0] >= elt[1][1] ) { + // r00 >= r11 + if ( elt[0][0] >= elt[2][2] ) { + // r00 is maximum diagonal term + rkAxis.x = 0.5 * sqrt(elt[0][0] - + elt[1][1] - elt[2][2] + 1.0); + fHalfInverse = 0.5 / rkAxis.x; + rkAxis.y = fHalfInverse * elt[0][1]; + rkAxis.z = fHalfInverse * elt[0][2]; + } else { + // r22 is maximum diagonal term + rkAxis.z = 0.5 * sqrt(elt[2][2] - + elt[0][0] - elt[1][1] + 1.0); + fHalfInverse = 0.5 / rkAxis.z; + rkAxis.x = fHalfInverse * elt[0][2]; + rkAxis.y = fHalfInverse * elt[1][2]; + } + } else { + // r11 > r00 + if ( elt[1][1] >= elt[2][2] ) { + // r11 is maximum diagonal term + rkAxis.y = 0.5 * sqrt(elt[1][1] - + elt[0][0] - elt[2][2] + 1.0); + fHalfInverse = 0.5 / rkAxis.y; + rkAxis.x = fHalfInverse * elt[0][1]; + rkAxis.z = fHalfInverse * elt[1][2]; + } else { + // r22 is maximum diagonal term + rkAxis.z = 0.5 * sqrt(elt[2][2] - + elt[0][0] - elt[1][1] + 1.0); + fHalfInverse = 0.5 / rkAxis.z; + rkAxis.x = fHalfInverse * elt[0][2]; + rkAxis.y = fHalfInverse * elt[1][2]; + } + } + } + } else { + // The angle is 0 and the matrix is the identity. Any axis will + // work, so just use the x-axis. + rkAxis.x = 1.0; + rkAxis.y = 0.0; + rkAxis.z = 0.0; + } +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromAxisAngle (const Vector3& _axis, float fRadians) { + Vector3 axis = _axis.direction(); + + Matrix3 m; + float fCos = cos(fRadians); + float fSin = sin(fRadians); + float fOneMinusCos = 1.0 - fCos; + float fX2 = square(axis.x); + float fY2 = square(axis.y); + float fZ2 = square(axis.z); + float fXYM = axis.x * axis.y * fOneMinusCos; + float fXZM = axis.x * axis.z * fOneMinusCos; + float fYZM = axis.y * axis.z * fOneMinusCos; + float fXSin = axis.x * fSin; + float fYSin = axis.y * fSin; + float fZSin = axis.z * fSin; + + m.elt[0][0] = fX2 * fOneMinusCos + fCos; + m.elt[0][1] = fXYM - fZSin; + m.elt[0][2] = fXZM + fYSin; + + m.elt[1][0] = fXYM + fZSin; + m.elt[1][1] = fY2 * fOneMinusCos + fCos; + m.elt[1][2] = fYZM - fXSin; + + m.elt[2][0] = fXZM - fYSin; + m.elt[2][1] = fYZM + fXSin; + m.elt[2][2] = fZ2 * fOneMinusCos + fCos; + + return m; +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesXYZ (float& rfXAngle, float& rfYAngle, + float& rfZAngle) const { + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + + if ( elt[0][2] < 1.0f ) { + if ( elt[0][2] > -1.0f ) { + rfXAngle = G3D::aTan2( -elt[1][2], elt[2][2]); + rfYAngle = (float) G3D::aSin(elt[0][2]); + rfZAngle = G3D::aTan2( -elt[0][1], elt[0][0]); + return true; + } else { + // WARNING. Not unique. XA - ZA = -atan2(r10,r11) + rfXAngle = -G3D::aTan2(elt[1][0], elt[1][1]); + rfYAngle = -(float)halfPi(); + rfZAngle = 0.0f; + return false; + } + } else { + // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11) + rfXAngle = G3D::aTan2(elt[1][0], elt[1][1]); + rfYAngle = (float)halfPi(); + rfZAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesXZY (float& rfXAngle, float& rfZAngle, + float& rfYAngle) const { + // rot = cy*cz -sz cz*sy + // sx*sy+cx*cy*sz cx*cz -cy*sx+cx*sy*sz + // -cx*sy+cy*sx*sz cz*sx cx*cy+sx*sy*sz + + if ( elt[0][1] < 1.0f ) { + if ( elt[0][1] > -1.0f ) { + rfXAngle = G3D::aTan2(elt[2][1], elt[1][1]); + rfZAngle = (float) asin( -elt[0][1]); + rfYAngle = G3D::aTan2(elt[0][2], elt[0][0]); + return true; + } else { + // WARNING. Not unique. XA - YA = atan2(r20,r22) + rfXAngle = G3D::aTan2(elt[2][0], elt[2][2]); + rfZAngle = (float)halfPi(); + rfYAngle = 0.0; + return false; + } + } else { + // WARNING. Not unique. XA + YA = atan2(-r20,r22) + rfXAngle = G3D::aTan2( -elt[2][0], elt[2][2]); + rfZAngle = -(float)halfPi(); + rfYAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesYXZ (float& rfYAngle, float& rfXAngle, + float& rfZAngle) const { + // rot = cy*cz+sx*sy*sz cz*sx*sy-cy*sz cx*sy + // cx*sz cx*cz -sx + // -cz*sy+cy*sx*sz cy*cz*sx+sy*sz cx*cy + + if ( elt[1][2] < 1.0 ) { + if ( elt[1][2] > -1.0 ) { + rfYAngle = G3D::aTan2(elt[0][2], elt[2][2]); + rfXAngle = (float) asin( -elt[1][2]); + rfZAngle = G3D::aTan2(elt[1][0], elt[1][1]); + return true; + } else { + // WARNING. Not unique. YA - ZA = atan2(r01,r00) + rfYAngle = G3D::aTan2(elt[0][1], elt[0][0]); + rfXAngle = (float)halfPi(); + rfZAngle = 0.0; + return false; + } + } else { + // WARNING. Not unique. YA + ZA = atan2(-r01,r00) + rfYAngle = G3D::aTan2( -elt[0][1], elt[0][0]); + rfXAngle = -(float)halfPi(); + rfZAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesYZX (float& rfYAngle, float& rfZAngle, + float& rfXAngle) const { + // rot = cy*cz sx*sy-cx*cy*sz cx*sy+cy*sx*sz + // sz cx*cz -cz*sx + // -cz*sy cy*sx+cx*sy*sz cx*cy-sx*sy*sz + + if ( elt[1][0] < 1.0 ) { + if ( elt[1][0] > -1.0 ) { + rfYAngle = G3D::aTan2( -elt[2][0], elt[0][0]); + rfZAngle = (float) asin(elt[1][0]); + rfXAngle = G3D::aTan2( -elt[1][2], elt[1][1]); + return true; + } else { + // WARNING. Not unique. YA - XA = -atan2(r21,r22); + rfYAngle = -G3D::aTan2(elt[2][1], elt[2][2]); + rfZAngle = -(float)halfPi(); + rfXAngle = 0.0; + return false; + } + } else { + // WARNING. Not unique. YA + XA = atan2(r21,r22) + rfYAngle = G3D::aTan2(elt[2][1], elt[2][2]); + rfZAngle = (float)halfPi(); + rfXAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesZXY (float& rfZAngle, float& rfXAngle, + float& rfYAngle) const { + // rot = cy*cz-sx*sy*sz -cx*sz cz*sy+cy*sx*sz + // cz*sx*sy+cy*sz cx*cz -cy*cz*sx+sy*sz + // -cx*sy sx cx*cy + + if ( elt[2][1] < 1.0 ) { + if ( elt[2][1] > -1.0 ) { + rfZAngle = G3D::aTan2( -elt[0][1], elt[1][1]); + rfXAngle = (float) asin(elt[2][1]); + rfYAngle = G3D::aTan2( -elt[2][0], elt[2][2]); + return true; + } else { + // WARNING. Not unique. ZA - YA = -atan(r02,r00) + rfZAngle = -G3D::aTan2(elt[0][2], elt[0][0]); + rfXAngle = -(float)halfPi(); + rfYAngle = 0.0f; + return false; + } + } else { + // WARNING. Not unique. ZA + YA = atan2(r02,r00) + rfZAngle = G3D::aTan2(elt[0][2], elt[0][0]); + rfXAngle = (float)halfPi(); + rfYAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesZYX (float& rfZAngle, float& rfYAngle, + float& rfXAngle) const { + // rot = cy*cz cz*sx*sy-cx*sz cx*cz*sy+sx*sz + // cy*sz cx*cz+sx*sy*sz -cz*sx+cx*sy*sz + // -sy cy*sx cx*cy + + if ( elt[2][0] < 1.0 ) { + if ( elt[2][0] > -1.0 ) { + rfZAngle = atan2f(elt[1][0], elt[0][0]); + rfYAngle = asinf(-(double)elt[2][1]); + rfXAngle = atan2f(elt[2][1], elt[2][2]); + return true; + } else { + // WARNING. Not unique. ZA - XA = -atan2(r01,r02) + rfZAngle = -G3D::aTan2(elt[0][1], elt[0][2]); + rfYAngle = (float)halfPi(); + rfXAngle = 0.0f; + return false; + } + } else { + // WARNING. Not unique. ZA + XA = atan2(-r01,-r02) + rfZAngle = G3D::aTan2( -elt[0][1], -elt[0][2]); + rfYAngle = -(float)halfPi(); + rfXAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesXYZ (float fYAngle, float fPAngle, + float fRAngle) { + float fCos, fSin; + + fCos = cosf(fYAngle); + fSin = sinf(fYAngle); + Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0, fSin, fCos); + + fCos = cosf(fPAngle); + fSin = sinf(fPAngle); + Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos); + + fCos = cosf(fRAngle); + fSin = sinf(fRAngle); + Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f); + + return kXMat * (kYMat * kZMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesXZY (float fYAngle, float fPAngle, + float fRAngle) { + + float fCos, fSin; + + fCos = cosf(fYAngle); + fSin = sinf(fYAngle); + Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos); + + fCos = cosf(fPAngle); + fSin = sinf(fPAngle); + Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0); + + fCos = cosf(fRAngle); + fSin = sinf(fRAngle); + Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos); + + return kXMat * (kZMat * kYMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesYXZ( + float fYAngle, + float fPAngle, + float fRAngle) { + + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f); + + return kYMat * (kXMat * kZMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesYZX( + float fYAngle, + float fPAngle, + float fRAngle) { + + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos); + + return kYMat * (kZMat * kXMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesZXY (float fYAngle, float fPAngle, + float fRAngle) { + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos); + + return kZMat * (kXMat * kYMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesZYX (float fYAngle, float fPAngle, + float fRAngle) { + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos); + + return kZMat * (kYMat * kXMat); +} + +//---------------------------------------------------------------------------- +void Matrix3::tridiagonal (float afDiag[3], float afSubDiag[3]) { + // Householder reduction T = Q^t M Q + // Input: + // mat, symmetric 3x3 matrix M + // Output: + // mat, orthogonal matrix Q + // diag, diagonal entries of T + // subd, subdiagonal entries of T (T is symmetric) + + float fA = elt[0][0]; + float fB = elt[0][1]; + float fC = elt[0][2]; + float fD = elt[1][1]; + float fE = elt[1][2]; + float fF = elt[2][2]; + + afDiag[0] = fA; + afSubDiag[2] = 0.0; + + if ( G3D::abs(fC) >= EPSILON ) { + float fLength = sqrt(fB * fB + fC * fC); + float fInvLength = 1.0 / fLength; + fB *= fInvLength; + fC *= fInvLength; + float fQ = 2.0 * fB * fE + fC * (fF - fD); + afDiag[1] = fD + fC * fQ; + afDiag[2] = fF - fC * fQ; + afSubDiag[0] = fLength; + afSubDiag[1] = fE - fB * fQ; + elt[0][0] = 1.0; + elt[0][1] = 0.0; + elt[0][2] = 0.0; + elt[1][0] = 0.0; + elt[1][1] = fB; + elt[1][2] = fC; + elt[2][0] = 0.0; + elt[2][1] = fC; + elt[2][2] = -fB; + } else { + afDiag[1] = fD; + afDiag[2] = fF; + afSubDiag[0] = fB; + afSubDiag[1] = fE; + elt[0][0] = 1.0; + elt[0][1] = 0.0; + elt[0][2] = 0.0; + elt[1][0] = 0.0; + elt[1][1] = 1.0; + elt[1][2] = 0.0; + elt[2][0] = 0.0; + elt[2][1] = 0.0; + elt[2][2] = 1.0; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::qLAlgorithm (float afDiag[3], float afSubDiag[3]) { + // QL iteration with implicit shifting to reduce matrix from tridiagonal + // to diagonal + + for (int i0 = 0; i0 < 3; i0++) { + const int iMaxIter = 32; + int iIter; + + for (iIter = 0; iIter < iMaxIter; iIter++) { + int i1; + + for (i1 = i0; i1 <= 1; i1++) { + float fSum = G3D::abs(afDiag[i1]) + + G3D::abs(afDiag[i1 + 1]); + + if ( G3D::abs(afSubDiag[i1]) + fSum == fSum ) + break; + } + + if ( i1 == i0 ) + break; + + float fTmp0 = (afDiag[i0 + 1] - afDiag[i0]) / (2.0 * afSubDiag[i0]); + + float fTmp1 = sqrt(fTmp0 * fTmp0 + 1.0); + + if ( fTmp0 < 0.0 ) + fTmp0 = afDiag[i1] - afDiag[i0] + afSubDiag[i0] / (fTmp0 - fTmp1); + else + fTmp0 = afDiag[i1] - afDiag[i0] + afSubDiag[i0] / (fTmp0 + fTmp1); + + float fSin = 1.0; + + float fCos = 1.0; + + float fTmp2 = 0.0; + + for (int i2 = i1 - 1; i2 >= i0; i2--) { + float fTmp3 = fSin * afSubDiag[i2]; + float fTmp4 = fCos * afSubDiag[i2]; + + if (G3D::abs(fTmp3) >= G3D::abs(fTmp0)) { + fCos = fTmp0 / fTmp3; + fTmp1 = sqrt(fCos * fCos + 1.0); + afSubDiag[i2 + 1] = fTmp3 * fTmp1; + fSin = 1.0 / fTmp1; + fCos *= fSin; + } else { + fSin = fTmp3 / fTmp0; + fTmp1 = sqrt(fSin * fSin + 1.0); + afSubDiag[i2 + 1] = fTmp0 * fTmp1; + fCos = 1.0 / fTmp1; + fSin *= fCos; + } + + fTmp0 = afDiag[i2 + 1] - fTmp2; + fTmp1 = (afDiag[i2] - fTmp0) * fSin + 2.0 * fTmp4 * fCos; + fTmp2 = fSin * fTmp1; + afDiag[i2 + 1] = fTmp0 + fTmp2; + fTmp0 = fCos * fTmp1 - fTmp4; + + for (int iRow = 0; iRow < 3; iRow++) { + fTmp3 = elt[iRow][i2 + 1]; + elt[iRow][i2 + 1] = fSin * elt[iRow][i2] + + fCos * fTmp3; + elt[iRow][i2] = fCos * elt[iRow][i2] - + fSin * fTmp3; + } + } + + afDiag[i0] -= fTmp2; + afSubDiag[i0] = fTmp0; + afSubDiag[i1] = 0.0; + } + + if ( iIter == iMaxIter ) { + // should not get here under normal circumstances + return false; + } + } + + return true; +} + +//---------------------------------------------------------------------------- +void Matrix3::eigenSolveSymmetric (float afEigenvalue[3], + Vector3 akEigenvector[3]) const { + Matrix3 kMatrix = *this; + float afSubDiag[3]; + kMatrix.tridiagonal(afEigenvalue, afSubDiag); + kMatrix.qLAlgorithm(afEigenvalue, afSubDiag); + + for (int i = 0; i < 3; i++) { + akEigenvector[i][0] = kMatrix[0][i]; + akEigenvector[i][1] = kMatrix[1][i]; + akEigenvector[i][2] = kMatrix[2][i]; + } + + // make eigenvectors form a right--handed system + Vector3 kCross = akEigenvector[1].cross(akEigenvector[2]); + + float fDet = akEigenvector[0].dot(kCross); + + if ( fDet < 0.0 ) { + akEigenvector[2][0] = - akEigenvector[2][0]; + akEigenvector[2][1] = - akEigenvector[2][1]; + akEigenvector[2][2] = - akEigenvector[2][2]; + } +} + +//---------------------------------------------------------------------------- +void Matrix3::tensorProduct (const Vector3& rkU, const Vector3& rkV, + Matrix3& rkProduct) { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + rkProduct[iRow][iCol] = rkU[iRow] * rkV[iCol]; + } + } +} + +//---------------------------------------------------------------------------- + +// Runs in 52 cycles on AMD, 76 cycles on Intel Centrino +// +// The loop unrolling is necessary for performance. +// I was unable to improve performance further by flattening the matrices +// into float*'s instead of 2D arrays. +// +// -morgan +void Matrix3::_mul(const Matrix3& A, const Matrix3& B, Matrix3& out) { + const float* ARowPtr = A.elt[0]; + float* outRowPtr = out.elt[0]; + outRowPtr[0] = + ARowPtr[0] * B.elt[0][0] + + ARowPtr[1] * B.elt[1][0] + + ARowPtr[2] * B.elt[2][0]; + outRowPtr[1] = + ARowPtr[0] * B.elt[0][1] + + ARowPtr[1] * B.elt[1][1] + + ARowPtr[2] * B.elt[2][1]; + outRowPtr[2] = + ARowPtr[0] * B.elt[0][2] + + ARowPtr[1] * B.elt[1][2] + + ARowPtr[2] * B.elt[2][2]; + + ARowPtr = A.elt[1]; + outRowPtr = out.elt[1]; + + outRowPtr[0] = + ARowPtr[0] * B.elt[0][0] + + ARowPtr[1] * B.elt[1][0] + + ARowPtr[2] * B.elt[2][0]; + outRowPtr[1] = + ARowPtr[0] * B.elt[0][1] + + ARowPtr[1] * B.elt[1][1] + + ARowPtr[2] * B.elt[2][1]; + outRowPtr[2] = + ARowPtr[0] * B.elt[0][2] + + ARowPtr[1] * B.elt[1][2] + + ARowPtr[2] * B.elt[2][2]; + + ARowPtr = A.elt[2]; + outRowPtr = out.elt[2]; + + outRowPtr[0] = + ARowPtr[0] * B.elt[0][0] + + ARowPtr[1] * B.elt[1][0] + + ARowPtr[2] * B.elt[2][0]; + outRowPtr[1] = + ARowPtr[0] * B.elt[0][1] + + ARowPtr[1] * B.elt[1][1] + + ARowPtr[2] * B.elt[2][1]; + outRowPtr[2] = + ARowPtr[0] * B.elt[0][2] + + ARowPtr[1] * B.elt[1][2] + + ARowPtr[2] * B.elt[2][2]; +} + +//---------------------------------------------------------------------------- +void Matrix3::_transpose(const Matrix3& A, Matrix3& out) { + out[0][0] = A.elt[0][0]; + out[0][1] = A.elt[1][0]; + out[0][2] = A.elt[2][0]; + out[1][0] = A.elt[0][1]; + out[1][1] = A.elt[1][1]; + out[1][2] = A.elt[2][1]; + out[2][0] = A.elt[0][2]; + out[2][1] = A.elt[1][2]; + out[2][2] = A.elt[2][2]; +} + +//----------------------------------------------------------------------------- +std::string Matrix3::toString() const { + return G3D::format("[%g, %g, %g; %g, %g, %g; %g, %g, %g]", + elt[0][0], elt[0][1], elt[0][2], + elt[1][0], elt[1][1], elt[1][2], + elt[2][0], elt[2][1], elt[2][2]); +} + + + +} // namespace + diff --git a/externals/g3dlite/G3D.lib/source/Matrix4.cpp b/externals/g3dlite/G3D.lib/source/Matrix4.cpp new file mode 100644 index 00000000000..091af4a9bd5 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Matrix4.cpp @@ -0,0 +1,433 @@ +/** + @file Matrix4.cpp + + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-10-02 + @edited 2008-07-17 + */ + +#include "G3D/platform.h" +#include "G3D/Matrix4.h" +#include "G3D/Matrix3.h" +#include "G3D/Vector4.h" +#include "G3D/Vector3.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Rect2D.h" + +namespace G3D { + +const Matrix4& Matrix4::identity() { + static Matrix4 m( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + return m; +} + + +const Matrix4& Matrix4::zero() { + static Matrix4 m( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0); + return m; +} + + +Matrix4::Matrix4(const class CoordinateFrame& cframe) { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + elt[r][c] = cframe.rotation[r][c]; + } + elt[r][3] = cframe.translation[r]; + } + elt[3][0] = 0.0f; + elt[3][1] = 0.0f; + elt[3][2] = 0.0f; + elt[3][3] = 1.0f; +} + +Matrix4::Matrix4(const Matrix3& upper3x3, const Vector3& lastCol) { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + elt[r][c] = upper3x3[r][c]; + } + elt[r][3] = lastCol[r]; + } + elt[3][0] = 0.0f; + elt[3][1] = 0.0f; + elt[3][2] = 0.0f; + elt[3][3] = 1.0f; +} + + +Matrix3 Matrix4::upper3x3() const { + return Matrix3(elt[0][0], elt[0][1], elt[0][2], + elt[1][0], elt[1][1], elt[1][2], + elt[2][0], elt[2][1], elt[2][2]); +} + + +Matrix4 Matrix4::orthogonalProjection( + const class Rect2D& rect, + float nearval, + float farval) { + return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval); +} + + +Matrix4 Matrix4::orthogonalProjection( + float left, + float right, + float bottom, + float top, + float nearval, + float farval) { + + // Adapted from Mesa. Note that Microsoft (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_8qnj.asp) + // and Linux (http://www.xfree86.org/current/glOrtho.3.html) have different matrices shown in their documentation. + + float x, y, z; + float tx, ty, tz; + + x = 2.0f / (right-left); + y = 2.0f / (top-bottom); + z = -2.0f / (farval-nearval); + tx = -(right+left) / (right-left); + ty = -(top+bottom) / (top-bottom); + tz = -(farval+nearval) / (farval-nearval); + + return + Matrix4( x , 0.0f, 0.0f, tx, + 0.0f, y , 0.0f, ty, + 0.0f, 0.0f, z , tz, + 0.0f, 0.0f, 0.0f, 1.0f); +} + + +Matrix4 Matrix4::perspectiveProjection( + float left, + float right, + float bottom, + float top, + float nearval, + float farval) { + + float x, y, a, b, c, d; + + x = (2.0f*nearval) / (right-left); + y = (2.0f*nearval) / (top-bottom); + a = (right+left) / (right-left); + b = (top+bottom) / (top-bottom); + + if ((float)farval >= (float)inf()) { + // Infinite view frustum + c = -1.0f; + d = -2.0f * nearval; + } else { + c = -(farval+nearval) / (farval-nearval); + d = -(2.0f*farval*nearval) / (farval-nearval); + } + + return Matrix4( + x, 0, a, 0, + 0, y, b, 0, + 0, 0, c, d, + 0, 0, -1, 0); +} + + +Matrix4::Matrix4( + float r1c1, float r1c2, float r1c3, float r1c4, + float r2c1, float r2c2, float r2c3, float r2c4, + float r3c1, float r3c2, float r3c3, float r3c4, + float r4c1, float r4c2, float r4c3, float r4c4) { + elt[0][0] = r1c1; elt[0][1] = r1c2; elt[0][2] = r1c3; elt[0][3] = r1c4; + elt[1][0] = r2c1; elt[1][1] = r2c2; elt[1][2] = r2c3; elt[1][3] = r2c4; + elt[2][0] = r3c1; elt[2][1] = r3c2; elt[2][2] = r3c3; elt[2][3] = r3c4; + elt[3][0] = r4c1; elt[3][1] = r4c2; elt[3][2] = r4c3; elt[3][3] = r4c4; +} + +/** + init should be <B>row major</B>. + */ +Matrix4::Matrix4(const float* init) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = init[r * 4 + c]; + } + } +} + + +Matrix4::Matrix4(const double* init) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = (float)init[r * 4 + c]; + } + } +} + + +Matrix4::Matrix4() { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = 0; + } + } +} + + +void Matrix4::setRow(int r, const Vector4& v) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = v[c]; + } +} + + +void Matrix4::setColumn(int c, const Vector4& v) { + for (int r = 0; r < 4; ++r) { + elt[r][c] = v[r]; + } +} + + +Vector4 Matrix4::getRow(int r) const { + return row(r); +} + + +const Vector4& Matrix4::row(int r) const { + return reinterpret_cast<const Vector4*>(elt[r])[0]; +} + + +Vector4 Matrix4::getColumn(int c) const { + return column(c); +} + +Vector4 Matrix4::column(int c) const { + Vector4 v; + for (int r = 0; r < 4; ++r) { + v[r] = elt[r][c]; + } + return v; +} + + +Matrix4 Matrix4::operator*(const Matrix4& other) const { + Matrix4 result; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + for (int i = 0; i < 4; ++i) { + result.elt[r][c] += elt[r][i] * other.elt[i][c]; + } + } + } + + return result; +} + + +Matrix4 Matrix4::operator*(const float s) const { + Matrix4 result; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + result.elt[r][c] = elt[r][c] * s; + } + } + + return result; +} + + +Vector3 Matrix4::homoMul(const class Vector3& v, float w) const { + Vector4 r = (*this) * Vector4(v, w); + return r.xyz() * (1.0f / r.w); +} + + +Vector4 Matrix4::operator*(const Vector4& vector) const { + Vector4 result(0,0,0,0); + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + result[r] += elt[r][c] * vector[c]; + } + } + + return result; +} + + +Matrix4 Matrix4::transpose() const { + Matrix4 result; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + result.elt[c][r] = elt[r][c]; + } + } + + return result; +} + + +bool Matrix4::operator!=(const Matrix4& other) const { + return ! (*this == other); +} + + +bool Matrix4::operator==(const Matrix4& other) const { + + // If the bit patterns are identical, they must be + // the same matrix. If not, they *might* still have + // equal elements due to floating point weirdness. + if (memcmp(this, &other, sizeof(Matrix4) == 0)) { + return true; + } + + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + if (elt[r][c] != other.elt[r][c]) { + return false; + } + } + } + + return true; +} + + +float Matrix4::determinant() const { + // Determinant is the dot product of the first row and the first row + // of cofactors (i.e. the first col of the adjoint matrix) + return cofactor().getRow(0).dot(getRow(0)); +} + + +Matrix4 Matrix4::adjoint() const { + return cofactor().transpose(); +} + + +Matrix4 Matrix4::inverse() const { + // Inverse = adjoint / determinant + + Matrix4 A = adjoint(); + + // Determinant is the dot product of the first row and the first row + // of cofactors (i.e. the first col of the adjoint matrix) + float det = A.getColumn(0).dot(getRow(0)); + + return A * (1.0f / det); +} + + +Matrix4 Matrix4::cofactor() const { + Matrix4 out; + + // We'll use i to incrementally compute -1 ^ (r+c) + int i = 1; + + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + // Compute the determinant of the 3x3 submatrix + float det = subDeterminant(r, c); + out.elt[r][c] = i * det; + i = -i; + } + i = -i; + } + + return out; +} + + +float Matrix4::subDeterminant(int excludeRow, int excludeCol) const { + // Compute non-excluded row and column indices + int row[3]; + int col[3]; + + for (int i = 0; i < 3; ++i) { + row[i] = i; + col[i] = i; + + if (i >= excludeRow) { + ++row[i]; + } + if (i >= excludeCol) { + ++col[i]; + } + } + + // Compute the first row of cofactors + float cofactor00 = + elt[row[1]][col[1]] * elt[row[2]][col[2]] - + elt[row[1]][col[2]] * elt[row[2]][col[1]]; + + float cofactor10 = + elt[row[1]][col[2]] * elt[row[2]][col[0]] - + elt[row[1]][col[0]] * elt[row[2]][col[2]]; + + float cofactor20 = + elt[row[1]][col[0]] * elt[row[2]][col[1]] - + elt[row[1]][col[1]] * elt[row[2]][col[0]]; + + // Product of the first row and the cofactors along the first row + return + elt[row[0]][col[0]] * cofactor00 + + elt[row[0]][col[1]] * cofactor10 + + elt[row[0]][col[2]] * cofactor20; +} + + +CoordinateFrame Matrix4::approxCoordinateFrame() const { + CoordinateFrame cframe; + + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + cframe.rotation[r][c] = elt[r][c]; + } + cframe.translation[r] = elt[r][3]; + } + + // Ensure that the rotation matrix is orthonormal + cframe.rotation.orthonormalize(); + + return cframe; +} + + +void Matrix4::serialize(class BinaryOutput& b) const { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + b.writeFloat32(elt[r][c]); + } + } +} + + +void Matrix4::deserialize(class BinaryInput& b) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = b.readFloat32(); + } + } +} + +std::string Matrix4::toString() const { + return G3D::format("[%g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g]", + elt[0][0], elt[0][1], elt[0][2], elt[0][3], + elt[1][0], elt[1][1], elt[1][2], elt[1][3], + elt[2][0], elt[2][1], elt[2][2], elt[2][3], + elt[3][0], elt[3][1], elt[3][2], elt[3][3]); +} + +} // namespace + + diff --git a/externals/g3dlite/G3D.lib/source/MeshAlg.cpp b/externals/g3dlite/G3D.lib/source/MeshAlg.cpp new file mode 100644 index 00000000000..24b90ab5a92 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/MeshAlg.cpp @@ -0,0 +1,733 @@ +/** + @file MeshAlg.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + @created 2003-09-14 + @edited 2008-09-03 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + + */ + +#include "G3D/MeshAlg.h" +#include "G3D/Table.h" +#include "G3D/Set.h" +#include "G3D/Box.h" +#include "G3D/Sphere.h" +#include "G3D/vectorMath.h" +#include "G3D/AABox.h" + +#include <climits> + +namespace G3D { + +const int MeshAlg::Face::NONE = INT_MIN; + +void MeshAlg::generateGrid( + Array<Vector3>& vertex, + Array<Vector2>& texCoord, + Array<int>& index, + int wCells, + int hCells, + const Vector2& textureScale, + bool spaceCentered, + bool twoSided, + const CoordinateFrame& xform) { + + vertex.fastClear(); + texCoord.fastClear(); + index.fastClear(); + + // Generate vertices + for (int z = 0; z <= hCells; ++z) { + for (int x = 0; x <= wCells; ++x) { + Vector3 v(x / (float)wCells, 0, z / (float)hCells); + + Vector2 t = v.xz() * textureScale; + + texCoord.append(t); + + if (spaceCentered) { + v -= Vector3(0.5f, 0, 0.5f); + } + v = xform.pointToWorldSpace(v); + vertex.append(v); + } + } + + // Generate indices + for (int z = 0; z < hCells; ++z) { + for (int x = 0; x < wCells; ++x) { + int A = x + z * (wCells + 1); + int B = A + 1; + int C = A + (wCells + 1); + int D = C + 1; + + // A B + // *-----* + // | \ | + // | \ | + // *-----* + // C D + + index.append(A, D, B); + index.append(A, C, D); + } + } + + if (twoSided) { + // The index array needs to have reversed winding for the bottom + // and offset by the original number of vertices + Array<int> ti = index; + ti.reverse(); + for (int i = 0; i < ti.size(); ++i) { + ti[i] += vertex.size(); + } + index.append(ti); + + // Duplicate the arrays + vertex.append(Array<Vector3>(vertex)); + texCoord.append(Array<Vector2>(texCoord)); + } +} + +MeshAlg::Face::Face() { + for (int i = 0; i < 3; ++i) { + edgeIndex[i] = 0; + vertexIndex[i] = 0; + } +} + + +MeshAlg::Edge::Edge() { + for (int i = 0; i < 2; ++i) { + vertexIndex[i] = 0; + // Negative face indices are faces that don't exist + faceIndex[i] = -1; + } +} + + +MeshAlg::Geometry& MeshAlg::Geometry::operator=(const MeshAlg::Geometry& src) { + vertexArray.resize(src.vertexArray.size()); + normalArray.resize(src.vertexArray.size()); + + System::memcpy(vertexArray.getCArray(), src.vertexArray.getCArray(), sizeof(Vector3)*vertexArray.size()); + System::memcpy(normalArray.getCArray(), src.normalArray.getCArray(), sizeof(Vector3)*normalArray.size()); + + return *this; +} + + +void MeshAlg::computeNormals( + Geometry& geometry, + const Array<int>& indexArray) { + + Array<Face> faceArray; + Array<Vertex> vertexArray; + Array<Edge> edgeArray; + Array<Vector3> faceNormalArray; + + computeAdjacency(geometry.vertexArray, indexArray, faceArray, edgeArray, vertexArray); + + computeNormals(geometry.vertexArray, faceArray, vertexArray, + geometry.normalArray, faceNormalArray); +} + + +void MeshAlg::computeNormals( + const Array<Vector3>& vertexGeometry, + const Array<Face>& faceArray, + const Array< Array<int> >& adjacentFaceArray, + Array<Vector3>& vertexNormalArray, + Array<Vector3>& faceNormalArray) { + + // Construct a fake vertex array for backwards compatibility + Array<Vertex> fakeVertexArray(adjacentFaceArray.size()); + + for (int v = 0; v < adjacentFaceArray.size(); ++v) { + fakeVertexArray[v].faceIndex = adjacentFaceArray[v]; + // We leave out the edges because they aren't used to compute normals + } + + computeNormals(vertexGeometry, faceArray, fakeVertexArray, + vertexNormalArray, faceNormalArray); +} + + +void MeshAlg::computeNormals( + const Array<Vector3>& vertexGeometry, + const Array<Face>& faceArray, + const Array<Vertex>& vertexArray, + Array<Vector3>& vertexNormalArray, + Array<Vector3>& faceNormalArray) { + + // Face normals (not unit length) + faceNormalArray.resize(faceArray.size()); + for (int f = 0; f < faceArray.size(); ++f) { + const Face& face = faceArray[f]; + + Vector3 vertex[3]; + for (int j = 0; j < 3; ++j) { + vertex[j] = vertexGeometry[face.vertexIndex[j]]; + debugAssert(vertex[j].isFinite()); + } + + faceNormalArray[f] = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]); +# ifdef G3D_DEBUG + const Vector3& N = faceNormalArray[f]; + debugAssert(N.isFinite()); +# endif + } + + // Per-vertex normals, computed by averaging + vertexNormalArray.resize(vertexGeometry.size()); + for (int v = 0; v < vertexNormalArray.size(); ++v) { + Vector3 sum = Vector3::zero(); + for (int k = 0; k < vertexArray[v].faceIndex.size(); ++k) { + const int f = vertexArray[v].faceIndex[k]; + sum += faceNormalArray[f]; + } + vertexNormalArray[v] = sum.directionOrZero(); +# ifdef G3D_DEBUG + const Vector3& N = vertexNormalArray[v]; + debugAssert(N.isUnit() || N.isZero()); +# endif + } + + + for (int f = 0; f < faceArray.size(); ++f) { + faceNormalArray[f] = faceNormalArray[f].directionOrZero(); +# ifdef G3D_DEBUG + const Vector3& N = faceNormalArray[f]; + debugAssert(N.isUnit() || N.isZero()); +# endif + } + +} + + +void MeshAlg::computeFaceNormals( + const Array<Vector3>& vertexArray, + const Array<MeshAlg::Face>& faceArray, + Array<Vector3>& faceNormals, + bool normalize) { + + faceNormals.resize(faceArray.size()); + + for (int f = 0; f < faceArray.size(); ++f) { + const MeshAlg::Face& face = faceArray[f]; + + const Vector3& v0 = vertexArray[face.vertexIndex[0]]; + const Vector3& v1 = vertexArray[face.vertexIndex[1]]; + const Vector3& v2 = vertexArray[face.vertexIndex[2]]; + + faceNormals[f] = (v1 - v0).cross(v2 - v0); + } + + if (normalize) { + for (int f = 0; f < faceArray.size(); ++f) { + faceNormals[f] = faceNormals[f].direction(); + } + } +} + + +void MeshAlg::identifyBackfaces( + const Array<Vector3>& vertexArray, + const Array<MeshAlg::Face>& faceArray, + const Vector4& HP, + Array<bool>& backface) { + + Vector3 P = HP.xyz(); + + backface.resize(faceArray.size()); + + if (fuzzyEq(HP.w, 0.0)) { + // Infinite case + for (int f = faceArray.size() - 1; f >= 0; --f) { + const MeshAlg::Face& face = faceArray[f]; + + const Vector3& v0 = vertexArray[face.vertexIndex[0]]; + const Vector3& v1 = vertexArray[face.vertexIndex[1]]; + const Vector3& v2 = vertexArray[face.vertexIndex[2]]; + + const Vector3 N = (v1 - v0).cross(v2 - v0); + + backface[f] = N.dot(P) < 0; + } + } else { + // Finite case + for (int f = faceArray.size() - 1; f >= 0; --f) { + const MeshAlg::Face& face = faceArray[f]; + + const Vector3& v0 = vertexArray[face.vertexIndex[0]]; + const Vector3& v1 = vertexArray[face.vertexIndex[1]]; + const Vector3& v2 = vertexArray[face.vertexIndex[2]]; + + const Vector3 N = (v1 - v0).cross(v2 - v0); + + backface[f] = N.dot(P - v0) < 0; + } + } +} + + +void MeshAlg::identifyBackfaces( + const Array<Vector3>& vertexArray, + const Array<MeshAlg::Face>& faceArray, + const Vector4& HP, + Array<bool>& backface, + const Array<Vector3>& faceNormals) { + + Vector3 P = HP.xyz(); + + backface.resize(faceArray.size()); + + if (fuzzyEq(HP.w, 0.0)) { + // Infinite case + for (int f = faceArray.size() - 1; f >= 0; --f) { + const Vector3& N = faceNormals[f]; + backface[f] = N.dot(P) < 0; + } + } else { + // Finite case + for (int f = faceArray.size() - 1; f >= 0; --f) { + const MeshAlg::Face& face = faceArray[f]; + const Vector3& v0 = vertexArray[face.vertexIndex[0]]; + const Vector3& N = faceNormals[f]; + + backface[f] = N.dot(P - v0) < 0; + } + } +} + + +void MeshAlg::createIndexArray(int n, Array<int>& array, int start, int run, int skip) { + debugAssert(skip >= 0); + debugAssert(run >= 0); + + array.resize(n); + if (skip == 0) { + for (int i = 0; i < n; ++i) { + array[i] = start + i; + } + } else { + int rcount = 0; + int j = start; + for (int i = 0; i < n; ++i) { + array[i] = j; + + ++j; + ++rcount; + + if (rcount == run) { + rcount = 0; + j += skip; + } + } + } +} + + +void MeshAlg::computeAreaStatistics( + const Array<Vector3>& vertexArray, + const Array<int>& indexArray, + double& minEdgeLength, + double& meanEdgeLength, + double& medianEdgeLength, + double& maxEdgeLength, + double& minFaceArea, + double& meanFaceArea, + double& medianFaceArea, + double& maxFaceArea) { + + debugAssert(indexArray.size() % 3 == 0); + + Array<double> area(indexArray.size() / 3); + Array<double> magnitude(indexArray.size()); + + for (int i = 0; i < indexArray.size(); i += 3) { + const Vector3& v0 = vertexArray[indexArray[i]]; + const Vector3& v1 = vertexArray[indexArray[i + 1]]; + const Vector3& v2 = vertexArray[indexArray[i + 2]]; + + area[i / 3] = (v1 - v0).cross(v2 - v0).magnitude() / 2.0; + magnitude[i] = (v1 - v0).magnitude(); + magnitude[i + 1] = (v2 - v1).magnitude(); + magnitude[i + 2] = (v0 - v2).magnitude(); + } + + area.sort(); + magnitude.sort(); + + minEdgeLength = max(0.0, magnitude[0]); + maxEdgeLength = max(0.0, magnitude.last()); + medianEdgeLength = max(0.0, magnitude[magnitude.size() / 2]); + meanEdgeLength = 0; + for (int i = 0; i < magnitude.size(); ++i) { + meanEdgeLength += magnitude[i]; + } + meanEdgeLength /= magnitude.size(); + + minFaceArea = max(0.0, area[0]); + maxFaceArea = max(0.0, area.last()); + medianFaceArea = max(0.0, area[area.size() / 2]); + meanFaceArea = 0; + for (int i = 0; i < area.size(); ++i) { + meanFaceArea += area[i]; + } + meanFaceArea /= area.size(); + + + // Make sure round-off hasn't pushed values less than zero + meanFaceArea = max(0.0, meanFaceArea); + meanEdgeLength = max(0.0, meanEdgeLength); +} + + +int MeshAlg::countBoundaryEdges(const Array<MeshAlg::Edge>& edgeArray) { + int b = 0; + + for (int i = 0; i < edgeArray.size(); ++i) { + if ((edgeArray[i].faceIndex[0] == MeshAlg::Face::NONE) != + (edgeArray[i].faceIndex[1] == MeshAlg::Face::NONE)) { + ++b; + } + } + + return b; +} + +void MeshAlg::computeBounds( + const Array<Vector3>& vertexArray, + const Array<int>& indexArray, + Box& box, + Sphere& sphere) { + + Array<Vector3> newArray(indexArray.size()); + for (int i = 0; i < indexArray.size(); ++i) { + newArray[i] = vertexArray[indexArray[i]]; + } + computeBounds(newArray, box, sphere); +} + + +void MeshAlg::computeBounds( + const Array<Vector3>& vertexArray, + Box& box, + Sphere& sphere) { + + Vector3 xmin, xmax, ymin, ymax, zmin, zmax; + + // FIRST PASS: find 6 minima/maxima points + xmin.x = ymin.y = zmin.z = inf(); + xmax.x = ymax.y = zmax.z = -inf(); + + for (int v = 0; v < vertexArray.size(); ++v) { + const Vector3& vertex = vertexArray[v]; + + if (vertex.x < xmin.x) { + xmin = vertex; + } + + if (vertex.x > xmax.x) { + xmax = vertex; + } + + if (vertex.y < ymin.y) { + ymin = vertex; + } + + if (vertex.y > ymax.y) { + ymax = vertex; + } + + if (vertex.z < zmin.z) { + zmin = vertex; + } + + if (vertex.z > zmax.z) { + zmax = vertex; + } + } + + // Set points dia1 & dia2 to the maximally separated pair + Vector3 dia1 = xmin; + Vector3 dia2 = xmax; + { + // Set xspan = distance between the 2 points xmin & xmax (squared) + double xspan = (xmax - xmin).squaredMagnitude(); + + // Same for y & z spans + double yspan = (ymax - ymin).squaredMagnitude(); + double zspan = (zmax - zmin).squaredMagnitude(); + + double maxspan = xspan; + + if (yspan > maxspan) { + maxspan = yspan; + dia1 = ymin; + dia2 = ymax; + } + + if (zspan > maxspan) { + maxspan = zspan; + dia1 = zmin; + dia2 = zmax; + } + } + + + // dia1, dia2 is a diameter of initial sphere + + // calc initial center + Vector3 center = (dia1 + dia2) / 2.0; + + // calculate initial radius^2 and radius + Vector3 d = dia2 - sphere.center; + + double radSq = d.squaredMagnitude(); + double rad = sqrt(radSq); + + // SECOND PASS: increment current sphere + double old_to_p, old_to_new; + + for (int v = 0; v < vertexArray.size(); ++v) { + const Vector3& vertex = vertexArray[v]; + + d = vertex - center; + + double old_to_p_sq = d.squaredMagnitude(); + + // do r^2 test first + if (old_to_p_sq > radSq) { + // this point is outside of current sphere + old_to_p = sqrt(old_to_p_sq); + + // calc radius of new sphere + rad = (rad + old_to_p) / 2.0; + + // for next r^2 compare + radSq = rad * rad; + old_to_new = old_to_p - rad; + + // calc center of new sphere + center = (rad * center + old_to_new * vertex) / old_to_p; + } + } + + const Vector3 min(xmin.x, ymin.y, zmin.z); + const Vector3 max(xmax.x, ymax.y, zmax.z); + + box = Box(min, max); + + const double boxRadSq = (max - min).squaredMagnitude() * 0.25; + + if (boxRadSq >= radSq){ + if (isNaN(center.x) || ! isFinite(rad)) { + sphere = Sphere(Vector3::zero(), inf()); + } else { + sphere = Sphere(center, rad); + } + }else{ + sphere = Sphere((max + min) * 0.5, sqrt(boxRadSq)); + } +} + + +void MeshAlg::computeTangentVectors( + const Vector3& normal, + const Vector3 position[3], + const Vector2 texCoord[3], + Vector3& tangent, + Vector3& binormal) { + + Vector3 v[3]; + Vector2 t[3]; + + // TODO: don't need the copy + // Make a copy so that we can sort + for (int i = 0; i < 3; ++i) { + v[i] = position[i]; + t[i] = texCoord[i]; + } + + ///////////////////////////////////////////////// + // Begin by computing the tangent + + // Bubble sort the vertices by decreasing texture coordinate y. + if (t[0].y < t[1].y) { + std::swap(v[0], v[1]); + std::swap(t[0], t[1]); + } + + // t0 >= t1 + + if (t[0].y < t[2].y) { + std::swap(v[0], v[2]); + std::swap(t[0], t[2]); + } + + // t0 >= t2, t0 >= t1 + + if (t[1].y < t[2].y) { + std::swap(v[1], v[2]); + std::swap(t[1], t[2]); + } + + // t0 >= t1 >= t2 + + float amount; + + // Compute the direction of constant y. + if (fuzzyEq(t[2].y, t[0].y)) { + // Degenerate case-- the texture coordinates do not vary across this + // triangle. + amount = 1.0; + } else { + // Solve lerp(t[0].y, t[2].y, amount) = t[1].y for amount: + // + // t0 + (t2 - t0) * a = t1 + // a = (t1 - t0) / (t2 - t0) + + amount = (t[1].y - t[0].y) / (t[2].y - t[0].y); + } + + tangent = lerp(v[0], v[2], amount) - v[1]; + + // Make sure the tangent points in the right direction and is + // perpendicular to the normal. + if (lerp(t[0].x, t[2].x, amount) < t[1].x) { + tangent = -tangent; + } + + // TODO: do we need this? We take this component off + // at the end anyway + tangent -= tangent.dot(normal) * normal; + + // Normalize the tangent so it contributes + // equally at the vertex (TODO: do we need this?) + if (fuzzyEq(tangent.magnitude(), 0.0)) { + tangent = Vector3::unitX(); + } else { + tangent = tangent.direction(); + } + + ////////////////////////////////////////////////// + // Now compute the binormal (same code, but in x) + + // Sort the vertices by texture coordinate x. + if (t[0].x < t[1].x) { + std::swap(v[0], v[1]); + std::swap(t[0], t[1]); + } + + if (t[0].x < t[2].x) { + std::swap(v[0], v[2]); + std::swap(t[0], t[2]); + } + + if (t[1].x < t[2].x) { + std::swap(v[1], v[2]); + std::swap(t[1], t[2]); + } + + // Compute the direction of constant x. + if (fuzzyEq(t[2].x, t[0].x)) { + amount = 1.0; + } else { + amount = (t[1].x - t[0].x) / (t[2].x - t[0].x); + } + + binormal = lerp(v[0], v[2], amount) - v[1]; + + // Make sure the binormal points in the right direction and is + // perpendicular to the normal. + if (lerp(t[0].y, t[2].y, amount) < t[1].y) { + binormal = -binormal; + } + + binormal -= binormal.dot(normal) * normal; + + // Normalize the binormal so that it contributes + // an equal amount to the per-vertex value (TODO: do we need this? + // Nelson Max showed that we don't for computing per-vertex normals) + if (fuzzyEq(binormal.magnitude(), 0.0)) { + binormal = Vector3::unitZ(); + } else { + binormal = binormal.direction(); + } + + // This computation gives the opposite convention of what we want. + binormal = -binormal; + +} + + +void MeshAlg::computeTangentSpaceBasis( + const Array<Vector3>& vertexArray, + const Array<Vector2>& texCoordArray, + const Array<Vector3>& vertexNormalArray, + const Array<Face>& faceArray, + Array<Vector3>& tangent, + Array<Vector3>& binormal) { + + debugAssertM(faceArray.size() != 0, "Unable to calculate valid tangent space without faces."); + + // The three vertices and texCoords of each face + Vector3 position[3]; + Vector2 texCoord[3]; + Vector3 t, b; + + tangent.resize(vertexArray.size()); + binormal.resize(vertexArray.size()); + + // Zero the output arrays. + System::memset(tangent.getCArray(), 0, sizeof(Vector3) * tangent.size()); + System::memset(binormal.getCArray(), 0, sizeof(Vector3) * binormal.size()); + + // Iterate over faces, computing the tangent vectors for each + // vertex. Accumulate those into the tangent and binormal arrays + // and then orthonormalize at the end. + + for (int f = 0; f < faceArray.size(); ++f) { + const Face& face = faceArray[f]; + + for (int v = 0; v < 3; ++v) { + int i = face.vertexIndex[v]; + position[v] = vertexArray[i]; + texCoord[v] = texCoordArray[i]; + } + + const Vector3 faceNormal((position[1] - position[0]).cross(position[2] - position[0]).direction()); + computeTangentVectors(faceNormal, position, texCoord, t, b); + + for (int v = 0; v < 3; ++v) { + int i = face.vertexIndex[v]; + tangent[i] += t; + binormal[i] += b; + } + } + + // Normalize the basis vectors + for (int v = 0; v < vertexArray.size(); ++v) { + // Remove the component parallel to the normal + const Vector3& N = vertexNormalArray[v]; + debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length"); + + tangent[v] -= tangent[v].dot(N) * N; + binormal[v] -= binormal[v].dot(N) * N; + + // Normalize + tangent[v] = tangent[v].directionOrZero(); + binormal[v] = binormal[v].directionOrZero(); + + // Note that the tangent and binormal might not be perpendicular anymore + } +} + + + +} // G3D namespace diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp new file mode 100644 index 00000000000..a8b35f32c86 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp @@ -0,0 +1,729 @@ +/** + @file MeshAlgAdjacency.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + @created 2003-09-14 + @edited 2005-02-24 + + Copyright 2000-2003, Morgan McGuire. + All rights reserved. + + */ + +#include "G3D/Table.h" +#include "G3D/MeshAlg.h" +#include "G3D/Set.h" + + +namespace G3D { + +/** + A half [i.e. directed] edge. + */ +class MeshDirectedEdgeKey { +public: + + /** vertexIndex[0] <= vertexIndex[1] */ + int vertexIndex[2]; + + MeshDirectedEdgeKey() {} + + MeshDirectedEdgeKey( + const int i0, + const int i1) { + + if (i0 <= i1) { + vertexIndex[0] = i0; + vertexIndex[1] = i1; + } else { + vertexIndex[0] = i1; + vertexIndex[1] = i0; + } + } + + + bool operator==(const MeshDirectedEdgeKey& e2) const { + for (int i = 0; i < 2; ++i) { + if (vertexIndex[i] != e2.vertexIndex[i]) { + return false; + } + } + return true; + } +}; + +} + +template<> struct HashTrait<G3D::MeshDirectedEdgeKey> { + static size_t hashCode(const G3D::MeshDirectedEdgeKey& key) { + return key.vertexIndex[0] + (key.vertexIndex[1] << 16); + } +}; + +namespace G3D { + +/** + A hashtable mapping edges to lists of indices for + faces. This is used instead of Table because of the + special logic in insert. + + Used only for MeshAlg::computeAdjacency. + + In the face lists, index <I>f</I> >= 0 indicates that + <I>f</I> contains the edge as a forward edge. Index <I>f</I> < 0 + indicates that ~<I>f</I> contains the edge as a backward edge. + */ +class MeshEdgeTable { +public: + typedef Table<MeshDirectedEdgeKey, Array<int> > ET; + +private: + + ET table; + +public: + + /** + Clears the table. + */ + void clear() { + table.clear(); + } + + /** + Inserts the faceIndex into the edge's face list. + The index may be a negative number indicating a backface. + */ + void insert(const MeshDirectedEdgeKey& edge, int faceIndex) { + + // debugAssertM((table.size() > 20) && (table.debugGetLoad() < 0.5 || table.debugGetNumBuckets() < 20), + // "MeshEdgeTable is using a poor hash function."); + + if (! table.containsKey(edge)) { + // First time + Array<int> x(1); + x[0] = faceIndex; + table.set(edge, x); + } else { + table[edge].append(faceIndex); + } + } + + /** + Returns the face list for a given edge + */ + const Array<int>& get(const MeshDirectedEdgeKey& edge) { + return table[edge]; + } + + ET::Iterator begin() { + return table.begin(); + } + + const ET::Iterator end() const { + return table.end(); + } + +}; + + +/** + edgeTable[edgeKey] is a list of faces containing + + Used and cleared by MeshModel::computeAdjacency() + */ +static MeshEdgeTable edgeTable; + +/** + Assigns the edge index into the next unassigned edge + index. The edge index may be negative, indicating + a reverse edge. + */ +static void assignEdgeIndex(MeshAlg::Face& face, int e) { + for (int i = 0; i < 3; ++i) { + if (face.edgeIndex[i] == MeshAlg::Face::NONE) { + face.edgeIndex[i] = e; + return; + } + } + + debugAssertM(false, "Face has already been assigned 3 edges"); +} + + +void MeshAlg::computeAdjacency( + const Array<Vector3>& vertexGeometry, + const Array<int>& indexArray, + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array< Array<int> >& adjacentFaceArray) { + + Array<Vertex> vertexArray; + + computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray); + + // Convert the vertexArray into adjacentFaceArray + adjacentFaceArray.clear(); + adjacentFaceArray.resize(vertexArray.size()); + for (int v = 0; v < adjacentFaceArray.size(); ++v) { + adjacentFaceArray[v] = vertexArray[v].faceIndex; + } +} + + +void MeshAlg::computeAdjacency( + const Array<Vector3>& vertexGeometry, + const Array<int>& indexArray, + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array<Vertex>& vertexArray) { + + edgeArray.clear(); + vertexArray.clear(); + faceArray.clear(); + edgeTable.clear(); + + // Face normals + Array<Vector3> faceNormal; + + // This array has the same size as the vertex array + vertexArray.resize(vertexGeometry.size()); + + // Iterate through the triangle list + for (int q = 0; q < indexArray.size(); q += 3) { + + Vector3 vertex[3]; + int f = faceArray.size(); + MeshAlg::Face& face = faceArray.next(); + + // Construct the face + for (int j = 0; j < 3; ++j) { + int v = indexArray[q + j]; + face.vertexIndex[j] = v; + face.edgeIndex[j] = Face::NONE; + + // Store back pointers in the vertices + vertexArray[v].faceIndex.append(f); + + // We'll need these vertices to find the face normal + vertex[j] = vertexGeometry[v]; + } + + // Compute the face normal + Vector3 N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]); + faceNormal.append(N.directionOrZero()); + + static const int nextIndex[] = {1, 2, 0}; + + // Add each edge to the edge table. + for (int j = 0; j < 3; ++j) { + const int i0 = indexArray[q + j]; + const int i1 = indexArray[q + nextIndex[j]]; + + const MeshDirectedEdgeKey edge(i0, i1); + + if (i0 == edge.vertexIndex[0]) { + // The edge was directed in the same manner as in the face + edgeTable.insert(edge, f); + } else { + // The edge was directed in the opposite manner as in the face + edgeTable.insert(edge, ~f); + } + } + } + + // For each edge in the edge table, create an edge in the edge array. + // Collapse every 2 edges from adjacent faces. + + MeshEdgeTable::ET::Iterator cur = edgeTable.begin(); + MeshEdgeTable::ET::Iterator end = edgeTable.end(); + + Array<Edge> tempEdgeArray; + while (cur != end) { + MeshDirectedEdgeKey& edgeKey = cur->key; + Array<int>& faceIndexArray = cur->value; + + // Process this edge + while (faceIndexArray.size() > 0) { + + // Remove the last index + int f0 = faceIndexArray.pop(); + + // Find the normal to that face + const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0]; + + bool found = false; + + // We try to find the matching face with the closest + // normal. This ensures that we don't introduce a lot + // of artificial ridges into flat parts of a mesh. + double ndotn = -2; + int f1 = -1, i1 = -1; + + // Try to Find the face with the matching edge + for (int i = faceIndexArray.size() - 1; i >= 0; --i) { + int f = faceIndexArray[i]; + + if ((f >= 0) != (f0 >= 0)) { + // This face contains the oppositely oriented edge + // and has not been assigned too many edges + + const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f]; + double d = n1.dot(n0); + + if (found) { + // We previously found a good face; see if this + // one is better. + if (d > ndotn) { + // This face is better. + ndotn = d; + f1 = f; + i1 = i; + } + } else { + // This is the first face we've found + found = true; + ndotn = d; + f1 = f; + i1 = i; + } + } + } + + // Create the new edge + int e = tempEdgeArray.size(); + Edge& edge = tempEdgeArray.next(); + + edge.vertexIndex[0] = edgeKey.vertexIndex[0]; + edge.vertexIndex[1] = edgeKey.vertexIndex[1]; + + if (f0 >= 0) { + edge.faceIndex[0] = f0; + edge.faceIndex[1] = Face::NONE; + assignEdgeIndex(faceArray[f0], e); + } else { + // The face indices above are two's complemented. + // this code restores them to regular indices. + debugAssert((~f0) >= 0); + edge.faceIndex[1] = ~f0; + edge.faceIndex[0] = Face::NONE; + + // The edge index *does* need to be inverted, however. + assignEdgeIndex(faceArray[~f0], ~e); + } + + if (found) { + // We found a matching face; remove both + // faces from the active list. + faceIndexArray.fastRemove(i1); + + if (f1 >= 0) { + edge.faceIndex[0] = f1; + assignEdgeIndex(faceArray[f1], e); + } else { + edge.faceIndex[1] = ~f1; + assignEdgeIndex(faceArray[~f1], ~e); + } + } + } + + ++cur; + } + + edgeTable.clear(); + + // Move boundary edges to the end of the list and then + // clean up the face references into them + { + // Map old edge indices to new edge indices + Array<int> newIndex(tempEdgeArray.size()); + + + // Index of the start and end of the edge array + int i = 0; + int j = tempEdgeArray.size() - 1; + + edgeArray.resize(tempEdgeArray.size()); + for (int e = 0; e < tempEdgeArray.size(); ++e) { + if (tempEdgeArray[e].boundary()) { + newIndex[e] = j; + --j; + } else { + newIndex[e] = i; + ++i; + } + edgeArray[newIndex[e]] = tempEdgeArray[e]; + } + + debugAssertM(i == j + 1, "Counting from front and back of array did not match"); + + // Fix the faces + for (int f = 0; f < faceArray.size(); ++f) { + Face& face = faceArray[f]; + for (int q = 0; q < 3; ++q) { + int e = face.edgeIndex[q]; + if (e < 0) { + // Backwards edge; twiddle before and after conversion + face.edgeIndex[q] = ~newIndex[~e]; + } else { + // Regular edge; remap the index + face.edgeIndex[q] = newIndex[e]; + } + } + } + } + + // Now order the edge indices inside the faces correctly. + for (int f = 0; f < faceArray.size(); ++f) { + Face& face = faceArray[f]; + int e0 = face.edgeIndex[0]; + int e1 = face.edgeIndex[1]; + int e2 = face.edgeIndex[2]; + + // e0 will always remain first. The only + // question is whether e1 and e2 should be swapped. + + // See if e1 begins at the vertex where e1 ends. + const int e0End = (e0 < 0) ? + edgeArray[~e0].vertexIndex[0] : + edgeArray[e0].vertexIndex[1]; + + const int e1Begin = (e1 < 0) ? + edgeArray[~e1].vertexIndex[1] : + edgeArray[e1].vertexIndex[0]; + + if (e0End != e1Begin) { + // We must swap e1 and e2 + face.edgeIndex[1] = e2; + face.edgeIndex[2] = e1; + } + } + + // Fill out the edge adjacency information in the vertex array + for (int e = 0; e < edgeArray.size(); ++e) { + const Edge& edge = edgeArray[e]; + vertexArray[edge.vertexIndex[0]].edgeIndex.append(e); + vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e); + } +} + + +void MeshAlg::weldBoundaryEdges( + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array<Vertex>& vertexArray) { + + // Copy over the original edge array + Array<Edge> oldEdgeArray = edgeArray; + + // newEdgeIndex[e] is the new index of the old edge with index e + // Note that newEdgeIndex[e] might be negative, indicating that + // the edge switched direction between the arrays. + Array<int> newEdgeIndex(edgeArray.size()); + edgeArray.resize(0); + + // boundaryEdgeIndices[v_low] is an array of the indices of + // all boundary edges whose lower vertex is v_low. + Table<int, Array<int> > boundaryEdgeIndices; + + // Copy over non-boundary edges to the new array + for (int e = 0; e < oldEdgeArray.size(); ++e) { + if (oldEdgeArray[e].boundary()) { + + // Add to the boundary table + const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]); + if (! boundaryEdgeIndices.containsKey(v_low)) { + boundaryEdgeIndices.set(v_low, Array<int>()); + } + boundaryEdgeIndices[v_low].append(e); + + // We'll fill out newEdgeIndex[e] later, when we find pairs + + } else { + + // Copy the edge to the new array + newEdgeIndex[e] = edgeArray.size(); + edgeArray.append(oldEdgeArray[e]); + + } + } + + + // Remove all edges from the table that have pairs. + Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin(); + Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end(); + while (cur != end) { + Array<int>& boundaryEdge = cur->value; + + for (int i = 0; i < boundaryEdge.size(); ++i) { + int ei = boundaryEdge[i]; + const Edge& edgei = oldEdgeArray[ei]; + + for (int j = i + 1; j < boundaryEdge.size(); ++j) { + int ej = boundaryEdge[j]; + const Edge& edgej = oldEdgeArray[ej]; + + // See if edge ei is the reverse (match) of edge ej. + + // True if the edges match + bool match = false; + + // True if edgej's vertex indices are reversed from + // edgei's (usually true). + bool reversej = false; + + int u = edgei.vertexIndex[0]; + int v = edgei.vertexIndex[1]; + + if (edgei.faceIndex[0] != Face::NONE) { + // verts|faces + // edgei = [u v A /] + + if (edgej.faceIndex[0] != Face::NONE) { + if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) { + // This is the most common of the four cases + + // edgej = [v u B /] + match = true; + reversej = true; + } + } else { + if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) { + // edgej = [u v / B] + match = true; + } + } + } else { + // edgei = [u v / A] + if (edgej.faceIndex[0] != Face::NONE) { + if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) { + // edgej = [u v B /] + match = true; + } + } else { + if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) { + // edgej = [v u / B] + match = true; + reversej = true; + } + } + } + + if (match) { + // ei and ej can be paired as a single edge + int e = edgeArray.size(); + Edge& edge = edgeArray.next(); + + // Follow the direction of edgei. + edge = edgei; + newEdgeIndex[ei] = e; + + // Insert the face index for edgej. + int fj = edgej.faceIndex[0]; + if (fj == Face::NONE) { + fj = edgej.faceIndex[1]; + } + + if (edge.faceIndex[0] == Face::NONE) { + edge.faceIndex[0] = fj; + } else { + edge.faceIndex[1] = fj; + } + + if (reversej) { + // The new edge is backwards of the old edge for ej + newEdgeIndex[ej] = ~e; + } else { + newEdgeIndex[ej] = e; + } + + // Remove both ei and ej from being candidates for future pairing. + // Remove ej first since it comes later in the list (removing + // ei would decrease the index of ej to j - 1). + boundaryEdge.fastRemove(j); + boundaryEdge.fastRemove(i); + + // Re-process element i, which is now a new edge index + --i; + + // Jump out of the j for-loop + break; + } + } + } + ++cur; + } + + // Anything remaining in the table is a real boundary edge; just copy it to + // the end of the array. + cur = boundaryEdgeIndices.begin(); + end = boundaryEdgeIndices.end(); + while (cur != end) { + Array<int>& boundaryEdge = cur->value; + + for (int b = 0; b < boundaryEdge.size(); ++b) { + const int e = boundaryEdge[b]; + + newEdgeIndex[e] = edgeArray.size(); + edgeArray.append(oldEdgeArray[e]); + } + + ++cur; + } + + // Finally, fix up edge indices in the face and vertex arrays + for (int f = 0; f < faceArray.size(); ++f) { + Face& face = faceArray[f]; + for (int i = 0; i < 3; ++i) { + int e = face.edgeIndex[i]; + + if (e < 0) { + face.edgeIndex[i] = ~newEdgeIndex[~e]; + } else { + face.edgeIndex[i] = newEdgeIndex[e]; + } + } + } + + for (int v = 0; v < vertexArray.size(); ++v) { + Vertex& vertex = vertexArray[v]; + for (int i = 0; i < vertex.edgeIndex.size(); ++i) { + int e = vertex.edgeIndex[i]; + + if (e < 0) { + vertex.edgeIndex[i] = ~newEdgeIndex[~e]; + } else { + vertex.edgeIndex[i] = newEdgeIndex[e]; + } + } + } +} + + +void MeshAlg::weldAdjacency( + const Array<Vector3>& originalGeometry, + Array<Face>& faceArray, + Array<Edge>& edgeArray, + Array<Vertex>& vertexArray, + double radius) { + + // Num vertices + const int n = originalGeometry.size(); + + // canonical[v] = first occurance of any vertex near oldVertexArray[v] + Array<int> canonical(n); + + Array<int> toNew, toOld; + // Throw away the new vertex array + Array<Vector3> dummy; + computeWeld(originalGeometry, dummy, toNew, toOld, radius); + + for (int v = 0; v < canonical.size(); ++v) { + // Round-trip through the toNew/toOld process. This will give + // us the original vertex. + canonical[v] = toOld[toNew[v]]; + } + + // Destroy vertexArray (we reconstruct it below) + vertexArray.clear(); + vertexArray.resize(n); + + bool hasBoundaryEdges = false; + + // Fix edge vertex indices + for (int e = 0; e < edgeArray.size(); ++e) { + Edge& edge = edgeArray[e]; + + const int v0 = canonical[edge.vertexIndex[0]]; + const int v1 = canonical[edge.vertexIndex[1]]; + + edge.vertexIndex[0] = v0; + edge.vertexIndex[1] = v1; + + vertexArray[v0].edgeIndex.append(e); + vertexArray[v1].edgeIndex.append(~e); + + hasBoundaryEdges = hasBoundaryEdges || edge.boundary(); + } + + // Fix face vertex indices + for (int f = 0; f < faceArray.size(); ++f) { + Face& face = faceArray[f]; + for (int i = 0; i < 3; ++i) { + const int v = canonical[face.vertexIndex[i]]; + + face.vertexIndex[i] = v; + + // Add the back pointer + vertexArray[v].faceIndex.append(f); + } + } + + if (hasBoundaryEdges) { + // As a result of the welding, some of the boundary edges at + // the end of the array may now have mates and no longer be + // boundaries. Try to pair these up. + + weldBoundaryEdges(faceArray, edgeArray, vertexArray); + } +} + + +void MeshAlg::debugCheckConsistency( + const Array<Face>& faceArray, + const Array<Edge>& edgeArray, + const Array<Vertex>& vertexArray) { + +#ifdef _DEBUG + for (int v = 0; v < vertexArray.size(); ++v) { + const MeshAlg::Vertex& vertex = vertexArray[v]; + + for (int i = 0; i < vertex.edgeIndex.size(); ++i) { + const int e = vertex.edgeIndex[i]; + debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v)); + } + + for (int i = 0; i < vertex.faceIndex.size(); ++i) { + const int f = vertex.faceIndex[i]; + debugAssert(faceArray[f].containsVertex(v)); + } + + } + + for (int e = 0; e < edgeArray.size(); ++e) { + const MeshAlg::Edge& edge = edgeArray[e]; + + for (int i = 0; i < 2; ++i) { + debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) || + faceArray[edge.faceIndex[i]].containsEdge(e)); + + debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e)); + } + } + + // Every face's edge must be on that face + for (int f = 0; f < faceArray.size(); ++f) { + const MeshAlg::Face& face = faceArray[f]; + for (int i = 0; i < 3; ++i) { + int e = face.edgeIndex[i]; + int ei = (e >= 0) ? e : ~e; + debugAssert(edgeArray[ei].inFace(f)); + + // Make sure the edge is oriented appropriately + if (e >= 0) { + debugAssert(edgeArray[ei].faceIndex[0] == (int)f); + } else { + debugAssert(edgeArray[ei].faceIndex[1] == (int)f); + } + + debugAssert(vertexArray[face.vertexIndex[i]].inFace(f)); + } + } +#else + (void)faceArray; + (void)edgeArray; + (void)vertexArray; +#endif // _DEBUG +} + +} // G3D namespace diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp new file mode 100644 index 00000000000..cd4d1f9c7d5 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp @@ -0,0 +1,213 @@ +/** + @file MeshAlgWeld.cpp + + The MeshAlg::computeWeld method. + + @maintainer Morgan McGuire, matrix@graphics3d.com + @created 2003-10-22 + @edited 2005-02-24 + + Copyright 2000-2003, Morgan McGuire. + All rights reserved. + + */ + +#include "G3D/MeshAlg.h" +#include "G3D/Table.h" +#include "G3D/Set.h" + +namespace G3D { + +namespace _internal { + +class Welder { +private: + + // Intentionally illegal + Welder& operator=(const Welder& w); + +public: + /** Indices of newVertexArray elements in <B>or near</B> a grid cell. */ + typedef Array<int> List; + + enum {GRID_RES = 32}; + + List grid[GRID_RES][GRID_RES][GRID_RES]; + + const Array<Vector3>& oldVertexArray; + Array<Vector3>& newVertexArray; + Array<int>& toNew; + Array<int>& toOld; + + /** Must be less than one grid cell, not checked */ + const double radius; + + /** (oldVertexArray[i] - offset) * scale is on the range [0, 1] */ + Vector3 offset; + Vector3 scale; + + Welder( + const Array<Vector3>& _oldVertexArray, + Array<Vector3>& _newVertexArray, + Array<int>& _toNew, + Array<int>& _toOld, + double _radius); + + /** + Computes the grid index from an ordinate. + */ + void toGridCoords(Vector3 v, int& x, int& y, int& z) const; + + /** Gets the index of a vertex, adding it to + newVertexArray if necessary. */ + int getIndex(const Vector3& vertex); + + void weld(); +}; + +} // namespace _internal + +} // namespace G3D + +template<> struct HashTrait<G3D::_internal::Welder::List*> { + static size_t hashCode(const G3D::_internal::Welder::List* key) { return reinterpret_cast<size_t>(key); } +}; + +namespace G3D { +namespace _internal { + +Welder::Welder( + const Array<Vector3>& _oldVertexArray, + Array<Vector3>& _newVertexArray, + Array<int>& _toNew, + Array<int>& _toOld, + double _radius) : + oldVertexArray(_oldVertexArray), + newVertexArray(_newVertexArray), + toNew(_toNew), + toOld(_toOld), + radius(_radius) { + + // Compute a scale factor that moves the range + // of all ordinates to [0, 1] + Vector3 minBound = Vector3::inf(); + Vector3 maxBound = -minBound; + + for (int i = 0; i < oldVertexArray.size(); ++i) { + minBound = minBound.min(oldVertexArray[i]); + maxBound = maxBound.max(oldVertexArray[i]); + } + + offset = minBound; + scale = maxBound - minBound; + for (int i = 0; i < 3; ++i) { + // The model might have zero extent along some axis + if (fuzzyEq(scale[i], 0.0)) { + scale[i] = 1.0; + } else { + scale[i] = 1.0 / scale[i]; + } + } +} + + +void Welder::toGridCoords(Vector3 v, int& x, int& y, int& z) const { + v = (v - offset) * scale; + x = iClamp(iFloor(v.x * GRID_RES), 0, GRID_RES - 1); + y = iClamp(iFloor(v.y * GRID_RES), 0, GRID_RES - 1); + z = iClamp(iFloor(v.z * GRID_RES), 0, GRID_RES - 1); +} + + +int Welder::getIndex(const Vector3& vertex) { + + int closestIndex = -1; + double distanceSquared = inf(); + + int ix, iy, iz; + toGridCoords(vertex, ix, iy, iz); + + // Check against all vertices within radius of this grid cube + const List& list = grid[ix][iy][iz]; + + for (int i = 0; i < list.size(); ++i) { + double d = (newVertexArray[list[i]] - vertex).squaredMagnitude(); + + if (d < distanceSquared) { + distanceSquared = d; + closestIndex = list[i]; + } + } + + if (distanceSquared <= radius * radius) { + + return closestIndex; + + } else { + + // This is a new vertex + int newIndex = newVertexArray.size(); + newVertexArray.append(vertex); + + // Create a new vertex and store its index in the + // neighboring grid cells (usually, only 1 neighbor) + + Set<List*> neighbors; + + for (float dx = -1; dx <= +1; ++dx) { + for (float dy = -1; dy <= +1; ++dy) { + for (float dz = -1; dz <= +1; ++dz) { + int ix, iy, iz; + toGridCoords(vertex + Vector3(dx, dy, dz) * radius, ix, iy, iz); + neighbors.insert(&(grid[ix][iy][iz])); + } + } + } + + Set<List*>::Iterator neighbor(neighbors.begin()); + Set<List*>::Iterator none(neighbors.end()); + + while (neighbor != none) { + (*neighbor)->append(newIndex); + ++neighbor; + } + + return newIndex; + } +} + + +void Welder::weld() { + newVertexArray.resize(0); + + // Prime the vertex positions + for (int i = 0; i < oldVertexArray.size(); ++i) { + getIndex(oldVertexArray[i]); + } + + // Now create the official remapping by snapping to + // nearby vertices. + toNew.resize(oldVertexArray.size()); + toOld.resize(newVertexArray.size()); + + for (int oi = 0; oi < oldVertexArray.size(); ++oi) { + toNew[oi] = getIndex(oldVertexArray[oi]); + toOld[toNew[oi]] = oi; + } +} + +} // internal namespace + + +void MeshAlg::computeWeld( + const Array<Vector3>& oldVertexArray, + Array<Vector3>& newVertexArray, + Array<int>& toNew, + Array<int>& toOld, + double radius) { + + _internal::Welder welder(oldVertexArray, newVertexArray, toNew, toOld, radius); + welder.weld(); +} + +} // G3D namespace diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp new file mode 100644 index 00000000000..13f731353a6 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp @@ -0,0 +1,377 @@ +/** + @file MeshAlgWeld2.cpp + + @author Morgan McGuire, Kyle Whitson, Corey Taylor + + @created 2008-07-30 + @edited 2008-11-10 + */ + +#include "G3D/platform.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Sphere.h" +#include "G3D/PointHashGrid.h" +#include "G3D/MeshAlg.h" + +namespace G3D { namespace _internal{ + +/** Used by WeldHelper2::smoothNormals. */ +class VN { +public: + Vector3 vertex; + Vector3 normal; + + VN() {} + VN(const Vector3& v, const Vector3& n) : vertex(v), normal(n) {} +}; + +/** Used by WeldHelper::getIndex to maintain a list of vertices by location. */ +class VNTi { +public: + Vector3 vertex; + Vector3 normal; + Vector2 texCoord; + int index; + + VNTi() : index(0) {} + + VNTi(const Vector3& v, const Vector3& n, const Vector2& t, int i) : + vertex(v), normal(n), texCoord(t), index(i) {} +}; + + +}} // G3D + +template <> struct HashTrait <G3D::_internal::VN> { + static size_t hashCode(const G3D::_internal::VN& k) { return static_cast<size_t>(k.vertex.hashCode()); } +}; +template <> struct HashTrait <G3D::_internal::VNTi> { + static size_t hashCode(const G3D::_internal::VNTi& k) { return static_cast<size_t>(k.vertex.hashCode()); } +}; + + +template<> struct EqualsTrait <G3D::_internal::VN> { + static bool equals(const G3D::_internal::VN& a, const G3D::_internal::VN& b) { return a.vertex == b.vertex; } +}; +template<> struct EqualsTrait <G3D::_internal::VNTi> { + static bool equals(const G3D::_internal::VNTi& a, const G3D::_internal::VNTi& b) { return a.vertex == b.vertex; } +}; + +template<> struct PositionTrait<G3D::_internal::VN> { + static void getPosition(const G3D::_internal::VN& v, G3D::Vector3& p) { p = v.vertex; } +}; +template<> struct PositionTrait<G3D::_internal::VNTi> { + static void getPosition(const G3D::_internal::VNTi& v, G3D::Vector3& p) { p = v.vertex; } +}; + +namespace G3D { namespace _internal { + +class WeldHelper { +private: + /** Used by getIndex and updateTriLists */ + PointHashGrid<VNTi> weldGrid; + + Array<Vector3>* outputVertexArray; + Array<Vector3>* outputNormalArray; + Array<Vector2>* outputTexCoordArray; + + float vertexWeldRadius; + /** Squared radius allowed for welding similar normals. */ + float normalWeldRadius2; + float texCoordWeldRadius2; + + float normalSmoothingAngle; + + /** + Returns the index of the vertex in + outputVertexArray/outputNormalArray/outputTexCoordArray + that is within the global tolerances of v,n,t. If there + is no such vertex, adds it to the arrays and returns that index. + + Called from updateTriLists(). + */ + int getIndex(const Vector3& v, const Vector3& n, const Vector2& t) { + PointHashGrid<VNTi>::SphereIterator it = + weldGrid.beginSphereIntersection(Sphere(v, vertexWeldRadius)); + + if (n.isZero()) { + // Don't bother trying to match the surface normal, since this vertex has no surface normal. + while (it.hasMore()) { + if ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2) { + // This is the vertex + return it->index; + } + ++it; + } + } else { + while (it.hasMore()) { + if (((n - it->normal).squaredLength() <= normalWeldRadius2) && + ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2)) { + // This is the vertex + return it->index; + } + ++it; + } + } + + // Note that a sliver triangle processed before its neighbors may reach here + // with a zero length normal. + + // The vertex does not exist. Create it. + const int i = outputVertexArray->size(); + outputVertexArray->append(v); + outputNormalArray->append(n); + outputTexCoordArray->append(t); + + // Store in the grid so that it will be remembered. + weldGrid.insert(VNTi(v, n, t, i)); + + return i; + } + + + /** + Updates each indexArray to refer to vertices in the + outputVertexArray. + + Called from process() + */ + void updateTriLists( + Array<Array<int>*>& indexArrayArray, + const Array<Vector3>& vertexArray, + const Array<Vector3>& normalArray, + const Array<Vector2>& texCoordArray) { + + // Compute a hash grid so that we can find neighbors quickly. + // It begins empty and is extended as the tri lists are iterated + // through. + weldGrid.clear(); + + // Process all triLists + int numTriLists = indexArrayArray.size(); + int u = 0; + for (int t = 0; t < numTriLists; ++t) { + Array<int>& triList = *(indexArrayArray[t]); + + // For all vertices in this list + for (int v = 0; v < triList.size(); ++v) { + // This vertex mapped to u in the flatVertexArray + triList[v] = getIndex(vertexArray[u], normalArray[u], texCoordArray[u]); + + /* +# ifdef G3D_DEBUG + { + int i = triList[v]; + Vector3 N = normalArray[i]; + debugAssertM(N.length() > 0.9f, "Produced non-unit normal"); + } +# endif + */ + ++u; + } + } + } + + /** Expands the indexed triangle lists into a triangle list. + + Called from process() */ + void unroll( + const Array<Array<int>*>& indexArrayArray, + const Array<Vector3>& vertexArray, + const Array<Vector2>& texCoordArray, + Array<Vector3>& unrolledVertexArray, + Array<Vector2>& unrolledTexCoordArray) { + + int numTriLists = indexArrayArray.size(); + for (int t = 0; t < numTriLists; ++t) { + const Array<int>& triList = *(indexArrayArray[t]); + for (int v = 0; v < triList.size(); ++v) { + int i = triList[v]; + unrolledVertexArray.append(vertexArray[i]); + unrolledTexCoordArray.append(texCoordArray[i]); + } + } + } + + /** For every three vertices, compute the face normal and store it three times. + Sliver triangles have a zero surface normal, which we will later take to + match *any* surface normal. */ + void computeFaceNormals( + const Array<Vector3>& vertexArray, + Array<Vector3>& faceNormalArray) { + + debugAssertM(vertexArray.size() % 3 == 0, "Input is not a triangle soup"); + debugAssertM(faceNormalArray.size() == 0, "Output must start empty."); + + for (int v = 0; v < vertexArray.size(); v += 3) { + const Vector3& e0 = vertexArray[v + 1] - vertexArray[v]; + const Vector3& e1 = vertexArray[v + 2] - vertexArray[v]; + + // Note that the length may be zero in the case of sliver polygons, e.g., + // those correcting a T-junction. + const Vector3& n = e0.cross(e1).directionOrZero(); + + // Append the normal once per vertex. + faceNormalArray.append(n, n, n); + } + } + + + /** + Computes @a smoothNormalArray, whose elements are those of normalArray averaged + with neighbors within the angular cutoff. + */ + void smoothNormals( + const Array<Vector3>& vertexArray, + const Array<Vector3>& normalArray, + Array<Vector3>& smoothNormalArray) { + + const float cosThresholdAngle = (float)cos(normalSmoothingAngle); + + debugAssert(vertexArray.size() == normalArray.size()); + smoothNormalArray.resize(normalArray.size()); + + // Compute a hash grid so that we can find neighbors quickly. + PointHashGrid<VN> grid(vertexWeldRadius); + for (int v = 0; v < normalArray.size(); ++v) { + grid.insert(VN(vertexArray[v], normalArray[v])); + } + + for (int v = 0; v < normalArray.size(); ++v) { + // Compute the sum of all nearby normals within the cutoff angle. + // Search within the vertexWeldRadius, since those are the vertices + // that will collapse to the same point. + PointHashGrid<VN>::SphereIterator it = + grid.beginSphereIntersection(Sphere(vertexArray[v], vertexWeldRadius)); + + Vector3 sum; + + const Vector3& original = normalArray[v]; + while (it.hasMore()) { + const Vector3& N = it->normal; + const float cosAngle = N.dot(original); + + if (cosAngle > cosThresholdAngle) { + // This normal is close enough to consider + sum += N; + } + ++it; + } + + const Vector3& average = sum.directionOrZero(); + + const bool indeterminate = average.isZero(); + // Never "smooth" a normal so far that it points backwards + const bool backFacing = original.dot(average) < 0; + + if (indeterminate || backFacing) { + // Revert to the face normal + smoothNormalArray[v] = original; + } else { + // Average available normals + smoothNormalArray[v] = average; + } + } + } + +public: + + + /** + Algorithm: + + 1. Unroll the indexed triangle list into a triangle list, where + there are duplicated vertices. + + 2. Compute face normals for all triangles, and expand those into + the triangle vertices. + + 3. At each vertex, average all normals that are within normalSmoothingAngle. + + 4. Generate output indexArrayArray. While doing so, merge all vertices where + the distance between position, texCoord, and normal is within the thresholds. + */ + void process( + Array<Vector3>& vertexArray, + Array<Vector2>& texCoordArray, + Array<Vector3>& normalArray, + Array<Array<int>*>& indexArrayArray, + float normAngle, + float texRadius, + float normRadius) { + + normalSmoothingAngle = normAngle; + normalWeldRadius2 = square(normRadius); + texCoordWeldRadius2 = square(texRadius); + + const bool hasTexCoords = (texCoordArray.size() > 0); + + if (hasTexCoords) { + debugAssertM(vertexArray.size() == texCoordArray.size(), + "Input arrays are not parallel."); + } + + Array<Vector3> unrolledVertexArray; + Array<Vector3> unrolledFaceNormalArray; + Array<Vector3> unrolledSmoothNormalArray; + Array<Vector2> unrolledTexCoordArray; + + if (! hasTexCoords) { + // Generate all zero texture coordinates + texCoordArray.resize(vertexArray.size()); + } + + // Generate a flat (unrolled) triangle list with texture coordinates. + unroll(indexArrayArray, vertexArray, texCoordArray, + unrolledVertexArray, unrolledTexCoordArray); + + // Put the output back into the input slots. Clear immediately to reduce peak + // memory. + outputVertexArray = &vertexArray; + outputNormalArray = &normalArray; + outputTexCoordArray = &texCoordArray; + outputVertexArray->fastClear(); + outputNormalArray->fastClear(); + outputTexCoordArray->fastClear(); + + // For every three vertices, generate their face normal and store it at + // each vertex. The output array has the same length as the input. + computeFaceNormals(unrolledVertexArray, unrolledFaceNormalArray); + + // Compute smooth normals at vertices. + smoothNormals(unrolledVertexArray, unrolledFaceNormalArray, unrolledSmoothNormalArray); + unrolledFaceNormalArray.clear(); + + // Regenerate the triangle lists + updateTriLists(indexArrayArray, unrolledVertexArray, unrolledSmoothNormalArray, unrolledTexCoordArray); + + if (! hasTexCoords) { + // Throw away the generated texCoords + texCoordArray.resize(0); + } + } + + WeldHelper(float vertRadius) : + weldGrid(vertRadius), + vertexWeldRadius(vertRadius) {} + +}; +} // Internal + +void MeshAlg::weld( + Array<Vector3>& vertexArray, + Array<Vector2>& texCoordArray, + Array<Vector3>& normalArray, + Array<Array<int>*>& indexArrayArray, + float normalSmoothingAngle, + float vertexWeldRadius, + float textureWeldRadius, + float normalWeldRadius) { + + _internal::WeldHelper(vertexWeldRadius).process( + vertexArray, texCoordArray, normalArray, indexArrayArray, + normalSmoothingAngle, textureWeldRadius, normalWeldRadius); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp b/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp new file mode 100644 index 00000000000..43ee6e50ac8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp @@ -0,0 +1,113 @@ +/** + @file MeshBuilder.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-02-27 + @edited 2005-02-24 + */ + +#include "G3D/MeshBuilder.h" +#include "G3D/MeshAlg.h" + +namespace G3D { + +void MeshBuilder::setName(const std::string& n) { + name = n; +} + + +void MeshBuilder::commit(std::string& n, Array<int>& indexArray, Array<Vector3>& outvertexArray) { + n = name; + + // Make the data fit in a unit cube + centerTriList(); + + Array<int> toNew, toOld; + + if (close == MeshBuilder::AUTO_WELD) { + Array<int> index; + MeshAlg::createIndexArray(triList.size(), index); + double minEdgeLen, maxEdgeLen, meanEdgeLen, medianEdgeLen; + double minFaceArea, maxFaceArea, meanFaceArea, medianFaceArea; + MeshAlg::computeAreaStatistics(triList, index, + minEdgeLen, meanEdgeLen, medianEdgeLen, maxEdgeLen, + minFaceArea, meanFaceArea, medianFaceArea, maxFaceArea); + close = minEdgeLen * 0.1; + } + + MeshAlg::computeWeld(triList, outvertexArray, toNew, toOld, close); + + // Construct triangles + for (int t = 0; t < triList.size(); t += 3) { + int index[3]; + + for (int i = 0; i < 3; ++i) { + index[i] = toNew[t + i]; + } + + // Throw out zero size triangles + if ((index[0] != index[1]) && + (index[1] != index[2]) && + (index[2] != index[0])) { + indexArray.append(index[0], index[1], index[2]); + } + } +} + + +void MeshBuilder::centerTriList() { + // Compute the range of the vertices + Vector3 vmin, vmax; + + computeBounds(vmin, vmax); + + Vector3 diagonal = vmax - vmin; + double scale = max(max(diagonal.x, diagonal.y), diagonal.z) / 2; + debugAssert(scale > 0); + + Vector3 translation = vmin + diagonal / 2; + + // Center and scale all vertices in the input list + int v; + + //Matrix3 rot90 = Matrix3::fromAxisAngle(Vector3::UNIT_Y, toRadians(180)) * Matrix3::fromAxisAngle(Vector3::UNIT_X, toRadians(90)); + for (v = 0; v < triList.size(); ++v) { + triList[v] = (triList[v] - translation) / scale; + //triList[v] = rot90 * triList[v]; + } +} + + +void MeshBuilder::computeBounds(Vector3& min, Vector3& max) { + min = Vector3::inf(); + max = -min; + + int v; + for (v = 0; v < triList.size(); ++v) { + min = min.min(triList[v]); + max = max.max(triList[v]); + } +} + + +void MeshBuilder::addTriangle(const Vector3& a, const Vector3& b, const Vector3& c) { + triList.append(a, b, c); + + if (_twoSided) { + triList.append(c, b, a); + } +} + + +void MeshBuilder::addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) { + addTriangle(a, b, c); + addTriangle(a, c, d); +} + + +void MeshBuilder::addTriangle(const Triangle& t) { + addTriangle(t.vertex(0), t.vertex(1), t.vertex(2)); +} + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/NetAddress.cpp b/externals/g3dlite/G3D.lib/source/NetAddress.cpp new file mode 100644 index 00000000000..64d692d4763 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/NetAddress.cpp @@ -0,0 +1,164 @@ +/** + @file NetMessage.cpp + + @maintainer Morgan McGuire, morgan@cs.brown.edu + @created 2005-02-06 + @edited 2005-02-06 + */ +#include "G3D/platform.h" +#include "G3D/NetAddress.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Array.h" +#include "G3D/stringutils.h" +#include "G3D/System.h" +#include "G3D/NetworkDevice.h" + +#if defined(G3D_LINUX) || defined(G3D_OSX) + #include <unistd.h> + #include <errno.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <netinet/tcp.h> + #define _alloca alloca + +# ifndef SOCKADDR_IN +# define SOCKADDR_IN struct sockaddr_in +# endif +# ifndef SOCKET +# define SOCKET int +# endif + +// SOCKADDR_IN is supposed to be defined in NetAddress.h +#ifndef SOCKADDR_IN +# error Network headers included in wrong order +#endif +#endif + + +namespace G3D { + +NetAddress::NetAddress() { + System::memset(&addr, 0, sizeof(addr)); +} + +void NetAddress::init(uint32 host, uint16 port) { + if ((host != 0) || (port != 0)) { + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (host == 0) { + host = INADDR_ANY; + } + addr.sin_addr.s_addr = htonl(host); + } else { + System::memset(&addr, 0, sizeof(addr)); + } +} + + +NetAddress::NetAddress( + const std::string& hostname, + uint16 port) { + init(hostname, port); +} + + +void NetAddress::init( + const std::string& hostname, + uint16 port) { + + uint32 addr; + + if (hostname == "") { + addr = INADDR_NONE; + } else { + addr = inet_addr(hostname.c_str()); + } + + // The address wasn't in numeric form, resolve it + if (addr == INADDR_NONE) { + // Get the IP address of the server and store it in host + struct hostent* host = gethostbyname(hostname.c_str()); + + if (host == NULL) { + init(0, 0); + return; + } + + System::memcpy(&addr, host->h_addr_list[0], host->h_length); + } + + if (addr != INADDR_NONE) { + addr = ntohl(addr); + } + init(addr, port); +} + + +NetAddress::NetAddress(uint32 hostip, uint16 port) { + init(hostip, port); +} + + +NetAddress NetAddress::broadcastAddress(uint16 port) { + return NetAddress(NetworkDevice::instance()->broadcastAddressArray()[0], port); +} + + +NetAddress::NetAddress(const std::string& hostnameAndPort) { + + Array<std::string> part = stringSplit(hostnameAndPort, ':'); + + debugAssert(part.length() == 2); + init(part[0], atoi(part[1].c_str())); +} + + +NetAddress::NetAddress(const SOCKADDR_IN& a) { + addr = a; +} + + +NetAddress::NetAddress(const struct in_addr& addr, uint16 port) { + #ifdef G3D_WIN32 + init(ntohl(addr.S_un.S_addr), port); + #else + init(htonl(addr.s_addr), port); + #endif +} + + +void NetAddress::serialize(class BinaryOutput& b) const { + b.writeUInt32(ip()); + b.writeUInt16(port()); +} + + +void NetAddress::deserialize(class BinaryInput& b) { + uint32 i; + uint16 p; + + i = b.readUInt32(); + p = b.readUInt16(); + + init(i, p); +} + + +bool NetAddress::ok() const { + return addr.sin_family != 0; +} + + +std::string NetAddress::ipString() const { + return format("%s", inet_ntoa(*(in_addr*)&(addr.sin_addr))); +} + + +std::string NetAddress::toString() const { + return ipString() + format(":%d", ntohs(addr.sin_port)); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp b/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp new file mode 100644 index 00000000000..246c97d4dbf --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp @@ -0,0 +1,1362 @@ +/** + @file NetworkDevice.cpp + + @maintainer Morgan McGuire, morgan@cs.brown.edu + @created 2002-11-22 + @edited 2006-02-24 + */ + +#include <stdlib.h> +#include <time.h> +#include "G3D/platform.h" +#include "G3D/TextOutput.h" +#include "G3D/NetworkDevice.h" +#include "G3D/NetAddress.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Log.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/stringutils.h" +#include "G3D/debug.h" + +#include <cstring> + +#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD) +# include <sys/types.h> +# include <sys/socket.h> +# include <ifaddrs.h> +# include <netinet/in.h> +# include <net/if.h> +# ifdef __linux__ +# include <sys/ioctl.h> +# include <netinet/in.h> +# include <unistd.h> +# include <string.h> +// Match Linux to FreeBSD +# define AF_LINK AF_PACKET +# else +# include <net/if_dl.h> +# include <sys/sockio.h> +# endif + + #include <unistd.h> + #include <errno.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <netinet/tcp.h> + #include <sys/ioctl.h> + #include <netinet/if_ether.h> + #include <net/ethernet.h> + #include <net/if.h> + + #include <sys/types.h> + + #define _alloca alloca + + /** Define an error code for non-windows platforms. */ + int WSAGetLastError() { + return -1; + } + + #define SOCKET_ERROR -1 + + static std::string socketErrorCode(int code) { + return G3D::format("CODE %d: %s\n", code, strerror(code)); + } + + static std::string socketErrorCode() { + return socketErrorCode(errno); + } + + static const int WSAEWOULDBLOCK = -100; + + typedef int SOCKET; + typedef struct sockaddr_in SOCKADDR_IN; + +#else + + // Windows + static std::string socketErrorCode(int code) { + LPTSTR formatMsg = NULL; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + code, + 0, + (LPTSTR)&formatMsg, + 0, + NULL); + + return G3D::format("CODE %d: %s\n", code, formatMsg); + } + + static std::string socketErrorCode() { + return socketErrorCode(GetLastError()); + } + +#endif + + +#ifndef _SOCKLEN_T +# if defined(G3D_WIN32) || defined(G3D_OSX) + typedef int socklen_t; +# endif +#endif + +namespace G3D { + +NetworkDevice* NetworkDevice::s_instance = NULL; + +std::ostream& operator<<(std::ostream& os, const NetAddress& a) { + return os << a.toString(); +} + + +static void logSocketInfo(const SOCKET& sock) { + uint32 val; + socklen_t sz = 4; + int ret; + + ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&val, (socklen_t*)&sz); + logPrintf("SOL_SOCKET/SO_RCVBUF = %d\n", val); + + ret = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&val, (socklen_t*)&sz); + logPrintf("SOL_SOCKET/SO_SNDBUF = %d\n", val); + + // Note: timeout = 0 means no timeout + ret = getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&val, (socklen_t*)&sz); + logPrintf("SOL_SOCKET/SO_RCVTIMEO = %d\n", val); + + ret = getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&val, (socklen_t*)&sz); + logPrintf("SOL_SOCKET/SO_SNDTIMEO = %d\n", val); +} + + +///////////////////////////////////////////////////////////////////////////// + +/** Invokes select on one socket. Returns SOCKET_ERROR on error, 0 if + there is no read pending, sock if there a read pending. */ +static int selectOneReadSocket(const SOCKET& sock) { + // 0 time timeout is specified to poll and return immediately + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + // Create a set that contains just this one socket + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(sock, &socketSet); + + int ret = select(sock + 1, &socketSet, NULL, NULL, &timeout); + + return ret; +} + + +/** Returns true if the socket has a read pending */ +static bool readWaiting(const SOCKET& sock) { + int ret = selectOneReadSocket(sock); + + switch (ret) { + case SOCKET_ERROR: + logPrintf("ERROR: selectOneReadSocket returned " + "SOCKET_ERROR in readWaiting(). %s", socketErrorCode().c_str()); + // Return true so that we'll force an error on read and close + // the socket. + return true; + + case 0: + return false; + + default: + return true; + } +} + + +/** Invokes select on one socket. */ +static int selectOneWriteSocket(const SOCKET& sock) { + // 0 time timeout is specified to poll and return immediately + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + // Create a set that contains just this one socket + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(sock, &socketSet); + + return select(sock + 1, NULL, &socketSet, NULL, &timeout); +} + +/////////////////////////////////////////////////////////////////////////////// + +NetworkDevice* NetworkDevice::instance() { + if (s_instance == NULL) { + s_instance = new NetworkDevice(); + if (! s_instance->init()) { + delete s_instance; + s_instance = NULL; + } + } + return s_instance; +} + + +void NetworkDevice::cleanup() { + if (s_instance) { + s_instance->_cleanup(); + delete s_instance; + s_instance = NULL; + } +} + + +NetworkDevice::NetworkDevice() { + initialized = false; +} + + +NetworkDevice::~NetworkDevice() { +} + + +std::string NetworkDevice::localHostName() const { + char ac[128]; + if (gethostname(ac, sizeof(ac)) == -1) { + Log::common()->printf("Error while getting local host name\n"); + return "localhost"; + } + return gethostbyname(ac)->h_name; +} + +#ifndef G3D_WIN32 +const char* errnoToString() { + switch (errno) { + case EBADF: + return "file descriptor is invalid."; + + case EINVAL: + return "Request or argp is not valid."; + + case ENOTTY: + return + "file descriptor is not associated with a character special device OR " + "The specified request does not apply to the " + "kind of object that the descriptor fildes references."; + + case EADDRNOTAVAIL: + return "Address not available."; + + default: + { + static char buffer[20]; + sprintf(buffer, "Error %d", errno); + return buffer; + } + } +} +#endif + + +NetworkDevice::EthernetAdapter::EthernetAdapter() { + name = ""; + ip = 0; + hostname = ""; + subnet = 0; + broadcast = 0; + for (int i = 0; i < 6; ++i) { + mac[i] = 0; + } +} + +void NetworkDevice::EthernetAdapter::describe(TextOutput& t) const { + t.writeSymbol("{"); + t.pushIndent(); + t.writeNewline(); + + t.writeSymbols("hostname", "="); + t.writeString(hostname); + t.writeNewline(); + + t.writeSymbols("name", "="); + t.writeString(name); + t.writeNewline(); + + t.writeSymbols("ip", "="); + t.writeSymbol(formatIP(ip)); + t.writeNewline(); + + t.writeSymbols("subnet", "="); + t.writeSymbol(formatIP(subnet)); + t.writeNewline(); + + t.writeSymbols("broadcast", "="); + t.writeSymbol(formatIP(broadcast)); + t.writeNewline(); + + t.writeSymbols("mac", "="); + t.writeSymbol(formatMAC(mac)); + t.writeNewline(); + + t.popIndent(); + t.writeSymbol("}"); + t.writeNewline(); +} + + +void NetworkDevice::addAdapter(const EthernetAdapter& a) { + m_adapterArray.append(a); + if (a.broadcast != 0) { + int i = m_broadcastAddresses.findIndex(a.broadcast); + if (i == -1) { + m_broadcastAddresses.append(a.broadcast); + } + } +} + + +std::string NetworkDevice::formatIP(uint32 addr) { + return format("%3d.%3d.%3d.%3d", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, + (addr >> 8) & 0xFF, addr & 0xFF); +} + + +std::string NetworkDevice::formatMAC(const uint8 MAC[6]) { + return format("%02x:%02x:%02x:%02x:%02x:%02x", MAC[0], MAC[1], MAC[2], MAC[3], MAC[4], MAC[5]); +} + + +#ifdef G3D_WIN32 + +bool NetworkDevice::init() { + debugAssert(! initialized); + + logPrintf("Network Startup"); + logPrintf("Starting WinSock networking.\n"); + WSADATA wsda; + WSAStartup(MAKEWORD(G3D_WINSOCK_MAJOR_VERSION, G3D_WINSOCK_MINOR_VERSION), &wsda); + + std::string hostname = "localhost"; + { + char ac[128]; + if (gethostname(ac, sizeof(ac)) == -1) { + logPrintf("Warning: Error while getting local host name\n"); + } else { + hostname = gethostbyname(ac)->h_name; + } + } + + EthernetAdapter a; + a.hostname = hostname; + a.name = ""; + a.ip = NetAddress(hostname, 0).ip(); + + // TODO: Find subnet on Win32 + a.subnet = 0x0000FFFF; + + // TODO: Find broadcast on Win32 + a.broadcast = 0xFFFFFFFF; + + // TODO: find MAC on Win32 + + addAdapter(a); + + std::string machine = localHostName(); + std::string addr = NetAddress(machine, 0).ipString(); + logPrintf( + "Network:\n" + " Status: %s\n" + " Loaded winsock specification version %d (%d is " + "the highest available)\n" + " %d sockets available\n" + " Largest UDP datagram packet size is %d bytes\n\n", + wsda.szDescription, + wsda.szSystemStatus, + wsda.wVersion, + wsda.wHighVersion, + wsda.iMaxSockets, + wsda.iMaxUdpDg); + + // TODO: WSAIoctl for subnet and broadcast addresses + // http://msdn.microsoft.com/en-us/library/ms741621(VS.85).aspx + // + // TODO: SIO_GET_INTERFACE_LIST + + initialized = true; + + return true; +} +#endif + + +#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD) + +const sockaddr_in* castToIP4(const sockaddr* addr) { + if (addr == NULL) { + return NULL; + } else if (addr->sa_family == AF_INET) { + // An IPv4 address + return reinterpret_cast<const sockaddr_in*>(addr); + } else { + // Not an IPv4 address + return NULL; + } +} + +uint32 getIP(const sockaddr_in* addr) { + if (addr != NULL) { + return ntohl(addr->sin_addr.s_addr); + } else { + return 0; + } +} + + +bool NetworkDevice::init() { + debugAssert(! initialized); + + // Used for combining the MAC and ip information + typedef Table<std::string, EthernetAdapter> AdapterTable; + + AdapterTable table; + + // Head of a linked list of network interfaces on this machine + ifaddrs* ifap = NULL; + + int r = getifaddrs(&ifap); + + if (r != 0) { + logPrintf("ERROR: getifaddrs returned %d\n", r); + return false; + } + + ifaddrs* current = ifap; + + if (current == NULL) { + logPrintf("WARNING: No network interfaces found\n"); + EthernetAdapter a; + a.name = "fallback"; + a.hostname = "localhost"; + a.ip = (127 << 24) | 1; + a.broadcast = 0xFFFFFFFF; + a.subnet = 0x000000FF; + addAdapter(a); + + } else { + + while (current != NULL) { + + bool up = (current->ifa_flags & IFF_UP); + bool loopback = (current->ifa_flags & IFF_LOOPBACK); + + if (! up || loopback) { + // Skip this adapter; it is offline or is a loopback + current = current->ifa_next; + continue; + } + + if (! table.containsKey(current->ifa_name)) { + EthernetAdapter a; + a.name = current->ifa_name; + table.set(a.name, a); + } + + // This adapter must exist because it was created above + EthernetAdapter& adapter = table[current->ifa_name]; + + const sockaddr_in* interfaceAddress = castToIP4(current->ifa_addr); + const sockaddr_in* broadcastAddress = castToIP4(current->ifa_dstaddr); + const sockaddr_in* subnetMask = castToIP4(current->ifa_netmask); + + uint32 ip = getIP(interfaceAddress); + uint32 ba = getIP(broadcastAddress); + uint32 sn = getIP(subnetMask); + + if (ip != 0) { + adapter.ip = ip; + } + + if (ba != 0) { + adapter.broadcast = ba; + } + + if (sn != 0) { + adapter.subnet = sn; + } + + uint8_t* MAC = NULL; + // Extract MAC address + if ((current->ifa_addr != NULL) && (current->ifa_addr->sa_family == AF_LINK)) { +# ifdef __linux__ + { + // Linux + struct ifreq ifr; + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + ifr.ifr_addr.sa_family = AF_INET; + strcpy(ifr.ifr_name, current->ifa_name); + ioctl(fd, SIOCGIFHWADDR, &ifr); + close(fd); + + MAC = reinterpret_cast<uint8_t*>(ifr.ifr_hwaddr.sa_data); + } +# else + { + // The MAC address and the interfaceAddress come in as + // different interfaces with the same name. + + // Posix/FreeBSD/Mac OS + sockaddr_dl* sdl = (struct sockaddr_dl *)current->ifa_addr; + MAC = reinterpret_cast<uint8_t*>(LLADDR(sdl)); + } +# endif + + // See if there was a MAC address + if (MAC != NULL) { + bool anyNonZero = false; + for (int i = 0; i < 6; ++i) { + anyNonZero = anyNonZero || (MAC[i] != 0); + } + if (anyNonZero) { + System::memcpy(adapter.mac, MAC, 6); + } + } + } + + current = current->ifa_next; + } + + freeifaddrs(ifap); + ifap = NULL; + } + + // Extract all interesting adapters from the table + for (AdapterTable::Iterator it = table.begin(); it.hasMore(); ++it) { + const EthernetAdapter& adapter = it->value; + + // Only add adapters that have IP addresses + if (adapter.ip != 0) { + addAdapter(adapter); + } else { + logPrintf("NetworkDevice: Ignored adapter %s because ip = 0\n", adapter.name.c_str()); + } + } + + initialized = true; + + return true; +} + +#endif + + +void NetworkDevice::_cleanup() { + debugAssert(initialized); + + logPrintf("Network Cleanup"); +# ifdef G3D_WIN32 + WSACleanup(); +# endif + logPrintf("Network cleaned up."); +} + +bool NetworkDevice::bind(SOCKET sock, const NetAddress& addr) const { + Log::common()->printf("Binding socket %d on port %d ", + sock, htons(addr.addr.sin_port)); + if (::bind(sock, (struct sockaddr*)&(addr.addr), sizeof(addr.addr)) == + SOCKET_ERROR) { + + Log::common()->println("FAIL"); + Log::common()->println(socketErrorCode()); + closesocket(sock); + return false; + } + + Log::common()->println("Ok"); + return true; +} + + +void NetworkDevice::closesocket(SOCKET& sock) const { + if (sock != 0) { + #ifdef G3D_WIN32 + ::closesocket(sock); + #else + close(sock); + #endif + + Log::common()->printf("Closed socket %d\n", sock); + sock = 0; + } +} + + +void NetworkDevice::localHostAddresses(Array<NetAddress>& array) const { + array.resize(0); + + char ac[128]; + + if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) { + Log::common()->printf("Error while getting local host name\n"); + return; + } + + struct hostent* phe = gethostbyname(ac); + if (phe == 0) { + Log::common()->printf("Error while getting local host address\n"); + return; + } + + for (int i = 0; (phe->h_addr_list[i] != 0); ++i) { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + array.append(NetAddress(addr)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +Conduit::Conduit() : binaryOutput("<memory>", G3D_LITTLE_ENDIAN) { + sock = 0; + mSent = 0; + mReceived = 0; + bSent = 0; + bReceived = 0; +} + + +Conduit::~Conduit() { + NetworkDevice::instance()->closesocket(sock); +} + + +uint64 Conduit::bytesSent() const { + return bSent; +} + + +uint64 Conduit::bytesReceived() const { + return bReceived; +} + + +uint64 Conduit::messagesSent() const { + return mSent; +} + + +uint64 Conduit::messagesReceived() const { + return mReceived; +} + + +bool Conduit::ok() const { + return (sock != 0) && (sock != SOCKET_ERROR); +} + + +bool Conduit::messageWaiting() { + return readWaiting(sock); +} + + +/** + Increases the send and receive sizes of a socket to 2 MB from 8k + */ +static void increaseBufferSize(SOCKET sock) { + + // Increase the buffer size; the default (8192) is too easy to + // overflow when the network latency is high. + { + uint32 val = 1024 * 1024 * 2; + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (char*)&val, sizeof(val)) == SOCKET_ERROR) { + Log::common()->printf("WARNING: Increasing socket " + "receive buffer to %d failed.\n", val); + Log::common()->println(socketErrorCode()); + } + + if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, + (char*)&val, sizeof(val)) == SOCKET_ERROR) { + Log::common()->printf("WARNING: Increasing socket " + "send buffer to %d failed.\n", val); + Log::common()->println(socketErrorCode()); + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +ReliableConduitRef ReliableConduit::create(const NetAddress& address) { + return new ReliableConduit(address); +} + + +ReliableConduit::ReliableConduit( + const NetAddress& _addr) : state(NO_MESSAGE), receiveBuffer(NULL), + receiveBufferTotalSize(0), receiveBufferUsedSize(0) { + + NetworkDevice* nd = NetworkDevice::instance(); + + messageType = 0; + + addr = _addr; + Log::common()->print("Creating a TCP socket "); + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + + if (sock == SOCKET_ERROR) { + Log::common()->println("FAIL"); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + return; + } + + Log::common()->println("Ok"); + + // Setup socket options (both constructors should set the same options) + + // Disable Nagle's algorithm (we send lots of small packets) + const int T = true; + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (const char*)&T, sizeof(T)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Disabling Nagel's " + "algorithm failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Disabled Nagel's algorithm."); + } + + // Set the NO LINGER option so the socket doesn't hang around if + // there is unsent data in the queue when it closes. + struct linger ling; + ling.l_onoff = 0; + ling.l_linger = 0; + if (setsockopt(sock, SOL_SOCKET, SO_LINGER, + (const char*)&ling, sizeof(ling)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Setting socket no linger failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Set socket option no_linger."); + } + + // Set reuse address so that a new server can start up soon after + // an old one has closed. + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (const char*)&T, sizeof(T)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Setting socket reuseaddr failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Set socket option reuseaddr."); + } + + // Ideally, we'd like to specify IPTOS_LOWDELAY as well. + + logSocketInfo(sock); + + increaseBufferSize(sock); + + Log::common()->printf("Created TCP socket %d\n", sock); + + std::string x = addr.toString(); + Log::common()->printf("Connecting to %s on TCP socket %d ", x.c_str(), sock); + + int ret = connect(sock, (struct sockaddr *) &(addr.addr), sizeof(addr.addr)); + + if (ret == WSAEWOULDBLOCK) { + RealTime t = System::time() + 5.0; + // Non-blocking; we must wait until select returns non-zero + while ((selectOneWriteSocket(sock) == 0) && (System::time() < t)) { + System::sleep(0.02); + } + + // TODO: check for failure on the select call + + } else if (ret != 0) { + sock = (SOCKET)SOCKET_ERROR; + Log::common()->println("FAIL"); + Log::common()->println(socketErrorCode()); + return; + } + + Log::common()->println("Ok"); +} + + +ReliableConduit::ReliableConduit( + const SOCKET& _sock, + const NetAddress& _addr) : + state(NO_MESSAGE), + receiveBuffer(NULL), + receiveBufferTotalSize(0), + receiveBufferUsedSize(0) { + sock = _sock; + addr = _addr; + + messageType = 0; + + // Setup socket options (both constructors should set the same options) + + // Disable Nagle's algorithm (we send lots of small packets) + const int T = true; + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (const char*)&T, sizeof(T)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Disabling Nagel's algorithm failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Disabled Nagel's algorithm."); + } + + // Set the NO LINGER option so the socket doesn't hang around if + // there is unsent data in the queue when it closes. + struct linger ling; + ling.l_onoff = 0; + ling.l_linger = 0; + if (setsockopt(sock, SOL_SOCKET, SO_LINGER, + (const char*)&ling, sizeof(ling)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Setting socket no linger failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Set socket option no_linger."); + } + + // Set reuse address so that a new server can start up soon after + // an old one has closed. + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (const char*)&T, sizeof(T)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Setting socket reuseaddr failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Set socket option reuseaddr."); + } + + // Ideally, we'd like to specify IPTOS_LOWDELAY as well. + + logSocketInfo(sock); +} + + +ReliableConduit::~ReliableConduit() { + free(receiveBuffer); + receiveBuffer = NULL; + receiveBufferTotalSize = 0; + receiveBufferUsedSize = 0; +} + + +bool ReliableConduit::messageWaiting() { + switch (state) { + case HOLDING: + // We've already read the message and are waiting + // for a receive call. + return true; + + case RECEIVING: + + if (! ok()) { + return false; + } + // We're currently receiving the message. Read a little more. + receiveIntoBuffer(); + + if (messageSize == receiveBufferUsedSize) { + // We've read the whole mesage. Switch to holding state + // and return true. + state = HOLDING; + return true; + } else { + // There are more bytes left to read. We'll read them on + // the next call. Because the *entire* message is not ready, + // return false. + return false; + } + break; + + case NO_MESSAGE: + if (Conduit::messageWaiting()) { + // Message incoming. Read the header. + + state = RECEIVING; + receiveHeader(); + + // Loop back around now that we're in the receive state; we + // may be able to read the whole message before returning + // to the caller. + return messageWaiting(); + } else { + // No message incoming. + return false; + } + } + + debugAssertM(false, "Should not reach this point"); + return false; +} + + +uint32 ReliableConduit::waitingMessageType() { + // The messageWaiting call is what actually receives the message. + if (messageWaiting()) { + return messageType; + } else { + return 0; + } +} + + +void ReliableConduit::sendBuffer(const BinaryOutput& b) { + NetworkDevice* nd = NetworkDevice::instance(); + int ret = ::send(sock, (const char*)b.getCArray(), b.size(), 0); + + if (ret == SOCKET_ERROR) { + Log::common()->println("Error occured while sending message."); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + return; + } + + ++mSent; + bSent += b.size(); + + // Verify the packet was actually sent + // Conversion to unsigned is safe because -1 is caught earlier + debugAssert(ret == b.size()); +} + + +/** Null serializer. Used by reliable conduit::send(type) */ +class Dummy { +public: + void serialize(BinaryOutput& b) const { (void)b; } +}; + + +void ReliableConduit::send(uint32 type) { + static Dummy dummy; + send(type, dummy); +} + + + +NetAddress ReliableConduit::address() const { + return addr; +} + + +void ReliableConduit::receiveHeader() { + NetworkDevice* nd = NetworkDevice::instance(); + debugAssert(state == RECEIVING); + + // Read the type + uint32 tmp; + int ret = recv(sock, (char*)&tmp, sizeof(tmp), 0); + + // The type is the first four bytes. It is little endian. + if (System::machineEndian() == G3D_LITTLE_ENDIAN) { + messageType = tmp; + } else { + // Swap the byte order + for (int i = 0; i < 4; ++i) { + ((char*)&messageType)[i] = ((char*)&tmp)[3 - i]; + } + } + + if ((ret == SOCKET_ERROR) || (ret != sizeof(messageType))) { + Log::common()->printf("Call to recv failed. ret = %d," + " sizeof(messageType) = %d\n", + (int)ret, (int)sizeof(messageType)); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + messageType = 0; + return; + } + + // Read the size + ret = recv(sock, (char*)&messageSize, sizeof(messageSize), 0); + + if ((ret == SOCKET_ERROR) || (ret != sizeof(messageSize))) { + Log::common()->printf("Call to recv failed. ret = %d," + " sizeof(len) = %d\n", (int)ret, + (int)sizeof(messageSize)); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + messageType = 0; + return; + } + + messageSize = ntohl(messageSize); + debugAssert(messageSize < 6e7); + + debugAssert(receiveBufferUsedSize == 0); + + // Extend the size of the buffer. + if (messageSize > receiveBufferTotalSize) { + receiveBuffer = realloc(receiveBuffer, messageSize); + receiveBufferTotalSize = messageSize; + } + + if (receiveBuffer == NULL) { + Log::common()->println("Could not allocate a memory buffer " + "during receivePacket."); + nd->closesocket(sock); + } + + bReceived += 4; +} + + +void ReliableConduit::receiveIntoBuffer() { + NetworkDevice* nd = NetworkDevice::instance(); + + debugAssert(state == RECEIVING); + debugAssert(messageType != 0); + debugAssertM(receiveBufferUsedSize < messageSize, "Message already received."); + debugAssertM(messageSize >= receiveBufferUsedSize, "Message size overflow."); + + // Read the data itself + int ret = 0; + uint32 left = messageSize - receiveBufferUsedSize; + int count = 0; + while ((ret != SOCKET_ERROR) && (left > 0) && (count < 100)) { + + ret = recv(sock, ((char*)receiveBuffer) + receiveBufferUsedSize, left, 0); + + if (ret > 0) { + left -= ret; + receiveBufferUsedSize += ret; + bReceived += ret; + + if (left > 0) { + // There's still more. Give the machine a chance to read + // more data, but don't wait forever. + + ++count; + System::sleep(0.001); + } + } else { + // Something went wrong; our blocking read returned nothing. + break; + } + } + + if ((ret == 0) || (ret == SOCKET_ERROR)) { + + if (ret == SOCKET_ERROR) { + Log::common()->printf("Call to recv failed. ret = %d," + " sizeof(messageSize) = %d\n", ret, messageSize); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->printf("recv returned 0\n"); + } + nd->closesocket(sock); + return; + } + + ++mReceived; +} + + +/////////////////////////////////////////////////////////////////////////////// +LightweightConduitRef LightweightConduit::create( + uint16 receivePort, + bool enableReceive, + bool enableBroadcast) { + + return new LightweightConduit(receivePort, enableReceive, enableBroadcast); +} + +LightweightConduit::LightweightConduit( + uint16 port, + bool enableReceive, + bool enableBroadcast) { + NetworkDevice* nd = NetworkDevice::instance(); + + Log::common()->print("Creating a UDP socket "); + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (sock == SOCKET_ERROR) { + sock = 0; + Log::common()->println("FAIL"); + Log::common()->println(socketErrorCode()); + return; + } + Log::common()->println("Ok"); + + if (enableReceive) { + debugAssert(port != 0); + if (! nd->bind(sock, NetAddress(0, port))) { + nd->closesocket(sock); + sock = (SOCKET)SOCKET_ERROR; + } + } + + // Figuring out the MTU seems very complicated, so we just set it to 1000, + // which is likely to be safe. See IP_MTU for more information. + MTU = 1000; + + increaseBufferSize(sock); + + if (enableBroadcast) { + int TR = true; + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, + (const char*)&TR, sizeof(TR)) != 0) { + Log::common()->println("Call to setsockopt failed"); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + sock = 0; + return; + } + } + + Log::common()->printf("Done creating UDP socket %d\n", sock); + + alreadyReadMessage = false; +} + + +LightweightConduit::~LightweightConduit() { +} + + +bool LightweightConduit::receive(NetAddress& sender) { + // This both checks to ensure that a message was waiting and + // actively consumes the message from the network stream if + // it has not been read yet. + uint32 t = waitingMessageType(); + if (t == 0) { + return false; + } + + sender = messageSender; + alreadyReadMessage = false; + + if (messageBuffer.size() < 4) { + // Something went wrong + return false; + } + + return true; +} + + +void LightweightConduit::sendBuffer(const NetAddress& a, BinaryOutput& b) { + NetworkDevice* nd = NetworkDevice::instance(); + if (sendto(sock, (const char*)b.getCArray(), b.size(), 0, + (struct sockaddr *) &(a.addr), sizeof(a.addr)) == SOCKET_ERROR) { + Log::common()->printf("Error occured while sending packet " + "to %s\n", inet_ntoa(a.addr.sin_addr)); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + } else { + ++mSent; + bSent += b.size(); + } +} + + +bool LightweightConduit::messageWaiting() { + // We may have already pulled the message off the network stream + return alreadyReadMessage || Conduit::messageWaiting(); +} + + +uint32 LightweightConduit::waitingMessageType() { + NetworkDevice* nd = NetworkDevice::instance(); + if (! messageWaiting()) { + return 0; + } + + if (! alreadyReadMessage) { + messageBuffer.resize(8192); + + SOCKADDR_IN remote_addr; + int iRemoteAddrLen = sizeof(sockaddr); + + int ret = recvfrom(sock, (char*)messageBuffer.getCArray(), + messageBuffer.size(), 0, (struct sockaddr *) &remote_addr, + (socklen_t*)&iRemoteAddrLen); + + if (ret == SOCKET_ERROR) { + Log::common()->println("Error: recvfrom failed in " + "LightweightConduit::waitingMessageType()."); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + messageBuffer.resize(0); + messageSender = NetAddress(); + messageType = 0; + return 0; + } + + messageSender = NetAddress(remote_addr); + + ++mReceived; + bReceived += ret; + + messageBuffer.resize(ret, DONT_SHRINK_UNDERLYING_ARRAY); + + // The type is the first four bytes. It is little endian. + if (System::machineEndian() == G3D_LITTLE_ENDIAN) { + messageType = *((uint32*)messageBuffer.getCArray()); + } else { + // Swap the byte order + for (int i = 0; i < 4; ++i) { + ((char*)&messageType)[i] = messageBuffer[3 - i]; + } + } + + alreadyReadMessage = true; + } + + return messageType; +} + + +/////////////////////////////////////////////////////////////////////////////// + +NetListenerRef NetListener::create(const uint16 port) { + return new NetListener(port); +} + + +NetListener::NetListener(uint16 port) { + NetworkDevice* nd = NetworkDevice::instance(); + + // Start the listener socket + Log::common()->print("Creating a listener "); + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + + if (sock == SOCKET_ERROR) { + Log::common()->printf("FAIL"); + Log::common()->println(socketErrorCode()); + return; + } + Log::common()->println("Ok"); + + const int T = true; + + // Set reuse address so that a new server can start up soon after + // an old one has closed. + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (const char*)&T, sizeof(T)) == SOCKET_ERROR) { + + Log::common()->println("WARNING: Setting socket reuseaddr failed."); + Log::common()->println(socketErrorCode()); + } else { + Log::common()->println("Set socket option reuseaddr."); + } + + + if (! nd->bind(sock, NetAddress(0, port))) { + Log::common()->printf("Unable to bind!\n"); + nd->closesocket(sock); + sock = (SOCKET)SOCKET_ERROR; + return; + } + + Log::common()->printf("Listening on port %5d ", port); + + // listen is supposed to return 0 when there is no error. + // The 2nd argument is the number of connections to allow pending + // at any time. + int L = listen(sock, 100); + if (L == SOCKET_ERROR) { + Log::common()->println("FAIL"); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + sock = (SOCKET)SOCKET_ERROR; + return; + } + Log::common()->println("Ok"); + Log::common()->printf("Now listening on socket %d.\n\n", sock); +} + + +NetListener::~NetListener() { + NetworkDevice* nd = NetworkDevice::instance(); + nd->closesocket(sock); +} + + +ReliableConduitRef NetListener::waitForConnection() { + NetworkDevice* nd = NetworkDevice::instance(); + // The address of the connecting host + SOCKADDR_IN remote_addr; + int iAddrLen = sizeof(remote_addr); + + Log::common()->println("Blocking in NetListener::waitForConnection()."); + + SOCKET sClient = accept(sock, (struct sockaddr*) &remote_addr, + (socklen_t*)&iAddrLen); + + if (sClient == SOCKET_ERROR) { + Log::common()->println("Error in NetListener::acceptConnection."); + Log::common()->println(socketErrorCode()); + nd->closesocket(sock); + return NULL; + } + + Log::common()->printf("%s connected, transferred to socket %d.\n", + inet_ntoa(remote_addr.sin_addr), sClient); + + #ifndef G3D_WIN32 + return new ReliableConduit(sClient, + NetAddress(htonl(remote_addr.sin_addr.s_addr), + ntohs(remote_addr.sin_port))); + #else + return new ReliableConduit(sClient, + NetAddress(ntohl(remote_addr.sin_addr.S_un.S_addr), + ntohs(remote_addr.sin_port))); + #endif +} + + +bool NetListener::ok() const { + return (sock != 0) && (sock != SOCKET_ERROR); +} + + +bool NetListener::clientWaiting() const { + return readWaiting(sock); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +void NetworkDevice::describeSystem( + TextOutput& t) { + + t.writeSymbols("Network", "{"); + t.writeNewline(); + t.pushIndent(); + + for (int i = 0; i < m_adapterArray.size(); ++i) { + m_adapterArray[i].describe(t); + } + + + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); +} + + +void NetworkDevice::describeSystem( + std::string& s) { + + TextOutput t; + describeSystem(t); + t.commitString(s); +} + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp b/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp new file mode 100644 index 00000000000..034e585d01f --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp @@ -0,0 +1,77 @@ +/** + @file PhysicsFrame.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-07-09 + @edited 2006-01-25 +*/ + +#include "G3D/platform.h" +#include "G3D/PhysicsFrame.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +PhysicsFrame::PhysicsFrame() { + translation = Vector3::zero(); + rotation = Quat(); +} + + +PhysicsFrame::PhysicsFrame( + const CoordinateFrame& coordinateFrame) { + + translation = coordinateFrame.translation; + rotation = Quat(coordinateFrame.rotation); +} + + +PhysicsFrame PhysicsFrame::operator*(const PhysicsFrame& other) const { + PhysicsFrame result; + + result.rotation = rotation * other.rotation; + result.translation = translation + rotation.toRotationMatrix() * other.translation; + + return result; +} + + +CoordinateFrame PhysicsFrame::toCoordinateFrame() const { + CoordinateFrame f; + + f.translation = translation; + f.rotation = rotation.toRotationMatrix(); + + return f; +} + + +PhysicsFrame PhysicsFrame::lerp( + const PhysicsFrame& other, + float alpha) const { + + PhysicsFrame result; + + result.translation = translation.lerp(other.translation, alpha); + result.rotation = rotation.slerp(other.rotation, alpha); + + return result; +} + + +void PhysicsFrame::deserialize(class BinaryInput& b) { + translation.deserialize(b); + rotation.deserialize(b); +} + + +void PhysicsFrame::serialize(class BinaryOutput& b) const { + translation.serialize(b); + rotation.serialize(b); +} + + +}; // namespace + diff --git a/externals/g3dlite/G3D.lib/source/Plane.cpp b/externals/g3dlite/G3D.lib/source/Plane.cpp new file mode 100644 index 00000000000..bb8ed5c6033 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Plane.cpp @@ -0,0 +1,149 @@ +/** + @file Plane.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2003-02-06 + @edited 2006-01-29 + */ + +#include "G3D/platform.h" +#include "G3D/Plane.h" +#include "G3D/BinaryOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/stringutils.h" + +namespace G3D { + +Plane::Plane(class BinaryInput& b) { + deserialize(b); +} + + +void Plane::serialize(class BinaryOutput& b) const { + _normal.serialize(b); + b.writeFloat64(_distance); +} + + +void Plane::deserialize(class BinaryInput& b) { + _normal.deserialize(b); + _distance = (float)b.readFloat64(); +} + + +Plane::Plane( + Vector4 point0, + Vector4 point1, + Vector4 point2) { + + debugAssertM( + point0.w != 0 || + point1.w != 0 || + point2.w != 0, + "At least one point must be finite."); + + // Rotate the points around so that the finite points come first. + + while ((point0.w == 0) && + ((point1.w == 0) || (point2.w != 0))) { + Vector4 temp = point0; + point0 = point1; + point1 = point2; + point2 = temp; + } + + Vector3 dir1; + Vector3 dir2; + + if (point1.w == 0) { + // 1 finite, 2 infinite points; the plane must contain + // the direction of the two direcitons + dir1 = point1.xyz(); + dir2 = point2.xyz(); + } else if (point2.w != 0) { + // 3 finite points, the plane must contain the directions + // betwseen the points. + dir1 = point1.xyz() - point0.xyz(); + dir2 = point2.xyz() - point0.xyz(); + } else { + // 2 finite, 1 infinite point; the plane must contain + // the direction between the first two points and the + // direction of the third point. + dir1 = point1.xyz() - point0.xyz(); + dir2 = point2.xyz(); + } + + _normal = dir1.cross(dir2).direction(); + _distance = _normal.dot(point0.xyz()); +} + + +Plane::Plane( + const Vector3& point0, + const Vector3& point1, + const Vector3& point2) { + + _normal = (point1 - point0).cross(point2 - point0).direction(); + _distance = _normal.dot(point0); +} + + +Plane::Plane( + const Vector3& __normal, + const Vector3& point) { + + _normal = __normal.direction(); + _distance = _normal.dot(point); +} + + +Plane Plane::fromEquation(float a, float b, float c, float d) { + Vector3 n(a, b, c); + float magnitude = n.magnitude(); + d /= magnitude; + n /= magnitude; + return Plane(n, -d); +} + + +void Plane::flip() { + _normal = -_normal; + _distance = -_distance; +} + + +void Plane::getEquation(Vector3& n, float& d) const { + double _d; + getEquation(n, _d); + d = (float)_d; +} + +void Plane::getEquation(Vector3& n, double& d) const { + n = _normal; + d = -_distance; +} + + +void Plane::getEquation(float& a, float& b, float& c, float& d) const { + double _a, _b, _c, _d; + getEquation(_a, _b, _c, _d); + a = (float)_a; + b = (float)_b; + c = (float)_c; + d = (float)_d; +} + +void Plane::getEquation(double& a, double& b, double& c, double& d) const { + a = _normal.x; + b = _normal.y; + c = _normal.z; + d = -_distance; +} + + +std::string Plane::toString() const { + return format("Plane(%g, %g, %g, %g)", _normal.x, _normal.y, _normal.z, _distance); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Quat.cpp b/externals/g3dlite/G3D.lib/source/Quat.cpp new file mode 100644 index 00000000000..225c5b51acc --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Quat.cpp @@ -0,0 +1,583 @@ +/** + @file Quat.cpp + + Quaternion implementation based on Watt & Watt page 363 + + @author Morgan McGuire, graphics3d.com + + @created 2002-01-23 + @edited 2006-01-31 + */ + +#include "G3D/Quat.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Quat Quat::fromAxisAngleRotation( + const Vector3& axis, + float angle) { + + Quat q; + + q.w = cos(angle / 2.0f); + q.imag() = axis.direction() * sin(angle / 2.0f); + + return q; +} + + +Quat::Quat( + const Matrix3& rot) { + + static const int plus1mod3[] = {1, 2, 0}; + + // Find the index of the largest diagonal component + // These ? operations hopefully compile to conditional + // move instructions instead of branches. + int i = (rot[1][1] > rot[0][0]) ? 1 : 0; + i = (rot[2][2] > rot[i][i]) ? 2 : i; + + // Find the indices of the other elements + int j = plus1mod3[i]; + int k = plus1mod3[j]; + + // Index the elements of the vector part of the quaternion as a float* + float* v = (float*)(this); + + // If we attempted to pre-normalize and trusted the matrix to be + // perfectly orthonormal, the result would be: + // + // double c = sqrt((rot[i][i] - (rot[j][j] + rot[k][k])) + 1.0) + // v[i] = -c * 0.5 + // v[j] = -(rot[i][j] + rot[j][i]) * 0.5 / c + // v[k] = -(rot[i][k] + rot[k][i]) * 0.5 / c + // w = (rot[j][k] - rot[k][j]) * 0.5 / c + // + // Since we're going to pay the sqrt anyway, we perform a post normalization, which also + // fixes any poorly normalized input. Multiply all elements by 2*c in the above, giving: + + // nc2 = -c^2 + double nc2 = ((rot[j][j] + rot[k][k]) - rot[i][i]) - 1.0; + v[i] = nc2; + w = (rot[j][k] - rot[k][j]); + v[j] = -(rot[i][j] + rot[j][i]); + v[k] = -(rot[i][k] + rot[k][i]); + + // We now have the correct result with the wrong magnitude, so normalize it: + float s = sqrt(x*x + y*y + z*z + w*w); + if (s > 0.00001f) { + s = 1.0f / s; + x *= s; + y *= s; + z *= s; + w *= s; + } else { + // The quaternion is nearly zero. Make it 0 0 0 1 + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 1.0f; + } +} + + +void Quat::toAxisAngleRotation( + Vector3& axis, + double& angle) const { + + // Decompose the quaternion into an angle and an axis. + + axis = Vector3(x, y, z); + angle = 2 * acos(w); + + float len = sqrt(1.0f - w * w); + + if (fuzzyGt(abs(len), 0.0f)) { + axis /= len; + } + + // Reduce the range of the angle. + + if (angle < 0) { + angle = -angle; + axis = -axis; + } + + while (angle > twoPi()) { + angle -= twoPi(); + } + + if (abs(angle) > pi()) { + angle -= twoPi(); + } + + // Make the angle positive. + + if (angle < 0.0f) { + angle = -angle; + axis = -axis; + } +} + + +Matrix3 Quat::toRotationMatrix() const { + Matrix3 out = Matrix3::zero(); + + toRotationMatrix(out); + + return out; +} + + +void Quat::toRotationMatrix( + Matrix3& rot) const { + + rot = Matrix3(*this); +} + + +Quat Quat::slerp( + const Quat& _quat1, + float alpha, + float threshold) const { + + // From: Game Physics -- David Eberly pg 538-540 + // Modified to include lerp for small angles, which + // is a common practice. + + // See also: + // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html + + const Quat& quat0 = *this; + Quat quat1 = _quat1; + + // angle between quaternion rotations + float phi; + float cosphi = quat0.dot(quat1); + + + if (cosphi < 0) { + // Change the sign and fix the dot product; we need to + // loop the other way to get the shortest path + quat1 = -quat1; + cosphi = -cosphi; + } + + // Using G3D::aCos will clamp the angle to 0 and pi + phi = static_cast<float>(G3D::aCos(cosphi)); + + if (phi >= threshold) { + // For large angles, slerp + float scale0, scale1; + + scale0 = sin((1.0f - alpha) * phi); + scale1 = sin(alpha * phi); + + return ( (quat0 * scale0) + (quat1 * scale1) ) / sin(phi); + } else { + // For small angles, linear interpolate + return quat0.nlerp(quat1, alpha); + } +} + + +Quat Quat::nlerp( + const Quat& quat1, + float alpha) const { + + Quat result = (*this) * (1.0f - alpha) + quat1 * alpha; + return result / result.magnitude(); +} + + +Quat Quat::operator*(const Quat& other) const { + + // Following Watt & Watt, page 360 + const Vector3& v1 = imag(); + const Vector3& v2 = other.imag(); + float s1 = w; + float s2 = other.w; + + return Quat(s1*v2 + s2*v1 + v1.cross(v2), s1*s2 - v1.dot(v2)); +} + + +// From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III. +Quat Quat::unitRandom() { + float x0 = uniformRandom(); + float r1 = sqrtf(1 - x0), + r2 = sqrtf(x0); + float t1 = (float)G3D::twoPi() * uniformRandom(); + float t2 = (float)G3D::twoPi() * uniformRandom(); + float c1 = cosf(t1), + s1 = sinf(t1); + float c2 = cosf(t2), + s2 = sinf(t2); + return Quat(s1 * r1, c1 * r1, s2 * r2, c2 * r2); +} + + +void Quat::deserialize(class BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); + z = b.readFloat32(); + w = b.readFloat32(); +} + + +void Quat::serialize(class BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); + b.writeFloat32(z); + b.writeFloat32(w); +} + + +// 2-char swizzles + +Vector2 Quat::xx() const { return Vector2 (x, x); } +Vector2 Quat::yx() const { return Vector2 (y, x); } +Vector2 Quat::zx() const { return Vector2 (z, x); } +Vector2 Quat::wx() const { return Vector2 (w, x); } +Vector2 Quat::xy() const { return Vector2 (x, y); } +Vector2 Quat::yy() const { return Vector2 (y, y); } +Vector2 Quat::zy() const { return Vector2 (z, y); } +Vector2 Quat::wy() const { return Vector2 (w, y); } +Vector2 Quat::xz() const { return Vector2 (x, z); } +Vector2 Quat::yz() const { return Vector2 (y, z); } +Vector2 Quat::zz() const { return Vector2 (z, z); } +Vector2 Quat::wz() const { return Vector2 (w, z); } +Vector2 Quat::xw() const { return Vector2 (x, w); } +Vector2 Quat::yw() const { return Vector2 (y, w); } +Vector2 Quat::zw() const { return Vector2 (z, w); } +Vector2 Quat::ww() const { return Vector2 (w, w); } + +// 3-char swizzles + +Vector3 Quat::xxx() const { return Vector3 (x, x, x); } +Vector3 Quat::yxx() const { return Vector3 (y, x, x); } +Vector3 Quat::zxx() const { return Vector3 (z, x, x); } +Vector3 Quat::wxx() const { return Vector3 (w, x, x); } +Vector3 Quat::xyx() const { return Vector3 (x, y, x); } +Vector3 Quat::yyx() const { return Vector3 (y, y, x); } +Vector3 Quat::zyx() const { return Vector3 (z, y, x); } +Vector3 Quat::wyx() const { return Vector3 (w, y, x); } +Vector3 Quat::xzx() const { return Vector3 (x, z, x); } +Vector3 Quat::yzx() const { return Vector3 (y, z, x); } +Vector3 Quat::zzx() const { return Vector3 (z, z, x); } +Vector3 Quat::wzx() const { return Vector3 (w, z, x); } +Vector3 Quat::xwx() const { return Vector3 (x, w, x); } +Vector3 Quat::ywx() const { return Vector3 (y, w, x); } +Vector3 Quat::zwx() const { return Vector3 (z, w, x); } +Vector3 Quat::wwx() const { return Vector3 (w, w, x); } +Vector3 Quat::xxy() const { return Vector3 (x, x, y); } +Vector3 Quat::yxy() const { return Vector3 (y, x, y); } +Vector3 Quat::zxy() const { return Vector3 (z, x, y); } +Vector3 Quat::wxy() const { return Vector3 (w, x, y); } +Vector3 Quat::xyy() const { return Vector3 (x, y, y); } +Vector3 Quat::yyy() const { return Vector3 (y, y, y); } +Vector3 Quat::zyy() const { return Vector3 (z, y, y); } +Vector3 Quat::wyy() const { return Vector3 (w, y, y); } +Vector3 Quat::xzy() const { return Vector3 (x, z, y); } +Vector3 Quat::yzy() const { return Vector3 (y, z, y); } +Vector3 Quat::zzy() const { return Vector3 (z, z, y); } +Vector3 Quat::wzy() const { return Vector3 (w, z, y); } +Vector3 Quat::xwy() const { return Vector3 (x, w, y); } +Vector3 Quat::ywy() const { return Vector3 (y, w, y); } +Vector3 Quat::zwy() const { return Vector3 (z, w, y); } +Vector3 Quat::wwy() const { return Vector3 (w, w, y); } +Vector3 Quat::xxz() const { return Vector3 (x, x, z); } +Vector3 Quat::yxz() const { return Vector3 (y, x, z); } +Vector3 Quat::zxz() const { return Vector3 (z, x, z); } +Vector3 Quat::wxz() const { return Vector3 (w, x, z); } +Vector3 Quat::xyz() const { return Vector3 (x, y, z); } +Vector3 Quat::yyz() const { return Vector3 (y, y, z); } +Vector3 Quat::zyz() const { return Vector3 (z, y, z); } +Vector3 Quat::wyz() const { return Vector3 (w, y, z); } +Vector3 Quat::xzz() const { return Vector3 (x, z, z); } +Vector3 Quat::yzz() const { return Vector3 (y, z, z); } +Vector3 Quat::zzz() const { return Vector3 (z, z, z); } +Vector3 Quat::wzz() const { return Vector3 (w, z, z); } +Vector3 Quat::xwz() const { return Vector3 (x, w, z); } +Vector3 Quat::ywz() const { return Vector3 (y, w, z); } +Vector3 Quat::zwz() const { return Vector3 (z, w, z); } +Vector3 Quat::wwz() const { return Vector3 (w, w, z); } +Vector3 Quat::xxw() const { return Vector3 (x, x, w); } +Vector3 Quat::yxw() const { return Vector3 (y, x, w); } +Vector3 Quat::zxw() const { return Vector3 (z, x, w); } +Vector3 Quat::wxw() const { return Vector3 (w, x, w); } +Vector3 Quat::xyw() const { return Vector3 (x, y, w); } +Vector3 Quat::yyw() const { return Vector3 (y, y, w); } +Vector3 Quat::zyw() const { return Vector3 (z, y, w); } +Vector3 Quat::wyw() const { return Vector3 (w, y, w); } +Vector3 Quat::xzw() const { return Vector3 (x, z, w); } +Vector3 Quat::yzw() const { return Vector3 (y, z, w); } +Vector3 Quat::zzw() const { return Vector3 (z, z, w); } +Vector3 Quat::wzw() const { return Vector3 (w, z, w); } +Vector3 Quat::xww() const { return Vector3 (x, w, w); } +Vector3 Quat::yww() const { return Vector3 (y, w, w); } +Vector3 Quat::zww() const { return Vector3 (z, w, w); } +Vector3 Quat::www() const { return Vector3 (w, w, w); } + +// 4-char swizzles + +Vector4 Quat::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Quat::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Quat::zxxx() const { return Vector4 (z, x, x, x); } +Vector4 Quat::wxxx() const { return Vector4 (w, x, x, x); } +Vector4 Quat::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Quat::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Quat::zyxx() const { return Vector4 (z, y, x, x); } +Vector4 Quat::wyxx() const { return Vector4 (w, y, x, x); } +Vector4 Quat::xzxx() const { return Vector4 (x, z, x, x); } +Vector4 Quat::yzxx() const { return Vector4 (y, z, x, x); } +Vector4 Quat::zzxx() const { return Vector4 (z, z, x, x); } +Vector4 Quat::wzxx() const { return Vector4 (w, z, x, x); } +Vector4 Quat::xwxx() const { return Vector4 (x, w, x, x); } +Vector4 Quat::ywxx() const { return Vector4 (y, w, x, x); } +Vector4 Quat::zwxx() const { return Vector4 (z, w, x, x); } +Vector4 Quat::wwxx() const { return Vector4 (w, w, x, x); } +Vector4 Quat::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Quat::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Quat::zxyx() const { return Vector4 (z, x, y, x); } +Vector4 Quat::wxyx() const { return Vector4 (w, x, y, x); } +Vector4 Quat::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Quat::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Quat::zyyx() const { return Vector4 (z, y, y, x); } +Vector4 Quat::wyyx() const { return Vector4 (w, y, y, x); } +Vector4 Quat::xzyx() const { return Vector4 (x, z, y, x); } +Vector4 Quat::yzyx() const { return Vector4 (y, z, y, x); } +Vector4 Quat::zzyx() const { return Vector4 (z, z, y, x); } +Vector4 Quat::wzyx() const { return Vector4 (w, z, y, x); } +Vector4 Quat::xwyx() const { return Vector4 (x, w, y, x); } +Vector4 Quat::ywyx() const { return Vector4 (y, w, y, x); } +Vector4 Quat::zwyx() const { return Vector4 (z, w, y, x); } +Vector4 Quat::wwyx() const { return Vector4 (w, w, y, x); } +Vector4 Quat::xxzx() const { return Vector4 (x, x, z, x); } +Vector4 Quat::yxzx() const { return Vector4 (y, x, z, x); } +Vector4 Quat::zxzx() const { return Vector4 (z, x, z, x); } +Vector4 Quat::wxzx() const { return Vector4 (w, x, z, x); } +Vector4 Quat::xyzx() const { return Vector4 (x, y, z, x); } +Vector4 Quat::yyzx() const { return Vector4 (y, y, z, x); } +Vector4 Quat::zyzx() const { return Vector4 (z, y, z, x); } +Vector4 Quat::wyzx() const { return Vector4 (w, y, z, x); } +Vector4 Quat::xzzx() const { return Vector4 (x, z, z, x); } +Vector4 Quat::yzzx() const { return Vector4 (y, z, z, x); } +Vector4 Quat::zzzx() const { return Vector4 (z, z, z, x); } +Vector4 Quat::wzzx() const { return Vector4 (w, z, z, x); } +Vector4 Quat::xwzx() const { return Vector4 (x, w, z, x); } +Vector4 Quat::ywzx() const { return Vector4 (y, w, z, x); } +Vector4 Quat::zwzx() const { return Vector4 (z, w, z, x); } +Vector4 Quat::wwzx() const { return Vector4 (w, w, z, x); } +Vector4 Quat::xxwx() const { return Vector4 (x, x, w, x); } +Vector4 Quat::yxwx() const { return Vector4 (y, x, w, x); } +Vector4 Quat::zxwx() const { return Vector4 (z, x, w, x); } +Vector4 Quat::wxwx() const { return Vector4 (w, x, w, x); } +Vector4 Quat::xywx() const { return Vector4 (x, y, w, x); } +Vector4 Quat::yywx() const { return Vector4 (y, y, w, x); } +Vector4 Quat::zywx() const { return Vector4 (z, y, w, x); } +Vector4 Quat::wywx() const { return Vector4 (w, y, w, x); } +Vector4 Quat::xzwx() const { return Vector4 (x, z, w, x); } +Vector4 Quat::yzwx() const { return Vector4 (y, z, w, x); } +Vector4 Quat::zzwx() const { return Vector4 (z, z, w, x); } +Vector4 Quat::wzwx() const { return Vector4 (w, z, w, x); } +Vector4 Quat::xwwx() const { return Vector4 (x, w, w, x); } +Vector4 Quat::ywwx() const { return Vector4 (y, w, w, x); } +Vector4 Quat::zwwx() const { return Vector4 (z, w, w, x); } +Vector4 Quat::wwwx() const { return Vector4 (w, w, w, x); } +Vector4 Quat::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Quat::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Quat::zxxy() const { return Vector4 (z, x, x, y); } +Vector4 Quat::wxxy() const { return Vector4 (w, x, x, y); } +Vector4 Quat::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Quat::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Quat::zyxy() const { return Vector4 (z, y, x, y); } +Vector4 Quat::wyxy() const { return Vector4 (w, y, x, y); } +Vector4 Quat::xzxy() const { return Vector4 (x, z, x, y); } +Vector4 Quat::yzxy() const { return Vector4 (y, z, x, y); } +Vector4 Quat::zzxy() const { return Vector4 (z, z, x, y); } +Vector4 Quat::wzxy() const { return Vector4 (w, z, x, y); } +Vector4 Quat::xwxy() const { return Vector4 (x, w, x, y); } +Vector4 Quat::ywxy() const { return Vector4 (y, w, x, y); } +Vector4 Quat::zwxy() const { return Vector4 (z, w, x, y); } +Vector4 Quat::wwxy() const { return Vector4 (w, w, x, y); } +Vector4 Quat::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Quat::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Quat::zxyy() const { return Vector4 (z, x, y, y); } +Vector4 Quat::wxyy() const { return Vector4 (w, x, y, y); } +Vector4 Quat::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Quat::yyyy() const { return Vector4 (y, y, y, y); } +Vector4 Quat::zyyy() const { return Vector4 (z, y, y, y); } +Vector4 Quat::wyyy() const { return Vector4 (w, y, y, y); } +Vector4 Quat::xzyy() const { return Vector4 (x, z, y, y); } +Vector4 Quat::yzyy() const { return Vector4 (y, z, y, y); } +Vector4 Quat::zzyy() const { return Vector4 (z, z, y, y); } +Vector4 Quat::wzyy() const { return Vector4 (w, z, y, y); } +Vector4 Quat::xwyy() const { return Vector4 (x, w, y, y); } +Vector4 Quat::ywyy() const { return Vector4 (y, w, y, y); } +Vector4 Quat::zwyy() const { return Vector4 (z, w, y, y); } +Vector4 Quat::wwyy() const { return Vector4 (w, w, y, y); } +Vector4 Quat::xxzy() const { return Vector4 (x, x, z, y); } +Vector4 Quat::yxzy() const { return Vector4 (y, x, z, y); } +Vector4 Quat::zxzy() const { return Vector4 (z, x, z, y); } +Vector4 Quat::wxzy() const { return Vector4 (w, x, z, y); } +Vector4 Quat::xyzy() const { return Vector4 (x, y, z, y); } +Vector4 Quat::yyzy() const { return Vector4 (y, y, z, y); } +Vector4 Quat::zyzy() const { return Vector4 (z, y, z, y); } +Vector4 Quat::wyzy() const { return Vector4 (w, y, z, y); } +Vector4 Quat::xzzy() const { return Vector4 (x, z, z, y); } +Vector4 Quat::yzzy() const { return Vector4 (y, z, z, y); } +Vector4 Quat::zzzy() const { return Vector4 (z, z, z, y); } +Vector4 Quat::wzzy() const { return Vector4 (w, z, z, y); } +Vector4 Quat::xwzy() const { return Vector4 (x, w, z, y); } +Vector4 Quat::ywzy() const { return Vector4 (y, w, z, y); } +Vector4 Quat::zwzy() const { return Vector4 (z, w, z, y); } +Vector4 Quat::wwzy() const { return Vector4 (w, w, z, y); } +Vector4 Quat::xxwy() const { return Vector4 (x, x, w, y); } +Vector4 Quat::yxwy() const { return Vector4 (y, x, w, y); } +Vector4 Quat::zxwy() const { return Vector4 (z, x, w, y); } +Vector4 Quat::wxwy() const { return Vector4 (w, x, w, y); } +Vector4 Quat::xywy() const { return Vector4 (x, y, w, y); } +Vector4 Quat::yywy() const { return Vector4 (y, y, w, y); } +Vector4 Quat::zywy() const { return Vector4 (z, y, w, y); } +Vector4 Quat::wywy() const { return Vector4 (w, y, w, y); } +Vector4 Quat::xzwy() const { return Vector4 (x, z, w, y); } +Vector4 Quat::yzwy() const { return Vector4 (y, z, w, y); } +Vector4 Quat::zzwy() const { return Vector4 (z, z, w, y); } +Vector4 Quat::wzwy() const { return Vector4 (w, z, w, y); } +Vector4 Quat::xwwy() const { return Vector4 (x, w, w, y); } +Vector4 Quat::ywwy() const { return Vector4 (y, w, w, y); } +Vector4 Quat::zwwy() const { return Vector4 (z, w, w, y); } +Vector4 Quat::wwwy() const { return Vector4 (w, w, w, y); } +Vector4 Quat::xxxz() const { return Vector4 (x, x, x, z); } +Vector4 Quat::yxxz() const { return Vector4 (y, x, x, z); } +Vector4 Quat::zxxz() const { return Vector4 (z, x, x, z); } +Vector4 Quat::wxxz() const { return Vector4 (w, x, x, z); } +Vector4 Quat::xyxz() const { return Vector4 (x, y, x, z); } +Vector4 Quat::yyxz() const { return Vector4 (y, y, x, z); } +Vector4 Quat::zyxz() const { return Vector4 (z, y, x, z); } +Vector4 Quat::wyxz() const { return Vector4 (w, y, x, z); } +Vector4 Quat::xzxz() const { return Vector4 (x, z, x, z); } +Vector4 Quat::yzxz() const { return Vector4 (y, z, x, z); } +Vector4 Quat::zzxz() const { return Vector4 (z, z, x, z); } +Vector4 Quat::wzxz() const { return Vector4 (w, z, x, z); } +Vector4 Quat::xwxz() const { return Vector4 (x, w, x, z); } +Vector4 Quat::ywxz() const { return Vector4 (y, w, x, z); } +Vector4 Quat::zwxz() const { return Vector4 (z, w, x, z); } +Vector4 Quat::wwxz() const { return Vector4 (w, w, x, z); } +Vector4 Quat::xxyz() const { return Vector4 (x, x, y, z); } +Vector4 Quat::yxyz() const { return Vector4 (y, x, y, z); } +Vector4 Quat::zxyz() const { return Vector4 (z, x, y, z); } +Vector4 Quat::wxyz() const { return Vector4 (w, x, y, z); } +Vector4 Quat::xyyz() const { return Vector4 (x, y, y, z); } +Vector4 Quat::yyyz() const { return Vector4 (y, y, y, z); } +Vector4 Quat::zyyz() const { return Vector4 (z, y, y, z); } +Vector4 Quat::wyyz() const { return Vector4 (w, y, y, z); } +Vector4 Quat::xzyz() const { return Vector4 (x, z, y, z); } +Vector4 Quat::yzyz() const { return Vector4 (y, z, y, z); } +Vector4 Quat::zzyz() const { return Vector4 (z, z, y, z); } +Vector4 Quat::wzyz() const { return Vector4 (w, z, y, z); } +Vector4 Quat::xwyz() const { return Vector4 (x, w, y, z); } +Vector4 Quat::ywyz() const { return Vector4 (y, w, y, z); } +Vector4 Quat::zwyz() const { return Vector4 (z, w, y, z); } +Vector4 Quat::wwyz() const { return Vector4 (w, w, y, z); } +Vector4 Quat::xxzz() const { return Vector4 (x, x, z, z); } +Vector4 Quat::yxzz() const { return Vector4 (y, x, z, z); } +Vector4 Quat::zxzz() const { return Vector4 (z, x, z, z); } +Vector4 Quat::wxzz() const { return Vector4 (w, x, z, z); } +Vector4 Quat::xyzz() const { return Vector4 (x, y, z, z); } +Vector4 Quat::yyzz() const { return Vector4 (y, y, z, z); } +Vector4 Quat::zyzz() const { return Vector4 (z, y, z, z); } +Vector4 Quat::wyzz() const { return Vector4 (w, y, z, z); } +Vector4 Quat::xzzz() const { return Vector4 (x, z, z, z); } +Vector4 Quat::yzzz() const { return Vector4 (y, z, z, z); } +Vector4 Quat::zzzz() const { return Vector4 (z, z, z, z); } +Vector4 Quat::wzzz() const { return Vector4 (w, z, z, z); } +Vector4 Quat::xwzz() const { return Vector4 (x, w, z, z); } +Vector4 Quat::ywzz() const { return Vector4 (y, w, z, z); } +Vector4 Quat::zwzz() const { return Vector4 (z, w, z, z); } +Vector4 Quat::wwzz() const { return Vector4 (w, w, z, z); } +Vector4 Quat::xxwz() const { return Vector4 (x, x, w, z); } +Vector4 Quat::yxwz() const { return Vector4 (y, x, w, z); } +Vector4 Quat::zxwz() const { return Vector4 (z, x, w, z); } +Vector4 Quat::wxwz() const { return Vector4 (w, x, w, z); } +Vector4 Quat::xywz() const { return Vector4 (x, y, w, z); } +Vector4 Quat::yywz() const { return Vector4 (y, y, w, z); } +Vector4 Quat::zywz() const { return Vector4 (z, y, w, z); } +Vector4 Quat::wywz() const { return Vector4 (w, y, w, z); } +Vector4 Quat::xzwz() const { return Vector4 (x, z, w, z); } +Vector4 Quat::yzwz() const { return Vector4 (y, z, w, z); } +Vector4 Quat::zzwz() const { return Vector4 (z, z, w, z); } +Vector4 Quat::wzwz() const { return Vector4 (w, z, w, z); } +Vector4 Quat::xwwz() const { return Vector4 (x, w, w, z); } +Vector4 Quat::ywwz() const { return Vector4 (y, w, w, z); } +Vector4 Quat::zwwz() const { return Vector4 (z, w, w, z); } +Vector4 Quat::wwwz() const { return Vector4 (w, w, w, z); } +Vector4 Quat::xxxw() const { return Vector4 (x, x, x, w); } +Vector4 Quat::yxxw() const { return Vector4 (y, x, x, w); } +Vector4 Quat::zxxw() const { return Vector4 (z, x, x, w); } +Vector4 Quat::wxxw() const { return Vector4 (w, x, x, w); } +Vector4 Quat::xyxw() const { return Vector4 (x, y, x, w); } +Vector4 Quat::yyxw() const { return Vector4 (y, y, x, w); } +Vector4 Quat::zyxw() const { return Vector4 (z, y, x, w); } +Vector4 Quat::wyxw() const { return Vector4 (w, y, x, w); } +Vector4 Quat::xzxw() const { return Vector4 (x, z, x, w); } +Vector4 Quat::yzxw() const { return Vector4 (y, z, x, w); } +Vector4 Quat::zzxw() const { return Vector4 (z, z, x, w); } +Vector4 Quat::wzxw() const { return Vector4 (w, z, x, w); } +Vector4 Quat::xwxw() const { return Vector4 (x, w, x, w); } +Vector4 Quat::ywxw() const { return Vector4 (y, w, x, w); } +Vector4 Quat::zwxw() const { return Vector4 (z, w, x, w); } +Vector4 Quat::wwxw() const { return Vector4 (w, w, x, w); } +Vector4 Quat::xxyw() const { return Vector4 (x, x, y, w); } +Vector4 Quat::yxyw() const { return Vector4 (y, x, y, w); } +Vector4 Quat::zxyw() const { return Vector4 (z, x, y, w); } +Vector4 Quat::wxyw() const { return Vector4 (w, x, y, w); } +Vector4 Quat::xyyw() const { return Vector4 (x, y, y, w); } +Vector4 Quat::yyyw() const { return Vector4 (y, y, y, w); } +Vector4 Quat::zyyw() const { return Vector4 (z, y, y, w); } +Vector4 Quat::wyyw() const { return Vector4 (w, y, y, w); } +Vector4 Quat::xzyw() const { return Vector4 (x, z, y, w); } +Vector4 Quat::yzyw() const { return Vector4 (y, z, y, w); } +Vector4 Quat::zzyw() const { return Vector4 (z, z, y, w); } +Vector4 Quat::wzyw() const { return Vector4 (w, z, y, w); } +Vector4 Quat::xwyw() const { return Vector4 (x, w, y, w); } +Vector4 Quat::ywyw() const { return Vector4 (y, w, y, w); } +Vector4 Quat::zwyw() const { return Vector4 (z, w, y, w); } +Vector4 Quat::wwyw() const { return Vector4 (w, w, y, w); } +Vector4 Quat::xxzw() const { return Vector4 (x, x, z, w); } +Vector4 Quat::yxzw() const { return Vector4 (y, x, z, w); } +Vector4 Quat::zxzw() const { return Vector4 (z, x, z, w); } +Vector4 Quat::wxzw() const { return Vector4 (w, x, z, w); } +Vector4 Quat::xyzw() const { return Vector4 (x, y, z, w); } +Vector4 Quat::yyzw() const { return Vector4 (y, y, z, w); } +Vector4 Quat::zyzw() const { return Vector4 (z, y, z, w); } +Vector4 Quat::wyzw() const { return Vector4 (w, y, z, w); } +Vector4 Quat::xzzw() const { return Vector4 (x, z, z, w); } +Vector4 Quat::yzzw() const { return Vector4 (y, z, z, w); } +Vector4 Quat::zzzw() const { return Vector4 (z, z, z, w); } +Vector4 Quat::wzzw() const { return Vector4 (w, z, z, w); } +Vector4 Quat::xwzw() const { return Vector4 (x, w, z, w); } +Vector4 Quat::ywzw() const { return Vector4 (y, w, z, w); } +Vector4 Quat::zwzw() const { return Vector4 (z, w, z, w); } +Vector4 Quat::wwzw() const { return Vector4 (w, w, z, w); } +Vector4 Quat::xxww() const { return Vector4 (x, x, w, w); } +Vector4 Quat::yxww() const { return Vector4 (y, x, w, w); } +Vector4 Quat::zxww() const { return Vector4 (z, x, w, w); } +Vector4 Quat::wxww() const { return Vector4 (w, x, w, w); } +Vector4 Quat::xyww() const { return Vector4 (x, y, w, w); } +Vector4 Quat::yyww() const { return Vector4 (y, y, w, w); } +Vector4 Quat::zyww() const { return Vector4 (z, y, w, w); } +Vector4 Quat::wyww() const { return Vector4 (w, y, w, w); } +Vector4 Quat::xzww() const { return Vector4 (x, z, w, w); } +Vector4 Quat::yzww() const { return Vector4 (y, z, w, w); } +Vector4 Quat::zzww() const { return Vector4 (z, z, w, w); } +Vector4 Quat::wzww() const { return Vector4 (w, z, w, w); } +Vector4 Quat::xwww() const { return Vector4 (x, w, w, w); } +Vector4 Quat::ywww() const { return Vector4 (y, w, w, w); } +Vector4 Quat::zwww() const { return Vector4 (z, w, w, w); } +Vector4 Quat::wwww() const { return Vector4 (w, w, w, w); } +} + diff --git a/externals/g3dlite/G3D.lib/source/Ray.cpp b/externals/g3dlite/G3D.lib/source/Ray.cpp new file mode 100644 index 00000000000..bf6aadc08d1 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Ray.cpp @@ -0,0 +1,112 @@ +/** + @file Ray.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-07-12 + @edited 2004-03-19 + */ + +#include "G3D/platform.h" +#include "G3D/Ray.h" +#include "G3D/Plane.h" +#include "G3D/Sphere.h" +#include "G3D/CollisionDetection.h" + +namespace G3D { + +Ray::Ray(class BinaryInput& b) { + deserialize(b); +} + + +void Ray::serialize(class BinaryOutput& b) const { + origin.serialize(b); + direction.serialize(b); +} + + +void Ray::deserialize(class BinaryInput& b) { + origin.deserialize(b); + direction.deserialize(b); +} + + +Ray Ray::refract( + const Vector3& newOrigin, + const Vector3& normal, + float iInside, + float iOutside) const { + + Vector3 D = direction.refractionDirection(normal, iInside, iOutside); + return Ray::fromOriginAndDirection( + newOrigin + (direction + normal * (float)sign(direction.dot(normal))) * 0.001f, D); +} + + +Ray Ray::reflect( + const Vector3& newOrigin, + const Vector3& normal) const { + + Vector3 D = direction.reflectionDirection(normal); + return Ray::fromOriginAndDirection(newOrigin + (D + normal) * 0.001f, D); +} + + +Vector3 Ray::intersection(const Plane& plane) const { + float d; + Vector3 normal = plane.normal(); + plane.getEquation(normal, d); + float rate = direction.dot(normal); + + if (rate >= 0.0f) { + return Vector3::inf(); + } else { + float t = -(d + origin.dot(normal)) / rate; + + return origin + direction * t; + } +} + + +float Ray::intersectionTime(const class Sphere& sphere) const { + Vector3 dummy; + return CollisionDetection::collisionTimeForMovingPointFixedSphere( + origin, direction, sphere, dummy); +} + + +float Ray::intersectionTime(const class Plane& plane) const { + Vector3 dummy; + return CollisionDetection::collisionTimeForMovingPointFixedPlane( + origin, direction, plane, dummy); +} + + +float Ray::intersectionTime(const class Box& box) const { + Vector3 dummy; + float time = CollisionDetection::collisionTimeForMovingPointFixedBox( + origin, direction, box, dummy); + + if ((time == inf()) && (box.contains(origin))) { + return 0.0f; + } else { + return time; + } +} + + +float Ray::intersectionTime(const class AABox& box) const { + Vector3 dummy; + bool inside; + float time = CollisionDetection::collisionTimeForMovingPointFixedAABox( + origin, direction, box, dummy, inside); + + if ((time == inf()) && inside) { + return 0.0f; + } else { + return time; + } +} + +} diff --git a/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp b/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp new file mode 100644 index 00000000000..28ff6955d9b --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp @@ -0,0 +1,290 @@ +/** + @file RegistryUtil.cpp + + @created 2006-04-06 + @edited 2006-04-24 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. +*/ + +#include "G3D/platform.h" + +// This file is only used on Windows +#ifdef G3D_WIN32 + +#include "G3D/RegistryUtil.h" +#include "G3D/System.h" + +namespace G3D { + +// static helpers +static HKEY getRootKeyFromString(const char* str, size_t length); + + +bool RegistryUtil::keyExists(const std::string& key) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if (hkey == NULL) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey); + + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + RegCloseKey(openKey); + return true; + } else { + return false; + } +} + +bool RegistryUtil::valueExists(const std::string& key, const std::string& value) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if ( hkey == NULL ) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + uint32 dataSize = 0; + result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize)); + + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + RegCloseKey(openKey); + } + return (result == ERROR_SUCCESS); +} + + +bool RegistryUtil::readInt32(const std::string& key, const std::string& value, int32& data) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if ( hkey == NULL ) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + uint32 dataSize = sizeof(int32); + result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize)); + + debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value."); + + RegCloseKey(openKey); + } + return (result == ERROR_SUCCESS); +} + +bool RegistryUtil::readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if (hkey == NULL) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + if (data == NULL) { + result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize)); + } else { + result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize)); + } + + debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value."); + + RegCloseKey(openKey); + } + return (result == ERROR_SUCCESS); +} + +bool RegistryUtil::readString(const std::string& key, const std::string& value, std::string& data) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if (hkey == NULL) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + uint32 dataSize = 0; + + result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize)); + debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value."); + + // increment datasize to allow for non null-terminated strings in registry + dataSize += 1; + + if (result == ERROR_SUCCESS) { + char* tmpStr = static_cast<char*>(System::malloc(dataSize)); + System::memset(tmpStr, 0, dataSize); + + result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(tmpStr), reinterpret_cast<LPDWORD>(&dataSize)); + debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value."); + + if (result == ERROR_SUCCESS) { + data = tmpStr; + } + + RegCloseKey(openKey); + System::free(tmpStr); + } + } + return (result == ERROR_SUCCESS); +} + +bool RegistryUtil::writeInt32(const std::string& key, const std::string& value, int32 data) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if (hkey == NULL) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + result = RegSetValueExA(openKey, value.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&data), sizeof(int32)); + + debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value."); + + RegCloseKey(openKey); + } + return (result == ERROR_SUCCESS); +} + +bool RegistryUtil::writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize) { + debugAssert(data); + + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if (hkey == NULL) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + if (data) { + result = RegSetValueExA(openKey, value.c_str(), 0, REG_BINARY, reinterpret_cast<const BYTE*>(data), dataSize); + } + + debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value."); + + RegCloseKey(openKey); + } + return (result == ERROR_SUCCESS); +} + +bool RegistryUtil::writeString(const std::string& key, const std::string& value, const std::string& data) { + size_t pos = key.find('\\', 0); + if (pos == std::string::npos) { + return false; + } + + HKEY hkey = getRootKeyFromString(key.c_str(), pos); + + if (hkey == NULL) { + return false; + } + + HKEY openKey; + int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey); + debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + + if (result == ERROR_SUCCESS) { + result = RegSetValueExA(openKey, value.c_str(), 0, REG_SZ, reinterpret_cast<const BYTE*>(data.c_str()), (data.size() + 1)); + debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value."); + + RegCloseKey(openKey); + } + return (result == ERROR_SUCCESS); +} + + +// static helpers +static HKEY getRootKeyFromString(const char* str, uint32 length) { + debugAssert(str); + + if (str) { + if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) { + return HKEY_CLASSES_ROOT; + } else if ( strncmp(str, "HKEY_CURRENT_CONFIG", length) == 0 ) { + return HKEY_CURRENT_CONFIG; + } else if ( strncmp(str, "HKEY_CURRENT_USER", length) == 0 ) { + return HKEY_CURRENT_USER; + } else if ( strncmp(str, "HKEY_LOCAL_MACHINE", length) == 0 ) { + return HKEY_LOCAL_MACHINE; + } else if ( strncmp(str, "HKEY_PERFORMANCE_DATA", length) == 0 ) { + return HKEY_PERFORMANCE_DATA; + } else if ( strncmp(str, "HKEY_PERFORMANCE_NLSTEXT", length) == 0 ) { + return HKEY_PERFORMANCE_NLSTEXT; + } else if ( strncmp(str, "HKEY_PERFORMANCE_TEXT", length) == 0 ) { + return HKEY_PERFORMANCE_TEXT; + } else if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) { + return HKEY_CLASSES_ROOT; + } else { + return NULL; + } + } else { + return NULL; + } +} + +} // namespace G3D + +#endif // G3D_WIN32 diff --git a/externals/g3dlite/G3D.lib/source/Sphere.cpp b/externals/g3dlite/G3D.lib/source/Sphere.cpp new file mode 100644 index 00000000000..935598d52dd --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Sphere.cpp @@ -0,0 +1,196 @@ +/** + @file Sphere.cpp + + Sphere class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-04-17 + @edited 2006-02-05 + */ + +#include "G3D/platform.h" +#include "G3D/Sphere.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/AABox.h" +#include "G3D/Plane.h" + +namespace G3D { + +int32 Sphere::dummy; + +Sphere::Sphere(class BinaryInput& b) { + deserialize(b); +} + + +void Sphere::serialize(class BinaryOutput& b) const { + center.serialize(b); + b.writeFloat64(radius); +} + + +void Sphere::deserialize(class BinaryInput& b) { + center.deserialize(b); + radius = (float)b.readFloat64(); +} + + +std::string Sphere::toString() const { + return format("Sphere(<%g, %g, %g>, %g)", + center.x, center.y, center.z, radius); +} + + +bool Sphere::contains(const Vector3& point) const { + double distance = (center - point).squaredMagnitude(); + return distance <= (radius * radius); +} + + +bool Sphere::intersects(const Sphere& other) const { + return (other.center - center).length() <= (radius + other.radius); +} + +bool Sphere::culledBy( + const Array<Plane>& plane, + int& cullingPlaneIndex, + const uint32 inMask, + uint32& outMask) const { + + return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask); +} + + +bool Sphere::culledBy( + const Array<Plane>& plane, + int& cullingPlaneIndex, + const uint32 inMask) const { + + return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask); +} + + +bool Sphere::culledBy( + const class Plane* plane, + int numPlanes, + int& cullingPlane, + const uint32 _inMask, + uint32& childMask) const { + + if (radius == inf()) { + // No plane can cull the infinite box + return false; + } + + uint32 inMask = _inMask; + assert(numPlanes < 31); + + childMask = 0; + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < numPlanes; p++) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + bool culledLow = ! plane[p].halfSpaceContainsFinite(center + plane[p].normal() * radius); + bool culledHigh = ! plane[p].halfSpaceContainsFinite(center - plane[p].normal() * radius); + + if (culledLow) { + // Plane p culled the sphere + cullingPlane = p; + + // The caller should not recurse into the children, + // since the parent is culled. If they do recurse, + // make them only test against this one plane, which + // will immediately cull the volume. + childMask = 1 << p; + return true; + + } else if (culledHigh) { + // The bounding volume straddled the plane; we have + // to keep testing against this plane + childMask |= (1 << p); + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool Sphere::culledBy( + const class Plane* plane, + int numPlanes, + int& cullingPlane, + const uint32 _inMask) const { + + uint32 inMask = _inMask; + assert(numPlanes < 31); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < numPlanes; p++) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + bool culled = ! plane[p].halfSpaceContains(center + plane[p].normal() * radius); + if (culled) { + // Plane p culled the sphere + cullingPlane = p; + return true; + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +Vector3 Sphere::randomSurfacePoint() const { + return Vector3::random() * radius + center; +} + + +Vector3 Sphere::randomInteriorPoint() const { + Vector3 result; + do { + result = Vector3(uniformRandom(-1, 1), + uniformRandom(-1, 1), + uniformRandom(-1, 1)); + } while (result.squaredMagnitude() >= 1.0f); + + return result * radius + center; +} + + +float Sphere::volume() const { + return (float)pi() * (4.0f / 3.0f) * powf((float)radius, 3.0f); +} + + +float Sphere::area() const { + return (float)pi() * 4.0f * powf((float)radius, 2.0f); +} + + +void Sphere::getBounds(AABox& out) const { + Vector3 extent(radius, radius, radius); + out = AABox(center - extent, center + extent); +} + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/SplineBase.cpp b/externals/g3dlite/G3D.lib/source/SplineBase.cpp new file mode 100644 index 00000000000..41221624b06 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/SplineBase.cpp @@ -0,0 +1,162 @@ +#include "G3D/platform.h" +#include "G3D/Spline.h" + +namespace G3D { + +float SplineBase::getFinalInterval() const { + if (! cyclic) { + return 0; + } else if (finalInterval <= 0) { + int N = time.size(); + if (N >= 2) { + return (time[1] - time[0] + time[N - 1] - time[N - 2]) * 0.5f; + } else { + return 1.0f; + } + } else { + return finalInterval; + } +} + + +Matrix4 SplineBase::computeBasis() { + // The standard Catmull-Rom spline basis (e.g., Watt & Watt p108) + // is for [u^3 u^2 u^1 u^0] * B * [p[0] p[1] p[2] p[3]]^T. + // We need a basis formed for: + // + // U * C * [2*p'[1] p[1] p[2] 2*p'[2]]^T + // + // U * C * [p2-p0 p1 p2 p3-p1]^T + // + // To make this transformation, compute the differences of columns in C: + // For [p0 p1 p2 p3] + Matrix4 basis = + Matrix4( -1, 3, -3, 1, + 2, -5, 4, -1, + -1, 0, 1, 0, + 0, 2, 0, 0) * 0.5f; + + // For [-p0 p1 p2 p3]^T + basis.setColumn(0, -basis.column(0)); + + // For [-p0 p1 p2 p3-p1]^T + basis.setColumn(1, basis.column(1) + basis.column(3)); + + // For [p2-p0 p1 p2 p3-p1]^T + basis.setColumn(2, basis.column(2) - basis.column(0)); + + return basis; +} + + +float SplineBase::duration() const { + if (time.size() == 0) { + return 0; + } else { + return time.last() - time[0] + getFinalInterval(); + } +} + + +void SplineBase::computeIndexInBounds(float s, int& i, float& u) const { + int N = time.size(); + float t0 = time[0]; + float tn = time[N - 1]; + + i = iFloor((N - 1) * (s - t0) / (tn - t0)); + + // Inclusive bounds for binary search + int hi = N - 1; + int lo = 0; + + while ((time[i] > s) || (time[i + 1] <= s)) { + + if (time[i] > s) { + // too big + hi = i - 1; + } else if (time[i + 1] <= s) { + // too small + lo = i + 1; + } + + i = (hi + lo) / 2; + } + + // Having exited the above loop, i must be correct, so compute u. + u = (s - time[i]) / (time[i + 1] - time[i]); +} + + +void SplineBase::computeIndex(float s, int& i, float& u) const { + int N = time.size(); + debugAssertM(N > 0, "No control points"); + float t0 = time[0]; + float tn = time[N - 1]; + + if (N < 2) { + // No control points to work with + i = 0; + u = 0.0; + } else if (cyclic) { + float fi = getFinalInterval(); + + // Cyclic spline + if ((s < t0) || (s >= tn + fi)) { + // Cyclic, off the bottom or top + + // Compute offset and reduce to the in-bounds case + + float d = duration(); + // Number of times we wrapped around the cyclic array + int wraps = iFloor((s - t0) / d); + + debugAssert(s - d * wraps >= t0); + debugAssert(s - d * wraps < tn + getFinalInterval()); + + computeIndex(s - d * wraps, i, u); + i += wraps * N; + + } else if (s >= tn) { + debugAssert(s < tn + fi); + // Cyclic, off the top but before the end of the last interval + i = N - 1; + u = (s - tn) / fi; + + } else { + // Cyclic, in bounds + computeIndexInBounds(s, i, u); + } + + } else { + // Non-cyclic + + if (s < t0) { + // Non-cyclic, off the bottom. Assume points are spaced + // following the first time interval. + + float dt = time[1] - t0; + float x = (s - t0) / dt; + i = iFloor(x); + u = x - i; + + } else if (s >= tn) { + // Non-cyclic, off the top. Assume points are spaced following + // the last time interval. + + float dt = tn - time[N - 2]; + float x = N - 1 + (s - tn) / dt; + i = iFloor(x); + u = x - i; + + } else { + // In bounds, non-cyclic. Assume a regular + // distribution (which gives O(1) for uniform spacing) + // and then binary search to handle the general case + // efficiently. + computeIndexInBounds(s, i, u); + + } // if in bounds + } // if cyclic +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Stopwatch.cpp b/externals/g3dlite/G3D.lib/source/Stopwatch.cpp new file mode 100644 index 00000000000..e55f689a9e2 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Stopwatch.cpp @@ -0,0 +1,96 @@ +/** + @file Stopwatch.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2005-10-05 + @edited 2005-10-05 + + Copyright 2000-2003, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/Stopwatch.h" +#include "G3D/System.h" + +namespace G3D { + +Stopwatch::Stopwatch() : inBetween(false), lastTockTime(-1), + lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0), + m_smoothFPS(0), emwaDuration(0) { + computeOverhead(); +} + + +void Stopwatch::computeOverhead() { + cycleOverhead = 0; + tick(); + tock(); + cycleOverhead = elapsedCycles(); +} + + +void Stopwatch::tick() { + // This is 'alwaysAssert' instead of 'debugAssert' + // since people rarely profile in debug mode. + alwaysAssertM(! inBetween, "Stopwatch::tick() called twice in a row."); + inBetween = true; + + // We read RDTSC twice here, but it is more abstract to implement this + // way and at least we're reading the cycle count last. + timeStart = System::time(); + System::beginCycleCount(cycleStart); +} + + +void Stopwatch::tock() { + System::endCycleCount(cycleStart); + RealTime now = System::time(); + lastDuration = now - timeStart; + if (abs(emwaDuration - lastDuration) > max(emwaDuration, lastDuration) * 0.50) { + // Off by more than 50% + emwaDuration = lastDuration; + } else { + emwaDuration = lastDuration * 0.05 + emwaDuration * 0.95; + } + + lastCycleCount = cycleStart - cycleOverhead; + if (lastCycleCount < 0) { + lastCycleCount = 0; + } + + if (lastTockTime != -1.0) { + m_fps = 1.0 / (now - lastTockTime); + + // Time smooth the average + emwaFPS = m_fps * 0.01 + emwaFPS * 0.99; + + if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * 0.08) { + // The difference between emwa and m_fps is way off + // update emwa directly. + emwaFPS = m_fps; + } + + // Update m_smoothFPS only when the value varies significantly + // We round so as to not mislead the user as to the accuracy of + // the number. + if (m_smoothFPS == 0) { + m_smoothFPS = m_fps; + } else if (emwaFPS <= 10) { + if (::fabs(m_smoothFPS - emwaFPS) > .75) { + m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0; + } + } else { + if (::fabs(m_smoothFPS - emwaFPS) > 1.25) { + m_smoothFPS = floor(emwaFPS + 0.5); + } + } + } + lastTockTime = now; + + alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick."); + inBetween = false; +} + +} + diff --git a/externals/g3dlite/G3D.lib/source/System.cpp b/externals/g3dlite/G3D.lib/source/System.cpp new file mode 100644 index 00000000000..748e13257f1 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/System.cpp @@ -0,0 +1,1864 @@ +/** + @file System.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + Note: every routine must call init() first. + + There are two kinds of detection used in this file. At compile + time, the _MSC_VER #define is used to determine whether x86 assembly + can be used at all. At runtime, processor detection is used to + determine if we can safely call the routines that use that assembly. + + @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm + @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1 + @cite Michael Herf http://www.stereopsis.com/memcpy.html + + @created 2003-01-25 + @edited 2008-09-02 + */ + +#include "G3D/platform.h" +#include "G3D/System.h" +#include "G3D/debug.h" +#include "G3D/fileutils.h" +#include "G3D/TextOutput.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/Crypto.h" +#include "G3D/prompt.h" +#include "G3D/Log.h" +#include <time.h> + +#include <cstring> +#include <cstdio> + +// Uncomment the following line to turn off G3D::System memory +// allocation and use the operating system's malloc. +//#define NO_BUFFERPOOL + +#if defined(__i386__) || defined(__x86_64__) || defined(G3D_WIN32) +# define G3D_NOT_OSX_PPC +#endif + +#ifdef G3D_WIN32 + +# include <conio.h> +# include <sys/timeb.h> +# include "G3D/RegistryUtil.h" + +#elif defined(G3D_LINUX) + + #include <stdlib.h> + #include <stdio.h> + #include <errno.h> + #include <sys/types.h> + #include <sys/select.h> + #include <termios.h> + #include <unistd.h> + #include <sys/ioctl.h> + #include <sys/time.h> + #include <pthread.h> + +#elif defined(G3D_OSX) + + #include <stdlib.h> + #include <stdio.h> + #include <errno.h> + #include <sys/types.h> + #include <sys/sysctl.h> + #include <sys/select.h> + #include <sys/time.h> + #include <termios.h> + #include <unistd.h> + #include <pthread.h> + #include <mach-o/arch.h> + + #include <sstream> + #include <CoreServices/CoreServices.h> +#endif + +#if defined(SSE) + #include <xmmintrin.h> +#endif + +namespace G3D { + +struct CpuInfo +{ +public: + int m_cpuSpeed; + bool m_hasCPUID; + bool m_hasRDTSC; + bool m_hasMMX; + bool m_hasSSE; + bool m_hasSSE2; + bool m_hasSSE3; + bool m_has3DNOW; + char m_cpuVendorStr[1024]; +}; + +// helper macro to call cpuid functions and return all values +#ifdef _MSC_VER + + // VC on Intel +# define CALL_CPUID(func, areg, breg, creg, dreg) \ + __asm mov eax, func \ + __asm cpuid \ + __asm mov areg, eax \ + __asm mov breg, ebx \ + __asm mov creg, ecx \ + __asm mov dreg, edx + +#elif defined(__GNUC__) && defined(G3D_OSX_INTEL) + // GCC on OS X intel +# define CALL_CPUID(func, areg, breg, creg, dreg) \ + areg = 0; \ + breg = 0; \ + creg = 0; \ + dreg = 0; +#else + // Any other compiler/platform, likely GCC +# define CALL_CPUID(func, areg, breg, creg, dreg) \ + __asm__ ( \ + "cpuid \n": \ + "=a" (areg), \ + "=b" (breg), \ + "=c" (creg), \ + "=d" (dreg): \ + "a" (func) \ + ); +#endif + +// this holds the data directory set by the application (currently GApp) for use by findDataFile +static char g_appDataDir[FILENAME_MAX] = ""; + +static CpuInfo g_cpuInfo = { + 0, false, false, false, false, false, false, false, {'U', 'n', 'k', 'n', 'o', 'w', 'n', '\0'}}; + +static G3DEndian _machineEndian = G3D_LITTLE_ENDIAN; +static char _cpuArchCstr[1024]; +static char _operatingSystemCstr[1024]; + +#ifdef G3D_WIN32 +/** Used by getTick() for timing */ +static LARGE_INTEGER _start; +static LARGE_INTEGER _counterFrequency; +#else +static struct timeval _start; +#endif + +static char versionCstr[1024]; +System::OutOfMemoryCallback System::outOfMemoryCallback = NULL; + +#ifdef G3D_OSX +long System::m_OSXCPUSpeed; +double System::m_secondsPerNS; +#endif + +/** The Real-World time of System::getTick() time 0. Set by initTime */ +static RealTime realWorldGetTickTime0; + + +static unsigned int maxSupportedCPUIDLevel = 0; +static unsigned int maxSupportedExtendedLevel = 0; + +/** Checks if the CPUID command is available on the processor (called from init) */ +static void checkForCPUID(); + +/** ReadRead the standard processor extensions. Called from init(). */ +static void getStandardProcessorExtensions(); + +/** Called from init */ +static void initTime(); + + +std::string System::findDataFile +(const std::string& full, + bool errorIfNotFound) { + + if (fileExists(full)) { + return full; + } + + std::string initialAppDataDir(g_appDataDir); + + std::string name = filenameBaseExt(full); + std::string originalPath = filenamePath(full); + + // Search several paths + Array<std::string> pathBase; + + int backlen = 4; + + // add what should be the current working directory + pathBase.append(""); + + // add application specified data directory to be searched first + pathBase.append(initialAppDataDir); + + // try walking back along the directory tree + std::string prev = ""; + for (int i = 0; i < backlen; ++i) { + pathBase.append(originalPath + prev); + prev = prev + "../"; + } + + prev = "../"; + for (int i = 0; i < backlen; ++i) { + pathBase.append(prev); + prev = prev + "../"; + } + + // Hard-code in likely install directories + int ver = G3D_VER; + std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100); + + if (G3D_VER % 100 != 0) { + lname = lname + format("-b%02d/", ver % 100); + } else { + lname = lname + "/"; + } + + // Look in some other likely places +# ifdef G3D_WIN32 + std::string lpath = "libraries/G3D/"; + pathBase.append(std::string("c:/") + lpath); + pathBase.append(std::string("d:/") + lpath); + pathBase.append(std::string("e:/") + lpath); + pathBase.append(std::string("f:/") + lpath); + pathBase.append(std::string("g:/") + lpath); + pathBase.append(std::string("x:/") + lpath); +# endif +# if defined(G3D_LINUX) + pathBase.append("/usr/local/"); + pathBase.append("/course/cs224/"); + pathBase.append("/map/gfx0/common/games/"); +# endif +# if defined(G3D_FREEBSD) + pathBase.append("/usr/local/"); + pathBase.append("/usr/local/371/"); + pathBase.append("/usr/cs-local/371/"); +# endif +# if defined(G3D_OSX) + pathBase.append("/usr/local/" + lname); + pathBase.append("/Volumes/McGuire/Projects/"); +# endif + + // Add the library name to all variations + int N = pathBase.size(); + for (int i = 0; i < N; ++i) { + pathBase.append(pathBase[i] + lname); + pathBase.append(pathBase[i] + "G3D/"); + } + + Array<std::string> subDir; + subDir.append("", "font/", "sky/", "gui/"); + subDir.append("image/", "quake2/", "quake2/players/"); + subDir.append("quake3/", "SuperShader/", "ifs/", "3ds/"); + subDir.append("quake2/speedway/"); + + Array<std::string> path; + for (int p = 0; p < pathBase.size(); ++p) { + for (int s = 0; s < subDir.size(); ++s) { + path.append(pathBase[p] + subDir[s]); + path.append(pathBase[p] + "data/" + subDir[s]); + path.append(pathBase[p] + "data-files/" + subDir[s]); + } + } + + for (int i = 0; i < path.length(); ++i) { + std::string filename = path[i] + name; + if (fileExists(filename)) { + logPrintf("\nWARNING: Could not find '%s' so '%s' " + "was substituted.\n", full.c_str(), + filename.c_str()); + return filename; + } + } + + if (errorIfNotFound) { + // Generate an error message + std::string locations; + for (int i = 0; i < path.size(); ++i) { + locations += path[i] + name + "\n"; + } + alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations); + } + // Not found + return ""; +} + + +void System::setAppDataDir(const std::string& path) { + // just copy the path, it needs to be valid + strncpy(g_appDataDir, path.c_str(), sizeof(g_appDataDir)); +} + + +std::string demoFindData(bool errorIfNotFound) { + // Directories that might contain the data + Array<std::string> potential; + + // Look back up the directory tree + std::string x = "../"; + std::string f = ""; + for (int i = 0; i < 6; ++i) { + potential.append(f); + f = f + x; + } + + // Hard-code in likely install directories + int ver = G3D_VER; + std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100); + + if (G3D_VER % 100 != 0) { + lname = lname + format("-b%02d/", ver % 100); + } else { + lname = lname + "/"; + } + + std::string lpath = "libraries/" + lname; + #ifdef G3D_WIN32 + potential.append(std::string("c:/") + lpath); + potential.append(std::string("d:/") + lpath); + potential.append(std::string("e:/") + lpath); + potential.append(std::string("f:/") + lpath); + potential.append(std::string("g:/") + lpath); + potential.append(std::string("x:/") + lpath); + #elif defined(G3D_LINUX) + potential.append("/usr/local/" + lname); + potential.append("/course/cs224/"); + potential.append("/map/gfx0/common/games/"); + #elif defined(G3D_FREEBSD) + potential.append("/usr/local/" + lname); + potential.append("/usr/local/371/") + potential.append("/usr/cs-local/371/") + #elif defined(G3D_OSX) + potential.append("/usr/local/" + lname); + potential.append("/Volumes/McGuire/Projects/"); + potential.append("/Volumes/McGuire/Projects/G3D/"); + #endif + + // Scan all potentials for the font directory + for (int p = 0; p < potential.size(); ++p) { + std::string path = potential[p]; + //debugPrintf("Looking at: %sdata\n", path.c_str()); + if (fileExists(path + "data") && fileExists(path + "data/font")) { + return path + "data/"; + } + if (fileExists(path + "data-files") && fileExists(path + "data-files/font")) { + return path + "data-files/"; + } + } + + if (errorIfNotFound) { + const char* choice[] = {"Exit"}; + + prompt("Demo Error", "The demo could not locate the data directory. " + "The data is required to run this demo. If you have not downloaded " + "the data zipfile, get it from http://g3d-cpp.sf.net. If you have " + "downloaded it, it needs to be no more than 4 directories above the " + "demo directory.", choice, 1, true); + } + + return ""; +} + +bool System::hasCPUID() { + init(); + return g_cpuInfo.m_hasCPUID; +} + +bool System::hasRDTSC() { + init(); + return g_cpuInfo.m_hasRDTSC; +} + + +bool System::hasSSE() { + init(); + return g_cpuInfo.m_hasSSE; +} + + +bool System::hasSSE2() { + init(); + return g_cpuInfo.m_hasSSE2; +} + +bool System::hasSSE3() { + init(); + return g_cpuInfo.m_hasSSE3; +} + +bool System::hasMMX() { + init(); + return g_cpuInfo.m_hasMMX; +} + + +bool System::has3DNow() { + init(); + return g_cpuInfo.m_has3DNOW; +} + + +const std::string& System::cpuVendor() { + init(); + static const std::string _cpuVendor = g_cpuInfo.m_cpuVendorStr; + return _cpuVendor; +} + + +G3DEndian System::machineEndian() { + init(); + return _machineEndian; +} + +const std::string& System::operatingSystem() { + init(); + static const std::string _operatingSystem =_operatingSystemCstr; + return _operatingSystem; +} + + +const std::string& System::cpuArchitecture() { + init(); + static const std::string _cpuArch = _cpuArchCstr; + return _cpuArch; +} + +const std::string& System::build() { + const static std::string b = +# ifdef _DEBUG + "Debug"; +# else + "Release"; +# endif + + return b; +} + +const std::string& System::version() { + init(); + + static const std::string _version = versionCstr; + return _version; +} + + +void System::init() { + // Cannot use most G3D data structures or utility functions in here because + // they are not initialized. + + static bool initialized = false; + + if (initialized) { + return; + } + + initialized = true; + + if ((G3D_VER % 100) != 0) { + sprintf(versionCstr, "G3D %d.%02d beta %d", + G3D_VER / 10000, + (G3D_VER / 100) % 100, + G3D_VER % 100); + } else { + sprintf(versionCstr, "G3D %d.%02d", + G3D_VER / 10000, + (G3D_VER / 100) % 100); + } + + // First of all we check if the CPUID command is available + checkForCPUID(); + + // Figure out if this machine is little or big endian. + { + int32 a = 1; + if (*(uint8*)&a == 1) { + _machineEndian = G3D_LITTLE_ENDIAN; + } else { + _machineEndian = G3D_BIG_ENDIAN; + } + } + +# ifdef G3D_NOT_OSX_PPC + // Process the CPUID information + if (g_cpuInfo.m_hasCPUID) { + // We read the standard CPUID level 0x00000000 which should + // be available on every x86 processor. This fills out + // a string with the processor vendor tag. + unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0, edxreg = 0; + + CALL_CPUID(0x00, eaxreg, ebxreg, ecxreg, edxreg); + + // Then we connect the single register values to the vendor string + *((unsigned int*) g_cpuInfo.m_cpuVendorStr) = ebxreg; + *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 4)) = edxreg; + *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 8)) = ecxreg; + g_cpuInfo.m_cpuVendorStr[12] = '\0'; + + // We can also read the max. supported standard CPUID level + maxSupportedCPUIDLevel = eaxreg & 0xFFFF; + + // Then we read the ext. CPUID level 0x80000000 + CALL_CPUID(0x80000000, eaxreg, ebxreg, ecxreg, edxreg); + + // ...to check the max. supported extended CPUID level + maxSupportedExtendedLevel = eaxreg; + + // Then we switch to the specific processor vendors. + // Fill out _cpuArch based on this information. It will + // be overwritten by the next block of code on Windows, + // but on Linux will stand. + switch (ebxreg) { + case 0x756E6547: // GenuineIntel + strcpy(_cpuArchCstr, "Intel Processor"); + break; + + case 0x68747541: // AuthenticAMD + strcpy(_cpuArchCstr, "AMD Processor"); + break; + + case 0x69727943: // CyrixInstead + strcpy(_cpuArchCstr, "Cyrix Processor"); + break; + + default: + strcpy(_cpuArchCstr, "Unknown Processor Vendor"); + break; + } + } + #endif // G3D_NOT_OSX_PPC + + #ifdef G3D_WIN32 + bool success = RegistryUtil::readInt32 + ("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "~MHz", g_cpuInfo.m_cpuSpeed); + + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + char* arch; + switch (systemInfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + arch = "Intel"; + break; + + case PROCESSOR_ARCHITECTURE_MIPS: + arch = "MIPS"; + break; + + case PROCESSOR_ARCHITECTURE_ALPHA: + arch = "Alpha"; + break; + + case PROCESSOR_ARCHITECTURE_PPC: + arch = "Power PC"; + break; + + default: + arch = "Unknown"; + } + + uint32 maxAddr = (uint32)systemInfo.lpMaximumApplicationAddress; + sprintf(_cpuArchCstr, "%d x %d-bit %s processor", + systemInfo.dwNumberOfProcessors, + (int)(::log((double)maxAddr) / ::log(2.0) + 2.0), + arch); + // _CPUSpeed / (1024.0 * 1024)); + + OSVERSIONINFO osVersionInfo; + osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + success = GetVersionEx(&osVersionInfo) != 0; + + if (success) { + sprintf(_operatingSystemCstr, "Windows %d.%d build %d Platform %d %s", + osVersionInfo.dwMajorVersion, + osVersionInfo.dwMinorVersion, + osVersionInfo.dwBuildNumber, + osVersionInfo.dwPlatformId, + osVersionInfo.szCSDVersion); + } else { + strcpy(_operatingSystemCstr, "Windows"); + } + + #elif defined(G3D_LINUX) + + { + // Shell out to the 'uname' command + FILE* f = popen("uname -a", "r"); + + int len = 100; + char* r = (char*)::malloc(len * sizeof(char)); + fgets(r, len, f); + // Remove trailing newline + if (r[strlen(r) - 1] == '\n') { + r[strlen(r) - 1] = '\0'; + } + fclose(f); + + strcpy(_operatingSystemCstr, r); + ::free(r); + } + + #elif defined(G3D_OSX) + + // Operating System: + SInt32 macVersion; + Gestalt(gestaltSystemVersion, &macVersion); + + int major = (macVersion >> 8) & 0xFF; + int minor = (macVersion >> 4) & 0xF; + int revision = macVersion & 0xF; + + sprintf(_operatingSystemCstr, "OS X %x.%x.%x", major, minor, revision); + + // Clock Cycle Timing Information: + Gestalt('pclk', &System::m_OSXCPUSpeed); + g_cpuInfo.m_cpuSpeed = iRound((double)m_OSXCPUSpeed / (1024 * 1024)); + m_secondsPerNS = 1.0 / 1.0e9; + + // System Architecture: + const NXArchInfo* pInfo = NXGetLocalArchInfo(); + + if (pInfo) { + strcpy(_cpuArchCstr, pInfo->description); + + switch (pInfo->cputype) { + case CPU_TYPE_POWERPC: + switch(pInfo->cpusubtype){ + case CPU_SUBTYPE_POWERPC_750: + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + strcpy(g_cpuInfo.m_cpuVendorStr, "Motorola"); + break; + case CPU_SUBTYPE_POWERPC_970: + strcpy(g_cpuInfo.m_cpuVendorStr, "IBM"); + break; + } + break; + + case CPU_TYPE_I386: + strcpy(g_cpuInfo.m_cpuVendorStr, "Intel"); + break; + } + } + #endif + + initTime(); + + getStandardProcessorExtensions(); +} + +static void checkForCPUID() { + unsigned int bitChanged = 0; + + // We've to check if we can toggle the flag register bit 21. + // If we can't the processor does not support the CPUID command. + +#if defined(_MSC_VER) + __asm { + push eax + push ebx + pushfd + pushfd + pop eax + mov ebx, eax + xor eax, 0x00200000 + push eax + popfd + pushfd + pop eax + popfd + xor eax, ebx + mov bitChanged, eax + pop ebx + pop eax + } +#elif defined(__GNUC__) && defined(i386) && !defined(G3D_OSX_INTEL) + // 32-bit g++ + __asm__ ( + "pushfl # Get original EFLAGS \n" + "pushfl \n" + "popl %%eax \n" + "movl %%eax, %%ecx \n" + "xorl $0x200000, %%eax # Flip ID bit in EFLAGS \n" + "pushl %%eax # Save new EFLAGS value on stack \n" + "popfl # Replace current EFLAGS value \n" + "pushfl # Get new EFLAGS \n" + "popl %%eax # Store new EFLAGS in EAX \n" + "popfl \n" + "xorl %%ecx, %%eax # Can not toggle ID bit, \n" + "movl %%eax, %0 # We have CPUID support \n" + : "=m" (bitChanged) + : // No inputs + : "%eax", "%ecx" + ); +#elif defined(__GNUC__) && defined(__x86_64__) && !defined(G3D_OSX_INTEL) + // x86_64 has SSE and CPUID + + bitChanged = 1; +#else + // Unknown architecture + bitChanged = 0; +#endif + + g_cpuInfo.m_hasCPUID = ((bitChanged == 0) ? false : true); +} + +void getStandardProcessorExtensions() { +#if !defined(G3D_OSX) || defined(G3D_OSX_INTEL) + if (! g_cpuInfo.m_hasCPUID) { + return; + } + + unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0; + unsigned int features = 0; + + // http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf + // call cpuid with function 0x01 in EAX + + // Invoking CPUID with '1' in EAX fills out edx with a bit string. + // The bits of this value indicate the presence or absence of + // useful processor features. + CALL_CPUID(0x01, eaxreg, ebxreg, ecxreg, features); + + #define checkBit(var, bit) ((var & (1 << bit)) ? true : false) + + g_cpuInfo.m_hasRDTSC = checkBit(features, 16); + g_cpuInfo.m_hasMMX = checkBit(features, 23); + g_cpuInfo.m_hasSSE = checkBit(features, 25); + g_cpuInfo.m_hasSSE2 = checkBit(features, 26); + g_cpuInfo.m_hasSSE3 = checkBit(ecxreg, 0); + + if (maxSupportedExtendedLevel >= 0x80000001) { + // function 0x80000001 changes bit 31 of edx to 3dnow support flag + CALL_CPUID(0x80000001, eaxreg, ebxreg, ecxreg, features); + g_cpuInfo.m_has3DNOW = checkBit(features, 31); + } else { + g_cpuInfo.m_has3DNOW = false; + } + + #undef checkBit +#endif +} + +#if defined(SSE) + +// Copy in 128 bytes chunks, where each chunk contains 8*float32x4 = 8 * 4 * 4 bytes = 128 bytes +// +// +void memcpySSE2(void* dst, const void* src, int nbytes) { + int remainingBytes = nbytes; + + if (nbytes > 128) { + + // Number of chunks + int N = nbytes / 128; + + float* restrict d = (float*)dst; + const float* restrict s = (const float*)src; + + // Finish when the destination pointer has moved 8N elements + float* stop = d + (N * 8 * 4); + + while (d < stop) { + // Inner loop unrolled 8 times + const __m128 r0 = _mm_loadu_ps(s); + const __m128 r1 = _mm_loadu_ps(s + 4); + const __m128 r2 = _mm_loadu_ps(s + 8); + const __m128 r3 = _mm_loadu_ps(s + 12); + const __m128 r4 = _mm_loadu_ps(s + 16); + const __m128 r5 = _mm_loadu_ps(s + 20); + const __m128 r6 = _mm_loadu_ps(s + 24); + const __m128 r7 = _mm_loadu_ps(s + 28); + + _mm_storeu_ps(d, r0); + _mm_storeu_ps(d + 4, r1); + _mm_storeu_ps(d + 8, r2); + _mm_storeu_ps(d + 12, r3); + _mm_storeu_ps(d + 16, r4); + _mm_storeu_ps(d + 20, r5); + _mm_storeu_ps(d + 24, r6); + _mm_storeu_ps(d + 28, r7); + + s += 32; + d += 32; + } + + remainingBytes -= N * 8 * 4 * 4; + } + + if (remainingBytes > 0) { + // Memcpy the rest + memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes); + } +} +#else + + // Fall back to memcpy + void memcpySSE2(void *dst, const void *src, int nbytes) { + memcpy(dst, src, nbytes); + } + +#endif + +#if defined(G3D_WIN32) && defined(SSE) +/** Michael Herf's fast memcpy */ +void memcpyMMX(void* dst, const void* src, int nbytes) { + int remainingBytes = nbytes; + + if (nbytes > 64) { + _asm { + mov esi, src + mov edi, dst + mov ecx, nbytes + shr ecx, 6 // 64 bytes per iteration + + loop1: + movq mm1, 0[ESI] // Read in source data + movq mm2, 8[ESI] + movq mm3, 16[ESI] + movq mm4, 24[ESI] + movq mm5, 32[ESI] + movq mm6, 40[ESI] + movq mm7, 48[ESI] + movq mm0, 56[ESI] + + movntq 0[EDI], mm1 // Non-temporal stores + movntq 8[EDI], mm2 + movntq 16[EDI], mm3 + movntq 24[EDI], mm4 + movntq 32[EDI], mm5 + movntq 40[EDI], mm6 + movntq 48[EDI], mm7 + movntq 56[EDI], mm0 + + add esi, 64 + add edi, 64 + dec ecx + jnz loop1 + + emms + } + remainingBytes -= ((nbytes >> 6) << 6); + } + + if (remainingBytes > 0) { + // Memcpy the rest + memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes); + } +} + +#else + // Fall back to memcpy + void memcpyMMX(void *dst, const void *src, int nbytes) { + memcpy(dst, src, nbytes); + } + +#endif + + +void System::memcpy(void* dst, const void* src, size_t numBytes) { + if (System::hasSSE2() && System::hasMMX()) { + G3D::memcpyMMX(dst, src, numBytes); + } else if (System::hasSSE() && System::hasMMX()) { + G3D::memcpyMMX(dst, src, numBytes); + } else { + ::memcpy(dst, src, numBytes); + } +} + + +/** Michael Herf's fastest memset. n32 must be filled with the same + character repeated. */ +#if defined(G3D_WIN32) && defined(SSE) + +// On x86 processors, use MMX +void memfill(void *dst, int n32, unsigned long i) { + + int originalSize = i; + int bytesRemaining = i; + + if (i > 16) { + + bytesRemaining = i % 16; + i -= bytesRemaining; + __asm { + movq mm0, n32 + punpckldq mm0, mm0 + mov edi, dst + + loopwrite: + + movntq 0[edi], mm0 + movntq 8[edi], mm0 + + add edi, 16 + sub i, 16 + jg loopwrite + + emms + } + } + + if (bytesRemaining > 0) { + ::memset((uint8*)dst + (originalSize - bytesRemaining), n32, bytesRemaining); + } +} + +#else + +// For non x86 processors, we fall back to the standard memset +void memfill(void *dst, int n32, unsigned long i) { + ::memset(dst, n32, i); +} + +#endif + + +void System::memset(void* dst, uint8 value, size_t numBytes) { + if (System::hasSSE() && System::hasMMX()) { + uint32 v = value; + v = v + (v << 8) + (v << 16) + (v << 24); + G3D::memfill(dst, v, numBytes); + } else { + ::memset(dst, value, numBytes); + } +} + + +std::string& System::appName() { + static std::string n = filenameBase(currentProgramFilename()); + return n; +} + + +std::string System::currentProgramFilename() { + char filename[2048]; + + #ifdef G3D_WIN32 + { + GetModuleFileNameA(NULL, filename, sizeof(filename)); + } + #else + { + int ret = readlink("/proc/self/exe", filename, sizeof(filename)); + + // In case of an error, leave the handling up to the caller + if (ret == -1) { + return ""; + } + + debugAssert((int)sizeof(filename) > ret); + + // Ensure proper NULL termination + filename[ret] = 0; + } + #endif + + return filename; +} + + +void System::sleep(RealTime t) { + + // Overhead of calling this function. + static const RealTime OVERHEAD = .000006; + + RealTime now = time(); + RealTime wakeupTime = now + t - OVERHEAD; + + RealTime remainingTime = wakeupTime - now; + RealTime sleepTime = 0; + + while (remainingTime > 0) { + + + if (remainingTime > 0.001) { + // Safe to use Sleep with a time... sleep for half the remaining time + sleepTime = max(remainingTime * .5, 0.0005); + } else if (remainingTime > 0.0001) { + // Safe to use Sleep with a zero time; + // causes the program to yield only + // the current time slice, and then return. + sleepTime = 0; + } else { + // Not safe to use Sleep; busy wait + sleepTime = -1; + } + + if (sleepTime >= 0) { + #ifdef G3D_WIN32 + // Translate to milliseconds + Sleep((int)(sleepTime * 1e3)); + #else + // Translate to microseconds + usleep((int)(sleepTime * 1e6)); + #endif + } + + now = time(); + remainingTime = wakeupTime - now; + } +} + + +void System::consoleClearScreen() { + #ifdef G3D_WIN32 + system("cls"); + #else + system("clear"); + #endif +} + + +bool System::consoleKeyPressed() { + #ifdef G3D_WIN32 + + return _kbhit() != 0; + + #else + + static const int STDIN = 0; + static bool initialized = false; + + if (! initialized) { + // Use termios to turn off line buffering + termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = true; + } + + #ifdef G3D_LINUX + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; + + #else + + timeval timeout; + fd_set rdset; + + FD_ZERO(&rdset); + FD_SET(STDIN, &rdset); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + return select(STDIN + 1, &rdset, NULL, NULL, &timeout); + #endif + #endif +} + + +int System::consoleReadKey() { + #ifdef G3D_WIN32 + return _getch(); + #else + char c; + read(0, &c, 1); + return c; + #endif +} + + +void initTime() { + #ifdef G3D_WIN32 + if (QueryPerformanceFrequency(&_counterFrequency)) { + QueryPerformanceCounter(&_start); + } + + struct _timeb t; + _ftime(&t); + + realWorldGetTickTime0 = (RealTime)t.time - t.timezone * G3D::MINUTE + (t.dstflag ? G3D::HOUR : 0); + + #else + gettimeofday(&_start, NULL); + // "sse" = "seconds since epoch". The time + // function returns the seconds since the epoch + // GMT (perhaps more correctly called UTC). + time_t gmt = time(NULL); + + // No call to free or delete is needed, but subsequent + // calls to asctime, ctime, mktime, etc. might overwrite + // local_time_vals. + tm* localTimeVals = localtime(&gmt); + + time_t local = gmt; + + if (localTimeVals) { + // tm_gmtoff is already corrected for daylight savings. + local = local + localTimeVals->tm_gmtoff; + } + + realWorldGetTickTime0 = local; + #endif +} + + +RealTime System::time() { + init(); + #ifdef G3D_WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + return ((RealTime)(now.QuadPart - _start.QuadPart) / + _counterFrequency.QuadPart) + realWorldGetTickTime0; + #else + // Linux resolution defaults to 100Hz. + // There is no need to do a separate RDTSC call as gettimeofday + // actually uses RDTSC when on systems that support it, otherwise + // it uses the system clock. + struct timeval now; + gettimeofday(&now, NULL); + + return (now.tv_sec - _start.tv_sec) + + (now.tv_usec - _start.tv_usec) / 1e6 + + realWorldGetTickTime0; + #endif +} + + +//////////////////////////////////////////////////////////////// + +#define REALPTR_TO_USERPTR(x) ((uint8*)(x) + sizeof (void *)) +#define USERPTR_TO_REALPTR(x) ((uint8*)(x) - sizeof (void *)) +#define REALBLOCK_SIZE(x) ((x) + sizeof (void *)) + +class BufferPool { +public: + + /** Only store buffers up to these sizes (in bytes) in each pool-> + Different pools have different management strategies. + + A large block is preallocated for tiny buffers; they are used with + tremendous frequency. Other buffers are allocated as demanded. + Tiny buffers are 128 bytes long because that seems to align well with + cache sizes on many machines. + */ + enum {tinyBufferSize = 128, smallBufferSize = 1024, medBufferSize = 4096}; + + /** + Most buffers we're allowed to store. + 128000 * 128 = 16 MB (preallocated) + 2048 * 1024 = 2 MB (allocated on demand) + 1024 * 4096 = 4 MB (allocated on demand) + */ + enum {maxTinyBuffers = 128000, maxSmallBuffers = 2048, maxMedBuffers = 1024}; + +private: + + class MemBlock { + public: + void* ptr; + size_t bytes; + + inline MemBlock() : ptr(NULL), bytes(0) {} + inline MemBlock(void* p, size_t b) : ptr(p), bytes(b) {} + }; + + MemBlock smallPool[maxSmallBuffers]; + int smallPoolSize; + + MemBlock medPool[maxMedBuffers]; + int medPoolSize; + + /** The tiny pool is a single block of storage into which all tiny + objects are allocated. This provides better locality for + small objects and avoids the search time, since all tiny + blocks are exactly the same size. */ + void* tinyPool[maxTinyBuffers]; + int tinyPoolSize; + + /** Pointer to the data in the tiny pool */ + void* tinyHeap; + +# ifdef G3D_WIN32 + CRITICAL_SECTION mutex; +# else + pthread_mutex_t mutex; +# endif + + /** Provide synchronization between threads */ + void lock() { +# ifdef G3D_WIN32 + EnterCriticalSection(&mutex); +# else + pthread_mutex_lock(&mutex); +# endif + } + + void unlock() { +# ifdef G3D_WIN32 + LeaveCriticalSection(&mutex); +# else + pthread_mutex_unlock(&mutex); +# endif + } + + /** + Malloc out of the tiny heap. Returns NULL if allocation failed. + */ + inline void* tinyMalloc(size_t bytes) { + // Note that we ignore the actual byte size + // and create a constant size block. + (void)bytes; + assert(tinyBufferSize >= bytes); + + void* ptr = NULL; + + if (tinyPoolSize > 0) { + --tinyPoolSize; + + // Return the old last pointer from the freelist + ptr = tinyPool[tinyPoolSize]; + +# ifdef G3D_DEBUG + if (tinyPoolSize > 0) { + assert(tinyPool[tinyPoolSize - 1] != ptr); + // "System::malloc heap corruption detected: " + // "the last two pointers on the freelist are identical (during tinyMalloc)."); + } +# endif + + // NULL out the entry to help detect corruption + tinyPool[tinyPoolSize] = NULL; + } + + return ptr; + } + + /** Returns true if this is a pointer into the tiny heap. */ + bool inTinyHeap(void* ptr) { + return + (ptr >= tinyHeap) && + (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize); + } + + void tinyFree(void* ptr) { + assert(ptr); + assert(tinyPoolSize < maxTinyBuffers); + // "Tried to free a tiny pool buffer when the tiny pool freelist is full."); + +# ifdef G3D_DEBUG + if (tinyPoolSize > 0) { + void* prevOnHeap = tinyPool[tinyPoolSize - 1]; + assert(prevOnHeap != ptr); +// "System::malloc heap corruption detected: " +// "the last two pointers on the freelist are identical (during tinyFree)."); + } +# endif + + assert(tinyPool[tinyPoolSize] == NULL); + + // Put the pointer back into the free list + tinyPool[tinyPoolSize] = ptr; + ++tinyPoolSize; + + } + + void flushPool(MemBlock* pool, int& poolSize) { + for (int i = 0; i < poolSize; ++i) { + ::free(pool[i].ptr); + pool[i].ptr = NULL; + pool[i].bytes = 0; + } + poolSize = 0; + } + + + /** Allocate out of a specific pool-> Return NULL if no suitable + memory was found. + + */ + void* malloc(MemBlock* pool, int& poolSize, size_t bytes) { + + // OPT: find the smallest block that satisfies the request. + + // See if there's something we can use in the buffer pool-> + // Search backwards since usually we'll re-use the last one. + for (int i = (int)poolSize - 1; i >= 0; --i) { + if (pool[i].bytes >= bytes) { + // We found a suitable entry in the pool-> + + // No need to offset the pointer; it is already offset + void* ptr = pool[i].ptr; + + // Remove this element from the pool + --poolSize; + pool[i] = pool[poolSize]; + + return ptr; + } + } + + return NULL; + } + +public: + + /** Count of memory allocations that have occurred. */ + int totalMallocs; + int mallocsFromTinyPool; + int mallocsFromSmallPool; + int mallocsFromMedPool; + + /** Amount of memory currently allocated (according to the application). + This does not count the memory still remaining in the buffer pool, + but does count extra memory required for rounding off to the size + of a buffer. + Primarily useful for detecting leaks.*/ + // TODO: make me an atomic int! + volatile int bytesAllocated; + + BufferPool() { + totalMallocs = 0; + + mallocsFromTinyPool = 0; + mallocsFromSmallPool = 0; + mallocsFromMedPool = 0; + + bytesAllocated = true; + + tinyPoolSize = 0; + tinyHeap = NULL; + + smallPoolSize = 0; + + medPoolSize = 0; + + + // Initialize the tiny heap as a bunch of pointers into one + // pre-allocated buffer. + tinyHeap = ::malloc(maxTinyBuffers * tinyBufferSize); + for (int i = 0; i < maxTinyBuffers; ++i) { + tinyPool[i] = (uint8*)tinyHeap + (tinyBufferSize * i); + } + tinyPoolSize = maxTinyBuffers; + +# ifdef G3D_WIN32 + InitializeCriticalSection(&mutex); +# else + pthread_mutex_init(&mutex, NULL); +# endif + } + + + ~BufferPool() { + ::free(tinyHeap); +# ifdef G3D_WIN32 + DeleteCriticalSection(&mutex); +# else + // No destruction on pthreads +# endif + } + + + void* realloc(void* ptr, size_t bytes) { + if (ptr == NULL) { + return malloc(bytes); + } + + if (inTinyHeap(ptr)) { + if (bytes <= tinyBufferSize) { + // The old pointer actually had enough space. + return ptr; + } else { + // Free the old pointer and malloc + + void* newPtr = malloc(bytes); + System::memcpy(newPtr, ptr, tinyBufferSize); + tinyFree(ptr); + return newPtr; + + } + } else { + // In one of our heaps. + + // See how big the block really was + size_t realSize = *(uint32*)USERPTR_TO_REALPTR(ptr); + if (bytes <= realSize) { + // The old block was big enough. + return ptr; + } + + // Need to reallocate + void* newPtr = malloc(bytes); + System::memcpy(newPtr, ptr, realSize); + free(ptr); + return newPtr; + } + } + + + void* malloc(size_t bytes) { + lock(); + ++totalMallocs; + + if (bytes <= tinyBufferSize) { + + void* ptr = tinyMalloc(bytes); + + if (ptr) { + ++mallocsFromTinyPool; + unlock(); + return ptr; + } + + } + + // Failure to allocate a tiny buffer is allowed to flow + // through to a small buffer + if (bytes <= smallBufferSize) { + + void* ptr = malloc(smallPool, smallPoolSize, bytes); + + if (ptr) { + ++mallocsFromSmallPool; + unlock(); + return ptr; + } + + } else if (bytes <= medBufferSize) { + // Note that a small allocation failure does *not* fall + // through into a medium allocation because that would + // waste the medium buffer's resources. + + void* ptr = malloc(medPool, medPoolSize, bytes); + + if (ptr) { + ++mallocsFromMedPool; + unlock(); + debugAssertM(ptr != NULL, "BufferPool::malloc returned NULL"); + return ptr; + } + } + + bytesAllocated += REALBLOCK_SIZE(bytes); + unlock(); + + // Heap allocate + + // Allocate 4 extra bytes for our size header (unfortunate, + // since malloc already added its own header). + void* ptr = ::malloc(REALBLOCK_SIZE(bytes)); + + if (ptr == NULL) { + // Flush memory pools to try and recover space + flushPool(smallPool, smallPoolSize); + flushPool(medPool, medPoolSize); + ptr = ::malloc(REALBLOCK_SIZE(bytes)); + } + + if (ptr == NULL) { + if ((System::outOfMemoryCallback != NULL) && + (System::outOfMemoryCallback(REALBLOCK_SIZE(bytes), true) == true)) { + // Re-attempt the malloc + ptr = ::malloc(REALBLOCK_SIZE(bytes)); + } + } + + if (ptr == NULL) { + if (System::outOfMemoryCallback != NULL) { + // Notify the application + System::outOfMemoryCallback(REALBLOCK_SIZE(bytes), false); + } +# ifdef G3D_DEBUG + debugPrintf("::malloc(%d) returned NULL\n", REALBLOCK_SIZE(bytes)); +# endif + debugAssertM(ptr != NULL, + "::malloc returned NULL. Either the " + "operating system is out of memory or the " + "heap is corrupt."); + return NULL; + } + + *(uint32*)ptr = bytes; + + return REALPTR_TO_USERPTR(ptr); + } + + + void free(void* ptr) { + if (ptr == NULL) { + // Free does nothing on null pointers + return; + } + + assert(isValidPointer(ptr)); + + if (inTinyHeap(ptr)) { + lock(); + tinyFree(ptr); + unlock(); + return; + } + + uint32 bytes = *(uint32*)USERPTR_TO_REALPTR(ptr); + + lock(); + if (bytes <= smallBufferSize) { + if (smallPoolSize < maxSmallBuffers) { + smallPool[smallPoolSize] = MemBlock(ptr, bytes); + ++smallPoolSize; + unlock(); + return; + } + } else if (bytes <= medBufferSize) { + if (medPoolSize < maxMedBuffers) { + medPool[medPoolSize] = MemBlock(ptr, bytes); + ++medPoolSize; + unlock(); + return; + } + } + bytesAllocated -= REALBLOCK_SIZE(bytes); + unlock(); + + // Free; the buffer pools are full or this is too big to store. + ::free(USERPTR_TO_REALPTR(ptr)); + } + + std::string performance() const { + if (totalMallocs > 0) { + int pooled = mallocsFromTinyPool + + mallocsFromSmallPool + + mallocsFromMedPool; + + int total = totalMallocs; + + return format("malloc performance: %5.1f%% <= %db, %5.1f%% <= %db, " + "%5.1f%% <= %db, %5.1f%% > %db", + 100.0 * mallocsFromTinyPool / total, + BufferPool::tinyBufferSize, + 100.0 * mallocsFromSmallPool / total, + BufferPool::smallBufferSize, + 100.0 * mallocsFromMedPool / total, + BufferPool::medBufferSize, + 100.0 * (1.0 - (double)pooled / total), + BufferPool::medBufferSize); + } else { + return "No System::malloc calls made yet."; + } + } + + std::string status() const { + return format("preallocated shared buffers: %5d/%d x %db", + maxTinyBuffers - tinyPoolSize, maxTinyBuffers, tinyBufferSize); + } +}; + +// Dynamically allocated because we need to ensure that +// the buffer pool is still around when the last global variable +// is deallocated. +static BufferPool* bufferpool = NULL; + +std::string System::mallocPerformance() { +#ifndef NO_BUFFERPOOL + return bufferpool->performance(); +#else + return "NO_BUFFERPOOL"; +#endif +} + +std::string System::mallocStatus() { +#ifndef NO_BUFFERPOOL + return bufferpool->status(); +#else + return "NO_BUFFERPOOL"; +#endif +} + + +void System::resetMallocPerformanceCounters() { +#ifndef NO_BUFFERPOOL + bufferpool->totalMallocs = 0; + bufferpool->mallocsFromMedPool = 0; + bufferpool->mallocsFromSmallPool = 0; + bufferpool->mallocsFromTinyPool = 0; +#endif +} + + +#ifndef NO_BUFFERPOOL +inline void initMem() { + // Putting the test here ensures that the system is always + // initialized, even when globals are being allocated. + static bool initialized = false; + if (! initialized) { + bufferpool = new BufferPool(); + initialized = true; + } +} +#endif + + +void* System::malloc(size_t bytes) { +#ifndef NO_BUFFERPOOL + initMem(); + return bufferpool->malloc(bytes); +#else + return ::malloc(bytes); +#endif +} + +void* System::calloc(size_t n, size_t x) { +#ifndef NO_BUFFERPOOL + void* b = System::malloc(n * x); + debugAssertM(b != NULL, "System::malloc returned NULL"); + debugAssertM(isValidHeapPointer(b), "System::malloc returned an invalid pointer"); + System::memset(b, 0, n * x); + return b; +#else + return ::calloc(n, x); +#endif +} + + +void* System::realloc(void* block, size_t bytes) { +#ifndef NO_BUFFERPOOL + initMem(); + return bufferpool->realloc(block, bytes); +#else + return ::realloc(block, bytes); +#endif +} + + +void System::free(void* p) { +#ifndef NO_BUFFERPOOL + bufferpool->free(p); +#else + return ::free(p); +#endif +} + + +void* System::alignedMalloc(size_t bytes, size_t alignment) { + + alwaysAssertM(isPow2(alignment), "alignment must be a power of 2"); + + // We must align to at least a word boundary. + alignment = iMax(alignment, sizeof(void *)); + + // Pad the allocation size with the alignment size and the + // size of the redirect pointer. + size_t totalBytes = bytes + alignment + sizeof(void*); + + size_t truePtr = (size_t)System::malloc(totalBytes); + + if (truePtr == 0) { + // malloc returned NULL + return NULL; + } + + debugAssert(isValidHeapPointer((void*)truePtr)); + #ifdef G3D_WIN32 + // The blocks we return will not be valid Win32 debug heap + // pointers because they are offset + // debugAssert(_CrtIsValidPointer((void*)truePtr, totalBytes, TRUE) ); + #endif + + // The return pointer will be the next aligned location (we must at least + // leave space for the redirect pointer, however). + size_t alignedPtr = truePtr + sizeof(void*); + + // 2^n - 1 has the form 1111... in binary. + uint32 bitMask = (alignment - 1); + + // Advance forward until we reach an aligned location. + while ((alignedPtr & bitMask) != 0) { + alignedPtr += sizeof(void*); + } + + debugAssert(alignedPtr - truePtr + bytes <= totalBytes); + + // Immediately before the aligned location, write the true array location + // so that we can free it correctly. + size_t* redirectPtr = (size_t *)(alignedPtr - sizeof(void *)); + redirectPtr[0] = truePtr; + + debugAssert(isValidHeapPointer((void*)truePtr)); + + #ifdef G3D_WIN32 + debugAssert( _CrtIsValidPointer((void*)alignedPtr, bytes, TRUE) ); + #endif + return (void *)alignedPtr; +} + + +void System::alignedFree(void* _ptr) { + if (_ptr == NULL) { + return; + } + + size_t alignedPtr = (size_t)_ptr; + + // Back up one word from the pointer the user passed in. + // We now have a pointer to a pointer to the true start + // of the memory block. + size_t* redirectPtr = (size_t*)(alignedPtr - sizeof(void *)); + + // Dereference that pointer so that ptr = true start + void* truePtr = (void*)redirectPtr[0]; + + debugAssert(isValidHeapPointer((void*)truePtr)); + System::free(truePtr); +} + + +void System::setEnv(const std::string& name, const std::string& value) { + std::string cmd = name + "=" + value; +# ifdef G3D_WIN32 + _putenv(cmd.c_str()); +# else + // Many linux implementations of putenv expect char* + putenv(const_cast<char*>(cmd.c_str())); +# endif +} + +const char* System::getEnv(const std::string& name) { + return getenv(name.c_str()); +} + +static void var(TextOutput& t, const std::string& name, const std::string& val) { + t.writeSymbols(name,"="); + t.writeString(val); + t.writeNewline(); +} + + +static void var(TextOutput& t, const std::string& name, const bool val) { + t.writeSymbols(name, "=", val ? "Yes" : "No"); + t.writeNewline(); +} + + +static void var(TextOutput& t, const std::string& name, const int val) { + t.writeSymbols(name,"="); + t.writeNumber(val); + t.writeNewline(); +} + +void System::describeSystem( + std::string& s) { + + TextOutput t; + describeSystem(t); + t.commitString(s); +} + +void System::describeSystem( + TextOutput& t) { + + t.writeSymbols("App", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Name", System::currentProgramFilename()); + char cwd[1024]; + getcwd(cwd, 1024); + var(t, "cwd", std::string(cwd)); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("OS", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Name", System::operatingSystem()); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("CPU", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Vendor", System::cpuVendor()); + var(t, "Architecture", System::cpuArchitecture()); + var(t, "hasCPUID", System::hasCPUID()); + var(t, "hasMMX", System::hasMMX()); + var(t, "hasSSE", System::hasSSE()); + var(t, "hasSSE2", System::hasSSE2()); + var(t, "hasSSE3", System::hasSSE3()); + var(t, "has3DNow", System::has3DNow()); + var(t, "hasRDTSC", System::hasRDTSC()); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("G3D", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Link version", G3D_VER); + var(t, "Compile version", System::version()); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); +} + + +int System::cpuSpeedMHz() { + return g_cpuInfo.m_cpuSpeed; +} + + +void System::setClipboardText(const std::string& s) { +# ifdef G3D_WIN32 + if (OpenClipboard(NULL)) { + HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, s.size() + 1); + if (hMem) { + char *pMem = (char*)GlobalLock(hMem); + strcpy(pMem, s.c_str()); + GlobalUnlock(hMem); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + } + + CloseClipboard(); + GlobalFree(hMem); + } +# endif +} + + +std::string System::getClipboardText() { + std::string s; + +# ifdef G3D_WIN32 + if (OpenClipboard(NULL)) { + HANDLE h = GetClipboardData(CF_TEXT); + + if (h) { + char* temp = (char*)GlobalLock(h); + if (temp) { + s = temp; + } + temp = NULL; + GlobalUnlock(h); + } + CloseClipboard(); + } +# endif + return s; +} + + +std::string System::currentDateString() { + time_t t1; + ::time(&t1); + tm* t = localtime(&t1); + return format("%d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); +} + + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/TextInput.cpp b/externals/g3dlite/G3D.lib/source/TextInput.cpp new file mode 100644 index 00000000000..d5dc14fb6a0 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/TextInput.cpp @@ -0,0 +1,988 @@ +/** + @file TextInput.cpp + + @author Morgan McGuire, graphics3d.com + + @cite Based on a lexer written by Aaron Orenstein. + + @created 2001-11-27 + @edited 2008-07-14 + */ + +#include "G3D/fileutils.h" +#include "G3D/TextInput.h" +#include "G3D/BinaryInput.h" +#include "G3D/stringutils.h" + +#ifdef _MSC_VER +# pragma warning (push) +// conversion from 'int' to 'char', possible loss of data (TODO: fix underlying problems) +# pragma warning (disable: 4244) +#endif + +namespace G3D { + +double Token::number() const { + if (_type == NUMBER) { + std::string s = toLower(_string); + if (s == "-1.#ind00") { + return nan(); + } + + if (s == "1.#inf00") { + return inf(); + } + + if (s == "-1.#inf00") { + return -inf(); + } + + double n; + if ((_string.length() > 2) && + (_string[0] == '0') && + (_string[1] == 'x')) { + // Hex + uint32 i; + sscanf(_string.c_str(), "%x", &i); + n = i; + } else { + sscanf(_string.c_str(), "%lg", &n); + } + return n; + } else { + return 0.0; + } +} + + +TextInput::Settings::Settings () + : cComments(true), cppComments(true), escapeSequencesInStrings(true), + otherCommentCharacter('\0'), otherCommentCharacter2('\0'), + signedNumbers(true), singleQuotedStrings(true), sourceFileName(), + startingLineNumberOffset(0), msvcSpecials(true), proofSymbols(false), + caseSensitive(true) +{ + trueSymbols.insert("true"); + falseSymbols.insert("false"); +} + + +Token TextInput::peek() { + if (stack.size() == 0) { + Token t = nextToken(); + push(t); + } + + return stack.front(); +} + + +int TextInput::peekLineNumber() { + return peek().line(); +} + + +int TextInput::peekCharacterNumber() { + return peek().character(); +} + + +Token TextInput::read() { + if (stack.size() > 0) { + Token t = stack.front(); + stack.pop_front(); + return t; + } else { + return nextToken(); + } +} + +static void toUpper(Set<std::string>& set) { + Array<std::string> symbols; + set.getMembers(symbols); + set.clear(); + for (int i = 0; i < symbols.size(); ++i) { + set.insert(toUpper(symbols[i])); + } +} + +void TextInput::init() { + currentCharOffset = 0; + charNumber = 1; + lineNumber = 1 + options.startingLineNumberOffset; + + if (! options.caseSensitive) { + // Convert true and false symbols to all uppercase for fast comparisons + toUpper(options.trueSymbols); + toUpper(options.falseSymbols); + } +} + + +void TextInput::push(const Token& t) { + stack.push_front(t); +} + + +bool TextInput::hasMore() { + return (peek()._type != Token::END); +} + + +int TextInput::eatInputChar() { + // Don't go off the end + if (currentCharOffset >= (unsigned int)buffer.length()) { + return EOF; + } + + unsigned char c = buffer[currentCharOffset]; + ++currentCharOffset; + + // update lineNumber and charNumber to reflect the location of the *next* + // character which will be read. + // + // We update even for CR because the user is allowed to do arbitrarily + // stupid things, like put a bunch of literal CRs inside a quoted string. + // + // We eat all whitespace between tokens, so they should never see a + // lineNumber that points to a CR. However, if they have some kind of + // syntax error in a token that appears *after* a quoted string + // containing CRs, they should get a correct character number. + + // TODO: http://sourceforge.net/tracker/index.php?func=detail&aid=1341266&group_id=76879&atid=548565 + + if (c == '\n') { + ++lineNumber; + charNumber = 1; + } else { + ++charNumber; + } + + return c; +} + +int TextInput::peekInputChar(unsigned int distance) { + // Don't go off the end + if ((currentCharOffset + distance) >= (unsigned int)buffer.length()) { + return EOF; + } + + unsigned char c = buffer[currentCharOffset + distance]; + return c; +} + + +Token TextInput::nextToken() { + Token t; + + t._line = lineNumber; + t._character = charNumber; + t._type = Token::END; + t._extendedType = Token::END_TYPE; + + int c = peekInputChar(); + if (c == EOF) { + return t; + } + + bool whitespaceDone = false; + while (! whitespaceDone) { + whitespaceDone = true; + + // Consume whitespace + while (isWhiteSpace(c)) { + c = eatAndPeekInputChar(); + } + + int c2 = peekInputChar(1); + if ((options.cppComments && c == '/' && c2 == '/') + || (options.otherCommentCharacter != '\0' + && c == options.otherCommentCharacter) + || (options.otherCommentCharacter2 != '\0' + && c == options.otherCommentCharacter2)) { + + // Single line comment, consume to newline or EOF. + + do { + c = eatAndPeekInputChar(); + } while (! isNewline(c) && c != EOF); + + // There is whitespace after the comment (in particular, the + // newline that terminates the comment). There might also be + // whitespace at the start of the next line. + whitespaceDone = false; + + } else if (options.cComments && (c == '/') && (c2 == '*')) { + + // consume both start-comment chars, can't let the trailing one + // help close the comment. + eatInputChar(); + eatInputChar(); + + // Multi-line comment, consume to end-marker or EOF. + c = peekInputChar(); + c2 = peekInputChar(1); + while (! ((c == '*') && (c2 == '/')) && (c != EOF)) { + eatInputChar(); + c = c2; + c2 = peekInputChar(1); + } + eatInputChar(); // eat closing '*' + eatInputChar(); // eat closing '/' + + c = peekInputChar(); + + // May be whitespace after comment. + whitespaceDone = false; + } + + } // while (! whitespaceDone) + + t._line = lineNumber; + t._character = charNumber; + + // handle EOF + if (c == EOF) { + return t; + } + + // Extended ASCII parses as itself, except for EOF + if (c > 127 && c < 255) { + t._type = Token::SYMBOL; + t._extendedType = Token::SYMBOL_TYPE; + t._string = c; + c = eatAndPeekInputChar(); + } + + + // Perform appropriate setup for a symbol (including setting up the token + // string to start with c), eat the input character, and overwrite + // 'c' with the peeked next input character. +#define SETUP_SYMBOL(c) \ + { \ + t._type = Token::SYMBOL; \ + t._extendedType = Token::SYMBOL_TYPE; \ + t._string = c; \ + c = eatAndPeekInputChar(); \ + } + + switch (c) { + + case '@': // Simple symbols -> just themselves. + case '(': + case ')': + case ',': + case ';': + case '{': + case '}': + case '[': + case ']': + case '#': + case '$': + case '?': + case '%': + SETUP_SYMBOL(c); + return t; + + case '-': // negative number, -, --, -=, or -> + SETUP_SYMBOL(c); + + switch (c) { + case '>': // -> + case '-': // -- + case '=': // -= + t._string += c; + eatInputChar(); + return t; + } + + if (options.signedNumbers + && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) { + + // Negative number. 'c' is still the first digit, and is + // the next input char. + + goto numLabel; + } + + // plain - + return t; + + case '+': // positive number, +, ++, or += + SETUP_SYMBOL(c); + + switch (c) { + case '+': // ++ + case '=': // += + t._string += c; + eatInputChar(); + return t; + } + + if (options.signedNumbers + && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) { + + // Positive number. 'c' is still the first digit, and is + // the next input char. + + goto numLabel; + } + + return t; + + case ':': // : or :: or ::> or ::= or := or :> + SETUP_SYMBOL(c); + + if (c == ':') { + t._string += c; + eatInputChar(); + + if (options.proofSymbols) { + c = peekInputChar(0); + + if ((c == '>') || (c == '=')) { + t._string += c; + eatInputChar(); + } + } + } + else if (options.proofSymbols && (c == '=' || c == '>')) { + t._string += c; + eatInputChar(); + } + return t; + + case '=': // = or == or => + SETUP_SYMBOL(c); + + if (c == '=') { + t._string += c; + eatInputChar(); + return t; + } else if (options.proofSymbols && (c == '>')) { + t._string += c; + eatInputChar(); + return t; + } + return t; + + case '*': // * or *= + case '/': // / or /= + case '!': // ! or != + case '~': // ~ or ~= + case '^': // ^ or ^= + SETUP_SYMBOL(c); + + if (c == '=') { + t._string += c; + eatInputChar(); + return t; + } + return t; + + case '>': // >, >>,or >= + case '<': // <<, <<, or <= or <- or <: + case '|': // ||, ||, or |= or |- + case '&': // &, &&, or &= + { + int orig_c = c; + SETUP_SYMBOL(c); + + if ((c == '=') || (orig_c == c)) { + t._string += c; + eatInputChar(); + return t; + } else if (options.proofSymbols) { + if ((orig_c == '<') && (c == '-')) { + t._string += c; + eatInputChar(); + } else if ((orig_c == '|') && (c == '-')) { + t._string += c; + eatInputChar(); + } else if ((orig_c == '<') && (c == ':')) { + t._string += c; + + c = eatAndPeekInputChar(); + + if (c == ':') { + t._string += c; + eatInputChar(); + } + } + } + } + return t; + + case '\\': // backslash or escaped comment char. + SETUP_SYMBOL(c); + + if ((options.otherCommentCharacter != '\0' + && c == options.otherCommentCharacter) + || (options.otherCommentCharacter2 != '\0' + && c == options.otherCommentCharacter2)) { + + // escaped comment character. Return the raw comment + // char (no backslash). + + t._string = c; + eatInputChar(); + return t; + } + return t; + + case '.': // number, ., .., or ... + if (isDigit(peekInputChar(1))) { + // We're parsing a float that began without a leading zero + goto numLabel; + } + + SETUP_SYMBOL(c); + + if (c == '.') { // .. or ... + t._string += c; + c = eatAndPeekInputChar(); + + if (c == '.') { // ... + t._string += c; + eatInputChar(); + } + return t; + } + + return t; + + } // switch (c) + +#undef SETUP_SYMBOL + +numLabel: + if (isDigit(c) || (c == '.')) { + + // A number. Note-- single dots have been + // parsed already, so a . indicates a number + // less than 1 in floating point form. + + // [0-9]*(\.[0-9]) or [0-9]+ or 0x[0-9,A-F]+ + + if (t._string != "-") { + // If we picked up a leading "-" sign above, keep it, + // otherwise drop the string parsed thus far + t._string = ""; + } + t._type = Token::NUMBER; + if (c == '.') { + t._extendedType = Token::FLOATING_POINT_TYPE; + } else { + t._extendedType = Token::INTEGER_TYPE; + } + + if ((c == '0') && (peekInputChar(1) == 'x')) { + // Hex number + t._string += "0x"; + + // skip the 0x + eatInputChar(); + eatInputChar(); + + c = peekInputChar(); + while (isDigit(c) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))) { + t._string += c; + c = eatAndPeekInputChar(); + } + + } else { + // Non-hex number + + // Read the part before the decimal. + while (isDigit(c)) { + t._string += c; + c = eatAndPeekInputChar(); + } + + // True if we are reading a floating-point special type + bool isSpecial = false; + + // Read the decimal, if one exists + if (c == '.') { + t._extendedType = Token::FLOATING_POINT_TYPE; + + // The '.' character was a decimal point, not the start of a + // method or range operator + t._string += c; + c = eatAndPeekInputChar(); + + // Floating point specials (msvc format only) + if (options.msvcSpecials && (c == '#')) { + isSpecial = true; + // We are reading a floating point special value + // of the form -1.#IND00, -1.#INF00, or 1.#INF00 + c = eatAndPeekInputChar(); + char test = c; + if (! options.caseSensitive) { + test = toupper(c); + } + if (test != 'I') { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or nan) " + "format.", + t.line(), charNumber); + } + c = eatAndPeekInputChar(); + test = c; + if (! options.caseSensitive) { + test = toupper(c); + } + if (test != 'N') { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or nan) " + "format.", + t.line(), charNumber); + } + t._string += "#IN"; + c = eatAndPeekInputChar(); + test = c; + if (! options.caseSensitive) { + test = toupper(c); + } + if ((test != 'F') && (test != 'D')) { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or nan) " + "format.", + t.line(), charNumber); + } + t._string += c; + for (int j = 0; j < 2; ++j) { + c = eatAndPeekInputChar(); + if (c != '0') { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or" + "nan) format.", + t.line(), charNumber); + } + t._string += (char)c; + } + + } else { + + // Read the part after the decimal + while (isDigit((char)c)) { + t._string += (char)c; + c = eatAndPeekInputChar(); + } + } + } + + if (! isSpecial && ((c == 'e') || (c == 'E'))) { + // Read exponent + t._extendedType = Token::FLOATING_POINT_TYPE; + t._string += c; + + c = eatAndPeekInputChar(); + if ((c == '-') || (c == '+')) { + t._string += c; + c = eatAndPeekInputChar(); + } + + while (isDigit(c)) { + t._string += c; + c = eatAndPeekInputChar(); + } + } + } + return t; + + } else if (isLetter(c) || (c == '_')) { + // Identifier or keyword + // [A-Za-z_][A-Za-z_0-9]* + + t._type = Token::SYMBOL; + t._extendedType = Token::SYMBOL_TYPE; + t._string = ""; + do { + t._string += c; + c = eatAndPeekInputChar(); + } while (isLetter(c) || isDigit(c) || (c == '_')); + + // See if this symbol is actually a boolean + if ((options.trueSymbols.size() > 0) || (options.falseSymbols.size() > 0)) { + std::string str = t._string; + if (! options.caseSensitive) { + str = toUpper(str); + } + if (options.trueSymbols.contains(str)) { + t._type = Token::BOOLEAN; + t._extendedType = Token::BOOLEAN_TYPE; + t._bool = true; + } else if (options.falseSymbols.contains(str)) { + t._type = Token::BOOLEAN; + t._extendedType = Token::BOOLEAN_TYPE; + t._bool = false; + } + } + + return t; + + } else if (c == '\"') { + + // Discard the double-quote. + eatInputChar(); + + // Double quoted string + parseQuotedString('\"', t); + return t; + + } else if (c == '\'') { + + // Discard the single-quote. + eatInputChar(); + + if (options.singleQuotedStrings) { + // Single quoted string + parseQuotedString('\'', t); + } else { + t._string = c; + t._type = Token::SYMBOL; + t._extendedType = Token::SYMBOL_TYPE; + } + return t; + + } // end of special case tokens + + if (c == EOF) { + t._type = Token::END; + t._extendedType = Token::END_TYPE; + t._string = ""; + return t; + } + + // Some unknown token + debugAssertM(false, + format("Unrecognized token type beginning with character '%c' (ASCII %d)", + c, c)); + return t; +} + + +void TextInput::parseQuotedString(unsigned char delimiter, Token& t) { + + t._type = Token::STRING; + + if (delimiter == '\'') { + t._extendedType = Token::SINGLE_QUOTED_TYPE; + } else { + t._extendedType = Token::DOUBLE_QUOTED_TYPE; + } + + while (true) { + // We're definitely going to consume the next input char, so we get + // it right now. This makes the condition handling below a bit easier. + int c = eatInputChar(); + + if (c == EOF) { + // END inside a quoted string. (We finish the string.) + break; + } + + if (options.escapeSequencesInStrings && (c == '\\')) { + // An escaped character. We're definitely going to consume it, + // so we get it (and consume it) now. + + c = eatInputChar(); + + switch (c) { + case 'r': + t._string += '\r'; + break; + case 'n': + t._string += '\n'; + break; + case 't': + t._string += '\t'; + break; + case '0': + t._string += '\0'; + break; + + case '\\': + case '\"': + case '\'': + t._string += (char)c; + break; + + default: + if (((c == options.otherCommentCharacter) && + (options.otherCommentCharacter != '\0')) || + ((c == options.otherCommentCharacter2) && + (options.otherCommentCharacter2 != '\0'))) { + t._string += c; + } + // otherwise, some illegal escape sequence; skip it. + break; + + } // switch + + } else if (c == delimiter) { + // End of the string. Already consumed the character. + break; + } else { + // All other chars, go on to the string. Already consumed the + // character. + t._string += (char)c; + } + + } +} + +bool TextInput::readBoolean() { + Token t(read()); + + if (t._type == Token::BOOLEAN) { + return t.boolean(); + } + + // Push initial token back, and throw an error. We intentionally + // indicate that the wrong type is the type of the initial token. + // Logically, the number started there. + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::BOOLEAN, t._type); +} + +double TextInput::readNumber() { + Token t(read()); + + if (t._type == Token::NUMBER) { + return t.number(); + } + + // Even if signedNumbers is disabled, readNumber attempts to + // read a signed number, so we handle that case here. + if (! options.signedNumbers + && (t._type == Token::SYMBOL) + && ((t._string == "-") + || (t._string == "+"))) { + + Token t2(read()); + + if ((t2._type == Token::NUMBER) + && (t2._character == t._character + 1)) { + + if (t._string == "-") { + return -t2.number(); + } else { + return t2.number(); + } + } + + // push back the second token. + push(t2); + } + + // Push initial token back, and throw an error. We intentionally + // indicate that the wrong type is the type of the initial token. + // Logically, the number started there. + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::NUMBER, t._type); +} + + +Token TextInput::readStringToken() { + Token t(read()); + + if (t._type == Token::STRING) { // fast path + return t; + } + + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::STRING, t._type); +} + +std::string TextInput::readString() { + return readStringToken()._string; +} + +void TextInput::readString(const std::string& s) { + Token t(readStringToken()); + + if (t._string == s) { // fast path + return; + } + + push(t); + throw WrongString(options.sourceFileName, t.line(), t.character(), + s, t._string); +} + + +Token TextInput::readSymbolToken() { + Token t(read()); + + if (t._type == Token::SYMBOL) { // fast path + return t; + } + + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::SYMBOL, t._type); +} + + +std::string TextInput::readSymbol() { + return readSymbolToken()._string; +} + +void TextInput::readSymbol(const std::string& symbol) { + Token t(readSymbolToken()); + + if (t._string == symbol) { // fast path + return; + } + + push(t); + throw WrongSymbol(options.sourceFileName, t.line(), t.character(), + symbol, t._string); +} + + +TextInput::TextInput(const std::string& filename, const Settings& opt) : options(opt) { + init(); + std::string input = readWholeFile(filename); + + if (options.sourceFileName.empty()) { + options.sourceFileName = filename; + } + int n = input.size(); + buffer.resize(n); + System::memcpy(buffer.getCArray(), input.c_str(), n); +} + + +TextInput::TextInput(FS fs, const std::string& str, const Settings& opt) : options(opt) { + (void)fs; + init(); + if (options.sourceFileName.empty()) { + if (str.length() < 14) { + options.sourceFileName = std::string("\"") + str + "\""; + } else { + options.sourceFileName = std::string("\"") + str.substr(0, 10) + "...\""; + } + } + buffer.resize(str.length()); // we don't bother copying trailing NUL. + System::memcpy(buffer.getCArray(), str.c_str(), buffer.size()); +} + + +const std::string& TextInput::filename() const { + return options.sourceFileName; +} + +/////////////////////////////////////////////////////////////////////////////////// + +TextInput::TokenException::TokenException( + const std::string& src, + int ln, + int ch) : sourceFile(src), line(ln), character(ch) { + + message = format("%s(%d) : ", sourceFile.c_str(), line); +} + +/////////////////////////////////////////////////////////////////////////////////// + +static const char* tokenTypeToString(Token::Type t) { + switch (t) { + case Token::SYMBOL: + return "Token::SYMBOL"; + case Token::STRING: + return "Token::STRING"; + case Token::NUMBER: + return "Token::NUMBER"; + case Token::END: + return "Token::END"; + default: + debugAssertM(false, "Fell through switch"); + return "?"; + } +} + +TextInput::WrongTokenType::WrongTokenType( + const std::string& src, + int ln, + int ch, + Token::Type e, + Token::Type a) : + TokenException(src, ln, ch), expected(e), actual(a) { + + message += format("Expected token of type %s, found type %s.", + tokenTypeToString(e), tokenTypeToString(a)); +} + + +TextInput::BadMSVCSpecial::BadMSVCSpecial( + const std::string& src, + int ln, + int ch) : + TokenException(src, ln, ch) { +} + + +TextInput::WrongSymbol::WrongSymbol( + const std::string& src, + int ln, + int ch, + const std::string& e, + const std::string& a) : + TokenException(src, ln, ch), expected(e), actual(a) { + + message += format("Expected symbol '%s', found symbol '%s'.", + e.c_str(), a.c_str()); +} + + +TextInput::WrongString::WrongString( + const std::string& src, + int ln, + int ch, + const std::string& e, + const std::string& a) : + TokenException(src, ln, ch), expected(e), actual(a) { + + message += format("Expected string '%s', found string '%s'.", + e.c_str(), a.c_str()); +} + + +void deserialize(bool& b, TextInput& ti) { + b = ti.readSymbol() == "true"; +} + + +void deserialize(int& b, TextInput& ti) { + b = iRound(ti.readNumber()); +} + + +void deserialize(uint8& b, TextInput& ti) { + b = (uint8)iRound(ti.readNumber()); +} + + +void deserialize(double& b, TextInput& ti) { + b = ti.readNumber(); +} + + +void deserialize(float& b, TextInput& ti) { + b = (float)ti.readNumber(); +} + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/G3D.lib/source/TextOutput.cpp b/externals/g3dlite/G3D.lib/source/TextOutput.cpp new file mode 100644 index 00000000000..f7a40d9798b --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/TextOutput.cpp @@ -0,0 +1,452 @@ +/** + @file TextOutput.cpp + + @maintainer Morgan McGuire, morgan@graphics3d.com + @created 2004-06-21 + @edited 2006-08-14 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/TextOutput.h" +#include "G3D/Log.h" +#include "G3D/fileutils.h" + +namespace G3D { + +TextOutput::TextOutput(const TextOutput::Settings& opt) : + startingNewLine(true), + currentColumn(0), + inDQuote(false), + filename(""), + indentLevel(0) +{ + setOptions(opt); +} + + +TextOutput::TextOutput(const std::string& fil, const TextOutput::Settings& opt) : + startingNewLine(true), + currentColumn(0), + inDQuote(false), + filename(fil), + indentLevel(0) +{ + + setOptions(opt); +} + + +void TextOutput::setIndentLevel(int i) { + indentLevel = i; + + // If there were more pops than pushes, don't let that take us below 0 indent. + // Don't ever indent more than the number of columns. + indentSpaces = + iClamp(option.spacesPerIndent * indentLevel, + 0, + option.numColumns - 1); +} + + +void TextOutput::setOptions(const Settings& _opt) { + option = _opt; + + debugAssert(option.numColumns > 1); + + setIndentLevel(indentLevel); + + newline = (option.newlineStyle == Settings::NEWLINE_WINDOWS) ? "\r\n" : "\n"; +} + + +void TextOutput::pushIndent() { + setIndentLevel(indentLevel + 1); +} + + +void TextOutput::popIndent() { + setIndentLevel(indentLevel - 1); +} + + +static std::string escape(const std::string& string) { + std::string result = ""; + + for (std::string::size_type i = 0; i < string.length(); ++i) { + char c = string.at(i); + switch (c) { + case '\0': + result += "\\0"; + break; + + case '\r': + result += "\\r"; + break; + + case '\n': + result += "\\n"; + break; + + case '\t': + result += "\\t"; + break; + + case '\\': + result += "\\\\"; + break; + + default: + result += c; + } + } + + return result; +} + +void TextOutput::writeString(const std::string& string) { + // Convert special characters to escape sequences + this->printf("\"%s\"", escape(string).c_str()); +} + + +void TextOutput::writeBoolean(bool b) { + this->printf("%s ", b ? option.trueSymbol.c_str() : option.falseSymbol.c_str()); +} + +void TextOutput::writeNumber(double n) { + this->printf("%f ", n); +} + + +void TextOutput::writeNumber(int n) { + this->printf("%d ", n); +} + + +void TextOutput::writeSymbol(const std::string& string) { + if (string.size() > 0) { + // TODO: check for legal symbols? + this->printf("%s ", string.c_str()); + } +} + +void TextOutput::writeSymbols( + const std::string& a, + const std::string& b, + const std::string& c, + const std::string& d, + const std::string& e, + const std::string& f) { + + writeSymbol(a); + writeSymbol(b); + writeSymbol(c); + writeSymbol(d); + writeSymbol(e); + writeSymbol(f); +} + + +void TextOutput::printf(const std::string formatString, ...) { + va_list argList; + va_start(argList, formatString); + this->vprintf(formatString.c_str(), argList); + va_end(argList); +} + + +void TextOutput::printf(const char* formatString, ...) { + va_list argList; + va_start(argList, formatString); + this->vprintf(formatString, argList); + va_end(argList); +} + + +void TextOutput::convertNewlines(const std::string& in, std::string& out) { + // TODO: can be significantly optimized in cases where + // single characters are copied in order by walking through + // the array and copying substrings as needed. + + if (option.convertNewlines) { + out = ""; + for (uint32 i = 0; i < in.size(); ++i) { + if (in[i] == '\n') { + // Unix newline + out += newline; + } else if ((in[i] == '\r') && (i + 1 < in.size()) && (in[i + 1] == '\n')) { + // Windows newline + out += newline; + ++i; + } else { + out += in[i]; + } + } + } else { + out = in; + } +} + + +void TextOutput::writeNewline() { + for (uint32 i = 0; i < newline.size(); ++i) { + indentAppend(newline[i]); + } +} + + +void TextOutput::writeNewlines(int numLines) { + for (int i = 0; i < numLines; ++i) { + writeNewline(); + } +} + + +void TextOutput::wordWrapIndentAppend(const std::string& str) { + // TODO: keep track of the last space character we saw so we don't + // have to always search. + + if ((option.wordWrap == Settings::WRAP_NONE) || + (currentColumn + (int)str.size() <= option.numColumns)) { + // No word-wrapping is needed + + // Add one character at a time. + // TODO: optimize for strings without newlines to add multiple + // characters. + for (uint32 i = 0; i < str.size(); ++i) { + indentAppend(str[i]); + } + return; + } + + // Number of columns to wrap against + int cols = option.numColumns - indentSpaces; + + // Copy forward until we exceed the column size, + // and then back up and try to insert newlines as needed. + for (uint32 i = 0; i < str.size(); ++i) { + + indentAppend(str[i]); + if ((str[i] == '\r') && (i + 1 < str.size()) && (str[i + 1] == '\n')) { + // \r\n, we need to hit the \n to enter word wrapping. + ++i; + indentAppend(str[i]); + } + + if (currentColumn >= cols) { + debugAssertM(str[i] != '\n' && str[i] != '\r', + "Should never enter word-wrapping on a newline character"); + + // True when we're allowed to treat a space as a space. + bool unquotedSpace = option.allowWordWrapInsideDoubleQuotes || ! inDQuote; + + // Cases: + // + // 1. Currently in a series of spaces that ends with a newline + // strip all spaces and let the newline + // flow through. + // + // 2. Currently in a series of spaces that does not end with a newline + // strip all spaces and replace them with single newline + // + // 3. Not in a series of spaces + // search backwards for a space, then execute case 2. + + // Index of most recent space + uint32 lastSpace = data.size() - 1; + + // How far back we had to look for a space + uint32 k = 0; + uint32 maxLookBackward = currentColumn - indentSpaces; + + // Search backwards (from current character), looking for a space. + while ((k < maxLookBackward) && + (lastSpace > 0) && + (! ((data[lastSpace] == ' ') && unquotedSpace))) { + --lastSpace; + ++k; + + if ((data[lastSpace] == '\"') && !option.allowWordWrapInsideDoubleQuotes) { + unquotedSpace = ! unquotedSpace; + } + } + + if (k == maxLookBackward) { + // We couldn't find a series of spaces + + if (option.wordWrap == Settings::WRAP_ALWAYS) { + // Strip the last character we wrote, force a newline, + // and replace the last character; + data.pop(); + writeNewline(); + indentAppend(str[i]); + } else { + // Must be Settings::WRAP_WITHOUT_BREAKING + // + // Don't write the newline; we'll come back to + // the word wrap code after writing another character + } + } else { + // We found a series of spaces. If they continue + // to the new string, strip spaces off both. Otherwise + // strip spaces from data only and insert a newline. + + // Find the start of the spaces. firstSpace is the index of the + // first non-space, looking backwards from lastSpace. + uint32 firstSpace = lastSpace; + while ((k < maxLookBackward) && + (firstSpace > 0) && + (data[firstSpace] == ' ')) { + --firstSpace; + ++k; + } + + if (k == maxLookBackward) { + ++firstSpace; + } + + if (lastSpace == (uint32)data.size() - 1) { + // Spaces continued up to the new string + data.resize(firstSpace + 1); + writeNewline(); + + // Delete the spaces from the new string + while ((i < str.size() - 1) && (str[i + 1] == ' ')) { + ++i; + } + } else { + // Spaces were somewhere in the middle of the old string. + // replace them with a newline. + + // Copy over the characters that should be saved + Array<char> temp; + for (uint32 j = lastSpace + 1; j < (uint32)data.size(); ++j) { + char c = data[j]; + + if (c == '\"') { + // Undo changes to quoting (they will be re-done + // when we paste these characters back on). + inDQuote = !inDQuote; + } + temp.append(c); + } + + // Remove those characters and replace with a newline. + data.resize(firstSpace + 1); + writeNewline(); + + // Write them back + for (uint32 j = 0; j < (uint32)temp.size(); ++j) { + indentAppend(temp[j]); + } + + // We are now free to continue adding from the + // new string, which may or may not begin with spaces. + + } // if spaces included new string + } // if hit indent + } // if line exceeded + } // iterate over str +} + + +void TextOutput::indentAppend(char c) { + + if (startingNewLine) { + for (int j = 0; j < indentSpaces; ++j) { + data.push(' '); + } + startingNewLine = false; + currentColumn = indentSpaces; + } + + data.push(c); + + // Don't increment the column count on return character + // newline is taken care of below. + if (c != '\r') { + ++currentColumn; + } + + if (c == '\"') { + inDQuote = ! inDQuote; + } + + startingNewLine = (c == '\n'); + if (startingNewLine) { + currentColumn = 0; + } +} + + +void TextOutput::vprintf(const char* formatString, va_list argPtr) { + std::string str = vformat(formatString, argPtr); + + std::string clean; + convertNewlines(str, clean); + wordWrapIndentAppend(clean); +} + + +void TextOutput::commit(bool flush) { + std::string p = filenamePath(filename); + if (! fileExists(p, false)) { + createDirectory(p); + } + + FILE* f = fopen(filename.c_str(), "wb"); + debugAssert(f); + fwrite(data.getCArray(), 1, data.size(), f); + if (flush) { + fflush(f); + } + fclose(f); +} + + +void TextOutput::commitString(std::string& out) { + // Null terminate + data.push('\0'); + out = data.getCArray(); + data.pop(); +} + + +std::string TextOutput::commitString() { + std::string str; + commitString(str); + return str; +} + + + +///////////////////////////////////////////////////////////////////// + +void serialize(const float& b, TextOutput& to) { + to.writeNumber(b); +} + + +void serialize(const bool& b, TextOutput& to) { + to.writeSymbol(b ? "true" : "false"); +} + + +void serialize(const int& b, TextOutput& to) { + to.writeNumber(b); +} + + +void serialize(const uint8& b, TextOutput& to) { + to.writeNumber(b); +} + + +void serialize(const double& b, TextOutput& to) { + to.writeNumber(b); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/ThreadSet.cpp b/externals/g3dlite/G3D.lib/source/ThreadSet.cpp new file mode 100644 index 00000000000..59060892247 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/ThreadSet.cpp @@ -0,0 +1,147 @@ +#include "G3D/ThreadSet.h" + +namespace G3D { + +int ThreadSet::size() const { + ThreadSet* me = const_cast<ThreadSet*>(this); + me->m_lock.lock(); + int s = m_thread.size(); + me->m_lock.unlock(); + return s; +} + + +int ThreadSet::numStarted() const { + ThreadSet* me = const_cast<ThreadSet*>(this); + me->m_lock.lock(); + int count = 0; + for (int i = 0; i < m_thread.size(); ++i) { + if (m_thread[i]->started()) { + ++count; + } + } + me->m_lock.unlock(); + return count; +} + + +void ThreadSet::start() const { + ThreadSet* me = const_cast<ThreadSet*>(this); + me->m_lock.lock(); + for (int i = 0; i < m_thread.size(); ++i) { + if (! m_thread[i]->started()) { + m_thread[i]->start(); + } + } + me->m_lock.unlock(); +} + + +void ThreadSet::terminate() const { + ThreadSet* me = const_cast<ThreadSet*>(this); + me->m_lock.lock(); + for (int i = 0; i < m_thread.size(); ++i) { + if (m_thread[i]->started()) { + m_thread[i]->terminate(); + } + } + me->m_lock.unlock(); +} + + +void ThreadSet::waitForCompletion() const { + ThreadSet* me = const_cast<ThreadSet*>(this); + me->m_lock.lock(); + for (int i = 0; i < m_thread.size(); ++i) { + if (m_thread[i]->started()) { + m_thread[i]->waitForCompletion(); + } + } + me->m_lock.unlock(); +} + + +int ThreadSet::removeCompleted() { + m_lock.lock(); + for (int i = 0; i < m_thread.size(); ++i) { + if (m_thread[i]->completed()) { + m_thread.fastRemove(i); + --i; + } + } + + int s = m_thread.size(); + m_lock.unlock(); + return s; +} + + +void ThreadSet::clear() { + m_lock.lock(); + m_thread.clear(); + m_lock.unlock(); +} + + +int ThreadSet::insert(const ThreadRef& t) { + m_lock.lock(); + bool found = false; + for (int i = 0; i < m_thread.size() && ! found; ++i) { + found = (m_thread[i] == t); + } + if (! found) { + m_thread.append(t); + } + int s = m_thread.size(); + m_lock.unlock(); + return s; +} + + +bool ThreadSet::remove(const ThreadRef& t) { + m_lock.lock(); + bool found = false; + for (int i = 0; i < m_thread.size() && ! found; ++i) { + found = (m_thread[i] == t); + if (found) { + m_thread.fastRemove(i); + } + } + m_lock.unlock(); + return found; +} + + +bool ThreadSet::contains(const ThreadRef& t) const { + ThreadSet* me = const_cast<ThreadSet*>(this); + me->m_lock.lock(); + bool found = false; + for (int i = 0; i < m_thread.size() && ! found; ++i) { + found = (m_thread[i] == t); + } + me->m_lock.unlock(); + return found; +} + + +ThreadSet::Iterator ThreadSet::begin() { + return m_thread.begin(); +} + + +ThreadSet::Iterator ThreadSet::end() { + return m_thread.end(); +} + + +ThreadSet::ConstIterator ThreadSet::begin() const { + return m_thread.begin(); +} + + +ThreadSet::ConstIterator ThreadSet::end() const { + return m_thread.end(); +} + + +} // namespace G3D diff --git a/externals/g3dlite/G3D.lib/source/Triangle.cpp b/externals/g3dlite/G3D.lib/source/Triangle.cpp new file mode 100644 index 00000000000..ad264b1f72a --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Triangle.cpp @@ -0,0 +1,135 @@ +/** + @file Triangle.cpp + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-04-06 + @edited 2006-01-20 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/platform.h" +#include "G3D/Triangle.h" +#include "G3D/Plane.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/debugAssert.h" +#include "G3D/AABox.h" + +namespace G3D { + + +void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& v2) { + + _plane = Plane(v0, v1, v2); + _vertex[0] = v0; + _vertex[1] = v1; + _vertex[2] = v2; + + static int next[] = {1,2,0}; + + for (int i = 0; i < 3; ++i) { + const Vector3& e = _vertex[next[i]] - _vertex[i]; + edgeMagnitude[i] = e.magnitude(); + + if (edgeMagnitude[i] == 0) { + edgeDirection[i] = Vector3::zero(); + } else { + edgeDirection[i] = e / (float)edgeMagnitude[i]; + } + } + + _edge01 = _vertex[1] - _vertex[0]; + _edge02 = _vertex[2] - _vertex[0]; + + _primaryAxis = _plane.normal().primaryAxis(); + _area = 0.5f * edgeDirection[0].cross(edgeDirection[2]).magnitude() * (edgeMagnitude[0] * edgeMagnitude[2]); + //0.5f * (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).dot(_plane.normal()); +} + + +Triangle::Triangle() { + init(Vector3::zero(), Vector3::zero(), Vector3::zero()); +} + + +Triangle::Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2) { + init(v0, v1, v2); +} + + +Triangle::~Triangle() { +} + + +Triangle::Triangle(class BinaryInput& b) { + deserialize(b); +} + + +void Triangle::serialize(class BinaryOutput& b) { + _vertex[0].serialize(b); + _vertex[1].serialize(b); + _vertex[2].serialize(b); +} + + +void Triangle::deserialize(class BinaryInput& b) { + _vertex[0].deserialize(b); + _vertex[1].deserialize(b); + _vertex[2].deserialize(b); + init(_vertex[0], _vertex[1], _vertex[2]); +} + + +float Triangle::area() const { + return _area; +} + + +const Vector3& Triangle::normal() const { + return _plane.normal(); +} + + +const Plane& Triangle::plane() const { + return _plane; +} + + +Vector3 Triangle::center() const { + return (_vertex[0] + _vertex[1] + _vertex[2]) / 3.0; +} + +Vector3 Triangle::randomPoint() const { + // Choose a random point in the parallelogram + + float s = uniformRandom(); + float t = uniformRandom(); + + if (t > 1.0f - s) { + // Outside the triangle; reflect about the + // diagonal of the parallelogram + t = 1.0f - t; + s = 1.0f - s; + } + + return _edge01 * s + _edge02 * t + _vertex[0]; +} + + +void Triangle::getBounds(AABox& out) const { + Vector3 lo = _vertex[0]; + Vector3 hi = lo; + + for (int i = 1; i < 3; ++i) { + lo = lo.min(_vertex[i]); + hi = hi.max(_vertex[i]); + } + + out = AABox(lo, hi); +} + +} // G3D diff --git a/externals/g3dlite/G3D.lib/source/UprightFrame.cpp b/externals/g3dlite/G3D.lib/source/UprightFrame.cpp new file mode 100644 index 00000000000..78b2c0bb0bb --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/UprightFrame.cpp @@ -0,0 +1,132 @@ +/** + @file UprightFrame.cpp + Box class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-05-02 + @edited 2007-05-05 +*/ + +#include "G3D/UprightFrame.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +UprightFrame::UprightFrame(const CoordinateFrame& cframe) { + Vector3 look = cframe.lookVector(); + + yaw = G3D::pi() + atan2(look.x, look.z); + pitch = asin(look.y); + + translation = cframe.translation; +} + + +CoordinateFrame UprightFrame::toCoordinateFrame() const { + CoordinateFrame cframe; + + Matrix3 P(Matrix3::fromAxisAngle(Vector3::unitX(), pitch)); + Matrix3 Y(Matrix3::fromAxisAngle(Vector3::unitY(), yaw)); + + cframe.rotation = Y * P; + cframe.translation = translation; + + return cframe; +} + + +UprightFrame UprightFrame::operator+(const UprightFrame& other) const { + return UprightFrame(translation + other.translation, pitch + other.pitch, yaw + other.yaw); +} + + +UprightFrame UprightFrame::operator*(const float k) const { + return UprightFrame(translation * k, pitch * k, yaw * k); +} + + +void UprightFrame::unwrapYaw(UprightFrame* a, int N) { + // Use the first point to establish the wrapping convention + for (int i = 1; i < N; ++i) { + const float prev = a[i - 1].yaw; + float& cur = a[i].yaw; + + // No two angles should be more than pi (i.e., 180-degrees) apart. + if (abs(cur - prev) > G3D::pi()) { + // These angles must have wrapped at zero, causing them + // to be interpolated the long way. + + // Find canonical [0, 2pi] versions of these numbers + float p = wrap(prev, twoPi()); + float c = wrap(cur, twoPi()); + + // Find the difference -pi < diff < pi between the current and previous values + float diff = c - p; + if (diff < -G3D::pi()) { + diff += twoPi(); + } else if (diff > G3D::pi()) { + diff -= twoPi(); + } + + // Offset the current from the previous by the difference + // between them. + cur = prev + diff; + } + } +} + + +void UprightFrame::serialize(class BinaryOutput& b) const { + translation.serialize(b); + b.writeFloat32(pitch); + b.writeFloat32(yaw); +} + + +void UprightFrame::deserialize(class BinaryInput& b) { + translation.deserialize(b); + pitch = b.readFloat32(); + yaw = b.readFloat32(); +} + + +void UprightSpline::serialize(class BinaryOutput& b) const { + b.writeBool8(cyclic); + + b.writeInt32(control.size()); + for (int i = 0; i < control.size(); ++i) { + control[i].serialize(b); + } + b.writeInt32(time.size()); + for (int i = 0; i < time.size(); ++i) { + b.writeFloat32(time[i]); + } +} + + +void UprightSpline::deserialize(class BinaryInput& b) { + cyclic = b.readBool8(); + + control.resize(b.readInt32()); + for (int i = 0; i < control.size(); ++i) { + control[i].deserialize(b); + } + + if (b.hasMore()) { + time.resize(b.readInt32()); + for (int i = 0; i < time.size(); ++i) { + time[i] = b.readFloat32(); + } + debugAssert(time.size() == control.size()); + } else { + // Import legacy path + time.resize(control.size()); + for (int i = 0; i < time.size(); ++i) { + time[i] = i; + } + } +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Vector2.cpp b/externals/g3dlite/G3D.lib/source/Vector2.cpp new file mode 100644 index 00000000000..6b7f96a764e --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector2.cpp @@ -0,0 +1,211 @@ +/** + @file Vector2.cpp + + 2D vector class, used for texture coordinates primarily. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @cite Portions based on Dave Eberly'x Magic Software Library + at http://www.magic-software.com + + @created 2001-06-02 + @edited 2006-01-16 + */ + +#include "G3D/platform.h" +#include <stdlib.h> +#include "G3D/Vector2.h" +#include "G3D/g3dmath.h" +#include "G3D/format.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" + +namespace G3D { + +const Vector2& Vector2::zero() { + static Vector2 v(0, 0); + return v; +} + +const Vector2& Vector2::unitX() { + static Vector2 v(1, 0); + return v; +} + +const Vector2& Vector2::unitY() { + static Vector2 v(0, 1); + return v; +} + +const Vector2& Vector2::inf() { + static Vector2 v((float)G3D::inf(), (float)G3D::inf()); + return v; +} + + +const Vector2& Vector2::nan() { + static Vector2 v((float)G3D::nan(), (float)G3D::nan()); + return v; +} + + +const Vector2& Vector2::minFinite() { + static Vector2 v(-FLT_MAX, -FLT_MAX); + return v; +} + + +const Vector2& Vector2::maxFinite() { + static Vector2 v(FLT_MAX, FLT_MAX); + return v; +} + + +size_t Vector2::hashCode() const { + unsigned int xhash = (*(int*)(void*)(&x)); + unsigned int yhash = (*(int*)(void*)(&y)); + + return xhash + (yhash * 37); +} + + +Vector2::Vector2(BinaryInput& b) { + deserialize(b); +} + + +void Vector2::deserialize(BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); +} + + +void Vector2::serialize(BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); +} + + +void Vector2::deserialize(TextInput& t) { + t.readSymbol("("); + x = (float)t.readNumber(); + t.readSymbol(","); + y = (float)t.readNumber(); + t.readSymbol(")"); +} + + +void Vector2::serialize(TextOutput& t) const { + t.writeSymbol("("); + t.writeNumber(x); + t.writeSymbol(","); + t.writeNumber(y); + t.writeSymbol(")"); +} + +//---------------------------------------------------------------------------- + +Vector2 Vector2::random() { + Vector2 result; + + do { + result = Vector2(uniformRandom(-1, 1), uniformRandom(-1, 1)); + + } while (result.squaredLength() >= 1.0f); + + result.unitize(); + + return result; +} + +//---------------------------------------------------------------------------- +Vector2 Vector2::operator/ (float fScalar) const { + Vector2 kQuot; + + if ( fScalar != 0.0f ) { + float fInvScalar = 1.0f / fScalar; + kQuot.x = fInvScalar * x; + kQuot.y = fInvScalar * y; + return kQuot; + } else { + return Vector2::inf(); + } +} + +//---------------------------------------------------------------------------- +Vector2& Vector2::operator/= (float fScalar) { + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + x *= fInvScalar; + y *= fInvScalar; + } else { + x = (float)G3D::inf(); + y = (float)G3D::inf(); + } + + return *this; +} + +//---------------------------------------------------------------------------- +float Vector2::unitize (float fTolerance) { + float fLength = length(); + + if (fLength > fTolerance) { + float fInvLength = 1.0f / fLength; + x *= fInvLength; + y *= fInvLength; + } else { + fLength = 0.0; + } + + return fLength; +} + +//---------------------------------------------------------------------------- + +std::string Vector2::toString() const { + return G3D::format("(%g, %g)", x, y); +} + +// 2-char swizzles + +Vector2 Vector2::xx() const { return Vector2 (x, x); } +Vector2 Vector2::yx() const { return Vector2 (y, x); } +Vector2 Vector2::xy() const { return Vector2 (x, y); } +Vector2 Vector2::yy() const { return Vector2 (y, y); } + +// 3-char swizzles + +Vector3 Vector2::xxx() const { return Vector3 (x, x, x); } +Vector3 Vector2::yxx() const { return Vector3 (y, x, x); } +Vector3 Vector2::xyx() const { return Vector3 (x, y, x); } +Vector3 Vector2::yyx() const { return Vector3 (y, y, x); } +Vector3 Vector2::xxy() const { return Vector3 (x, x, y); } +Vector3 Vector2::yxy() const { return Vector3 (y, x, y); } +Vector3 Vector2::xyy() const { return Vector3 (x, y, y); } +Vector3 Vector2::yyy() const { return Vector3 (y, y, y); } + +// 4-char swizzles + +Vector4 Vector2::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Vector2::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Vector2::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Vector2::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Vector2::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Vector2::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Vector2::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Vector2::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Vector2::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Vector2::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Vector2::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Vector2::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Vector2::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Vector2::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Vector2::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Vector2::yyyy() const { return Vector4 (y, y, y, y); } + + + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/Vector2int16.cpp b/externals/g3dlite/G3D.lib/source/Vector2int16.cpp new file mode 100644 index 00000000000..04efee2b4b7 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector2int16.cpp @@ -0,0 +1,47 @@ +/** + @file Vector2int16.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2003-08-09 + @edited 2006-01-29 + */ + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector2int16.h" +#include "G3D/Vector2.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Vector2int16::Vector2int16(const class Vector2& v) { + x = (int16)iFloor(v.x + 0.5); + y = (int16)iFloor(v.y + 0.5); +} + + +Vector2int16::Vector2int16(class BinaryInput& bi) { + deserialize(bi); +} + + +void Vector2int16::serialize(class BinaryOutput& bo) const { + bo.writeInt16(x); + bo.writeInt16(y); +} + + +void Vector2int16::deserialize(class BinaryInput& bi) { + x = bi.readInt16(); + y = bi.readInt16(); +} + + +Vector2int16 Vector2int16::clamp(const Vector2int16& lo, const Vector2int16& hi) { + return Vector2int16(iClamp(x, lo.x, hi.x), iClamp(y, lo.y, hi.y)); +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/Vector3.cpp b/externals/g3dlite/G3D.lib/source/Vector3.cpp new file mode 100644 index 00000000000..bc52640d297 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector3.cpp @@ -0,0 +1,493 @@ +/** + @file Vector3.cpp + + 3D vector class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com + + @created 2001-06-02 + @edited 2006-01-30 + */ + +#include <limits> +#include <stdlib.h> +#include "G3D/Vector3.h" +#include "G3D/g3dmath.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/Vector3int16.h" +#include "G3D/Matrix3.h" +#include "G3D/Vector2.h" +#include "G3D/Vector4int8.h" + +namespace G3D { + +Vector3 Vector3::dummy; + + +Vector3::Vector3(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f) {} + +Vector3::Vector3(const class Vector2& v, float _z) : x(v.x), y(v.y), z(_z) { +} + + +Vector3::Axis Vector3::primaryAxis() const { + + Axis a = X_AXIS; + + double nx = abs(x); + double ny = abs(y); + double nz = abs(z); + + if (nx > ny) { + if (nx > nz) { + a = X_AXIS; + } else { + a = Z_AXIS; + } + } else { + if (ny > nz) { + a = Y_AXIS; + } else { + a = Z_AXIS; + } + } + + return a; +} + + +size_t Vector3::hashCode() const { + unsigned int xhash = (*(int*)(void*)(&x)); + unsigned int yhash = (*(int*)(void*)(&y)); + unsigned int zhash = (*(int*)(void*)(&z)); + + return xhash + (yhash * 37) + (zhash * 101); +} + +std::ostream& operator<<(std::ostream& os, const Vector3& v) { + return os << v.toString(); +} + + +//---------------------------------------------------------------------------- + +double frand() { + return rand() / (double) RAND_MAX; +} + +Vector3::Vector3(TextInput& t) { + deserialize(t); +} + +Vector3::Vector3(BinaryInput& b) { + deserialize(b); +} + + +Vector3::Vector3(const class Vector3int16& v) { + x = v.x; + y = v.y; + z = v.z; +} + + +void Vector3::deserialize(BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); + z = b.readFloat32(); +} + + +void Vector3::deserialize(TextInput& t) { + t.readSymbol("("); + x = (float)t.readNumber(); + t.readSymbol(","); + y = (float)t.readNumber(); + t.readSymbol(","); + z = (float)t.readNumber(); + t.readSymbol(")"); +} + + +void Vector3::serialize(TextOutput& t) const { + t.writeSymbol("("); + t.writeNumber(x); + t.writeSymbol(","); + t.writeNumber(y); + t.writeSymbol(","); + t.writeNumber(z); + t.writeSymbol(")"); +} + + +void Vector3::serialize(BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); + b.writeFloat32(z); +} + + +Vector3 Vector3::random() { + Vector3 result; + + do { + result = Vector3(uniformRandom(-1.0, 1.0), + uniformRandom(-1.0, 1.0), + uniformRandom(-1.0, 1.0)); + } while (result.squaredMagnitude() >= 1.0f); + + result.unitize(); + + return result; +} + +//---------------------------------------------------------------------------- +Vector3 Vector3::operator/ (float fScalar) const { + Vector3 kQuot; + + if ( fScalar != 0.0 ) { + float fInvScalar = 1.0f / fScalar; + kQuot.x = fInvScalar * x; + kQuot.y = fInvScalar * y; + kQuot.z = fInvScalar * z; + return kQuot; + } else { + return Vector3::inf(); + } +} + +//---------------------------------------------------------------------------- +Vector3& Vector3::operator/= (float fScalar) { + if (fScalar != 0.0) { + float fInvScalar = 1.0f / fScalar; + x *= fInvScalar; + y *= fInvScalar; + z *= fInvScalar; + } else { + x = (float)G3D::inf(); + y = (float)G3D::inf(); + z = (float)G3D::inf(); + } + + return *this; +} + +//---------------------------------------------------------------------------- +float Vector3::unitize (float fTolerance) { + float fMagnitude = magnitude(); + + if (fMagnitude > fTolerance) { + float fInvMagnitude = 1.0f / fMagnitude; + x *= fInvMagnitude; + y *= fInvMagnitude; + z *= fInvMagnitude; + } else { + fMagnitude = 0.0f; + } + + return fMagnitude; +} + +//---------------------------------------------------------------------------- + +Vector3 Vector3::reflectAbout(const Vector3& normal) const { + + Vector3 out; + + Vector3 N = normal.direction(); + + // 2 * normal.dot(this) * normal - this + return N * 2 * this->dot(N) - *this; +} + +//---------------------------------------------------------------------------- +Vector3 Vector3::cosRandom(const Vector3& normal) { + double e1 = G3D::uniformRandom(0, 1); + double e2 = G3D::uniformRandom(0, 1); + + // Angle from normal + double theta = acos(sqrt(e1)); + + // Angle about normal + double phi = 2 * pi() * e2; + + // Make a coordinate system + Vector3 U = normal.direction(); + Vector3 V = Vector3::unitX(); + + if (abs(U.dot(V)) > .9) { + V = Vector3::unitY(); + } + + Vector3 W = U.cross(V).direction(); + V = W.cross(U); + + // Convert to rectangular form + return cos(theta) * U + sin(theta) * (cos(phi) * V + sin(phi) * W); +} +//---------------------------------------------------------------------------- + +Vector3 Vector3::hemiRandom(const Vector3& normal) { + Vector3 V = Vector3::random(); + + if (V.dot(normal) < 0) { + return -V; + } else { + return V; + } +} + +//---------------------------------------------------------------------------- + +Vector3 Vector3::reflectionDirection(const Vector3& normal) const { + return -reflectAbout(normal).direction(); +} + +//---------------------------------------------------------------------------- + +Vector3 Vector3::refractionDirection( + const Vector3& normal, + float iInside, + float iOutside) const { + + // From pg. 24 of Henrik Wann Jensen. Realistic Image Synthesis + // Using Photon Mapping. AK Peters. ISBN: 1568811470. July 2001. + + // Invert the directions from Wann Jensen's formulation + // and normalize the vectors. + const Vector3 W = -direction(); + Vector3 N = normal.direction(); + + float h1 = iOutside; + float h2 = iInside; + + if (normal.dot(*this) > 0.0f) { + h1 = iInside; + h2 = iOutside; + N = -N; + } + + const float hRatio = h1 / h2; + const float WdotN = W.dot(N); + + float det = 1.0f - (float)square(hRatio) * (1.0f - (float)square(WdotN)); + + if (det < 0) { + // Total internal reflection + return Vector3::zero(); + } else { + return -hRatio * (W - WdotN * N) - N * sqrt(det); + } +} + +//---------------------------------------------------------------------------- +void Vector3::orthonormalize (Vector3 akVector[3]) { + // If the input vectors are v0, v1, and v2, then the Gram-Schmidt + // orthonormalization produces vectors u0, u1, and u2 as follows, + // + // u0 = v0/|v0| + // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0| + // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1| + // + // where |A| indicates length of vector A and A*B indicates dot + // product of vectors A and B. + + // compute u0 + akVector[0].unitize(); + + // compute u1 + float fDot0 = akVector[0].dot(akVector[1]); + akVector[1] -= akVector[0] * fDot0; + akVector[1].unitize(); + + // compute u2 + float fDot1 = akVector[1].dot(akVector[2]); + fDot0 = akVector[0].dot(akVector[2]); + akVector[2] -= akVector[0] * fDot0 + akVector[1] * fDot1; + akVector[2].unitize(); +} + +//---------------------------------------------------------------------------- +void Vector3::generateOrthonormalBasis (Vector3& rkU, Vector3& rkV, + Vector3& rkW, bool bUnitLengthW) { + if ( !bUnitLengthW ) + rkW.unitize(); + + if ( G3D::abs(rkW.x) >= G3D::abs(rkW.y) + && G3D::abs(rkW.x) >= G3D::abs(rkW.z) ) { + rkU.x = -rkW.y; + rkU.y = + rkW.x; + rkU.z = 0.0; + } else { + rkU.x = 0.0; + rkU.y = + rkW.z; + rkU.z = -rkW.y; + } + + rkU.unitize(); + rkV = rkW.cross(rkU); +} + +//---------------------------------------------------------------------------- + +std::string Vector3::toString() const { + return G3D::format("(%g, %g, %g)", x, y, z); +} + + +//---------------------------------------------------------------------------- + +Matrix3 Vector3::cross() const { + return Matrix3( 0, -z, y, + z, 0, -x, + -y, x, 0); +} + + +void serialize(const Vector3::Axis& a, class BinaryOutput& bo) { + bo.writeUInt8((uint8)a); +} + +void deserialize(Vector3::Axis& a, class BinaryInput& bi) { + a = (Vector3::Axis)bi.readUInt8(); +} + +//---------------------------------------------------------------------------- +// 2-char swizzles + +Vector2 Vector3::xx() const { return Vector2 (x, x); } +Vector2 Vector3::yx() const { return Vector2 (y, x); } +Vector2 Vector3::zx() const { return Vector2 (z, x); } +Vector2 Vector3::xy() const { return Vector2 (x, y); } +Vector2 Vector3::yy() const { return Vector2 (y, y); } +Vector2 Vector3::zy() const { return Vector2 (z, y); } +Vector2 Vector3::xz() const { return Vector2 (x, z); } +Vector2 Vector3::yz() const { return Vector2 (y, z); } +Vector2 Vector3::zz() const { return Vector2 (z, z); } + +// 3-char swizzles + +Vector3 Vector3::xxx() const { return Vector3 (x, x, x); } +Vector3 Vector3::yxx() const { return Vector3 (y, x, x); } +Vector3 Vector3::zxx() const { return Vector3 (z, x, x); } +Vector3 Vector3::xyx() const { return Vector3 (x, y, x); } +Vector3 Vector3::yyx() const { return Vector3 (y, y, x); } +Vector3 Vector3::zyx() const { return Vector3 (z, y, x); } +Vector3 Vector3::xzx() const { return Vector3 (x, z, x); } +Vector3 Vector3::yzx() const { return Vector3 (y, z, x); } +Vector3 Vector3::zzx() const { return Vector3 (z, z, x); } +Vector3 Vector3::xxy() const { return Vector3 (x, x, y); } +Vector3 Vector3::yxy() const { return Vector3 (y, x, y); } +Vector3 Vector3::zxy() const { return Vector3 (z, x, y); } +Vector3 Vector3::xyy() const { return Vector3 (x, y, y); } +Vector3 Vector3::yyy() const { return Vector3 (y, y, y); } +Vector3 Vector3::zyy() const { return Vector3 (z, y, y); } +Vector3 Vector3::xzy() const { return Vector3 (x, z, y); } +Vector3 Vector3::yzy() const { return Vector3 (y, z, y); } +Vector3 Vector3::zzy() const { return Vector3 (z, z, y); } +Vector3 Vector3::xxz() const { return Vector3 (x, x, z); } +Vector3 Vector3::yxz() const { return Vector3 (y, x, z); } +Vector3 Vector3::zxz() const { return Vector3 (z, x, z); } +Vector3 Vector3::xyz() const { return Vector3 (x, y, z); } +Vector3 Vector3::yyz() const { return Vector3 (y, y, z); } +Vector3 Vector3::zyz() const { return Vector3 (z, y, z); } +Vector3 Vector3::xzz() const { return Vector3 (x, z, z); } +Vector3 Vector3::yzz() const { return Vector3 (y, z, z); } +Vector3 Vector3::zzz() const { return Vector3 (z, z, z); } + +// 4-char swizzles + +Vector4 Vector3::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Vector3::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Vector3::zxxx() const { return Vector4 (z, x, x, x); } +Vector4 Vector3::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Vector3::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Vector3::zyxx() const { return Vector4 (z, y, x, x); } +Vector4 Vector3::xzxx() const { return Vector4 (x, z, x, x); } +Vector4 Vector3::yzxx() const { return Vector4 (y, z, x, x); } +Vector4 Vector3::zzxx() const { return Vector4 (z, z, x, x); } +Vector4 Vector3::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Vector3::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Vector3::zxyx() const { return Vector4 (z, x, y, x); } +Vector4 Vector3::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Vector3::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Vector3::zyyx() const { return Vector4 (z, y, y, x); } +Vector4 Vector3::xzyx() const { return Vector4 (x, z, y, x); } +Vector4 Vector3::yzyx() const { return Vector4 (y, z, y, x); } +Vector4 Vector3::zzyx() const { return Vector4 (z, z, y, x); } +Vector4 Vector3::xxzx() const { return Vector4 (x, x, z, x); } +Vector4 Vector3::yxzx() const { return Vector4 (y, x, z, x); } +Vector4 Vector3::zxzx() const { return Vector4 (z, x, z, x); } +Vector4 Vector3::xyzx() const { return Vector4 (x, y, z, x); } +Vector4 Vector3::yyzx() const { return Vector4 (y, y, z, x); } +Vector4 Vector3::zyzx() const { return Vector4 (z, y, z, x); } +Vector4 Vector3::xzzx() const { return Vector4 (x, z, z, x); } +Vector4 Vector3::yzzx() const { return Vector4 (y, z, z, x); } +Vector4 Vector3::zzzx() const { return Vector4 (z, z, z, x); } +Vector4 Vector3::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Vector3::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Vector3::zxxy() const { return Vector4 (z, x, x, y); } +Vector4 Vector3::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Vector3::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Vector3::zyxy() const { return Vector4 (z, y, x, y); } +Vector4 Vector3::xzxy() const { return Vector4 (x, z, x, y); } +Vector4 Vector3::yzxy() const { return Vector4 (y, z, x, y); } +Vector4 Vector3::zzxy() const { return Vector4 (z, z, x, y); } +Vector4 Vector3::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Vector3::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Vector3::zxyy() const { return Vector4 (z, x, y, y); } +Vector4 Vector3::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Vector3::yyyy() const { return Vector4 (y, y, y, y); } +Vector4 Vector3::zyyy() const { return Vector4 (z, y, y, y); } +Vector4 Vector3::xzyy() const { return Vector4 (x, z, y, y); } +Vector4 Vector3::yzyy() const { return Vector4 (y, z, y, y); } +Vector4 Vector3::zzyy() const { return Vector4 (z, z, y, y); } +Vector4 Vector3::xxzy() const { return Vector4 (x, x, z, y); } +Vector4 Vector3::yxzy() const { return Vector4 (y, x, z, y); } +Vector4 Vector3::zxzy() const { return Vector4 (z, x, z, y); } +Vector4 Vector3::xyzy() const { return Vector4 (x, y, z, y); } +Vector4 Vector3::yyzy() const { return Vector4 (y, y, z, y); } +Vector4 Vector3::zyzy() const { return Vector4 (z, y, z, y); } +Vector4 Vector3::xzzy() const { return Vector4 (x, z, z, y); } +Vector4 Vector3::yzzy() const { return Vector4 (y, z, z, y); } +Vector4 Vector3::zzzy() const { return Vector4 (z, z, z, y); } +Vector4 Vector3::xxxz() const { return Vector4 (x, x, x, z); } +Vector4 Vector3::yxxz() const { return Vector4 (y, x, x, z); } +Vector4 Vector3::zxxz() const { return Vector4 (z, x, x, z); } +Vector4 Vector3::xyxz() const { return Vector4 (x, y, x, z); } +Vector4 Vector3::yyxz() const { return Vector4 (y, y, x, z); } +Vector4 Vector3::zyxz() const { return Vector4 (z, y, x, z); } +Vector4 Vector3::xzxz() const { return Vector4 (x, z, x, z); } +Vector4 Vector3::yzxz() const { return Vector4 (y, z, x, z); } +Vector4 Vector3::zzxz() const { return Vector4 (z, z, x, z); } +Vector4 Vector3::xxyz() const { return Vector4 (x, x, y, z); } +Vector4 Vector3::yxyz() const { return Vector4 (y, x, y, z); } +Vector4 Vector3::zxyz() const { return Vector4 (z, x, y, z); } +Vector4 Vector3::xyyz() const { return Vector4 (x, y, y, z); } +Vector4 Vector3::yyyz() const { return Vector4 (y, y, y, z); } +Vector4 Vector3::zyyz() const { return Vector4 (z, y, y, z); } +Vector4 Vector3::xzyz() const { return Vector4 (x, z, y, z); } +Vector4 Vector3::yzyz() const { return Vector4 (y, z, y, z); } +Vector4 Vector3::zzyz() const { return Vector4 (z, z, y, z); } +Vector4 Vector3::xxzz() const { return Vector4 (x, x, z, z); } +Vector4 Vector3::yxzz() const { return Vector4 (y, x, z, z); } +Vector4 Vector3::zxzz() const { return Vector4 (z, x, z, z); } +Vector4 Vector3::xyzz() const { return Vector4 (x, y, z, z); } +Vector4 Vector3::yyzz() const { return Vector4 (y, y, z, z); } +Vector4 Vector3::zyzz() const { return Vector4 (z, y, z, z); } +Vector4 Vector3::xzzz() const { return Vector4 (x, z, z, z); } +Vector4 Vector3::yzzz() const { return Vector4 (y, z, z, z); } +Vector4 Vector3::zzzz() const { return Vector4 (z, z, z, z); } + + + + + + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/Vector3int16.cpp b/externals/g3dlite/G3D.lib/source/Vector3int16.cpp new file mode 100644 index 00000000000..8d8bb00bbef --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector3int16.cpp @@ -0,0 +1,49 @@ +/** + @file Vector3int16.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2003-04-07 + @edited 2006-01-17 + */ + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3int16.h" +#include "G3D/Vector3.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/format.h" + +namespace G3D { + +Vector3int16::Vector3int16(const class Vector3& v) { + x = (int16)iFloor(v.x + 0.5); + y = (int16)iFloor(v.y + 0.5); + z = (int16)iFloor(v.z + 0.5); +} + + +Vector3int16::Vector3int16(class BinaryInput& bi) { + deserialize(bi); +} + + +void Vector3int16::serialize(class BinaryOutput& bo) const { + bo.writeInt16(x); + bo.writeInt16(y); + bo.writeInt16(z); +} + + +void Vector3int16::deserialize(class BinaryInput& bi) { + x = bi.readInt16(); + y = bi.readInt16(); + z = bi.readInt16(); +} + +std::string Vector3int16::toString() const { + return G3D::format("(%d, %d, %d)", x, y, z); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Vector3int32.cpp b/externals/g3dlite/G3D.lib/source/Vector3int32.cpp new file mode 100644 index 00000000000..1328ba1aba5 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector3int32.cpp @@ -0,0 +1,57 @@ +/** + @file Vector3int32.cpp + + @author Morgan McGuire, matrix@graphics3d.com + + @created 2008-07-01 + @edited 2008-07-01 + */ + +#include "G3D/platform.h" +#include "G3D/g3dmath.h" +#include "G3D/Vector3int32.h" +#include "G3D/Vector3int16.h" +#include "G3D/Vector3.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/format.h" + +namespace G3D { + +Vector3int32::Vector3int32(const class Vector3& v) { + x = (int32)iFloor(v.x + 0.5); + y = (int32)iFloor(v.y + 0.5); + z = (int32)iFloor(v.z + 0.5); +} + + +Vector3int32::Vector3int32(const class Vector3int16& v) { + x = v.x; + y = v.y; + z = v.z; +} + + +Vector3int32::Vector3int32(class BinaryInput& bi) { + deserialize(bi); +} + + +void Vector3int32::serialize(class BinaryOutput& bo) const { + bo.writeInt32(x); + bo.writeInt32(y); + bo.writeInt32(z); +} + + +void Vector3int32::deserialize(class BinaryInput& bi) { + x = bi.readInt32(); + y = bi.readInt32(); + z = bi.readInt32(); +} + +std::string Vector3int32::toString() const { + return G3D::format("(%d, %d, %d)", x, y, z); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/Vector4.cpp b/externals/g3dlite/G3D.lib/source/Vector4.cpp new file mode 100644 index 00000000000..f562f2d6877 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector4.cpp @@ -0,0 +1,475 @@ +/** + @file Vector4.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2001-07-09 + @edited 2007-02-29 + */ + +#include <stdlib.h> +#include <limits> +#include "G3D/Vector4.h" +#include "G3D/Color4.h" +#include "G3D/g3dmath.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Vector4int8.h" +#include "G3D/Matrix4.h" + +namespace G3D { + +Vector4::Vector4(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f), w(v.w / 127.0f) { +} + +size_t Vector4::hashCode() const { + unsigned int xhash = (*(int*)(void*)(&x)); + unsigned int yhash = (*(int*)(void*)(&y)); + unsigned int zhash = (*(int*)(void*)(&z)); + unsigned int whash = (*(int*)(void*)(&w)); + + return xhash + (yhash * 37) + (zhash * 101) + (whash * 241); +} + + +Vector4::Vector4(const class Color4& c) { + x = c.r; + y = c.g; + z = c.b; + w = c.a; +} + + +Vector4::Vector4(const Vector2& v1, const Vector2& v2) { + x = v1.x; + y = v1.y; + z = v2.x; + w = v2.y; +} + + +Vector4::Vector4(const Vector2& v1, float fz, float fw) { + x = v1.x; + y = v1.y; + z = fz; + w = fw; +} + +Vector4::Vector4(BinaryInput& b) { + deserialize(b); +} + + +void Vector4::deserialize(BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); + z = b.readFloat32(); + w = b.readFloat32(); +} + + +void Vector4::serialize(BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); + b.writeFloat32(z); + b.writeFloat32(w); +} + +//---------------------------------------------------------------------------- + +Vector4 Vector4::operator*(const Matrix4& M) const { + Vector4 result; + for (int i = 0; i < 4; ++i) { + result[i] = 0.0f; + for (int j = 0; j < 4; ++j) { + result[i] += (*this)[j] * M[j][i]; + } + } + return result; +} + + +Vector4 Vector4::operator/ (float fScalar) const { + Vector4 kQuot; + + if ( fScalar != 0.0 ) { + float fInvScalar = 1.0f / fScalar; + kQuot.x = fInvScalar * x; + kQuot.y = fInvScalar * y; + kQuot.z = fInvScalar * z; + kQuot.w = fInvScalar * w; + return kQuot; + } else { + return Vector4::inf(); + } +} + +//---------------------------------------------------------------------------- +Vector4& Vector4::operator/= (float fScalar) { + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + x *= fInvScalar; + y *= fInvScalar; + z *= fInvScalar; + w *= fInvScalar; + } else { + *this = Vector4::inf(); + } + + return *this; +} + + +//---------------------------------------------------------------------------- + +std::string Vector4::toString() const { + return G3D::format("(%g, %g, %g, %g)", x, y, z, w); +} +// 2-char swizzles + +Vector2 Vector4::xx() const { return Vector2 (x, x); } +Vector2 Vector4::yx() const { return Vector2 (y, x); } +Vector2 Vector4::zx() const { return Vector2 (z, x); } +Vector2 Vector4::wx() const { return Vector2 (w, x); } +Vector2 Vector4::xy() const { return Vector2 (x, y); } +Vector2 Vector4::yy() const { return Vector2 (y, y); } +Vector2 Vector4::zy() const { return Vector2 (z, y); } +Vector2 Vector4::wy() const { return Vector2 (w, y); } +Vector2 Vector4::xz() const { return Vector2 (x, z); } +Vector2 Vector4::yz() const { return Vector2 (y, z); } +Vector2 Vector4::zz() const { return Vector2 (z, z); } +Vector2 Vector4::wz() const { return Vector2 (w, z); } +Vector2 Vector4::xw() const { return Vector2 (x, w); } +Vector2 Vector4::yw() const { return Vector2 (y, w); } +Vector2 Vector4::zw() const { return Vector2 (z, w); } +Vector2 Vector4::ww() const { return Vector2 (w, w); } + +// 3-char swizzles + +Vector3 Vector4::xxx() const { return Vector3 (x, x, x); } +Vector3 Vector4::yxx() const { return Vector3 (y, x, x); } +Vector3 Vector4::zxx() const { return Vector3 (z, x, x); } +Vector3 Vector4::wxx() const { return Vector3 (w, x, x); } +Vector3 Vector4::xyx() const { return Vector3 (x, y, x); } +Vector3 Vector4::yyx() const { return Vector3 (y, y, x); } +Vector3 Vector4::zyx() const { return Vector3 (z, y, x); } +Vector3 Vector4::wyx() const { return Vector3 (w, y, x); } +Vector3 Vector4::xzx() const { return Vector3 (x, z, x); } +Vector3 Vector4::yzx() const { return Vector3 (y, z, x); } +Vector3 Vector4::zzx() const { return Vector3 (z, z, x); } +Vector3 Vector4::wzx() const { return Vector3 (w, z, x); } +Vector3 Vector4::xwx() const { return Vector3 (x, w, x); } +Vector3 Vector4::ywx() const { return Vector3 (y, w, x); } +Vector3 Vector4::zwx() const { return Vector3 (z, w, x); } +Vector3 Vector4::wwx() const { return Vector3 (w, w, x); } +Vector3 Vector4::xxy() const { return Vector3 (x, x, y); } +Vector3 Vector4::yxy() const { return Vector3 (y, x, y); } +Vector3 Vector4::zxy() const { return Vector3 (z, x, y); } +Vector3 Vector4::wxy() const { return Vector3 (w, x, y); } +Vector3 Vector4::xyy() const { return Vector3 (x, y, y); } +Vector3 Vector4::yyy() const { return Vector3 (y, y, y); } +Vector3 Vector4::zyy() const { return Vector3 (z, y, y); } +Vector3 Vector4::wyy() const { return Vector3 (w, y, y); } +Vector3 Vector4::xzy() const { return Vector3 (x, z, y); } +Vector3 Vector4::yzy() const { return Vector3 (y, z, y); } +Vector3 Vector4::zzy() const { return Vector3 (z, z, y); } +Vector3 Vector4::wzy() const { return Vector3 (w, z, y); } +Vector3 Vector4::xwy() const { return Vector3 (x, w, y); } +Vector3 Vector4::ywy() const { return Vector3 (y, w, y); } +Vector3 Vector4::zwy() const { return Vector3 (z, w, y); } +Vector3 Vector4::wwy() const { return Vector3 (w, w, y); } +Vector3 Vector4::xxz() const { return Vector3 (x, x, z); } +Vector3 Vector4::yxz() const { return Vector3 (y, x, z); } +Vector3 Vector4::zxz() const { return Vector3 (z, x, z); } +Vector3 Vector4::wxz() const { return Vector3 (w, x, z); } +Vector3 Vector4::xyz() const { return Vector3 (x, y, z); } +Vector3 Vector4::yyz() const { return Vector3 (y, y, z); } +Vector3 Vector4::zyz() const { return Vector3 (z, y, z); } +Vector3 Vector4::wyz() const { return Vector3 (w, y, z); } +Vector3 Vector4::xzz() const { return Vector3 (x, z, z); } +Vector3 Vector4::yzz() const { return Vector3 (y, z, z); } +Vector3 Vector4::zzz() const { return Vector3 (z, z, z); } +Vector3 Vector4::wzz() const { return Vector3 (w, z, z); } +Vector3 Vector4::xwz() const { return Vector3 (x, w, z); } +Vector3 Vector4::ywz() const { return Vector3 (y, w, z); } +Vector3 Vector4::zwz() const { return Vector3 (z, w, z); } +Vector3 Vector4::wwz() const { return Vector3 (w, w, z); } +Vector3 Vector4::xxw() const { return Vector3 (x, x, w); } +Vector3 Vector4::yxw() const { return Vector3 (y, x, w); } +Vector3 Vector4::zxw() const { return Vector3 (z, x, w); } +Vector3 Vector4::wxw() const { return Vector3 (w, x, w); } +Vector3 Vector4::xyw() const { return Vector3 (x, y, w); } +Vector3 Vector4::yyw() const { return Vector3 (y, y, w); } +Vector3 Vector4::zyw() const { return Vector3 (z, y, w); } +Vector3 Vector4::wyw() const { return Vector3 (w, y, w); } +Vector3 Vector4::xzw() const { return Vector3 (x, z, w); } +Vector3 Vector4::yzw() const { return Vector3 (y, z, w); } +Vector3 Vector4::zzw() const { return Vector3 (z, z, w); } +Vector3 Vector4::wzw() const { return Vector3 (w, z, w); } +Vector3 Vector4::xww() const { return Vector3 (x, w, w); } +Vector3 Vector4::yww() const { return Vector3 (y, w, w); } +Vector3 Vector4::zww() const { return Vector3 (z, w, w); } +Vector3 Vector4::www() const { return Vector3 (w, w, w); } + +// 4-char swizzles + +Vector4 Vector4::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Vector4::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Vector4::zxxx() const { return Vector4 (z, x, x, x); } +Vector4 Vector4::wxxx() const { return Vector4 (w, x, x, x); } +Vector4 Vector4::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Vector4::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Vector4::zyxx() const { return Vector4 (z, y, x, x); } +Vector4 Vector4::wyxx() const { return Vector4 (w, y, x, x); } +Vector4 Vector4::xzxx() const { return Vector4 (x, z, x, x); } +Vector4 Vector4::yzxx() const { return Vector4 (y, z, x, x); } +Vector4 Vector4::zzxx() const { return Vector4 (z, z, x, x); } +Vector4 Vector4::wzxx() const { return Vector4 (w, z, x, x); } +Vector4 Vector4::xwxx() const { return Vector4 (x, w, x, x); } +Vector4 Vector4::ywxx() const { return Vector4 (y, w, x, x); } +Vector4 Vector4::zwxx() const { return Vector4 (z, w, x, x); } +Vector4 Vector4::wwxx() const { return Vector4 (w, w, x, x); } +Vector4 Vector4::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Vector4::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Vector4::zxyx() const { return Vector4 (z, x, y, x); } +Vector4 Vector4::wxyx() const { return Vector4 (w, x, y, x); } +Vector4 Vector4::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Vector4::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Vector4::zyyx() const { return Vector4 (z, y, y, x); } +Vector4 Vector4::wyyx() const { return Vector4 (w, y, y, x); } +Vector4 Vector4::xzyx() const { return Vector4 (x, z, y, x); } +Vector4 Vector4::yzyx() const { return Vector4 (y, z, y, x); } +Vector4 Vector4::zzyx() const { return Vector4 (z, z, y, x); } +Vector4 Vector4::wzyx() const { return Vector4 (w, z, y, x); } +Vector4 Vector4::xwyx() const { return Vector4 (x, w, y, x); } +Vector4 Vector4::ywyx() const { return Vector4 (y, w, y, x); } +Vector4 Vector4::zwyx() const { return Vector4 (z, w, y, x); } +Vector4 Vector4::wwyx() const { return Vector4 (w, w, y, x); } +Vector4 Vector4::xxzx() const { return Vector4 (x, x, z, x); } +Vector4 Vector4::yxzx() const { return Vector4 (y, x, z, x); } +Vector4 Vector4::zxzx() const { return Vector4 (z, x, z, x); } +Vector4 Vector4::wxzx() const { return Vector4 (w, x, z, x); } +Vector4 Vector4::xyzx() const { return Vector4 (x, y, z, x); } +Vector4 Vector4::yyzx() const { return Vector4 (y, y, z, x); } +Vector4 Vector4::zyzx() const { return Vector4 (z, y, z, x); } +Vector4 Vector4::wyzx() const { return Vector4 (w, y, z, x); } +Vector4 Vector4::xzzx() const { return Vector4 (x, z, z, x); } +Vector4 Vector4::yzzx() const { return Vector4 (y, z, z, x); } +Vector4 Vector4::zzzx() const { return Vector4 (z, z, z, x); } +Vector4 Vector4::wzzx() const { return Vector4 (w, z, z, x); } +Vector4 Vector4::xwzx() const { return Vector4 (x, w, z, x); } +Vector4 Vector4::ywzx() const { return Vector4 (y, w, z, x); } +Vector4 Vector4::zwzx() const { return Vector4 (z, w, z, x); } +Vector4 Vector4::wwzx() const { return Vector4 (w, w, z, x); } +Vector4 Vector4::xxwx() const { return Vector4 (x, x, w, x); } +Vector4 Vector4::yxwx() const { return Vector4 (y, x, w, x); } +Vector4 Vector4::zxwx() const { return Vector4 (z, x, w, x); } +Vector4 Vector4::wxwx() const { return Vector4 (w, x, w, x); } +Vector4 Vector4::xywx() const { return Vector4 (x, y, w, x); } +Vector4 Vector4::yywx() const { return Vector4 (y, y, w, x); } +Vector4 Vector4::zywx() const { return Vector4 (z, y, w, x); } +Vector4 Vector4::wywx() const { return Vector4 (w, y, w, x); } +Vector4 Vector4::xzwx() const { return Vector4 (x, z, w, x); } +Vector4 Vector4::yzwx() const { return Vector4 (y, z, w, x); } +Vector4 Vector4::zzwx() const { return Vector4 (z, z, w, x); } +Vector4 Vector4::wzwx() const { return Vector4 (w, z, w, x); } +Vector4 Vector4::xwwx() const { return Vector4 (x, w, w, x); } +Vector4 Vector4::ywwx() const { return Vector4 (y, w, w, x); } +Vector4 Vector4::zwwx() const { return Vector4 (z, w, w, x); } +Vector4 Vector4::wwwx() const { return Vector4 (w, w, w, x); } +Vector4 Vector4::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Vector4::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Vector4::zxxy() const { return Vector4 (z, x, x, y); } +Vector4 Vector4::wxxy() const { return Vector4 (w, x, x, y); } +Vector4 Vector4::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Vector4::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Vector4::zyxy() const { return Vector4 (z, y, x, y); } +Vector4 Vector4::wyxy() const { return Vector4 (w, y, x, y); } +Vector4 Vector4::xzxy() const { return Vector4 (x, z, x, y); } +Vector4 Vector4::yzxy() const { return Vector4 (y, z, x, y); } +Vector4 Vector4::zzxy() const { return Vector4 (z, z, x, y); } +Vector4 Vector4::wzxy() const { return Vector4 (w, z, x, y); } +Vector4 Vector4::xwxy() const { return Vector4 (x, w, x, y); } +Vector4 Vector4::ywxy() const { return Vector4 (y, w, x, y); } +Vector4 Vector4::zwxy() const { return Vector4 (z, w, x, y); } +Vector4 Vector4::wwxy() const { return Vector4 (w, w, x, y); } +Vector4 Vector4::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Vector4::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Vector4::zxyy() const { return Vector4 (z, x, y, y); } +Vector4 Vector4::wxyy() const { return Vector4 (w, x, y, y); } +Vector4 Vector4::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Vector4::yyyy() const { return Vector4 (y, y, y, y); } +Vector4 Vector4::zyyy() const { return Vector4 (z, y, y, y); } +Vector4 Vector4::wyyy() const { return Vector4 (w, y, y, y); } +Vector4 Vector4::xzyy() const { return Vector4 (x, z, y, y); } +Vector4 Vector4::yzyy() const { return Vector4 (y, z, y, y); } +Vector4 Vector4::zzyy() const { return Vector4 (z, z, y, y); } +Vector4 Vector4::wzyy() const { return Vector4 (w, z, y, y); } +Vector4 Vector4::xwyy() const { return Vector4 (x, w, y, y); } +Vector4 Vector4::ywyy() const { return Vector4 (y, w, y, y); } +Vector4 Vector4::zwyy() const { return Vector4 (z, w, y, y); } +Vector4 Vector4::wwyy() const { return Vector4 (w, w, y, y); } +Vector4 Vector4::xxzy() const { return Vector4 (x, x, z, y); } +Vector4 Vector4::yxzy() const { return Vector4 (y, x, z, y); } +Vector4 Vector4::zxzy() const { return Vector4 (z, x, z, y); } +Vector4 Vector4::wxzy() const { return Vector4 (w, x, z, y); } +Vector4 Vector4::xyzy() const { return Vector4 (x, y, z, y); } +Vector4 Vector4::yyzy() const { return Vector4 (y, y, z, y); } +Vector4 Vector4::zyzy() const { return Vector4 (z, y, z, y); } +Vector4 Vector4::wyzy() const { return Vector4 (w, y, z, y); } +Vector4 Vector4::xzzy() const { return Vector4 (x, z, z, y); } +Vector4 Vector4::yzzy() const { return Vector4 (y, z, z, y); } +Vector4 Vector4::zzzy() const { return Vector4 (z, z, z, y); } +Vector4 Vector4::wzzy() const { return Vector4 (w, z, z, y); } +Vector4 Vector4::xwzy() const { return Vector4 (x, w, z, y); } +Vector4 Vector4::ywzy() const { return Vector4 (y, w, z, y); } +Vector4 Vector4::zwzy() const { return Vector4 (z, w, z, y); } +Vector4 Vector4::wwzy() const { return Vector4 (w, w, z, y); } +Vector4 Vector4::xxwy() const { return Vector4 (x, x, w, y); } +Vector4 Vector4::yxwy() const { return Vector4 (y, x, w, y); } +Vector4 Vector4::zxwy() const { return Vector4 (z, x, w, y); } +Vector4 Vector4::wxwy() const { return Vector4 (w, x, w, y); } +Vector4 Vector4::xywy() const { return Vector4 (x, y, w, y); } +Vector4 Vector4::yywy() const { return Vector4 (y, y, w, y); } +Vector4 Vector4::zywy() const { return Vector4 (z, y, w, y); } +Vector4 Vector4::wywy() const { return Vector4 (w, y, w, y); } +Vector4 Vector4::xzwy() const { return Vector4 (x, z, w, y); } +Vector4 Vector4::yzwy() const { return Vector4 (y, z, w, y); } +Vector4 Vector4::zzwy() const { return Vector4 (z, z, w, y); } +Vector4 Vector4::wzwy() const { return Vector4 (w, z, w, y); } +Vector4 Vector4::xwwy() const { return Vector4 (x, w, w, y); } +Vector4 Vector4::ywwy() const { return Vector4 (y, w, w, y); } +Vector4 Vector4::zwwy() const { return Vector4 (z, w, w, y); } +Vector4 Vector4::wwwy() const { return Vector4 (w, w, w, y); } +Vector4 Vector4::xxxz() const { return Vector4 (x, x, x, z); } +Vector4 Vector4::yxxz() const { return Vector4 (y, x, x, z); } +Vector4 Vector4::zxxz() const { return Vector4 (z, x, x, z); } +Vector4 Vector4::wxxz() const { return Vector4 (w, x, x, z); } +Vector4 Vector4::xyxz() const { return Vector4 (x, y, x, z); } +Vector4 Vector4::yyxz() const { return Vector4 (y, y, x, z); } +Vector4 Vector4::zyxz() const { return Vector4 (z, y, x, z); } +Vector4 Vector4::wyxz() const { return Vector4 (w, y, x, z); } +Vector4 Vector4::xzxz() const { return Vector4 (x, z, x, z); } +Vector4 Vector4::yzxz() const { return Vector4 (y, z, x, z); } +Vector4 Vector4::zzxz() const { return Vector4 (z, z, x, z); } +Vector4 Vector4::wzxz() const { return Vector4 (w, z, x, z); } +Vector4 Vector4::xwxz() const { return Vector4 (x, w, x, z); } +Vector4 Vector4::ywxz() const { return Vector4 (y, w, x, z); } +Vector4 Vector4::zwxz() const { return Vector4 (z, w, x, z); } +Vector4 Vector4::wwxz() const { return Vector4 (w, w, x, z); } +Vector4 Vector4::xxyz() const { return Vector4 (x, x, y, z); } +Vector4 Vector4::yxyz() const { return Vector4 (y, x, y, z); } +Vector4 Vector4::zxyz() const { return Vector4 (z, x, y, z); } +Vector4 Vector4::wxyz() const { return Vector4 (w, x, y, z); } +Vector4 Vector4::xyyz() const { return Vector4 (x, y, y, z); } +Vector4 Vector4::yyyz() const { return Vector4 (y, y, y, z); } +Vector4 Vector4::zyyz() const { return Vector4 (z, y, y, z); } +Vector4 Vector4::wyyz() const { return Vector4 (w, y, y, z); } +Vector4 Vector4::xzyz() const { return Vector4 (x, z, y, z); } +Vector4 Vector4::yzyz() const { return Vector4 (y, z, y, z); } +Vector4 Vector4::zzyz() const { return Vector4 (z, z, y, z); } +Vector4 Vector4::wzyz() const { return Vector4 (w, z, y, z); } +Vector4 Vector4::xwyz() const { return Vector4 (x, w, y, z); } +Vector4 Vector4::ywyz() const { return Vector4 (y, w, y, z); } +Vector4 Vector4::zwyz() const { return Vector4 (z, w, y, z); } +Vector4 Vector4::wwyz() const { return Vector4 (w, w, y, z); } +Vector4 Vector4::xxzz() const { return Vector4 (x, x, z, z); } +Vector4 Vector4::yxzz() const { return Vector4 (y, x, z, z); } +Vector4 Vector4::zxzz() const { return Vector4 (z, x, z, z); } +Vector4 Vector4::wxzz() const { return Vector4 (w, x, z, z); } +Vector4 Vector4::xyzz() const { return Vector4 (x, y, z, z); } +Vector4 Vector4::yyzz() const { return Vector4 (y, y, z, z); } +Vector4 Vector4::zyzz() const { return Vector4 (z, y, z, z); } +Vector4 Vector4::wyzz() const { return Vector4 (w, y, z, z); } +Vector4 Vector4::xzzz() const { return Vector4 (x, z, z, z); } +Vector4 Vector4::yzzz() const { return Vector4 (y, z, z, z); } +Vector4 Vector4::zzzz() const { return Vector4 (z, z, z, z); } +Vector4 Vector4::wzzz() const { return Vector4 (w, z, z, z); } +Vector4 Vector4::xwzz() const { return Vector4 (x, w, z, z); } +Vector4 Vector4::ywzz() const { return Vector4 (y, w, z, z); } +Vector4 Vector4::zwzz() const { return Vector4 (z, w, z, z); } +Vector4 Vector4::wwzz() const { return Vector4 (w, w, z, z); } +Vector4 Vector4::xxwz() const { return Vector4 (x, x, w, z); } +Vector4 Vector4::yxwz() const { return Vector4 (y, x, w, z); } +Vector4 Vector4::zxwz() const { return Vector4 (z, x, w, z); } +Vector4 Vector4::wxwz() const { return Vector4 (w, x, w, z); } +Vector4 Vector4::xywz() const { return Vector4 (x, y, w, z); } +Vector4 Vector4::yywz() const { return Vector4 (y, y, w, z); } +Vector4 Vector4::zywz() const { return Vector4 (z, y, w, z); } +Vector4 Vector4::wywz() const { return Vector4 (w, y, w, z); } +Vector4 Vector4::xzwz() const { return Vector4 (x, z, w, z); } +Vector4 Vector4::yzwz() const { return Vector4 (y, z, w, z); } +Vector4 Vector4::zzwz() const { return Vector4 (z, z, w, z); } +Vector4 Vector4::wzwz() const { return Vector4 (w, z, w, z); } +Vector4 Vector4::xwwz() const { return Vector4 (x, w, w, z); } +Vector4 Vector4::ywwz() const { return Vector4 (y, w, w, z); } +Vector4 Vector4::zwwz() const { return Vector4 (z, w, w, z); } +Vector4 Vector4::wwwz() const { return Vector4 (w, w, w, z); } +Vector4 Vector4::xxxw() const { return Vector4 (x, x, x, w); } +Vector4 Vector4::yxxw() const { return Vector4 (y, x, x, w); } +Vector4 Vector4::zxxw() const { return Vector4 (z, x, x, w); } +Vector4 Vector4::wxxw() const { return Vector4 (w, x, x, w); } +Vector4 Vector4::xyxw() const { return Vector4 (x, y, x, w); } +Vector4 Vector4::yyxw() const { return Vector4 (y, y, x, w); } +Vector4 Vector4::zyxw() const { return Vector4 (z, y, x, w); } +Vector4 Vector4::wyxw() const { return Vector4 (w, y, x, w); } +Vector4 Vector4::xzxw() const { return Vector4 (x, z, x, w); } +Vector4 Vector4::yzxw() const { return Vector4 (y, z, x, w); } +Vector4 Vector4::zzxw() const { return Vector4 (z, z, x, w); } +Vector4 Vector4::wzxw() const { return Vector4 (w, z, x, w); } +Vector4 Vector4::xwxw() const { return Vector4 (x, w, x, w); } +Vector4 Vector4::ywxw() const { return Vector4 (y, w, x, w); } +Vector4 Vector4::zwxw() const { return Vector4 (z, w, x, w); } +Vector4 Vector4::wwxw() const { return Vector4 (w, w, x, w); } +Vector4 Vector4::xxyw() const { return Vector4 (x, x, y, w); } +Vector4 Vector4::yxyw() const { return Vector4 (y, x, y, w); } +Vector4 Vector4::zxyw() const { return Vector4 (z, x, y, w); } +Vector4 Vector4::wxyw() const { return Vector4 (w, x, y, w); } +Vector4 Vector4::xyyw() const { return Vector4 (x, y, y, w); } +Vector4 Vector4::yyyw() const { return Vector4 (y, y, y, w); } +Vector4 Vector4::zyyw() const { return Vector4 (z, y, y, w); } +Vector4 Vector4::wyyw() const { return Vector4 (w, y, y, w); } +Vector4 Vector4::xzyw() const { return Vector4 (x, z, y, w); } +Vector4 Vector4::yzyw() const { return Vector4 (y, z, y, w); } +Vector4 Vector4::zzyw() const { return Vector4 (z, z, y, w); } +Vector4 Vector4::wzyw() const { return Vector4 (w, z, y, w); } +Vector4 Vector4::xwyw() const { return Vector4 (x, w, y, w); } +Vector4 Vector4::ywyw() const { return Vector4 (y, w, y, w); } +Vector4 Vector4::zwyw() const { return Vector4 (z, w, y, w); } +Vector4 Vector4::wwyw() const { return Vector4 (w, w, y, w); } +Vector4 Vector4::xxzw() const { return Vector4 (x, x, z, w); } +Vector4 Vector4::yxzw() const { return Vector4 (y, x, z, w); } +Vector4 Vector4::zxzw() const { return Vector4 (z, x, z, w); } +Vector4 Vector4::wxzw() const { return Vector4 (w, x, z, w); } +Vector4 Vector4::xyzw() const { return Vector4 (x, y, z, w); } +Vector4 Vector4::yyzw() const { return Vector4 (y, y, z, w); } +Vector4 Vector4::zyzw() const { return Vector4 (z, y, z, w); } +Vector4 Vector4::wyzw() const { return Vector4 (w, y, z, w); } +Vector4 Vector4::xzzw() const { return Vector4 (x, z, z, w); } +Vector4 Vector4::yzzw() const { return Vector4 (y, z, z, w); } +Vector4 Vector4::zzzw() const { return Vector4 (z, z, z, w); } +Vector4 Vector4::wzzw() const { return Vector4 (w, z, z, w); } +Vector4 Vector4::xwzw() const { return Vector4 (x, w, z, w); } +Vector4 Vector4::ywzw() const { return Vector4 (y, w, z, w); } +Vector4 Vector4::zwzw() const { return Vector4 (z, w, z, w); } +Vector4 Vector4::wwzw() const { return Vector4 (w, w, z, w); } +Vector4 Vector4::xxww() const { return Vector4 (x, x, w, w); } +Vector4 Vector4::yxww() const { return Vector4 (y, x, w, w); } +Vector4 Vector4::zxww() const { return Vector4 (z, x, w, w); } +Vector4 Vector4::wxww() const { return Vector4 (w, x, w, w); } +Vector4 Vector4::xyww() const { return Vector4 (x, y, w, w); } +Vector4 Vector4::yyww() const { return Vector4 (y, y, w, w); } +Vector4 Vector4::zyww() const { return Vector4 (z, y, w, w); } +Vector4 Vector4::wyww() const { return Vector4 (w, y, w, w); } +Vector4 Vector4::xzww() const { return Vector4 (x, z, w, w); } +Vector4 Vector4::yzww() const { return Vector4 (y, z, w, w); } +Vector4 Vector4::zzww() const { return Vector4 (z, z, w, w); } +Vector4 Vector4::wzww() const { return Vector4 (w, z, w, w); } +Vector4 Vector4::xwww() const { return Vector4 (x, w, w, w); } +Vector4 Vector4::ywww() const { return Vector4 (y, w, w, w); } +Vector4 Vector4::zwww() const { return Vector4 (z, w, w, w); } +Vector4 Vector4::wwww() const { return Vector4 (w, w, w, w); } + + +}; // namespace diff --git a/externals/g3dlite/G3D.lib/source/Vector4int8.cpp b/externals/g3dlite/G3D.lib/source/Vector4int8.cpp new file mode 100644 index 00000000000..29ca0d678fc --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/Vector4int8.cpp @@ -0,0 +1,58 @@ +/** + @file Vector4int8.cpp + + Homogeneous vector class. + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2007-02-09 + @edited 2007-02-09 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/platform.h" +#include "G3D/Vector4int8.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include <string> + +namespace G3D { + +Vector4int8::Vector4int8(const Vector4& source) { + x = iClamp(iRound(source.x), -128, 127); + y = iClamp(iRound(source.y), -128, 127); + z = iClamp(iRound(source.z), -128, 127); + w = iClamp(iRound(source.w), -128, 127); +} + +Vector4int8::Vector4int8(const Vector3& source, int8 w) : w(w) { + x = iClamp(iRound(source.x), -128, 127); + y = iClamp(iRound(source.y), -128, 127); + z = iClamp(iRound(source.z), -128, 127); +} + +Vector4int8::Vector4int8(class BinaryInput& b) { + deserialize(b); +} + +void Vector4int8::serialize(class BinaryOutput& b) const { + // Intentionally write individual bytes to avoid endian issues + b.writeInt8(x); + b.writeInt8(y); + b.writeInt8(z); + b.writeInt8(w); +} + +void Vector4int8::deserialize(class BinaryInput& b) { + x = b.readInt8(); + y = b.readInt8(); + z = b.readInt8(); + w = b.readInt8(); +} + +} // namespace G3D + diff --git a/externals/g3dlite/G3D.lib/source/WinMain.cpp b/externals/g3dlite/G3D.lib/source/WinMain.cpp new file mode 100644 index 00000000000..94653de4d43 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/WinMain.cpp @@ -0,0 +1,155 @@ +/* + Dervied from SDL_main.c, which was placed in the public domain by Sam Lantinga 4/13/98 + + The WinMain function -- calls your program's main() function +*/ + +#include "G3D/platform.h" + +#ifdef G3D_WIN32 + +#include <stdio.h> +#include <stdlib.h> +#include <cctype> + +#ifdef main +# ifndef _WIN32_WCE_EMULATION +# undef main +# endif /* _WIN32_WCE_EMULATION */ +#endif /* main */ + +#if defined(_WIN32_WCE) && _WIN32_WCE < 300 +/* seems to be undefined in Win CE although in online help */ +#define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t')) +#endif /* _WIN32_WCE < 300 */ + +// Turn off the G3D for loop scoping for C++ +#ifdef for +# undef for +#endif + +extern int main(int argc, const char** argv); + +/* Parse a command line buffer into arguments */ +static int ParseCommandLine(char *cmdline, char **argv) { + char *bufp; + int argc; + + argc = 0; + for (bufp = cmdline; *bufp;) { + /* Skip leading whitespace */ + while (isspace(*bufp)) { + ++bufp; + } + /* Skip over argument */ + if (*bufp == '"') { + ++bufp; + if (*bufp) { + if (argv) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while (*bufp && (*bufp != '"')) { + ++bufp; + } + } else { + if (*bufp) { + if (argv) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while (*bufp && !isspace(*bufp)) { + ++bufp; + } + } + if (*bufp) { + if (argv) { + *bufp = '\0'; + } + ++bufp; + } + } + if (argv) { + argv[argc] = NULL; + } + return (argc); +} + +/* Show an error message */ +static void ShowError(const char *title, const char *message) { +/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */ +#ifdef USE_MESSAGEBOX + MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK); +#else + fprintf(stderr, "%s: %s\n", title, message); +#endif +} + +/* Pop up an out of memory message, returns to Windows */ +static BOOL OutOfMemory(void) { + ShowError("Fatal Error", "Out of memory - aborting"); + return FALSE; +} + + +int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) { + char **argv; + int argc; + int status; + char *cmdline; +# ifdef _WIN32_WCE + wchar_t *bufp; + int nLen; +# else + char *bufp; + size_t nLen; +# endif + +#ifdef _WIN32_WCE +#error WinCE not supported + /* + nLen = wcslen(szCmdLine) + 128 + 1; + bufp = SDL_stack_alloc(wchar_t, nLen * 2); + wcscpy(bufp, TEXT("\"")); + GetModuleFileName(NULL, bufp + 1, 128 - 3); + wcscpy(bufp + wcslen(bufp), TEXT("\" ")); + wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp)); + nLen = wcslen(bufp) + 1; + cmdline = SDL_stack_alloc(char, nLen); + if (cmdline == NULL) { + return OutOfMemory(); + } + WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL); + */ +#else + /* Grab the command line */ + bufp = GetCommandLineA(); + nLen = strlen(bufp) + 1; + cmdline = (char*)malloc(sizeof(char) * nLen); + if (cmdline == NULL) { + return OutOfMemory(); + } + strncpy(cmdline, bufp, nLen); +#endif + + /* Parse it into argv and argc */ + argc = ParseCommandLine(cmdline, NULL); + argv = (char**)malloc(sizeof(char*) * (argc + 1)); + if (argv == NULL) { + return OutOfMemory(); + } + ParseCommandLine(cmdline, argv); + + /* Run the main program */ + status = main(argc, (const char**)argv); + free(argv); + free(cmdline); + + return status; +} + +#endif // if Win32 diff --git a/externals/g3dlite/G3D.lib/source/debugAssert.cpp b/externals/g3dlite/G3D.lib/source/debugAssert.cpp new file mode 100644 index 00000000000..4b24546a661 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/debugAssert.cpp @@ -0,0 +1,392 @@ +/** + @file debugAssert.cpp + + Windows implementation of assertion routines. + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-08-26 + @edited 2006-02-02 + */ + +#include "G3D/debugAssert.h" +#include "G3D/platform.h" +#ifdef G3D_WIN32 + #include <tchar.h> +#endif +#include "G3D/format.h" +#include "G3D/prompt.h" +#include <string> +#include "G3D/debugPrintf.h" +#include "G3D/Log.h" + +#include <cstdlib> + +#ifdef _MSC_VER + // disable: "C++ exception handler used" +# pragma warning (push) +# pragma warning (disable : 4530) +#endif + +using namespace std; + +namespace G3D { namespace _internal { + +ConsolePrintHook _consolePrintHook; +AssertionHook _debugHook = _handleDebugAssert_; +AssertionHook _failureHook = _handleErrorCheck_; + +#ifdef G3D_LINUX + Display* x11Display = NULL; + Window x11Window = 0; +#endif + + +#ifdef G3D_WIN32 +static void postToClipboard(const char *text) { + if (OpenClipboard(NULL)) { + HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, strlen(text) + 1); + if (hMem) { + char *pMem = (char*)GlobalLock(hMem); + strcpy(pMem, text); + GlobalUnlock(hMem); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + } + + CloseClipboard(); + GlobalFree(hMem); + } +} +#endif + +/** + outTitle should be set before the call + */ +static void createErrorMessage( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + std::string& outTitle, + std::string& outMessage) { + + std::string le = ""; + const char* newline = "\n"; + + #ifdef G3D_WIN32 + newline = "\r\n"; + + // The last error value. (Which is preserved across the call). + DWORD lastErr = GetLastError(); + + // The decoded message from FormatMessage + LPTSTR formatMsg = NULL; + + if (NULL == formatMsg) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + 0, + (LPTSTR)&formatMsg, + 0, + NULL); + } + + // Make sure the message got translated into something. + LPTSTR realLastErr; + if (NULL != formatMsg) { + realLastErr = formatMsg; + } else { + realLastErr = _T("Last error code does not exist."); + } + + if (lastErr != 0) { + le = G3D::format("Last Error (0x%08X): %s\r\n\r\n", lastErr, (LPCSTR)realLastErr); + } + + // Get rid of the allocated memory from FormatMessage. + if (NULL != formatMsg) { + LocalFree((LPVOID)formatMsg); + } + + char modulePath[MAX_PATH]; + GetModuleFileNameA(NULL, modulePath, MAX_PATH); + + const char* moduleName = strrchr(modulePath, '\\'); + outTitle = outTitle + string(" - ") + string(moduleName ? (moduleName + 1) : modulePath); + + #endif + + // Build the message. + outMessage = + G3D::format("%s%s%sExpression: %s%s%s:%d%s%s%s", + message.c_str(), newline, newline, expression, newline, + filename, lineNumber, newline, newline, le.c_str()); +} + + +bool _handleDebugAssert_( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + bool& ignoreAlways, + bool useGuiPrompt) { + + std::string dialogTitle = "Assertion Failure"; + std::string dialogText = ""; + createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText); + + #ifdef G3D_WIN32 + DWORD lastErr = GetLastError(); + postToClipboard(dialogText.c_str()); + debugPrintf("\n%s\n", dialogText.c_str()); + #endif + + const int cBreak = 0; + const int cIgnore = 1; + const int cIgnoreAlways = 2; + const int cAbort = 3; + + static const char* choices[] = {"Debug", "Ignore", "Ignore Always", "Exit"}; + + // Log the error + Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText); + + int result = G3D::prompt(dialogTitle.c_str(), dialogText.c_str(), (const char**)choices, 4, useGuiPrompt); + +# ifdef G3D_WIN32 + // Put the incoming last error back. + SetLastError(lastErr); +# endif + + switch (result) { + // -1 shouldn't actually occur because it means + // that we're in release mode. + case -1: + case cBreak: + return true; + break; + + case cIgnore: + return false; + break; + + case cIgnoreAlways: + ignoreAlways = true; + return false; + break; + + case cAbort: + exit(-1); + break; + } + + // Should never get here + return false; +} + + +bool _handleErrorCheck_( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + bool& ignoreAlways, + bool useGuiPrompt) { + + (void)ignoreAlways; + + std::string dialogTitle = "Critical Error"; + std::string dialogText = ""; + + createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText); + + // Log the error + Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText); + #ifdef G3D_WIN32 + DWORD lastErr = GetLastError(); + postToClipboard(dialogText.c_str()); + debugPrintf("\n%s\n", dialogText.c_str()); + #endif + + static const char* choices[] = {"Ok"}; + + std::string m = + std::string("An internal error has occured in your program and it will now close. Details about the error have been reported in \"") + + Log::getCommonLogFilename() + "\"."; + + int result = G3D::prompt("Error", m.c_str(), (const char**)choices, 1, useGuiPrompt); + (void)result; + + return true; +} + + +#ifdef G3D_WIN32 +static HCURSOR oldCursor; +static RECT oldCursorRect; +static POINT oldCursorPos; +static int oldShowCursorCount; +#endif + +void _releaseInputGrab_() { + #ifdef G3D_WIN32 + + GetCursorPos(&oldCursorPos); + + // Stop hiding the cursor if the application hid it. + oldShowCursorCount = ShowCursor(true) - 1; + + if (oldShowCursorCount < -1) { + for (int c = oldShowCursorCount; c < -1; ++c) { + ShowCursor(true); + } + } + + // Set the default cursor in case the application + // set the cursor to NULL. + oldCursor = GetCursor(); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + // Allow the cursor full access to the screen + GetClipCursor(&oldCursorRect); + ClipCursor(NULL); + + #elif defined(G3D_LINUX) + if (x11Display != NULL) { + XUngrabPointer(x11Display, CurrentTime); + XUngrabKeyboard(x11Display, CurrentTime); + if (x11Window != 0) { + //XUndefineCursor(x11Display, x11Window); + // TODO: Note that we leak this cursor; it should be + // freed in the restore code. + Cursor c = XCreateFontCursor(x11Display, 68); + XDefineCursor(x11Display, x11Window, c); + } + XSync(x11Display, false); + XAllowEvents(x11Display, AsyncPointer, CurrentTime); + XFlush(x11Display); + } + #elif defined(G3D_OSX) + // TODO: OS X + #endif +} + + +void _restoreInputGrab_() { + #ifdef G3D_WIN32 + + // Restore the old clipping region + ClipCursor(&oldCursorRect); + + SetCursorPos(oldCursorPos.x, oldCursorPos.y); + + // Restore the old cursor + SetCursor(oldCursor); + + // Restore old visibility count + if (oldShowCursorCount < 0) { + for (int c = 0; c > oldShowCursorCount; --c) { + ShowCursor(false); + } + } + + #elif defined(G3D_LINUX) + // TODO: Linux + #elif defined(G3D_OSX) + // TODO: OS X + #endif +} + + +}; // internal namespace + +void setAssertionHook(AssertionHook hook) { + G3D::_internal::_debugHook = hook; +} + +AssertionHook assertionHook() { + return G3D::_internal::_debugHook; +} + +void setFailureHook(AssertionHook hook) { + G3D::_internal::_failureHook = hook; +} + +AssertionHook failureHook() { + return G3D::_internal::_failureHook; +} + + +void setConsolePrintHook(ConsolePrintHook h) { + G3D::_internal::_consolePrintHook = h; +} + +ConsolePrintHook consolePrintHook() { + return G3D::_internal::_consolePrintHook; +} + + +std::string __cdecl debugPrint(const std::string& s) { +# ifdef G3D_WIN32 + const int MAX_STRING_LEN = 1024; + + // Windows can't handle really long strings sent to + // the console, so we break the string. + if (s.size() < MAX_STRING_LEN) { + OutputDebugStringA(s.c_str()); + } else { + for (unsigned int i = 0; i < s.size(); i += MAX_STRING_LEN) { + std::string sub = s.substr(i, MAX_STRING_LEN); + OutputDebugStringA(sub.c_str()); + } + } +# else + fprintf(stderr, "%s", s.c_str()); + fflush(stderr); +# endif + + return s; +} + +std::string __cdecl debugPrintf(const char* fmt ...) { + va_list argList; + va_start(argList, fmt); + std::string s = G3D::vformat(fmt, argList); + va_end(argList); + + return debugPrint(consolePrint(s)); +} + +std::string consolePrint(const std::string& s) { + FILE* L = Log::common()->getFile(); + fprintf(L, "%s", s.c_str()); + + if (consolePrintHook()) { + consolePrintHook()(s); + } + + fflush(L); + return s; +} + + +std::string __cdecl consolePrintf(const char* fmt ...) { + va_list argList; + va_start(argList, fmt); + std::string s = G3D::vformat(fmt, argList); + va_end(argList); + + return consolePrint(s); +} + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/G3D.lib/source/fileutils.cpp b/externals/g3dlite/G3D.lib/source/fileutils.cpp new file mode 100644 index 00000000000..3a5e8c715c8 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/fileutils.cpp @@ -0,0 +1,1092 @@ +/** + @file fileutils.cpp + + @author Morgan McGuire, graphics3d.com + + @author 2002-06-06 + @edited 2008-01-20 + */ + +#include "G3D/platform.h" +#include "G3D/fileutils.h" +#include "G3D/BinaryInput.h" +#include "G3D/g3dmath.h" +#include <sys/stat.h> +#include <sys/types.h> +#include <zip/zip.h> +#include <zip/unzip.h> +#include "G3D/stringutils.h" +#include "G3D/Set.h" + +#include <cstring> + +#ifdef G3D_WIN32 + // Needed for _getcwd + #include <direct.h> + #include <io.h> +#else + #include <dirent.h> + #include <fnmatch.h> + #include <unistd.h> + #define _getcwd getcwd +#endif +#include <stdio.h> +#include "G3D/BinaryOutput.h" + +#ifdef G3D_WIN32 + //for _mkdir and _stat +# include <direct.h> +#else +# define _stat stat +#endif + + +namespace G3D { + +namespace _internal { + Set<std::string> currentFilesUsed; +} + +std::string pathConcat(const std::string& dirname, const std::string& file) { + // Ensure that the directory ends in a slash + if ((dirname.size() != 0) && + (dirname[dirname.size() - 1] != '/') && + (dirname[dirname.size() - 1] != '\\') && + (dirname[dirname.size() - 1] != ':')) { + return dirname + '/' + file; + } else { + return dirname + file; + } +} + +std::string resolveFilename(const std::string& filename) { + if (filename.size() >= 1) { + if ((filename[0] == '/') || (filename[0] == '\\')) { + // Already resolved + return filename; + } else { + + #ifdef G3D_WIN32 + if ((filename.size() >= 2) && (filename[1] == ':')) { + // There is a drive spec on the front. + if ((filename.size() >= 3) && ((filename[2] == '\\') || + (filename[2] == '/'))) { + // Already fully qualified + return filename; + } else { + // The drive spec is relative to the + // working directory on that drive. + debugAssertM(false, "Files of the form d:path are" + " not supported (use a fully qualified" + " name)."); + return filename; + } + } + #endif + } + } + + char buffer[1024]; + + // Prepend the working directory. + _getcwd(buffer, 1024); + + return format("%s/%s", buffer, filename.c_str()); +} + +bool zipfileExists(const std::string& filename) { + std::string outZipfile; + std::string outInternalFile; + return zipfileExists(filename, outZipfile, outInternalFile); +} + +std::string readWholeFile( + const std::string& filename) { + + _internal::currentFilesUsed.insert(filename); + + std::string s; + + debugAssert(filename != ""); + if (fileExists(filename, false)) { + + int64 length = fileLength(filename); + + char* buffer = (char*)System::alignedMalloc(length + 1, 16); + debugAssert(buffer); + FILE* f = fopen(filename.c_str(), "rb"); + debugAssert(f); + int ret = fread(buffer, 1, length, f); + debugAssert(ret == length);(void)ret; + fclose(f); + + buffer[length] = '\0'; + s = std::string(buffer); + + System::alignedFree(buffer); + + } else if (zipfileExists(filename)) { + + void* zipBuffer; + size_t length; + zipRead(filename, zipBuffer, length); + + char* buffer = (char*)System::alignedMalloc(length + 1, 16); + System::memcpy(buffer,zipBuffer, length + 1); + zipClose(zipBuffer); + + buffer[length] = '\0'; + s = std::string(buffer); + System::alignedFree(buffer); + } else { + debugAssertM(false, filename + " not found"); + } + + return s; +} + + +void zipRead(const std::string& file, + void*& data, + size_t& length) { + std::string zip, desiredFile; + + if (zipfileExists(file, zip, desiredFile)) { + unzFile f = unzOpen(zip.c_str()); + { + unzLocateFile(f, desiredFile.c_str(), 2); + unz_file_info info; + unzGetCurrentFileInfo(f, &info, NULL, 0, NULL, 0, NULL, 0); + length = info.uncompressed_size; + // sets machines up to use MMX, if they want + data = System::alignedMalloc(length, 16); + unzOpenCurrentFile(f); + { + int test = unzReadCurrentFile(f, data, length); + debugAssertM((size_t)test == length, + desiredFile + " was corrupt because it unzipped to the wrong size."); + (void)test; + } + unzCloseCurrentFile(f); + } + unzClose(f); + } else { + data = NULL; + } +} + + +void zipClose(void* data) { + System::alignedFree(data); +} + + +int64 fileLength(const std::string& filename) { + struct _stat st; + int result = _stat(filename.c_str(), &st); + + if (result == -1) { + std::string zip, contents; + if(zipfileExists(filename, zip, contents)){ + int64 requiredMem; + + unzFile f = unzOpen(zip.c_str()); + { + unzLocateFile(f, contents.c_str(), 2); + unz_file_info info; + unzGetCurrentFileInfo(f, &info, NULL, 0, NULL, 0, NULL, 0); + requiredMem = info.uncompressed_size; + } + unzClose(f); + return requiredMem; + } else { + return -1; + } + } + + return st.st_size; +} + +/** Used by robustTmpfile. Returns nonzero if fread, fwrite, and fseek all +succeed on the file. + @author Morgan McGuire, morgan@graphics3d.com */ +static int isFileGood(FILE* f) { + + int x, n, result; + + /* Must be a valid file handle */ + if (f == NULL) { + return 0; + } + + /* Try to write */ + x = 1234; + n = fwrite(&x, sizeof(int), 1, f); + + if (n != 1) { + return 0; + } + + /* Seek back to the beginning */ + result = fseek(f, 0, SEEK_SET); + if (result != 0) { + return 0; + } + + /* Read */ + n = fread(&x, sizeof(int), 1, f); + if (n != 1) { + return 0; + } + + /* Seek back to the beginning again */ + fseek(f, 0, SEEK_SET); + + return 1; +} + +FILE* createTempFile() { + FILE* t = NULL; + +//# ifdef G3D_WIN32 + t = tmpfile(); +//# else +// // On Unix, tmpfile generates a warning for any code that links against it. +// const char* tempfilename = "/tmp/g3dtemp.XXXXXXXX"; +// mktemp(tempfilename); +// t = fopen(tempfilename, "w"); +//# endif + +# ifdef _WIN32 + char* n = NULL; +# endif + char name[256]; + + if (isFileGood(t)) { + return t; + } + +# ifdef G3D_WIN32 + /* tmpfile failed; try the tmpnam routine */ + t = fopen(tmpnam(NULL), "w+"); + if (isFileGood(t)) { + return t; + } + + n = _tempnam("c:/tmp/", "t"); + /* Try to create something in C:\tmp */ + t = fopen(n, "w+"); + if (isFileGood(t)) { + return t; + } + + /* Try c:\temp */ + n = _tempnam("c:/temp/", "t"); + t = fopen(n, "w+"); + if (isFileGood(t)) { + return t; + } + + /* try the current directory */ + n = _tempnam("./", "t"); + t = fopen(n, "w+"); + if (isFileGood(t)) { + return t; + } + + sprintf(name, "%s/tmp%d", "c:/temp", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } + + /* Try some hardcoded paths */ + sprintf(name, "%s/tmp%d", "c:/tmp", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } +# else + sprintf(name, "%s/tmp%d", "/tmp", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } +#endif + + sprintf(name, "tmp%d", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } + + fprintf(stderr, "Unable to create a temporary file; robustTmpfile returning NULL\n"); + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +void writeWholeFile( + const std::string& filename, + const std::string& str, + bool flush) { + + // Make sure the directory exists. + std::string root, base, ext, path; + Array<std::string> pathArray; + parseFilename(filename, root, pathArray, base, ext); + + path = root + stringJoin(pathArray, '/'); + if (! fileExists(path, false)) { + createDirectory(path); + } + + FILE* file = fopen(filename.c_str(), "wb"); + + debugAssert(file); + + fwrite(str.c_str(), str.size(), 1, file); + + if (flush) { + fflush(file); + } + fclose(file); +} + +/////////////////////////////////////////////////////////////////////////////// + +/** + Creates the directory (which may optionally end in a /) + and any parents needed to reach it. + */ +void createDirectory( + const std::string& dir) { + + if (dir == "") { + return; + } + + std::string d; + + // Add a trailing / if there isn't one. + switch (dir[dir.size() - 1]) { + case '/': + case '\\': + d = dir; + break; + + default: + d = dir + "/"; + } + + // If it already exists, do nothing + if (fileExists(d.substr(0, d.size() - 1)), false) { + return; + } + + // Parse the name apart + std::string root, base, ext; + Array<std::string> path; + + std::string lead; + parseFilename(d, root, path, base, ext); + debugAssert(base == ""); + debugAssert(ext == ""); + + // Begin with an extra period so "c:\" becomes "c:\.\" after + // appending a path and "c:" becomes "c:.\", not root: "c:\" + std::string p = root + "."; + + // Create any intermediate that doesn't exist + for (int i = 0; i < path.size(); ++i) { + p += "/" + path[i]; + if (! fileExists(p, false)) { + // Windows only requires one argument to mkdir, + // where as unix also requires the permissions. +# ifndef G3D_WIN32 + mkdir(p.c_str(), 0777); +# else + _mkdir(p.c_str()); +# endif + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool fileExists( + const std::string& filename, + const bool lookInZipfiles) { + + if (filename == "") { + return true; + } + + + // Useful for debugging + //char curdir[1024]; _getcwd(curdir, 1024); + + struct _stat st; + int ret = _stat(filename.c_str(), &st); + + // _stat returns zero on success + bool exists = (ret == 0); + + if (exists) { + // Exists + return true; + } else if (lookInZipfiles) { + // Does not exist standalone, but might exist in a zipfile + + // These output arguments will be ignored + std::string zipDir, internalPath; + return zipfileExists(filename, zipDir, internalPath); + } else { + // Does not exist + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +/* Helper methods for zipfileExists()*/ +// Given a string (the drive) and an array (the path), computes the directory +static void _zip_resolveDirectory(std::string& completeDir, const std::string& drive, const Array<std::string>& path, const int length){ + completeDir = drive; + int tempLength; + // if the given length is longer than the array, we correct it + if(length > path.length()){ + tempLength = path.length(); + } else{ + tempLength = length; + } + + for(int t = 0; t < tempLength; ++t){ + if(t > 0){ + completeDir += "/"; + } + completeDir += path[t]; + } +} + + +// assumes that zipDir references a .zip file +static bool _zip_zipContains(const std::string& zipDir, const std::string& desiredFile){ + unzFile f = unzOpen(zipDir.c_str()); + //the last parameter, an int, determines case sensitivity: + //1 is sensitive, 2 is not, 0 is default + int test = unzLocateFile(f, desiredFile.c_str(), 2); + unzClose(f); + if(test == UNZ_END_OF_LIST_OF_FILE){ + return false; + } + return true; +} + + +// If no zipfile exists, outZipfile and outInternalFile are unchanged +bool zipfileExists(const std::string& filename, std::string& outZipfile, + std::string& outInternalFile){ + if (fileExists(filename, false)) { + // Not inside a zipfile if the file itself exists + return false; + } else { + Array<std::string> path; + std::string drive, base, ext, zipfile, infile; + parseFilename(filename, drive, path, base, ext); + + // Put the filename back together + if ((base != "") && (ext != "")) { + infile = base + "." + ext; + } else { + infile = base + ext; + } + + // Remove "." from path + for (int i = 0; i < path.length(); ++i) { + if (path[i] == ".") { + path.remove(i); + --i; + } + } + + // Remove ".." from path + for (int i = 1; i < path.length(); ++i) { + if ((path[i] == "..") && (i > 0) && (path[i - 1] != "..")) { + // Remove both i and i - 1 + path.remove(i - 1, 2); + i -= 2; + } + } + + // Walk the path backwards, accumulating pieces onto the infile until + // we find a zipfile that contains it + for (int t = 0; t < path.length(); ++t){ + _zip_resolveDirectory(zipfile, drive, path, path.length() - t); + if (t > 0) { + infile = path[path.length() - t] + "/" + infile; + } + + if (fileExists(zipfile, false)) { + // test if it actually is a zipfile + // if not, return false, a bad + // directory structure has been given, + // not a .zip + if (isZipfile(zipfile)){ + + if (_zip_zipContains(zipfile, infile)){ + outZipfile = zipfile; + outInternalFile = infile; + return true; + } else { + return false; + } + } else { + // the directory structure was valid but did not point to a .zip + return false; + } + } + + } + } + + // not a valid directory structure ever, + // obviously no .zip was found within the path + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string generateFilenameBase(const std::string& prefix) { + Array<std::string> exist; + + // Note "template" is a reserved word in C++ + std::string templat = prefix + System::currentDateString(); + getFiles(templat + "*", exist); + + // Remove extensions + for (int i = 0; i < exist.size(); ++i) { + exist[i] = filenameBase(exist[i]); + } + + int num = 0; + std::string result; + templat += "_%03d"; + do { + result = format(templat.c_str(), num); + ++num; + } while (exist.contains(result)); + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +void copyFile( + const std::string& source, + const std::string& dest) { + + #ifdef G3D_WIN32 + CopyFileA(source.c_str(), dest.c_str(), FALSE); + #else + // TODO: don't use BinaryInput and BinaryOutput + // Read it all in, then dump it out + BinaryInput in(source, G3D_LITTLE_ENDIAN); + BinaryOutput out(dest, G3D_LITTLE_ENDIAN); + out.writeBytes(in.getCArray(), in.size()); + out.commit(false); + #endif +} + +////////////////////////////////////////////////////////////////////////////// + +void parseFilename( + const std::string& filename, + std::string& root, + Array<std::string>& path, + std::string& base, + std::string& ext) { + + std::string f = filename; + + root = ""; + path.clear(); + base = ""; + ext = ""; + + if (f == "") { + // Empty filename + return; + } + + // See if there is a root/drive spec. + if ((f.size() >= 2) && (f[1] == ':')) { + + if ((f.size() > 2) && isSlash(f[2])) { + + // e.g. c:\foo + root = f.substr(0, 3); + f = f.substr(3, f.size() - 3); + + } else { + + // e.g. c:foo + root = f.substr(2); + f = f.substr(2, f.size() - 2); + + } + + } else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) { + + // e.g. //foo + root = f.substr(0, 2); + f = f.substr(2, f.size() - 2); + + } else if (isSlash(f[0])) { + + root = f.substr(0, 1); + f = f.substr(1, f.size() - 1); + + } + + // Pull the extension off + { + // Find the period + size_t i = f.rfind('.'); + + if (i != std::string::npos) { + // Make sure it is after the last slash! + size_t j = iMax(f.rfind('/'), f.rfind('\\')); + if ((j == std::string::npos) || (i > j)) { + ext = f.substr(i + 1, f.size() - i - 1); + f = f.substr(0, i); + } + } + } + + // Pull the basename off + { + // Find the last slash + size_t i = iMax(f.rfind('/'), f.rfind('\\')); + + if (i == std::string::npos) { + + // There is no slash; the basename is the whole thing + base = f; + f = ""; + + } else if ((i != std::string::npos) && (i < f.size() - 1)) { + + base = f.substr(i + 1, f.size() - i - 1); + f = f.substr(0, i); + + } + } + + // Parse what remains into path. + size_t prev, cur = 0; + + while (cur < f.size()) { + prev = cur; + + // Allow either slash + size_t i = f.find('/', prev + 1); + size_t j = f.find('\\', prev + 1); + if (i == std::string::npos) { + i = f.size(); + } + + if (j == std::string::npos) { + j = f.size(); + } + + cur = iMin(i, j); + + if (cur == std::string::npos) { + cur = f.size(); + } + + path.append(f.substr(prev, cur - prev)); + ++cur; + } +} + + +/** + Helper for getFileList and getDirectoryList. + + @param wantFiles If false, returns the directories, otherwise + returns the files. + @param includePath If true, the names include paths + */ +static void getFileOrDirListNormal +( + const std::string& filespec, + Array<std::string>& files, + bool wantFiles, + bool includePath) { + + bool test = wantFiles ? true : false; + + std::string path = ""; + + // Find the place where the path ends and the file-spec begins + size_t i = filespec.rfind('/'); + size_t j = filespec.rfind('\\'); + + // Drive letters on Windows can separate a path + size_t k = filespec.rfind(':'); + + if (((j != std::string::npos) && (j > i)) || + (i == std::string::npos)) { + i = j; + } + + if (((k != std::string::npos) && (k > i)) || + (i == std::string::npos)) { + i = k; + } + + // If there is a path, pull it off + if (i != std::string::npos) { + path = filespec.substr(0, i + 1); + } + + std::string prefix = path; + + if (path.size() > 0) { + // Strip the trailing character + path = path.substr(0, path.size() - 1); + } + +# ifdef G3D_WIN32 + { + struct _finddata_t fileinfo; + + long handle = _findfirst(filespec.c_str(), &fileinfo); + int result = handle; + + while (result != -1) { + if ((((fileinfo.attrib & _A_SUBDIR) == 0) == test) && + strcmp(fileinfo.name, ".") && + strcmp(fileinfo.name, "..")) { + + if (includePath) { + files.append(prefix + fileinfo.name); + } else { + files.append(fileinfo.name); + } + } + + result = _findnext(handle, &fileinfo); + } + } +# else + { + if (path == "") { + // Empty paths don't work on Unix + path = "."; + } + + // Unix implementation + DIR* dir = opendir(path.c_str()); + + if (dir != NULL) { + struct dirent* entry = readdir(dir); + + while (entry != NULL) { + + // Exclude '.' and '..' + if ((strcmp(entry->d_name, ".") != 0) && + (strcmp(entry->d_name, "..") != 0)) { + + // Form a name with a path + std::string filename = prefix + entry->d_name; + // See if this is a file or a directory + struct _stat st; + bool exists = _stat(filename.c_str(), &st) != -1; + + if (exists && + + // Make sure it has the correct type + (((st.st_mode & S_IFDIR) == 0) == test) && + + // Make sure it matches the wildcard + (fnmatch(filespec.c_str(), + filename.c_str(), + FNM_PATHNAME) == 0)) { + + if (includePath) { + files.append(filename); + } else { + files.append(entry->d_name); + } + } + } + + entry = readdir(dir); + } + closedir(dir); + } + } +# endif +} + +/** + + c:/temp/foo.zip/plainsky\\* +" path " + "prefix " + + @param path The zipfile name (no trailing slash) + @param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty. + @param file Name inside the zipfile that we are testing to see if it matches prefix + "*" + */ +static void _zip_addEntry(const std::string& path, + const std::string& prefix, + const std::string& file, + Set<std::string>& files, + bool wantFiles, + bool includePath) { + + // Make certain we are within the desired parent folder (prefix) + if (beginsWith(file, prefix)) { + // validityTest was prefix/file + + // Extract everything to the right of the prefix + std::string s = file.substr(prefix.length()); + + if (s == "") { + // This was the name of the prefix + return; + } + + // See if there are any slashes + size_t slashPos = s.find('/'); + + bool add = false; + + if (slashPos == std::string::npos) { + // No slashes, so s must be a file + add = wantFiles; + } else if (! wantFiles) { + // Not all zipfiles list directories as explicit entries. + // Because of this, if we're looking for directories and see + // any path longer than prefix, we must add the subdirectory. + // The Set will fix duplicates for us. + s = s.substr(0, slashPos); + add = true; + } + + if (add) { + if (includePath) { + files.insert(path + "/" + prefix + s); + } else { + files.insert(s); + } + } + } +} + + +static void getFileOrDirListZip(const std::string& path, + const std::string& prefix, + Array<std::string>& files, + bool wantFiles, + bool includePath){ + unzFile f = unzOpen(path.c_str()); + + enum {MAX_STRING_LENGTH=1024}; + char filename[MAX_STRING_LENGTH]; + Set<std::string> fileSet; + + do { + + // prefix is valid, either "" or a subfolder + unzGetCurrentFileInfo(f, NULL, filename, MAX_STRING_LENGTH, NULL, 0, NULL, 0); + _zip_addEntry(path, prefix, filename, fileSet, wantFiles, includePath); + + } while (unzGoToNextFile(f) != UNZ_END_OF_LIST_OF_FILE); + + unzClose(f); + + fileSet.getMembers(files); +} + + +static void determineFileOrDirList( + const std::string& filespec, + Array<std::string>& files, + bool wantFiles, + bool includePath) { + + // if it is a .zip, prefix will specify the folder within + // whose contents we want to see + std::string prefix = ""; + std::string path = filenamePath(filespec); + + if ((path.size() > 0) && isSlash(path[path.size() - 1])) { + // Strip the trailing slash + path = path.substr(0, path.length() -1); + } + + if (fileExists(path, false)) { + if (isZipfile(path)) { + // .zip should only work if * is specified as the Base + Ext + // Here, we have been asked for the root's contents + debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard"); + getFileOrDirListZip(path, prefix, files, wantFiles, includePath); + } else { + // It is a normal directory + getFileOrDirListNormal(filespec, files, wantFiles, includePath); + } + } else if (zipfileExists(filenamePath(filespec), path, prefix)) { + // .zip should only work if * is specified as the Base + Ext + // Here, we have been asked for the contents of a folder within the .zip + debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard"); + getFileOrDirListZip(path, prefix, files, wantFiles, includePath); + } +} + + +void getFiles( + const std::string& filespec, + Array<std::string>& files, + bool includePath) { + + determineFileOrDirList(filespec, files, true, includePath); +} + + +void getDirs( + const std::string& filespec, + Array<std::string>& files, + bool includePath) { + + determineFileOrDirList(filespec, files, false, includePath); +} + + +std::string filenameBaseExt(const std::string& filename) { + int i = filename.rfind("/"); + int j = filename.rfind("\\"); + + if ((j > i) && (j >= 0)) { + i = j; + } + +# ifdef G3D_WIN32 + j = filename.rfind(":"); + if ((i == -1) && (j >= 0)) { + i = j; + } +# endif + + if (i == -1) { + return filename; + } else { + return filename.substr(i + 1, filename.length() - i); + } +} + + +std::string filenameBase(const std::string& s) { + std::string drive; + std::string base; + std::string ext; + Array<std::string> path; + + parseFilename(s, drive, path, base, ext); + return base; +} + + +std::string filenameExt(const std::string& filename) { + int i = filename.rfind("."); + if (i >= 0) { + return filename.substr(i + 1, filename.length() - i); + } else { + return ""; + } +} + + +std::string filenamePath(const std::string& filename) { + int i = filename.rfind("/"); + int j = filename.rfind("\\"); + + if ((j > i) && (j >= 0)) { + i = j; + } + +# ifdef G3D_WIN32 + j = filename.rfind(":"); + if ((i == -1) && (j >= 0)) { + i = j; + } +# endif + + if (i == -1) { + return ""; + } else { + return filename.substr(0, i+1); + } +} + + +bool isZipfile(const std::string& filename) { + + FILE* f = fopen(filename.c_str(), "r"); + if (f == NULL) { + return false; + } + uint8 header[4]; + fread(header, 4, 1, f); + + const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04}; + for (int i = 0; i < 4; ++i) { + if (header[i] != zipHeader[i]) { + fclose(f); + return false; + } + } + + fclose(f); + return true; +} + + +bool isDirectory(const std::string& filename) { + struct _stat st; + bool exists = _stat(filename.c_str(), &st) != -1; + return exists && ((st.st_mode & S_IFDIR) != 0); +} + + +bool filenameContainsWildcards(const std::string& filename) { + return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos); +} + + +bool fileIsNewer(const std::string& src, const std::string& dst) { + struct _stat sts; + bool sexists = _stat(src.c_str(), &sts) != -1; + + struct _stat dts; + bool dexists = _stat(dst.c_str(), &dts) != -1; + + return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime)); +} + + +Array<std::string> filesUsed() { + Array<std::string> f; + _internal::currentFilesUsed.getMembers(f); + return f; +} + +} + +#ifndef G3D_WIN32 + #undef _stat +#endif diff --git a/externals/g3dlite/G3D.lib/source/filter.cpp b/externals/g3dlite/G3D.lib/source/filter.cpp new file mode 100644 index 00000000000..f6c0467820f --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/filter.cpp @@ -0,0 +1,32 @@ +/** + @file filter.cpp + + @author Morgan McGuire, matrix@graphics3d.com + @created 2007-03-01 + @edited 2007-03-01 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + */ +#include "G3D/filter.h" + +namespace G3D { + +void gaussian1D(Array<float>& coeff, int N, float std) { + coeff.resize(N); + float sum = 0.0f; + for (int i = 0; i < N; ++i) { + float x = i - (N - 1) / 2.0f; + float p = -square(x / std) / 2.0f; + float y = exp(p); + coeff[i] = y; + sum += y; + } + + for (int i = 0; i < N; ++i) { + coeff[i] /= sum; + } +} + + +} // namespace diff --git a/externals/g3dlite/G3D.lib/source/format.cpp b/externals/g3dlite/G3D.lib/source/format.cpp new file mode 100644 index 00000000000..d9d1b516393 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/format.cpp @@ -0,0 +1,164 @@ +/** + @file format.cpp + + @author Morgan McGuire, graphics3d.com + + @created 2000-09-09 + @edited 2006-08-14 +*/ + +#include "G3D/format.h" +#include "G3D/platform.h" +#include "G3D/System.h" + +#ifdef _MSC_VER + // disable: "C++ exception handler used" +# pragma warning (push) +# pragma warning (disable : 4530) +#endif // _MSC_VER + +// If your platform does not have vsnprintf, you can find a +// implementation at http://www.ijs.si/software/snprintf/ + +namespace G3D { + +std::string __cdecl format(const char* fmt,...) { + va_list argList; + va_start(argList,fmt); + std::string result = vformat(fmt, argList); + va_end(argList); + + return result; +} + +#if defined(_MSC_VER) && (_MSC_VER >= 1300) +// Both MSVC seems to use the non-standard vsnprintf +// so we are using vscprintf to determine buffer size, however +// only MSVC7 and up headers include vscprintf for some reason. +std::string vformat(const char *fmt, va_list argPtr) { + // We draw the line at a 1MB string. + const int maxSize = 1000000; + + // If the string is less than 161 characters, + // allocate it on the stack because this saves + // the malloc/free time. + const int bufSize = 161; + char stackBuffer[bufSize]; + + // MSVC does not support va_copy + int actualSize = _vscprintf(fmt, argPtr) + 1; + + if (actualSize > bufSize) { + + // Now use the heap. + char* heapBuffer = NULL; + + if (actualSize < maxSize) { + + heapBuffer = (char*)System::malloc(maxSize + 1); + _vsnprintf(heapBuffer, maxSize, fmt, argPtr); + heapBuffer[maxSize] = '\0'; + } else { + heapBuffer = (char*)System::malloc(actualSize); + vsprintf(heapBuffer, fmt, argPtr); + } + + std::string formattedString(heapBuffer); + System::free(heapBuffer); + return formattedString; + } else { + + vsprintf(stackBuffer, fmt, argPtr); + return std::string(stackBuffer); + } +} + +#elif defined(_MSC_VER) && (_MSC_VER < 1300) + +std::string vformat(const char *fmt, va_list argPtr) { + // We draw the line at a 1MB string. + const int maxSize = 1000000; + + // If the string is less than 161 characters, + // allocate it on the stack because this saves + // the malloc/free time. + const int bufSize = 161; + char stackBuffer[bufSize]; + + // MSVC6 doesn't support va_copy, however it also seems to compile + // correctly if we just pass our argument list along. Note that + // this whole code block is only compiled if we're on MSVC6 anyway + int actualWritten = _vsnprintf(stackBuffer, bufSize, fmt, argPtr); + + // Not a big enough buffer, bufSize characters written + if (actualWritten == -1) { + + int heapSize = 512; + double powSize = 1.0; + char* heapBuffer = (char*)System::malloc(heapSize); + + while ((_vsnprintf(heapBuffer, heapSize, fmt, argPtr) == -1) && + (heapSize < maxSize)) { + + heapSize = iCeil(heapSize * ::pow((double)2.0, powSize++)); + heapBuffer = (char*)System::realloc(heapBuffer, heapSize); + } + + heapBuffer[heapSize-1] = '\0'; + + std::string heapString(heapBuffer); + System::free(heapBuffer); + + return heapString; + } else { + + return std::string(stackBuffer); + } +} + +#else + +// glibc 2.1 has been updated to the C99 standard +std::string vformat(const char* fmt, va_list argPtr) { + // If the string is less than 161 characters, + // allocate it on the stack because this saves + // the malloc/free time. The number 161 is chosen + // to support two lines of text on an 80 character + // console (plus the null terminator). + const int bufSize = 161; + char stackBuffer[bufSize]; + + va_list argPtrCopy; + va_copy(argPtrCopy, argPtr); + int numChars = vsnprintf(stackBuffer, bufSize, fmt, argPtrCopy); + va_end(argPtrCopy); + + if (numChars >= bufSize) { + // We didn't allocate a big enough string. + char* heapBuffer = (char*)System::malloc((numChars + 1) * sizeof(char)); + + debugAssert(heapBuffer); + int numChars2 = vsnprintf(heapBuffer, numChars + 1, fmt, argPtr); + debugAssert(numChars2 == numChars); + (void)numChars2; + + std::string result(heapBuffer); + + System::free(heapBuffer); + + return result; + + } else { + + return std::string(stackBuffer); + + } +} + +#endif + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/G3D.lib/source/g3dmath.cpp b/externals/g3dlite/G3D.lib/source/g3dmath.cpp new file mode 100644 index 00000000000..95c9e16cc24 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/g3dmath.cpp @@ -0,0 +1,70 @@ +/** + @file g3dmath.cpp + + @author Morgan McGuire, graphics3d.com + + @created 2001-06-02 + @edited 2004-02-24 + */ + +#include "G3D/g3dmath.h" +#include <stdlib.h> + +namespace G3D { + +float gaussRandom(float mean, float stdev) { + + // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html + // Modified to specify standard deviation and mean of distribution + float w, x1, x2; + + // Loop until w is less than 1 so that log(w) is negative + do { + x1 = uniformRandom(-1.0, 1.0); + x2 = uniformRandom(-1.0, 1.0); + + w = float(square(x1) + square(x2)); + } while (w > 1.0f); + + // Transform to gassian distribution + // Multiply by sigma (stdev ^ 2) and add mean. + return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean; +} + + +int highestBit(uint32 x) { + // Binary search. + int base = 0; + + if (x & 0xffff0000) { + base = 16; + x >>= 16; + } + if (x & 0x0000ff00) { + base += 8; + x >>= 8; + } + if (x & 0x000000f0) { + base += 4; + x >>= 4; + } + + static const int lut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3}; + return base + lut[x]; +} + + +int iRandom(int low, int high) { + int r = iFloor(low + (high - low + 1) * (double)rand() / RAND_MAX); + + // There is a *very small* chance of generating + // a number larger than high. + if (r > high) { + return high; + } else { + return r; + } +} + + +} diff --git a/externals/g3dlite/G3D.lib/source/license.cpp b/externals/g3dlite/G3D.lib/source/license.cpp new file mode 100644 index 00000000000..b362ad3f45f --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/license.cpp @@ -0,0 +1,70 @@ +/** + @file license.cpp + + @author Morgan McGuire, graphics3d.com + + @created 2004-04-15 + @edited 2004-04-15 +*/ + +#include "G3D/format.h" +#include <string> + +namespace G3D { + +std::string license() { + return format( + +"This software is based in part on the PNG Reference Library which is\n" +"Copyright (c) 2004 Glenn Randers-Pehrson\n\n" +"This software is based in part on the work of the Independent JPEG Group.\n\n" +"This software is based on part on the FFmpeg libavformat and libavcodec libraries\n" +"(\"FFmpeg\", http://ffmpeg.mplayerhq.hu), which are included under the terms of the\n" +"GNU Lesser General Public License (LGPL), (http://www.gnu.org/copyleft/lesser.html).\n\n" +"%s" +"This program uses the G3D Library (http://g3d-cpp.sf.net), which\n" +"is licensed under the \"BSD\" Open Source license. The Graphics3D library\n" +"source code is Copyright © 2000-2008, Morgan McGuire, All rights reserved.\n" +"The BSD license requires the following statement regarding G3D:\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions\n" +"are met:\n" +"\n" +"Redistributions of source code must retain the above copyright\n" +"notice, this list of conditions and the following disclaimer.\n" +"\n" +"Redistributions in binary form must reproduce the above copyright\n" +"notice, this list of conditions and the following disclaimer in the\n" +"documentation and/or other materials provided with the distribution.\n" +"\n" +"Neither the name of Morgan McGuire, Brown University, Williams College, nor the names\n" +"of the G3D contributors may be used to endorse or promote products derived\n" +"from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n\n" +"G3D VERSION %d\n", + +#ifdef G3D_WIN32 + "" // Win32 doesn't use SDL +#else + "This software uses the Simple DirectMedia Layer library (\"SDL\",\n" + "http://www.libsdl.org), which is included under the terms of the\n" + "GNU Lesser General Public License, (http://www.gnu.org/copyleft/lesser.html).\n\n" +#endif +, +G3D_VER); +} + +} diff --git a/externals/g3dlite/G3D.lib/source/prompt.cpp b/externals/g3dlite/G3D.lib/source/prompt.cpp new file mode 100644 index 00000000000..1fb3bd4cfee --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/prompt.cpp @@ -0,0 +1,716 @@ +/** + @file prompt.cpp + + @author Morgan McGuire, morgan@graphics3d.com + @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com + @cite Font setting code by Kurt Miller, kurt@flipcode.com + + @created 2000-08-26 + @edited 2005-01-14 + */ + +#include "G3D/prompt.h" +#include "G3D/platform.h" + +#include <stdio.h> + +#ifdef G3D_WIN32 +# include <sstream> +# include <conio.h> +#else +# define _getch getchar +#endif + +#ifdef G3D_OSX +# include <Carbon/Carbon.h> +#endif + +namespace G3D { + +#ifdef G3D_WIN32 + +namespace _internal { +/** + Generic Win32 dialog facility. + @author Max McGuire + */ +class DialogTemplate { +public: + + DialogTemplate(LPCSTR caption, DWORD style, + int x, int y, int w, int h, + LPCSTR font = NULL, WORD fontSize = 8) { + + usedBufferLength = sizeof(DLGTEMPLATE); + totalBufferLength = usedBufferLength; + + dialogTemplate = (DLGTEMPLATE*)malloc(totalBufferLength); + + dialogTemplate->style = style; + + if (font != NULL) { + dialogTemplate->style |= DS_SETFONT; + } + + dialogTemplate->x = (short)x; + dialogTemplate->y = (short)y; + dialogTemplate->cx = (short)w; + dialogTemplate->cy = (short)h; + dialogTemplate->cdit = 0; + + dialogTemplate->dwExtendedStyle = 0; + + // The dialog box doesn't have a menu or a special class + AppendData("\0", 2); + AppendData("\0", 2); + + // Add the dialog's caption to the template + + AppendString(caption); + + if (font != NULL) { + AppendData(&fontSize, sizeof(WORD)); + AppendString(font); + } + } + + void AddComponent(LPCSTR type, LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + DLGITEMTEMPLATE item; + + item.style = style; + item.x = (short)x; + item.y = (short)y; + item.cx = (short)w; + item.cy = (short)h; + item.id = id; + + item.dwExtendedStyle = exStyle; + + AppendData(&item, sizeof(DLGITEMTEMPLATE)); + + AppendString(type); + AppendString(caption); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + // Increment the component count + dialogTemplate->cdit++; + + } + + + void AddButton(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0080, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddEditBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0081, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddStatic(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0082, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddListBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0083, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = sizeof(WORD) + 5 * sizeof(WCHAR); + AppendData(&creationDataLength, sizeof(WORD)); + + AppendString("TEST"); + + } + + + void AddScrollBar(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0084, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddComboBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0085, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + /** + * + * Returns a pointer to the Win32 dialog template which the object + * represents. This pointer may become invalid if additional components + * are added to the template. + * + */ + operator const DLGTEMPLATE*() const { + return dialogTemplate; + } + + virtual ~DialogTemplate() { + free(dialogTemplate); + } + +protected: + + void AddStandardComponent(WORD type, LPCSTR caption, DWORD style, DWORD exStyle, + int x, int y, int w, int h, WORD id, LPSTR font = NULL, WORD fontSize = 8) { + + DLGITEMTEMPLATE item; + + // DWORD align the beginning of the component data + + AlignData(sizeof(DWORD)); + + item.style = style; + if (font != NULL) { + item.style |= DS_SETFONT; + } + item.x = (short)x; + item.y = (short)y; + item.cx = (short)w; + item.cy = (short)h; + item.id = id; + + item.dwExtendedStyle = exStyle; + + AppendData(&item, sizeof(DLGITEMTEMPLATE)); + + WORD preType = 0xFFFF; + + AppendData(&preType, sizeof(WORD)); + AppendData(&type, sizeof(WORD)); + + AppendString(caption); + + if (font != NULL) { + AppendData(&fontSize, sizeof(WORD)); + AppendString(font); + } + + // Increment the component count + dialogTemplate->cdit++; + } + + + void AlignData(int size) { + + int paddingSize = usedBufferLength % size; + + if (paddingSize != 0) { + EnsureSpace(paddingSize); + usedBufferLength += paddingSize; + } + + } + + void AppendString(LPCSTR string) { + + int length = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0); + + WCHAR* wideString = (WCHAR*)malloc(sizeof(WCHAR) * length); + MultiByteToWideChar(CP_ACP, 0, string, -1, wideString, length); + + AppendData(wideString, length * sizeof(WCHAR)); + free(wideString); + + } + + void AppendData(const void* data, int dataLength) { + + EnsureSpace(dataLength); + + memcpy((char*)dialogTemplate + usedBufferLength, data, dataLength); + usedBufferLength += dataLength; + + } + + void EnsureSpace(int length) { + if (length + usedBufferLength > totalBufferLength) { + totalBufferLength += length * 2; + + void* newBuffer = malloc(totalBufferLength); + memcpy(newBuffer, dialogTemplate, usedBufferLength); + + free(dialogTemplate); + dialogTemplate = (DLGTEMPLATE*)newBuffer; + } + } + +private: + + DLGTEMPLATE* dialogTemplate; + + int totalBufferLength; + int usedBufferLength; + +}; + + +struct PromptParams { + const char* message; + const char* title; +}; + +/** + * Constants for controls. + */ +#define IDC_MESSAGE 1000 +#define IDC_BUTTON0 2000 + +INT_PTR CALLBACK PromptDlgProc(HWND hDlg, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_INITDIALOG: + { + PromptParams *params = (PromptParams*)lParam; + ::SetWindowTextA(::GetDlgItem(hDlg, IDC_MESSAGE), params->message); + + ::SetFocus(::GetDlgItem(hDlg, IDC_BUTTON0)); + + SetWindowTextA(hDlg, params->title); + + HFONT hfont = + CreateFontA(16, 0, 0, 0, FW_NORMAL, + FALSE, FALSE, FALSE, + ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, + PROOF_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New"); + + SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE,0)); + + + break; + } + case WM_COMMAND: + { + int choiceNumber = LOWORD(wParam) - IDC_BUTTON0; + if ((choiceNumber >= 0) && (choiceNumber < 10)) { + EndDialog(hDlg, choiceNumber); + return TRUE; + } + } + + break; + + case WM_NCDESTROY: + // Under SDL 1.2.6 we get a NCDESTROY message for no reason and the + // window is immediately closed. This is here to debug the problem. + (void)0; + break; + + } + + return FALSE; +} + +}; // namespace _internal + + +using namespace _internal; + +/** + * Show a dialog prompt. + */ +static int guiPrompt( + const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices) { + + int width = 280; + int height = 128; + + const int buttonSpacing = 2; + const int buttonWidth = + (width - buttonSpacing * 2 - + buttonSpacing * (numChoices - 1)) / numChoices; + const int buttonHeight = 13; + + + DialogTemplate dialogTemplate( + windowTitle, + WS_CAPTION | DS_CENTER | WS_SYSMENU, + 10, 10, width, height, + "Tahoma"); + + dialogTemplate.AddEditBox( + "Edit", WS_VISIBLE | ES_READONLY | ES_OEMCONVERT | ES_MULTILINE | WS_TABSTOP, WS_EX_STATICEDGE, + 2, 2, width - 4, height - buttonHeight - 7, IDC_MESSAGE); + + int i; + for (i = 0; i < numChoices; i++) { + + int x = buttonSpacing + i * (buttonWidth + buttonSpacing); + int y = height - buttonHeight - buttonSpacing; + + dialogTemplate.AddButton(choice[i], WS_VISIBLE | WS_TABSTOP, 0, + x, y, buttonWidth, buttonHeight, IDC_BUTTON0 + (WORD)i); + + } + + // Convert all single \n characters to \r\n for proper printing + int strLen = 0; + const char* pStr = prompt; + + while (*pStr != '\0') { + if ((*pStr == '\n') && (pStr != prompt)) { + if (*(pStr - 1) != '\r') { + ++strLen; + } + } + ++strLen; + ++pStr; + } + + char* newStr = (char*)malloc(strLen + 1); + + const char* pStr2 = prompt; + char* pNew = newStr; + + while (*pStr2 != '\0') { + if ((*pStr2 == '\n') && (pStr2 != prompt)) { + if (*(pStr2 - 1) != '\r') { + *pNew = '\r'; + ++pNew; + } + } + *pNew = *pStr2; + ++pNew; + ++pStr2; + } + + *pNew = '\0'; + + PromptParams params; + params.message = newStr;; + params.title = windowTitle; + + HMODULE module = GetModuleHandle(0); + int ret = DialogBoxIndirectParam(module, dialogTemplate, NULL, (DLGPROC) PromptDlgProc, (DWORD)¶ms); + + free(newStr); + + /* + For debugging when DialogBoxIndirectParam fails: + + // The last error value. (Which is preserved across the call). + DWORD lastErr = GetLastError(); + + // The decoded message from FormatMessage + LPTSTR formatMsg = NULL; + + if (NULL == formatMsg) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + 0, + (LPTSTR)&formatMsg, + 0, + NULL); + } + + // Make sure the message got translated into something. + LPTSTR realLastErr; + if (NULL != formatMsg) { + realLastErr = formatMsg; + } else { + realLastErr = "Last error code does not exist."; + } + + // Get rid of the allocated memory from FormatMessage. + if (NULL != formatMsg) { + LocalFree((LPVOID)formatMsg); + } + */ + + return ret; +} + +#endif + + +/** + * Show a prompt on stdout + */ +static int textPrompt( + const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices) { + + printf("\n___________________________________________________\n"); + printf("%s\n", windowTitle); + printf("%s", prompt); + + if (numChoices > 10) { + numChoices = 10; + } + + int c = -1; + if (numChoices > 1) { + printf("\n"); + printf("Choose an option by number:"); + + while ((c < 0) || (c >= numChoices)) { + printf("\n"); + + for (int i = 0; i < numChoices; i++) { + if (numChoices <= 3) { + printf(" (%d) %s ", i, choice[i]); + } else { + printf(" (%d) %s\n", i, choice[i]); + } + } + + printf("\n> "); + c = _getch() - '0'; + + if ((c < 0) || (c >= numChoices)) { + printf("'%d' is not a valid choice.", c); + } else { + printf("%d", c); + } + } + + } else if (numChoices == 1) { + + printf("\nPress any key for '%s'...", choice[0]); + _getch(); + c = 0; + + } else { + + printf("\nPress any key..."); + _getch(); + c = 0; + } + + printf("\n___________________________________________________\n"); + return c; +} + +#ifdef G3D_OSX + +// See http://developer.apple.com/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html + +#define CARBON_COMMANDID_START 128 +#define CARBON_BUTTON_SPACING 12 +#define CARBON_BUTTON_HEIGHT 20 +#define CARBON_BUTTON_MINWIDTH 69 +#define CARBON_WINDOW_PADDING 20 + +struct CallbackData { + WindowRef refWindow; + + /** Index of this particular button */ + int myIndex; + + /** Buttons store their index into here when pressed. */ + int* whichButton; +}; + +/** + Assumes that userData is a pointer to a carbon_evt_data_t. + + */ +static pascal OSStatus DoCommandEvent(EventHandlerCallRef handlerRef, EventRef event, void* userData) { + // See http://developer.apple.com/documentation/Carbon/Conceptual/HandlingWindowsControls/index.html + + CallbackData& callbackData = *(CallbackData*)userData; + +# pragma unused(handlerRef) + + callbackData.whichButton[0] = callbackData.myIndex; + + // If we get here we can close the window + QuitAppModalLoopForWindow(callbackData.refWindow); + + // Return noErr to indicate that we handled the event + return noErr; +} + +static int guiPrompt +(const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices) { + + WindowRef window; + + int iNumButtonRows = 0; + int iButtonWidth = -1; + OSStatus err = noErr; + + // Determine number of rows of buttons + while (iButtonWidth < CARBON_BUTTON_MINWIDTH) { + ++iNumButtonRows; + iButtonWidth = + (550 - (CARBON_WINDOW_PADDING*2 + + (CARBON_BUTTON_SPACING*numChoices))) / + (numChoices/iNumButtonRows); + } + + // Window Variables + Rect rectWin = {0, 0, 200 + ((iNumButtonRows-1) * (CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)), 550}; // top, left, bottom, right + CFStringRef szWindowTitle = CFStringCreateWithCString(kCFAllocatorDefault, windowTitle, kCFStringEncodingUTF8); + + window = NULL; + + err = CreateNewWindow(kMovableAlertWindowClass, kWindowStandardHandlerAttribute|kWindowCompositingAttribute, &rectWin, &window); + err = SetWindowTitleWithCFString(window, szWindowTitle); + err = SetThemeWindowBackground(window, kThemeBrushAlertBackgroundActive, false); + assert(err == noErr); + + // Event Handler Variables + EventTypeSpec buttonSpec[] = {{ kEventClassControl, kEventControlHit }, { kEventClassCommand, kEventCommandProcess }}; + EventHandlerUPP buttonHandler = NewEventHandlerUPP(DoCommandEvent); + + // Static Text Variables + Rect rectStatic = {20, 20, 152, 530}; + CFStringRef szStaticText = CFStringCreateWithCString(kCFAllocatorDefault, prompt, kCFStringEncodingUTF8); + ControlRef refStaticText = NULL; + err = CreateStaticTextControl(window, &rectStatic, szStaticText, NULL, &refStaticText); + + // Button Variables + Rect bounds[numChoices]; + CFStringRef caption[numChoices]; + ControlRef button[numChoices]; + + int whichButton=-1; + CallbackData callbackData[numChoices]; + + // Create the Buttons and assign event handlers + for (int i = 0; i < numChoices; ++i) { + bounds[i].top = 160 + ((CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)*(i%iNumButtonRows)); + bounds[i].right = 530 - ((iButtonWidth+CARBON_BUTTON_SPACING)*(i/iNumButtonRows)); + bounds[i].left = bounds[i].right - iButtonWidth; + bounds[i].bottom = bounds[i].top + CARBON_BUTTON_HEIGHT; + + // Convert the button captions to Apple strings + caption[i] = CFStringCreateWithCString(kCFAllocatorDefault, choice[i], kCFStringEncodingUTF8); + + err = CreatePushButtonControl(window, &bounds[i], caption[i], &button[i]); + assert(err == noErr); + + err = SetControlCommandID(button[i], CARBON_COMMANDID_START + i); + assert(err == noErr); + + callbackData[i].refWindow = window; + callbackData[i].myIndex = i; + callbackData[i].whichButton = &whichButton; + + err = InstallControlEventHandler(button[i], buttonHandler, + GetEventTypeCount(buttonSpec), buttonSpec, + &callbackData[i], NULL); + assert(err == noErr); + } + + // Show Dialog + err = RepositionWindow(window, NULL, kWindowCenterOnMainScreen); + ShowWindow(window); + BringToFront(window); + err = ActivateWindow(window, true); + + // Hack to get our window/process to the front... + ProcessSerialNumber psn = { 0, kCurrentProcess}; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess (&psn); + + // Run in Modal State + err = RunAppModalLoopForWindow(window); + + // Dispose of Button Related Data + for (int i = 0; i < numChoices; ++i) { + // Dispose of controls + DisposeControl(button[i]); + + // Release CFStrings + CFRelease(caption[i]); + } + + // Dispose of Other Controls + DisposeControl(refStaticText); + + // Dispose of Event Handlers + DisposeEventHandlerUPP(buttonHandler); + + // Dispose of Window + DisposeWindow(window); + + // Release CFStrings + CFRelease(szWindowTitle); + CFRelease(szStaticText); + + // Return Selection + return whichButton; +} + +#endif + +int prompt( + const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices, + bool useGui) { + + #ifdef G3D_WIN32 + if (useGui) { + // Build the message box + return guiPrompt(windowTitle, prompt, choice, numChoices); + } + #endif + + #ifdef G3D_OSX + if (useGui){ + //Will default to text prompt if numChoices > 4 + return guiPrompt(windowTitle, prompt, choice, numChoices); + } + #endif + return textPrompt(windowTitle, prompt, choice, numChoices); +} + + +void msgBox( + const std::string& message, + const std::string& title) { + + const char *choice[] = {"Ok"}; + prompt(title.c_str(), message.c_str(), choice, 1, true); +} + +#ifndef G3D_WIN32 + #undef _getch +#endif + +};// namespace + diff --git a/externals/g3dlite/G3D.lib/source/stringutils.cpp b/externals/g3dlite/G3D.lib/source/stringutils.cpp new file mode 100644 index 00000000000..a21fc1f377c --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/stringutils.cpp @@ -0,0 +1,231 @@ +/** + @file stringutils.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2000-09-09 + @edited 2008-01-10 +*/ + +#include "G3D/platform.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryInput.h" +#include <algorithm> + +namespace G3D { + +#ifdef _MSC_VER + // disable: "C++ exception handler used" +# pragma warning (push) +# pragma warning (disable : 4530) +#endif +#ifdef G3D_WIN32 + const char* NEWLINE = "\r\n"; +#else + const char* NEWLINE = "\n"; + static bool iswspace(int ch) { return (ch==' ' || ch=='\t' || ch=='\n'); } +#endif + +bool beginsWith( + const std::string& test, + const std::string& pattern) { + + if (test.size() >= pattern.size()) { + for (int i = 0; i < (int)pattern.size(); ++i) { + if (pattern[i] != test[i]) { + return false; + } + } + return true; + } else { + return false; + } +} + + +bool endsWith( + const std::string& test, + const std::string& pattern) { + + if (test.size() >= pattern.size()) { + int te = test.size() - 1; + int pe = pattern.size() - 1; + for (int i = pattern.size() - 1; i >= 0; --i) { + if (pattern[pe - i] != test[te - i]) { + return false; + } + } + return true; + } else { + return false; + } +} + + +std::string wordWrap( + const std::string& input, + int numCols) { + + std::string output; + size_t c = 0; + int len; + + // Don't make lines less than this length + int minLength = numCols / 4; + size_t inLen = input.size(); + + bool first = true; + while (c < inLen) { + if (first) { + first = false; + } else { + output += NEWLINE; + } + + if ((int)inLen - (int)c - 1 < numCols) { + // The end + output += input.substr(c, inLen - c); + break; + } + + len = numCols; + + // Look at character c + numCols, see if it is a space. + while ((len > minLength) && + (input[c + len] != ' ')) { + len--; + } + + if (len == minLength) { + // Just crop + len = numCols; + + } + + output += input.substr(c, len); + c += len; + if (c < input.size()) { + // Collapse multiple spaces. + while ((input[c] == ' ') && (c < input.size())) { + c++; + } + } + } + + return output; +} + + +int stringCompare( + const std::string& s1, + const std::string& s2) { + + return stringPtrCompare(&s1, &s2); +} + + +int stringPtrCompare( + const std::string* s1, + const std::string* s2) { + + return s1->compare(*s2); +} + + +std::string toUpper(const std::string& x) { + std::string result = x; + std::transform(result.begin(), result.end(), result.begin(), toupper); + return result; +} + + +std::string toLower(const std::string& x) { + std::string result = x; + std::transform(result.begin(), result.end(), result.begin(), tolower); + return result; +} + + +Array<std::string> stringSplit( + const std::string& x, + char splitChar) { + + Array<std::string> out; + + // Pointers to the beginning and end of the substring + const char* start = x.c_str(); + const char* stop = start; + + while ((stop = strchr(start, splitChar))) { + out.append(std::string(start, stop - start)); + start = stop + 1; + } + + // Append the last one + out.append(std::string(start)); + + return out; +} + + +std::string stringJoin( + const Array<std::string>& a, + char joinChar) { + + std::string out; + + for (int i = 0; i < (int)a.size() - 1; ++i) { + out += a[i] + joinChar; + } + + if (a.size() > 0) { + return out + a.last(); + } else { + return out; + } +} + + +std::string stringJoin( + const Array<std::string>& a, + const std::string& joinStr) { + + std::string out; + + for (int i = 0; i < (int)a.size() - 1; ++i) { + out += a[i] + joinStr; + } + + if (a.size() > 0) { + return out + a.last(); + } else { + return out; + } +} + + +std::string trimWhitespace( + const std::string& s) { + + size_t left = 0; + + // Trim from left + while ((left < s.length()) && iswspace(s[left])) { + ++left; + } + + int right = s.length() - 1; + // Trim from right + while ((right > (int)left) && iswspace(s[right])) { + --right; + } + + return s.substr(left, right - left + 1); +} + +}; // namespace + +#undef NEWLINE +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/G3D.lib/source/uint128.cpp b/externals/g3dlite/G3D.lib/source/uint128.cpp new file mode 100644 index 00000000000..450009a5cff --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/uint128.cpp @@ -0,0 +1,155 @@ +/** + @file uint128.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + @author Kyle Whitson + + @created 2008-07-17 + @edited 2008-07-17 + */ + +#include "G3D/uint128.h" + +namespace G3D { + +/** Adds two 64-bit integers, placing the result and the overflow into 64-bit integers.*/ +static void addAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) { + + // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros. + // This eliminates the need to and with 0xFFFFFFFF. + uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32}; + uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32}; + + uint64 tmp = uint64(a[0]) + b[0]; + + result = tmp & 0xFFFFFFFF; + uint32 c = tmp >> 32; + + tmp = uint64(c) + a[1] + b[1]; + result += tmp << 32; + carry = (tmp >> 32); +} + +/** Multiplies two unsigned 64-bit integers, placing the result into one 64-bit int and the overflow into another.*/ +void multiplyAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) { + + // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros. + // This eliminates the need to and with 0xFFFFFFFF. + uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32}; + uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32}; + + uint64 prod [2][2]; + for(int i = 0; i < 2; ++i) { + for(int j = 0; j < 2; ++j) { + prod[i][j] = uint64(a[i]) * b[j]; + } + } + + // The product of the low bits of a and b will always fit into the result + result = prod[0][0]; + + // The product of the high bits of a and b will never fit into the result + carry = prod[1][1]; + + // The high 32 bits of prod[0][1] and prod[1][0] will never fit into the result + carry += prod[0][1] >> 32; + carry += prod[1][0] >> 32; + + uint64 tmp; + addAndCarry(result, (prod[0][1] << 32), tmp, result); + carry += tmp; + addAndCarry(result, (prod[1][0] << 32), tmp, result); + carry += tmp; +} + + +uint128::uint128(const uint64& hi, const uint64& lo) : hi(hi), lo(lo) { +} + +uint128::uint128(const uint64& lo) : hi(0), lo(lo) { +} + +uint128& uint128::operator+=(const uint128& x) { + + G3D::uint64 carry; + addAndCarry(lo, x.lo, carry, lo); + + // Adding the carry will change hi. Save the old hi bits in case this == x. + const uint64 xHi = x.hi; + hi += carry; + hi += xHi; + return *this; +} + +uint128& uint128::operator*=(const uint128& x) { + + // The low bits will get overwritten when doing the multiply, so back up both (in case &x == this) + const uint64 oldLo = lo; + const uint64 oldXLo = x.lo; + + G3D::uint64 carry; + multiplyAndCarry(oldLo, oldXLo, carry, lo); + + // Overflow doesn't matter here because the result is going into hi - any overflow will exceed the capacity of a 128-bit number + // Note: hi * x.hi will always overflow, since (x * 2^64) * (y * 2^64) = x*y*(2^128). The largest number expressable in 128 bits is + // 2^128 - 1. + hi = carry + (oldLo * x.hi) + (hi * oldXLo); + + return *this; +} + +uint128& uint128::operator^=(const uint128& x) { + hi ^= x.hi; + lo ^= x.lo; + return *this; +} + +uint128& uint128::operator&=(const uint128& x) { + hi &= x.hi; + lo &= x.lo; + return *this; +} + +uint128& uint128::operator|=(const uint128& x) { + hi |= x.hi; + lo |= x.lo; + return *this; +} + +bool uint128::operator==(const uint128& x) { + return (hi == x.hi) && (lo == x.lo); +} + +uint128& uint128::operator>>=(const int x) { + + //Before shifting, mask out the bits that will be shifted out of hi. + //Put a 1 in the first bit that will not be lost in the shift, then subtract 1 to get the mask. + uint64 mask = ((uint64)1L << x) - 1; + uint64 tmp = hi & mask; + hi >>= x; + + //Shift lo and add the bits shifted down from hi + lo = (lo >> x) + (tmp << (64 - x)); + + return *this; +} + +uint128& uint128::operator<<=(const int x) { + + //Before shifting, mask out the bits that will be shifted out of lo. + //Put a 1 in the last bit that will be lost in the shift, then subtract 1 to get the logical inverse of the mask. + //A bitwise NOT will then produce the correct mask. + uint64 mask = ~((((uint64)1L) << (64 - x)) - 1); + uint64 tmp = lo & mask; + lo <<= x; + + //Shift hi and add the bits shifted up from lo + hi = (hi << x) + (tmp >> (64 - x)); + + return *this; +} + +uint128 uint128::operator&(const uint128& x) { + return uint128(hi & x.hi, lo & x.lo); +} +} diff --git a/externals/g3dlite/delme b/externals/g3dlite/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/g3dlite/delme +++ /dev/null diff --git a/externals/g3dlite/doc-files/changelog.dox b/externals/g3dlite/doc-files/changelog.dox new file mode 100644 index 00000000000..fd52b99f271 --- /dev/null +++ b/externals/g3dlite/doc-files/changelog.dox @@ -0,0 +1,1872 @@ +/** + @page changelog Change Log + +<P> + + Major version numbers introduce API changes that are not backwards + compatible. Minor versions are backwards compatible to the + previous major release, except for critical bug fixes. Deprecated functionality + will be supported until (at least) the next major release. + + +<hr> + + <P> + Changes in 7.01: + <ul> + <li> Video file reading and writing via FFmpeg added + <li> Added computeBounds method to ArticulatedModel::Part that calls computeBounds on each TriList. Changed updateAll to automatically call computeBounds [Kyle] + <li> Added constructor to Matrix4 to construct a matrix from an upper-left 3x3 submatrix and an upper-right 3x1 submatrix [Kyle] + <li> <b>Incompatible change</b>: RegistryUtil functions now require an explicit value parameter instead of extracting the value from the key string. + <li> <b>Incompatible change</b>:GApp now calls the onLogic handler before the simulation handlers but after the user input and network handlers + <li> <b>Incompatible change</b>: Changed GHashCode and other functors to traits. See \link guidenewuser \endlink. Added typedefs and adapters to make this mostly backwards compatible. + <li> Added parallax occlusion mapping to G3D::SuperShader (specify Material::parallaxSteps > 1) + <li> Added normal mapping to G3D::SuperShader (specify Material::parallaxSteps == 0) + <li> G3D::Texture resizes textures that exceed the device maximum size + <li> G3D::Array now allows control over MIN_ELEMENTS and MIN_BYTES using template parameters + <li> Clarified G3D::Any file format in documentation + <li> G3D::Texture::PreProcess::gammaAdjust + <li> G3D::ShadowMap::lightProjection(), G3D::ShadowMap::lightFrame() + <li> Added Barycentric coordinates to CollisionDetection::isPointInTriangle + <li> G3D::RenderDevice::beginOpenGL, G3D::RenderDevice::endOpenGL + <li> PointAABSPTree::clearData + <li> AABSPTree -> KDTree + <li> GApp now allows the MidgetManager to process events in onEvent before the GApp::onEvent executes + <li> Added spotlight support to SuperShader + <li> Switched SuperShader to use ph + <li> Adjusted standard deviation used in G3D::GaussianBlur to provide smoother filtering + <li> Put HashTrait and EqualsTrait in their own headers separate from Table.h + <li> ArticulatedModel::facet + <li> Renamed GWindow to G3D::OSWindow + <li> ReferenceCountedPointer now asserts that the pointer is not NULL on method invocation + <li> G3D::ShadowMap now computes appropriate matrices for spot lights + <li> Added ImageFormat::convert [Danny and Kyle] + <li> 3- and 4- argument min and max + <li> G3D::GaussianBlur now correctly sets the output viewport + <li> G3D::Framebuffer::clear + <li> IFSModel and ArticulatedModel now load Princeton Shape Benchmark OFF files. + <li> G3D::Any CoordinateFrame now serialized using angles + <li> CameraControlWindow now prints angles in degrees + <li> ImageXXX classes now have a format() method + <li> OSWindow::create + <li> MeshAlg::toIndexedTriList now supports TRIANGLE_FAN input. + <li> Tuned Table and hash functions for performance [Kyle & Morgan] + <li> GEvent::toString + <li> G3D::TextInput now treats characters with ASCII code greater than 127 as symbols + <li> G3D::ThreadSet + <li> G3D::Texture::white + <li> G3D::Matrix4::upper3x3 + <li> G3D::Matrix4::homoMul + <li> ArticulateModel::fromFile now takes a Matrix4 instead of a CoordinateFrame to allow arbitrary linear transformations. + <li> ArticulatedModel::createCornellBox + <li> Material::createDiffuse + <li> ImageFormat::convert + <li> G3D::filenameBase + <li> Removed SDL_SysWMEvent, which was never supported by GEvent anyway + <li> Removed TextureFormat::SAME_AS_SCREEN to break dependence on OpenGL + <li> TextureFormat has been renamed to G3D::ImageFormat and moved into G3D.lib + <li> Added variable time control points to G3D::Spline + <li> Gui controls now have configureable GuiControl::setCaptionSize + <li> Gui controls now default to no indent if the caption is "" (use " " for indent with no caption) + <li> G3D::GuiContainer + <li> G3D::GThread::started + <li> buildg3d installation arguments changed--see + <li> G3D::Vector3int32 + <li> GuiButton now accepts an optional callback function/method + <li> FileDialog now accepts an extra "note" argument + <li> FileDialog::getFilename non-static to support subclassing + <li> System::currentDateString + <li> Expanded G3D::ArticulatedModel documentation + <li> Build system now executes on multiple processors (about 1.8x speedup for dual-core) + <li> Build system now caches dependencies (about 5x speedup for small incremental builds) + <li> Patched LOAD_EXTENSION to work around gcc pointer-to-function cast issues + <li> Tool buttons added to a G3D::GuiPane automatically align to the previous one. + <li> Added invisible GuiPane style + <li> G3D::uint128 [Kyle] + <li> Increased BSPMap rendering by 10% by reducing state changes + <li> Added prompt argument to FileDialog::getFilename + <li> G3D::PosedModel::getBoxBounds on an array + <li> G3D::PosedModel::getSphereBounds on an array + <li> Changed RenderDevice::screenshot to save .png instead of .jpg files + <li> G3D::SuperShader now supports a customMap and customConstant for experimenting with shaders. + <li> G3D::SuperShader now does not ever light the "back" of a bump-mapped poly, even if the bumps should create a light-facing surface + <li> G3D::Material promoted to its own class (was G3D::SuperShader::Material) + <li> G3D::Matrix2 + <li> G3D::VertexAndPixelShader::ArgList::size + <li> G3D::pathConcat + <li> G3D::WidgetManager::moveWidgetToBack + <li> SuperShader / NonShadowed.pix now uses arrays of lights instead of separate variables + <li> Reduced cost of release-mode Shader argument validation + <li> G3D::PosedModel::sortAndRender now performs view-frustum culling of objects + <li> G3D::Draw::lighting for visualizing light sources + <li> G3D::SuperShader::Pass::purgeCache + <li> G3D::GuiSlider::setRange + <li> G3D::GuiPane::addPane no longer takes a + <li> G3D::VertexAndPixelShader::ArgList::remove + <li> Optimized G3D::Matrix::pseudoInverse; now about 2x faster + <li> G3D::GLight::effectSphere + <li> G3D::GuiWindow::moveTo + <li> G3D::GuiWindow::setEnabled, enabled + <li> G3D::GuiButton now sizes to its caption + <li> G3D::GuiSlider now fires events on change and drag + <li> G3D::Shader arguments (in G3D::VertexAndPixelShader::ArgList) may now be "optional" + <li> G3D::GLight::point now has quadratic attenuation by default. + <li> G3D::ImageFormat::name + <li> g3dmath.h now includes inttypes.h on gcc and simulates it on visual studio + <li> G3D::RenderDevice::cullFace + <li> G3D::LineSegment2D::intersection + <li> G3D::BinaryInput::setEndian + <li> G3D::GEvent::MOUSE_BUTTON_CLICK + <li> Generalized ShadowMap to work with spotlights as well as directional lights + <li> G3D::GLCaps::supportsTexture, G3D::GLCaps::supportsRenderBuffer + <li> Opaque G3D::ArticulatedModels now support more than 2 non-shadow casting light sources + <li> Added proof symbol parsing to TextInput [Corey Taylor] + <li> Added G3D::AABox::corner() to match G3D::Box::corner() [Corey Taylor] + <li> OS X: G3D::CarbonWindow [Casey] + <li> OS X: iCompile now generates OS X application bundles and dmg files + <li> OS X build no longer depends on X11 + <li> G3D::FileDialog + <li> G3D::Table now allows overriding the default equality operator for keys + <li> <b>Incompatible change</b>: GApp::onBeforeSimulation now allows mutation of the timesteps + <li> <b>Incompatible change</b>: Merged GApp::simTime and idealSimTime (the sim time is now idealized) + <li> cmake now generates project files for Xcode, MinGW, and all Visual Studio versions [Corey Taylor] + <li> OS X: icompile and buildg3d now generate universal binaries on Intel machines + <li> G3D::PosedModel::objectSpaceTangents + <li> G3D::IFSModel::fromData + <li> G3D::MeshAlg::generateGrid + <li> G3D::BinaryOutput::ok() + <li> G3D::generateFilenameBase + <li> G3D::IFSModel::fromFile now defaults to not welding for improved performance + <li> G3D::IFSModel members are now protected to allow subclassing + <li> Removed G3D::uint in favor of G3D::uint32 [Corey Taylor] + <li> Added G3D::GMaterial(TextureRef) constructor + <li> Made G3D::GMaterial fields floats + <li> G3D::GuiControl::setCaption, G3D::GuiWindow::setCaption + <li> G3D::GuiControl can now be subclassed for custom user-defined controls + <li> G3D::GuiTheme::renderCanvas + <li> G3D::GuiTheme::pauseRendering, G3D::GuiTheme::resumeRendering + <li> G3D::PosedModel::sortAndRender + <li> G3D::Framebuffer can now attach cube map faces + <li> System::describeSystem now prints current working directory and application name + <li> Added /usr/local/-G3D dir- to system data file path [Kai Schroeder] + <li> Various patches for detecting new CPUs in System.cpp [Corey Taylor] + <li> g3d_Index macro now available in G3D::Shader GLSL code + <li> G3D::BackgroundWidget + <li> G3D::TriangleShape + <li> Fix: <B>incompatible change</b> OSWindow::Settings::asynchronous is now spelled correclty, with two "n"s + <li> Fix: Fixes for point-in-triangle and moving-sphere-fixed-tri; previous code projected onto the wrong axes, so barycentric coords were wrong for nearly vertical triangles. + <li> Fix: Changed some doubles to floats in G3D::Triangle + <li> Fix: Changed all of the isXXX(char) methods to take unsigned char arguments so that they can parse extended symbols + <li> Fix: AABSPTree::deserializeStructure was missing a return statement [Derex] + <li> Fix: Draw::plane was drawing the plane reflected through the origin [Macklin Chaffee] + <li> Fix: Added template parameters to friends in AABSPTree and PointAABSPTree [expiring_frog] + <li> Fix: System::findDataFile uses data directory set by GApp + <li> Fix: AtomicInt32 decrement returns int32 instead of uint32 + <li> Fix: OS X function keys now work correctly under CarbonWindow + <li> Fix: OS X modifier keys now work correctly under CarbonWindow + <li> Fix: OS X arrow keys now work correctly under CarbonWindow + <li> Fix: Rewrote buildg3d to fix many longstanding bugs, including mismatched 'bin' directories and confusion about the 'install' target + <li> Fix: gfxmeter reports now format correctly regardless of monitor width + <li> Fix: patch to initialize correctly on the Mesa library, which crashes when requesting DEPTH24_STENCIL8 + <li> Fix: stringSplit now works correctly for adjacent split characters [Akita Noek] + <li> Fix: Draw::axes labels now obey current viewport + <li> Fix: GuiWindow now loses focus when hidden + <li> Fix: GFont::draw2D now computes correct horizontal bounds on text + <li> Fix: GuiPane no longer renders when invisible + <li> Fix: Clicking off of all GuiWindows makes none of them have focus + <li> Fix: Win32Window now allows windows programmatically positioned anywhere on a multiple monitor screen + <li> Fix: Win32Window now does not fail when dragging a GL context between multiple monitors + <li> Fix: SuperShader now correctly lights bump-mapped surfaces in tangent space [Kyle Whitson] + <li> Fix: GuiPane now renders its caption + <li> Fix: Rect2D::border now grows the correct way (positive = grow) + <li> Fix: Added % operator to TextInput + <li> Fix: Added multi-line printing to GConsole + <li> Fix: G3D::Texture can now create empty cube maps + <li> Fix: G3D::Table iterator now correctly parameterized on hashfunction and equality function as well as key and value + <li> Fix: G3D::Table now passes Values by reference when setting them, avoiding one copy + <li> Fix: various Framebuffer/empty texture initialization bugs on ATI cards + <li> Fix: uniform arrays for GLSL + <li> Fix: All aliasing warnings have been fixed no longer needs -fno-strict-aliasing [Corey Taylor] + <li> Fix: [ 1829704 ] debugAssert in Array::operator[](unsigned int n) wrong + <li> Fix: GuiWindow::pack now recursively packs all child panes + <li> Fix: patch to continue build when javac isn't found on both windows and linux [Corey Taylor] + <li> Fix: [ 1599139 ] Fixes to make buildg3d work on non C:\ windows systems [Patrick Burke] + <li> Fix: Added faster overloads of GImage::stripAlpha() and GImage::insertRedAsAlpha() [Corey Taylor] + <li> Fix: GImage::save() with odd-widths bmp files [Corey Taylor] + <li> Fix: Draw::capule renders properly (capule was not visible) [Corey Taylor] + <li> Fix: Patched ShadowMap to work around ATI and OS X driver shadow map bugs. + <b>Incompatible change:</b>Required changing several interfaces to take ShadowMapRef arguments. + <li> Fix: GCamera::Frustum was facing backwards + <li> Fix: [ 1774479 ] texture glformats wrong (caused incorrect font rendering on Intel) + <li> Fix: ArticulatedModel static methods do not force loading of shaders unless an ArticulatedModel has actually been loaded. + <li> Fix: RenderDevice::setAlphaWrite/setColorWrite implemented correctly + <li> Fix: Implemented ImageFormat::fromCode + <li> Fix: [ 1766509 ] Texture doesn't handle 3D textures correctly + <li> Fix: Separate bool, float, and int back ends for GLSL shaders + </ul> + +<hr> + <P> + Changes in 7.00: + <ul> + <li> Upgraded to iCompile 0.5.3. Requires users to delete their old ice.txt and ~/.icompile files + <li> Key to toggle the user camera is now F2 (was Tab) + <li> Support for g++ 4.1 [Corey] + <li> Patches for 64-bit compilation [Corey] + <li> WinMain is now compiled as C++ code (fixed some "manifest" errors on certain VC8 installations) + <li> G3D::ShadowMap + <li> Cleaned up and documented G3D::GCamera project/unproject API [Jeff Marsceill] + <li> Made G3D::MD2Model::Pose easier to use + <li> G3D::Matrix + <li> Added G3D::Texture methods for reading back color and depth textures + <li> G3D::RenderDevice::getFixedFunctionLighting + <li> G3D::debugPrintf -> G3D::consolePrintf + <li> G3D::GApp::debugPrintf -> G3D::screenPrintf + <li> Reduced ArticulatedModel and MD2Model push/popState calls + <li> G3D::Shader now defines platform and graphics vendor macros + <li> User camera control requires right mouse click to move + <li> G3D::AnyVal::fromFile, G3D::AnyVal::load, G3D::AnyVal::save + <li> G3D::AnyVal now uses copy-on-mutate for fast Array/Table copies + <li> G3D::AnyVal no longer separates table entries with ";" + <li> G3D::AnyVal now provides casts to basic data types + <li> G3D::AnyVal G3D::Rect2D and G3D::AABox types + <li> G3D::NetListener, G3D::ReliableConduit, and G3D::LightweightConduit can now be created without an explicit NetworkDevice pointer + <li> GApp::onGraphics now takes posed model arguments + <li> G3D::RenderDevice::beginFrame no longer executes a pushState-- state will carry over between frames + <li> G3D::GUniqueID + <li> GApp::onPose + <li> All classes that read from files can now read data inside zipfiles. + <li> Changed G3D::hashCode(T) to ::GHashCode<T>(T) [Corey] + <li> Removed "G3D::" from the printed portion of the documentation index to make it easier to read. + <li> OSWindow::fireEvent for inserting user events into the queue + <li> G3D::logPrintf + <li> System::findDataFile + <li> On OS X, G3D::FirstPersonManipulator now treats ctrl-click as right click + <li> Increased mouse sensitivity of G3D::FirstPersonManipulator on OS X + <li> System::getClipboardText, System::setClipboardText + <li> Widget::window() accessor + <li> Replaced SDLEvent with G3D::GEvent + <li> Increased GFont rendering performance, added GFont::send2DQuads for fast rendering of large amounts of text + <li> G3D::GFont now supports fonts with 256 characters (.fnt file format version 2) + <li> Upgraded to the OpenGL 2.0 glext/glxext/wglext headers + <li> G3D::GuiTheme, G3D::GuiText + <li> G3D::UprightFrame, G3D::UprightSpline + <li> G3D::Spline + <li> G3D::RenderDevice::clip2D + <li> G3D::Win32Window now returns events for up to 5 mouse buttons + <li> Significantly changed GEvent delivery and Widget mechanisms to incorporate notions of focus--see upgrade.html + <li> Motion events (joystick/mouse) can no longer be cancelled by Widget::onEvent + <li> Mouse motion events for all platforms + <li> G3D::PosedModel::Ref::sendGeometry + <li> G3D::RenderDevice::pushState(FramebufferRef) + <li> G3D::Texture::PreProcess::computeNormalMap + <li> direct.h is now included by fileutils.h on Win32 to ensure that chdir, mkdir, etc. are available + <li> Changed distribution directories to place include, lib, and bin under a directory named after the platform + and all other files one directory up. + <li> G3D::GCamera::unproject + <li> Made G3D::Ray non-virtual for efficiency + <li> RenderDevice::alphaWrite now defaults to true + <li> Changed G3D install directory from g3d-7_00 to g3d-7.00 + <li> On a GL 2.0 or greater driver, G3D::GLCaps now assumes the presence of all GL2 extensions even if they are not explicitly listed by the card [Corey Taylor] + <li> debugPrintf now flushes stderr on Unix systems + <li> G3D::Lighting::fromSky + <li> G3D::Texture::newGLTexture2D [Corey Taylor] + <li> Added 10-bit cinema texture formats [Corey Taylor] + <li> Added G3D::ArticulatedModel to the core (includes 3DS loading) + <li> Added G3D::SuperShader to the core + <li> Merged GApp and GApplet into GApp to make the common case easier to implement + <li> Removed GApp::debugMode options, removed "debug" prefix from most fields. + <li> G3D::zipfileExists for testing if a filename path contains a .zip to be opened [Eric] + <li> G3D::isZipfile tests the header of a file to see if it is a .zip [Eric] + <li> G3D::zipRead and G3D::zipClose to open and close .zip files [Eric] + <li> G3D::fileExists supports filename paths that contain .zip [Eric] + <li> G3D::fileLength supports filename paths that contain .zip [Eric] + <li> G3D::getFiles and G3D::getDirs support filename paths that contain .zip [Eric] + <li> Texture::isSupportedImage - static method, returns true if a filename exists and is compatible with Texture [Eric] + <li> Viewer (tool) - Allows drag and drop viewing of many supported file formats [Eric] + <li> GFXMeter (tool) - Updated for 7.00 compatibility: GApplet structure removed [Eric] + <li> OSWindow::Settings has an allowMaximize field. Win32Window will have an activated 'Maximize' button if true [Eric] + <li> GApp2::debugCamera -> GApp2::defaultCamera + <li> GApp2::debugController -> GApp2::defaultController + <li> Made GApp2::debugText private + <li> Added g3d_WorldLight0 to the G3D shader extensions + <li> Added Shader support for GL_FLOAT_MAT3_ARB uniforms + <li> Tab (command completion) no longer auto-repeats in GConsole + <li> GConsole now limits the key repeat rate to the framerate + <li> Removed GApp::Settings::useNetwork because it was no longer needed--Win32 does not trigger firewall checks anymore + <li> Optimized AABSPTree balance and queries; now about 20% faster than 6.10, but requires 30 bytes more memory per member + <li> G3D::Array::pop now shrinks the underlying array by default (Array::popRemove does not shrink the array and is faster) + <li> Fast O(<i>n</i>), non-destructive G3D::Array::partition and G3D::Array::medianPartition + <li> Increased ReliableConduit read attempts before timeout from 10 to 100 + <li> G3D::GImage::pixel1 now returns G3D::Color1uint8* + <li> G3D::Color1, G3D::Color1uint8 + <li> G3D::Image1, G3D::Image1uint8 + <li> G3D::Image3, G3D::Image3uint8 + <li> G3D::Image4, G3D::Image4uint8 + <li> BinaryInput::flipEndian32, BinaryInput::flipEndian16 + <li> Texture::createEmpty now intializes invertY = true, which is usually desirable for Framebuffer rendering. + <li> G3D::Map2D + <li> G3D::Vector4int8 + <li> G3D::PointShape + <li> G3D::GImage::RGBAtoRGB + <li> Added GLEW compatibility [nico] + <li> Added data-files directory to the locations searched for G3D demo data + <li> On Win32, assertions now print ot the Output window as well as the dialog box. + <li> On Win32, $TEMP is now used for the logfile location instead of c:\tmp + <li> G3D::GKey replaces old SDL key enumeration + <li> Decreased memory requirements and increased balance speed of G3D::AABSPTree by adding a level of indirection + between tree nodes and the data stored in them. + <li> Added OSWindow::Settings::caption + <li> <b>Win32 programs must call the macro <b><code>G3D_START_AT_MAIN();</code></b> at top-level if they do not define WinMain themselves.</b> + <li> Win32 switched from MBCS to UNICODE for the binaries (G3D sources compile under either, but UNICODE is the VC8 default) + <li> Replaced SDL event types with G3D event types + <li> Added support for GL_ARB_point_sprite + <li> Win32 programs must call the macro <b><code>G3D_START_AT_MAIN();</code></b> at top-level if they do not define WinMain themselves. + <li> Win32 switched from MBCS to UNICODE for the binaries (G3D sources compile under either) + <li> Changed G3D::GApp::main to return an int + <li> Added header support for GL_EXT_geometry_shader4 ("Geometry shaders") + <li> Changed IFSModel::create to IFSModel::fromFile + <li> G3D::BSPMap for loading Quake3 .bsp files + <li> G3D::TextInput::Settings::caseSensitive + <li> G3D::TextInput::readBoolean, G3D::TextInput::Settings::trueSymbols, G3D::TextInput::Settings::falseSymbols, G3D::TextOutput::Settings::trueSymbol, G3D::TextOutput::Settings::falseSymbol, G3D::TextOutput::writeBoolean, G3D::Token::BOOLEAN_TYPE + <li> G3D::TextInput::Settings::msvcSpecials now defaults to true + <li> Made the input to G3D::tessellateComplexPolygon a constant array reference + <li> Removed SDL from Win32 build + <li> Increased maximum ReliableConduit message size to 60 MB + <li> Removed error macro + <li> G3D::GConsole + <li> Rect2D::lerp + <li> Added GL_EXT_packed_depth_stencil + <li> Added G3D::ImageFormat::DEPTH24_STENCIL8 packed stencil mode + <li> getOpenGLState now includes GL_LIGHT_MODEL_TWO_SIDE value + <li> Made ThirdPersonManipulator constructor protected (use ThirdPersonManipulator::create now) + <li> G3D::ToneMap + <li> Changed Renderbuffer::createEmpty argument order to match Texture::createEmpty + <li> G3D::IFSModel can now remove degenerate faces on load + <li> G3D::MD2Model::textureFromFile + <li> Replaced AABSPTree::beginRayIntersection with simpler AABSPTree::intersectRay interface. + <li> G3D::MD2Model now uses floating point texture coordinates, which makes it easier to + write pixel shaders for MD2Models + <li> G3D::GImage::computeNormalMap now accepts a scale factor indicating how steep the input bump map is + <li> Added G3D::Shader support for the GLSL #version directive + <li> Optimized G3D::GImage::computeNormalMap to use primarily integer math + <li> G3D::AABox::contains(AABox) + <li> Added large file (>2GB) support to BinaryInput [Peter] + <li> Renamed graphics3d.h to G3D/G3D.h + <li> Renamed GLG3D.h to GLG3D/GLG3D.h + <li> Renamed G3DAll.h to G3D/G3DAll.h + <li> G3D::Quat::unitize now normalizes in place + <li> Added G3D::Quat::operator*= + <li> Removed G3D::FirstPersonManipulator's constructor--use the static G3D::FirstPersonManipulator::create method now. + <li> Changed BSPMap leaf bounds from Box to AABox--50% improvement in frustum culling speed + <li> Optimized BSPMap rendering performance + <li> Removed all deprecated APIs + <li> All accessors of the form "getXXX" that take no arguments are now named just "xxx" + <li> G3D::Sky::create is now named G3D::Sky::fromFile and no longer accepts a G3D::RenderDevice. G3D::Sky::render now requires a RenderDevice. + <li> G3D::GFont::fromFile no longer accepts a G3D::RenderDevice and G3D::GFont::draw2D now requires a RenderDevice. + <li> Removed an assertion in BinaryInput requiring that compressed buffers be copied on construction [Nick Bray] + <li> Removed CoordinateFrame::zLookDirection (use -1) + <li> Removed Capsule::endPoint (use point) + <li> Removed CoordinateFrame::getStrafeVector (use rightVector) + <li> Removed static constants (use equivalent lower-case methods) + <li> Removed Cylinder::getPoint1 (use point) + <li> Fix: BSPMap::getStartingPosition now works correctly on all maps [Jeff] + <li> Fix: Workaround for ATI drivers that do not support zero-area scissor region + <li> Fix: GL_EXT_texture_3D -> GL_EXT_texture3D + <li> Fix: ARB_texture_cube_map and EXT_texture_cube_map are now aliases on all cards + <li> Fix: EXT_texture_edge_clamp and SGIS_texture_edge_clamp are now aliases on all cards + <li> Fix: G3D::Sky now turns off lighting + <li> Fix: Win32Window now returns mousebutton and motion events according to the GEvent spec + <li> Fix: Win32Window now operates correctly in fullscreen mode + <li> Fix: Fixed TGA decode to load from the middle of a binaryinput + <li> Fix: Added texture coordinates to posed MD2Models + <li> Fix: prompt/debugAssert now correctly responds to button presses on OS X + <li> Fix: GConsole now filename completes after the first word + <li> Fix: RenderDevice::getDepthBufferValue now checks for the presence of a depth buffer. + <li> Fix: G3D::AABSPTree now correctly handles members with infinite bounds on ray intersection tests + <li> Fix: Removed use of tmpfile on Unix + <li> Fix: Java ReliableConduit now properly waits if the buffer is full when sending + <li> Fix: [ 1607693 ] Triangles/Second display is now correct (rates were too low in 6.10) + <li> Fix: NetworkDevice now does not perform a test broadcast during initialization + <li> Fix: G3D::LightweightConduit::ok is now false if any error occurs during initialization + <li> Fix: BSPMAP for cards without glMultiDrawElementsEXT + <li> Fix: [ 1584335 ] ReliableConduit incorrectly assumes it's ok + <li> Fix: RenderDevide::push2D no longer resets the frameBuffer. + <li> Fix: Framebuffer logic for counting number of attachments was broken + <li> Fix: [ 1581986 ] Matrix3::fromAxisAngle now normalizes the input vector + <li> Fix: [1490643] Linux/FreeBSD/OS X binaries are now compiled with -fno-strict-aliasing, which fixes some + memory corruption problems that occurred with full optimizations. + <li> Fix: Fixed all warnings on gcc-4.1 and VC8. + <li> Fix: Matrix and CoordinateFrame serializers inside G3D::AnyVal dropped data + <li> Fix: [ 1535759 ] valgrind finds initialization/deletion errors in TextOutput, Matrix [Chris Demetriou] + <li> Fix: Patched MD2Model to automatically reduce animation times to less than 100000; large time were overflowing double->int conversion and causing animations to appear scrambled. + <li> Fix: [ 1535292 ] global Table hashCode overloads broken [Chris Demetriou] + <li> Fix: [ 1535736 ] Fixed System.cpp memory allocator to compile on 64-bit machines correctly [Chris Demetriou] + </ul> + + <hr> + <P> + Changes in 6.10: + <UL> + <LI> Optimized G3D::CoordinateFrame::pointToObjectSpace to be fully inlined via left-multiplication + <LI> G3D::Matrix4::orthogonalProjection from a G3D::Rect2D + <LI> G3D::RenderDevice::swapBuffers + <LI> G3D::AnyVal + <LI> G3D::ThirdPersonManipulator + <LI> Added support for GL_SGIS_texture_lod in Texture. + <LI> Fix: [ 1490655 ] MeshAlg::Edge::containVertex goes off the end of the array + <LI> Fix: [ 1511729 ] NVIDIA rectangle generates errors in mipmap code + <LI> Fix: [ 1507296 ] RenderDevice must swapBuffer on resize + </UL> +<hr> + <P> + Changes in 6.09: + <UL> + <LI> glDepthBoundsEXT + <LI> G3D::Quat::sameRotation + <LI> Full loading of the GL_ATI_separate_stencil extension, support within RenderDevice + <LI> platform.h undefines WIN32_LEAN_AND_MEAN, NOMINMAX after it has defined them + <LI> G3D::Texture::Settings::maxMipMap + <LI> Renamed Texture::Parameters to Texture::Settings (backwards compatible typedef added) + <LI> Optimized IFSModel rendering by increasing internal VAR cache size and reducing the number of state changes. + Can now render more than 1000 IFSModels at 30 fps on GeForce 7800. + <LI> G3D::System::mallocStatus + <LI> Range checking on Vector2int16::operator[] + <li> GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC, GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC + <LI> IFSModel and MD2Model now allocated their posed models using System::malloc + <LI> Increased the memory maintained by G3D::System for buffer pools up to a total of 13 MB: + 8 MB tiny (preallocated), 1 MB small (on demand), 4 MB medium (on demand). This was observed to + dramatically increase performance (15x) in real programs that were + performance limited by memory allocation time. + <LI> NetworkDevice now uses Winsock2.0 on Windows (controlled by the G3D_WINSOCK_MAJOR_VERSION/G3D_WINSOCK_MINOR_VERSION settings in NetAddress.h) + <LI> G3D::Manipulator + <LI> G3D::GApplet now runs installed G3D::GModules (except for graphics, which is left to the progrmamer) + <LI> G3D::GApp::addWidget, G3D::GApplet::addWidget, G3D::GApp::removeWidget, G3D::GApplet::removeWidget + <LI> G3D::Widget, G3D::WidgetManager + <LI> G3D::System::getEnv() + <LI> G3D::PosedModel2D + <LI> G3D::DXCaps + <LI> Increased precision of several Quat operations + <LI> G3D::Quat::fuzzyEq + <LI> G3D::Quat::operator- + <LI> G3D::LineSegment::length, G3D::LineSegment::point + <LI> Increased fuzzyEpsilon by a factor of 10 to take into account the new float32 focus of the APIs + <LI> G3D::RegistryUtil + <LI> G3D::LineSegment2D + <LI> G3D::ConvexPolygon2D + <LI> G3D::AxesShape + <LI> contrib/shaders/showDepth + <LI> G3D::Crypto with MD5 and CRC32 hashes + <LI> TextureManager::findTexture, TextureManager::cacheTexture [Erik] + <LI> Win32Window::_directInput created on-demand [Erik] + <LI> WeakReferenceCountedPointer has more comparison operators [Erik] + <LI> GImage::resolveFormat utility function [Erik] + <LI> GLCaps supports MESA + <LI> G3D::Win32Window and G3D::SDLWindow now release input capture and make the mouse visible on destruction + <LI> G3D::OSWindow::setInputCaptureCount, G3D::OSWindow::inputCaptureCount, G3D::OSWindow::incInputCaptureCount, G3D::OSWindow::decInputCaptureCount + <LI> GImage::makeCheckerboard + <LI> G3D::Vector3::one() + <LI> G3D::Shader now supports g3d_size(sampler2D) and g3d_invSize(sampler2D) extensions in GLSL shaders. + <LI> Renamed GAppSettings to G3D::GApp::Settings (old name is supported but deprecated) + <LI> Renamed GWindowSettings to G3D::OSWindow::Settings (old name is supported but deprecated) + <LI> Renamed TextInput::Options to G3D::TextInput::Options (old name is supported but deprecated) + <LI> G3D::FPManualController::setAutoActive for World of Warcraft style controller + <LI> G3D::isSlash, G3D::isQuote + <LI> G3D::GApplet::onEvent can now consume (i.e., prevent G3D::GApp from seeing) the event + <LI> G3D::CoordinateFrame::fuzzyIsIdentity, G3D::CoordinateFrame::isIdentity, G3D::CoordinateFrame::fuzzyEq + <LI> Matrix3::isOrthonormal + <LI> [1421201] Removed excess gl (NVIDIA) headers + <LI> Win32Window destructor now releases the mouse if it was captured and the current GL context is that window and the window was not created from an existing HDC/HWND + <LI> Fix: com.graphics3d.g3d.ReliableConduit now correctly selects on the waiting socket + <LI> Fix: [ 1166057 ] AABSPTree::beginBoxIntersection + <LI> Fix: GLCaps::supports(ImageFormat) now returns correct results on all cards + <LI> Fix: Shadow map rendering of default PosedModels now enables lighting + <LI> Fix: G3D::UserInput now restores the mouse position after pureDeltaMouse is turned off + <LI> Fix: G3D::Win32Window now clips precisely to the client area during an input grab. + <LI> Fix: [ 1383042 ] free static variables on shutdown + <LI> Fix: [1449115 ] Texture loading for odd-byte rows + <LI> Fix: G3D::Win32Window now produces correct character and scan codes for key events + <LI> Fix: G3D::GApplet::onEvent calls GApplet::processEvent by default + <LI> Fix: [ 1444320 ] TextInput parsed ".1" as "1" instead of "0.1" + <LI> Fix: G3D::Shape::type is now const + <LI> Fix: 0 --> 0.0f FrameBuffer.h [Erik] + <LI> Fix: Fixed Texture read-back dimensions for cube-map + <LI> Fix: Missing #include in SkyParameters.h [Erik] + <LI> Fix: Quad triangle counts are now accurate (were off by factor of 4 in 6.08) + <LI> Fix: contrib/ArticulatedModel now correctly masks all components using the diffuse alpha in fixed function mode + <LI> Fix: G3D::CoordinateFrame::getHeading was flipped front-to-back + <LI> Fix: [ 1404487 ] Missing Alt key up/down events on Win32 + <LI> Fix: [ 1484924 ] collisionTimeForMovingPointFixedBox normals + </UL> + <P> +<hr> + Changes in 6.08: + <UL> + <LI> Moved Win32 linker statements out of platform.h for IncrediBuild compatibility. + <LI> G3D::Texture and G3D::Sky now accept a rescaling factor + <LI> Added GFont::fromMemory() [Corey] + <LI> Added optional argument to Quat::slerp() for slerp/lerp angle threshold. [Corey] + <LI> Across-the-board performance optimizations. Most apps should render 10% faster. + Includes removal of Milestones when using VBO VAR [Nick Bray], GFont::draw2D and + Draw::rect2D stripped down to raw OpenGL, consistent internal use of float, + increased RenderDevice state change optimization. + <LI> Minimized header interdependencies (GLG3D headers no longer include all of G3D) + <LI> Added GThread and GMutex classes. [Corey] + <LI> Added ImageFormat::fromCode(). [Corey] + <LI> Added Plane::distance() and Plane::closestPoint() helper methods. [Corey] + <LI> G3D::ImageFormat::code, G3D::ImageFormat::colorSpace + <LI> <B>incompatible change</B> G3D::MeshAlg::computeTangentSpace basis now computes a right-handed coordinate frame, + where the binormal direction is the negative of the direction it faced in G3D 6.07. + <LI> Exposed G3D::RenderDevice::beforePrimitive and G3D::RenderDevice::afterPrimitive to end-user code for + integrating raw OpenGL calls. + <LI> G3D::Framebuffer and G3D::Renderbuffer to implement the Framebuffer_object extension [Dan Hilferty] + <LI> G3D::Shader::hasArgument + <LI> G3D::Texture::getImage + <LI> Changed SECOND, MINUTE, DAY, HOUR, SUNRISE, SUNSET, MIDNIGHT, METER, KILOMETER to enum values instead of #defines + <LI> G3D::Texture::Parameters; deprecated most Texture constructors in favor of ones that use this class + <LI> Moved most image manipulation routines into GImage. + <LI> G3D::GImage now allocates the underlying buffer in multiples of bytes to allow slight overflor for MMX algorithms + <LI> G3D::GImage::BAYER_R8G8_G8R8_to_R8G8B8_MHC + <LI> G3D::GImage::R8G8B8_to_Y8U8V8 + <LI> G3D::GImage::Y8U8V8_to_R8G8B8 + <LI> G3D::GImage now supports PPM binary + <LI> Various Rect2D helpers [Nick Bray] + <LI> ConvexPolyhedron improved clipping [Nick Bray] + <LI> G3D::System::build + <LI> G3D::System::calloc + <LI> G3D::GImage::convertToRGBA + <LI> contrib/AVI can read most AVI files on Windows. + <LI> contrib/wxGWindow now uses wxWidgets 2.6.2 + <LI> G3D_DEBUG now controls whether debug code is enabled; it defaults to the value of _DEBUG + <LI> zlib upgraded to 1.2.3 [Corey] + <LI> zlib now statically linked on Win32 (no longer requires zlib1.dll at runtime) [Corey] + <LI> G3D::MeshShape + <LI> Changed std::string hashCode to use CRC32 to reduce collisions + <LI> G3D::crc32 + <LI> Added occlusion query #defines [Nick Bray] + <LI> G3D::Win32Window now shares textures and vertex buffers across all GL contexts + <LI> G3D::Win32Window now enforces single-threading among GL contexts + <LI> G3D::GLCaps::slowVBO + <LI> G3D::VARArea now uses main memory vertex buffers on cards with slow VBO implementations. + <LI> G3D::Matrix3::toString [Peter] + <LI> G3D::Matrix4::toString [Peter] + <LI> G3D::Color3::fromHSV [Peter] + <LI> G3D::Color3::toHSV [Peter] + <LI> G3D::Color3::jetColorMap [Peter] + <LI> Optimized G3D::iRound (now faster than casting!) + <LI> G3D::MD2Model::create now accepts a scale factor + <LI> #G3D_DEPRECATED macro + <LI> #G3D_CHECK_PRINTF_ARGS, #G3D_CHECK_VPRINTF_ARGS macros to allow + checking of printf argument strings under gcc at compile time with + -Wformat. + <LI> G3D::TextInput::filename + <LI> G3D::TextInput::Options::msvcSpecials + <LI> G3D::TextInput::Options::startingLineNumberOffset + <LI> G3D::TextInput::readSymbolToken [cgd] + <LI> G3D::TextInput::readStringToken [cgd] + <LI> G3D_DEPRECATED macro + <LI> Threadsafe G3D::ReferenceCountedPointer + <LI> G3D::AtomicInt32 + <LI> G3D::GThread [Corey] + <LI> G3D::Array::popDiscard + <LI> Optimized multi-argument Array::append + <LI> G3D::GFont 2x faster than in G3D 6.07 + <LI> G3D::RenderDevice::pushState 2x faster than in G3D 6.07 + <LI> G3D::RenderDevice::pushState no longer stores GL texgen and fog information + <LI> G3D::Draw::fastRect2D + <LI> G3D::System::outOfMemoryCallback + <LI> G3D::Queue::fastClear [Chris Demetriou] + <LI> G3D::Rect2D::x0y1 and x1y0 + <LI> G3D::GLCaps bug tests now run in a separate GL context [Erik Cassel] + <LI> G3D::GApplet tracks real and simulation time. + <LI> contrib/Q3Map updated to correctly render instanced objects [Alex Rice] + <LI> G3D::OSWindow subclasses now required to invoke OSWindow::loadExtensions + <LI> G3D::Quat::log for non-unit quats and for real-only quats. + <LI> G3D::GApplet::doUserInput + <LI> G3D::GApp prints time for each component + <LI> G3D::Stopwatch + <LI> G3D::OSWindow::renderDevice() + <LI> G3D::OSWindow::current() + <LI> G3D::GLCaps::hasBug_redBlueMipmapSwap and workaround for G3D::Texture on Radeon 7500 + <LI> Fix: CollisionDetection::penetrationDepthForFixedSphereFixedPlane() contact point and normal values. [Corey] + <LI> Fix: Quat::slerp has invalid shortest path [Corey] + <LI> Fix: G3D::drawFeatureEdges now uses correctly normalized face edges (and offers a crease angle) + <LI> Fix: G3D::SDLWindow now releases the mouse on Linux during an assertion. + <LI> Fix: All keys are reset to up when Win32Window loses focus. [Corey] + <LI> Fix: gaussRandom is unit gaussian [Corey] + <LI> Fix: [ 1418276 ] 6.08: Unsupported format for depth texture + <LI> Fix: Ignoring extra/unused set Shader arguments. [Corey] + <LI> Fix: [ 1229205 ] uniform texture array (Could not set indexed array uniforms). [Corey] + <LI> Fix: <B>incompatible change</B> BinaryInput/BinaryOutput copy constructors and assignments were accessible. [Corey] + <LI> Fix: RenderDevice::screenshotPic would corrupt GImage's heap. [Corey] + <LI> Fix: Alt-Tab window switching caused an invalid Alt key state. [Corey] + <LI> Fix: Incorrect window size event in Win32Window sent to OpenGL. [Corey] + <LI> Fix: [ 1227915 ] Textures don't bind on ATI under GLSL. + <LI> Fix: [ 1358477 ] ray-plane intersection bug [Dan Keefe] + <LI> Fix: [ 1370665 ] hash_map moved to stdext in VC8 (2005) + <LI> Fix: ToneMap extended to use DIM_2D_NPOT instead of DIM_2D_RECT + <LI> Fix: Texture::copyFromScreen now works with DIM_2D_NPOT textures + <LI> Fix: Wrapped debugAssertM in do {} while (0) to ensure correct compilation in single-line statements [ERik Cassel] + <LI> Fix: G3D::Draw::cylinder now renders the bottom correctly + <LI> Fix: Array::front now compiles under gcc + <LI> Fix: G3D::Ray::distance used to measure against the origin [David] + <LI> Fix: [ 1293151 ] ArticulatedModel clipping on Radeon -- disabled auto-mipmap generation on mobile radeon 9xxx + <LI> Fix: G3D::TextInput now parses ^=, character 255 correctly [cgd] + <LI> Fix: G3D::TextInput now reports line numbers correctly with raw newlines [cgd] + <LI> Fix: .ICO files with transparency loaded incorrectly [Corey] + <LI> Fix: G3D::Draw::rect2DBorder inner border was 1 pixel too thick. + <LI> Fix: [ 1326173 ] Win32Window::init should call makeCurrent.[Erik Cassel] + <LI> Fix: [ 1326423 ] G3D::Queue::_copy broken [Chris Demetriou] + <LI> Fix: [ 1313293 ] 6.08: TextInput gets symbol extendedType() wrong [Chris Demetriou] + <LI> Fix: IFSModel::save, for PLY2 forgot newlines [Peter] + <LI> Fix: Quat(Matrix3) now computes trace correctly (gave negative quats in some cases) + <LI> Fix: Setting RenderDevice::polygonOffset now always produces a depth shift, + even for faces perpendicular to the view axis. + <LI> Fix: GImage now auto-resolves formats for files with 1 character base names + <LI> Fix: WeakReferenceCountedPointer cycle bug + <LI> Fix: Corrected lag encountered when using some ReliableConduit constructors [Dan Keefe] + </UL> + + <P> +<hr> + Changes in 6.07: + <UL> + <LI> G3D::OSWindow::makeCurrent + <LI> Win32 release binaries now built with no debug information (used to have line numbers) + <LI> AABox::AABox enforces the constraint low <= high + <LI> Optimized G3D::Array, Table, Queue, and Set for performance. Now significantly (up to 10x) faster + than their std::counterparts. + <LI> G3D::Vector3(Vector2, float) constructor + <LI> G3D::Vector2::fastDirection + <LI> G3D::TextInput::Options::cComments + <LI> G3D::TextInput::Options::escapeSequencesInStrings + <LI> G3D::TextInput::Options::otherCommentCharacter2 + <LI> G3D::TextInput::WrongString + <LI> GLCaps::supports_GL_ATI_separate_stencil + <LI> GLCaps can now test a card/driver and detect specific bugs: + <ul><li>G3D::GLCaps::hasBug_glMultiTexCoord3fvARB + <LI> G3D::GLCaps::hasBug_normalMapTexGen + </ul> + <LI> G3D::ReferenceCountedPointer::downcast for non VC6 compilers + <LI> Improved G3D::ReferenceCountedPointer documentation to make subclassing features clearer + <LI> Moved typedef for uint into G3D namespace and into g3d (was in glg3d) + <LI> G3D::Shape + <LI> G3D::Cylinder + <LI> G3D::System::malloc, G3D::System::realloc, G3D::System::free for fast allocation of small objects + <LI> G3D::Draw::plane + <LI> G3D::Draw::cylinder + <LI> G3D::gaussRandom + <LI> GCamera deserialize(BinaryInput) & serialize(BinaryOutput) functions [Peter] + <LI> G3D::GApp now writes a description of the whole system to the log to aid debugging. + <LI> [ 1217928 ] OpenGL occlusion query entry points are loaded on initialization + <LI> New texture interpolation modes: BILINEAR_MIPMAP, NEAREST_MIPMAP, NEAREST_NO_MIPMAP + <LI> New texture formats: + <UL> + <LI> G3D::ImageFormat::L16; + <LI> G3D::ImageFormat::L16F; + <LI> G3D::ImageFormat::L32F; + <LI> G3D::ImageFormat::A16; + <LI> G3D::ImageFormat::A16F; + <LI> G3D::ImageFormat::A32F; + <LI> G3D::ImageFormat::LA4; + <LI> G3D::ImageFormat::LA16; + <LI> G3D::ImageFormat::LA16F; + <LI> G3D::ImageFormat::LA32F; + <LI> G3D::ImageFormat::RGB16; + <LI> G3D::ImageFormat::RGB16F; + <LI> G3D::ImageFormat::RGB32F; + <LI> G3D::ImageFormat::RGBA16; + <LI> G3D::ImageFormat::RGBA16F; + <LI> G3D::ImageFormat::RGBA32F; + </UL> + <LI> isValidPointer and isValidHeapPointer no longer check the Win32 debug heap in order to support offset and padded memory blocks. + <LI> Restructured unit tests + <LI> G3D::CoordinateFrame::lookRay [David Baszucki] + <LI> G3D::System::describeSystem, G3D::NetworkDevice::describeSystem, G3D::RenderDevice::describeSystem + <LI> G3D::Array performance tuning for short arrays and arrays of small objects + <LI> Added glext.h entries for GL_ARB_draw_buffers, GL_ARB_texture_rectangle, + GL_ARB_color_buffer_float, GL_ARB_half_float_pixel, GL_ARB_texture_float, + and GL_ARB_pixel_buffer_object extensions + <LI> IFSModel::create added weld option, defaults to true (to keep compatibility). [Peter] + <LI> G3D::RenderDevice::alphaTestReference, RenderDevice::alphaTest + <LI> G3D::VAR::set + <LI> G3D::Log::vprintf + <LI> G3D::WeakReferenceCountedPointer + <LI> GCC 4.0 build support added [Corey] + <LI> G3D::CoordinateFrame::lookAt now gives a valid output even when look == up + <LI> contrib/GChunk + <LI> GLCaps now loads GL_EXT_framebuffer_object functions + <LI> Added MSVC 6 support for C99 restrict keyword + <LI> G3D::Win32Window properly resizes viewport on window resize [Corey] + <LI> G3D::BinaryFormat, G3D::byteSize, G3D::binaryFormatOf + <LI> Removed dead ManualCameraControllerHelper code + <li> Added consistent area and volume methods to geometric primitives, deprecated old methods. + <LI> Fast G3D::BinaryInput::read / G3D::BinaryOutput::write methods for arrays + <LI> Enabled cube mapping on Radeon mobility cards and added a workaround to the known problems with texcoords on those cards. + <LI> Can now create G3D::Win32Window with existing HWND and HDC [Corey] + <LI> G3D::VertexAndPixelShader::ArgList::set(std::string, Array<T>)-- [ 1192401 ] Shader support arrays + <LI> Fix: SDLWindow used std::string's instead of C strings in printf and format inside some exception handling code. [Peter] + <LI> G3D::X11Window (same as SDLWindow in this release) + <LI> Fix: [ 1277854 ] Win32Window fails on 24-bit modes + <LI> RFE: [ 1242466 ] Inline Matrix3 methods + <LI> Fix: [ 1226272 ] end caps of capsules in wrong position + <LI> Fix: G3D::ImageFormat::LA8 now has 8-bits per channel + <LI> Fix: [ 1124491 ] Remove GL_SAMPLER_2DRECT_ARB + <LI> Fix: [ 1257113 ] G3D::Queue problems comining pushFront and pushBack + <LI> Fix: MeshAlg::Weld now linear time (was O(n^2) due to a bug) + <LI> Fix: [ 1298873 ] fast & correct CoordinateFrame::lerp + </UL> + + <P> +<hr> + Changes in 6.06: + <UL> + <LI> G3D::Lighting::emissiveScale + <LI> G3D::RenderDevice::drawBuffer + <LI> G3D::RenderDevice::debugNumMinorStateChanges, debugNumMinorOpenGLStateChanges, debugNumMajorStateChanges, debugNumMajorOpenGLStateChanges. + <LI> In stereo mode, Texture::copyFromScreen automatically chooses the left/right buffer to read based on the current glDrawBuffer + <LI> contrib/ArticulatedModel/ToneMap + <LI> Lazy state changes for shaders + <LI> 50% performance improvement for G3D::BinaryInput, G3D::BinaryOutput when machine endian matches file endian + <LI> Textures load with default of maxAnisotroy = 2.0 + <LI> maxAnisotropy argument to G3D::Texture constructors. + <LI> GLCaps now loads GL_ATI_fragment_shader extension + <LI> contrib/ArticulatedModel now supports rigid body hierarchies + <LI> Added TEX_SUBTRACT, TEX_ADD_SIGNED, TEX_DOT3, TEX_DOT3_RGBA modes for G3D::RenderDevice::setTextureCombineMode + <LI> G3D::RenderDevice now cleans up all static G3D::VARArea s when it shuts down + <LI> FIX: [ 1208157 ] GLSL slow on ATI + <LI> FIX: Off-by-one on viewport scale for 2D rendering + <LI> FIX: MeshAlg::computeTangentSpaceBasis now works correctly + <LI> FIX: 6.05 enabled all fixed function lights by default. This caused major performance problems on some cards. + <LI> FIX: Extended cube map workaround to all Radeon Mobility cards + <LI> FIX: Added check for glBlendEq before calling in RenderDevice + <LI> FIX: Added a test for GL_EXT_texture_env_add in RenderDevice + <LI> FIX: [ 1191817 ] unsigned warnings in BinaryInput + </UL> + +<hr> + <P> + Changes in 6.05: + <UL> + <LI> G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC + <LI> G3D::Quarter_R8G8B8_to_BAYER_G8B8_R8G8 + <LI> G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8 + <LI> contrib/Matrix + <LI> contrib/Java + <LI> Texture::alphaOnlyVersion + <LI> Draw::sphere speed improved over 25% with single quad strip (improves Draw::capsule) [Corey] + <LI> Allow 1-channel GImage saving - BMP (expanded to RGB), PNG [Corey] + <LI> Allow 1-channel GImage loading - PNG [Corey] + <LI> Added shader and framebuffer extensions to glext.h + <LI> All files used during current execution are available via G3D::getFiles() [Corey] + <LI> Implemented OSX version of glGetCurrentContext with CGL. [Corey + Derek] + <LI> ReferenceCountedObject is-in-heap checks were removed to allow better multiple and virtual inheritance for reference counted objects. ReferenceCountedPointer still appropriately checks does an is-in-heap check on assignment. [Corey] + <LI> Added Dev C++ compatability + <LI> glGetAttribLocationARB + <LI> Changed GLight == operator to not use memcpy (was causing issues due to byte padding on some compilers) + <LI> Made CoordinateFrame destructor non-virtual (eliminates vtable) + <LI> Added new FAQ documentation + <LI> Added support to G3D::BinaryInput and G3D::BinaryOutput + reading and writing huge (larger than available memory) files. + Files are still restricted to about 2 GB total, and compressed + files must fit entirely in memory. + <LI> Tweaked allocation strategy for small G3D::Array + <LI> G3D::Texture::rect2DBounds, G3D::Texture::vector2Bounds + <LI> G3D::Vector4 * G3D::Vector4, Vector4 / Vector4 + <LI> G3D::Array::operator=(std::vector) + <LI> G3D::Sky::getEnvironmentMap now returns the top texture on machines + that don't support cube maps. + <LI> glDisableAllTextures() + <LI> G3D::setFailureHook + <LI> G3D::Shader::fromStrings now accepts optional names for the vertex and pixel shader + <LI> G3D::Shader no longer requires values for declared but unused uniform variables + <LI> G3D::RenderDevice now stores texture matrix at 32-bit precision (for faster push/popState) + <LI> G3D::RenderDevice::setTextureLODBias + <LI> G3D::Shader now supports shadow map arguments + <LI> G3D::Shader::ArgList checks to see if Texture arguments are null + <LI> G3D::RenderDevice::setAlphaWrite now defaults to true if the OSWindow has an alpha channel. + <LI> G3D::RenderDevice::screenshotPic now supports alpha + <LI> contrib/VideoSerializer + <LI> G3D::BinaryOutput::writeBits, G3d::BinaryInput::readBits + <LI> G3D::Sky can now be initialized with a NULL renderDevice, provided a non-null one + is used with the G3D::Sky::render method. + <LI> G3D::pi(), G3D::halfPi(), G3D::twoPi() added to replace defines [Corey] + <LI> contrib/Q3Map + <LI> Increased G3D::Draw::sphere performance using vertex arrays. + <LI> G3D::Array::fastClear + <LI> G3D::AABSPTree::insert(Array<T>) + <LI> G3D::Texture::sizeOfAllTexturesInMemory + <LI> G3D::VARArea::sizeOfAllVARAreasInMemory + <LI> G3D::RenderDevice stores cameraToWorldMatrixInverse for faster coordinate system changes. + <LI> inlined G3D::Matrix3::operator= for performance + <LI> Created installer for Windows install [Corey] + <LI> Reorganized the documentation topic index based on abstraction level, added hyperlinks to demo/contrib code + <LI> G3D::ReliableConduit and G3D::LightweightConduit now send and receive + objects directly; no need to make a G3D::NetMessage. G3D::NetMessage + and associated methods are now deprecated. + <LI> Win32 GUI G3D::prompt now auto-expands \\n to \\r\\n in prompt string [Corey] + <LI> G3D::Draw::frustum + <LI> Increased timeout and attempts for G3D::ReliableConduit to handle huge (1 MB) packets + <LI> G3D::BinaryOutput::reset (memory writing only; not supported for disk) + <LI> Reduced overhead for G3D::ReliableConduit and + G3D::LightWeightConduit send routines + <LI> Added PPM/PGM/PBM ASCII encode/decode support to G3D::GImage [Corey] + <LI> New G3D::PosedModel rendering methods appropriate for shadow casting + (with efficient default implementations). + <LI> G3D::Lighting + <LI> Changed RenderDevice::TEX_INTERPOLATE to mean GL_DECAL and added TEX_BLEND for GL_BLEND + <LI> G3D::CoordinateFrame::upVector + <LI> G3D::GLight::diffuse + <LI> G3D::Rect2D::contains is now const + <LI> Rewrote G3D::BinaryOutput to not use G3D::Array + <LI> G3D::MD2Model::textureMatrix + <LI> G3D::MeshAlg::computeBounds(vertex, index, ...) + <LI> G3D::RenderDevice::colorWriteEnabled(), depthWriteEnabled, alphaWriteEnabled + <LI> G3D::RenderDevice::setSpecularCoefficient(Color3) + <LI> G3D::VAR::maxSize + <LI> G3D::RenderDevice::enableTwoSidedLighting + <LI> G3D::PosedModel::hasTransparency + <LI> G3D::PosedModel::sort + <LI> G3D::RenderDevice::renderMode + <LI> G3D::MeshAlg::computeNormals(geometry, indexArray); + <LI> contrib/ArticulatedModel (beta 3DS support) + <LI> G3D::RenderDevice::swapBuffersAutomatically allows caller to suppress page flip. + <LI> Added coordinate system documentation. + <LI> RenderDevice::enableClip2D, RenderDevice::disableClip2D (scissor region) + <LI> contrib/wxGWindow is stable and full featured-- use wxWidgets 2.5.3 with G3D! + <LI> G3D::fileIsNewer + <LI> G3D::isDirectory + <LI> G3D::filenameContainsWildcards + <LI> G3D::filenamePath + <LI> G3D::Draw::lineSegment now accepts a scale (allowing arrows and axes to thicken appropriately) + <LI> G3D::Rect2D::largestCenteredSubRect + <LI> G3D::Matrix4::serialize, G3D::Matrix4::deserialize + <LI> glTexImage3DEXT + <LI> Removed glut.lib and glut.dll from the win32-lib directory. + <LI> G3D::writeStringToFile, G3D::TextOutput, and G3D::BinaryOutput now flush by default (safe, not fast). + <LI> Shifted push2D by 0.375 pixels as recommended in the OpenGL guide to bias integer coords towards pixel centers + <LI> G3D::Draw::rect2DBorder + <LI> G3D::Rect2D::border + <LI> G3D::RenderDevice now creates a G3D::Win32Window on Windows instead of a G3D::SDLWindow. SDLWindow is now + deprecated on Windows. + <LI> G3D::VARArea now updates allocation sizes instead of G3D::VAR internally. Added + more accessor methods to VARArea to futher remove VAR from VARArea internals. [Corey] + <LI> VARSystem.cpp moved to VARArea.cpp - filename change only! [Corey] + <LI> Linux build system updated: + Builds only static libraries, Does not require libtool/libtoolize anymore, + Does not check for or require libraries that normally linked with the .so files, + Automatically builds Test project with iCompile during install. [Corey] + <LI> G3D::Quat::deserialize, G3D::Quat::serialize + <LI> G3D::PhysicsFrame::deserialize, G3D::PhysicsFrame::serialize + <LI> G3D::TextInput::Options::singleQuotedStrings (defaults to true, changing the behavior + from previous versions). + <LI> G3D::Token::extendedType returns information disambiguating characters and strings + and floats and ints. + <LI> Added data/ah64-body and ah64-rotor + <LI> demos/Network_Demo now uses a helicopter model instead of a plane + <LI> G3D::VARArea::gl_vertexBufferObject and G3D::VARArea::gl_basePointer for breaking + the VARArea abstraction. + <LI> GLG3D.h no longer links against SDLMain.lib on Windows if _CONSOLE is defined + (since console programs have no WinMain). + <LI> SDL's redefinition of main is cleared when not linking sdlmain.lib [Corey] + <LI> Moved contrib/Win32Window to G3D::Win32Window + <LI> G3D::TextInput::readSymbols + <LI> contrib/Image [Morgan] + <LI> contrib/wxGWindow [Morgan] + <LI> Added support for full-screen antialiasing to contrib/Win32Window + <LI> Added joystick support to contrib/Win32Window [Corey] + <LI> Win32Window fully-implements OSWindow [Corey] + <LI> Texture now supports DDS(2D/CubeMap) and PNG files [Corey] + <LI> Added Win32 pbuffer routines (no G3D wrapper, though-- we're waiting for the new ARB API). + <LI> G3D::PosedModel::texCoords + <LI> G3D::IFSModel now loads IFS 1.1 [Peter] + <LI> G3D::IFSModel now loads and saves PLY2 files (plain text IFS format) [Peter] + <LI> Automatically switch to glCompressedTexImage2D in G3D::Texture::fromMemory [Corey] + <LI> Added G3D::Sky::fromCubeMap for preloaded CubeMap Texture::Ref's [Corey] + <LI> Added G3D::Sky::fromFile and deprecated Sky::create [Corey] + <LI> Demo and Test projects now build with iCompile, which is included [Corey] + <LI> Fix: TextOutput::writeString now escapes special characters + <LI> Fix: AABSPTree::serializeStructure + <LI> Fix: Properly handle gl_ uniforms on Radeon for Shader + <LI> Fix: [ 875467 ] OS X debugBreak (requires default XCode debug menu item 'Break on DebugStr()') [Corey + Derek] + <LI> Fix: Can make a G3D::Texture::fromGImage with one channel (defaults to L8 format) + <LI> Fix: [ 1149972 ] 6.05: Make Sky render correctly on low-end cards (no Cube mapping) [Corey] + <LI> Fix: [ 1032742 ] OS X _DEBUG not defined [Derek] + <LI> Fix: 16-bit integer reads in BinaryInput that always reversed endianness. (OSX file reading) [Corey + Derek] + <LI> Fix: Matrix4 operator[] was returning a matrix value cast to a pointer [Corey] + <LI> Fix: Matrix3 and Matrix4 had missing float* / const float* operators [Corey] + <LI> Fix: Rect2D::clip broken for types other than Vector2 + <LI> Fix: RenderDevice::configureShadowMap result depends on objectToWorldMatrix + <LI> Fix: [ 1150650 ] DebugBreak() undefined + <LI> Fix: [ 1111534 ] Network Demo crashes starting 2nd server on same machine + <LI> Fix: [ 1102091 ] ReliableConduit::receive times out + <LI> Fix: Implemented MD2Model::objectSpaceBoundingX methods. + <LI> Fix: G3D::Triangle::area is now zero for zero-area triangles (was inf) + <LI> Fix: AABSPTree with extent on MSVC 6 no longer enters infinite loop in std::sort + <LI> Fix: [ 1105641 ] Does not build with g++ 3.4.x [Corey] + <LI> Fix: [ 1103619 ] RenderDevice::countPrimitive is wrong (changed to RenderDevice::countTriangles) [Corey] + <LI> Fix: AABSPTree::BoxIntersectionIterator doesn't compile + <LI> Fix: [ 1101680 ] copyfile won't overwrite (on Windows now overwrites) [Corey] + <LI> Fix: [ 1101646 ] GCamera::frustum incorrect for non-square viewport + <LI> Fix: Ultra bright lens flare at sunset [Nicholas Bray] + <LI> Fix: IP address strings were reversed by NetAddress(std::string) + <LI> Fix: TextInput now returns end of file token for files without trailing whitespace + <LI> Fix: [ 1094166 ] 6.05: Release mouse stuck on x-axis [Corey + Morgan] + <LI> Fix: Recognize buggy ATI Radeon Mobility cube maps and work around + <LI> Fix: Textures now initialize without setting error bit on cards without GL_ARB_shadow + <LI> Fix: filenameBaseExt now operates correctly on strings with both \ and / slashes. + <LI> Fix: [ 1062659 ] BinaryInput::BinaryInput() memory leak + <LI> Fix: Removed RenderDevice::polygonCount, which was never used. + <LI> Fix: TextInput::readNumber no longer accepts double preceeding +/- on numbers when Options::signedNumbers is true + <LI> Fix: [ 1038733 ] OSWindow cannot set icon properly [Corey] + <LI> Fix: [ 939400 ] Linux mouse set position (Wild camera swinging on startup) [Corey] + <LI> Fix: [ 1042591 ] Software GL Causes Assertion [Corey] + <LI> Fix: [ 1036634 ] debugAssert doesn't work on MSVC 7 [Corey] + <LI> Fix: [ 1049024 ] Fix compile warnings from gcc/Linux build [Corey] + <LI> Fix: [ 1051272 ] Win32Window doesn't use GWindowSettings properly. [Corey] + <LI> Fix: Win32Window clips the proper cursor region during input capture. [Corey] + <LI> Fix: GWindows now center and maximize on the primary monitor for Windows. + <LI> Fix: [ 1052945 ] TextOutput wordWrap starts on newlines + <LI> Fix: [ 1050957 ] TextInput readNumber support for capital 'E' numbers. + <LI> Fix: [ 1049674 ] TextInput failes on X. numbers. + <LI> Fix: [ 1044028 ] Linux TextOutput Warning + <LI> Fix: [ 1032750 ] Grayscale JPG errors [Corey] + <LI> Fix: [ 1036225 ] Encode TGA support strips alpha channel [Corey] + <LI> Fix: [ 1038631 ] CoordinateFrame::slerp (Quat::slerp has fix) [Corey] + <LI> Fix: [ 1033686 ] GImage::GImage(filename) dies on certain (BMP) images [Corey] + <LI> Fix: Texture mapping modes for pre-OpenGL 1.3 cards [Dan & Morgan] + </UL> + +<hr> + <P> + Changes in 6.04: + <UL> + <LI> G3D Manual! [ Morgan and Sascha ] + <LI> Initial MSVC7 build script. MSVC7 is not an officially supported platform + however the release contains MSVC7 precompiled binaries and the build script + will automatically build on both 6 and 7. + <LI> Improved performance of G3D::writeStringToFile + <LI> G3D::ReferenceCountedPointer assignment now allows compile time subtyping + <LI> G3D::ReferenceCountedPointer != operator + <LI> G3D::ReferenceCountedPointer::notNull + <LI> G3D::GLight::directional now normalizes the light vector + <LI> G3D::setAssertionHook + <LI> [ 1029256 ] G3D::Shader / G3D::VertexAndPixelShader define g3d_ uniforms inside shaders + <LI> static G3D::IFSModel::save/load for writing/reading IFS files + <LI> G3D::TextInput allows ' inside quoted strings + <LI> G3D::TextInput allows \ as a symbol token + <LI> G3D::TextInput supports an arbitrary comment character (e.g. '#') + <LI> Precompiled binaries for VisualC++ 7 (.NET 2002/2003) + <LI> VisualC++ 7 (.NET 2002/2003) supported by build script + <LI> Build now MOVEs binaries instead of COPYing them on Windows (allows + two compilers to output to the same location) + <LI> G3D Guide overview documentation + <LI> Changelog and Error FAQ moved under Doxygen + <LI> Build scripts and documentation now under the 'doc' .dsp on Windows + <LI> Textures now support a DepthReadMode that can be used to perform hardware + shadow map comparisions. <B>RenderDevice::configureShadowMap now requires + an appropriately configured texture-- in previous releases it would + reconfigure the texture for you.</B> + <LI> G3D::UserInput::keyReleased, G3D::UserInput::ReleasedKeys + <LI> G3D::Array::randomElement + <LI> G3D::Array::insert + <LI> G3D::RenderDevice::getObjectToWorldMatrix and getCameraToWorldMatrix now return + const CoordinateFrame& + <LI> Optimized G3D::Array::randomize + <LI> G3D::cyclicCatmullRomSpline + <LI> G3D::wrap + <LI> contrib/AudioDevice + <LI> G3D::System::time(); + <LI> More precise System::sleep + <LI> G3D::IFSModel::pose with no arguments + <LI> G3D::AABSPTree::serializeStructure, deserializeStructure, + <LI> serialize(Vector3::Axis, BinaryOutput), deserialize(Vector3::Axis, BinaryInput), + <LI> "glslc" GLSL compiler in the tools directory for getting compile-time errors from shaders + <LI> GLCaps::init now takes optional debug log + <LI> G3D::VertexAndPixelShader static constructors take optional 'debug' argument + <LI> GWindowSettings::visible; Win32Window can now start invisible + <LI> [ 991147 ] glBlendEquationEXT, RenderDevice::BlendEq, min, max, subtract, reverse subtract alpha blending + <LI> [ 989785 ] Draw::rect2D + <LI> GLCaps::numTextureCoords, GLCaps::numTextureUnits, GLCaps::numTextures + <LI> GLCaps::G3D_MAX_TEXTURE_UNITS + <LI> Rect2D::corner + <LI> GCamera::getFrustum, GCamera::frustum, GCamera::Frustum, GCamera::Frustum::Face + <LI> Plane constructor that accepts Vector4s (possibly at infinity) + <LI> AABox::inf, AABox::zero, AABox::maxFinite + <LI> AABox::intersects(Sphere) + <LI> Vector3::minFinite, Vector3::maxFinite + <LI> Plane::halfSpaceContainsFinite + <LI> Plane::halfSpaceContains(Vector4) + <LI> AABSPTree::getIntersectingMembers(Array<Plane>) + <LI> AABSPTree::getIntersectingMembers(GCamera::Frustum) for view-frustum culling + <LI> AABSPTree::getIntersectingMembers(Sphere) + <LI> AABox::split + <LI> Extended AABox::culledBy, Box::culledBy, and Sphere::culledBy with extra + information for bounding volume hierarchies + <LI> G3D::computeNormalMap + <LI> Matrix3::fuzzyEq(Matrix3) + <LI> Removed System::sleep(0.02) from GLG3D demo to give more accurate performance measure + <LI> [ 965824 ] changed link library defaults + <LI> serialize/deserialize for int, bool, double, float, std::string + <LI> G3D::TextOutput + <LI> [ 976924 ] Texture::texelWidth + <LI> [ 973413 ] VertexAndPixelShader::ArgList::set can be called more than once per variable + <LI> OSWindow::setIcon(std::string filename) + <LI> Texture::fromMemory that takes a single image (instead of an array of images) + <LI> [972604] RenderDevice::setTextureMatrix(uint, Matrix4) + <LI> [972747] Rect2D::center + <LI> GImage and Texture now load ICO files + <LI> GL_SAMPLER_1D_ARB, 2D, 3D, CUBE + <LI> Win32Window mouse events + <LI> Added normals to AABox collision results + <LI> Fix: [ 1026534 ]various cast bugs using Ref types. + Removed G3D::ReferenceCountedPointer implicit cast to underlying pointer type + This is technically an <B>incompatible change</B>, however we found no occurance + in the library or demos using this that was not a bug! + <LI> Fix: VAR constructor takes VARAreaRef instead of VARArea* <B>Incompatible change</B> + <LI> Fix: ManualCameraController is prevented from looking precisely along the Y-axis, which would cause + a singularity. + <LI> Fix: Added '?' as a valid symbol Token + <LI> Fix: [ 946235 ] GFont::align right w/ fixed_spacing + <LI> Fix: [ 1001033 ] RenderDevice with 0 texture units + <LI> Fix: GLCaps:: ARB stencil two side -> EXT stencil two side (stencilled shadows were broken) + <LI> Fix: [ 993449 ] vsnprintf crashes MSVC 7 + <LI> Fix: [ 991320 ] Pointer truncation Warnings + <LI> Fix: [ 981440 ] AUTO with Texture::fromMemory + <LI> Fix: Plane::halfSpaceContains now works for infinite and semi-infinite points + <LI> Fix: [ 979032 ] Quat <-> Matrix3 roundtrip inverts + <LI> Fix: [ 976743 ] document GLCaps functions + <LI> Fix: [ 976746 ] #include GLCaps in g3dall + <LI> Fix: [ 973550 ] sampler2DRect now supported in GLSL shaders (NVIDIA only; ATI drivers are broken) + <LI> Fix: [ 973490 ] Win32Window width/height off by non-client amount + <LI> Fix: [ 961827 ] In debug mode, RenderDevice tries to access + GL_MAX_TEXTURE_IMAGE_UNITS_ARB and an assertion fails on cards that + don't support it. + <LI> Fix: Texture binding for VertexAndPixelShader + </UL> +<hr> + <P> + Changes in 6.03: + <UL> + <LI> Matrix4::approxCoordinateFrame + <LI> Vector2(const Vector2int16&) [Giulio] + <LI> RenderDevice::setObjectShader + <LI> RenderDevice::setVertexAndPixelShader + <LI> G3D::RenderDevice supports "..._CURRENT" as an option for most settings + <LI> inf -> inf(), nan -> nan(), NAN -> NAN() + <B>This is an incompatible change-- it was needed to fix a bug with the order + of initialization of globals</B> + <LI> GImage::sizeInMemory + <LI> Defined std::ostream << NetAddress, std::ostream << Vector3 + <LI> 'build doc' copies the contrib directory to the install directory + <LI> LightweightConduit::PacketSizeException + <LI> Quat::unitRandom() [Giulio] + <LI> Color3::wheelRandom + <LI> GImage::save and encode now const [Thanks Arni Mar Jonsson] + <LI> LightweightConduit::send that accepts multiple destinations + <LI> ReliableConduit::multisend + <LI> Moved IFSBuilder from demos to contrib + <LI> LightweightConduit and ReliableConduit send/receive can now take references as well as pointers + <LI> RenderDevice::clear() that takes no arguments + <LI> RenderDevice::setShader + <LI> G3D::GApp now catches ShaderGroup::ArgumentError exceptions + <LI> System::operatingSystem() now includes a version number on Linux + <LI> SDLWindow no longer initializes the audio system; use SDL_InitSubsytem if you need audio. + <LI> Extended GLenumToString with GL_SHADER_OBJECTS_ARB types. + <LI> NVIDIA p-buffer: GLX_SAMPLE_BUFFERS_ARB, GLX_SAMPLES_ARB, GLX_FLOAT_COMPONENTS_NV, + glXDestroyGLXPbufferSGIX, glXChooseFBConfigSGIX, glXCreateGLXPbufferSGIX, + glXCreateContextWithConfigSGIX, glXQueryGLXPbufferSGIX + <LI> NVIDIA swap lock: glXJoinSwapGroupNV, glXBindSwapBarrierNV, glXQuerySwapGroupNV, + glXQueryMaxSwapGroupsNV, glXQueryFrameCountNV, glXResetFrameCountNV + <LI> OSWindow::requiresMainLoop, OSWindow::runMainLoop (Beta) + <LI> OSWindow::pollEvent, SDLWindow::pollEvent + <LI> G3D::GApp accepts an optional OSWindow on construction + <LI> G3D::VertexAndPixelShader, G3D::ObjectShader (Beta) + <LI> Deprecated GPUProgram, VertexProgram, and PixelProgram (the OpenGL 1.5 shaders + follow a different paradigm than the OpenGL 1.3 ones, so the G3D API must change + to match it). + <LI> Support for GL_ARB_vertex_shader, GL_ARB_fragment_shader, and GL_ARB_shader_objects + <LI> G3D::drawFeatureEdges + <LI> const Array<Vector3>& G3D::MD2Model::PosedModel::objectSpaceFaceNormals(); + <LI> G3D::RenderDevice::sendSequentialIndices + <LI> Network_Demo + <LI> contrib/Win32Window + <LI> contrib/pingtest + <LI> contrib/GlutWindow [Morgan and Dan Keefe] + <LI> contrib/ObjModel [Corey Taylor] + <LI> G3D::GLCaps + <LI> GAppSettings::logFilename + <LI> Deprecated RenderDevice::suportsOpenGLExtension, RenderDevice::supportsImageFormat, + other supports shortcuts (use GLCaps instead). + <LI> DiscoveryClient::cleanup + <LI> Optimized BinaryInput::readUInt32, readUInt16 + <LI> Extended network documentation + <LI> 'fastlib' build target for G3D library developers + <LI> glGetVector2, glGetVector3, glGetVector4 + <LI> float * Quat (double * Quat already existed) + <LI> GApp automatically generates g3d-license.txt at runtime ([RFE#856338] CREDIT.TXT) + <LI> G3D::license + <LI> Removed several large files (tag, ppt, exe) from the source zipfile, bringing it down to 3 MB + <LI> Improved CoordinateFrame:pointToObjectSpace() (RFE#715996) [Giulio] + <LI> [RFE#945935] Make static constants into functions [Giulio] + <LI> Fix: LightweightConduit::send verifies that the packet size is smaller than the UDP limit + <LI> Fix: Multitexture on ATI and Wildcat cards + <LI> Fix: Incorrect occlusion in GLG3D_Demo (was caused by global constant problem) + <LI> Fix: [BUG#949377] Checks for stencil extensions [Giulio] + <LI> Fix: [BUG#922725] Non-multitexture implementation for getTextureState() [Giulio] + <LI> Fix: Restore ambient light color after RenderDevice::popState + <LI> Fix: RenderDevice now initializes OpenGL extensions before testing for multitexture [Erik Cassel, Dan Keefe] + <LI> Fix: Bottom clipping plane of GCamera frustum now correct (was slanted incorrectly, making frustum too big) + <LI> Fix: GFont::draw2D now returns correct y value (used to be too small) + <LI> Fix: NetworkDevice now returns useful hostname on Linux (used to be "localhost") + <LI> Fix: The conduit returned from NetworkDevice::createReliableConduit now has ok() == false when connect fails + <LI> Fix: Tangent space computation of constant u, v now correct (was missing a factor of 2, leading to slight errors) [Max McGuire] + <LI> Fix: [ 925456 ] select broken on Linux (Networking was broken on Linux) + <LI> Fix: getDepthBufferValue off by 1 [Andi Fein] + </UL> + +<hr> + <P> + Changes in 6.02: + <UL> + <LI> Default constructor for Line. + <LI> Various patches to make G3D work with the CAVE [Dan Keefe] + <LI> AABox::set + <LI> Made OSWindow::setPosition non-const + <LI> VARArea now tests for the presence of all VBO extensions, on the freak chance that + a driver has only partial support (due to a bug) + <LI> Linux build statically links OpenGL 1.2.1 and loads extensions through OpenGL 1.5 + to work around Wildcat Linux driver bug (Windows and Mac statically link OpenGL 1.1 + and load extensions through OpenGL 1.5) + <LI> Triangle stores precomputed edge lengths + <LI> Ray-triangle with vertex weights + <LI> Highly optimized ray-triangle intersection test [Tomas Moller & Ben Trumbore] + <LI> Create a texture from 6 different cube-map filenames + <LI> Added contrib directory built as part of the 'doc' target + <LI> contrib/CoreyGWindow: OSWindow implementations for various platforms + <LI> AABSPSet::beginRayIntersection [Pete Hopkins] + <LI> AABSPTree::beginBoxIntersection + <LI> CollisionDetection::intersectionTimeForMovingPointFixedAABox, Ray::intersectionTime(AABox) + [Pierre Terdiman and Andrew Woo] + <LI> Triangle::center + <LI> Renamed KDTreeSet to AABSPTree, old name is #defined + <LI> RenderDevice now works on cards without multitexture + <LI> void glTexCoord(const G3D::Vector4& t); [Dan Keefe] + <LI> Overloaded float, double, and int * Matrix3 + <LI> Fix: [ 923944 ] Matrix/Quat ambiguity + <LI> Fix: fuzzyEq(inf, inf) is true + <LI> Fix: Triangle::randomPoint returns values outside the triangle + <LI> Fix: [ 913763 ] tokenTypeToString(Token::END) + <LI> Fix: Compute number of texture coordinates before RenderDevice::setVideoMode [Dan Keefe] + <LI> Changed the default depth bits to '0' for wider compatibility + (Fix: Unable to create OpenGL screen: Couldn't find matching GLX visual) + <LI> Fix: [912305] Table, Queue, and Set assignment operators do not free old values + <LI> Fix: Separate specular and Multisample on Tablet PC w/ Trident [Dan Keefe] + <LI> Fix: Linux debug build now has line numbers + <LI> Upgraded to SDL 1.2.7 + Fix: [ 838030 ] SDL 1.2.6 blocks prompt + Fix: FSAA does not work under SDL + Fix: Default Win32 refresh rate + <LI> Draw::vertexVectors + <LI> New meshes from Brown University: hemisphere.ifs, curvy.ifs, head.ifs, + closed-low-poly-teapot.ifs, bump.ifs + <LI> GLight::specular + <LI> SDLWindow::setWindowDimensions and setWindowPosition now work on Win32 + <LI> GWindowSettings::x, GWindowSettings::y, GWindowSettings::center + <LI> System::setEnv + <LI> [ 909999 ] OSWindow Joystick interface + <LI> double * Quat ([ 909305 ] scalar * {quat, vector, matrix}) + <LI> Increased the precision of several Vector2 and Vector3 methods + <LI> MeshAlg::computeNormals now returns 0 instead of NaN for degenerate normals + <LI> Updated main-no-GApp.cpp for 6.02 + <LI> RenderDevice::screenshotPic can copy from the back buffer + <LI> Improved VAR documentation. + <LI> If NO_SDL_MAIN is defined, G3D does not attempt to link against sdlmain.lib + <LI> UserInput::setPureDeltaMouse + <LI> UserInput::mouseXY, mouseX, mouseY + <LI> UserInput::mouseDXY + <LI> Deprecated UserInput keyMapping constructor argument + <LI> RenderDevice::setDrawBuffer [Dan Keefe] + <LI> GFont::draw3D [Dan Keefe] + <LI> GImage::pixel3(x, y) and GImage::pixel4(x, y) + <LI> debugAssert, debugBreak, debugAssertM, etc. all release input grab + when an assertion fails (Win32 and Linux) and restore it when the + program continues (Win32). This also fixes the DirectInput laggy + cursor that occurs after a break. + </UL> + +<hr> + <P> + Changes in 6.01: + <UL> + <LI> Default constructor for G3D::LineSegment + <LI> Rect2D::clipPoly (Pete & Morgan) + <LI> Draw::poly2D, Draw::poly2DOutline (Pete & Morgan) + <LI> Added instructions for rotated text to G3D::GFont::draw2D + <LI> Fix: iRandom now compiles correctly under gcc. + <LI> Fix: [ 852076 ] Compute better/faster vertex normals in MeshAlg + MeshAlg::computeNormals now weighs adjacent faces by their area + <LI> Fix: [ 896028 ] Textures broken on Trident TabletPC (Dan Keefe) + <LI> Fix: [ 860800 ] ManualCameraController cursor jumps + <LI> Fix: G3D::UserInput no longer offsets the mouse position by 1/2 pixel + <LI> Fix: Alt-Tab no longer toggles the GApp camera before switching windows + <LI> Fix: [ 901248 ] Font bounds y-value incorrect + <LI> Fix: G3D::PhysicsFrame::toCoordinateFrame() was rotated by 90 degrees + <LI> Fix: [ 895493 ] Radeon 7500 Cube Map + <LI> Fix: G3D::MeshAlg::computeWeld produces linker errors on Linux + <LI> G3D::TextInput::peekLineNumber(), G3D::TextInput::peekCharacterNumber() + <LI> G3D::GAppSettings::dataDir + <LI> html/gettingstarted.html + <LI> G3D::MeshAlg::debugCheckConsistency + <LI> G3D::MD2Model and G3D::IFSModel now weld their adjacency information + <LI> Renamed/retyped G3D::PosedModel::adjacentFaces to G3D::PosedModel::vertices + (most programs can be fixed by changing the type from Array< Array<int> > to + Array<MeshAlg::Vertex> and adjacentVertexArray[v] to vertexArray[v].faceIndex) + <LI> Shadow volumes now use the welded adjacency information + <LI> G3D::PosedModel now offers both welded and non-welded adjacency information + <LI> G3D::contains for C-Arrays + <LI> Generate .tag files in the build + <LI> G3D::MeshAlg::computeAdjacency does not merge colocated vertices + <LI> G3D::MeshAlg::computeAdjacency does not remove degenerate faces and edges + <LI> G3D::MeshAlg::Vertex + <LI> G3D::Vector3::directionOrZero + <LI> G3D::GMaterial + <LI> ManualCameraController renamed to G3D::FPCameraController + <LI> glGetCurrentContext (beta) + <LI> G3D::RenderDevice::supportsImageFormat + <LI> G3D::Vector3::magnitude + <LI> G3D::Vector3::cross() [returns Matrix3] + <LI> G3D::Quat changes (API is still in beta) + <LI> G3D::Quat::norm now returns the 2-norm, not the function Dave Eberly uses. + <LI> Matrix3 default constructor + <LI> Switched UserInput to use SDLWindow internally + <LI> Switched RenderDevice to use SDLWindow internally + <LI> G3D::Window + <LI> G3D::SDLWindow + <LI> Renamed G3D::RenderDeviceSettings to G3D::WindowSettings (with a typedef for the old name) + <LI> IFSModel now loads models with up to 10 million polygons (like the buddha). + <LI> Internal G3D::KDTreeSet state now private. + </UL> + +<hr> + <P> + Changes in 6.00: + <UL> + <LI> FIX: warning: passing `double' for argument 1 of `void G3D::Queue<T>::repackAndRealloc(int)' + <LI> Optimized static Matrix3::transpose (36 cycle) and + Matrix3::mul (52 cycle) variations. + <LI> Changed some lerp arguments from float to double + <LI> MeshAlg::computeTangentSpaceBasis + <LI> Draw::axes now uses scale to compute axis length + <LI> New ParallaxBump demo + <LI> Changed several Vector3 return values from float to double + <LI> Real-world stars, sun, and moon path (Nick Musurca) + <LI> Now compiles under MSVC++ 7.0 (David Baszucki) + <LI> Now compiles under g++ OS/X (Ben Landon) + <LI> Changed the default RenderDeviceSettings::alphaBits to 0 in the hope that it + will work with more graphics cards. + <LI> Matrix3::fromX methods became factory methods + <LI> G3D::sinc + <LI> Multi-platform lib directories + <LI> Vector3::average(), Color3::average(), Vector3::sum(), Color3::sum() + <LI> Ray::reflect, Ray::refract + <LI> Physically correct sky model + <LI> FIX: Older graphics cards can now initialize properly + <LI> Increased fuzzyEpsilon to 0.000001 + <LI> Color3::max, Color3::min, Color4::max, Color4::min + <LI> Array::sortSubArray + <LI> GCamera::getClipPlanes now takes a G3D::Array + <LI> G3D::AABox + <LI> Box::randomInteriorPoint, Box::randomSurfacePoint + <LI> Vector3::cosRandom, Vector3::hemiRandom, Vector3::reflectAbout, Vector3::reflectionDirection, Vector3::refractionDirection + <LI> log(Color3) + <LI> Upgraded to zlib 1.2.1 + <LI> VAR::valid (Peter) + <LI> System::getLocalTime, System::getTicks + <LI> High-performance cycle count and time queries on Linux + <LI> UserInput::anyKeyPressed + <LI> G3D::Box now provides axes, center, and extent information + (serialization is backwards compatible to 5.xx) + <LI> TextInput's exceptions now provide file, line, and character numbers + as well as preformatted error messages in the style of MSVC++. + <LI> G3D::Texture::fromGImage + <LI> G3D::TextInput now parses hex numbers of the form 0x##### + <LI> G3D::CollisionDetection::penetrationDepthForFixedSphereFixedPlane + <LI> G3D::CollisionDetection::penetrationDepthForFixedSphereFixedBox + <LI> G3D::beginMarkShadows, G3D::endMarkShadows, G3D::markShadows + <LI> GFont::draw2D now returns the string bounds + <LI> Sphere::surfaceArea, Sphere::volume, Box::surfaceArea, Box::volume + <LI> Two-sided stencil operations + <LI> Removed G3D::Real + <LI> FIX: [ 855947 ] Fonts are broken on Radeon + <LI> Switched vertex arrays to use the new ARB_vertex_buffer_object extension. + Compared to 5.xx rendering speed: NVIDIA/Win32 is the same (fast), + ATI and Linux rendering are about 10x faster. The API has changed + slightly-- most significant, the vertex, normal, color, etc. arrays + must all come from the same VARArea now. + <LI> Disabled the "conditional is constant" level 4 warning on Windows + that is triggered by the for-loop scoping fix. + <LI> G3D::SkyParameters::directionalLight + <LI> G3D::TextureManager (Peter S. & Morgan) + <LI> Flipped skybox X-axis to match OpenGL cube map coordinates + <LI> Texture now uses hardware MIP-map generation + <LI> Texture::copyFromScreen for cube map faces + <LI> RenderDevice::configureReflectionMap + <LI> RenderDevice::configureShadowMap + <LI> Renamed CFont to GFont + <LI> Renamed CImage to GImage + <LI> G3D::Matrix3::getRow + <LI> Added optional argument drawCelestialBodies to Sky::create. + <LI> RenderDevice::getTextureMatrix + <LI> Depth Textures + <LI> Texture::createEmpty + <LI> RenderDevice::setViewport has flipped the y-axis since version 5.00 + <LI> ReferenceCountedPointer::isLastReference + <LI> Support for textures beyond the number of texture units (which occurs on NVIDIA cards) + <LI> G3D::PosedModel + <LI> G3D::IFSModel + <LI> G3D::CoordinateFrame::normalToObjectSpace, G3D::CoordinateFrame::normalToWorldSpace + <LI> Simplified arguments on Texture::copyFromScreen + <LI> Moved Camera in GLG3D to GCamera in G3D + <LI> Moved setProjectionAndCameraMatrix from Camera to RenderDevice + <LI> Moved G3D::Rect2D to G3D from GLG3D, changed interface + <LI> G3D::setRenderMode + <LI> G3D::RenderDevice::setSpecularCoefficient, G3D::RenderDevice::setShininess + <LI> G3D::GLight + <LI> Renamed G3D::RenderDevice::configureDirectionalLight, configurePointLight to G3D::RenderDevice::setLight + <LI> Changed G3D::Rect2D to use doubles + <LI> G3D::Camera::setPosition() + <LI> G3D::Camera::lookAt() + <LI> G3D::ManualCameraController::setPosition() + <LI> G3D::System::getTick, G3D::System::getLocalTime + <LI> Fixed [ 839618 ] peak var only updated on reset() + <LI> G3D::Array::findIndex (thanks to David Baszucki for the suggestion) + <LI> Removed RenderDevice::setProjectionMatrix3D and RenderDevice::setProjectionMatrix2D + <LI> RenderDevice::project + <LI> RenderDevice::push2D() now uses the current viewport instead of full screen by default + <LI> RenderDevice::getViewport + <LI> G3D::SimTime + <LI> Sky::render no longer needs a camera matrix (it gets it from the render device) + <LI> SkyRef, Sky::create() + <LI> Removed Sky::getName + <LI> Removed RenderDevice::setAmbientLightLevel (duplicated RenderDevice::setAmbientLightColor) + <LI> G3D::GApp, G3D::GApplet, G3D::GAppSettings + <LI> RenderDevice::getCardDescription + <LI> GPUProgram interface for setting program constants [Peter, Morgan & Dan] + <LI> RenderDevice::getModelViewMatrix + <LI> RenderDevice::getModelViewProjectionMatrix + <LI> RenderDevice::getProjectionMatrix + <LI> Documented some more common compiler errors. + <LI> Moved RenderDevice::debugDraw methods to the Draw class, changed rendering from + cylinders to lines for wireframe (for performance) + <LI> Ray::direction no longer has unit length + <LI> Line::point, Line::direction + <LI> LineSegment::endPoint + <LI> IFSBuilder loads Brown University Sketch Model (sm) format + <LI> New IFS models: angel, distributor-cap, dragon2, duck, elephant, hippo, hub, mech-part, rotor, sandal, trumpet, venus-torso, woman + <LI> RenderDevices are now optionally resizable + <LI> MeshAlg::computeWeld + <LI> Array::randomize + <LI> Table now refuses to push the load factor above 19/20 and stops rehashing + <LI> Table always keeps an odd number of buckets + <LI> Sphere::randomInteriorPoint, Sphere::randomSurfacePoint + <LI> LineSegment::randomPoint + <LI> Hardcoded some common paths into demoFindData + <LI> Deprecated old RenderDevice::init method. + <LI> Full screen anti-aliasing (FSAA) + <LI> G3D::RenderDeviceSettings + <LI> All 2, 3, and 4 character swizzles for Vector2, Vector3, Vector4 are defined. + <LI> G3D::rsqrt + <LI> Most vector methods are also defined as functions now + <LI> sign(Vector2), sign(Vector3), sign(Vector4) + <LI> G3D::Matrix4 + <LI> Changed G3D_VER from double to integer + <LI> G3D::lerp + <LI> Changed G3D::PI, G3D::HALF_PI, and G3D::TWO_PI to #defines + <LI> Vector2::clamp, Vector3::clamp, Vector4::clamp + <LI> Changed order of arguments to all lerp methods to match DirectX/Cg + <LI> Changed order of arguments to G3D::clamp and G3D::iClamp to match DirectX/Cg + <LI> G3D::ManualCameraController::ManualCameraController now requires a G3D::UserInput + <LI> G3D::UserInput::appHasFocus + <LI> G3D::ManualCameraController now stops tracking the mouse when the app loses focus + <LI> G3D::ManualCameraController::setActive + <LI> G3D::ManualCameraController now manages the mouse cursor instead of G3D::RenderDevice + <LI> G3D::UserInput::getMouseXY, G3D::UserInput::getXY + <LI> RenderDevice::debugDrawVertexNormals + <LI> GPUProgram, VertexProgram, and PixelProgram now recognize the output of the + Cg compiler and automatically bind constants. + <LI> RenderDevice now loads glActiveStencilFaceEXT + <LI> RenderDevice::numTextureCoords + <LI> Moved changelog to a separate page + <LI> Reformatted overview to be smaller + <LI> Added model debugging info to the IFSBuilder display + <LI> Welded some broken vertices in the teapot.ifs file + <LI> Renamed Font.* to CFont.* + <LI> CFont::draw2DString renamed to CFont::draw2D (use a #define to port old code) + <LI> MeshAlg + <LI> RenderDevice now enables GL_COLOR_MATERIAL by default + <LI> msgBox + <LI> MD2 model gallery in documentation (Kevin) + <LI> MD2Documentor (Kevin) + <LI> debugAssertGLOk macro + <LI> VertexProgram now supports NVIDIA Vertex Program 2.0 + <LI> RenderDevice now loads glGenProgramsNV, glDeleteProgramsNV, glBindProgramNV, glLoadProgramNV, glTrackMatrixNV, glProgramParameter4fvNV, glGetProgramParameterfvNV, glGetProgramParameterdvNV extensions + <LI> VertexProgram and PixelProgram static factory methods now return reference counted values. + <LI> Split the reference value from RenderDevice::setStencilTest into setStencilConstant + <LI> RenderDevice::STENCIL_INVERT, RenderDevice::STENCIL_REPLACE, RenderDevice::STENCIL_ZERO + <LI> Added brighten argument to Texture::fromFile + <LI> Increased CImage JPEG save quality + <LI> RenderDevice::screenshot now returns the name of the file that was written + <LI> nextPowerOf2 renamed to ceilPow2 + <LI> System::alignedMalloc, System::alignedFree + <LI> Carbon, Crackman, Edenmill, Futurist, Interplanetary, + Iomanoid, Starlight, Lesser, and Wild fonts by Ray Larabie. + Like all of our fonts, they are free, but please consider a + donation to him if you like them. http://www.larabiefonts.com/ + <LI> MD2Model_Demo + <LI> G3D::MD2Model + <LI> FIX: Fixed a bug in Array shrinking that could cause memory corruption + <LI> FIX: RenderDevice windows with an aspect ratio of less than 1 now allowed. + <LI> FIX: TextInput now parses '#', '~', '~=', '&', '&&', '|', '||' correctly + <LI> VARArea::reset() now waits for rendering calls using its vertex + arrays to complete before wiping the memory. + <LI> G3D::filenameBaseExt, G3D::filenameExt + <LI> VARArea::finish() + <LI> Milestone + <LI> TextInput::Options::signedNumbers + <LI> RenderDevice now loads glFlushVertexArrayRangeNV + <LI> Vector2int16 + <LI> RenderDevice::freeVARSize() + <LI> Array now allocates 16-byte aligned pointers. + <LI> Decreased the default camera movement rate by 50% for better resolution. + <LI> RenderDevice enables GL_NORMALIZE by default + <LI> Improved the performance of Array::append/Array::push/Array::next + <LI> Fix: [ 875219 ] Array::sort must use std::sort + <LI> Array::next + <LI> Array::reverse + <LI> PCX file loading + <LI> Test images + <LI> Color3uint8 as uint8[] addressing + <LI> Color4uint8 as uint8[] addressing + <LI> Removed const from VAR::pointer + <LI> ReferenceCountedPointer::isNull + <LI> alwaysAssertM + <LI> Log::common, Log::getCommonLogFilename + <LI> Switched from static to dynamic linking of zlib + <LI> Upgraded to zlib 1.1.3 + <LI> On Win32 the lib list is automatically updated through pragmas + (5.xx programs should revert to linking against default libraries) + <LI> Increased default sky quality to 1.00 + <LI> G3D::CFontRef + <LI> RenderDevice now loads all register combiner extensions (NVIDIA only) + <LI> Sky::getEnvironmentMap + <LI> Sky implementation now uses a cube map (when one is available) + <LI> G3D::Sky constructor now takes a render device + <LI> Rotated Sky box 90 degrees to match environment maps + <LI> G3D::Sky now takes the environment filenames as "sky_*.jpg" instead of "sky_ft.jpg" + <LI> Added default filename for Sky constructor + <LI> Added caustics textures created with Kjell Andersson's generator http://www.lysator.liu.se/~kand/caustics/ + <LI> #defined "for" under MSVC so that it obeys C99 scoping rules + <LI> System::consoleKeyPressed + <LI> System::consoleClearScreen + <LI> System::consoleReadKey + <LI> NetMessage::type() + <LI> Changed the Conduit message protocol to include a message type. + The API is backwards compatible to 5.01 even though the protocol is not. + <LI> Removed optional argument maxSize from LightweightConduit::receive. + <LI> NetAddress::serialize + <LI> NetAddress::deserialize + <LI> NetAddress == NetAddress + <LI> hashCode(NetAddress) + <LI> RenderDevice::init now prints ATI or NVIDIA driver version to the log under Windows + <LI> readme.html library build instructions now have downloads for required libraries + <LI> Library list has changed for Win32 (added version.lib) + <LI> System::cpuArchitecture + <LI> System::operatingSystem + <LI> double-precision Plane::getEquation + <LI> Vector2::lerp + <LI> Platform specific #defines G3D_WIN32, G3D_LINUX, G3D_OSX + <LI> G3D::Array::contains + <LI> G3D::Queue::contains + <LI> G3D::ImageFormat + <LI> G3D::Texture::DIM_CUBE_MAP + <LI> G3D::Texture resizes non-power of two textures + <LI> G3D::Texture constructors are completely changed from 5.01 (and hopefully easier to use) + <LI> G3D::CImage now supports images with alpha + <LI> Removed most of the width/height arguments from G3D::Camera methods + <LI> BinaryInput::readBytes and BinaryOutput::writeBytes now take void* as an argument to avoid casting + <LI> Plane::fromEquation + <LI> Removed Plane::getNormal (use Plane::normal instead) + <LI> Removed CDTriangle (use G3D::Triangle instead) + <LI> Removed Font (use G3D::CFont instead) + <LI> FIX: Camera::getClipPlanes now transforms infinite planes correctly. + <LI> FIX: The last reference of an RGC pointer assigned to itself no + longer tries to collect before re-assigning + </UL> + +<hr> + <P> + Changes in 5.01 + <UL> + <LI> G3D::tesselateComplexPolygon + <LI> G3D::ConvexPolygon + <LI> G3D::ConvexPolyhedron + <LI> G3D::iClamp, G3D::clamp + <LI> G3D::iWrap + <LI> G3D::iRandom, G3D::random + <LI> G3D::getFiles + <LI> G3D::getDirs + <LI> G3D::VAR::pointer + <LI> G3D::realWorldLocalTime + <LI> G3D::Texture::TRANSPARENT_BORDER + <LI> DECLARE_GLFORMATOF + <LI> G3D::System::machineEndian + <LI> G3D::VertexProgram, G3D::VertexProgramRef, G3D::RenderDevice::setVertexProgram + <LI> G3D::PixelProgram, G3D::PixelProgramRef, G3D::RenderDevice::setPixelProgram + <LI> G3D::GPUProgram, G3D::GPUProgramRef + <LI> G3D::sizeOfGLFormat + <LI> G3D::RenderDevice::setVertexAttrib + <LI> G3D::Vector2*=Vector2, /= Vector2, * Vector2, / Vector2 + <LI> glFormatOf + <LI> G3D::Color4uint8 + <LI> G3D::Color3uint8 + <LI> G3D::Vector3int16 + <LI> G3D::System::currentProgramFilename + <LI> CImage::insertRedAsAlpha + <LI> CImage::stripAlpha + <LI> Texture::hasAlpha + <LI> Added support for TGA with alpha channel + <LI> Re-implemented Texture to support a broader range of formats and cleaner implementation. + <LI> Fix: Improved Texture::LUMINANCE support + <LI> Added == and != overloads for Texture::Ref so that "a != NULL" is now legal and does not require a cast to Texture::Ref. + <LI> G3D::CFont is a typedef for G3D::Font to avoid name conflicts with X11 Font under Linux. In future releases, the name Font will be deprecated. + <LI> RenderDevice::setPointSize + <LI> Added a new teapot (teapot.ifs) that is closed, with a properly fitting top. The classic teapot is now called "utah-teapot.ifs" (Sebastian Schuberth and Simon Winkelbach) + <LI> RenderDevice::init now loads glPointParameterfvARB, glPointParameterfARB, + glMultiDrawArraysEXT, and glMultiDrawElementsEXT functions. + <LI> GLenumToString(4) now returns "GL_TRIANGLES" instead of "GL_LINE_BIT" (both are correct) + <LI> Added TextInput::Options to optionally allow C++ comments to + be treated as two slashes instead of a comment + <LI> Added data/image/meter.jpg, a meter stick texture convenient for testing + <LI> Added sansserif, news, and terminal fonts based on Bitstream's <A HREF="http://www.gnome.org/fonts/">free fonts</A> + <LI> RenderDevice::numTextureUnits + <LI> Added stars to night Sky + <LI> Added classic GL dinosaur model as data/ifs/dinosaur.ifs + <LI> Documented G3D::glGetProcAddress + <LI> Fix: Texture now restored GL_ENABLE bits properly after creation + <LI> Fix: Texture::sizeInMemory now accounts for MIP-map levels + <LI> Fix: Fonts and skies now adjust their brightness for the screen gamma level + <LI> Fix: Strange compilation bug was causing Sky to be black for some programs + <LI> resolveFilename + <LI> GLProgram_Demo to show how to use vertex programs in G3D + <LI> Support for GL_ARB_vertex_program + <LI> Modified ManualCameraController so that diagonal movement does not exceed + maximum rate. + <LI> Added support for non-GL_FLOAT vertex arrays to RenderDevice + <LI> Added support for Wavefront OBJ files to IFSBuilder + <LI> Removed duplicate copies of SDL.dll from the source tree + <LI> Renamed G3D::CDTriangle to G3D::Triangle + <LI> Added several G3D::Triangle methods + <LI> Moved CollisionDetection::primaryAxis to Vector3::primaryAxis + <LI> Fix: Texture::sizeInMemory now returns correct results for RGB8 textures. + <LI> Changed texture constructors in ways that slightly break backwards compatibility + <LI> Deprecated several arguments to the texture constructors. + </UL> + +<hr> + Changes in 5.00 + <UL> + <LI> Color3::operator*=(const Color3&) + <LI> Color3::operator*(const Color3&) + <LI> Eliminated duplicate GL headers [James O'Sullivan] + <LI> Linux Makefiles [James O'Sullivan, Jordan Parker] + <LI> RenderDevice::getProjectionMatrixParams + <LI> RenderDevice::debugDrawCylinder + <LI> Added an option to not copy input memory for BinaryInput + <LI> Added data/ifs/sphere.ifs + <LI> Added data/ifs/spikeball.ifs + <LI> Added a new (imperfect) demo/tool that converts 3DS and MD2 to IFS. + <LI> Added RenderDevice to the Font constructor + <LI> Removed RenderDevice from Font::drawString + <LI> Included glut32.lib, .dll, and .h (Version 3.7.6) in the distribution. + The windows glut port is by Nate Robbins and is from + http://www.xmission.com/~nate/glut.html. + glut was originally written by Mark Kilgard. + <LI> Modified OpenGL headers to work cross platform, with the latest NVIDIA extensions + <LI> Changed library name from graphics3D.lib to G3D.lib, same for + debug version. + <LI> Changed directory structure and added readme.html to explain + the new setup. + <LI> Changed BinaryInput::readBytes to allow reading onto the stack + <LI> Added Vector4::isFinite + <LI> G3D::CDTriangle (for 35% faster collision detection) + <LI> CollisionDetection::closestPointToRectangle + <LI> CollisionDetection::movingSpherePassesThroughFixedBox + <LI> CollisionDetection::movingSpherePassesThroughFixedSphere + <LI> Changed CollisionDetection::movingXFixedTriangle arguments + <LI> CollisionDetection::collisionTimeForMovingSphereFixedSphere + <LI> Changed CollisionDetection::distanceToX methods to closestPointToX + <LI> Vector3::NAN3 + <LI> Made Vector3::isUnit fuzzy + <LI> Made Vector3::isZero fuzzy + <LI> Fix: Texture(std::string, std::string) constructor now works for alpha-only textures. + <LI> FIX: Array now calls copy constructor when resizing + <LI> FIX: Triangle-sphere and rectangle-sphere collision detection + returned an incorrect collision location; now fixed. + <LI> FIX: changed VectorX::isFinite to call isFinite (used to give bad result for NaNs) + <LI> FIX: Used the normalized edge to compute intersection in + CollisionDetection::distanceToTrianglePerimeter + <LI> FIX: Changed the order of corners returned from Box::getFaceCorners so the + face is ccw, facing out + <LI> FIX: ManualCameraController::lookAt now faces along the -z axis. + <LI> FIX: data/ifs/icosa.ifs model is now an icosahedron + <LI> Made Set::begin() and Set::end() const + <LI> Added ifdef _WIN32 all over for typedefing types from Windows to Linux and vice versa. + <LI> G3D::isNaN, G3D::isFinite + <LI> Added a single triangle triangle.ifs file + <LI> G3D::LineSegment + <LI> RenderDevice::debugDrawRay + <LI> CoordinateFrame::toObjectSpace(Ray&) + <LI> CoordinateFrame::toObjectSpace(Box&) + <LI> CoordinateFrame::toObjectSpace(Sphere&) + <LI> Changed CollisionDetection routines to return the surface normal of the + surface at the collision location. + <LI> CollisionDetection::collisionTimeForMovingPointFixedCapsule + <LI> CollisionDetection::collisionTimeForMovingSphereFixedCapsule + <LI> G3D::Capsule class + <LI> Removed e-mail addresses from contributor list to protect them from spammers + <LI> Linux port [Hari Khalsa & Chris Kern] + <LI> Added serialize and deserialize methods, deserializing constructor to + Vector2, Vector3, Vector4, Color3, Color4, Matrix3, CoordinateFrame, Box, + Sphere, Plane, Ray, Line, Capsule, LineSegment + <LI> Moved parts of Plane.h into Plane.cpp + <LI> BinaryInput::readBool8 and BinaryOutput::writeBool8 + <LI> G3D::System [based on Michael Herf, Rob Wyatt, and Benjamin + Jurke's work] + <LI> Networking infrastructure: G3D::NetworkDevice, G3D::NetAddress, + G3D::ReliableConduit, G3D::LightweightConduit, G3D::NetListener + <LI> G3D::Camera + <LI> Vector2::toString + <LI> G3D::createTempFile + <LI> G3D::fileLength + <LI> UserInput::setKeyMapping + <LI> UserInput::keyCodeToString, UserInput::stringToKeyCode + <LI> JPEG library uses createTempFile + <LI> JPEG library will allocate up to 6MB before resorting to temp + files-- faster and more reliable + <LI> Moved SDL initialization to RenderDevice constructor from the init + method so extension can be used earlier + <LI> Support for up to 8 texture units, no longer crashes on machines + that have more than 4 units + <LI> Made Arrays allocate at least 32 bytes when resized to improve + performance of small char stacks + <LI> Added UserInput key codes for mouse wheel buttons + <LI> UserInput::keyPressed, UserInput::pressedKeys() + <LI> UserInput::GKey + <LI> Renamed UserInput::poll() to UserInput::endEvents(), added + UserInput::beginEvents() + <LI> Moved custom UserInput key codes into an enum so they are + compile-time constants + <LI> Changed all <io.h> to <stdio.h> for cross-platform [Rob & Chris] + <LI> Moved LITTLE_ENDIAN and BIG_ENDIAN constants to an enum and renamed + them to G3D_LITTLE_ENDIAN and G3D_BIG_ENDIAN for cross-platform + [Rob & Chris] + <LI> Permanently fixed the precision of Real to be 32-bit float. + <LI> RenderDevice now loads the NVIDIA VAR fence extensions. + <LI> Renamed RenderDevice::begin to RenderDevice::beginPrimitive, same + for end. + <LI> Redesigned the vertex array system; see VAR and VARArea. + <LI> Changed GLG3D demo to demonstrate the use of the new VAR and + VARArea classes + <LI> CoordinateFrame(Vector3) constructor. + <LI> Improved the performance of zero-radius sphere [aka point] + collision detection + </UL> + +<hr> + <P> + Changes in 4.01 + <UL> + <LI> trimWhitespace() + <LI> Pointwise multiplication and division for Vector3 + <LI> Array::sort now uses > operator by default; two alternative sort methods allow qsort style sorting + <LI> Texture::copyFromScreen + <LI> Texture::invertY + <LI> BinaryInput/BinaryOutput compression (via zlib) + <LI> Alpha-only G3D::Texture mode + <LI> G3D::Font and fonts in data/font + <LI> Array::fastRemove + <LI> TextInput [Morgan & Aaron] + <LI> Color4::CLEAR + <LI> Table [] operator now returns a non-const reference + <LI> RenderDevice::getFrameRate, RenderDevice::getTriangleRate, RenderDevice::getTriangleCount + <LI> ManualCameraController::setMoveRate, ManualCameraController::setTurnRate + <LI> SkyParameters default constructor + <LI> Vector2, Vector3, Vector4 isZero(), isUnit(), isFinite() + <LI> Vector4::length(), Vector4::squaredLength() + <LI> isValidPointer now returns false for 0xFEEEFEEE + <LI> RenderDevice checks for texture compression extensions + <LI> Restructured the directories for the CPP sources (only affects people who build G3D) + <LI> Included NVIDIA and SGI OpenGL headers in the distribution, changed install notes + <LI> Fixed a bug that previously prevented textures from being garbage collected + <LI> Fixed Line::distance returning values too small + <LI> Fixed Plane(normal, point) constructor to compute point from normalized direction [Kevin] + <LI> LED font by Matthew Welch daffy-duck@worldnet.att.net + <LI> VenusRising font by Ray Larabie <A HREF="mailto:drowsy@cheerful.com">drowsy@cheerful.com</A> + <LI> VideoFreak font by Jakob Fischer pizzadude@pizzadude.dk + </UL> + +<hr> + <P> + Changes in 4.00 + <UL> + <LI> Moved texture combine modes from Textures onto RenderDevice texture units + <LI> Documented RenderDevice::getHDC() (Windows only) + <LI> Renamed RenderDevice::swapBuffers() to RenderDevice::endFrame(), added corresponding RenderDevice::beginFrame() + <LI> Moved getNumJoySticks from RenderDevice to UserInput + <LI> Added TEX_ADD combine mode + <LI> Table::getKeys and Set::getMembers now have overloads that take an Array as input. + <LI> BinaryOutput::getCArray + <LI> RenderDevice::getObjectToWorldMatrix(), RenderDevice::getCameraToWorldMatrix() + <LI> RenderDevice::debugDrawAxes(), RenderDevice::debugDrawBox(), RenderDevice::debugDrawSphere() + <LI> Color3::Color3(const Vector3&) and Color4::Color4(const Vector4&) + <LI> Moved hashCode(const Vector3&) and hashCode(const Vector4&) to the global namespace [Kevin] + <LI> isValidPointer now returns false for 0xCCCCCCCC and 0xDEADBEEF + <LI> Fix: RenderDevice::setPolygonOffset now affects polygons rendered in line and point mode + <LI> Fix: Sun is now invisible after it goes below the horizon + <LI> Fix: BinaryInput now supports endian-ness correctly in memory read mode + <LI> Fix: Table.copyFrom and copy constructor now work + </UL> + +<hr> + <P> + Changes in 3.02 + <UL> + <LI> Built libraries using "Multithreaded DLL" [Kevin & Darius] + <LI> Added depth, color, and stencil bit depth preferences to G3D::RenderDevice + <LI> G3D::Sky (plus sky directory in the data distribution) + <LI> Sky cube data [Jauhn Dabz, jauhn@yahoo.com, http://nullpoint.fragland.net] + <LI> G3D::UserInput + <LI> G3D::ManualCameraController + <LI> G3D::SkyParameters + <LI> G3D::toSeconds, G3D::AMPM, G3D::GameTime, G3D::RealTime + <LI> G3D::RenderDevice::project + <LI> G3D::linearSpline + <LI> G3D::Color3::fromARGB and G3D::Color4::fromARGB + <LI> Added non-const G3D::Array::last() [Kevin] + <LI> Modified G3D::RenderDevice::configureDirectionalLight to operate in world space + <LI> Fix: Flipped the y-axis of G3D::RenderDevice::getDepthBufferValue so it matches the documentation. + <LI> Removed brief descriptions from documentation + <LI> Removed sqrt, sin, cos, etc. that conflict with standard library names + <LI> Removed TWO_PI constant + <LI> Removed G3D::Matrix3 virtual destructor + <LI> Removed G3D::Quat virtual destructor [Kevin] + </UL> + + <hr> + Changes in 3.01 + <UL> + <LI> Changed an assert() to debugAssert() in Queue.h + <LI> G3D::Table doesn't grow the number of buckets under bad hash codes [Morgan & Darius] + <LI> G3D::Table allocates only 10 initial buckets + <LI> G3D::Table::debugGetLoad() + <LI> G3D::CollisionDetection::collisionTimeForMovingPointFixedRectangle + <LI> G3D::CollisionDetection::collisionTimeForMovingPointFixedBox + <LI> G3D::Ray::intersectionTime, G3D::Ray::unit() + <LI> G3D::Log [Morgan & Aaron] + <LI> G3D::RenderDevice (OpenGL state abstraction. VertexBuffer support is beta only) + <LI> G3D::Texture (includes texture compression, image loading, and texture rectangle) + <LI> Added a comment to the vector classes noting that they can't be sublcassed [Kevin Egan] + </UL> +<hr> + Changes in 3.00 + <UL> + <LI> G3D::NEWLINE + <LI> writeStringToFile + <LI> Fixed empty stringJoin bug + <LI> Fixed parseFilename with no path bug + <LI> Vector3::INF3, Vector3::ZERO3 + <LI> G3D::PhysicsFrame (beta-- this interface is going to change in 4.00) + <LI> G3D::Vector4 + <LI> G3D::Queue + <LI> Default constructor for G3D::CImage + <LI> G3D::isValidHeapPointer, G3D::isValidPointer + <LI> G3D::Ray + <LI> CImage copy constructor, CImage::load + <LI> Removed \#pragma once for gcc compatibility + <LI> Renamed several hashcode methods to hashCode + <LI> Fixed fuzzy math to work with infinite numbers + <LI> Fixed Table::remove(), Set::remove() bug [Darius Jazayeri] + <LI> G3D::CoordinateFrame.toObjectSpace(Vector4), G3D::CoordinateFrame.toWorldSpace(Vector4) + <LI> Added the data directory + <LI> G3D::CollisionDetection + <LI> G3D::Sphere::culledBy() + <LI> Added the GLG3D library [Morgan McGuire & Seth Block] + <LI> Changed SDL_GL_Demo to use GLG3D, rotate triangle, and use color blending + <LI> Fixed debugPrintf to handle long strings on Win32 + <LI> Wrapped the MMX headers with \#ifdefs [Nate Miller] + <LI> Moved OpenGL code out of CoordinateFrame.h/cpp + <LI> Fixed BinaryInput readVector*, readColor* to read in correct order [Nate Miller] + <LI> BinaryInput::readVector4, BinaryInput::readColor4, BinaryOutput::writeVector4, BinaryOutput::writeColor4 + <LI> IFS_Demo for loading IFS files, dealing with models in OpenGL [Nate Miller] + </UL> + +<hr> + <P> + Changes in 2.00 + <UL> + <LI> Vector2 members renamed to x,y from s,t + <LI> Added SDL_GL_Demo and Win32_Demo + <LI> Removed Group + </UL> + +<hr> + <P> + Changes in 1.10 + <UL> + <LI> CImage, color conversion routines [Morgan McGuire, John Chisholm, and Edward Resnick] + <LI> Array dereference for BinaryInput + <LI> BinaryInput from memory + <LI> BinaryOutput to memory + <LI> toUpper(std::string), toLower(std::string) + <LI> Group::clear() + <LI> inf, nan as global constants (double precision) + <LI> Can iterate over const Tables + <LI> Table::deleteValues() + <LI> Fixed an off-by-one bug in BinaryInput::readString() + <LI> beginsWith() and wordWrap() string utilities + <LI> prompt dialogs have fixed width font [Kurt Miller] + <LI> iMax(), iMin() + <LI> Array::sort() + <LI> stringCompare(), stringPtrCompare() + <LI> readFileAsString() + <LI> Fixed textPrompt() to wait for input + <LI> BinaryInput.getFilename(), BinaryOutput.getFilename() + <LI> ReferenceCount [Justin Miller] + <LI> endsWith() + <LI> stringSplit(), stringJoin() + <LI> Renamed format.* to stringutils.* + <LI> fileExists(), parseFilename(), createDirectory(), copyFile() + <LI> highestBit() [Jukka Liimatta] + <LI> flipRGBVertical() + <LI> Changed all header guards to use G3D_ prefix + <LI> ConvexPolyhedron + <LI> Virtual destructors on almost all objects. + <LI> RGBtoBGR() + <LI> Color4 + <LI> Array::pop(bool shrinkArray=true) + <LI> Vector2::isFinite, Vector2::fuzzyEq, Vector::fuzzyNe + </UL> + <P> + +<hr> + Changes in 1.09 + <UL> + <LI> Removed pointer hash [Aaron Orenstein] + <LI> Changed some includes from quotes to pointy brackets [Aaron Orenstein] + <LI> Sphere::toString() + <LI> Plane::toString() + <LI> Added a change log + </UL> + + */ diff --git a/externals/g3dlite/doc-files/contributors.dox b/externals/g3dlite/doc-files/contributors.dox new file mode 100644 index 00000000000..3cd946a0bf1 --- /dev/null +++ b/externals/g3dlite/doc-files/contributors.dox @@ -0,0 +1,85 @@ +/** +@page contributors G3D Developer Credits + + <b>Team Members</b> + <br><i>7.01 Release Team</i> + <br>Morgan McGuire [Williams College] - Project Manager + <br>Corey Taylor [EA] - Assistant Project Manager and Linux Lead + <br>Casey O'Donnell [RPI] - OS X Lead + <br>Dan Keefe [Brown University] - Developer + <br>Kyle Whitson [Williams College] - Developer + <br>Danny Yuxing Huang [Williams College] - Developer + + <b>Previous Contributors and Cited Sources</b> <br>This library + contains code and resources contributed by the following people, or + based open code and articles by them. Starred (*) developers are + previous members of the %G3D team. + + <p>David Baszucki + <BR>Seth Block + <BR>Nicholas Bray + <BR>Nick Capens + <BR>Erik Cassel + <BR>John Chisholm* + <BR>Jauhn Dabz + <BR>Erik de Castro Lopo + <br>Rich Deeson* + <BR>Chris Demetriou + <BR>L. Peter Deutsch + <br>Dmitri* + <BR>Dave Eberly + <BR>Kevin Egan* + <BR>Cass Everitt + <BR>Dan Fast* + <BR>Andi Fein + <BR>Jakob Fischer + <BR>Dan Keefe* + <BR>Harishabd Khalsa + <BR>Nicolai Haehnle + <BR>Michael Herf + <br>Daniel Hilferty* + <BR>Pete Hopkins + <br>Danny Yuxing Huang + <BR>Peter Hunt + <BR>Robert Hunter + <BR>Ed Johnson + <BR>Benjamin Jurke + <BR>Chris Kern + <BR>Independent JPEG Group + <BR>Darius Jazayeri + <BR>Ben Landon* + <BR>Thomas G. Lane + <BR>Ray Larabie + <BR>Jukka Liimatta + <BR>Giulio Mainardi + <BR>Jeff Marsceill + <BR>Max McGuire + <BR>Morgan McGuire + <BR>Justin Miller + <BR>Kurt Miller + <BR>Nate Miller + <BR>Tomas Moller + <BR>Eric Muller* + <BR>Nick Musurca + <BR>Akita Noek + <BR>James O'Sullivan* + <BR>Aaron Orenstein + <BR>Jordan Parker + <BR>Edward Resnick + <BR>Alex Rice + <BR>Jack Ritter + <BR>Nate Robbins + <BR>Joshua Schpok* + <BR>Sebastian Schubert + <BR>SGI + <BR>Ben Shine* + <BR>Peter Sibley* + <br>Gabe Taubman* + <BR>Corey Taylor* + <BR>Pierre Terdiman + <BR>Ben Trumbore + <BR>Matthew Welch + <BR>Simon Winkelbach + <BR>Laura Wollstadt + <BR>Andrew Woo +*/ diff --git a/externals/g3dlite/doc-files/license.dox b/externals/g3dlite/doc-files/license.dox new file mode 100644 index 00000000000..b4302651fbf --- /dev/null +++ b/externals/g3dlite/doc-files/license.dox @@ -0,0 +1,120 @@ +/** @page license License + +\htmlonly +<TABLE BORDER=0 WIDTH=80%><TR><TD><I><FONT FACE="Arial"> +<A HREF="guideintro.html"><IMG SRC="backarrow.gif" BORDER=0 ALIGN=MIDDLE> +Introduction</A></I></FONT></TD><TD ALIGN=RIGHT><FONT FACE="Arial"><I> +<A HREF="guideinstall.html"> +Installation <IMG SRC="forwardarrow.gif" BORDER=0 ALIGN=MIDDLE></A></I></FONT></TD></TR></TABLE> +\endhtmlonly + +@section intent Intent of License + (This section is informal and not legally binding.) + + <BR> This library is free code-- you can use it without charge and + it is minimally legally encumbered. Unlike some other free libraries, + we <B>do not</B> require you to release + your source code or make your own program open source. + + <P> I intend the license (below) to protect me and the other + contributors from liability and allow you to use the source however + you want. You can make your own closed or open-source programs, + sell them, give them away, whatever. + + <P> + You have an obligation to say "this software is based in part on + the work of the Independent JPEG Group" in your documentation or + application help if you use the G3D::GImage class because it is based on + the IJG library. The OpenGL headers and ZLib headers included may + be freely distributed provided their copyright notices remain + intact. + + <P> + For convenience, G3D::license is a function that returns the license + string you must put in your documentation. G3D::GApp will automatically + write a file (g3d-license.txt) to disk with the contents of this + license unless you tell it not to. + + <P> + Most of the data resources have either entered the public domain and have + been in several published papers or are data that I have explicitly + received permission to distribute with G3D. The G3D fonts are actually font + images, not TrueType font descriptions and may be freely + distributed. As a rule of thumb, you can freely use and distribute + anything you find in the data directory but may need permission to use + it in a commercial product. Check the various copyright.txt files + in the data directories for specific information. + + <P> + + You are required by the BSD license to acknowledge G3D in your + documentation. This can be as minimal as a note buried in the + fine print at the end of a manual or a text file accompanying + your program. I appreciate it if you acknowledged the library + more publicly but you aren't required to. + + <P> + + Likewise, you are encouraged but not required to submit patches to + improve the library for the benefit of all. E-mail me with bugs, + patches, and questions. <P> + + -Morgan McGuire + <<I><A HREF="mailto:matrix@graphics3d.com">matrix@graphics3d.com</A></I>> + + <HR> + + @section reallicense License + + <I>G3D is licensed under the <A HREF="http://www.opensource.org/licenses/bsd-license.php">BSD license</A>, + with portions controlled by the <A HREF="IJG-README.TXT">IJG license</A>, + <A HREF="libpng-LICENSE.txt">PNG Reference Library license</A> and + <A HREF="http://www.gnu.org/copyleft/lesser.html">GNU Lesser General Public License (LGPL)</A>.</I> + + <CODE> + <IMG SRC="http://opensource.org/trademarks/osi-certified/web/osi-certified-120x100.gif"> + <DT>This product uses software from the G3D project (http://g3d-cpp.sf.net) + <DT>Copyright © 2000-2008, Morgan McGuire + <DT>All rights reserved. + <P> + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + <P> + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + <P> + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + <P> + Neither the name of Morgan McGuire, Williams College, Brown University, nor the names of + its contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + <P> + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + </CODE> + + <P> + You must also agree to be bound by the terms of the Independent JPEG + Group license for the portions of this library that are based + on the work of the Independent JPEG Group, <B>if you use those + portions</B>. Note: if you do not use the G3D::GImage class, + this clause does not apply to you because the linker will + strip that code from your project. The <A + HREF="IJG-README.TXT">IJG-README.TXT</A> file contains the + Independent JPEG Group license. + + </OL> + + */
\ No newline at end of file diff --git a/externals/g3dlite/win/VC90/g3dlite.vcproj b/externals/g3dlite/win/VC90/g3dlite.vcproj new file mode 100644 index 00000000000..e76d610ab69 --- /dev/null +++ b/externals/g3dlite/win/VC90/g3dlite.vcproj @@ -0,0 +1,463 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="g3dlite" + ProjectGUID="{8072769E-CF10-48BF-B9E1-12752A5DAC6E}" + RootNamespace="sockets" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)" + ConfigurationType="4" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/MP" + Optimization="0" + AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;" + AdditionalUsingDirectories="" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="false" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + BufferSecurityCheck="true" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + WarningLevel="3" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="3" + CallingConvention="0" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + OutputFile="$(OutDir)/g3dlite.lib" + IgnoreAllDefaultLibraries="true" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)" + ConfigurationType="4" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/MP" + Optimization="0" + AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;" + AdditionalUsingDirectories="" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="false" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + BufferSecurityCheck="true" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + WarningLevel="3" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="3" + CallingConvention="0" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + OutputFile="$(OutDir)/g3dlite.lib" + IgnoreAllDefaultLibraries="true" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)" + ConfigurationType="4" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/MP" + AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;" + AdditionalUsingDirectories="" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_SECURE_SCL=0" + RuntimeLibrary="2" + EnableEnhancedInstructionSet="1" + UsePrecompiledHeader="0" + AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + WarningLevel="3" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="3" + CallingConvention="0" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + OutputFile="$(OutDir)/g3dlite.lib" + IgnoreAllDefaultLibraries="true" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)" + ConfigurationType="4" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/MP" + AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;" + AdditionalUsingDirectories="" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_SECURE_SCL=0" + RuntimeLibrary="2" + EnableEnhancedInstructionSet="0" + UsePrecompiledHeader="0" + AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\" + WarningLevel="3" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="3" + CallingConvention="0" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + OutputFile="$(OutDir)/g3dlite.lib" + IgnoreAllDefaultLibraries="true" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Header Files" + > + </Filter> + <Filter + Name="Source Files" + > + <File + RelativePath="..\..\G3D.lib\source\AABox.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\AnyVal.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\BinaryFormat.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\BinaryInput.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\BinaryOutput.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Box.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Capsule.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\CollisionDetection.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\CoordinateFrame.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Crypto.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Cylinder.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\debugAssert.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\fileutils.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\format.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\g3dmath.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Line.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\LineSegment.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Log.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Matrix3.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Matrix4.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Plane.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\prompt.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Quat.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Ray.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\RegistryUtil.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Sphere.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\stringutils.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\System.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\TextInput.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\TextOutput.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Triangle.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\UprightFrame.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Vector2.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Vector3.cpp" + > + </File> + <File + RelativePath="..\..\G3D.lib\source\Vector4.cpp" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/externals/g3dlite/win/delme b/externals/g3dlite/win/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/g3dlite/win/delme +++ /dev/null diff --git a/externals/g3dlite/win/g3dlite.sln b/externals/g3dlite/win/g3dlite.sln new file mode 100644 index 00000000000..875374e3944 --- /dev/null +++ b/externals/g3dlite/win/g3dlite.sln @@ -0,0 +1,25 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "g3dlite", "VC90\g3dlite.vcproj", "{8072769E-CF10-48BF-B9E1-12752A5DAC6E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|Win32.ActiveCfg = Debug|Win32 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|Win32.Build.0 = Debug|Win32 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|x64.ActiveCfg = Debug|x64 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|x64.Build.0 = Debug|x64 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|Win32.ActiveCfg = Release|Win32 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|Win32.Build.0 = Release|Win32 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|x64.ActiveCfg = Release|x64 + {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/externals/g3dlite/zip.lib/include/zip/ioapi.h b/externals/g3dlite/zip.lib/include/zip/ioapi.h new file mode 100644 index 00000000000..7d457baab34 --- /dev/null +++ b/externals/g3dlite/zip.lib/include/zip/ioapi.h @@ -0,0 +1,75 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/externals/g3dlite/zip.lib/include/zip/unzip.h b/externals/g3dlite/zip.lib/include/zip/unzip.h new file mode 100644 index 00000000000..b247937c807 --- /dev/null +++ b/externals/g3dlite/zip.lib/include/zip/unzip.h @@ -0,0 +1,354 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/externals/g3dlite/zip.lib/include/zip/zip.h b/externals/g3dlite/zip.lib/include/zip/zip.h new file mode 100644 index 00000000000..acacce83b9b --- /dev/null +++ b/externals/g3dlite/zip.lib/include/zip/zip.h @@ -0,0 +1,235 @@ +/* zip.h -- IO for compress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow creates .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + For uncompress .zip file, look at unzip.h + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.html for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _zip_H +#define _zip_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCtypting)); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCtypting : crc of file to compress (needed for crypting) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); +/* + Close the current file in the zipfile, for fiel opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip_H */ diff --git a/externals/g3dlite/zip.lib/source/crypt.h b/externals/g3dlite/zip.lib/source/crypt.h new file mode 100644 index 00000000000..622f4bc2ec4 --- /dev/null +++ b/externals/g3dlite/zip.lib/source/crypt.h @@ -0,0 +1,132 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize<RAND_HEAD_LEN) + return 0; + + /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the + * output of rand() to get less predictability, since rand() is + * often poorly implemented. + */ + if (++calls == 1) + { + srand((unsigned)(time(NULL) ^ ZCR_SEED2)); + } + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + c = (rand() >> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/externals/g3dlite/zip.lib/source/ioapi.c b/externals/g3dlite/zip.lib/source/ioapi.c new file mode 100644 index 00000000000..f1bee23e64b --- /dev/null +++ b/externals/g3dlite/zip.lib/source/ioapi.c @@ -0,0 +1,177 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "zlib.h" +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + +voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + + +uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + + +uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +long ZCALLBACK ftell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + fseek((FILE *)stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK fclose_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +int ZCALLBACK ferror_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/externals/g3dlite/zip.lib/source/iowin32.c b/externals/g3dlite/zip.lib/source/iowin32.c new file mode 100644 index 00000000000..ce911e3636e --- /dev/null +++ b/externals/g3dlite/zip.lib/source/iowin32.c @@ -0,0 +1,272 @@ +#ifdef _MSC_VER +/* iowin32.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + This IO API version uses the Win32 API (for Microsoft Windows) + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include <stdlib.h> + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +voidpf ZCALLBACK win32_open_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK win32_read_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK win32_write_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK win32_tell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK win32_seek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK win32_close_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK win32_error_file_func OF(( + voidpf opaque, + voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + +voidpf ZCALLBACK win32_open_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = 0; + voidpf ret=NULL; + + dwDesiredAccess = dwShareMode = dwFlagsAndAttributes = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + dwDesiredAccess = GENERIC_READ; + dwCreationDisposition = OPEN_EXISTING; + dwShareMode = FILE_SHARE_READ; + } + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + dwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + dwCreationDisposition = OPEN_EXISTING; + } + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + dwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + dwCreationDisposition = CREATE_ALWAYS; + } + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, + dwCreationDisposition, dwFlagsAndAttributes, NULL); + + if (hFile == INVALID_HANDLE_VALUE) + hFile = NULL; + + if (hFile != NULL) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + if (ret==NULL) + CloseHandle(hFile); + else *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + + +uLong ZCALLBACK win32_read_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile !=NULL) + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + + return ret; +} + +long ZCALLBACK win32_tell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)dwSet; + } + return ret; +} + +long ZCALLBACK win32_seek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque=NULL; +} +#endif diff --git a/externals/g3dlite/zip.lib/source/iowin32.h b/externals/g3dlite/zip.lib/source/iowin32.h new file mode 100644 index 00000000000..1978f6152dd --- /dev/null +++ b/externals/g3dlite/zip.lib/source/iowin32.h @@ -0,0 +1,23 @@ +#ifdef _MSC_VER +/* iowin32.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + This IO API version uses the Win32 API (for Microsoft Windows) + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include <windows.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/externals/g3dlite/zip.lib/source/unzip.c b/externals/g3dlite/zip.lib/source/unzip.c new file mode 100644 index 00000000000..e80bc5bde7c --- /dev/null +++ b/externals/g3dlite/zip.lib/source/unzip.c @@ -0,0 +1,1604 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x = 0; + int i = 0; + int err = 0; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x = 0; + int i = 0; + int err = 0; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1<c2) + return -1; + if (c1>c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize,uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pos<us.offset_central_dir+us.size_central_dir) && + (err==UNZ_OK)) + err=UNZ_BADZIPFILE; + + if (err!=UNZ_OK) + { + ZCLOSE(us.z_filefunc, us.filestream); + return NULL; + } + + us.byte_before_the_zipfile = central_pos - + (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + us.encrypted = 0; + + + s=(unz_s*)ALLOC(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + +extern unzFile ZEXPORT unzOpen (path) + const char *path; +{ + return unzOpen2(path, NULL); +} + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzClose (file) + unzFile file; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename<fileNameBufferSize) + { + *(szFileName+file_info.size_filename)='\0'; + uSizeRead = file_info.size_filename; + } + else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename>0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extra<extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (lSeek!=0) + { + if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_comment<commentBufferSize) + { + *(szComment+file_info.size_file_comment)='\0'; + uSizeRead = file_info.size_file_comment; + } + else + uSizeRead = commentBufferSize; + + if (lSeek!=0) + { + if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressed<uReadThis) + uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;i<uReadThis;i++) + pfile_in_zip_read_info->read_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;i<uDoCopy;i++) + *(pfile_in_zip_read_info->stream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/externals/g3dlite/zip.lib/source/zip.c b/externals/g3dlite/zip.lib/source/zip.c new file mode 100644 index 00000000000..d5f9fe53d26 --- /dev/null +++ b/externals/g3dlite/zip.lib/source/zip.c @@ -0,0 +1,1221 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.01e, February 12th, 2005 + + 27 Dec 2004 Rolf Kalbermatter + Modification to zipOpen2 to support globalComment retrieval. + + Copyright (C) 1998-2005 Gilles Vollant + + Read zip.h for more info +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] = + " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + uLong pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralheader; /* size of the central header for cur file */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile_info; + +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile_info ci; /* info on the file curretly writing */ + + uLong begin_pos; /* position of the beginning of the zipfile */ + uLong add_position_when_writting_offset; + uLong number_entry; +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif +} zip_internal; + + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(ldi) + linkedlist_datablock_internal* ldi; +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(ll) + linkedlist_data* ll; +{ + ll->first_block = ll->last_block = NULL; +} + +/* +// Never used! +local void free_linkedlist(ll) + linkedlist_data* ll; +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} +*/ + +local int add_data_in_datablock(ll,buf,len) + linkedlist_data* ll; + const void* buf; + uLong len; +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;i<copy_this;i++) + *(to_copy+i)=*(from_copy+i); + + ldi->filled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 or 4 (byte, short or long) +*/ + +local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, uLong x, int nbByte)); +local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong x; + int nbByte; +{ + unsigned char buf[4]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte)); +local void ziplocal_putValue_inmemory (dest, x, nbByte) + void* dest; + uLong x; + int nbByte; +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) + const tm_zip* ptm; + uLong dosDate; +{ + uLong year = (uLong)ptm->tm_year; + if (year>1980) + year-=1980; + else if (year>80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int ziplocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int ziplocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x = 0; + int i = 0; + int err = 0; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int ziplocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x = 0; + int i = 0; + int err = 0; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong ziplocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize,uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + +/************************************************************/ +extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def) + const char *pathname; + int append; + zipcharpc* globalcomment; + zlib_filefunc_def* pzlib_filefunc_def; +{ + zip_internal ziinit; + zip_internal* zi; + int err=ZIP_OK; + + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&ziinit.z_filefunc); + else + ziinit.z_filefunc = *pzlib_filefunc_def; + + ziinit.filestream = (*(ziinit.z_filefunc.zopen_file)) + (ziinit.z_filefunc.opaque, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + zi = (zip_internal*)ALLOC(sizeof(zip_internal)); + if (zi==NULL) + { + ZCLOSE(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory */ + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry; + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong size_comment; + + central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); + if (central_pos==0) + err=ZIP_ERRNO; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* zipfile global comment length */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((central_pos<offset_central_dir+size_central_dir) && + (err==ZIP_OK)) + err=ZIP_BADZIPFILE; + + if (err!=ZIP_OK) + { + ZCLOSE(ziinit.z_filefunc, ziinit.filestream); + return NULL; + } + + if (size_comment>0) + { + ziinit.globalcomment = ALLOC(size_comment+1); + if (ziinit.globalcomment) + { + size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); + ziinit.globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - + (offset_central_dir+size_central_dir); + ziinit.add_position_when_writting_offset = byte_before_the_zipfile; + + { + uLong size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir + byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + uLong read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&ziinit.central_dir,buf_read, + (uLong)read_this); + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + ziinit.begin_pos = byte_before_the_zipfile; + ziinit.number_entry = number_entry_CD; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen (pathname, append) + const char *pathname; + int append; +{ + return zipOpen2(pathname,append,NULL,NULL); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; + int windowBits; + int memLevel; + int strategy; + const char* password; + uLong crcForCrypting; +{ + zip_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; + + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); + } + + zi->ci.flag = 0; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ; + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + + size_extrafield_global + size_comment; + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader); + + ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); + ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4); + + for (i=0;i<size_filename;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;i<size_extrafield_global;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;i<size_comment;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + /* write the local header */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2); + + if ((err==ZIP_OK) && (size_filename>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + + if ((err==ZIP_OK) && (size_extrafield_local>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local) + !=size_extrafield_local) + err = ZIP_ERRNO; + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, + Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = 1; + } +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; +{ + return zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; +{ + return zipOpenNewFileInZip2 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0); +} + +local int zipFlushWriteBuffer(zi) + zip_internal* zi; +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;i<zi->ci.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, + zi->ci.buffered_data[i],t); +#endif + } + if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) + !=zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + zi->ci.pos_in_buffered_data = 0; + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (file, buf, len) + zipFile file; + const void* buf; + unsigned len; +{ + zip_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.stream.next_in = (void*)buf; + zi->ci.stream.avail_in = len; + zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + for (i=0;i<copy_this;i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) + zipFile file; + uLong uncompressed_size; + uLong crc32; +{ + zip_internal* zi; + uLong compressed_size; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + if (zipFlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + err=deflateEnd(&zi->ci.stream); + zi->ci.stream_initialised = 0; + } + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = (uLong)zi->ci.stream.total_in; + } + compressed_size = (uLong)zi->ci.stream.total_out; +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20, + compressed_size,4); /*compr size*/ + if (zi->ci.stream.data_type == Z_ASCII) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + ziplocal_putValue_inmemory(zi->ci.central_header+24, + uncompressed_size,4); /*uncompr size*/ + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header, + (uLong)zi->ci.size_centralheader); + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + long cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (ZSEEK(zi->z_filefunc,zi->filestream, + zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if (err==ZIP_OK) /* compressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + + if (ZSEEK(zi->z_filefunc,zi->filestream, + cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (file) + zipFile file; +{ + return zipCloseFileInZipRaw (file,0,0); +} + +extern int ZEXPORT zipClose (file, global_comment) + zipFile file; + const char* global_comment; +{ + zip_internal* zi; + int err = 0; + uLong size_centraldir = 0; + uLong centraldir_pos_inzip; + uInt size_global_comment; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + if (global_comment==NULL) + size_global_comment = 0; + else + size_global_comment = (uInt)strlen(global_comment); + + centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block ; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + ldi->data,ldi->filled_in_this_block) + !=ldi->filled_in_this_block ) + err = ZIP_ERRNO; + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_datablock(zi->central_dir.first_block); + + if (err==ZIP_OK) /* Magic End */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* size of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the + starting disk number */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream, + (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + + if (err==ZIP_OK) /* zipfile comment length */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if ((err==ZIP_OK) && (size_global_comment>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + global_comment,size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + + if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} |
