diff options
Diffstat (limited to 'externals')
302 files changed, 98139 insertions, 4 deletions
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h new file mode 100644 index 00000000000..1178fad93c3 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h @@ -0,0 +1,1609 @@ +/** + @file AABSPTree.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-11 + @edited 2008-11-19 + + Copyright 2000-2008, Morgan McGuire. + All rights reserved. + + */ + +#ifndef G3D_KDTREE_H +#define G3D_KDTREE_H + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Table.h" +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/Triangle.h" +#include "G3D/Ray.h" +#include "G3D/GCamera.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/CollisionDetection.h" +#include "G3D/GCamera.h" +#include "G3D/BoundsTrait.h" +#include <algorithm> + +// If defined, in debug mode the tree is checked for consistency +// as a way of detecting corruption due to implementation bugs +// #define VERIFY_TREE + +template<> struct BoundsTrait<class G3D::Vector2> { + static void getBounds(const G3D::Vector2& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(v, 0)); } +}; + +template<> struct BoundsTrait<class G3D::Vector3> { + static void getBounds(const G3D::Vector3& v, G3D::AABox& out) { out = G3D::AABox(v); } +}; + +template<> struct BoundsTrait<class G3D::Vector4> { + static void getBounds(const G3D::Vector4& v, G3D::AABox& out) { out = G3D::AABox(v.xyz()); } +}; + +template<> struct BoundsTrait<class G3D::AABox> { + static void getBounds(const G3D::AABox& v, G3D::AABox& out) { out = v; } +}; + +template<> struct BoundsTrait<class G3D::Sphere> { + static void getBounds(const G3D::Sphere& s, G3D::AABox& out) { s.getBounds(out); } +}; + +template<> struct BoundsTrait<class G3D::Box> { + static void getBounds(const G3D::Box& b, G3D::AABox& out) { b.getBounds(out); } +}; + +template<> struct BoundsTrait<class G3D::Vector2*> { + static void getBounds(const G3D::Vector2*& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(*v, 0)); } +}; + +template<> struct BoundsTrait<class G3D::Vector3*> { + static void getBounds(const G3D::Vector3*& v, G3D::AABox& out) { out = G3D::AABox(*v); } +}; + +template<> struct BoundsTrait<class G3D::Vector4*> { + static void getBounds(const G3D::Vector4*& v, G3D::AABox& out) { out = G3D::AABox(v->xyz()); } +}; + +template<> struct BoundsTrait<class G3D::AABox*> { + static void getBounds(const G3D::AABox*& v, G3D::AABox& out) { out = *v; } +}; + +template<> struct BoundsTrait<class G3D::Sphere*> { + static void getBounds(const G3D::Sphere*& s, G3D::AABox& out) { s->getBounds(out); } +}; + +template<> struct BoundsTrait<class G3D::Box*> { + static void getBounds(const G3D::Box*& b, G3D::AABox& out) { b->getBounds(out); } +}; + + +template<> struct BoundsTrait<class G3D::Triangle*> { + static void getBounds(const G3D::Triangle*& t, G3D::AABox& out) { t->getBounds(out); } +}; + +namespace G3D { + namespace _internal { + + /** + Wraps a pointer value so that it can be treated as the instance itself; + convenient for inserting pointers into a Table but using the + object equality instead of pointer equality. + */ + template<class Type> + class Indirector { + public: + Type* handle; + + inline Indirector(Type* h) : handle(h) {} + + inline Indirector() : handle(NULL) {} + + /** Returns true iff the values referenced by the handles are equivalent. */ + inline bool operator==(const Indirector& m) const { + return *handle == *(m.handle); + } + + inline bool operator==(const Type& m) const { + return *handle == m; + } + + inline size_t hashCode() const { + return handle->hashCode(); + } + }; + } // namespace internal +} // namespace G3D + +template <class Handle> struct HashTrait<typename G3D::_internal::Indirector<Handle> > { + static size_t hashCode(const G3D::_internal::Indirector<Handle>& key) { return key.hashCode(); } +}; + +namespace G3D { + +/** + A set that supports spatial queries using a KD tree (axis-aligned + BSP tree) for speed. + + KDTree allows you to quickly find objects in 3D that lie within + a box or along a ray. For large sets of objects it is much faster + than testing each object for a collision. + + KDTree is as powerful as but more general than a Quad Tree, Oct + Tree, or regular KD tree that cycles through axes, but less general than an unconstrained BSP tree + (which is much slower to create). + + Internally, objects + are arranged into a tree according to their + axis-aligned bounds. This increases the cost of insertion to + O(log n) but allows fast overlap queries. + + <B>Template Parameters</B> + <DT>The template parameter <I>T</I> must be one for which + the following functions are all overloaded: + + <pre> + T::T();</CODE> <I>(public constructor of no arguments)</I> + template <> struct HashTrait<T> { static size_t hashCode(int key); }; + template<> struct BoundsTrait<T> { static void getBounds(const T& obj, G3D::AABox& out); }; + </pre> + + G3D provides these for common classes like G3D::Vector3 and G3D::Sphere. + If you use a custom class, or a pointer to a custom class, you will need + to define those functions. + + <B>Moving %Set Members</B> + <DT>It is important that objects do not move without updating the + KDTree. If the axis-aligned bounds of an object are about + to change, KDTree::remove it before they change and + KDTree::insert it again afterward. For objects + where the hashCode and == operator are invariant with respect + to the 3D position, + you can use the KDTree::update method as a shortcut to + insert/remove an object in one step after it has moved. + + + Note: Do not mutate any value once it has been inserted into KDTree. Values + are copied interally. All KDTree iterators convert to pointers to constant + values to reinforce this. + + If you want to mutate the objects you intend to store in a KDTree + simply insert <I>pointers</I> to your objects instead of the objects + themselves, and ensure that the above operations are defined. (And + actually, because values are copied, if your values are large you may + want to insert pointers anyway, to save space and make the balance + operation faster.) + + <B>Dimensions</B> + Although designed as a 3D-data structure, you can use the KDTree + for data distributed along 2 or 1 axes by simply returning bounds + that are always zero along one or more dimensions. + +*/ +template< class T, + class BoundsFunc = BoundsTrait<T>, + class HashFunc = HashTrait<T>, + class EqualsFunc = EqualsTrait<T> > +class KDTree { +protected: +#define TreeType KDTree<T, BoundsFunc, HashFunc, EqualsFunc> + + /** Wrapper for a value that includes a cache of its bounds. + Except for the test value used in a set-query operation, there + is only ever one instance of the handle associated with any + value and the memberTable and Nodes maintain pointers to that + heap-allocated value. + */ + class Handle { + public: + /** The bounds of each object are constrained to AABox::large */ + AABox bounds; + + /** Center of bounds. We cache this value to avoid recomputing it + during the median sort, and because MSVC 6 std::sort goes into + an infinite loop if we compute the midpoint on the fly (possibly + a floating point roundoff issue, where B<A and A<B both are true).*/ + Vector3 center; + + T value; + + Handle() {} + + inline Handle(const T& v) : value(v) { + BoundsFunc::getBounds(v, bounds); + bounds = bounds.intersect(AABox::large()); + center = bounds.center(); + } + + inline bool operator==(const Handle& other) const { + return EqualsFunc::equals(value, other.value); + } + + inline size_t hashCode() const { + return HashFunc::hashCode(value); + } + }; + + /** Returns the bounds of the sub array. Used by makeNode. */ + static AABox computeBounds( + const Array<Handle*>& point, + int beginIndex, + int endIndex) { + + Vector3 lo = Vector3::inf(); + Vector3 hi = -lo; + + debugAssertM(beginIndex <= endIndex, "No points"); + for (int p = beginIndex; p <= endIndex; ++p) { + // This code is written with the vector min and max expanded + // because otherwise it compiles incorrectly with -O3 on + // gcc 3.4 + + const Vector3& pLo = point[p]->bounds.low(); + const Vector3& pHi = point[p]->bounds.high(); + for (int a = 0; a < 3; ++a) { + lo[a] = G3D::min(lo[a], pLo[a]); + hi[a] = G3D::max(hi[a], pHi[a]); + } + } + + return AABox(lo, hi); + } + + /** Compares centers */ + class CenterComparator { + public: + Vector3::Axis sortAxis; + + CenterComparator(Vector3::Axis a) : sortAxis(a) {} + + inline int operator()(Handle* A, const Handle* B) const { + float a = A->center[sortAxis]; + float b = B->center[sortAxis]; + + if (a < b) { + return 1; + } else if (a > b) { + return -1; + } else { + return 0; + } + } + }; + + + /** Compares bounds for strict >, <, or overlap*/ + class BoundsComparator { + public: + Vector3::Axis sortAxis; + + BoundsComparator(Vector3::Axis a) : sortAxis(a) {} + + inline int operator()(Handle* A, const Handle* B) const { + const AABox& a = A->bounds; + const AABox& b = B->bounds; + + if (a.high()[sortAxis] < b.low()[sortAxis]) { + return 1; + } else if (a.low()[sortAxis] > b.high()[sortAxis]) { + return -1; + } else { + return 0; + } + } + }; + + + /** Compares bounds to the sort location */ + class Comparator { + public: + Vector3::Axis sortAxis; + float sortLocation; + + Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {} + + inline int operator()(Handle* ignore, const Handle* handle) const { + const AABox& box = handle->bounds; + debugAssert(ignore == NULL); + + if (box.high()[sortAxis] < sortLocation) { + // Box is strictly below the sort location + return -1; + } else if (box.low()[sortAxis] > sortLocation) { + // Box is strictly above the sort location + return 1; + } else { + // Box overlaps the sort location + return 0; + } + } + }; + + // Using System::malloc with this class provided no speed improvement. + class Node { + public: + + /** Spatial bounds on all values at this node and its children, based purely on + the parent's splitting planes. May be infinite. */ + AABox splitBounds; + + Vector3::Axis splitAxis; + + /** Location along the specified axis */ + float splitLocation; + + /** child[0] contains all values strictly + smaller than splitLocation along splitAxis. + + child[1] contains all values strictly + larger. + + Both may be NULL if there are not enough + values to bother recursing. + */ + Node* child[2]; + + /** Array of values at this node (i.e., values + straddling the split plane + all values if + this is a leaf node). + + This is an array of pointers because that minimizes + data movement during tree building, which accounts + for about 15% of the time cost of tree building. + */ + Array<Handle*> valueArray; + + /** For each object in the value array, a copy of its bounds. + Packing these into an array at the node level + instead putting them in the valueArray improves + cache coherence, which is about a 3x performance + increase when performing intersection computations. + */ + Array<AABox> boundsArray; + + /** Creates node with NULL children */ + Node() { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + splitBounds = AABox(-Vector3::inf(), Vector3::inf()); + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** + Doesn't clone children. + */ + Node(const Node& other) : valueArray(other.valueArray), boundsArray(other.boundsArray) { + splitAxis = other.splitAxis; + splitLocation = other.splitLocation; + splitBounds = other.splitBounds; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** Copies the specified subarray of pt into point, NULLs the children. + Assumes a second pass will set splitBounds. */ + Node(const Array<Handle*>& pt) : valueArray(pt) { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + + boundsArray.resize(valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + boundsArray[i] = valueArray[i]->bounds; + } + } + + /** Deletes the children (but not the values) */ + ~Node() { + for (int i = 0; i < 2; ++i) { + delete child[i]; + } + } + + /** Returns true if this node is a leaf (no children) */ + inline bool isLeaf() const { + return (child[0] == NULL) && (child[1] == NULL); + } + + + /** + Recursively appends all handles and children's handles + to the array. + */ + void getHandles(Array<Handle*>& handleArray) const { + handleArray.append(valueArray); + for (int i = 0; i < 2; ++i) { + if (child[i] != NULL) { + child[i]->getHandles(handleArray); + } + } + } + + void verifyNode(const Vector3& lo, const Vector3& hi) { + // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n", + // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z); + + debugAssertM(lo == splitBounds.low(), + format("lo = %s, splitBounds.lo = %s", + lo.toString().c_str(), splitBounds.low().toString().c_str())); + debugAssert(hi == splitBounds.high()); + + for (int i = 0; i < valueArray.length(); ++i) { + const AABox& b = valueArray[i]->bounds; + debugAssert(b == boundsArray[i]); + + for(int axis = 0; axis < 3; ++axis) { + debugAssert(b.low()[axis] <= b.high()[axis]); + debugAssert(b.low()[axis] >= lo[axis]); + debugAssert(b.high()[axis] <= hi[axis]); + } + } + + if (child[0] || child[1]) { + debugAssert(lo[splitAxis] < splitLocation); + debugAssert(hi[splitAxis] > splitLocation); + } + + Vector3 newLo = lo; + newLo[splitAxis] = splitLocation; + Vector3 newHi = hi; + newHi[splitAxis] = splitLocation; + + if (child[0] != NULL) { + child[0]->verifyNode(lo, newHi); + } + + if (child[1] != NULL) { + child[1]->verifyNode(newLo, hi); + } + } + + + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + static void serializeStructure(const Node* n, BinaryOutput& bo) { + if (n == NULL) { + bo.writeUInt8(0); + } else { + bo.writeUInt8(1); + n->splitBounds.serialize(bo); + serialize(n->splitAxis, bo); + bo.writeFloat32(n->splitLocation); + for (int c = 0; c < 2; ++c) { + serializeStructure(n->child[c], bo); + } + } + } + + /** Clears the member table */ + static Node* deserializeStructure(BinaryInput& bi) { + if (bi.readUInt8() == 0) { + return NULL; + } else { + Node* n = new Node(); + n->splitBounds.deserialize(bi); + deserialize(n->splitAxis, bi); + n->splitLocation = bi.readFloat32(); + for (int c = 0; c < 2; ++c) { + n->child[c] = deserializeStructure(bi); + } + return n; + } + } + + /** Returns the deepest node that completely contains bounds. */ + Node* findDeepestContainingNode(const AABox& bounds) { + + // See which side of the splitting plane the bounds are on + if (bounds.high()[splitAxis] < splitLocation) { + // Bounds are on the low side. Recurse into the child + // if it exists. + if (child[0] != NULL) { + return child[0]->findDeepestContainingNode(bounds); + } + } else if (bounds.low()[splitAxis] > splitLocation) { + // Bounds are on the high side, recurse into the child + // if it exists. + if (child[1] != NULL) { + return child[1]->findDeepestContainingNode(bounds); + } + } + + // There was no containing child, so this node is the + // deepest containing node. + return this; + } + + + /** Appends all members that intersect the box. + If useSphere is true, members that pass the box test + face a second test against the sphere. */ + void getIntersectingMembers( + const AABox& box, + const Sphere& sphere, + Array<T>& members, + bool useSphere) const { + + // Test all values at this node + for (int v = 0; v < boundsArray.size(); ++v) { + const AABox& bounds = boundsArray[v]; + if (bounds.intersects(box) && + (! useSphere || bounds.intersects(sphere))) { + members.append(valueArray[v]->value); + } + } + + // If the left child overlaps the box, recurse into it + if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) { + child[0]->getIntersectingMembers(box, sphere, members, useSphere); + } + + // If the right child overlaps the box, recurse into it + if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) { + child[1]->getIntersectingMembers(box, sphere, members, useSphere); + } + } + + /** + Recurse through the tree, assigning splitBounds fields. + */ + void assignSplitBounds(const AABox& myBounds) { + splitBounds = myBounds; + + AABox childBounds[2]; + myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]); + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + // Verify the split + for (int v = 0; v < boundsArray.size(); ++v) { + const AABox& bounds = boundsArray[v]; + debugAssert(myBounds.contains(bounds)); + } +# endif + + for (int c = 0; c < 2; ++c) { + if (child[c]) { + child[c]->assignSplitBounds(childBounds[c]); + } + } + } + + /** Returns true if the ray intersects this node */ + bool intersects(const Ray& ray, float distance) const { + // See if the ray will ever hit this node or its children + Vector3 location; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + CollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds); + + bool canHitThisNode = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + + return canHitThisNode; + } + + template<typename RayCallback> + void intersectRay( + const Ray& ray, + RayCallback& intersectCallback, + float& distance, + bool intersectCallbackIsFast) const { + + if (! intersects(ray, distance)) { + // The ray doesn't hit this node, so it can't hit the children of the node. + return; + } + + // Test for intersection against every object at this node. + for (int v = 0; v < valueArray.size(); ++v) { + bool canHitThisObject = true; + + if (! intersectCallbackIsFast) { + // See if + Vector3 location; + const AABox& bounds = boundsArray[v]; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + CollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, bounds, location, alreadyInsideBounds); + + canHitThisObject = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + } + + if (canHitThisObject) { + // It is possible that this ray hits this object. Look for the intersection using the + // callback. + const T& value = valueArray[v]->value; + intersectCallback(ray, value, distance); + } + } + + // There are three cases to consider next: + // + // 1. the ray can start on one side of the splitting plane and never enter the other, + // 2. the ray can start on one side and enter the other, and + // 3. the ray can travel exactly down the splitting plane + + enum {NONE = -1}; + int firstChild = NONE; + int secondChild = NONE; + + if (ray.origin[splitAxis] < splitLocation) { + + // The ray starts on the small side + firstChild = 0; + + if (ray.direction[splitAxis] > 0) { + // The ray will eventually reach the other side + secondChild = 1; + } + + } else if (ray.origin[splitAxis] > splitLocation) { + + // The ray starts on the large side + firstChild = 1; + + if (ray.direction[splitAxis] < 0) { + secondChild = 0; + } + } else { + // The ray starts on the splitting plane + if (ray.direction[splitAxis] < 0) { + // ...and goes to the small side + firstChild = 0; + } else if (ray.direction[splitAxis] > 0) { + // ...and goes to the large side + firstChild = 1; + } + } + + // Test on the side closer to the ray origin. + if ((firstChild != NONE) && child[firstChild]) { + child[firstChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast); + } + + if (ray.direction[splitAxis] != 0) { + // See if there was an intersection before hitting the splitting plane. + // If so, there is no need to look on the far side and recursion terminates. + float distanceToSplittingPlane = (splitLocation - ray.origin[splitAxis]) / ray.direction[splitAxis]; + if (distanceToSplittingPlane > distance) { + // We aren't going to hit anything else before hitting the splitting plane, + // so don't bother looking on the far side of the splitting plane at the other + // child. + return; + } + } + + // Test on the side farther from the ray origin. + if ((secondChild != NONE) && child[secondChild]) { + child[secondChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast); + } + + } + }; + + + /** + Recursively subdivides the subarray. + + Clears the source array as soon as it is no longer needed. + + Call assignSplitBounds() on the root node after making a tree. + */ + Node* makeNode( + Array<Handle*>& source, + int valuesPerNode, + int numMeanSplits, + Array<Handle*>& temp) { + + Node* node = NULL; + + if (source.size() <= valuesPerNode) { + // Make a new leaf node + node = new Node(source); + + // Set the pointers in the memberTable + for (int i = 0; i < source.size(); ++i) { + memberTable.set(Member(source[i]), node); + } + source.clear(); + + } else { + // Make a new internal node + node = new Node(); + + const AABox& bounds = computeBounds(source, 0, source.size() - 1); + const Vector3& extent = bounds.high() - bounds.low(); + + Vector3::Axis splitAxis = extent.primaryAxis(); + + float splitLocation; + + // Arrays for holding the children + Array<Handle*> lt, gt; + + if (numMeanSplits <= 0) { + + source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis)); + + // Choose the split location to be the center of whatever fell in the center + splitLocation = node->valueArray[0]->center[splitAxis]; + + // Some of the elements in the lt or gt array might really overlap the split location. + // Move them as needed. + for (int i = 0; i < lt.size(); ++i) { + const AABox& bounds = lt[i]->bounds; + if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { + node->valueArray.append(lt[i]); + // Remove this element and process the new one that + // is swapped in in its place. + lt.fastRemove(i); --i; + } + } + + for (int i = 0; i < gt.size(); ++i) { + const AABox& bounds = gt[i]->bounds; + if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { + node->valueArray.append(gt[i]); + // Remove this element and process the new one that + // is swapped in in its place. + gt.fastRemove(i); --i; + } + } + + if ((node->valueArray.size() > (source.size() / 2)) && + (source.size() > 6)) { + // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the + // objects. We could try to split on a different axis, or use a different partition (e.g., the extents mean, + // or geometric mean). This implementation falls back on the extents mean, since that case is already handled + // below. + numMeanSplits = 1; + } + } + + // Note: numMeanSplits may have been increased by the code in the previous case above in order to + // force a re-partition. + + if (numMeanSplits > 0) { + // Split along the mean + splitLocation = + bounds.high()[splitAxis] * 0.5f + + bounds.low()[splitAxis] * 0.5f; + + debugAssertM(isFinite(splitLocation), + "Internal error: split location must be finite."); + + source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation)); + + // The Comparator ensures that elements are strictly on the correct side of the split + } + + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size()); + // Verify that all objects ended up on the correct side of the split. + // (i.e., make sure that the Array partition was correct) + for (int i = 0; i < lt.size(); ++i) { + const AABox& bounds = lt[i]->bounds; + debugAssert(bounds.high()[splitAxis] < splitLocation); + } + + for (int i = 0; i < gt.size(); ++i) { + const AABox& bounds = gt[i]->bounds; + debugAssert(bounds.low()[splitAxis] > splitLocation); + } + + for (int i = 0; i < node->valueArray.size(); ++i) { + const AABox& bounds = node->valueArray[i]->bounds; + debugAssert(bounds.high()[splitAxis] >= splitLocation); + debugAssert(bounds.low()[splitAxis] <= splitLocation); + } +# endif + + // The source array is no longer needed + source.clear(); + + node->splitAxis = splitAxis; + node->splitLocation = splitLocation; + + // Update the bounds array and member table + node->boundsArray.resize(node->valueArray.size()); + for (int i = 0; i < node->valueArray.size(); ++i) { + Handle* v = node->valueArray[i]; + node->boundsArray[i] = v->bounds; + memberTable.set(Member(v), node); + } + + if (lt.size() > 0) { + node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp); + } + + if (gt.size() > 0) { + node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp); + } + + } + + return node; + } + + /** + Recursively clone the passed in node tree, setting + pointers for members in the memberTable as appropriate. + called by the assignment operator. + */ + Node* cloneTree(Node* src) { + Node* dst = new Node(*src); + + // Make back pointers + for (int i = 0; i < dst->valueArray.size(); ++i) { + memberTable.set(Member(dst->valueArray[i]), dst); + } + + // Clone children + for (int i = 0; i < 2; ++i) { + if (src->child[i] != NULL) { + dst->child[i] = cloneTree(src->child[i]); + } + } + + return dst; + } + + /** + Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but + stores only Handle* internally to avoid memory copies. + */ + typedef _internal::Indirector<Handle> Member; + + typedef Table<Member, Node*> MemberTable; + + /** Maps members to the node containing them */ + MemberTable memberTable; + + Node* root; + +public: + + /** To construct a balanced tree, insert the elements and then call + KDTree::balance(). */ + KDTree() : root(NULL) {} + + + KDTree(const KDTree& src) : root(NULL) { + *this = src; + } + + + KDTree& operator=(const KDTree& src) { + delete root; + // Clone tree takes care of filling out the memberTable. + root = cloneTree(src.root); + return *this; + } + + + ~KDTree() { + clear(); + } + + /** + Throws out all elements of the set. + */ + void clear() { + typedef typename Table<_internal::Indirector<Handle>, Node*>::Iterator It; + + // Delete all handles stored in the member table + It cur = memberTable.begin(); + It end = memberTable.end(); + while (cur != end) { + delete cur->key.handle; + cur->key.handle = NULL; + ++cur; + } + memberTable.clear(); + + // Delete the tree structure itself + delete root; + root = NULL; + } + + int size() const { + return memberTable.size(); + } + + /** + Inserts an object into the set if it is not + already present. O(log n) time. Does not + cause the tree to be balanced. + */ + void insert(const T& value) { + if (contains(value)) { + // Already in the set + return; + } + + Handle* h = new Handle(value); + + if (root == NULL) { + // This is the first node; create a root node + root = new Node(); + } + + Node* node = root->findDeepestContainingNode(h->bounds); + + // Insert into the node + node->valueArray.append(h); + node->boundsArray.append(h->bounds); + + // Insert into the node table + Member m(h); + memberTable.set(m, node); + } + + /** Inserts each elements in the array in turn. If the tree + begins empty (no structure and no elements), this is faster + than inserting each element in turn. You still need to balance + the tree at the end.*/ + void insert(const Array<T>& valueArray) { + if (root == NULL) { + // Optimized case for an empty tree; don't bother + // searching or reallocating the root node's valueArray + // as we incrementally insert. + root = new Node(); + root->valueArray.resize(valueArray.size()); + root->boundsArray.resize(root->valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + // Insert in opposite order so that we have the exact same + // data structure as if we inserted each (i.e., order is reversed + // from array). + Handle* h = new Handle(valueArray[i]); + int j = valueArray.size() - i - 1; + root->valueArray[j] = h; + root->boundsArray[j] = h->bounds; + memberTable.set(Member(h), root); + } + + } else { + // Insert at appropriate tree depth. + for (int i = 0; i < valueArray.size(); ++i) { + insert(valueArray[i]); + } + } + } + + + /** + Returns true if this object is in the set, otherwise + returns false. O(1) time. + */ + bool contains(const T& value) { + // Temporarily create a handle and member + Handle h(value); + return memberTable.containsKey(Member(&h)); + } + + + /** + Removes an object from the set in O(1) time. + It is an error to remove members that are not already + present. May unbalance the tree. + + Removing an element never causes a node (split plane) to be removed... + nodes are only changed when the tree is rebalanced. This behavior + is desirable because it allows the split planes to be serialized, + and then deserialized into an empty tree which can be repopulated. + */ + void remove(const T& value) { + debugAssertM(contains(value), + "Tried to remove an element from a " + "KDTree that was not present"); + + // Get the list of elements at the node + Handle h(value); + Member m(&h); + + Array<Handle*>& list = memberTable[m]->valueArray; + + Handle* ptr = NULL; + + // Find the element and remove it + for (int i = list.length() - 1; i >= 0; --i) { + if (list[i]->value == value) { + // This was the element. Grab the pointer so that + // we can delete it below + ptr = list[i]; + + // Remove the handle from the node + list.fastRemove(i); + + // Remove the corresponding bounds + memberTable[m]->boundsArray.fastRemove(i); + break; + } + } + + // Remove the member + memberTable.remove(m); + + // Delete the handle data structure + delete ptr; + ptr = NULL; + } + + + /** + If the element is in the set, it is removed. + The element is then inserted. + + This is useful when the == and hashCode methods + on <I>T</I> are independent of the bounds. In + that case, you may call update(v) to insert an + element for the first time and call update(v) + again every time it moves to keep the tree + up to date. + */ + void update(const T& value) { + if (contains(value)) { + remove(value); + } + insert(value); + } + + + /** + Rebalances the tree (slow). Call when objects + have moved substantially from their original positions + (which unbalances the tree and causes the spatial + queries to be slow). + + @param valuesPerNode Maximum number of elements to put at + a node. + + @param numMeanSplits numMeanSplits = 0 gives a + fully axis aligned BSP-tree, where the balance operation attempts to balance + the tree so that every splitting plane has an equal number of left + and right children (i.e. it is a <B>median</B> split along that axis). + This tends to maximize average performance. + + You can override this behavior by + setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT + creates a full oct-tree, which tends to optimize peak performance at the expense of + average performance. It tends to have better clustering behavior when + members are not uniformly distributed. + */ + void balance(int valuesPerNode = 5, int numMeanSplits = 3) { + if (root == NULL) { + // Tree is empty + return; + } + + // Get all handles and delete the old tree structure + Node* oldRoot = root; + for (int c = 0; c < 2; ++c) { + if (root->child[c] != NULL) { + root->child[c]->getHandles(root->valueArray); + + // Delete the child; this will delete all structure below it + delete root->child[c]; + root->child[c] = NULL; + } + } + + Array<Handle*> temp; + // Make a new root. Work with a copy of the value array because + // makeNode clears the source array as it progresses + Array<Handle*> copy(oldRoot->valueArray); + root = makeNode(copy, valuesPerNode, numMeanSplits, temp); + + // Throw away the old root node + delete oldRoot; + oldRoot = NULL; + + // Walk the tree, assigning splitBounds. We start with unbounded + // space. This will override the current member table. + const AABox& LARGE = AABox::large(); + root->assignSplitBounds(LARGE); + +# ifdef _DEBUG + { + // Ensure that the balanced tree is still correct + root->verifyNode(LARGE.low(), LARGE.high()); + } +# endif + } + +protected: + + /** + @param parentMask The mask that this node returned from culledBy. + */ + static void getIntersectingMembers( + const Array<Plane>& plane, + Array<T>& members, + Node* node, + uint32 parentMask) { + + int dummy; + + if (parentMask == 0) { + // None of these planes can cull anything + for (int v = node->valueArray.size() - 1; v >= 0; --v) { + members.append(node->valueArray[v]->value); + } + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c]) { + getIntersectingMembers(plane, members, node->child[c], 0); + } + } + } else { + + // Test values at this node against remaining planes + for (int v = node->boundsArray.size() - 1; v >= 0; --v) { + if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) { + members.append(node->valueArray[v]->value); + } + } + + uint32 childMask = 0xFFFFFF; + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c] && + ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) { + // This node was not culled + getIntersectingMembers(plane, members, node->child[c], childMask); + } + } + } + } + +public: + + /** + Returns all members inside the set of planes. + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const { + if (root == NULL) { + return; + } + + getIntersectingMembers(plane, members, root, 0xFFFFFF); + } + + /** + Typically used to find all visible + objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects + <B>not<B> culled by frustum. + + Example: + <PRE> + Array<Object*> visible; + tree.getIntersectingMembers(camera.frustum(), visible); + // ... Draw all objects in the visible array. + </PRE> + @param members The results are appended to this array. + */ + void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const { + Array<Plane> plane; + + for (int i = 0; i < frustum.faceArray.size(); ++i) { + plane.append(frustum.faceArray[i].plane); + } + + getIntersectingMembers(plane, members); + } + + /** + C++ STL style iterator variable. See beginBoxIntersection(). + The iterator overloads the -> (dereference) operator, so this + acts like a pointer to the current member. + */ + // This iterator turns Node::getIntersectingMembers into a + // coroutine. It first translates that method from recursive to + // stack based, then captures the system state (analogous to a Scheme + // continuation) after each element is appended to the member array, + // and allowing the computation to be restarted. + class BoxIntersectionIterator { + private: + friend class TreeType; + + /** True if this is the "end" iterator instance */ + bool isEnd; + + /** The box that we're testing against. */ + AABox box; + + /** Node that we're currently looking at. Undefined if isEnd + is true. */ + Node* node; + + /** Nodes waiting to be processed */ + // We could use backpointers within the tree and careful + // state management to avoid ever storing the stack-- but + // it is much easier this way and only inefficient if the + // caller uses post increment (which they shouldn't!). + Array<Node*> stack; + + /** The next index of current->valueArray to return. + Undefined when isEnd is true.*/ + int nextValueArrayIndex; + + BoxIntersectionIterator() : isEnd(true) {} + + BoxIntersectionIterator(const AABox& b, const Node* root) : + isEnd(root == NULL), box(b), + node(const_cast<Node*>(root)), nextValueArrayIndex(-1) { + + // We intentionally start at the "-1" index of the current + // node so we can use the preincrement operator to move + // ourselves to element 0 instead of repeating all of the + // code from the preincrement method. Note that this might + // cause us to become the "end" instance. + ++(*this); + } + + public: + + inline bool operator!=(const BoxIntersectionIterator& other) const { + return ! (*this == other); + } + + bool operator==(const BoxIntersectionIterator& other) const { + if (isEnd) { + return other.isEnd; + } else if (other.isEnd) { + return false; + } else { + // Two non-end iterators; see if they match. This is kind of + // silly; users shouldn't call == on iterators in general unless + // one of them is the end iterator. + if ((box != other.box) || (node != other.node) || + (nextValueArrayIndex != other.nextValueArrayIndex) || + (stack.length() != other.stack.length())) { + return false; + } + + // See if the stacks are the same + for (int i = 0; i < stack.length(); ++i) { + if (stack[i] != other.stack[i]) { + return false; + } + } + + // We failed to find a difference; they must be the same + return true; + } + } + + /** + Pre increment. + */ + BoxIntersectionIterator& operator++() { + ++nextValueArrayIndex; + + bool foundIntersection = false; + while (! isEnd && ! foundIntersection) { + + // Search for the next node if we've exhausted this one + while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) { + // If we entered this loop, then the iterator has exhausted the elements at + // node (possibly because it just switched to a child node with no members). + // This loop continues until it finds a node with members or reaches + // the end of the whole intersection search. + + // If the right child overlaps the box, push it onto the stack for + // processing. + if ((node->child[1] != NULL) && + (box.high()[node->splitAxis] > node->splitLocation)) { + stack.push(node->child[1]); + } + + // If the left child overlaps the box, push it onto the stack for + // processing. + if ((node->child[0] != NULL) && + (box.low()[node->splitAxis] < node->splitLocation)) { + stack.push(node->child[0]); + } + + if (stack.length() > 0) { + // Go on to the next node (which may be either one of the ones we + // just pushed, or one from farther back the tree). + node = stack.pop(); + nextValueArrayIndex = 0; + } else { + // That was the last node; we're done iterating + isEnd = true; + } + } + + // Search for the next intersection at this node until we run out of children + while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) { + if (box.intersects(node->boundsArray[nextValueArrayIndex])) { + foundIntersection = true; + } else { + ++nextValueArrayIndex; + // If we exhaust this node, we'll loop around the master loop + // to find a new node. + } + } + } + + return *this; + } + + private: + /** + Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code. + */ + BoxIntersectionIterator operator++(int); + /*{ + BoxIntersectionIterator old = *this; + ++this; + return old; + }*/ + + public: + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + const T& operator*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return node->valueArray[nextValueArrayIndex]->value; + } + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + T const * operator->() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex]->value); + } + + /** Overloaded cast operator so the iterator can masquerade as a pointer + to a member */ + operator T*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex]->value); + } + }; + + + /** + Iterates through the members that intersect the box + */ + BoxIntersectionIterator beginBoxIntersection(const AABox& box) const { + return BoxIntersectionIterator(box, root); + } + + BoxIntersectionIterator endBoxIntersection() const { + // The "end" iterator instance + return BoxIntersectionIterator(); + } + + /** + Appends all members whose bounds intersect the box. + See also KDTree::beginBoxIntersection. + */ + void getIntersectingMembers(const AABox& box, Array<T>& members) const { + if (root == NULL) { + return; + } + root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false); + } + + + /** + Invoke a callback for every member along a ray until the closest intersection is found. + + @param callback either a function or an instance of a class with an overloaded operator() of the form: + + <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object + before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to + the intersection, otherwise leaves it unmodified. A common example is: + + <pre> + class Entity { + public: + + void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) { + float d = maxDist; + + // ... search for intersection distance d + + if ((d > 0) && (d < maxDist)) { + // Intersection occured + maxDist = d; + outLocation = ...; + outNormal = ...; + } + } + }; + + // Finds the surface normal and location of the first intersection with the scene + class Intersection { + public: + Entity* closestEntity; + Vector3 hitLocation; + Vector3 hitNormal; + + void operator()(const Ray& ray, const Entity* entity, float& distance) { + entity->intersect(ray, distance, hitLocation, hitNormal); + } + }; + + KDTree<Entity*> scene; + + Intersection intersection; + float distance = inf(); + scene.intersectRay(camera.worldRay(x, y), intersection, distance); + </pre> + + + @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection. + On return, this is set to the distance to the first intersection encountered. + + @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked. + If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true. + */ + template<typename RayCallback> + void intersectRay( + const Ray& ray, + RayCallback& intersectCallback, + float& distance, + bool intersectCallbackIsFast = false) const { + + root->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast); + + } + + + /** + @brief Finds all members whose bounding boxes intersect the sphere. The actual + elements may not intersect the sphere. + + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const { + if (root == NULL) { + return; + } + + AABox box; + sphere.getBounds(box); + root->getIntersectingMembers(box, sphere, members, true); + + } + + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + void serializeStructure(BinaryOutput& bo) const { + Node::serializeStructure(root, bo); + } + + /** Clears the member table */ + void deserializeStructure(BinaryInput& bi) { + clear(); + root = Node::deserializeStructure(bi); + } + + /** + Returns an array of all members of the set. See also KDTree::begin. + */ + void getMembers(Array<T>& members) const { + Array<Member> temp; + memberTable.getKeys(temp); + for (int i = 0; i < temp.size(); ++i) { + members.append(*(temp.handle)); + } + } + + + /** + C++ STL style iterator variable. See begin(). + Overloads the -> (dereference) operator, so this acts like a pointer + to the current member. + */ + class Iterator { + private: + friend class TreeType; + + // Note: this is a Table iterator, we are currently defining + // Set iterator + typename Table<Member, Node*>::Iterator it; + + Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {} + + public: + + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + bool operator==(const Iterator& other) const { + return it == other.it; + } + + /** + Pre increment. + */ + Iterator& operator++() { + ++it; + return *this; + } + + private: + /** + Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code. + */ + Iterator operator++(int);/* { + Iterator old = *this; + ++(*this); + return old; + }*/ + public: + + const T& operator*() const { + return it->key.handle->value; + } + + T* operator->() const { + return &(it->key.handle->value); + } + + operator T*() const { + return &(it->key.handle->value); + } + }; + + + /** + C++ STL style iterator method. Returns the first member. + Use preincrement (++entry) to get to the next element (iteration + order is arbitrary). + Do not modify the set while iterating. + */ + Iterator begin() const { + return Iterator(memberTable.begin()); + } + + + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + Iterator end() const { + return Iterator(memberTable.end()); + } +#undef TreeType +}; + +/** @deprecated For backwards compatibility */ +#define AABSPTree KDTree + +} + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABox.h b/externals/g3dlite/G3D.lib/include/G3D/AABox.h new file mode 100644 index 00000000000..76c5d6d5195 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AABox.h @@ -0,0 +1,281 @@ +/** + @file AABox.h + + Axis-aligned box class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-10 + @edited 2006-02-10 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#ifndef G3D_AABOX_H +#define G3D_AABOX_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/debug.h" +#include "G3D/Array.h" +#include "G3D/Plane.h" + +namespace G3D { + +/** + An axis-aligned box. + */ +class AABox { +private: + + /** Optional argument placeholder */ + static int dummy; + + Vector3 lo; + Vector3 hi; + +public: + + /** Does not initialize the fields */ + inline AABox() {} + + /** + Constructs a zero-area AABox at v. + */ + inline explicit AABox(const Vector3& v) { + lo = hi = v; + } + + /** Assumes that low is less than or equal to high along each dimension. + To have this automatically enforced, use + <code>AABox(low.min(high), low.max(high));</code> + */ + inline AABox(const Vector3& low, const Vector3& high) { + set(low, high); + } + + /** Assumes that low is less than or equal to high along each dimension. + */ + inline void set(const Vector3& low, const Vector3& high) { + debugAssert( + (low.x <= high.x) && + (low.y <= high.y) && + (low.z <= high.z)); + lo = low; + hi = high; + } + + /** + Grows to include the bounds of a + */ + inline void merge(const AABox& a) { + lo = lo.min(a.lo); + hi = hi.max(a.hi); + } + + inline void merge(const Vector3& a) { + lo = lo.min(a); + hi = hi.max(a); + } + + void serialize(class BinaryOutput& b) const; + + void deserialize(class BinaryInput& b); + + inline const Vector3& low() const { + return lo; + } + + inline const Vector3& high() const { + return hi; + } + + /** + The largest possible finite box. + */ + static inline const AABox& maxFinite() { + static const AABox b = AABox(Vector3::minFinite(), + Vector3::maxFinite()); + return b; + } + + /** A large finite box. This is smaller than FLT_MAX + because it leaves room to add boxes together. */ + static inline const AABox& large() { + static const AABox b = AABox(Vector3::minFinite() * 0.5f, + Vector3::maxFinite() * 0.5f); + return b; + } + + static inline const AABox& inf() { + static const AABox b = AABox(-Vector3::inf(), Vector3::inf()); + return b; + } + + static inline const AABox& zero() { + static const AABox b = AABox(Vector3::zero(), Vector3::zero()); + return b; + } + + /** + Returns the centroid of the box. + */ + inline Vector3 center() const { + return (lo + hi) * 0.5; + } + + Vector3 corner(int index) const; + + /** + Distance from corner(0) to the next corner along axis a. + */ + inline float extent(int a) const { + debugAssert(a < 3); + return hi[a] - lo[a]; + } + + + inline Vector3 extent() const { + return hi - lo; + } + + + /** + Splits the box into two AABoxes along the specified axis. low contains + the part that was closer to negative infinity along axis, high contains + the other part. Either may have zero volume. + */ + void split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const; + + /** + Conservative culling test for up to 32 planes. + Returns true if there exists a <CODE>plane[p]</CODE> for + which the entire object is in the negative half space + (opposite the plane normal). + + <CODE>testMask</CODE> and <CODE>childMask</CODE> + are used for optimizing bounding volume hierarchies. + The version of this method that produces childMask + is slower than the version without; it should only + be used for parent nodes. + + @param cullingPlaneIndex The index of the first plane for which + the entire object is in the negative half-space. The function + exits early when one plane is found. -1 when the function + returns false (i.e. when no plane culls the whole object). + + @param testMask If bit <I>p</I> is 0, the + bounding volume automatically passes the culling test for + <CODE>plane[p]</CODE> (i.e. it is known that the volume + is entirely within the positive half space). The function + must return false if testMask is 0 and test all planes + when testMask is -1 (0xFFFFFFFF). + + @param childMask Test mask for the children of this volume. + + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex, + const uint32 testMask, + uint32& childMask) const; + + /** + Conservative culling test that does not produce a mask for children. + */ + bool culledBy( + const Array<Plane>& plane, + int32& cullingPlaneIndex = dummy, + const uint32 testMask = 0xFFFFFFFF) const; + + /** less than or equal to containment */ + inline bool contains(const AABox& other) const { + return + (other.hi.x <= hi.x) && + (other.hi.y <= hi.y) && + (other.hi.z <= hi.z) && + (other.lo.x >= lo.x) && + (other.lo.y >= lo.y) && + (other.lo.z >= lo.z); + } + + inline bool contains( + const Vector3& point) const { + return + (point.x >= lo.x) && + (point.y >= lo.y) && + (point.z >= lo.z) && + (point.x <= hi.x) && + (point.y <= hi.y) && + (point.z <= hi.z); + } + + inline float area() const { + Vector3 diag = hi - lo; + return 2.0f * (diag.x * diag.y + diag.y * diag.z + diag.x * diag.z); + } + + inline float volume() const { + Vector3 diag = hi - lo; + return diag.x * diag.y * diag.z; + } + + Vector3 randomInteriorPoint() const; + + Vector3 randomSurfacePoint() const; + + /** Returns true if there is any overlap */ + bool intersects(const AABox& other) const; + + /** Returns true if there is any overlap. + @cite Jim Arvo's algorithm from Graphics Gems II*/ + bool intersects(const class Sphere& other) const; + + /** Return the intersection of the two boxes */ + AABox intersect(const AABox& other) const { + Vector3 H = hi.min(other.hi); + Vector3 L = lo.max(other.lo).min(H); + return AABox(L, H); + } + + inline size_t hashCode() const { + return lo.hashCode() + hi.hashCode(); + } + + inline bool operator==(const AABox& b) const { + return (lo == b.lo) && (hi == b.hi); + } + + inline bool operator!=(const AABox& b) const { + return !((lo == b.lo) && (hi == b.hi)); + } + + inline AABox operator+(const Vector3& v) const { + AABox out; + out.lo = lo + v; + out.hi = hi + v; + return out; + } + + inline AABox operator-(const Vector3& v) const { + AABox out; + out.lo = lo - v; + out.hi = hi - v; + return out; + } + + void getBounds(AABox& out) const { + out = *this; + } +}; + +} + +template <> struct HashTrait<G3D::AABox> { + static size_t hashCode(const G3D::AABox& key) { return key.hashCode(); } +}; + + + +#endif diff --git a/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h new file mode 100644 index 00000000000..8254dd73c93 --- /dev/null +++ b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h @@ -0,0 +1,506 @@ +/** + @file AnyVal.h + @author Morgan McGuire + @created 2006-06-11 + @edited 2008-07-14 + */ + +#ifndef G3D_ANYVAL_H +#define G3D_ANYVAL_H + +#include "G3D/platform.h" +#include <string> +#include "G3D/Array.h" +#include "G3D/TextInput.h" + +namespace G3D { +// Forward declarations for G3D types +class Vector2; +class Vector3; +class Vector4; +class Color1; +class Color3; +class Color4; +class Quat; +class Matrix2; +class Matrix3; +class Matrix4; +class CoordinateFrame; +class TextInput; +class TextOutput; +class BinaryInput; +class BinaryOutput; +class Rect2D; +class AABox; + +/** + A generic value, useful for defining property trees that can + be loaded from and saved to disk. The values are intentionally + restricted to a small set. + + When written to files, the syntax is as follows. Note that you can + nest arrays and tables in order to create full tree (i.e., XML-like) + structures as configuration files: + + <table> + <tr><td>NULL</td><td><code>Nil</code></td></tr> + <tr><td>double</td><td><i>The number in printf double format</i></td></tr> + <tr><td>bool</td><td><code>true</code> <i>or</i> <code>false</code></td></tr> + <tr><td>std::string</td><td><i>The string in double-quotes (</i><code>"</code><i>)</i></td></tr> + <tr><td>Rect2D</td><td><code>R(</code><i>x<sub>0</sub></i><code>,</code><i>y<sub>0</sub></i><code>,</code><i>x<sub>1</sub></i><code>,</code><i>y<sub>1</sub></i><code>)</code></td></tr> + <tr><td>Color1</td><td><code>C1(</code><i>value</i><code>)</code></td></tr> + <tr><td>Color3</td><td><code>C3(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>)</code></td></tr> + <tr><td>Color4</td><td><code>C4(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>,</code><i>a</i><code>)</code></td></tr> + <tr><td>Vector2</td><td><code>V2(</code><i>x</i><code>,</code><i>y</i><code>)</code></td></tr> + <tr><td>Vector3</td><td><code>V3(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>)</code></td></tr> + <tr><td>Vector4</td><td><code>V4(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>,</code><i>w</i><code>)</code></td></tr> + <tr><td>Quat</td><td><code>V(</code>x<code>,</code>y<code>,</code>z<code>,</code>w<code>)</code></td></tr> + <tr><td>AABox</td><td><code>AAB(</code>low Vector3<code>, </code>high Vector3<code>)</code></td></tr> + <tr><td>Matrix2</td><td><code>M2(</code>r0c0<code>, </code>r0c1<code>, + <br> </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; +} diff --git a/externals/jemalloc/CMakeLists.txt b/externals/jemalloc/CMakeLists.txt new file mode 100644 index 00000000000..c3e4e81782c --- /dev/null +++ b/externals/jemalloc/CMakeLists.txt @@ -0,0 +1,27 @@ +SET(jmalloc_STAT_SRC + arena.c + chunk.c + chunk_mmap.c + ckh.c + extent.c + huge.c + mb.c + prof.c + tcache.c + base.c + chunk_dss.c + chunk_swap.c + ctl.c + hash.c + jemalloc.c + mutex.c + stats.c + ) + +include_directories( + ${CMAKE_SOURCE_DIR}/dep/include + ) + +add_definitions(-D_GNU_SOURCE -D_REENTRANT) + +add_library(jmalloc STATIC ${jmalloc_STAT_SRC})
\ No newline at end of file diff --git a/externals/jemalloc/arena.c b/externals/jemalloc/arena.c new file mode 100644 index 00000000000..e74b4701907 --- /dev/null +++ b/externals/jemalloc/arena.c @@ -0,0 +1,2446 @@ +#define JEMALLOC_ARENA_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +size_t opt_lg_qspace_max = LG_QSPACE_MAX_DEFAULT; +size_t opt_lg_cspace_max = LG_CSPACE_MAX_DEFAULT; +ssize_t opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT; +uint8_t const *small_size2bin; + +/* Various bin-related settings. */ +unsigned nqbins; +unsigned ncbins; +unsigned nsbins; +unsigned nbins; +size_t qspace_max; +size_t cspace_min; +size_t cspace_max; +size_t sspace_min; +size_t sspace_max; + +size_t lg_mspace; +size_t mspace_mask; + +/* + * const_small_size2bin is a static constant lookup table that in the common + * case can be used as-is for small_size2bin. For dynamically linked programs, + * this avoids a page of memory overhead per process. + */ +#define S2B_1(i) i, +#define S2B_2(i) S2B_1(i) S2B_1(i) +#define S2B_4(i) S2B_2(i) S2B_2(i) +#define S2B_8(i) S2B_4(i) S2B_4(i) +#define S2B_16(i) S2B_8(i) S2B_8(i) +#define S2B_32(i) S2B_16(i) S2B_16(i) +#define S2B_64(i) S2B_32(i) S2B_32(i) +#define S2B_128(i) S2B_64(i) S2B_64(i) +#define S2B_256(i) S2B_128(i) S2B_128(i) +/* + * The number of elements in const_small_size2bin is dependent on page size + * and on the definition for SUBPAGE. If SUBPAGE changes, the '- 255' must also + * change, along with the addition/removal of static lookup table element + * definitions. + */ +static const uint8_t const_small_size2bin[STATIC_PAGE_SIZE - 255] = { + S2B_1(0xffU) /* 0 */ +#if (LG_QUANTUM == 4) +/* 16-byte quantum **********************/ +# ifdef JEMALLOC_TINY +# if (LG_TINY_MIN == 2) + S2B_4(0) /* 4 */ + S2B_4(1) /* 8 */ + S2B_8(2) /* 16 */ +# define S2B_QMIN 2 +# elif (LG_TINY_MIN == 3) + S2B_8(0) /* 8 */ + S2B_8(1) /* 16 */ +# define S2B_QMIN 1 +# else +# error "Unsupported LG_TINY_MIN" +# endif +# else + S2B_16(0) /* 16 */ +# define S2B_QMIN 0 +# endif + S2B_16(S2B_QMIN + 1) /* 32 */ + S2B_16(S2B_QMIN + 2) /* 48 */ + S2B_16(S2B_QMIN + 3) /* 64 */ + S2B_16(S2B_QMIN + 4) /* 80 */ + S2B_16(S2B_QMIN + 5) /* 96 */ + S2B_16(S2B_QMIN + 6) /* 112 */ + S2B_16(S2B_QMIN + 7) /* 128 */ +# define S2B_CMIN (S2B_QMIN + 8) +#else +/* 8-byte quantum ***********************/ +# ifdef JEMALLOC_TINY +# if (LG_TINY_MIN == 2) + S2B_4(0) /* 4 */ + S2B_4(1) /* 8 */ +# define S2B_QMIN 1 +# else +# error "Unsupported LG_TINY_MIN" +# endif +# else + S2B_8(0) /* 8 */ +# define S2B_QMIN 0 +# endif + S2B_8(S2B_QMIN + 1) /* 16 */ + S2B_8(S2B_QMIN + 2) /* 24 */ + S2B_8(S2B_QMIN + 3) /* 32 */ + S2B_8(S2B_QMIN + 4) /* 40 */ + S2B_8(S2B_QMIN + 5) /* 48 */ + S2B_8(S2B_QMIN + 6) /* 56 */ + S2B_8(S2B_QMIN + 7) /* 64 */ + S2B_8(S2B_QMIN + 8) /* 72 */ + S2B_8(S2B_QMIN + 9) /* 80 */ + S2B_8(S2B_QMIN + 10) /* 88 */ + S2B_8(S2B_QMIN + 11) /* 96 */ + S2B_8(S2B_QMIN + 12) /* 104 */ + S2B_8(S2B_QMIN + 13) /* 112 */ + S2B_8(S2B_QMIN + 14) /* 120 */ + S2B_8(S2B_QMIN + 15) /* 128 */ +# define S2B_CMIN (S2B_QMIN + 16) +#endif +/****************************************/ + S2B_64(S2B_CMIN + 0) /* 192 */ + S2B_64(S2B_CMIN + 1) /* 256 */ + S2B_64(S2B_CMIN + 2) /* 320 */ + S2B_64(S2B_CMIN + 3) /* 384 */ + S2B_64(S2B_CMIN + 4) /* 448 */ + S2B_64(S2B_CMIN + 5) /* 512 */ +# define S2B_SMIN (S2B_CMIN + 6) + S2B_256(S2B_SMIN + 0) /* 768 */ + S2B_256(S2B_SMIN + 1) /* 1024 */ + S2B_256(S2B_SMIN + 2) /* 1280 */ + S2B_256(S2B_SMIN + 3) /* 1536 */ + S2B_256(S2B_SMIN + 4) /* 1792 */ + S2B_256(S2B_SMIN + 5) /* 2048 */ + S2B_256(S2B_SMIN + 6) /* 2304 */ + S2B_256(S2B_SMIN + 7) /* 2560 */ + S2B_256(S2B_SMIN + 8) /* 2816 */ + S2B_256(S2B_SMIN + 9) /* 3072 */ + S2B_256(S2B_SMIN + 10) /* 3328 */ + S2B_256(S2B_SMIN + 11) /* 3584 */ + S2B_256(S2B_SMIN + 12) /* 3840 */ +#if (STATIC_PAGE_SHIFT == 13) + S2B_256(S2B_SMIN + 13) /* 4096 */ + S2B_256(S2B_SMIN + 14) /* 4352 */ + S2B_256(S2B_SMIN + 15) /* 4608 */ + S2B_256(S2B_SMIN + 16) /* 4864 */ + S2B_256(S2B_SMIN + 17) /* 5120 */ + S2B_256(S2B_SMIN + 18) /* 5376 */ + S2B_256(S2B_SMIN + 19) /* 5632 */ + S2B_256(S2B_SMIN + 20) /* 5888 */ + S2B_256(S2B_SMIN + 21) /* 6144 */ + S2B_256(S2B_SMIN + 22) /* 6400 */ + S2B_256(S2B_SMIN + 23) /* 6656 */ + S2B_256(S2B_SMIN + 24) /* 6912 */ + S2B_256(S2B_SMIN + 25) /* 7168 */ + S2B_256(S2B_SMIN + 26) /* 7424 */ + S2B_256(S2B_SMIN + 27) /* 7680 */ + S2B_256(S2B_SMIN + 28) /* 7936 */ +#endif +}; +#undef S2B_1 +#undef S2B_2 +#undef S2B_4 +#undef S2B_8 +#undef S2B_16 +#undef S2B_32 +#undef S2B_64 +#undef S2B_128 +#undef S2B_256 +#undef S2B_QMIN +#undef S2B_CMIN +#undef S2B_SMIN + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static void arena_run_split(arena_t *arena, arena_run_t *run, size_t size, + bool large, bool zero); +static arena_chunk_t *arena_chunk_alloc(arena_t *arena); +static void arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk); +static arena_run_t *arena_run_alloc(arena_t *arena, size_t size, bool large, + bool zero); +static void arena_purge(arena_t *arena); +static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty); +static void arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, + arena_run_t *run, size_t oldsize, size_t newsize); +static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, + arena_run_t *run, size_t oldsize, size_t newsize, bool dirty); +static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin); +static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin); +static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size); +static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, + arena_run_t *run, arena_bin_t *bin); +static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, + void *ptr, size_t size, size_t oldsize); +static bool arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, + void *ptr, size_t size, size_t oldsize); +static bool arena_ralloc_large(void *ptr, size_t size, size_t oldsize); +#ifdef JEMALLOC_TINY +static size_t pow2_ceil(size_t x); +#endif +static bool small_size2bin_init(void); +#ifdef JEMALLOC_DEBUG +static void small_size2bin_validate(void); +#endif +static bool small_size2bin_init_hard(void); + +/******************************************************************************/ + +static inline int +arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) +{ + uintptr_t a_mapelm = (uintptr_t)a; + uintptr_t b_mapelm = (uintptr_t)b; + + assert(a != NULL); + assert(b != NULL); + + return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm)); +} + +/* Generate red-black tree functions. */ +rb_gen(static JEMALLOC_ATTR(unused), arena_run_tree_, arena_run_tree_t, + arena_chunk_map_t, u.rb_link, arena_run_comp) + +static inline int +arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) +{ + int ret; + size_t a_size = a->bits & ~PAGE_MASK; + size_t b_size = b->bits & ~PAGE_MASK; + + assert((a->bits & CHUNK_MAP_KEY) == CHUNK_MAP_KEY || (a->bits & + CHUNK_MAP_DIRTY) == (b->bits & CHUNK_MAP_DIRTY)); + + ret = (a_size > b_size) - (a_size < b_size); + if (ret == 0) { + uintptr_t a_mapelm, b_mapelm; + + if ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY) + a_mapelm = (uintptr_t)a; + else { + /* + * Treat keys as though they are lower than anything + * else. + */ + a_mapelm = 0; + } + b_mapelm = (uintptr_t)b; + + ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm); + } + + return (ret); +} + +/* Generate red-black tree functions. */ +rb_gen(static JEMALLOC_ATTR(unused), arena_avail_tree_, arena_avail_tree_t, + arena_chunk_map_t, u.rb_link, arena_avail_comp) + +static inline void * +arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) +{ + void *ret; + + assert(run->magic == ARENA_RUN_MAGIC); + assert(run->nfree > 0); + + run->nfree--; + ret = run->avail; + if (ret != NULL) { + run->avail = *(void **)ret; + /* Double free can cause assertion failure.*/ + assert(ret != NULL); + /* Write-after free can cause assertion failure. */ + assert((uintptr_t)ret >= (uintptr_t)run + + (uintptr_t)bin->reg0_offset); + assert((uintptr_t)ret < (uintptr_t)run->next); + assert(((uintptr_t)ret - ((uintptr_t)run + + (uintptr_t)bin->reg0_offset)) % (uintptr_t)bin->reg_size == + 0); + return (ret); + } + ret = run->next; + run->next = (void *)((uintptr_t)ret + (uintptr_t)bin->reg_size); + assert(ret != NULL); + return (ret); +} + +static inline void +arena_run_reg_dalloc(arena_run_t *run, void *ptr) +{ + + assert(run->nfree < run->bin->nregs); + /* Freeing an interior pointer can cause assertion failure. */ + assert(((uintptr_t)ptr - ((uintptr_t)run + + (uintptr_t)run->bin->reg0_offset)) % (uintptr_t)run->bin->reg_size + == 0); + + *(void **)ptr = run->avail; + run->avail = ptr; + run->nfree++; +} + +static void +arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, + bool zero) +{ + arena_chunk_t *chunk; + size_t old_ndirty, run_ind, total_pages, need_pages, rem_pages, i; + size_t flag_dirty; + arena_avail_tree_t *runs_avail; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + old_ndirty = chunk->ndirty; + run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) + >> PAGE_SHIFT); + flag_dirty = chunk->map[run_ind].bits & CHUNK_MAP_DIRTY; + runs_avail = (flag_dirty != 0) ? &arena->runs_avail_dirty : + &arena->runs_avail_clean; + total_pages = (chunk->map[run_ind].bits & ~PAGE_MASK) >> + PAGE_SHIFT; + assert((chunk->map[run_ind+total_pages-1].bits & CHUNK_MAP_DIRTY) == + flag_dirty); + need_pages = (size >> PAGE_SHIFT); + assert(need_pages > 0); + assert(need_pages <= total_pages); + rem_pages = total_pages - need_pages; + + arena_avail_tree_remove(runs_avail, &chunk->map[run_ind]); + arena->nactive += need_pages; + + /* Keep track of trailing unused pages for later use. */ + if (rem_pages > 0) { + if (flag_dirty != 0) { + chunk->map[run_ind+need_pages].bits = (rem_pages << + PAGE_SHIFT) | CHUNK_MAP_DIRTY; + chunk->map[run_ind+total_pages-1].bits = (rem_pages << + PAGE_SHIFT) | CHUNK_MAP_DIRTY; + } else { + chunk->map[run_ind+need_pages].bits = (rem_pages << + PAGE_SHIFT) | (chunk->map[run_ind+need_pages].bits & + CHUNK_MAP_ZEROED); + chunk->map[run_ind+total_pages-1].bits = (rem_pages << + PAGE_SHIFT) | + (chunk->map[run_ind+total_pages-1].bits & + CHUNK_MAP_ZEROED); + } + arena_avail_tree_insert(runs_avail, + &chunk->map[run_ind+need_pages]); + } + + /* Update dirty page accounting. */ + if (flag_dirty != 0) { + chunk->ndirty -= need_pages; + arena->ndirty -= need_pages; + } + + /* + * Update the page map separately for large vs. small runs, since it is + * possible to avoid iteration for large mallocs. + */ + if (large) { + if (zero) { + if (flag_dirty == 0) { + /* + * The run is clean, so some pages may be + * zeroed (i.e. never before touched). + */ + for (i = 0; i < need_pages; i++) { + if ((chunk->map[run_ind + i].bits & + CHUNK_MAP_ZEROED) == 0) { + memset((void *)((uintptr_t) + chunk + ((run_ind + i) << + PAGE_SHIFT)), 0, + PAGE_SIZE); + } + } + } else { + /* + * The run is dirty, so all pages must be + * zeroed. + */ + memset((void *)((uintptr_t)chunk + (run_ind << + PAGE_SHIFT)), 0, (need_pages << + PAGE_SHIFT)); + } + } + + /* + * Set the last element first, in case the run only contains one + * page (i.e. both statements set the same element). + */ + chunk->map[run_ind+need_pages-1].bits = CHUNK_MAP_LARGE | + CHUNK_MAP_ALLOCATED | flag_dirty; + chunk->map[run_ind].bits = size | CHUNK_MAP_LARGE | +#ifdef JEMALLOC_PROF + CHUNK_MAP_CLASS_MASK | +#endif + CHUNK_MAP_ALLOCATED | flag_dirty; + } else { + assert(zero == false); + /* + * Propagate the dirty flag to the allocated small run, so that + * arena_dalloc_bin_run() has the ability to conditionally trim + * clean pages. + */ + chunk->map[run_ind].bits = CHUNK_MAP_ALLOCATED | flag_dirty; + for (i = 1; i < need_pages - 1; i++) { + chunk->map[run_ind + i].bits = (i << PAGE_SHIFT) + | CHUNK_MAP_ALLOCATED; + } + chunk->map[run_ind + need_pages - 1].bits = ((need_pages - 1) << + PAGE_SHIFT) | CHUNK_MAP_ALLOCATED | flag_dirty; + } +} + +static arena_chunk_t * +arena_chunk_alloc(arena_t *arena) +{ + arena_chunk_t *chunk; + size_t i; + + if (arena->spare != NULL) { + arena_avail_tree_t *runs_avail; + + chunk = arena->spare; + arena->spare = NULL; + + /* Insert the run into the appropriate runs_avail_* tree. */ + if ((chunk->map[arena_chunk_header_npages].bits & + CHUNK_MAP_DIRTY) == 0) + runs_avail = &arena->runs_avail_clean; + else + runs_avail = &arena->runs_avail_dirty; + arena_avail_tree_insert(runs_avail, + &chunk->map[arena_chunk_header_npages]); + } else { + bool zero; + size_t zeroed; + + zero = false; + malloc_mutex_unlock(&arena->lock); + chunk = (arena_chunk_t *)chunk_alloc(chunksize, &zero); + malloc_mutex_lock(&arena->lock); + if (chunk == NULL) + return (NULL); +#ifdef JEMALLOC_STATS + arena->stats.mapped += chunksize; +#endif + + chunk->arena = arena; + ql_elm_new(chunk, link_dirty); + chunk->dirtied = false; + + /* + * Claim that no pages are in use, since the header is merely + * overhead. + */ + chunk->ndirty = 0; + + /* + * Initialize the map to contain one maximal free untouched run. + * Mark the pages as zeroed iff chunk_alloc() returned a zeroed + * chunk. + */ + zeroed = zero ? CHUNK_MAP_ZEROED : 0; + for (i = 0; i < arena_chunk_header_npages; i++) + chunk->map[i].bits = 0; + chunk->map[i].bits = arena_maxclass | zeroed; + for (i++; i < chunk_npages-1; i++) + chunk->map[i].bits = zeroed; + chunk->map[chunk_npages-1].bits = arena_maxclass | zeroed; + + /* Insert the run into the runs_avail_clean tree. */ + arena_avail_tree_insert(&arena->runs_avail_clean, + &chunk->map[arena_chunk_header_npages]); + } + + return (chunk); +} + +static void +arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) +{ + arena_avail_tree_t *runs_avail; + + while (arena->spare != NULL) { + arena_chunk_t *spare = arena->spare; + + arena->spare = NULL; + if (spare->dirtied) { + ql_remove(&chunk->arena->chunks_dirty, spare, + link_dirty); + arena->ndirty -= spare->ndirty; + } + malloc_mutex_unlock(&arena->lock); + chunk_dealloc((void *)spare, chunksize); + malloc_mutex_lock(&arena->lock); +#ifdef JEMALLOC_STATS + arena->stats.mapped -= chunksize; +#endif + } + + /* + * Remove run from the appropriate runs_avail_* tree, so that the arena + * does not use it. + */ + if ((chunk->map[arena_chunk_header_npages].bits & + CHUNK_MAP_DIRTY) == 0) + runs_avail = &arena->runs_avail_clean; + else + runs_avail = &arena->runs_avail_dirty; + arena_avail_tree_remove(runs_avail, + &chunk->map[arena_chunk_header_npages]); + + arena->spare = chunk; +} + +static arena_run_t * +arena_run_alloc(arena_t *arena, size_t size, bool large, bool zero) +{ + arena_chunk_t *chunk; + arena_run_t *run; + arena_chunk_map_t *mapelm, key; + + assert(size <= arena_maxclass); + assert((size & PAGE_MASK) == 0); + + /* Search the arena's chunks for the lowest best fit. */ + key.bits = size | CHUNK_MAP_KEY; + mapelm = arena_avail_tree_nsearch(&arena->runs_avail_dirty, &key); + if (mapelm != NULL) { + arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); + size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map) + / sizeof(arena_chunk_map_t); + + run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << + PAGE_SHIFT)); + arena_run_split(arena, run, size, large, zero); + return (run); + } + mapelm = arena_avail_tree_nsearch(&arena->runs_avail_clean, &key); + if (mapelm != NULL) { + arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); + size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map) + / sizeof(arena_chunk_map_t); + + run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << + PAGE_SHIFT)); + arena_run_split(arena, run, size, large, zero); + return (run); + } + + /* + * No usable runs. Create a new chunk from which to allocate the run. + */ + chunk = arena_chunk_alloc(arena); + if (chunk != NULL) { + run = (arena_run_t *)((uintptr_t)chunk + + (arena_chunk_header_npages << PAGE_SHIFT)); + arena_run_split(arena, run, size, large, zero); + return (run); + } + + /* + * arena_chunk_alloc() failed, but another thread may have made + * sufficient memory available while this one dropped arena->lock in + * arena_chunk_alloc(), so search one more time. + */ + mapelm = arena_avail_tree_nsearch(&arena->runs_avail_dirty, &key); + if (mapelm != NULL) { + arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); + size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map) + / sizeof(arena_chunk_map_t); + + run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << + PAGE_SHIFT)); + arena_run_split(arena, run, size, large, zero); + return (run); + } + mapelm = arena_avail_tree_nsearch(&arena->runs_avail_clean, &key); + if (mapelm != NULL) { + arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); + size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map) + / sizeof(arena_chunk_map_t); + + run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << + PAGE_SHIFT)); + arena_run_split(arena, run, size, large, zero); + return (run); + } + + return (NULL); +} + +static inline void +arena_maybe_purge(arena_t *arena) +{ + + /* Enforce opt_lg_dirty_mult. */ + if (opt_lg_dirty_mult >= 0 && arena->ndirty > arena->npurgatory && + (arena->ndirty - arena->npurgatory) > chunk_npages && + (arena->nactive >> opt_lg_dirty_mult) < (arena->ndirty - + arena->npurgatory)) + arena_purge(arena); +} + +static inline void +arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk) +{ + ql_head(arena_chunk_map_t) mapelms; + arena_chunk_map_t *mapelm; + size_t pageind, flag_zeroed; +#ifdef JEMALLOC_DEBUG + size_t ndirty; +#endif +#ifdef JEMALLOC_STATS + size_t nmadvise; +#endif + + ql_new(&mapelms); + + flag_zeroed = +#ifdef JEMALLOC_SWAP + swap_enabled ? 0 : +#endif + CHUNK_MAP_ZEROED; + + /* + * If chunk is the spare, temporarily re-allocate it, 1) so that its + * run is reinserted into runs_avail_dirty, and 2) so that it cannot be + * completely discarded by another thread while arena->lock is dropped + * by this thread. Note that the arena_run_dalloc() call will + * implicitly deallocate the chunk, so no explicit action is required + * in this function to deallocate the chunk. + * + * Note that once a chunk contains dirty pages, it cannot again contain + * a single run unless 1) it is a dirty run, or 2) this function purges + * dirty pages and causes the transition to a single clean run. Thus + * (chunk == arena->spare) is possible, but it is not possible for + * this function to be called on the spare unless it contains a dirty + * run. + */ + if (chunk == arena->spare) { + assert((chunk->map[arena_chunk_header_npages].bits & + CHUNK_MAP_DIRTY) != 0); + arena_chunk_alloc(arena); + } + + /* Temporarily allocate all free dirty runs within chunk. */ + for (pageind = arena_chunk_header_npages; pageind < chunk_npages;) { + mapelm = &chunk->map[pageind]; + if ((mapelm->bits & CHUNK_MAP_ALLOCATED) == 0) { + size_t npages; + + npages = mapelm->bits >> PAGE_SHIFT; + assert(pageind + npages <= chunk_npages); + if (mapelm->bits & CHUNK_MAP_DIRTY) { + size_t i; + + arena_avail_tree_remove( + &arena->runs_avail_dirty, mapelm); + + /* + * Update internal elements in the page map, so + * that CHUNK_MAP_ZEROED is properly set. + * madvise(..., MADV_DONTNEED) results in + * zero-filled pages for anonymous mappings, + * but not for file-backed mappings. + */ + mapelm->bits = (npages << PAGE_SHIFT) | + CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED | + flag_zeroed; + for (i = 1; i < npages - 1; i++) { + chunk->map[pageind + i].bits = + flag_zeroed; + } + if (npages > 1) { + chunk->map[pageind + npages - 1].bits = + (npages << PAGE_SHIFT) | + CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED | + flag_zeroed; + } + + arena->nactive += npages; + /* Append to list for later processing. */ + ql_elm_new(mapelm, u.ql_link); + ql_tail_insert(&mapelms, mapelm, u.ql_link); + } + + pageind += npages; + } else { + /* Skip allocated run. */ + if (mapelm->bits & CHUNK_MAP_LARGE) + pageind += mapelm->bits >> PAGE_SHIFT; + else { + arena_run_t *run = (arena_run_t *)((uintptr_t) + chunk + (uintptr_t)(pageind << PAGE_SHIFT)); + + assert((mapelm->bits >> PAGE_SHIFT) == 0); + assert(run->magic == ARENA_RUN_MAGIC); + pageind += run->bin->run_size >> PAGE_SHIFT; + } + } + } + assert(pageind == chunk_npages); + +#ifdef JEMALLOC_DEBUG + ndirty = chunk->ndirty; +#endif +#ifdef JEMALLOC_STATS + arena->stats.purged += chunk->ndirty; +#endif + arena->ndirty -= chunk->ndirty; + chunk->ndirty = 0; + ql_remove(&arena->chunks_dirty, chunk, link_dirty); + chunk->dirtied = false; + + malloc_mutex_unlock(&arena->lock); +#ifdef JEMALLOC_STATS + nmadvise = 0; +#endif + ql_foreach(mapelm, &mapelms, u.ql_link) { + size_t pageind = ((uintptr_t)mapelm - (uintptr_t)chunk->map) / + sizeof(arena_chunk_map_t); + size_t npages = mapelm->bits >> PAGE_SHIFT; + + assert(pageind + npages <= chunk_npages); +#ifdef JEMALLOC_DEBUG + assert(ndirty >= npages); + ndirty -= npages; +#endif + madvise((void *)((uintptr_t)chunk + (pageind << PAGE_SHIFT)), + (npages << PAGE_SHIFT), MADV_DONTNEED); +#ifdef JEMALLOC_STATS + nmadvise++; +#endif + } +#ifdef JEMALLOC_DEBUG + assert(ndirty == 0); +#endif + malloc_mutex_lock(&arena->lock); +#ifdef JEMALLOC_STATS + arena->stats.nmadvise += nmadvise; +#endif + + /* Deallocate runs. */ + for (mapelm = ql_first(&mapelms); mapelm != NULL; + mapelm = ql_first(&mapelms)) { + size_t pageind = ((uintptr_t)mapelm - (uintptr_t)chunk->map) / + sizeof(arena_chunk_map_t); + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)(pageind << PAGE_SHIFT)); + + ql_remove(&mapelms, mapelm, u.ql_link); + arena_run_dalloc(arena, run, false); + } +} + +static void +arena_purge(arena_t *arena) +{ + arena_chunk_t *chunk; + size_t npurgatory; +#ifdef JEMALLOC_DEBUG + size_t ndirty = 0; + + ql_foreach(chunk, &arena->chunks_dirty, link_dirty) { + assert(chunk->dirtied); + ndirty += chunk->ndirty; + } + assert(ndirty == arena->ndirty); +#endif + assert(arena->ndirty > arena->npurgatory); + assert(arena->ndirty > chunk_npages); + assert((arena->nactive >> opt_lg_dirty_mult) < arena->ndirty); + +#ifdef JEMALLOC_STATS + arena->stats.npurge++; +#endif + + /* + * Compute the minimum number of pages that this thread should try to + * purge, and add the result to arena->npurgatory. This will keep + * multiple threads from racing to reduce ndirty below the threshold. + */ + npurgatory = (arena->ndirty - arena->npurgatory) - (arena->nactive >> + opt_lg_dirty_mult); + arena->npurgatory += npurgatory; + + while (npurgatory > 0) { + /* Get next chunk with dirty pages. */ + chunk = ql_first(&arena->chunks_dirty); + if (chunk == NULL) { + /* + * This thread was unable to purge as many pages as + * originally intended, due to races with other threads + * that either did some of the purging work, or re-used + * dirty pages. + */ + arena->npurgatory -= npurgatory; + return; + } + while (chunk->ndirty == 0) { + ql_remove(&arena->chunks_dirty, chunk, link_dirty); + chunk->dirtied = false; + chunk = ql_first(&arena->chunks_dirty); + if (chunk == NULL) { + /* Same logic as for above. */ + arena->npurgatory -= npurgatory; + return; + } + } + + if (chunk->ndirty > npurgatory) { + /* + * This thread will, at a minimum, purge all the dirty + * pages in chunk, so set npurgatory to reflect this + * thread's commitment to purge the pages. This tends + * to reduce the chances of the following scenario: + * + * 1) This thread sets arena->npurgatory such that + * (arena->ndirty - arena->npurgatory) is at the + * threshold. + * 2) This thread drops arena->lock. + * 3) Another thread causes one or more pages to be + * dirtied, and immediately determines that it must + * purge dirty pages. + * + * If this scenario *does* play out, that's okay, + * because all of the purging work being done really + * needs to happen. + */ + arena->npurgatory += chunk->ndirty - npurgatory; + npurgatory = chunk->ndirty; + } + + arena->npurgatory -= chunk->ndirty; + npurgatory -= chunk->ndirty; + arena_chunk_purge(arena, chunk); + } +} + +static void +arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) +{ + arena_chunk_t *chunk; + size_t size, run_ind, run_pages, flag_dirty; + arena_avail_tree_t *runs_avail; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) + >> PAGE_SHIFT); + assert(run_ind >= arena_chunk_header_npages); + assert(run_ind < chunk_npages); + if ((chunk->map[run_ind].bits & CHUNK_MAP_LARGE) != 0) + size = chunk->map[run_ind].bits & ~PAGE_MASK; + else + size = run->bin->run_size; + run_pages = (size >> PAGE_SHIFT); + arena->nactive -= run_pages; + + /* + * The run is dirty if the caller claims to have dirtied it, as well as + * if it was already dirty before being allocated. + */ + if ((chunk->map[run_ind].bits & CHUNK_MAP_DIRTY) != 0) + dirty = true; + flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0; + runs_avail = dirty ? &arena->runs_avail_dirty : + &arena->runs_avail_clean; + + /* Mark pages as unallocated in the chunk map. */ + if (dirty) { + chunk->map[run_ind].bits = size | flag_dirty; + chunk->map[run_ind+run_pages-1].bits = size | flag_dirty; + + chunk->ndirty += run_pages; + arena->ndirty += run_pages; + } else { + chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & + CHUNK_MAP_ZEROED); + chunk->map[run_ind+run_pages-1].bits = size | + (chunk->map[run_ind+run_pages-1].bits & CHUNK_MAP_ZEROED); + } + + /* Try to coalesce forward. */ + if (run_ind + run_pages < chunk_npages && + (chunk->map[run_ind+run_pages].bits & CHUNK_MAP_ALLOCATED) == 0 && + (chunk->map[run_ind+run_pages].bits & CHUNK_MAP_DIRTY) == + flag_dirty) { + size_t nrun_size = chunk->map[run_ind+run_pages].bits & + ~PAGE_MASK; + + /* + * Remove successor from runs_avail; the coalesced run is + * inserted later. + */ + arena_avail_tree_remove(runs_avail, + &chunk->map[run_ind+run_pages]); + + size += nrun_size; + run_pages = size >> PAGE_SHIFT; + + assert((chunk->map[run_ind+run_pages-1].bits & ~PAGE_MASK) + == nrun_size); + chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & + CHUNK_MAP_FLAGS_MASK); + chunk->map[run_ind+run_pages-1].bits = size | + (chunk->map[run_ind+run_pages-1].bits & + CHUNK_MAP_FLAGS_MASK); + } + + /* Try to coalesce backward. */ + if (run_ind > arena_chunk_header_npages && (chunk->map[run_ind-1].bits & + CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[run_ind-1].bits & + CHUNK_MAP_DIRTY) == flag_dirty) { + size_t prun_size = chunk->map[run_ind-1].bits & ~PAGE_MASK; + + run_ind -= prun_size >> PAGE_SHIFT; + + /* + * Remove predecessor from runs_avail; the coalesced run is + * inserted later. + */ + arena_avail_tree_remove(runs_avail, &chunk->map[run_ind]); + + size += prun_size; + run_pages = size >> PAGE_SHIFT; + + assert((chunk->map[run_ind].bits & ~PAGE_MASK) == prun_size); + chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & + CHUNK_MAP_FLAGS_MASK); + chunk->map[run_ind+run_pages-1].bits = size | + (chunk->map[run_ind+run_pages-1].bits & + CHUNK_MAP_FLAGS_MASK); + } + + /* Insert into runs_avail, now that coalescing is complete. */ + arena_avail_tree_insert(runs_avail, &chunk->map[run_ind]); + + /* + * Deallocate chunk if it is now completely unused. The bit + * manipulation checks whether the first run is unallocated and extends + * to the end of the chunk. + */ + if ((chunk->map[arena_chunk_header_npages].bits & (~PAGE_MASK | + CHUNK_MAP_ALLOCATED)) == arena_maxclass) + arena_chunk_dealloc(arena, chunk); + + /* + * It is okay to do dirty page processing even if the chunk was + * deallocated above, since in that case it is the spare. Waiting + * until after possible chunk deallocation to do dirty processing + * allows for an old spare to be fully deallocated, thus decreasing the + * chances of spuriously crossing the dirty page purging threshold. + */ + if (dirty) { + if (chunk->dirtied == false) { + ql_tail_insert(&arena->chunks_dirty, chunk, link_dirty); + chunk->dirtied = true; + } + arena_maybe_purge(arena); + } +} + +static void +arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, + size_t oldsize, size_t newsize) +{ + size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT; + size_t head_npages = (oldsize - newsize) >> PAGE_SHIFT; + size_t flags = chunk->map[pageind].bits & CHUNK_MAP_FLAGS_MASK; + + assert(oldsize > newsize); + + /* + * Update the chunk map so that arena_run_dalloc() can treat the + * leading run as separately allocated. + */ + assert(chunk->map[pageind].bits & CHUNK_MAP_LARGE); + assert(chunk->map[pageind].bits & CHUNK_MAP_ALLOCATED); + chunk->map[pageind].bits = (oldsize - newsize) | flags; + chunk->map[pageind+head_npages].bits = newsize | flags; + + arena_run_dalloc(arena, run, false); +} + +static void +arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, + size_t oldsize, size_t newsize, bool dirty) +{ + size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT; + size_t npages = newsize >> PAGE_SHIFT; + size_t flags = chunk->map[pageind].bits & CHUNK_MAP_FLAGS_MASK; + + assert(oldsize > newsize); + + /* + * Update the chunk map so that arena_run_dalloc() can treat the + * trailing run as separately allocated. + */ + assert(chunk->map[pageind].bits & CHUNK_MAP_LARGE); + assert(chunk->map[pageind].bits & CHUNK_MAP_ALLOCATED); + chunk->map[pageind].bits = newsize | flags; + chunk->map[pageind+npages-1].bits = newsize | flags; + chunk->map[pageind+npages].bits = (oldsize - newsize) | flags; + + arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize), + dirty); +} + +static arena_run_t * +arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) +{ + arena_chunk_map_t *mapelm; + arena_run_t *run; + + /* Look for a usable run. */ + mapelm = arena_run_tree_first(&bin->runs); + if (mapelm != NULL) { + arena_chunk_t *chunk; + size_t pageind; + + /* run is guaranteed to have available space. */ + arena_run_tree_remove(&bin->runs, mapelm); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm); + pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / + sizeof(arena_chunk_map_t)); + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + (mapelm->bits >> PAGE_SHIFT)) + << PAGE_SHIFT)); +#ifdef JEMALLOC_STATS + bin->stats.reruns++; +#endif + return (run); + } + /* No existing runs have any space available. */ + + /* Allocate a new run. */ + malloc_mutex_unlock(&bin->lock); + /******************************/ + malloc_mutex_lock(&arena->lock); + run = arena_run_alloc(arena, bin->run_size, false, false); + if (run != NULL) { + /* Initialize run internals. */ + run->bin = bin; + run->avail = NULL; + run->next = (void *)(((uintptr_t)run) + + (uintptr_t)bin->reg0_offset); + run->nfree = bin->nregs; +#ifdef JEMALLOC_DEBUG + run->magic = ARENA_RUN_MAGIC; +#endif + } + malloc_mutex_unlock(&arena->lock); + /********************************/ + malloc_mutex_lock(&bin->lock); + if (run != NULL) { +#ifdef JEMALLOC_STATS + bin->stats.nruns++; + bin->stats.curruns++; + if (bin->stats.curruns > bin->stats.highruns) + bin->stats.highruns = bin->stats.curruns; +#endif + return (run); + } + + /* + * arena_run_alloc() failed, but another thread may have made + * sufficient memory available while this one dopped bin->lock above, + * so search one more time. + */ + mapelm = arena_run_tree_first(&bin->runs); + if (mapelm != NULL) { + arena_chunk_t *chunk; + size_t pageind; + + /* run is guaranteed to have available space. */ + arena_run_tree_remove(&bin->runs, mapelm); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm); + pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / + sizeof(arena_chunk_map_t)); + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + (mapelm->bits >> PAGE_SHIFT)) + << PAGE_SHIFT)); +#ifdef JEMALLOC_STATS + bin->stats.reruns++; +#endif + return (run); + } + + return (NULL); +} + +/* Re-fill bin->runcur, then call arena_run_reg_alloc(). */ +static void * +arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) +{ + void *ret; + arena_run_t *run; + + bin->runcur = NULL; + run = arena_bin_nonfull_run_get(arena, bin); + if (bin->runcur != NULL && bin->runcur->nfree > 0) { + /* + * Another thread updated runcur while this one ran without the + * bin lock in arena_bin_nonfull_run_get(). + */ + assert(bin->runcur->magic == ARENA_RUN_MAGIC); + assert(bin->runcur->nfree > 0); + ret = arena_run_reg_alloc(bin->runcur, bin); + if (run != NULL) { + malloc_mutex_unlock(&bin->lock); + malloc_mutex_lock(&arena->lock); + arena_run_dalloc(arena, run, false); + malloc_mutex_unlock(&arena->lock); + malloc_mutex_lock(&bin->lock); + } + return (ret); + } + + if (run == NULL) + return (NULL); + + bin->runcur = run; + + assert(bin->runcur->magic == ARENA_RUN_MAGIC); + assert(bin->runcur->nfree > 0); + + return (arena_run_reg_alloc(bin->runcur, bin)); +} + +#ifdef JEMALLOC_PROF +void +arena_prof_accum(arena_t *arena, uint64_t accumbytes) +{ + + if (prof_interval != 0) { + arena->prof_accumbytes += accumbytes; + if (arena->prof_accumbytes >= prof_interval) { + prof_idump(); + arena->prof_accumbytes -= prof_interval; + } + } +} +#endif + +#ifdef JEMALLOC_TCACHE +void +arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind +# ifdef JEMALLOC_PROF + , uint64_t prof_accumbytes +# endif + ) +{ + unsigned i, nfill; + arena_bin_t *bin; + arena_run_t *run; + void *ptr; + + assert(tbin->ncached == 0); + +#ifdef JEMALLOC_PROF + malloc_mutex_lock(&arena->lock); + arena_prof_accum(arena, prof_accumbytes); + malloc_mutex_unlock(&arena->lock); +#endif + bin = &arena->bins[binind]; + malloc_mutex_lock(&bin->lock); + for (i = 0, nfill = (tbin->ncached_max >> 1); i < nfill; i++) { + if ((run = bin->runcur) != NULL && run->nfree > 0) + ptr = arena_run_reg_alloc(run, bin); + else + ptr = arena_bin_malloc_hard(arena, bin); + if (ptr == NULL) + break; + *(void **)ptr = tbin->avail; + tbin->avail = ptr; + } +#ifdef JEMALLOC_STATS + bin->stats.allocated += (i - tbin->ncached) * bin->reg_size; + bin->stats.nmalloc += i; + bin->stats.nrequests += tbin->tstats.nrequests; + bin->stats.nfills++; + tbin->tstats.nrequests = 0; +#endif + malloc_mutex_unlock(&bin->lock); + tbin->ncached = i; + if (tbin->ncached > tbin->high_water) + tbin->high_water = tbin->ncached; +} +#endif + +/* + * Calculate bin->run_size such that it meets the following constraints: + * + * *) bin->run_size >= min_run_size + * *) bin->run_size <= arena_maxclass + * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). + * *) run header size < PAGE_SIZE + * + * bin->nregs and bin->reg0_offset are also calculated here, since these + * settings are all interdependent. + */ +static size_t +arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) +{ + size_t try_run_size, good_run_size; + uint32_t try_nregs, good_nregs; + uint32_t try_hdr_size, good_hdr_size; +#ifdef JEMALLOC_PROF + uint32_t try_cnt0_offset, good_cnt0_offset; +#endif + uint32_t try_reg0_offset, good_reg0_offset; + + assert(min_run_size >= PAGE_SIZE); + assert(min_run_size <= arena_maxclass); + + /* + * Calculate known-valid settings before entering the run_size + * expansion loop, so that the first part of the loop always copies + * valid settings. + * + * The do..while loop iteratively reduces the number of regions until + * the run header and the regions no longer overlap. A closed formula + * would be quite messy, since there is an interdependency between the + * header's mask length and the number of regions. + */ + try_run_size = min_run_size; + try_nregs = ((try_run_size - sizeof(arena_run_t)) / bin->reg_size) + + 1; /* Counter-act try_nregs-- in loop. */ + do { + try_nregs--; + try_hdr_size = sizeof(arena_run_t); +#ifdef JEMALLOC_PROF + if (opt_prof && prof_promote == false) { + /* Pad to a quantum boundary. */ + try_hdr_size = QUANTUM_CEILING(try_hdr_size); + try_cnt0_offset = try_hdr_size; + /* Add space for one (prof_thr_cnt_t *) per region. */ + try_hdr_size += try_nregs * sizeof(prof_thr_cnt_t *); + } else + try_cnt0_offset = 0; +#endif + try_reg0_offset = try_run_size - (try_nregs * bin->reg_size); + } while (try_hdr_size > try_reg0_offset); + + /* run_size expansion loop. */ + do { + /* + * Copy valid settings before trying more aggressive settings. + */ + good_run_size = try_run_size; + good_nregs = try_nregs; + good_hdr_size = try_hdr_size; +#ifdef JEMALLOC_PROF + good_cnt0_offset = try_cnt0_offset; +#endif + good_reg0_offset = try_reg0_offset; + + /* Try more aggressive settings. */ + try_run_size += PAGE_SIZE; + try_nregs = ((try_run_size - sizeof(arena_run_t)) / + bin->reg_size) + 1; /* Counter-act try_nregs-- in loop. */ + do { + try_nregs--; + try_hdr_size = sizeof(arena_run_t); +#ifdef JEMALLOC_PROF + if (opt_prof && prof_promote == false) { + /* Pad to a quantum boundary. */ + try_hdr_size = QUANTUM_CEILING(try_hdr_size); + try_cnt0_offset = try_hdr_size; + /* + * Add space for one (prof_thr_cnt_t *) per + * region. + */ + try_hdr_size += try_nregs * + sizeof(prof_thr_cnt_t *); + } +#endif + try_reg0_offset = try_run_size - (try_nregs * + bin->reg_size); + } while (try_hdr_size > try_reg0_offset); + } while (try_run_size <= arena_maxclass + && try_run_size <= arena_maxclass + && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX + && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size + && try_hdr_size < PAGE_SIZE); + + assert(good_hdr_size <= good_reg0_offset); + + /* Copy final settings. */ + bin->run_size = good_run_size; + bin->nregs = good_nregs; +#ifdef JEMALLOC_PROF + bin->cnt0_offset = good_cnt0_offset; +#endif + bin->reg0_offset = good_reg0_offset; + + return (good_run_size); +} + +void * +arena_malloc_small(arena_t *arena, size_t size, bool zero) +{ + void *ret; + arena_bin_t *bin; + arena_run_t *run; + size_t binind; + + binind = small_size2bin[size]; + assert(binind < nbins); + bin = &arena->bins[binind]; + size = bin->reg_size; + + malloc_mutex_lock(&bin->lock); + if ((run = bin->runcur) != NULL && run->nfree > 0) + ret = arena_run_reg_alloc(run, bin); + else + ret = arena_bin_malloc_hard(arena, bin); + + if (ret == NULL) { + malloc_mutex_unlock(&bin->lock); + return (NULL); + } + +#ifdef JEMALLOC_STATS + bin->stats.allocated += size; + bin->stats.nmalloc++; + bin->stats.nrequests++; +#endif + malloc_mutex_unlock(&bin->lock); +#ifdef JEMALLOC_PROF + if (isthreaded == false) { + malloc_mutex_lock(&arena->lock); + arena_prof_accum(arena, size); + malloc_mutex_unlock(&arena->lock); + } +#endif + + if (zero == false) { +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ret, 0xa5, size); + else if (opt_zero) + memset(ret, 0, size); +#endif + } else + memset(ret, 0, size); + + return (ret); +} + +void * +arena_malloc_large(arena_t *arena, size_t size, bool zero) +{ + void *ret; + + /* Large allocation. */ + size = PAGE_CEILING(size); + malloc_mutex_lock(&arena->lock); + ret = (void *)arena_run_alloc(arena, size, true, zero); + if (ret == NULL) { + malloc_mutex_unlock(&arena->lock); + return (NULL); + } +#ifdef JEMALLOC_STATS + arena->stats.nmalloc_large++; + arena->stats.nrequests_large++; + arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } +#endif +#ifdef JEMALLOC_PROF + arena_prof_accum(arena, size); +#endif + malloc_mutex_unlock(&arena->lock); + + if (zero == false) { +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ret, 0xa5, size); + else if (opt_zero) + memset(ret, 0, size); +#endif + } + + return (ret); +} + +void * +arena_malloc(size_t size, bool zero) +{ + + assert(size != 0); + assert(QUANTUM_CEILING(size) <= arena_maxclass); + + if (size <= small_maxclass) { +#ifdef JEMALLOC_TCACHE + tcache_t *tcache; + + if ((tcache = tcache_get()) != NULL) + return (tcache_alloc_small(tcache, size, zero)); + else + +#endif + return (arena_malloc_small(choose_arena(), size, zero)); + } else { +#ifdef JEMALLOC_TCACHE + if (size <= tcache_maxclass) { + tcache_t *tcache; + + if ((tcache = tcache_get()) != NULL) + return (tcache_alloc_large(tcache, size, zero)); + else { + return (arena_malloc_large(choose_arena(), + size, zero)); + } + } else +#endif + return (arena_malloc_large(choose_arena(), size, zero)); + } +} + +/* Only handles large allocations that require more than page alignment. */ +void * +arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size) +{ + void *ret; + size_t offset; + arena_chunk_t *chunk; + + assert((size & PAGE_MASK) == 0); + assert((alignment & PAGE_MASK) == 0); + + malloc_mutex_lock(&arena->lock); + ret = (void *)arena_run_alloc(arena, alloc_size, true, false); + if (ret == NULL) { + malloc_mutex_unlock(&arena->lock); + return (NULL); + } + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret); + + offset = (uintptr_t)ret & (alignment - 1); + assert((offset & PAGE_MASK) == 0); + assert(offset < alloc_size); + if (offset == 0) + arena_run_trim_tail(arena, chunk, ret, alloc_size, size, false); + else { + size_t leadsize, trailsize; + + leadsize = alignment - offset; + if (leadsize > 0) { + arena_run_trim_head(arena, chunk, ret, alloc_size, + alloc_size - leadsize); + ret = (void *)((uintptr_t)ret + leadsize); + } + + trailsize = alloc_size - leadsize - size; + if (trailsize != 0) { + /* Trim trailing space. */ + assert(trailsize < alloc_size); + arena_run_trim_tail(arena, chunk, ret, size + trailsize, + size, false); + } + } + +#ifdef JEMALLOC_STATS + arena->stats.nmalloc_large++; + arena->stats.nrequests_large++; + arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } +#endif + malloc_mutex_unlock(&arena->lock); + +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ret, 0xa5, size); + else if (opt_zero) + memset(ret, 0, size); +#endif + return (ret); +} + +/* Return the size of the allocation pointed to by ptr. */ +size_t +arena_salloc(const void *ptr) +{ + size_t ret; + arena_chunk_t *chunk; + size_t pageind, mapbits; + + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapbits = chunk->map[pageind].bits; + assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); + if ((mapbits & CHUNK_MAP_LARGE) == 0) { + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) << + PAGE_SHIFT)); + assert(run->magic == ARENA_RUN_MAGIC); + assert(((uintptr_t)ptr - ((uintptr_t)run + + (uintptr_t)run->bin->reg0_offset)) % run->bin->reg_size == + 0); + ret = run->bin->reg_size; + } else { + assert(((uintptr_t)ptr & PAGE_MASK) == 0); + ret = mapbits & ~PAGE_MASK; + assert(ret != 0); + } + + return (ret); +} + +#ifdef JEMALLOC_PROF +void +arena_prof_promoted(const void *ptr, size_t size) +{ + arena_chunk_t *chunk; + size_t pageind, binind; + + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + assert(isalloc(ptr) == PAGE_SIZE); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + binind = small_size2bin[size]; + assert(binind < nbins); + chunk->map[pageind].bits = (chunk->map[pageind].bits & + ~CHUNK_MAP_CLASS_MASK) | (binind << CHUNK_MAP_CLASS_SHIFT); +} + +size_t +arena_salloc_demote(const void *ptr) +{ + size_t ret; + arena_chunk_t *chunk; + size_t pageind, mapbits; + + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapbits = chunk->map[pageind].bits; + assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); + if ((mapbits & CHUNK_MAP_LARGE) == 0) { + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) << + PAGE_SHIFT)); + assert(run->magic == ARENA_RUN_MAGIC); + assert(((uintptr_t)ptr - ((uintptr_t)run + + (uintptr_t)run->bin->reg0_offset)) % run->bin->reg_size == + 0); + ret = run->bin->reg_size; + } else { + assert(((uintptr_t)ptr & PAGE_MASK) == 0); + ret = mapbits & ~PAGE_MASK; + if (prof_promote && ret == PAGE_SIZE && (mapbits & + CHUNK_MAP_CLASS_MASK) != CHUNK_MAP_CLASS_MASK) { + size_t binind = ((mapbits & CHUNK_MAP_CLASS_MASK) >> + CHUNK_MAP_CLASS_SHIFT); + assert(binind < nbins); + ret = chunk->arena->bins[binind].reg_size; + } + assert(ret != 0); + } + + return (ret); +} + +static inline unsigned +arena_run_regind(arena_run_t *run, arena_bin_t *bin, const void *ptr, + size_t size) +{ + unsigned shift, diff, regind; + + assert(run->magic == ARENA_RUN_MAGIC); + + /* + * Avoid doing division with a variable divisor if possible. Using + * actual division here can reduce allocator throughput by over 20%! + */ + diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset); + + /* Rescale (factor powers of 2 out of the numerator and denominator). */ + shift = ffs(size) - 1; + diff >>= shift; + size >>= shift; + + if (size == 1) { + /* The divisor was a power of 2. */ + regind = diff; + } else { + /* + * To divide by a number D that is not a power of two we + * multiply by (2^21 / D) and then right shift by 21 positions. + * + * X / D + * + * becomes + * + * (X * size_invs[D - 3]) >> SIZE_INV_SHIFT + * + * We can omit the first three elements, because we never + * divide by 0, and 1 and 2 are both powers of two, which are + * handled above. + */ +#define SIZE_INV_SHIFT 21 +#define SIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s)) + 1) + static const unsigned size_invs[] = { + SIZE_INV(3), + SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7), + SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11), + SIZE_INV(12), SIZE_INV(13), SIZE_INV(14), SIZE_INV(15), + SIZE_INV(16), SIZE_INV(17), SIZE_INV(18), SIZE_INV(19), + SIZE_INV(20), SIZE_INV(21), SIZE_INV(22), SIZE_INV(23), + SIZE_INV(24), SIZE_INV(25), SIZE_INV(26), SIZE_INV(27), + SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31) + }; + + if (size <= ((sizeof(size_invs) / sizeof(unsigned)) + 2)) + regind = (diff * size_invs[size - 3]) >> SIZE_INV_SHIFT; + else + regind = diff / size; +#undef SIZE_INV +#undef SIZE_INV_SHIFT + } + assert(diff == regind * size); + assert(regind < bin->nregs); + + return (regind); +} + +prof_thr_cnt_t * +arena_prof_cnt_get(const void *ptr) +{ + prof_thr_cnt_t *ret; + arena_chunk_t *chunk; + size_t pageind, mapbits; + + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapbits = chunk->map[pageind].bits; + assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); + if ((mapbits & CHUNK_MAP_LARGE) == 0) { + if (prof_promote) + ret = (prof_thr_cnt_t *)(uintptr_t)1U; + else { + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) << + PAGE_SHIFT)); + arena_bin_t *bin = run->bin; + unsigned regind; + + assert(run->magic == ARENA_RUN_MAGIC); + regind = arena_run_regind(run, bin, ptr, bin->reg_size); + ret = *(prof_thr_cnt_t **)((uintptr_t)run + + bin->cnt0_offset + (regind * + sizeof(prof_thr_cnt_t *))); + } + } else + ret = chunk->map[pageind].prof_cnt; + + return (ret); +} + +void +arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt) +{ + arena_chunk_t *chunk; + size_t pageind, mapbits; + + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapbits = chunk->map[pageind].bits; + assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); + if ((mapbits & CHUNK_MAP_LARGE) == 0) { + if (prof_promote == false) { + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) << + PAGE_SHIFT)); + arena_bin_t *bin = run->bin; + unsigned regind; + + assert(run->magic == ARENA_RUN_MAGIC); + regind = arena_run_regind(run, bin, ptr, bin->reg_size); + + *((prof_thr_cnt_t **)((uintptr_t)run + bin->cnt0_offset + + (regind * sizeof(prof_thr_cnt_t *)))) = cnt; + } else + assert((uintptr_t)cnt == (uintptr_t)1U); + } else + chunk->map[pageind].prof_cnt = cnt; +} +#endif + +static void +arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, + arena_bin_t *bin) +{ + size_t npages, run_ind, past; + + /* Dissociate run from bin. */ + if (run == bin->runcur) + bin->runcur = NULL; + else if (bin->nregs != 1) { + size_t run_pageind = (((uintptr_t)run - (uintptr_t)chunk)) >> + PAGE_SHIFT; + arena_chunk_map_t *run_mapelm = &chunk->map[run_pageind]; + /* + * This block's conditional is necessary because if the run + * only contains one region, then it never gets inserted into + * the non-full runs tree. + */ + arena_run_tree_remove(&bin->runs, run_mapelm); + } + + malloc_mutex_unlock(&bin->lock); + /******************************/ + npages = bin->run_size >> PAGE_SHIFT; + run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT); + past = (size_t)(((uintptr_t)run->next - (uintptr_t)1U - + (uintptr_t)chunk) >> PAGE_SHIFT) + 1; + malloc_mutex_lock(&arena->lock); + + /* + * If the run was originally clean, and some pages were never touched, + * trim the clean pages before deallocating the dirty portion of the + * run. + */ + if ((chunk->map[run_ind].bits & CHUNK_MAP_DIRTY) == 0 && past - run_ind + < npages) { + /* + * Trim clean pages. Convert to large run beforehand. Set the + * last map element first, in case this is a one-page run. + */ + chunk->map[run_ind+npages-1].bits = CHUNK_MAP_LARGE | + (chunk->map[run_ind].bits & CHUNK_MAP_FLAGS_MASK); + chunk->map[run_ind].bits = bin->run_size | CHUNK_MAP_LARGE | + (chunk->map[run_ind].bits & CHUNK_MAP_FLAGS_MASK); + arena_run_trim_tail(arena, chunk, run, (npages << PAGE_SHIFT), + ((npages - (past - run_ind)) << PAGE_SHIFT), false); + npages = past - run_ind; + } +#ifdef JEMALLOC_DEBUG + run->magic = 0; +#endif + arena_run_dalloc(arena, run, true); + malloc_mutex_unlock(&arena->lock); + /****************************/ + malloc_mutex_lock(&bin->lock); +#ifdef JEMALLOC_STATS + bin->stats.curruns--; +#endif +} + +void +arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, + arena_chunk_map_t *mapelm) +{ + size_t pageind; + arena_run_t *run; + arena_bin_t *bin; +#if (defined(JEMALLOC_FILL) || defined(JEMALLOC_STATS)) + size_t size; +#endif + + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + (mapelm->bits >> PAGE_SHIFT)) << PAGE_SHIFT)); + assert(run->magic == ARENA_RUN_MAGIC); + bin = run->bin; +#if (defined(JEMALLOC_FILL) || defined(JEMALLOC_STATS)) + size = bin->reg_size; +#endif + +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ptr, 0x5a, size); +#endif + + arena_run_reg_dalloc(run, ptr); + + if (run->nfree == bin->nregs) + arena_dalloc_bin_run(arena, chunk, run, bin); + else if (run->nfree == 1 && run != bin->runcur) { + /* + * Make sure that bin->runcur always refers to the lowest + * non-full run, if one exists. + */ + if (bin->runcur == NULL) + bin->runcur = run; + else if ((uintptr_t)run < (uintptr_t)bin->runcur) { + /* Switch runcur. */ + if (bin->runcur->nfree > 0) { + arena_chunk_t *runcur_chunk = + CHUNK_ADDR2BASE(bin->runcur); + size_t runcur_pageind = + (((uintptr_t)bin->runcur - + (uintptr_t)runcur_chunk)) >> PAGE_SHIFT; + arena_chunk_map_t *runcur_mapelm = + &runcur_chunk->map[runcur_pageind]; + + /* Insert runcur. */ + arena_run_tree_insert(&bin->runs, + runcur_mapelm); + } + bin->runcur = run; + } else { + size_t run_pageind = (((uintptr_t)run - + (uintptr_t)chunk)) >> PAGE_SHIFT; + arena_chunk_map_t *run_mapelm = + &chunk->map[run_pageind]; + + assert(arena_run_tree_search(&bin->runs, run_mapelm) == + NULL); + arena_run_tree_insert(&bin->runs, run_mapelm); + } + } + +#ifdef JEMALLOC_STATS + bin->stats.allocated -= size; + bin->stats.ndalloc++; +#endif +} + +#ifdef JEMALLOC_STATS +void +arena_stats_merge(arena_t *arena, size_t *nactive, size_t *ndirty, + arena_stats_t *astats, malloc_bin_stats_t *bstats, + malloc_large_stats_t *lstats) +{ + unsigned i; + + malloc_mutex_lock(&arena->lock); + *nactive += arena->nactive; + *ndirty += arena->ndirty; + + astats->mapped += arena->stats.mapped; + astats->npurge += arena->stats.npurge; + astats->nmadvise += arena->stats.nmadvise; + astats->purged += arena->stats.purged; + astats->allocated_large += arena->stats.allocated_large; + astats->nmalloc_large += arena->stats.nmalloc_large; + astats->ndalloc_large += arena->stats.ndalloc_large; + astats->nrequests_large += arena->stats.nrequests_large; + + for (i = 0; i < nlclasses; i++) { + lstats[i].nmalloc += arena->stats.lstats[i].nmalloc; + lstats[i].ndalloc += arena->stats.lstats[i].ndalloc; + lstats[i].nrequests += arena->stats.lstats[i].nrequests; + lstats[i].highruns += arena->stats.lstats[i].highruns; + lstats[i].curruns += arena->stats.lstats[i].curruns; + } + malloc_mutex_unlock(&arena->lock); + + for (i = 0; i < nbins; i++) { + arena_bin_t *bin = &arena->bins[i]; + + malloc_mutex_lock(&bin->lock); + bstats[i].allocated += bin->stats.allocated; + bstats[i].nmalloc += bin->stats.nmalloc; + bstats[i].ndalloc += bin->stats.ndalloc; + bstats[i].nrequests += bin->stats.nrequests; +#ifdef JEMALLOC_TCACHE + bstats[i].nfills += bin->stats.nfills; + bstats[i].nflushes += bin->stats.nflushes; +#endif + bstats[i].nruns += bin->stats.nruns; + bstats[i].reruns += bin->stats.reruns; + bstats[i].highruns += bin->stats.highruns; + bstats[i].curruns += bin->stats.curruns; + malloc_mutex_unlock(&bin->lock); + } +} +#endif + +void +arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) +{ + + /* Large allocation. */ +#ifdef JEMALLOC_FILL +# ifndef JEMALLOC_STATS + if (opt_junk) +# endif +#endif + { +#if (defined(JEMALLOC_FILL) || defined(JEMALLOC_STATS)) + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> + PAGE_SHIFT; + size_t size = chunk->map[pageind].bits & ~PAGE_MASK; +#endif + +#ifdef JEMALLOC_FILL +# ifdef JEMALLOC_STATS + if (opt_junk) +# endif + memset(ptr, 0x5a, size); +#endif +#ifdef JEMALLOC_STATS + arena->stats.ndalloc_large++; + arena->stats.allocated_large -= size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].ndalloc++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns--; +#endif + } + + arena_run_dalloc(arena, (arena_run_t *)ptr, true); +} + +static void +arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, + size_t size, size_t oldsize) +{ + + assert(size < oldsize); + + /* + * Shrink the run, and make trailing pages available for other + * allocations. + */ + malloc_mutex_lock(&arena->lock); + arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, + true); +#ifdef JEMALLOC_STATS + arena->stats.ndalloc_large++; + arena->stats.allocated_large -= oldsize; + arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].ndalloc++; + arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].curruns--; + + arena->stats.nmalloc_large++; + arena->stats.nrequests_large++; + arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } +#endif + malloc_mutex_unlock(&arena->lock); +} + +static bool +arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, + size_t size, size_t oldsize) +{ + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT; + size_t npages = oldsize >> PAGE_SHIFT; + + assert(oldsize == (chunk->map[pageind].bits & ~PAGE_MASK)); + + /* Try to extend the run. */ + assert(size > oldsize); + malloc_mutex_lock(&arena->lock); + if (pageind + npages < chunk_npages && (chunk->map[pageind+npages].bits + & CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[pageind+npages].bits & + ~PAGE_MASK) >= size - oldsize) { + /* + * The next run is available and sufficiently large. Split the + * following run, then merge the first part with the existing + * allocation. + */ + arena_run_split(arena, (arena_run_t *)((uintptr_t)chunk + + ((pageind+npages) << PAGE_SHIFT)), size - oldsize, true, + false); + + chunk->map[pageind].bits = size | CHUNK_MAP_LARGE | + CHUNK_MAP_ALLOCATED; + chunk->map[pageind+npages].bits = CHUNK_MAP_LARGE | + CHUNK_MAP_ALLOCATED; + +#ifdef JEMALLOC_STATS + arena->stats.ndalloc_large++; + arena->stats.allocated_large -= oldsize; + arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].ndalloc++; + arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].curruns--; + + arena->stats.nmalloc_large++; + arena->stats.nrequests_large++; + arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } +#endif + malloc_mutex_unlock(&arena->lock); + return (false); + } + malloc_mutex_unlock(&arena->lock); + + return (true); +} + +/* + * Try to resize a large allocation, in order to avoid copying. This will + * always fail if growing an object, and the following run is already in use. + */ +static bool +arena_ralloc_large(void *ptr, size_t size, size_t oldsize) +{ + size_t psize; + + psize = PAGE_CEILING(size); + if (psize == oldsize) { + /* Same size class. */ +#ifdef JEMALLOC_FILL + if (opt_junk && size < oldsize) { + memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - + size); + } +#endif + return (false); + } else { + arena_chunk_t *chunk; + arena_t *arena; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + arena = chunk->arena; + assert(arena->magic == ARENA_MAGIC); + + if (psize < oldsize) { +#ifdef JEMALLOC_FILL + /* Fill before shrinking in order avoid a race. */ + if (opt_junk) { + memset((void *)((uintptr_t)ptr + size), 0x5a, + oldsize - size); + } +#endif + arena_ralloc_large_shrink(arena, chunk, ptr, psize, + oldsize); + return (false); + } else { + bool ret = arena_ralloc_large_grow(arena, chunk, ptr, + psize, oldsize); +#ifdef JEMALLOC_FILL + if (ret == false && opt_zero) { + memset((void *)((uintptr_t)ptr + oldsize), 0, + size - oldsize); + } +#endif + return (ret); + } + } +} + +void * +arena_ralloc(void *ptr, size_t size, size_t oldsize) +{ + void *ret; + size_t copysize; + + /* Try to avoid moving the allocation. */ + if (oldsize <= arena_maxclass) { + if (oldsize <= small_maxclass) { + if (size <= small_maxclass && small_size2bin[size] == + small_size2bin[oldsize]) + goto IN_PLACE; + } else { + assert(size <= arena_maxclass); + if (size > small_maxclass) { + if (arena_ralloc_large(ptr, size, oldsize) == + false) + return (ptr); + } + } + } + + /* + * If we get here, then size and oldsize are different enough that we + * need to move the object. In that case, fall back to allocating new + * space and copying. + */ + ret = arena_malloc(size, false); + if (ret == NULL) + return (NULL); + + /* Junk/zero-filling were already done by arena_malloc(). */ + copysize = (size < oldsize) ? size : oldsize; + memcpy(ret, ptr, copysize); + idalloc(ptr); + return (ret); +IN_PLACE: +#ifdef JEMALLOC_FILL + if (opt_junk && size < oldsize) + memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size); + else if (opt_zero && size > oldsize) + memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize); +#endif + return (ptr); +} + +bool +arena_new(arena_t *arena, unsigned ind) +{ + unsigned i; + arena_bin_t *bin; + size_t prev_run_size; + + arena->ind = ind; + + if (malloc_mutex_init(&arena->lock)) + return (true); + +#ifdef JEMALLOC_STATS + memset(&arena->stats, 0, sizeof(arena_stats_t)); + arena->stats.lstats = (malloc_large_stats_t *)base_alloc(nlclasses * + sizeof(malloc_large_stats_t)); + if (arena->stats.lstats == NULL) + return (true); + memset(arena->stats.lstats, 0, nlclasses * + sizeof(malloc_large_stats_t)); +# ifdef JEMALLOC_TCACHE + ql_new(&arena->tcache_ql); +# endif +#endif + +#ifdef JEMALLOC_PROF + arena->prof_accumbytes = 0; +#endif + + /* Initialize chunks. */ + ql_new(&arena->chunks_dirty); + arena->spare = NULL; + + arena->nactive = 0; + arena->ndirty = 0; + arena->npurgatory = 0; + + arena_avail_tree_new(&arena->runs_avail_clean); + arena_avail_tree_new(&arena->runs_avail_dirty); + + /* Initialize bins. */ + prev_run_size = PAGE_SIZE; + + i = 0; +#ifdef JEMALLOC_TINY + /* (2^n)-spaced tiny bins. */ + for (; i < ntbins; i++) { + bin = &arena->bins[i]; + if (malloc_mutex_init(&bin->lock)) + return (true); + bin->runcur = NULL; + arena_run_tree_new(&bin->runs); + + bin->reg_size = (1U << (LG_TINY_MIN + i)); + + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); + +#ifdef JEMALLOC_STATS + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); +#endif + } +#endif + + /* Quantum-spaced bins. */ + for (; i < ntbins + nqbins; i++) { + bin = &arena->bins[i]; + if (malloc_mutex_init(&bin->lock)) + return (true); + bin->runcur = NULL; + arena_run_tree_new(&bin->runs); + + bin->reg_size = (i - ntbins + 1) << LG_QUANTUM; + + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); + +#ifdef JEMALLOC_STATS + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); +#endif + } + + /* Cacheline-spaced bins. */ + for (; i < ntbins + nqbins + ncbins; i++) { + bin = &arena->bins[i]; + if (malloc_mutex_init(&bin->lock)) + return (true); + bin->runcur = NULL; + arena_run_tree_new(&bin->runs); + + bin->reg_size = cspace_min + ((i - (ntbins + nqbins)) << + LG_CACHELINE); + + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); + +#ifdef JEMALLOC_STATS + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); +#endif + } + + /* Subpage-spaced bins. */ + for (; i < nbins; i++) { + bin = &arena->bins[i]; + if (malloc_mutex_init(&bin->lock)) + return (true); + bin->runcur = NULL; + arena_run_tree_new(&bin->runs); + + bin->reg_size = sspace_min + ((i - (ntbins + nqbins + ncbins)) + << LG_SUBPAGE); + + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); + +#ifdef JEMALLOC_STATS + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); +#endif + } + +#ifdef JEMALLOC_DEBUG + arena->magic = ARENA_MAGIC; +#endif + + return (false); +} + +#ifdef JEMALLOC_TINY +/* Compute the smallest power of 2 that is >= x. */ +static size_t +pow2_ceil(size_t x) +{ + + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; +#if (SIZEOF_PTR == 8) + x |= x >> 32; +#endif + x++; + return (x); +} +#endif + +#ifdef JEMALLOC_DEBUG +static void +small_size2bin_validate(void) +{ + size_t i, size, binind; + + assert(small_size2bin[0] == 0xffU); + i = 1; +# ifdef JEMALLOC_TINY + /* Tiny. */ + for (; i < (1U << LG_TINY_MIN); i++) { + size = pow2_ceil(1U << LG_TINY_MIN); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + assert(small_size2bin[i] == binind); + } + for (; i < qspace_min; i++) { + size = pow2_ceil(i); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + assert(small_size2bin[i] == binind); + } +# endif + /* Quantum-spaced. */ + for (; i <= qspace_max; i++) { + size = QUANTUM_CEILING(i); + binind = ntbins + (size >> LG_QUANTUM) - 1; + assert(small_size2bin[i] == binind); + } + /* Cacheline-spaced. */ + for (; i <= cspace_max; i++) { + size = CACHELINE_CEILING(i); + binind = ntbins + nqbins + ((size - cspace_min) >> + LG_CACHELINE); + assert(small_size2bin[i] == binind); + } + /* Sub-page. */ + for (; i <= sspace_max; i++) { + size = SUBPAGE_CEILING(i); + binind = ntbins + nqbins + ncbins + ((size - sspace_min) + >> LG_SUBPAGE); + assert(small_size2bin[i] == binind); + } +} +#endif + +static bool +small_size2bin_init(void) +{ + + if (opt_lg_qspace_max != LG_QSPACE_MAX_DEFAULT + || opt_lg_cspace_max != LG_CSPACE_MAX_DEFAULT + || sizeof(const_small_size2bin) != small_maxclass + 1) + return (small_size2bin_init_hard()); + + small_size2bin = const_small_size2bin; +#ifdef JEMALLOC_DEBUG + assert(sizeof(const_small_size2bin) == small_maxclass + 1); + small_size2bin_validate(); +#endif + return (false); +} + +static bool +small_size2bin_init_hard(void) +{ + size_t i, size, binind; + uint8_t *custom_small_size2bin; + + assert(opt_lg_qspace_max != LG_QSPACE_MAX_DEFAULT + || opt_lg_cspace_max != LG_CSPACE_MAX_DEFAULT + || sizeof(const_small_size2bin) != small_maxclass + 1); + + custom_small_size2bin = (uint8_t *)base_alloc(small_maxclass + 1); + if (custom_small_size2bin == NULL) + return (true); + + custom_small_size2bin[0] = 0xffU; + i = 1; +#ifdef JEMALLOC_TINY + /* Tiny. */ + for (; i < (1U << LG_TINY_MIN); i++) { + size = pow2_ceil(1U << LG_TINY_MIN); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + custom_small_size2bin[i] = binind; + } + for (; i < qspace_min; i++) { + size = pow2_ceil(i); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + custom_small_size2bin[i] = binind; + } +#endif + /* Quantum-spaced. */ + for (; i <= qspace_max; i++) { + size = QUANTUM_CEILING(i); + binind = ntbins + (size >> LG_QUANTUM) - 1; + custom_small_size2bin[i] = binind; + } + /* Cacheline-spaced. */ + for (; i <= cspace_max; i++) { + size = CACHELINE_CEILING(i); + binind = ntbins + nqbins + ((size - cspace_min) >> + LG_CACHELINE); + custom_small_size2bin[i] = binind; + } + /* Sub-page. */ + for (; i <= sspace_max; i++) { + size = SUBPAGE_CEILING(i); + binind = ntbins + nqbins + ncbins + ((size - sspace_min) >> + LG_SUBPAGE); + custom_small_size2bin[i] = binind; + } + + small_size2bin = custom_small_size2bin; +#ifdef JEMALLOC_DEBUG + small_size2bin_validate(); +#endif + return (false); +} + +bool +arena_boot(void) +{ + size_t header_size; + + /* Set variables according to the value of opt_lg_[qc]space_max. */ + qspace_max = (1U << opt_lg_qspace_max); + cspace_min = CACHELINE_CEILING(qspace_max); + if (cspace_min == qspace_max) + cspace_min += CACHELINE; + cspace_max = (1U << opt_lg_cspace_max); + sspace_min = SUBPAGE_CEILING(cspace_max); + if (sspace_min == cspace_max) + sspace_min += SUBPAGE; + assert(sspace_min < PAGE_SIZE); + sspace_max = PAGE_SIZE - SUBPAGE; + +#ifdef JEMALLOC_TINY + assert(LG_QUANTUM >= LG_TINY_MIN); +#endif + assert(ntbins <= LG_QUANTUM); + nqbins = qspace_max >> LG_QUANTUM; + ncbins = ((cspace_max - cspace_min) >> LG_CACHELINE) + 1; + nsbins = ((sspace_max - sspace_min) >> LG_SUBPAGE) + 1; + nbins = ntbins + nqbins + ncbins + nsbins; + + /* + * The small_size2bin lookup table uses uint8_t to encode each bin + * index, so we cannot support more than 256 small size classes. This + * limit is difficult to exceed (not even possible with 16B quantum and + * 4KiB pages), and such configurations are impractical, but + * nonetheless we need to protect against this case in order to avoid + * undefined behavior. + * + * Further constrain nbins to 255 if prof_promote is true, since all + * small size classes, plus a "not small" size class must be stored in + * 8 bits of arena_chunk_map_t's bits field. + */ +#ifdef JEMALLOC_PROF + if (opt_prof && prof_promote) { + if (nbins > 255) { + char line_buf[UMAX2S_BUFSIZE]; + malloc_write("<jemalloc>: Too many small size classes ("); + malloc_write(umax2s(nbins, 10, line_buf)); + malloc_write(" > max 255)\n"); + abort(); + } + } else +#endif + if (nbins > 256) { + char line_buf[UMAX2S_BUFSIZE]; + malloc_write("<jemalloc>: Too many small size classes ("); + malloc_write(umax2s(nbins, 10, line_buf)); + malloc_write(" > max 256)\n"); + abort(); + } + + if (small_size2bin_init()) + return (true); + + /* + * Compute the header size such that it is large enough to contain the + * page map. + */ + header_size = sizeof(arena_chunk_t) + + (sizeof(arena_chunk_map_t) * (chunk_npages - 1)); + arena_chunk_header_npages = (header_size >> PAGE_SHIFT) + + ((header_size & PAGE_MASK) != 0); + arena_maxclass = chunksize - (arena_chunk_header_npages << PAGE_SHIFT); + + return (false); +} diff --git a/externals/jemalloc/base.c b/externals/jemalloc/base.c new file mode 100644 index 00000000000..605197eaced --- /dev/null +++ b/externals/jemalloc/base.c @@ -0,0 +1,106 @@ +#define JEMALLOC_BASE_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +malloc_mutex_t base_mtx; + +/* + * Current pages that are being used for internal memory allocations. These + * pages are carved up in cacheline-size quanta, so that there is no chance of + * false cache line sharing. + */ +static void *base_pages; +static void *base_next_addr; +static void *base_past_addr; /* Addr immediately past base_pages. */ +static extent_node_t *base_nodes; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static bool base_pages_alloc(size_t minsize); + +/******************************************************************************/ + +static bool +base_pages_alloc(size_t minsize) +{ + size_t csize; + bool zero; + + assert(minsize != 0); + csize = CHUNK_CEILING(minsize); + zero = false; + base_pages = chunk_alloc(csize, &zero); + if (base_pages == NULL) + return (true); + base_next_addr = base_pages; + base_past_addr = (void *)((uintptr_t)base_pages + csize); + + return (false); +} + +void * +base_alloc(size_t size) +{ + void *ret; + size_t csize; + + /* Round size up to nearest multiple of the cacheline size. */ + csize = CACHELINE_CEILING(size); + + malloc_mutex_lock(&base_mtx); + /* Make sure there's enough space for the allocation. */ + if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) { + if (base_pages_alloc(csize)) { + malloc_mutex_unlock(&base_mtx); + return (NULL); + } + } + /* Allocate. */ + ret = base_next_addr; + base_next_addr = (void *)((uintptr_t)base_next_addr + csize); + malloc_mutex_unlock(&base_mtx); + + return (ret); +} + +extent_node_t * +base_node_alloc(void) +{ + extent_node_t *ret; + + malloc_mutex_lock(&base_mtx); + if (base_nodes != NULL) { + ret = base_nodes; + base_nodes = *(extent_node_t **)ret; + malloc_mutex_unlock(&base_mtx); + } else { + malloc_mutex_unlock(&base_mtx); + ret = (extent_node_t *)base_alloc(sizeof(extent_node_t)); + } + + return (ret); +} + +void +base_node_dealloc(extent_node_t *node) +{ + + malloc_mutex_lock(&base_mtx); + *(extent_node_t **)node = base_nodes; + base_nodes = node; + malloc_mutex_unlock(&base_mtx); +} + +bool +base_boot(void) +{ + + base_nodes = NULL; + if (malloc_mutex_init(&base_mtx)) + return (true); + + return (false); +} diff --git a/externals/jemalloc/chunk.c b/externals/jemalloc/chunk.c new file mode 100644 index 00000000000..e6e3bcd195a --- /dev/null +++ b/externals/jemalloc/chunk.c @@ -0,0 +1,150 @@ +#define JEMALLOC_CHUNK_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +size_t opt_lg_chunk = LG_CHUNK_DEFAULT; +#ifdef JEMALLOC_SWAP +bool opt_overcommit = true; +#endif + +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) +malloc_mutex_t chunks_mtx; +chunk_stats_t stats_chunks; +#endif + +/* Various chunk-related settings. */ +size_t chunksize; +size_t chunksize_mask; /* (chunksize - 1). */ +size_t chunk_npages; +size_t arena_chunk_header_npages; +size_t arena_maxclass; /* Max size class for arenas. */ + +/******************************************************************************/ + +/* + * If the caller specifies (*zero == false), it is still possible to receive + * zeroed memory, in which case *zero is toggled to true. arena_chunk_alloc() + * takes advantage of this to avoid demanding zeroed chunks, but taking + * advantage of them if they are returned. + */ +void * +chunk_alloc(size_t size, bool *zero) +{ + void *ret; + + assert(size != 0); + assert((size & chunksize_mask) == 0); + +#ifdef JEMALLOC_SWAP + if (swap_enabled) { + ret = chunk_alloc_swap(size, zero); + if (ret != NULL) + goto RETURN; + } + + if (swap_enabled == false || opt_overcommit) { +#endif +#ifdef JEMALLOC_DSS + ret = chunk_alloc_dss(size, zero); + if (ret != NULL) + goto RETURN; +#endif + ret = chunk_alloc_mmap(size); + if (ret != NULL) { + *zero = true; + goto RETURN; + } +#ifdef JEMALLOC_SWAP + } +#endif + + /* All strategies for allocation failed. */ + ret = NULL; +RETURN: +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + if (ret != NULL) { +# ifdef JEMALLOC_PROF + bool udump; +# endif + malloc_mutex_lock(&chunks_mtx); +# ifdef JEMALLOC_STATS + stats_chunks.nchunks += (size / chunksize); +# endif + stats_chunks.curchunks += (size / chunksize); + if (stats_chunks.curchunks > stats_chunks.highchunks) { + stats_chunks.highchunks = stats_chunks.curchunks; +# ifdef JEMALLOC_PROF + udump = true; +# endif + } +# ifdef JEMALLOC_PROF + else + udump = false; +# endif + malloc_mutex_unlock(&chunks_mtx); +# ifdef JEMALLOC_PROF + if (opt_prof && opt_prof_udump && udump) + prof_udump(); +# endif + } +#endif + + assert(CHUNK_ADDR2BASE(ret) == ret); + return (ret); +} + +void +chunk_dealloc(void *chunk, size_t size) +{ + + assert(chunk != NULL); + assert(CHUNK_ADDR2BASE(chunk) == chunk); + assert(size != 0); + assert((size & chunksize_mask) == 0); + +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + malloc_mutex_lock(&chunks_mtx); + stats_chunks.curchunks -= (size / chunksize); + malloc_mutex_unlock(&chunks_mtx); +#endif + +#ifdef JEMALLOC_SWAP + if (swap_enabled && chunk_dealloc_swap(chunk, size) == false) + return; +#endif +#ifdef JEMALLOC_DSS + if (chunk_dealloc_dss(chunk, size) == false) + return; +#endif + chunk_dealloc_mmap(chunk, size); +} + +bool +chunk_boot(void) +{ + + /* Set variables according to the value of opt_lg_chunk. */ + chunksize = (1LU << opt_lg_chunk); + assert(chunksize >= PAGE_SIZE); + chunksize_mask = chunksize - 1; + chunk_npages = (chunksize >> PAGE_SHIFT); + +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + if (malloc_mutex_init(&chunks_mtx)) + return (true); + memset(&stats_chunks, 0, sizeof(chunk_stats_t)); +#endif + +#ifdef JEMALLOC_SWAP + if (chunk_swap_boot()) + return (true); +#endif +#ifdef JEMALLOC_DSS + if (chunk_dss_boot()) + return (true); +#endif + + return (false); +} diff --git a/externals/jemalloc/chunk_dss.c b/externals/jemalloc/chunk_dss.c new file mode 100644 index 00000000000..d9bd63c3ac4 --- /dev/null +++ b/externals/jemalloc/chunk_dss.c @@ -0,0 +1,268 @@ +#define JEMALLOC_CHUNK_DSS_C_ +#include "jemalloc/internal/jemalloc_internal.h" +#ifdef JEMALLOC_DSS +/******************************************************************************/ +/* Data. */ + +malloc_mutex_t dss_mtx; + +/* Base address of the DSS. */ +static void *dss_base; +/* Current end of the DSS, or ((void *)-1) if the DSS is exhausted. */ +static void *dss_prev; +/* Current upper limit on DSS addresses. */ +static void *dss_max; + +/* + * Trees of chunks that were previously allocated (trees differ only in node + * ordering). These are used when allocating chunks, in an attempt to re-use + * address space. Depending on function, different tree orderings are needed, + * which is why there are two trees with the same contents. + */ +static extent_tree_t dss_chunks_szad; +static extent_tree_t dss_chunks_ad; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static void *chunk_recycle_dss(size_t size, bool *zero); +static extent_node_t *chunk_dealloc_dss_record(void *chunk, size_t size); + +/******************************************************************************/ + +static void * +chunk_recycle_dss(size_t size, bool *zero) +{ + extent_node_t *node, key; + + key.addr = NULL; + key.size = size; + malloc_mutex_lock(&dss_mtx); + node = extent_tree_szad_nsearch(&dss_chunks_szad, &key); + if (node != NULL) { + void *ret = node->addr; + + /* Remove node from the tree. */ + extent_tree_szad_remove(&dss_chunks_szad, node); + if (node->size == size) { + extent_tree_ad_remove(&dss_chunks_ad, node); + base_node_dealloc(node); + } else { + /* + * Insert the remainder of node's address range as a + * smaller chunk. Its position within dss_chunks_ad + * does not change. + */ + assert(node->size > size); + node->addr = (void *)((uintptr_t)node->addr + size); + node->size -= size; + extent_tree_szad_insert(&dss_chunks_szad, node); + } + malloc_mutex_unlock(&dss_mtx); + + if (*zero) + memset(ret, 0, size); + return (ret); + } + malloc_mutex_unlock(&dss_mtx); + + return (NULL); +} + +void * +chunk_alloc_dss(size_t size, bool *zero) +{ + void *ret; + + ret = chunk_recycle_dss(size, zero); + if (ret != NULL) + return (ret); + + /* + * sbrk() uses a signed increment argument, so take care not to + * interpret a huge allocation request as a negative increment. + */ + if ((intptr_t)size < 0) + return (NULL); + + malloc_mutex_lock(&dss_mtx); + if (dss_prev != (void *)-1) { + intptr_t incr; + + /* + * The loop is necessary to recover from races with other + * threads that are using the DSS for something other than + * malloc. + */ + do { + /* Get the current end of the DSS. */ + dss_max = sbrk(0); + + /* + * Calculate how much padding is necessary to + * chunk-align the end of the DSS. + */ + incr = (intptr_t)size + - (intptr_t)CHUNK_ADDR2OFFSET(dss_max); + if (incr == (intptr_t)size) + ret = dss_max; + else { + ret = (void *)((intptr_t)dss_max + incr); + incr += size; + } + + dss_prev = sbrk(incr); + if (dss_prev == dss_max) { + /* Success. */ + dss_max = (void *)((intptr_t)dss_prev + incr); + malloc_mutex_unlock(&dss_mtx); + *zero = true; + return (ret); + } + } while (dss_prev != (void *)-1); + } + malloc_mutex_unlock(&dss_mtx); + + return (NULL); +} + +static extent_node_t * +chunk_dealloc_dss_record(void *chunk, size_t size) +{ + extent_node_t *xnode, *node, *prev, key; + + xnode = NULL; + while (true) { + key.addr = (void *)((uintptr_t)chunk + size); + node = extent_tree_ad_nsearch(&dss_chunks_ad, &key); + /* Try to coalesce forward. */ + if (node != NULL && node->addr == key.addr) { + /* + * Coalesce chunk with the following address range. + * This does not change the position within + * dss_chunks_ad, so only remove/insert from/into + * dss_chunks_szad. + */ + extent_tree_szad_remove(&dss_chunks_szad, node); + node->addr = chunk; + node->size += size; + extent_tree_szad_insert(&dss_chunks_szad, node); + break; + } else if (xnode == NULL) { + /* + * It is possible that base_node_alloc() will cause a + * new base chunk to be allocated, so take care not to + * deadlock on dss_mtx, and recover if another thread + * deallocates an adjacent chunk while this one is busy + * allocating xnode. + */ + malloc_mutex_unlock(&dss_mtx); + xnode = base_node_alloc(); + malloc_mutex_lock(&dss_mtx); + if (xnode == NULL) + return (NULL); + } else { + /* Coalescing forward failed, so insert a new node. */ + node = xnode; + xnode = NULL; + node->addr = chunk; + node->size = size; + extent_tree_ad_insert(&dss_chunks_ad, node); + extent_tree_szad_insert(&dss_chunks_szad, node); + break; + } + } + /* Discard xnode if it ended up unused do to a race. */ + if (xnode != NULL) + base_node_dealloc(xnode); + + /* Try to coalesce backward. */ + prev = extent_tree_ad_prev(&dss_chunks_ad, node); + if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) == + chunk) { + /* + * Coalesce chunk with the previous address range. This does + * not change the position within dss_chunks_ad, so only + * remove/insert node from/into dss_chunks_szad. + */ + extent_tree_szad_remove(&dss_chunks_szad, prev); + extent_tree_ad_remove(&dss_chunks_ad, prev); + + extent_tree_szad_remove(&dss_chunks_szad, node); + node->addr = prev->addr; + node->size += prev->size; + extent_tree_szad_insert(&dss_chunks_szad, node); + + base_node_dealloc(prev); + } + + return (node); +} + +bool +chunk_dealloc_dss(void *chunk, size_t size) +{ + bool ret; + + malloc_mutex_lock(&dss_mtx); + if ((uintptr_t)chunk >= (uintptr_t)dss_base + && (uintptr_t)chunk < (uintptr_t)dss_max) { + extent_node_t *node; + + /* Try to coalesce with other unused chunks. */ + node = chunk_dealloc_dss_record(chunk, size); + if (node != NULL) { + chunk = node->addr; + size = node->size; + } + + /* Get the current end of the DSS. */ + dss_max = sbrk(0); + + /* + * Try to shrink the DSS if this chunk is at the end of the + * DSS. The sbrk() call here is subject to a race condition + * with threads that use brk(2) or sbrk(2) directly, but the + * alternative would be to leak memory for the sake of poorly + * designed multi-threaded programs. + */ + if ((void *)((uintptr_t)chunk + size) == dss_max + && (dss_prev = sbrk(-(intptr_t)size)) == dss_max) { + /* Success. */ + dss_max = (void *)((intptr_t)dss_prev - (intptr_t)size); + + if (node != NULL) { + extent_tree_szad_remove(&dss_chunks_szad, node); + extent_tree_ad_remove(&dss_chunks_ad, node); + base_node_dealloc(node); + } + } else + madvise(chunk, size, MADV_DONTNEED); + + ret = false; + goto RETURN; + } + + ret = true; +RETURN: + malloc_mutex_unlock(&dss_mtx); + return (ret); +} + +bool +chunk_dss_boot(void) +{ + + if (malloc_mutex_init(&dss_mtx)) + return (true); + dss_base = sbrk(0); + dss_prev = dss_base; + dss_max = dss_base; + extent_tree_szad_new(&dss_chunks_szad); + extent_tree_ad_new(&dss_chunks_ad); + + return (false); +} + +/******************************************************************************/ +#endif /* JEMALLOC_DSS */ diff --git a/externals/jemalloc/chunk_mmap.c b/externals/jemalloc/chunk_mmap.c new file mode 100644 index 00000000000..8f0711384e3 --- /dev/null +++ b/externals/jemalloc/chunk_mmap.c @@ -0,0 +1,201 @@ +#define JEMALLOC_CHUNK_MMAP_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +/* + * Used by chunk_alloc_mmap() to decide whether to attempt the fast path and + * potentially avoid some system calls. We can get away without TLS here, + * since the state of mmap_unaligned only affects performance, rather than + * correct function. + */ +static +#ifndef NO_TLS + __thread +#endif + bool mmap_unaligned +#ifndef NO_TLS + JEMALLOC_ATTR(tls_model("initial-exec")) +#endif + ; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static void *pages_map(void *addr, size_t size); +static void pages_unmap(void *addr, size_t size); +static void *chunk_alloc_mmap_slow(size_t size, bool unaligned); + +/******************************************************************************/ + +static void * +pages_map(void *addr, size_t size) +{ + void *ret; + + /* + * We don't use MAP_FIXED here, because it can cause the *replacement* + * of existing mappings, and we only want to create new mappings. + */ + ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, + -1, 0); + assert(ret != NULL); + + if (ret == MAP_FAILED) + ret = NULL; + else if (addr != NULL && ret != addr) { + /* + * We succeeded in mapping memory, but not in the right place. + */ + if (munmap(ret, size) == -1) { + char buf[STRERROR_BUF]; + + strerror_r(errno, buf, sizeof(buf)); + malloc_write("<jemalloc>: Error in munmap(): "); + malloc_write(buf); + malloc_write("\n"); + if (opt_abort) + abort(); + } + ret = NULL; + } + + assert(ret == NULL || (addr == NULL && ret != addr) + || (addr != NULL && ret == addr)); + return (ret); +} + +static void +pages_unmap(void *addr, size_t size) +{ + + if (munmap(addr, size) == -1) { + char buf[STRERROR_BUF]; + + strerror_r(errno, buf, sizeof(buf)); + malloc_write("<jemalloc>: Error in munmap(): "); + malloc_write(buf); + malloc_write("\n"); + if (opt_abort) + abort(); + } +} + +static void * +chunk_alloc_mmap_slow(size_t size, bool unaligned) +{ + void *ret; + size_t offset; + + /* Beware size_t wrap-around. */ + if (size + chunksize <= size) + return (NULL); + + ret = pages_map(NULL, size + chunksize); + if (ret == NULL) + return (NULL); + + /* Clean up unneeded leading/trailing space. */ + offset = CHUNK_ADDR2OFFSET(ret); + if (offset != 0) { + /* Note that mmap() returned an unaligned mapping. */ + unaligned = true; + + /* Leading space. */ + pages_unmap(ret, chunksize - offset); + + ret = (void *)((uintptr_t)ret + + (chunksize - offset)); + + /* Trailing space. */ + pages_unmap((void *)((uintptr_t)ret + size), + offset); + } else { + /* Trailing space only. */ + pages_unmap((void *)((uintptr_t)ret + size), + chunksize); + } + + /* + * If mmap() returned an aligned mapping, reset mmap_unaligned so that + * the next chunk_alloc_mmap() execution tries the fast allocation + * method. + */ + if (unaligned == false) + mmap_unaligned = false; + + return (ret); +} + +void * +chunk_alloc_mmap(size_t size) +{ + void *ret; + + /* + * Ideally, there would be a way to specify alignment to mmap() (like + * NetBSD has), but in the absence of such a feature, we have to work + * hard to efficiently create aligned mappings. The reliable, but + * slow method is to create a mapping that is over-sized, then trim the + * excess. However, that always results in at least one call to + * pages_unmap(). + * + * A more optimistic approach is to try mapping precisely the right + * amount, then try to append another mapping if alignment is off. In + * practice, this works out well as long as the application is not + * interleaving mappings via direct mmap() calls. If we do run into a + * situation where there is an interleaved mapping and we are unable to + * extend an unaligned mapping, our best option is to switch to the + * slow method until mmap() returns another aligned mapping. This will + * tend to leave a gap in the memory map that is too small to cause + * later problems for the optimistic method. + * + * Another possible confounding factor is address space layout + * randomization (ASLR), which causes mmap(2) to disregard the + * requested address. mmap_unaligned tracks whether the previous + * chunk_alloc_mmap() execution received any unaligned or relocated + * mappings, and if so, the current execution will immediately fall + * back to the slow method. However, we keep track of whether the fast + * method would have succeeded, and if so, we make a note to try the + * fast method next time. + */ + + if (mmap_unaligned == false) { + size_t offset; + + ret = pages_map(NULL, size); + if (ret == NULL) + return (NULL); + + offset = CHUNK_ADDR2OFFSET(ret); + if (offset != 0) { + mmap_unaligned = true; + /* Try to extend chunk boundary. */ + if (pages_map((void *)((uintptr_t)ret + size), + chunksize - offset) == NULL) { + /* + * Extension failed. Clean up, then revert to + * the reliable-but-expensive method. + */ + pages_unmap(ret, size); + ret = chunk_alloc_mmap_slow(size, true); + } else { + /* Clean up unneeded leading space. */ + pages_unmap(ret, chunksize - offset); + ret = (void *)((uintptr_t)ret + (chunksize - + offset)); + } + } + } else + ret = chunk_alloc_mmap_slow(size, false); + + return (ret); +} + +void +chunk_dealloc_mmap(void *chunk, size_t size) +{ + + pages_unmap(chunk, size); +} diff --git a/externals/jemalloc/chunk_swap.c b/externals/jemalloc/chunk_swap.c new file mode 100644 index 00000000000..b8c880f0a17 --- /dev/null +++ b/externals/jemalloc/chunk_swap.c @@ -0,0 +1,383 @@ +#define JEMALLOC_CHUNK_SWAP_C_ +#include "jemalloc/internal/jemalloc_internal.h" +#ifdef JEMALLOC_SWAP +/******************************************************************************/ +/* Data. */ + +malloc_mutex_t swap_mtx; +bool swap_enabled; +bool swap_prezeroed; +size_t swap_nfds; +int *swap_fds; +#ifdef JEMALLOC_STATS +size_t swap_avail; +#endif + +/* Base address of the mmap()ed file(s). */ +static void *swap_base; +/* Current end of the space in use (<= swap_max). */ +static void *swap_end; +/* Absolute upper limit on file-backed addresses. */ +static void *swap_max; + +/* + * Trees of chunks that were previously allocated (trees differ only in node + * ordering). These are used when allocating chunks, in an attempt to re-use + * address space. Depending on function, different tree orderings are needed, + * which is why there are two trees with the same contents. + */ +static extent_tree_t swap_chunks_szad; +static extent_tree_t swap_chunks_ad; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static void *chunk_recycle_swap(size_t size, bool *zero); +static extent_node_t *chunk_dealloc_swap_record(void *chunk, size_t size); + +/******************************************************************************/ + +static void * +chunk_recycle_swap(size_t size, bool *zero) +{ + extent_node_t *node, key; + + key.addr = NULL; + key.size = size; + malloc_mutex_lock(&swap_mtx); + node = extent_tree_szad_nsearch(&swap_chunks_szad, &key); + if (node != NULL) { + void *ret = node->addr; + + /* Remove node from the tree. */ + extent_tree_szad_remove(&swap_chunks_szad, node); + if (node->size == size) { + extent_tree_ad_remove(&swap_chunks_ad, node); + base_node_dealloc(node); + } else { + /* + * Insert the remainder of node's address range as a + * smaller chunk. Its position within swap_chunks_ad + * does not change. + */ + assert(node->size > size); + node->addr = (void *)((uintptr_t)node->addr + size); + node->size -= size; + extent_tree_szad_insert(&swap_chunks_szad, node); + } +#ifdef JEMALLOC_STATS + swap_avail -= size; +#endif + malloc_mutex_unlock(&swap_mtx); + + if (*zero) + memset(ret, 0, size); + return (ret); + } + malloc_mutex_unlock(&swap_mtx); + + return (NULL); +} + +void * +chunk_alloc_swap(size_t size, bool *zero) +{ + void *ret; + + assert(swap_enabled); + + ret = chunk_recycle_swap(size, zero); + if (ret != NULL) + return (ret); + + malloc_mutex_lock(&swap_mtx); + if ((uintptr_t)swap_end + size <= (uintptr_t)swap_max) { + ret = swap_end; + swap_end = (void *)((uintptr_t)swap_end + size); +#ifdef JEMALLOC_STATS + swap_avail -= size; +#endif + malloc_mutex_unlock(&swap_mtx); + + if (swap_prezeroed) + *zero = true; + else if (*zero) + memset(ret, 0, size); + } else { + malloc_mutex_unlock(&swap_mtx); + return (NULL); + } + + return (ret); +} + +static extent_node_t * +chunk_dealloc_swap_record(void *chunk, size_t size) +{ + extent_node_t *xnode, *node, *prev, key; + + xnode = NULL; + while (true) { + key.addr = (void *)((uintptr_t)chunk + size); + node = extent_tree_ad_nsearch(&swap_chunks_ad, &key); + /* Try to coalesce forward. */ + if (node != NULL && node->addr == key.addr) { + /* + * Coalesce chunk with the following address range. + * This does not change the position within + * swap_chunks_ad, so only remove/insert from/into + * swap_chunks_szad. + */ + extent_tree_szad_remove(&swap_chunks_szad, node); + node->addr = chunk; + node->size += size; + extent_tree_szad_insert(&swap_chunks_szad, node); + break; + } else if (xnode == NULL) { + /* + * It is possible that base_node_alloc() will cause a + * new base chunk to be allocated, so take care not to + * deadlock on swap_mtx, and recover if another thread + * deallocates an adjacent chunk while this one is busy + * allocating xnode. + */ + malloc_mutex_unlock(&swap_mtx); + xnode = base_node_alloc(); + malloc_mutex_lock(&swap_mtx); + if (xnode == NULL) + return (NULL); + } else { + /* Coalescing forward failed, so insert a new node. */ + node = xnode; + xnode = NULL; + node->addr = chunk; + node->size = size; + extent_tree_ad_insert(&swap_chunks_ad, node); + extent_tree_szad_insert(&swap_chunks_szad, node); + break; + } + } + /* Discard xnode if it ended up unused do to a race. */ + if (xnode != NULL) + base_node_dealloc(xnode); + + /* Try to coalesce backward. */ + prev = extent_tree_ad_prev(&swap_chunks_ad, node); + if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) == + chunk) { + /* + * Coalesce chunk with the previous address range. This does + * not change the position within swap_chunks_ad, so only + * remove/insert node from/into swap_chunks_szad. + */ + extent_tree_szad_remove(&swap_chunks_szad, prev); + extent_tree_ad_remove(&swap_chunks_ad, prev); + + extent_tree_szad_remove(&swap_chunks_szad, node); + node->addr = prev->addr; + node->size += prev->size; + extent_tree_szad_insert(&swap_chunks_szad, node); + + base_node_dealloc(prev); + } + + return (node); +} + +bool +chunk_dealloc_swap(void *chunk, size_t size) +{ + bool ret; + + assert(swap_enabled); + + malloc_mutex_lock(&swap_mtx); + if ((uintptr_t)chunk >= (uintptr_t)swap_base + && (uintptr_t)chunk < (uintptr_t)swap_max) { + extent_node_t *node; + + /* Try to coalesce with other unused chunks. */ + node = chunk_dealloc_swap_record(chunk, size); + if (node != NULL) { + chunk = node->addr; + size = node->size; + } + + /* + * Try to shrink the in-use memory if this chunk is at the end + * of the in-use memory. + */ + if ((void *)((uintptr_t)chunk + size) == swap_end) { + swap_end = (void *)((uintptr_t)swap_end - size); + + if (node != NULL) { + extent_tree_szad_remove(&swap_chunks_szad, + node); + extent_tree_ad_remove(&swap_chunks_ad, node); + base_node_dealloc(node); + } + } else + madvise(chunk, size, MADV_DONTNEED); + + ret = false; + goto RETURN; + } + + ret = true; +RETURN: +#ifdef JEMALLOC_STATS + swap_avail += size; +#endif + malloc_mutex_unlock(&swap_mtx); + return (ret); +} + +bool +chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed) +{ + bool ret; + unsigned i; + off_t off; + void *vaddr; + size_t cumsize, voff; + size_t sizes[nfds]; + + malloc_mutex_lock(&swap_mtx); + + /* Get file sizes. */ + for (i = 0, cumsize = 0; i < nfds; i++) { + off = lseek(fds[i], 0, SEEK_END); + if (off == ((off_t)-1)) { + ret = true; + goto RETURN; + } + if (PAGE_CEILING(off) != off) { + /* Truncate to a multiple of the page size. */ + off &= ~PAGE_MASK; + if (ftruncate(fds[i], off) != 0) { + ret = true; + goto RETURN; + } + } + sizes[i] = off; + if (cumsize + off < cumsize) { + /* + * Cumulative file size is greater than the total + * address space. Bail out while it's still obvious + * what the problem is. + */ + ret = true; + goto RETURN; + } + cumsize += off; + } + + /* Round down to a multiple of the chunk size. */ + cumsize &= ~chunksize_mask; + if (cumsize == 0) { + ret = true; + goto RETURN; + } + + /* + * Allocate a chunk-aligned region of anonymous memory, which will + * be the final location for the memory-mapped files. + */ + vaddr = chunk_alloc_mmap(cumsize); + if (vaddr == NULL) { + ret = true; + goto RETURN; + } + + /* Overlay the files onto the anonymous mapping. */ + for (i = 0, voff = 0; i < nfds; i++) { + void *addr = mmap((void *)((uintptr_t)vaddr + voff), sizes[i], + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fds[i], 0); + if (addr == MAP_FAILED) { + char buf[STRERROR_BUF]; + + strerror_r(errno, buf, sizeof(buf)); + malloc_write( + "<jemalloc>: Error in mmap(..., MAP_FIXED, ...): "); + malloc_write(buf); + malloc_write("\n"); + if (opt_abort) + abort(); + if (munmap(vaddr, voff) == -1) { + strerror_r(errno, buf, sizeof(buf)); + malloc_write("<jemalloc>: Error in munmap(): "); + malloc_write(buf); + malloc_write("\n"); + } + ret = true; + goto RETURN; + } + assert(addr == (void *)((uintptr_t)vaddr + voff)); + + /* + * Tell the kernel that the mapping will be accessed randomly, + * and that it should not gratuitously sync pages to the + * filesystem. + */ +#ifdef MADV_RANDOM + madvise(addr, sizes[i], MADV_RANDOM); +#endif +#ifdef MADV_NOSYNC + madvise(addr, sizes[i], MADV_NOSYNC); +#endif + + voff += sizes[i]; + } + + swap_prezeroed = prezeroed; + swap_base = vaddr; + swap_end = swap_base; + swap_max = (void *)((uintptr_t)vaddr + cumsize); + + /* Copy the fds array for mallctl purposes. */ + swap_fds = (int *)base_alloc(nfds * sizeof(int)); + if (swap_fds == NULL) { + ret = true; + goto RETURN; + } + memcpy(swap_fds, fds, nfds * sizeof(int)); + swap_nfds = nfds; + +#ifdef JEMALLOC_STATS + swap_avail = cumsize; +#endif + + swap_enabled = true; + + ret = false; +RETURN: + malloc_mutex_unlock(&swap_mtx); + return (ret); +} + +bool +chunk_swap_boot(void) +{ + + if (malloc_mutex_init(&swap_mtx)) + return (true); + + swap_enabled = false; + swap_prezeroed = false; /* swap.* mallctl's depend on this. */ + swap_nfds = 0; + swap_fds = NULL; +#ifdef JEMALLOC_STATS + swap_avail = 0; +#endif + swap_base = NULL; + swap_end = NULL; + swap_max = NULL; + + extent_tree_szad_new(&swap_chunks_szad); + extent_tree_ad_new(&swap_chunks_ad); + + return (false); +} + +/******************************************************************************/ +#endif /* JEMALLOC_SWAP */ diff --git a/externals/jemalloc/ckh.c b/externals/jemalloc/ckh.c new file mode 100644 index 00000000000..a0c4162aa19 --- /dev/null +++ b/externals/jemalloc/ckh.c @@ -0,0 +1,601 @@ +/* + ******************************************************************************* + * Implementation of (2^1+,2) cuckoo hashing, where 2^1+ indicates that each + * hash bucket contains 2^n cells, for n >= 1, and 2 indicates that two hash + * functions are employed. The original cuckoo hashing algorithm was described + * in: + * + * Pagh, R., F.F. Rodler (2004) Cuckoo Hashing. Journal of Algorithms + * 51(2):122-144. + * + * Generalization of cuckoo hashing was discussed in: + * + * Erlingsson, U., M. Manasse, F. McSherry (2006) A cool and practical + * alternative to traditional hash tables. In Proceedings of the 7th + * Workshop on Distributed Data and Structures (WDAS'06), Santa Clara, CA, + * January 2006. + * + * This implementation uses precisely two hash functions because that is the + * fewest that can work, and supporting multiple hashes is an implementation + * burden. Here is a reproduction of Figure 1 from Erlingsson et al. (2006) + * that shows approximate expected maximum load factors for various + * configurations: + * + * | #cells/bucket | + * #hashes | 1 | 2 | 4 | 8 | + * --------+-------+-------+-------+-------+ + * 1 | 0.006 | 0.006 | 0.03 | 0.12 | + * 2 | 0.49 | 0.86 |>0.93< |>0.96< | + * 3 | 0.91 | 0.97 | 0.98 | 0.999 | + * 4 | 0.97 | 0.99 | 0.999 | | + * + * The number of cells per bucket is chosen such that a bucket fits in one cache + * line. So, on 32- and 64-bit systems, we use (8,2) and (4,2) cuckoo hashing, + * respectively. + * + ******************************************************************************/ +#define CKH_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static bool ckh_grow(ckh_t *ckh); +static void ckh_shrink(ckh_t *ckh); + +/******************************************************************************/ + +/* + * Search bucket for key and return the cell number if found; SIZE_T_MAX + * otherwise. + */ +JEMALLOC_INLINE size_t +ckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key) +{ + ckhc_t *cell; + unsigned i; + + for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) { + cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i]; + if (cell->key != NULL && ckh->keycomp(key, cell->key)) + return ((bucket << LG_CKH_BUCKET_CELLS) + i); + } + + return (SIZE_T_MAX); +} + +/* + * Search table for key and return cell number if found; SIZE_T_MAX otherwise. + */ +JEMALLOC_INLINE size_t +ckh_isearch(ckh_t *ckh, const void *key) +{ + size_t hash1, hash2, bucket, cell; + + assert(ckh != NULL); + assert(ckh->magic = CKH_MAGIG); + + ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2); + + /* Search primary bucket. */ + bucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1); + cell = ckh_bucket_search(ckh, bucket, key); + if (cell != SIZE_T_MAX) + return (cell); + + /* Search secondary bucket. */ + bucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1); + cell = ckh_bucket_search(ckh, bucket, key); + return (cell); +} + +JEMALLOC_INLINE bool +ckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key, + const void *data) +{ + ckhc_t *cell; + unsigned offset, i; + + /* + * Cycle through the cells in the bucket, starting at a random position. + * The randomness avoids worst-case search overhead as buckets fill up. + */ + prn32(offset, LG_CKH_BUCKET_CELLS, ckh->prn_state, CKH_A, CKH_C); + for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) { + cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + + ((i + offset) & ((ZU(1) << LG_CKH_BUCKET_CELLS) - 1))]; + if (cell->key == NULL) { + cell->key = key; + cell->data = data; + ckh->count++; + return (false); + } + } + + return (true); +} + +/* + * No space is available in bucket. Randomly evict an item, then try to find an + * alternate location for that item. Iteratively repeat this + * eviction/relocation procedure until either success or detection of an + * eviction/relocation bucket cycle. + */ +JEMALLOC_INLINE bool +ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey, + void const **argdata) +{ + const void *key, *data, *tkey, *tdata; + ckhc_t *cell; + size_t hash1, hash2, bucket, tbucket; + unsigned i; + + bucket = argbucket; + key = *argkey; + data = *argdata; + while (true) { + /* + * Choose a random item within the bucket to evict. This is + * critical to correct function, because without (eventually) + * evicting all items within a bucket during iteration, it + * would be possible to get stuck in an infinite loop if there + * were an item for which both hashes indicated the same + * bucket. + */ + prn32(i, LG_CKH_BUCKET_CELLS, ckh->prn_state, CKH_A, CKH_C); + cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i]; + assert(cell->key != NULL); + + /* Swap cell->{key,data} and {key,data} (evict). */ + tkey = cell->key; tdata = cell->data; + cell->key = key; cell->data = data; + key = tkey; data = tdata; + +#ifdef CKH_COUNT + ckh->nrelocs++; +#endif + + /* Find the alternate bucket for the evicted item. */ + ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2); + tbucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1); + if (tbucket == bucket) { + tbucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1); + /* + * It may be that (tbucket == bucket) still, if the + * item's hashes both indicate this bucket. However, + * we are guaranteed to eventually escape this bucket + * during iteration, assuming pseudo-random item + * selection (true randomness would make infinite + * looping a remote possibility). The reason we can + * never get trapped forever is that there are two + * cases: + * + * 1) This bucket == argbucket, so we will quickly + * detect an eviction cycle and terminate. + * 2) An item was evicted to this bucket from another, + * which means that at least one item in this bucket + * has hashes that indicate distinct buckets. + */ + } + /* Check for a cycle. */ + if (tbucket == argbucket) { + *argkey = key; + *argdata = data; + return (true); + } + + bucket = tbucket; + if (ckh_try_bucket_insert(ckh, bucket, key, data) == false) + return (false); + } +} + +JEMALLOC_INLINE bool +ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) +{ + size_t hash1, hash2, bucket; + const void *key = *argkey; + const void *data = *argdata; + + ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2); + + /* Try to insert in primary bucket. */ + bucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1); + if (ckh_try_bucket_insert(ckh, bucket, key, data) == false) + return (false); + + /* Try to insert in secondary bucket. */ + bucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1); + if (ckh_try_bucket_insert(ckh, bucket, key, data) == false) + return (false); + + /* + * Try to find a place for this item via iterative eviction/relocation. + */ + return (ckh_evict_reloc_insert(ckh, bucket, argkey, argdata)); +} + +/* + * Try to rebuild the hash table from scratch by inserting all items from the + * old table into the new. + */ +JEMALLOC_INLINE bool +ckh_rebuild(ckh_t *ckh, ckhc_t *aTab) +{ + size_t count, i, nins; + const void *key, *data; + + count = ckh->count; + ckh->count = 0; + for (i = nins = 0; nins < count; i++) { + if (aTab[i].key != NULL) { + key = aTab[i].key; + data = aTab[i].data; + if (ckh_try_insert(ckh, &key, &data)) { + ckh->count = count; + return (true); + } + nins++; + } + } + + return (false); +} + +static bool +ckh_grow(ckh_t *ckh) +{ + bool ret; + ckhc_t *tab, *ttab; + size_t lg_curcells; + unsigned lg_prevbuckets; + +#ifdef CKH_COUNT + ckh->ngrows++; +#endif + + /* + * It is possible (though unlikely, given well behaved hashes) that the + * table will have to be doubled more than once in order to create a + * usable table. + */ + lg_prevbuckets = ckh->lg_curbuckets; + lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS; + while (true) { + lg_curcells++; + tab = (ckhc_t *) ipalloc((ZU(1) << LG_CACHELINE), + sizeof(ckhc_t) << lg_curcells); + if (tab == NULL) { + ret = true; + goto RETURN; + } + memset(tab, 0, sizeof(ckhc_t) << lg_curcells); + /* Swap in new table. */ + ttab = ckh->tab; + ckh->tab = tab; + tab = ttab; + ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS; + + if (ckh_rebuild(ckh, tab) == false) { + idalloc(tab); + break; + } + + /* Rebuilding failed, so back out partially rebuilt table. */ + idalloc(ckh->tab); + ckh->tab = tab; + ckh->lg_curbuckets = lg_prevbuckets; + } + + ret = false; +RETURN: + return (ret); +} + +static void +ckh_shrink(ckh_t *ckh) +{ + ckhc_t *tab, *ttab; + size_t lg_curcells; + unsigned lg_prevbuckets; + + /* + * It is possible (though unlikely, given well behaved hashes) that the + * table rebuild will fail. + */ + lg_prevbuckets = ckh->lg_curbuckets; + lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS - 1; + tab = (ckhc_t *)ipalloc((ZU(1) << LG_CACHELINE), + sizeof(ckhc_t) << lg_curcells); + if (tab == NULL) { + /* + * An OOM error isn't worth propagating, since it doesn't + * prevent this or future operations from proceeding. + */ + return; + } + memset(tab, 0, sizeof(ckhc_t) << lg_curcells); + /* Swap in new table. */ + ttab = ckh->tab; + ckh->tab = tab; + tab = ttab; + ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS; + + if (ckh_rebuild(ckh, tab) == false) { + idalloc(tab); +#ifdef CKH_COUNT + ckh->nshrinks++; +#endif + return; + } + + /* Rebuilding failed, so back out partially rebuilt table. */ + idalloc(ckh->tab); + ckh->tab = tab; + ckh->lg_curbuckets = lg_prevbuckets; +#ifdef CKH_COUNT + ckh->nshrinkfails++; +#endif +} + +bool +ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp) +{ + bool ret; + size_t mincells; + unsigned lg_mincells; + + assert(minitems > 0); + assert(hash != NULL); + assert(keycomp != NULL); + +#ifdef CKH_COUNT + ckh->ngrows = 0; + ckh->nshrinks = 0; + ckh->nshrinkfails = 0; + ckh->ninserts = 0; + ckh->nrelocs = 0; +#endif + ckh->prn_state = 42; /* Value doesn't really matter. */ + ckh->count = 0; + + /* + * Find the minimum power of 2 that is large enough to fit aBaseCount + * entries. We are using (2+,2) cuckoo hashing, which has an expected + * maximum load factor of at least ~0.86, so 0.75 is a conservative load + * factor that will typically allow 2^aLgMinItems to fit without ever + * growing the table. + */ + assert(LG_CKH_BUCKET_CELLS > 0); + mincells = ((minitems + (3 - (minitems % 3))) / 3) << 2; + for (lg_mincells = LG_CKH_BUCKET_CELLS; + (ZU(1) << lg_mincells) < mincells; + lg_mincells++) + ; /* Do nothing. */ + ckh->lg_minbuckets = lg_mincells - LG_CKH_BUCKET_CELLS; + ckh->lg_curbuckets = lg_mincells - LG_CKH_BUCKET_CELLS; + ckh->hash = hash; + ckh->keycomp = keycomp; + + ckh->tab = (ckhc_t *)ipalloc((ZU(1) << LG_CACHELINE), + sizeof(ckhc_t) << lg_mincells); + if (ckh->tab == NULL) { + ret = true; + goto RETURN; + } + memset(ckh->tab, 0, sizeof(ckhc_t) << lg_mincells); + +#ifdef JEMALLOC_DEBUG + ckh->magic = CKH_MAGIG; +#endif + + ret = false; +RETURN: + return (ret); +} + +void +ckh_delete(ckh_t *ckh) +{ + + assert(ckh != NULL); + assert(ckh->magic = CKH_MAGIG); + +#ifdef CKH_VERBOSE + malloc_printf( + "%s(%p): ngrows: %"PRIu64", nshrinks: %"PRIu64"," + " nshrinkfails: %"PRIu64", ninserts: %"PRIu64"," + " nrelocs: %"PRIu64"\n", __func__, ckh, + (unsigned long long)ckh->ngrows, + (unsigned long long)ckh->nshrinks, + (unsigned long long)ckh->nshrinkfails, + (unsigned long long)ckh->ninserts, + (unsigned long long)ckh->nrelocs); +#endif + + idalloc(ckh->tab); +#ifdef JEMALLOC_DEBUG + memset(ckh, 0x5a, sizeof(ckh_t)); +#endif +} + +size_t +ckh_count(ckh_t *ckh) +{ + + assert(ckh != NULL); + assert(ckh->magic = CKH_MAGIG); + + return (ckh->count); +} + +bool +ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data) +{ + size_t i, ncells; + + for (i = *tabind, ncells = (ZU(1) << (ckh->lg_curbuckets + + LG_CKH_BUCKET_CELLS)); i < ncells; i++) { + if (ckh->tab[i].key != NULL) { + if (key != NULL) + *key = (void *)ckh->tab[i].key; + if (data != NULL) + *data = (void *)ckh->tab[i].data; + *tabind = i + 1; + return (false); + } + } + + return (true); +} + +bool +ckh_insert(ckh_t *ckh, const void *key, const void *data) +{ + bool ret; + + assert(ckh != NULL); + assert(ckh->magic = CKH_MAGIG); + assert(ckh_search(ckh, key, NULL, NULL)); + +#ifdef CKH_COUNT + ckh->ninserts++; +#endif + + while (ckh_try_insert(ckh, &key, &data)) { + if (ckh_grow(ckh)) { + ret = true; + goto RETURN; + } + } + + ret = false; +RETURN: + return (ret); +} + +bool +ckh_remove(ckh_t *ckh, const void *searchkey, void **key, void **data) +{ + size_t cell; + + assert(ckh != NULL); + assert(ckh->magic = CKH_MAGIG); + + cell = ckh_isearch(ckh, searchkey); + if (cell != SIZE_T_MAX) { + if (key != NULL) + *key = (void *)ckh->tab[cell].key; + if (data != NULL) + *data = (void *)ckh->tab[cell].data; + ckh->tab[cell].key = NULL; + ckh->tab[cell].data = NULL; /* Not necessary. */ + + ckh->count--; + /* Try to halve the table if it is less than 1/4 full. */ + if (ckh->count < (ZU(1) << (ckh->lg_curbuckets + + LG_CKH_BUCKET_CELLS - 2)) && ckh->lg_curbuckets + > ckh->lg_minbuckets) { + /* Ignore error due to OOM. */ + ckh_shrink(ckh); + } + + return (false); + } + + return (true); +} + +bool +ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data) +{ + size_t cell; + + assert(ckh != NULL); + assert(ckh->magic = CKH_MAGIG); + + cell = ckh_isearch(ckh, searchkey); + if (cell != SIZE_T_MAX) { + if (key != NULL) + *key = (void *)ckh->tab[cell].key; + if (data != NULL) + *data = (void *)ckh->tab[cell].data; + return (false); + } + + return (true); +} + +void +ckh_string_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2) +{ + size_t ret1, ret2; + uint64_t h; + + assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64)); + assert(hash1 != NULL); + assert(hash2 != NULL); + + h = hash(key, strlen((const char *)key), 0x94122f335b332aeaLLU); + if (minbits <= 32) { + /* + * Avoid doing multiple hashes, since a single hash provides + * enough bits. + */ + ret1 = h & ZU(0xffffffffU); + ret2 = h >> 32; + } else { + ret1 = h; + ret2 = hash(key, strlen((const char *)key), + 0x8432a476666bbc13U); + } + + *hash1 = ret1; + *hash2 = ret2; +} + +bool +ckh_string_keycomp(const void *k1, const void *k2) +{ + + assert(k1 != NULL); + assert(k2 != NULL); + + return (strcmp((char *)k1, (char *)k2) ? false : true); +} + +void +ckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1, + size_t *hash2) +{ + size_t ret1, ret2; + uint64_t h; + + assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64)); + assert(hash1 != NULL); + assert(hash2 != NULL); + + h = hash(&key, sizeof(void *), 0xd983396e68886082LLU); + if (minbits <= 32) { + /* + * Avoid doing multiple hashes, since a single hash provides + * enough bits. + */ + ret1 = h & ZU(0xffffffffU); + ret2 = h >> 32; + } else { + assert(SIZEOF_PTR == 8); + ret1 = h; + ret2 = hash(&key, sizeof(void *), 0x5e2be9aff8709a5dLLU); + } + + *hash1 = ret1; + *hash2 = ret2; +} + +bool +ckh_pointer_keycomp(const void *k1, const void *k2) +{ + + return ((k1 == k2) ? true : false); +} diff --git a/externals/jemalloc/ctl.c b/externals/jemalloc/ctl.c new file mode 100644 index 00000000000..ffb732d5bef --- /dev/null +++ b/externals/jemalloc/ctl.c @@ -0,0 +1,1482 @@ +#define JEMALLOC_CTL_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +static malloc_mutex_t ctl_mtx; +static bool ctl_initialized; +static uint64_t ctl_epoch; +static ctl_stats_t ctl_stats; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +#define CTL_PROTO(n) \ +static int n##_ctl(const size_t *mib, size_t miblen, void *oldp, \ + size_t *oldlenp, void *newp, size_t newlen); + +#define INDEX_PROTO(n) \ +const ctl_node_t *n##_index(const size_t *mib, size_t miblen, \ + size_t i); + +#ifdef JEMALLOC_STATS +static bool ctl_arena_init(ctl_arena_stats_t *astats); +#endif +static void ctl_arena_clear(ctl_arena_stats_t *astats); +#ifdef JEMALLOC_STATS +static void ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, + arena_t *arena); +static void ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, + ctl_arena_stats_t *astats); +#endif +static void ctl_arena_refresh(arena_t *arena, unsigned i); +static void ctl_refresh(void); +static bool ctl_init(void); +static int ctl_lookup(const char *name, ctl_node_t const **nodesp, + size_t *mibp, size_t *depthp); + +CTL_PROTO(version) +CTL_PROTO(epoch) +#ifdef JEMALLOC_TCACHE +CTL_PROTO(tcache_flush) +#endif +CTL_PROTO(config_debug) +CTL_PROTO(config_dss) +CTL_PROTO(config_dynamic_page_shift) +CTL_PROTO(config_fill) +CTL_PROTO(config_lazy_lock) +CTL_PROTO(config_prof) +CTL_PROTO(config_prof_libgcc) +CTL_PROTO(config_prof_libunwind) +CTL_PROTO(config_stats) +CTL_PROTO(config_swap) +CTL_PROTO(config_sysv) +CTL_PROTO(config_tcache) +CTL_PROTO(config_tiny) +CTL_PROTO(config_tls) +CTL_PROTO(config_xmalloc) +CTL_PROTO(opt_abort) +#ifdef JEMALLOC_FILL +CTL_PROTO(opt_junk) +#endif +#ifdef JEMALLOC_SYSV +CTL_PROTO(opt_sysv) +#endif +#ifdef JEMALLOC_XMALLOC +CTL_PROTO(opt_xmalloc) +#endif +#ifdef JEMALLOC_ZERO +CTL_PROTO(opt_zero) +#endif +#ifdef JEMALLOC_TCACHE +CTL_PROTO(opt_tcache) +CTL_PROTO(opt_lg_tcache_gc_sweep) +#endif +#ifdef JEMALLOC_PROF +CTL_PROTO(opt_prof) +CTL_PROTO(opt_prof_active) +CTL_PROTO(opt_lg_prof_bt_max) +CTL_PROTO(opt_lg_prof_sample) +CTL_PROTO(opt_lg_prof_interval) +CTL_PROTO(opt_prof_udump) +CTL_PROTO(opt_prof_leak) +#endif +CTL_PROTO(opt_stats_print) +CTL_PROTO(opt_lg_qspace_max) +CTL_PROTO(opt_lg_cspace_max) +CTL_PROTO(opt_lg_dirty_mult) +CTL_PROTO(opt_lg_chunk) +#ifdef JEMALLOC_SWAP +CTL_PROTO(opt_overcommit) +#endif +CTL_PROTO(arenas_bin_i_size) +CTL_PROTO(arenas_bin_i_nregs) +CTL_PROTO(arenas_bin_i_run_size) +INDEX_PROTO(arenas_bin_i) +CTL_PROTO(arenas_lrun_i_size) +INDEX_PROTO(arenas_lrun_i) +CTL_PROTO(arenas_narenas) +CTL_PROTO(arenas_initialized) +CTL_PROTO(arenas_quantum) +CTL_PROTO(arenas_cacheline) +CTL_PROTO(arenas_subpage) +CTL_PROTO(arenas_pagesize) +CTL_PROTO(arenas_chunksize) +#ifdef JEMALLOC_TINY +CTL_PROTO(arenas_tspace_min) +CTL_PROTO(arenas_tspace_max) +#endif +CTL_PROTO(arenas_qspace_min) +CTL_PROTO(arenas_qspace_max) +CTL_PROTO(arenas_cspace_min) +CTL_PROTO(arenas_cspace_max) +CTL_PROTO(arenas_sspace_min) +CTL_PROTO(arenas_sspace_max) +#ifdef JEMALLOC_TCACHE +CTL_PROTO(arenas_tcache_max) +#endif +CTL_PROTO(arenas_ntbins) +CTL_PROTO(arenas_nqbins) +CTL_PROTO(arenas_ncbins) +CTL_PROTO(arenas_nsbins) +CTL_PROTO(arenas_nbins) +#ifdef JEMALLOC_TCACHE +CTL_PROTO(arenas_nhbins) +#endif +CTL_PROTO(arenas_nlruns) +#ifdef JEMALLOC_PROF +CTL_PROTO(prof_active) +CTL_PROTO(prof_dump) +CTL_PROTO(prof_interval) +#endif +#ifdef JEMALLOC_STATS +CTL_PROTO(stats_chunks_current) +CTL_PROTO(stats_chunks_total) +CTL_PROTO(stats_chunks_high) +CTL_PROTO(stats_huge_allocated) +CTL_PROTO(stats_huge_nmalloc) +CTL_PROTO(stats_huge_ndalloc) +CTL_PROTO(stats_arenas_i_small_allocated) +CTL_PROTO(stats_arenas_i_small_nmalloc) +CTL_PROTO(stats_arenas_i_small_ndalloc) +CTL_PROTO(stats_arenas_i_small_nrequests) +CTL_PROTO(stats_arenas_i_large_allocated) +CTL_PROTO(stats_arenas_i_large_nmalloc) +CTL_PROTO(stats_arenas_i_large_ndalloc) +CTL_PROTO(stats_arenas_i_large_nrequests) +CTL_PROTO(stats_arenas_i_bins_j_allocated) +CTL_PROTO(stats_arenas_i_bins_j_nmalloc) +CTL_PROTO(stats_arenas_i_bins_j_ndalloc) +CTL_PROTO(stats_arenas_i_bins_j_nrequests) +#ifdef JEMALLOC_TCACHE +CTL_PROTO(stats_arenas_i_bins_j_nfills) +CTL_PROTO(stats_arenas_i_bins_j_nflushes) +#endif +CTL_PROTO(stats_arenas_i_bins_j_nruns) +CTL_PROTO(stats_arenas_i_bins_j_nreruns) +CTL_PROTO(stats_arenas_i_bins_j_highruns) +CTL_PROTO(stats_arenas_i_bins_j_curruns) +INDEX_PROTO(stats_arenas_i_bins_j) +CTL_PROTO(stats_arenas_i_lruns_j_nmalloc) +CTL_PROTO(stats_arenas_i_lruns_j_ndalloc) +CTL_PROTO(stats_arenas_i_lruns_j_nrequests) +CTL_PROTO(stats_arenas_i_lruns_j_highruns) +CTL_PROTO(stats_arenas_i_lruns_j_curruns) +INDEX_PROTO(stats_arenas_i_lruns_j) +#endif +CTL_PROTO(stats_arenas_i_pactive) +CTL_PROTO(stats_arenas_i_pdirty) +#ifdef JEMALLOC_STATS +CTL_PROTO(stats_arenas_i_mapped) +CTL_PROTO(stats_arenas_i_npurge) +CTL_PROTO(stats_arenas_i_nmadvise) +CTL_PROTO(stats_arenas_i_purged) +#endif +INDEX_PROTO(stats_arenas_i) +#ifdef JEMALLOC_STATS +CTL_PROTO(stats_allocated) +CTL_PROTO(stats_active) +CTL_PROTO(stats_mapped) +#endif +#ifdef JEMALLOC_SWAP +# ifdef JEMALLOC_STATS +CTL_PROTO(swap_avail) +# endif +CTL_PROTO(swap_prezeroed) +CTL_PROTO(swap_nfds) +CTL_PROTO(swap_fds) +#endif + +/******************************************************************************/ +/* mallctl tree. */ + +/* Maximum tree depth. */ +#define CTL_MAX_DEPTH 6 + +#define NAME(n) true, {.named = {n +#define CHILD(c) sizeof(c##_node) / sizeof(ctl_node_t), c##_node}}, NULL +#define CTL(c) 0, NULL}}, c##_ctl + +/* + * Only handles internal indexed nodes, since there are currently no external + * ones. + */ +#define INDEX(i) false, {.indexed = {i##_index}}, NULL + +#ifdef JEMALLOC_TCACHE +static const ctl_node_t tcache_node[] = { + {NAME("flush"), CTL(tcache_flush)} +}; +#endif + +static const ctl_node_t config_node[] = { + {NAME("debug"), CTL(config_debug)}, + {NAME("dss"), CTL(config_dss)}, + {NAME("dynamic_page_shift"), CTL(config_dynamic_page_shift)}, + {NAME("fill"), CTL(config_fill)}, + {NAME("lazy_lock"), CTL(config_lazy_lock)}, + {NAME("prof"), CTL(config_prof)}, + {NAME("prof_libgcc"), CTL(config_prof_libgcc)}, + {NAME("prof_libunwind"), CTL(config_prof_libunwind)}, + {NAME("stats"), CTL(config_stats)}, + {NAME("swap"), CTL(config_swap)}, + {NAME("sysv"), CTL(config_sysv)}, + {NAME("tcache"), CTL(config_tcache)}, + {NAME("tiny"), CTL(config_tiny)}, + {NAME("tls"), CTL(config_tls)}, + {NAME("xmalloc"), CTL(config_xmalloc)} +}; + +static const ctl_node_t opt_node[] = { + {NAME("abort"), CTL(opt_abort)}, +#ifdef JEMALLOC_FILL + {NAME("junk"), CTL(opt_junk)}, +#endif +#ifdef JEMALLOC_SYSV + {NAME("sysv"), CTL(opt_sysv)}, +#endif +#ifdef JEMALLOC_XMALLOC + {NAME("xmalloc"), CTL(opt_xmalloc)}, +#endif +#ifdef JEMALLOC_ZERO + {NAME("zero"), CTL(opt_zero)}, +#endif +#ifdef JEMALLOC_TCACHE + {NAME("tcache"), CTL(opt_tcache)}, + {NAME("lg_tcache_gc_sweep"), CTL(opt_lg_tcache_gc_sweep)}, +#endif +#ifdef JEMALLOC_PROF + {NAME("prof"), CTL(opt_prof)}, + {NAME("prof_active"), CTL(opt_prof_active)}, + {NAME("lg_prof_bt_max"), CTL(opt_lg_prof_bt_max)}, + {NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)}, + {NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)}, + {NAME("prof_udump"), CTL(opt_prof_udump)}, + {NAME("prof_leak"), CTL(opt_prof_leak)}, +#endif + {NAME("stats_print"), CTL(opt_stats_print)}, + {NAME("lg_qspace_max"), CTL(opt_lg_qspace_max)}, + {NAME("lg_cspace_max"), CTL(opt_lg_cspace_max)}, + {NAME("lg_dirty_mult"), CTL(opt_lg_dirty_mult)}, + {NAME("lg_chunk"), CTL(opt_lg_chunk)} +#ifdef JEMALLOC_SWAP + , + {NAME("overcommit"), CTL(opt_overcommit)} +#endif +}; + +static const ctl_node_t arenas_bin_i_node[] = { + {NAME("size"), CTL(arenas_bin_i_size)}, + {NAME("nregs"), CTL(arenas_bin_i_nregs)}, + {NAME("run_size"), CTL(arenas_bin_i_run_size)} +}; +static const ctl_node_t super_arenas_bin_i_node[] = { + {NAME(""), CHILD(arenas_bin_i)} +}; + +static const ctl_node_t arenas_bin_node[] = { + {INDEX(arenas_bin_i)} +}; + +static const ctl_node_t arenas_lrun_i_node[] = { + {NAME("size"), CTL(arenas_lrun_i_size)} +}; +static const ctl_node_t super_arenas_lrun_i_node[] = { + {NAME(""), CHILD(arenas_lrun_i)} +}; + +static const ctl_node_t arenas_lrun_node[] = { + {INDEX(arenas_lrun_i)} +}; + +static const ctl_node_t arenas_node[] = { + {NAME("narenas"), CTL(arenas_narenas)}, + {NAME("initialized"), CTL(arenas_initialized)}, + {NAME("quantum"), CTL(arenas_quantum)}, + {NAME("cacheline"), CTL(arenas_cacheline)}, + {NAME("subpage"), CTL(arenas_subpage)}, + {NAME("pagesize"), CTL(arenas_pagesize)}, + {NAME("chunksize"), CTL(arenas_chunksize)}, +#ifdef JEMALLOC_TINY + {NAME("tspace_min"), CTL(arenas_tspace_min)}, + {NAME("tspace_max"), CTL(arenas_tspace_max)}, +#endif + {NAME("qspace_min"), CTL(arenas_qspace_min)}, + {NAME("qspace_max"), CTL(arenas_qspace_max)}, + {NAME("cspace_min"), CTL(arenas_cspace_min)}, + {NAME("cspace_max"), CTL(arenas_cspace_max)}, + {NAME("sspace_min"), CTL(arenas_sspace_min)}, + {NAME("sspace_max"), CTL(arenas_sspace_max)}, +#ifdef JEMALLOC_TCACHE + {NAME("tcache_max"), CTL(arenas_tcache_max)}, +#endif + {NAME("ntbins"), CTL(arenas_ntbins)}, + {NAME("nqbins"), CTL(arenas_nqbins)}, + {NAME("ncbins"), CTL(arenas_ncbins)}, + {NAME("nsbins"), CTL(arenas_nsbins)}, + {NAME("nbins"), CTL(arenas_nbins)}, +#ifdef JEMALLOC_TCACHE + {NAME("nhbins"), CTL(arenas_nhbins)}, +#endif + {NAME("bin"), CHILD(arenas_bin)}, + {NAME("nlruns"), CTL(arenas_nlruns)}, + {NAME("lrun"), CHILD(arenas_lrun)} +}; + +#ifdef JEMALLOC_PROF +static const ctl_node_t prof_node[] = { + {NAME("active"), CTL(prof_active)}, + {NAME("dump"), CTL(prof_dump)}, + {NAME("interval"), CTL(prof_interval)} +}; +#endif + +#ifdef JEMALLOC_STATS +static const ctl_node_t stats_chunks_node[] = { + {NAME("current"), CTL(stats_chunks_current)}, + {NAME("total"), CTL(stats_chunks_total)}, + {NAME("high"), CTL(stats_chunks_high)} +}; + +static const ctl_node_t stats_huge_node[] = { + {NAME("allocated"), CTL(stats_huge_allocated)}, + {NAME("nmalloc"), CTL(stats_huge_nmalloc)}, + {NAME("ndalloc"), CTL(stats_huge_ndalloc)} +}; + +static const ctl_node_t stats_arenas_i_small_node[] = { + {NAME("allocated"), CTL(stats_arenas_i_small_allocated)}, + {NAME("nmalloc"), CTL(stats_arenas_i_small_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_small_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_small_nrequests)} +}; + +static const ctl_node_t stats_arenas_i_large_node[] = { + {NAME("allocated"), CTL(stats_arenas_i_large_allocated)}, + {NAME("nmalloc"), CTL(stats_arenas_i_large_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_large_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_large_nrequests)} +}; + +static const ctl_node_t stats_arenas_i_bins_j_node[] = { + {NAME("allocated"), CTL(stats_arenas_i_bins_j_allocated)}, + {NAME("nmalloc"), CTL(stats_arenas_i_bins_j_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_bins_j_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_bins_j_nrequests)}, +#ifdef JEMALLOC_TCACHE + {NAME("nfills"), CTL(stats_arenas_i_bins_j_nfills)}, + {NAME("nflushes"), CTL(stats_arenas_i_bins_j_nflushes)}, +#endif + {NAME("nruns"), CTL(stats_arenas_i_bins_j_nruns)}, + {NAME("nreruns"), CTL(stats_arenas_i_bins_j_nreruns)}, + {NAME("highruns"), CTL(stats_arenas_i_bins_j_highruns)}, + {NAME("curruns"), CTL(stats_arenas_i_bins_j_curruns)} +}; +static const ctl_node_t super_stats_arenas_i_bins_j_node[] = { + {NAME(""), CHILD(stats_arenas_i_bins_j)} +}; + +static const ctl_node_t stats_arenas_i_bins_node[] = { + {INDEX(stats_arenas_i_bins_j)} +}; + +static const ctl_node_t stats_arenas_i_lruns_j_node[] = { + {NAME("nmalloc"), CTL(stats_arenas_i_lruns_j_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_lruns_j_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_lruns_j_nrequests)}, + {NAME("highruns"), CTL(stats_arenas_i_lruns_j_highruns)}, + {NAME("curruns"), CTL(stats_arenas_i_lruns_j_curruns)} +}; +static const ctl_node_t super_stats_arenas_i_lruns_j_node[] = { + {NAME(""), CHILD(stats_arenas_i_lruns_j)} +}; + +static const ctl_node_t stats_arenas_i_lruns_node[] = { + {INDEX(stats_arenas_i_lruns_j)} +}; +#endif + +static const ctl_node_t stats_arenas_i_node[] = { + {NAME("pactive"), CTL(stats_arenas_i_pactive)}, + {NAME("pdirty"), CTL(stats_arenas_i_pdirty)} +#ifdef JEMALLOC_STATS + , + {NAME("mapped"), CTL(stats_arenas_i_mapped)}, + {NAME("npurge"), CTL(stats_arenas_i_npurge)}, + {NAME("nmadvise"), CTL(stats_arenas_i_nmadvise)}, + {NAME("purged"), CTL(stats_arenas_i_purged)}, + {NAME("small"), CHILD(stats_arenas_i_small)}, + {NAME("large"), CHILD(stats_arenas_i_large)}, + {NAME("bins"), CHILD(stats_arenas_i_bins)}, + {NAME("lruns"), CHILD(stats_arenas_i_lruns)} +#endif +}; +static const ctl_node_t super_stats_arenas_i_node[] = { + {NAME(""), CHILD(stats_arenas_i)} +}; + +static const ctl_node_t stats_arenas_node[] = { + {INDEX(stats_arenas_i)} +}; + +static const ctl_node_t stats_node[] = { +#ifdef JEMALLOC_STATS + {NAME("allocated"), CTL(stats_allocated)}, + {NAME("active"), CTL(stats_active)}, + {NAME("mapped"), CTL(stats_mapped)}, + {NAME("chunks"), CHILD(stats_chunks)}, + {NAME("huge"), CHILD(stats_huge)}, +#endif + {NAME("arenas"), CHILD(stats_arenas)} +}; + +#ifdef JEMALLOC_SWAP +static const ctl_node_t swap_node[] = { +# ifdef JEMALLOC_STATS + {NAME("avail"), CTL(swap_avail)}, +# endif + {NAME("prezeroed"), CTL(swap_prezeroed)}, + {NAME("nfds"), CTL(swap_nfds)}, + {NAME("fds"), CTL(swap_fds)} +}; +#endif + +static const ctl_node_t root_node[] = { + {NAME("version"), CTL(version)}, + {NAME("epoch"), CTL(epoch)}, +#ifdef JEMALLOC_TCACHE + {NAME("tcache"), CHILD(tcache)}, +#endif + {NAME("config"), CHILD(config)}, + {NAME("opt"), CHILD(opt)}, + {NAME("arenas"), CHILD(arenas)}, +#ifdef JEMALLOC_PROF + {NAME("prof"), CHILD(prof)}, +#endif + {NAME("stats"), CHILD(stats)} +#ifdef JEMALLOC_SWAP + , + {NAME("swap"), CHILD(swap)} +#endif +}; +static const ctl_node_t super_root_node[] = { + {NAME(""), CHILD(root)} +}; + +#undef NAME +#undef CHILD +#undef CTL +#undef INDEX + +/******************************************************************************/ + +#ifdef JEMALLOC_STATS +static bool +ctl_arena_init(ctl_arena_stats_t *astats) +{ + + if (astats->bstats == NULL) { + astats->bstats = (malloc_bin_stats_t *)base_alloc(nbins * + sizeof(malloc_bin_stats_t)); + if (astats->bstats == NULL) + return (true); + } + if (astats->lstats == NULL) { + astats->lstats = (malloc_large_stats_t *)base_alloc(nlclasses * + sizeof(malloc_large_stats_t)); + if (astats->lstats == NULL) + return (true); + } + + return (false); +} +#endif + +static void +ctl_arena_clear(ctl_arena_stats_t *astats) +{ + + astats->pactive = 0; + astats->pdirty = 0; +#ifdef JEMALLOC_STATS + memset(&astats->astats, 0, sizeof(arena_stats_t)); + astats->allocated_small = 0; + astats->nmalloc_small = 0; + astats->ndalloc_small = 0; + astats->nrequests_small = 0; + memset(astats->bstats, 0, nbins * sizeof(malloc_bin_stats_t)); + memset(astats->lstats, 0, nlclasses * sizeof(malloc_large_stats_t)); +#endif +} + +#ifdef JEMALLOC_STATS +static void +ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena) +{ + unsigned i; + + arena_stats_merge(arena, &cstats->pactive, &cstats->pdirty, + &cstats->astats, cstats->bstats, cstats->lstats); + + for (i = 0; i < nbins; i++) { + cstats->allocated_small += cstats->bstats[i].allocated; + cstats->nmalloc_small += cstats->bstats[i].nmalloc; + cstats->ndalloc_small += cstats->bstats[i].ndalloc; + cstats->nrequests_small += cstats->bstats[i].nrequests; + } +} + +static void +ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats) +{ + unsigned i; + + sstats->pactive += astats->pactive; + sstats->pdirty += astats->pdirty; + + sstats->astats.mapped += astats->astats.mapped; + sstats->astats.npurge += astats->astats.npurge; + sstats->astats.nmadvise += astats->astats.nmadvise; + sstats->astats.purged += astats->astats.purged; + + sstats->allocated_small += astats->allocated_small; + sstats->nmalloc_small += astats->nmalloc_small; + sstats->ndalloc_small += astats->ndalloc_small; + sstats->nrequests_small += astats->nrequests_small; + + sstats->astats.allocated_large += astats->astats.allocated_large; + sstats->astats.nmalloc_large += astats->astats.nmalloc_large; + sstats->astats.ndalloc_large += astats->astats.ndalloc_large; + sstats->astats.nrequests_large += astats->astats.nrequests_large; + + for (i = 0; i < nlclasses; i++) { + sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc; + sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc; + sstats->lstats[i].nrequests += astats->lstats[i].nrequests; + sstats->lstats[i].highruns += astats->lstats[i].highruns; + sstats->lstats[i].curruns += astats->lstats[i].curruns; + } + + for (i = 0; i < nbins; i++) { + sstats->bstats[i].allocated += astats->bstats[i].allocated; + sstats->bstats[i].nmalloc += astats->bstats[i].nmalloc; + sstats->bstats[i].ndalloc += astats->bstats[i].ndalloc; + sstats->bstats[i].nrequests += astats->bstats[i].nrequests; +#ifdef JEMALLOC_TCACHE + sstats->bstats[i].nfills += astats->bstats[i].nfills; + sstats->bstats[i].nflushes += astats->bstats[i].nflushes; +#endif + sstats->bstats[i].nruns += astats->bstats[i].nruns; + sstats->bstats[i].reruns += astats->bstats[i].reruns; + sstats->bstats[i].highruns += astats->bstats[i].highruns; + sstats->bstats[i].curruns += astats->bstats[i].curruns; + } +} +#endif + +static void +ctl_arena_refresh(arena_t *arena, unsigned i) +{ + ctl_arena_stats_t *astats = &ctl_stats.arenas[i]; + ctl_arena_stats_t *sstats = &ctl_stats.arenas[narenas]; + + ctl_arena_clear(astats); + +#ifdef JEMALLOC_STATS + ctl_arena_stats_amerge(astats, arena); + /* Merge into sum stats as well. */ + ctl_arena_stats_smerge(sstats, astats); +#else + astats->pactive += arena->nactive; + astats->pdirty += arena->ndirty; + /* Merge into sum stats as well. */ + sstats->pactive += arena->nactive; + sstats->pdirty += arena->ndirty; +#endif +} + +static void +ctl_refresh(void) +{ + unsigned i; + arena_t *tarenas[narenas]; + +#ifdef JEMALLOC_STATS + malloc_mutex_lock(&chunks_mtx); + ctl_stats.chunks.current = stats_chunks.curchunks; + ctl_stats.chunks.total = stats_chunks.nchunks; + ctl_stats.chunks.high = stats_chunks.highchunks; + malloc_mutex_unlock(&chunks_mtx); + + malloc_mutex_lock(&huge_mtx); + ctl_stats.huge.allocated = huge_allocated; + ctl_stats.huge.nmalloc = huge_nmalloc; + ctl_stats.huge.ndalloc = huge_ndalloc; + malloc_mutex_unlock(&huge_mtx); +#endif + + /* + * Clear sum stats, since they will be merged into by + * ctl_arena_refresh(). + */ + ctl_arena_clear(&ctl_stats.arenas[narenas]); + + malloc_mutex_lock(&arenas_lock); + memcpy(tarenas, arenas, sizeof(arena_t *) * narenas); + malloc_mutex_unlock(&arenas_lock); + for (i = 0; i < narenas; i++) { + bool initialized = (tarenas[i] != NULL); + + ctl_stats.arenas[i].initialized = initialized; + if (initialized) + ctl_arena_refresh(tarenas[i], i); + } + +#ifdef JEMALLOC_STATS + ctl_stats.allocated = ctl_stats.arenas[narenas].allocated_small + + ctl_stats.arenas[narenas].astats.allocated_large + + ctl_stats.huge.allocated; + ctl_stats.active = (ctl_stats.arenas[narenas].pactive << PAGE_SHIFT) + + ctl_stats.huge.allocated; + ctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk); + +# ifdef JEMALLOC_SWAP + malloc_mutex_lock(&swap_mtx); + ctl_stats.swap_avail = swap_avail; + malloc_mutex_unlock(&swap_mtx); +# endif +#endif + + ctl_epoch++; +} + +static bool +ctl_init(void) +{ + + if (ctl_initialized == false) { +#ifdef JEMALLOC_STATS + unsigned i; +#endif + + /* + * Allocate space for one extra arena stats element, which + * contains summed stats across all arenas. + */ + ctl_stats.arenas = (ctl_arena_stats_t *)base_alloc( + (narenas + 1) * sizeof(ctl_arena_stats_t)); + if (ctl_stats.arenas == NULL) + return (true); + memset(ctl_stats.arenas, 0, (narenas + 1) * + sizeof(ctl_arena_stats_t)); + + /* + * Initialize all stats structures, regardless of whether they + * ever get used. Lazy initialization would allow errors to + * cause inconsistent state to be viewable by the application. + */ +#ifdef JEMALLOC_STATS + for (i = 0; i <= narenas; i++) { + if (ctl_arena_init(&ctl_stats.arenas[i])) + return (true); + } +#endif + ctl_stats.arenas[narenas].initialized = true; + + ctl_epoch = 0; + ctl_refresh(); + ctl_initialized = true; + } + + return (false); +} + +static int +ctl_lookup(const char *name, ctl_node_t const **nodesp, size_t *mibp, + size_t *depthp) +{ + int ret; + const char *elm, *tdot, *dot; + size_t elen, i, j; + const ctl_node_t *node; + + elm = name; + /* Equivalent to strchrnul(). */ + dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : strchr(elm, '\0'); + elen = (size_t)((uintptr_t)dot - (uintptr_t)elm); + if (elen == 0) { + ret = ENOENT; + goto RETURN; + } + node = super_root_node; + for (i = 0; i < *depthp; i++) { + assert(node->named); + assert(node->u.named.nchildren > 0); + if (node->u.named.children[0].named) { + const ctl_node_t *pnode = node; + + /* Children are named. */ + for (j = 0; j < node->u.named.nchildren; j++) { + const ctl_node_t *child = + &node->u.named.children[j]; + if (strlen(child->u.named.name) == elen + && strncmp(elm, child->u.named.name, + elen) == 0) { + node = child; + if (nodesp != NULL) + nodesp[i] = node; + mibp[i] = j; + break; + } + } + if (node == pnode) { + ret = ENOENT; + goto RETURN; + } + } else { + unsigned long index; + const ctl_node_t *inode; + + /* Children are indexed. */ + index = strtoul(elm, NULL, 10); + if (index == ULONG_MAX) { + ret = ENOENT; + goto RETURN; + } + + inode = &node->u.named.children[0]; + node = inode->u.indexed.index(mibp, *depthp, + index); + if (node == NULL) { + ret = ENOENT; + goto RETURN; + } + + if (nodesp != NULL) + nodesp[i] = node; + mibp[i] = (size_t)index; + } + + if (node->ctl != NULL) { + /* Terminal node. */ + if (*dot != '\0') { + /* + * The name contains more elements than are + * in this path through the tree. + */ + ret = ENOENT; + goto RETURN; + } + /* Complete lookup successful. */ + *depthp = i + 1; + break; + } + + /* Update elm. */ + if (*dot == '\0') { + /* No more elements. */ + ret = ENOENT; + goto RETURN; + } + elm = &dot[1]; + dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : + strchr(elm, '\0'); + elen = (size_t)((uintptr_t)dot - (uintptr_t)elm); + } + + ret = 0; +RETURN: + return (ret); +} + +int +ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp, + size_t newlen) +{ + int ret; + size_t depth; + ctl_node_t const *nodes[CTL_MAX_DEPTH]; + size_t mib[CTL_MAX_DEPTH]; + + malloc_mutex_lock(&ctl_mtx); + if (ctl_init()) { + ret = EAGAIN; + goto RETURN; + } + + depth = CTL_MAX_DEPTH; + ret = ctl_lookup(name, nodes, mib, &depth); + if (ret != 0) + goto RETURN; + + if (nodes[depth-1]->ctl == NULL) { + /* The name refers to a partial path through the ctl tree. */ + ret = ENOENT; + goto RETURN; + } + ret = nodes[depth-1]->ctl(mib, depth, oldp, oldlenp, newp, newlen); + +RETURN: + malloc_mutex_unlock(&ctl_mtx); + return(ret); +} + +int +ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp) +{ + int ret; + + malloc_mutex_lock(&ctl_mtx); + if (ctl_init()) { + ret = EAGAIN; + goto RETURN; + } + + ret = ctl_lookup(name, NULL, mibp, miblenp); + +RETURN: + malloc_mutex_unlock(&ctl_mtx); + return(ret); +} + +int +ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + const ctl_node_t *node; + size_t i; + + malloc_mutex_lock(&ctl_mtx); + if (ctl_init()) { + ret = EAGAIN; + goto RETURN; + } + + /* Iterate down the tree. */ + node = super_root_node; + for (i = 0; i < miblen; i++) { + if (node->u.named.children[0].named) { + /* Children are named. */ + if (node->u.named.nchildren <= mib[i]) { + ret = ENOENT; + goto RETURN; + } + node = &node->u.named.children[mib[i]]; + } else { + const ctl_node_t *inode; + + /* Indexed element. */ + inode = &node->u.named.children[0]; + node = inode->u.indexed.index(mib, miblen, mib[i]); + if (node == NULL) { + ret = ENOENT; + goto RETURN; + } + } + } + + /* Call the ctl function. */ + if (node->ctl == NULL) { + /* Partial MIB. */ + ret = ENOENT; + goto RETURN; + } + ret = node->ctl(mib, miblen, oldp, oldlenp, newp, newlen); + +RETURN: + malloc_mutex_unlock(&ctl_mtx); + return(ret); +} + +bool +ctl_boot(void) +{ + + if (malloc_mutex_init(&ctl_mtx)) + return (true); + + ctl_initialized = false; + + return (false); +} + +/******************************************************************************/ +/* *_ctl() functions. */ + +#define READONLY() do { \ + if (newp != NULL || newlen != 0) { \ + ret = EPERM; \ + goto RETURN; \ + } \ +} while (0) + +#define WRITEONLY() do { \ + if (oldp != NULL || oldlenp != NULL) { \ + ret = EPERM; \ + goto RETURN; \ + } \ +} while (0) + +#define VOID() do { \ + READONLY(); \ + WRITEONLY(); \ +} while (0) + +#define READ(v, t) do { \ + if (oldp != NULL && oldlenp != NULL) { \ + if (*oldlenp != sizeof(t)) { \ + size_t copylen = (sizeof(t) <= *oldlenp) \ + ? sizeof(t) : *oldlenp; \ + memcpy(oldp, (void *)&v, copylen); \ + ret = EINVAL; \ + goto RETURN; \ + } else \ + *(t *)oldp = v; \ + } \ +} while (0) + +#define WRITE(v, t) do { \ + if (newp != NULL) { \ + if (newlen != sizeof(t)) { \ + ret = EINVAL; \ + goto RETURN; \ + } \ + v = *(t *)newp; \ + } \ +} while (0) + +#define CTL_RO_GEN(n, v, t) \ +static int \ +n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ + void *newp, size_t newlen) \ +{ \ + int ret; \ + t oldval; \ + \ + READONLY(); \ + oldval = v; \ + READ(oldval, t); \ + \ + ret = 0; \ +RETURN: \ + return (ret); \ +} + +#define CTL_RO_TRUE_GEN(n) \ +static int \ +n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ + void *newp, size_t newlen) \ +{ \ + int ret; \ + bool oldval; \ + \ + READONLY(); \ + oldval = true; \ + READ(oldval, bool); \ + \ + ret = 0; \ +RETURN: \ + return (ret); \ +} + +#define CTL_RO_FALSE_GEN(n) \ +static int \ +n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ + void *newp, size_t newlen) \ +{ \ + int ret; \ + bool oldval; \ + \ + READONLY(); \ + oldval = false; \ + READ(oldval, bool); \ + \ + ret = 0; \ +RETURN: \ + return (ret); \ +} + +CTL_RO_GEN(version, JEMALLOC_VERSION, const char *) + +static int +epoch_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + uint64_t newval; + + newval = 0; + WRITE(newval, uint64_t); + if (newval != 0) + ctl_refresh(); + READ(ctl_epoch, uint64_t); + + ret = 0; +RETURN: + return (ret); +} + +#ifdef JEMALLOC_TCACHE +static int +tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + tcache_t *tcache; + + VOID(); + + tcache = tcache_tls; + if (tcache == NULL) { + ret = 0; + goto RETURN; + } + tcache_destroy(tcache); + tcache_tls = NULL; + + ret = 0; +RETURN: + return (ret); +} +#endif + +/******************************************************************************/ + +#ifdef JEMALLOC_DEBUG +CTL_RO_TRUE_GEN(config_debug) +#else +CTL_RO_FALSE_GEN(config_debug) +#endif + +#ifdef JEMALLOC_DSS +CTL_RO_TRUE_GEN(config_dss) +#else +CTL_RO_FALSE_GEN(config_dss) +#endif + +#ifdef JEMALLOC_DYNAMIC_PAGE_SHIFT +CTL_RO_TRUE_GEN(config_dynamic_page_shift) +#else +CTL_RO_FALSE_GEN(config_dynamic_page_shift) +#endif + +#ifdef JEMALLOC_FILL +CTL_RO_TRUE_GEN(config_fill) +#else +CTL_RO_FALSE_GEN(config_fill) +#endif + +#ifdef JEMALLOC_LAZY_LOCK +CTL_RO_TRUE_GEN(config_lazy_lock) +#else +CTL_RO_FALSE_GEN(config_lazy_lock) +#endif + +#ifdef JEMALLOC_PROF +CTL_RO_TRUE_GEN(config_prof) +#else +CTL_RO_FALSE_GEN(config_prof) +#endif + +#ifdef JEMALLOC_PROF_LIBGCC +CTL_RO_TRUE_GEN(config_prof_libgcc) +#else +CTL_RO_FALSE_GEN(config_prof_libgcc) +#endif + +#ifdef JEMALLOC_PROF_LIBUNWIND +CTL_RO_TRUE_GEN(config_prof_libunwind) +#else +CTL_RO_FALSE_GEN(config_prof_libunwind) +#endif + +#ifdef JEMALLOC_STATS +CTL_RO_TRUE_GEN(config_stats) +#else +CTL_RO_FALSE_GEN(config_stats) +#endif + +#ifdef JEMALLOC_SWAP +CTL_RO_TRUE_GEN(config_swap) +#else +CTL_RO_FALSE_GEN(config_swap) +#endif + +#ifdef JEMALLOC_SYSV +CTL_RO_TRUE_GEN(config_sysv) +#else +CTL_RO_FALSE_GEN(config_sysv) +#endif + +#ifdef JEMALLOC_TCACHE +CTL_RO_TRUE_GEN(config_tcache) +#else +CTL_RO_FALSE_GEN(config_tcache) +#endif + +#ifdef JEMALLOC_TINY +CTL_RO_TRUE_GEN(config_tiny) +#else +CTL_RO_FALSE_GEN(config_tiny) +#endif + +#ifdef JEMALLOC_TLS +CTL_RO_TRUE_GEN(config_tls) +#else +CTL_RO_FALSE_GEN(config_tls) +#endif + +#ifdef JEMALLOC_XMALLOC +CTL_RO_TRUE_GEN(config_xmalloc) +#else +CTL_RO_FALSE_GEN(config_xmalloc) +#endif + +/******************************************************************************/ + +CTL_RO_GEN(opt_abort, opt_abort, bool) +#ifdef JEMALLOC_FILL +CTL_RO_GEN(opt_junk, opt_junk, bool) +#endif +#ifdef JEMALLOC_SYSV +CTL_RO_GEN(opt_sysv, opt_sysv, bool) +#endif +#ifdef JEMALLOC_XMALLOC +CTL_RO_GEN(opt_xmalloc, opt_xmalloc, bool) +#endif +#ifdef JEMALLOC_ZERO +CTL_RO_GEN(opt_zero, opt_zero, bool) +#endif +#ifdef JEMALLOC_TCACHE +CTL_RO_GEN(opt_tcache, opt_tcache, bool) +CTL_RO_GEN(opt_lg_tcache_gc_sweep, opt_lg_tcache_gc_sweep, ssize_t) +#endif +#ifdef JEMALLOC_PROF +CTL_RO_GEN(opt_prof, opt_prof, bool) +CTL_RO_GEN(opt_prof_active, opt_prof_active, bool) +CTL_RO_GEN(opt_lg_prof_bt_max, opt_lg_prof_bt_max, size_t) +CTL_RO_GEN(opt_lg_prof_sample, opt_lg_prof_sample, size_t) +CTL_RO_GEN(opt_lg_prof_interval, opt_lg_prof_interval, ssize_t) +CTL_RO_GEN(opt_prof_udump, opt_prof_udump, bool) +CTL_RO_GEN(opt_prof_leak, opt_prof_leak, bool) +#endif +CTL_RO_GEN(opt_stats_print, opt_stats_print, bool) +CTL_RO_GEN(opt_lg_qspace_max, opt_lg_qspace_max, size_t) +CTL_RO_GEN(opt_lg_cspace_max, opt_lg_cspace_max, size_t) +CTL_RO_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t) +CTL_RO_GEN(opt_lg_chunk, opt_lg_chunk, size_t) +#ifdef JEMALLOC_SWAP +CTL_RO_GEN(opt_overcommit, opt_overcommit, bool) +#endif + +/******************************************************************************/ + +CTL_RO_GEN(arenas_bin_i_size, arenas[0]->bins[mib[2]].reg_size, size_t) +CTL_RO_GEN(arenas_bin_i_nregs, arenas[0]->bins[mib[2]].nregs, uint32_t) +CTL_RO_GEN(arenas_bin_i_run_size, arenas[0]->bins[mib[2]].run_size, size_t) +const ctl_node_t * +arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i) +{ + + if (i > nbins) + return (NULL); + return (super_arenas_bin_i_node); +} + +CTL_RO_GEN(arenas_lrun_i_size, ((mib[2]+1) << PAGE_SHIFT), size_t) +const ctl_node_t * +arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i) +{ + + if (i > nlclasses) + return (NULL); + return (super_arenas_lrun_i_node); +} + +CTL_RO_GEN(arenas_narenas, narenas, unsigned) + +static int +arenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + unsigned nread, i; + + READONLY(); + if (*oldlenp != narenas * sizeof(bool)) { + ret = EINVAL; + nread = (*oldlenp < narenas * sizeof(bool)) + ? (*oldlenp / sizeof(bool)) : narenas; + } else { + ret = 0; + nread = narenas; + } + + for (i = 0; i < nread; i++) + ((bool *)oldp)[i] = ctl_stats.arenas[i].initialized; + +RETURN: + return (ret); +} + +CTL_RO_GEN(arenas_quantum, QUANTUM, size_t) +CTL_RO_GEN(arenas_cacheline, CACHELINE, size_t) +CTL_RO_GEN(arenas_subpage, SUBPAGE, size_t) +CTL_RO_GEN(arenas_pagesize, PAGE_SIZE, size_t) +CTL_RO_GEN(arenas_chunksize, chunksize, size_t) +#ifdef JEMALLOC_TINY +CTL_RO_GEN(arenas_tspace_min, (1U << LG_TINY_MIN), size_t) +CTL_RO_GEN(arenas_tspace_max, (qspace_min >> 1), size_t) +#endif +CTL_RO_GEN(arenas_qspace_min, qspace_min, size_t) +CTL_RO_GEN(arenas_qspace_max, qspace_max, size_t) +CTL_RO_GEN(arenas_cspace_min, cspace_min, size_t) +CTL_RO_GEN(arenas_cspace_max, cspace_max, size_t) +CTL_RO_GEN(arenas_sspace_min, sspace_min, size_t) +CTL_RO_GEN(arenas_sspace_max, sspace_max, size_t) +#ifdef JEMALLOC_TCACHE +CTL_RO_GEN(arenas_tcache_max, tcache_maxclass, size_t) +#endif +CTL_RO_GEN(arenas_ntbins, ntbins, unsigned) +CTL_RO_GEN(arenas_nqbins, nqbins, unsigned) +CTL_RO_GEN(arenas_ncbins, ncbins, unsigned) +CTL_RO_GEN(arenas_nsbins, nsbins, unsigned) +CTL_RO_GEN(arenas_nbins, nbins, unsigned) +#ifdef JEMALLOC_TCACHE +CTL_RO_GEN(arenas_nhbins, nhbins, unsigned) +#endif +CTL_RO_GEN(arenas_nlruns, nlclasses, size_t) + +/******************************************************************************/ + +#ifdef JEMALLOC_PROF +static int +prof_active_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + bool oldval; + + oldval = opt_prof_active; + if (newp != NULL) { + /* + * The memory barriers will tend to make opt_prof_active + * propagate faster on systems with weak memory ordering. + */ + mb_write(); + WRITE(opt_prof_active, bool); + mb_write(); + } + READ(oldval, bool); + + ret = 0; +RETURN: + return (ret); +} + +static int +prof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + const char *filename = NULL; + + WRITEONLY(); + WRITE(filename, const char *); + + if (prof_mdump(filename)) { + ret = EFAULT; + goto RETURN; + } + + ret = 0; +RETURN: + return (ret); +} + +CTL_RO_GEN(prof_interval, prof_interval, uint64_t) +#endif + +/******************************************************************************/ + +#ifdef JEMALLOC_STATS +CTL_RO_GEN(stats_chunks_current, ctl_stats.chunks.current, size_t) +CTL_RO_GEN(stats_chunks_total, ctl_stats.chunks.total, uint64_t) +CTL_RO_GEN(stats_chunks_high, ctl_stats.chunks.high, size_t) +CTL_RO_GEN(stats_huge_allocated, huge_allocated, size_t) +CTL_RO_GEN(stats_huge_nmalloc, huge_nmalloc, uint64_t) +CTL_RO_GEN(stats_huge_ndalloc, huge_ndalloc, uint64_t) +CTL_RO_GEN(stats_arenas_i_small_allocated, + ctl_stats.arenas[mib[2]].allocated_small, size_t) +CTL_RO_GEN(stats_arenas_i_small_nmalloc, + ctl_stats.arenas[mib[2]].nmalloc_small, uint64_t) +CTL_RO_GEN(stats_arenas_i_small_ndalloc, + ctl_stats.arenas[mib[2]].ndalloc_small, uint64_t) +CTL_RO_GEN(stats_arenas_i_small_nrequests, + ctl_stats.arenas[mib[2]].nrequests_small, uint64_t) +CTL_RO_GEN(stats_arenas_i_large_allocated, + ctl_stats.arenas[mib[2]].astats.allocated_large, size_t) +CTL_RO_GEN(stats_arenas_i_large_nmalloc, + ctl_stats.arenas[mib[2]].astats.nmalloc_large, uint64_t) +CTL_RO_GEN(stats_arenas_i_large_ndalloc, + ctl_stats.arenas[mib[2]].astats.ndalloc_large, uint64_t) +CTL_RO_GEN(stats_arenas_i_large_nrequests, + ctl_stats.arenas[mib[2]].astats.nrequests_large, uint64_t) + +CTL_RO_GEN(stats_arenas_i_bins_j_allocated, + ctl_stats.arenas[mib[2]].bstats[mib[4]].allocated, size_t) +CTL_RO_GEN(stats_arenas_i_bins_j_nmalloc, + ctl_stats.arenas[mib[2]].bstats[mib[4]].nmalloc, uint64_t) +CTL_RO_GEN(stats_arenas_i_bins_j_ndalloc, + ctl_stats.arenas[mib[2]].bstats[mib[4]].ndalloc, uint64_t) +CTL_RO_GEN(stats_arenas_i_bins_j_nrequests, + ctl_stats.arenas[mib[2]].bstats[mib[4]].nrequests, uint64_t) +#ifdef JEMALLOC_TCACHE +CTL_RO_GEN(stats_arenas_i_bins_j_nfills, + ctl_stats.arenas[mib[2]].bstats[mib[4]].nfills, uint64_t) +CTL_RO_GEN(stats_arenas_i_bins_j_nflushes, + ctl_stats.arenas[mib[2]].bstats[mib[4]].nflushes, uint64_t) +#endif +CTL_RO_GEN(stats_arenas_i_bins_j_nruns, + ctl_stats.arenas[mib[2]].bstats[mib[4]].nruns, uint64_t) +CTL_RO_GEN(stats_arenas_i_bins_j_nreruns, + ctl_stats.arenas[mib[2]].bstats[mib[4]].reruns, uint64_t) +CTL_RO_GEN(stats_arenas_i_bins_j_highruns, + ctl_stats.arenas[mib[2]].bstats[mib[4]].highruns, size_t) +CTL_RO_GEN(stats_arenas_i_bins_j_curruns, + ctl_stats.arenas[mib[2]].bstats[mib[4]].curruns, size_t) + +const ctl_node_t * +stats_arenas_i_bins_j_index(const size_t *mib, size_t miblen, size_t j) +{ + + if (j > nbins) + return (NULL); + return (super_stats_arenas_i_bins_j_node); +} + +CTL_RO_GEN(stats_arenas_i_lruns_j_nmalloc, + ctl_stats.arenas[mib[2]].lstats[mib[4]].nmalloc, uint64_t) +CTL_RO_GEN(stats_arenas_i_lruns_j_ndalloc, + ctl_stats.arenas[mib[2]].lstats[mib[4]].ndalloc, uint64_t) +CTL_RO_GEN(stats_arenas_i_lruns_j_nrequests, + ctl_stats.arenas[mib[2]].lstats[mib[4]].nrequests, uint64_t) +CTL_RO_GEN(stats_arenas_i_lruns_j_curruns, + ctl_stats.arenas[mib[2]].lstats[mib[4]].curruns, size_t) +CTL_RO_GEN(stats_arenas_i_lruns_j_highruns, + ctl_stats.arenas[mib[2]].lstats[mib[4]].highruns, size_t) + +const ctl_node_t * +stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j) +{ + + if (j > nlclasses) + return (NULL); + return (super_stats_arenas_i_lruns_j_node); +} + +#endif +CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t) +CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t) +#ifdef JEMALLOC_STATS +CTL_RO_GEN(stats_arenas_i_mapped, ctl_stats.arenas[mib[2]].astats.mapped, + size_t) +CTL_RO_GEN(stats_arenas_i_npurge, ctl_stats.arenas[mib[2]].astats.npurge, + uint64_t) +CTL_RO_GEN(stats_arenas_i_nmadvise, ctl_stats.arenas[mib[2]].astats.nmadvise, + uint64_t) +CTL_RO_GEN(stats_arenas_i_purged, ctl_stats.arenas[mib[2]].astats.purged, + uint64_t) +#endif + +const ctl_node_t * +stats_arenas_i_index(const size_t *mib, size_t miblen, size_t i) +{ + + if (ctl_stats.arenas[i].initialized == false) + return (NULL); + return (super_stats_arenas_i_node); +} + +#ifdef JEMALLOC_STATS +CTL_RO_GEN(stats_allocated, ctl_stats.allocated, size_t) +CTL_RO_GEN(stats_active, ctl_stats.active, size_t) +CTL_RO_GEN(stats_mapped, ctl_stats.mapped, size_t) +#endif + +/******************************************************************************/ + +#ifdef JEMALLOC_SWAP +# ifdef JEMALLOC_STATS +CTL_RO_GEN(swap_avail, ctl_stats.swap_avail, size_t) +# endif + +static int +swap_prezeroed_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + + if (swap_enabled) { + READONLY(); + } else { + /* + * swap_prezeroed isn't actually used by the swap code until it + * is set during a successful chunk_swap_enabled() call. We + * use it here to store the value that we'll pass to + * chunk_swap_enable() in a swap.fds mallctl(). This is not + * very clean, but the obvious alternatives are even worse. + */ + WRITE(swap_prezeroed, bool); + } + + READ(swap_prezeroed, bool); + + ret = 0; +RETURN: + return (ret); +} + +CTL_RO_GEN(swap_nfds, swap_nfds, size_t) + +static int +swap_fds_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + + if (swap_enabled) { + READONLY(); + } else if (newp != NULL) { + size_t nfds = newlen / sizeof(int); + + { + int fds[nfds]; + + memcpy(fds, newp, nfds * sizeof(int)); + if (chunk_swap_enable(fds, nfds, swap_prezeroed)) { + ret = EFAULT; + goto RETURN; + } + } + } + + if (oldp != NULL && oldlenp != NULL) { + if (*oldlenp != swap_nfds * sizeof(int)) { + size_t copylen = (swap_nfds * sizeof(int) <= *oldlenp) + ? swap_nfds * sizeof(int) : *oldlenp; + + memcpy(oldp, swap_fds, copylen); + ret = EINVAL; + goto RETURN; + } else + memcpy(oldp, swap_fds, *oldlenp); + } + + ret = 0; +RETURN: + return (ret); +} +#endif diff --git a/externals/jemalloc/delme b/externals/jemalloc/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/jemalloc/delme +++ /dev/null diff --git a/externals/jemalloc/extent.c b/externals/jemalloc/extent.c new file mode 100644 index 00000000000..3c04d3aa5d1 --- /dev/null +++ b/externals/jemalloc/extent.c @@ -0,0 +1,41 @@ +#define JEMALLOC_EXTENT_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ + +#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS)) +static inline int +extent_szad_comp(extent_node_t *a, extent_node_t *b) +{ + int ret; + size_t a_size = a->size; + size_t b_size = b->size; + + ret = (a_size > b_size) - (a_size < b_size); + if (ret == 0) { + uintptr_t a_addr = (uintptr_t)a->addr; + uintptr_t b_addr = (uintptr_t)b->addr; + + ret = (a_addr > b_addr) - (a_addr < b_addr); + } + + return (ret); +} + +/* Generate red-black tree functions. */ +rb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, link_szad, + extent_szad_comp) +#endif + +static inline int +extent_ad_comp(extent_node_t *a, extent_node_t *b) +{ + uintptr_t a_addr = (uintptr_t)a->addr; + uintptr_t b_addr = (uintptr_t)b->addr; + + return ((a_addr > b_addr) - (a_addr < b_addr)); +} + +/* Generate red-black tree functions. */ +rb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, + extent_ad_comp) diff --git a/externals/jemalloc/hash.c b/externals/jemalloc/hash.c new file mode 100644 index 00000000000..6a13d7a03c0 --- /dev/null +++ b/externals/jemalloc/hash.c @@ -0,0 +1,2 @@ +#define HASH_C_ +#include "jemalloc/internal/jemalloc_internal.h" diff --git a/externals/jemalloc/huge.c b/externals/jemalloc/huge.c new file mode 100644 index 00000000000..d35aa5cdd00 --- /dev/null +++ b/externals/jemalloc/huge.c @@ -0,0 +1,298 @@ +#define JEMALLOC_HUGE_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +#ifdef JEMALLOC_STATS +uint64_t huge_nmalloc; +uint64_t huge_ndalloc; +size_t huge_allocated; +#endif + +malloc_mutex_t huge_mtx; + +/******************************************************************************/ + +/* Tree of chunks that are stand-alone huge allocations. */ +static extent_tree_t huge; + +void * +huge_malloc(size_t size, bool zero) +{ + void *ret; + size_t csize; + extent_node_t *node; + + /* Allocate one or more contiguous chunks for this request. */ + + csize = CHUNK_CEILING(size); + if (csize == 0) { + /* size is large enough to cause size_t wrap-around. */ + return (NULL); + } + + /* Allocate an extent node with which to track the chunk. */ + node = base_node_alloc(); + if (node == NULL) + return (NULL); + + ret = chunk_alloc(csize, &zero); + if (ret == NULL) { + base_node_dealloc(node); + return (NULL); + } + + /* Insert node into huge. */ + node->addr = ret; + node->size = csize; + + malloc_mutex_lock(&huge_mtx); + extent_tree_ad_insert(&huge, node); +#ifdef JEMALLOC_STATS + huge_nmalloc++; + huge_allocated += csize; +#endif + malloc_mutex_unlock(&huge_mtx); + +#ifdef JEMALLOC_FILL + if (zero == false) { + if (opt_junk) + memset(ret, 0xa5, csize); + else if (opt_zero) + memset(ret, 0, csize); + } +#endif + + return (ret); +} + +/* Only handles large allocations that require more than chunk alignment. */ +void * +huge_palloc(size_t alignment, size_t size) +{ + void *ret; + size_t alloc_size, chunk_size, offset; + extent_node_t *node; + bool zero; + + /* + * This allocation requires alignment that is even larger than chunk + * alignment. This means that huge_malloc() isn't good enough. + * + * Allocate almost twice as many chunks as are demanded by the size or + * alignment, in order to assure the alignment can be achieved, then + * unmap leading and trailing chunks. + */ + assert(alignment >= chunksize); + + chunk_size = CHUNK_CEILING(size); + + if (size >= alignment) + alloc_size = chunk_size + alignment - chunksize; + else + alloc_size = (alignment << 1) - chunksize; + + /* Allocate an extent node with which to track the chunk. */ + node = base_node_alloc(); + if (node == NULL) + return (NULL); + + zero = false; + ret = chunk_alloc(alloc_size, &zero); + if (ret == NULL) { + base_node_dealloc(node); + return (NULL); + } + + offset = (uintptr_t)ret & (alignment - 1); + assert((offset & chunksize_mask) == 0); + assert(offset < alloc_size); + if (offset == 0) { + /* Trim trailing space. */ + chunk_dealloc((void *)((uintptr_t)ret + chunk_size), alloc_size + - chunk_size); + } else { + size_t trailsize; + + /* Trim leading space. */ + chunk_dealloc(ret, alignment - offset); + + ret = (void *)((uintptr_t)ret + (alignment - offset)); + + trailsize = alloc_size - (alignment - offset) - chunk_size; + if (trailsize != 0) { + /* Trim trailing space. */ + assert(trailsize < alloc_size); + chunk_dealloc((void *)((uintptr_t)ret + chunk_size), + trailsize); + } + } + + /* Insert node into huge. */ + node->addr = ret; + node->size = chunk_size; + + malloc_mutex_lock(&huge_mtx); + extent_tree_ad_insert(&huge, node); +#ifdef JEMALLOC_STATS + huge_nmalloc++; + huge_allocated += chunk_size; +#endif + malloc_mutex_unlock(&huge_mtx); + +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ret, 0xa5, chunk_size); + else if (opt_zero) + memset(ret, 0, chunk_size); +#endif + + return (ret); +} + +void * +huge_ralloc(void *ptr, size_t size, size_t oldsize) +{ + void *ret; + size_t copysize; + + /* Avoid moving the allocation if the size class would not change. */ + if (oldsize > arena_maxclass && + CHUNK_CEILING(size) == CHUNK_CEILING(oldsize)) { +#ifdef JEMALLOC_FILL + if (opt_junk && size < oldsize) { + memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize + - size); + } else if (opt_zero && size > oldsize) { + memset((void *)((uintptr_t)ptr + oldsize), 0, size + - oldsize); + } +#endif + return (ptr); + } + + /* + * If we get here, then size and oldsize are different enough that we + * need to use a different size class. In that case, fall back to + * allocating new space and copying. + */ + ret = huge_malloc(size, false); + if (ret == NULL) + return (NULL); + + copysize = (size < oldsize) ? size : oldsize; + memcpy(ret, ptr, copysize); + idalloc(ptr); + return (ret); +} + +void +huge_dalloc(void *ptr) +{ + extent_node_t *node, key; + + malloc_mutex_lock(&huge_mtx); + + /* Extract from tree of huge allocations. */ + key.addr = ptr; + node = extent_tree_ad_search(&huge, &key); + assert(node != NULL); + assert(node->addr == ptr); + extent_tree_ad_remove(&huge, node); + +#ifdef JEMALLOC_STATS + huge_ndalloc++; + huge_allocated -= node->size; +#endif + + malloc_mutex_unlock(&huge_mtx); + + /* Unmap chunk. */ +#ifdef JEMALLOC_FILL +#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS)) + if (opt_junk) + memset(node->addr, 0x5a, node->size); +#endif +#endif + chunk_dealloc(node->addr, node->size); + + base_node_dealloc(node); +} + +size_t +huge_salloc(const void *ptr) +{ + size_t ret; + extent_node_t *node, key; + + malloc_mutex_lock(&huge_mtx); + + /* Extract from tree of huge allocations. */ + key.addr = __DECONST(void *, ptr); + node = extent_tree_ad_search(&huge, &key); + assert(node != NULL); + + ret = node->size; + + malloc_mutex_unlock(&huge_mtx); + + return (ret); +} + +#ifdef JEMALLOC_PROF +prof_thr_cnt_t * +huge_prof_cnt_get(const void *ptr) +{ + prof_thr_cnt_t *ret; + extent_node_t *node, key; + + malloc_mutex_lock(&huge_mtx); + + /* Extract from tree of huge allocations. */ + key.addr = __DECONST(void *, ptr); + node = extent_tree_ad_search(&huge, &key); + assert(node != NULL); + + ret = node->prof_cnt; + + malloc_mutex_unlock(&huge_mtx); + + return (ret); +} + +void +huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt) +{ + extent_node_t *node, key; + + malloc_mutex_lock(&huge_mtx); + + /* Extract from tree of huge allocations. */ + key.addr = __DECONST(void *, ptr); + node = extent_tree_ad_search(&huge, &key); + assert(node != NULL); + + node->prof_cnt = cnt; + + malloc_mutex_unlock(&huge_mtx); +} +#endif + +bool +huge_boot(void) +{ + + /* Initialize chunks data. */ + if (malloc_mutex_init(&huge_mtx)) + return (true); + extent_tree_ad_new(&huge); + +#ifdef JEMALLOC_STATS + huge_nmalloc = 0; + huge_ndalloc = 0; + huge_allocated = 0; +#endif + + return (false); +} diff --git a/externals/jemalloc/include/internal/arena.h b/externals/jemalloc/include/internal/arena.h new file mode 100644 index 00000000000..bb4ce2a54f7 --- /dev/null +++ b/externals/jemalloc/include/internal/arena.h @@ -0,0 +1,537 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +/* + * Subpages are an artificially designated partitioning of pages. Their only + * purpose is to support subpage-spaced size classes. + * + * There must be at least 4 subpages per page, due to the way size classes are + * handled. + */ +#define LG_SUBPAGE 8 +#define SUBPAGE ((size_t)(1U << LG_SUBPAGE)) +#define SUBPAGE_MASK (SUBPAGE - 1) + +/* Return the smallest subpage multiple that is >= s. */ +#define SUBPAGE_CEILING(s) \ + (((s) + SUBPAGE_MASK) & ~SUBPAGE_MASK) + +#ifdef JEMALLOC_TINY + /* Smallest size class to support. */ +# define LG_TINY_MIN LG_SIZEOF_PTR +#endif + +/* + * Maximum size class that is a multiple of the quantum, but not (necessarily) + * a power of 2. Above this size, allocations are rounded up to the nearest + * power of 2. + */ +#define LG_QSPACE_MAX_DEFAULT 7 + +/* + * Maximum size class that is a multiple of the cacheline, but not (necessarily) + * a power of 2. Above this size, allocations are rounded up to the nearest + * power of 2. + */ +#define LG_CSPACE_MAX_DEFAULT 9 + +/* + * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized + * as small as possible such that this setting is still honored, without + * violating other constraints. The goal is to make runs as small as possible + * without exceeding a per run external fragmentation threshold. + * + * We use binary fixed point math for overhead computations, where the binary + * point is implicitly RUN_BFP bits to the left. + * + * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be + * honored for some/all object sizes, since there is one bit of header overhead + * per object (plus a constant). This constraint is relaxed (ignored) for runs + * that are so small that the per-region overhead is greater than: + * + * (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP)) + */ +#define RUN_BFP 12 +/* \/ Implicit binary fixed point. */ +#define RUN_MAX_OVRHD 0x0000003dU +#define RUN_MAX_OVRHD_RELAX 0x00001800U + +/* + * The minimum ratio of active:dirty pages per arena is computed as: + * + * (nactive >> opt_lg_dirty_mult) >= ndirty + * + * So, supposing that opt_lg_dirty_mult is 5, there can be no less than 32 + * times as many active pages as dirty pages. + */ +#define LG_DIRTY_MULT_DEFAULT 5 + +typedef struct arena_chunk_map_s arena_chunk_map_t; +typedef struct arena_chunk_s arena_chunk_t; +typedef struct arena_run_s arena_run_t; +typedef struct arena_bin_s arena_bin_t; +typedef struct arena_s arena_t; + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +/* Each element of the chunk map corresponds to one page within the chunk. */ +struct arena_chunk_map_s { + union { + /* + * Linkage for run trees. There are two disjoint uses: + * + * 1) arena_t's runs_avail_{clean,dirty} trees. + * 2) arena_run_t conceptually uses this linkage for in-use + * non-full runs, rather than directly embedding linkage. + */ + rb_node(arena_chunk_map_t) rb_link; + /* + * List of runs currently in purgatory. arena_chunk_purge() + * temporarily allocates runs that contain dirty pages while + * purging, so that other threads cannot use the runs while the + * purging thread is operating without the arena lock held. + */ + ql_elm(arena_chunk_map_t) ql_link; + } u; + +#ifdef JEMALLOC_PROF + /* Profile counters, used for large object runs. */ + prof_thr_cnt_t *prof_cnt; +#endif + + /* + * Run address (or size) and various flags are stored together. The bit + * layout looks like (assuming 32-bit system): + * + * ???????? ???????? ????---- ----dzla + * + * ? : Unallocated: Run address for first/last pages, unset for internal + * pages. + * Small: Run page offset. + * Large: Run size for first page, unset for trailing pages. + * - : Unused. + * d : dirty? + * z : zeroed? + * l : large? + * a : allocated? + * + * Following are example bit patterns for the three types of runs. + * + * p : run page offset + * s : run size + * c : size class (used only if prof_promote is true) + * x : don't care + * - : 0 + * + : 1 + * [DZLA] : bit set + * [dzla] : bit unset + * + * Unallocated (clean): + * ssssssss ssssssss ssss---- ----dz-- + * xxxxxxxx xxxxxxxx xxxx---- -----Zxx + * ssssssss ssssssss ssss---- ----dZ-- + * + * Unallocated (dirty): + * ssssssss ssssssss ssss---- ----D--- + * xxxxxxxx xxxxxxxx xxxx---- ----xxxx + * ssssssss ssssssss ssss---- ----D--- + * + * Small: + * pppppppp pppppppp pppp---- ----d--a + * pppppppp pppppppp pppp---- -------a + * pppppppp pppppppp pppp---- ----d--a + * + * Large: + * ssssssss ssssssss ssss++++ ++++D-la + * xxxxxxxx xxxxxxxx xxxx---- ----xxxx + * -------- -------- -------- ----D-la + * + * Large (sampled, size <= PAGE_SIZE): + * ssssssss ssssssss sssscccc ccccD-la + * + * Large (not sampled, size == PAGE_SIZE): + * ssssssss ssssssss ssss++++ ++++D-la + */ + size_t bits; +#ifdef JEMALLOC_PROF +#define CHUNK_MAP_CLASS_SHIFT 4 +#define CHUNK_MAP_CLASS_MASK ((size_t)0xff0U) +#endif +#define CHUNK_MAP_FLAGS_MASK ((size_t)0xfU) +#define CHUNK_MAP_DIRTY ((size_t)0x8U) +#define CHUNK_MAP_ZEROED ((size_t)0x4U) +#define CHUNK_MAP_LARGE ((size_t)0x2U) +#define CHUNK_MAP_ALLOCATED ((size_t)0x1U) +#define CHUNK_MAP_KEY CHUNK_MAP_ALLOCATED +}; +typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t; +typedef rb_tree(arena_chunk_map_t) arena_run_tree_t; + +/* Arena chunk header. */ +struct arena_chunk_s { + /* Arena that owns the chunk. */ + arena_t *arena; + + /* Linkage for the arena's chunks_dirty list. */ + ql_elm(arena_chunk_t) link_dirty; + + /* + * True if the chunk is currently in the chunks_dirty list, due to + * having at some point contained one or more dirty pages. Removal + * from chunks_dirty is lazy, so (dirtied && ndirty == 0) is possible. + */ + bool dirtied; + + /* Number of dirty pages. */ + size_t ndirty; + + /* Map of pages within chunk that keeps track of free/large/small. */ + arena_chunk_map_t map[1]; /* Dynamically sized. */ +}; +typedef rb_tree(arena_chunk_t) arena_chunk_tree_t; + +struct arena_run_s { +#ifdef JEMALLOC_DEBUG + uint32_t magic; +# define ARENA_RUN_MAGIC 0x384adf93 +#endif + + /* Bin this run is associated with. */ + arena_bin_t *bin; + + /* Stack of available freed regions, or NULL. */ + void *avail; + + /* Next region that has never been allocated, or run boundary. */ + void *next; + + /* Number of free regions in run. */ + unsigned nfree; +}; + +struct arena_bin_s { + /* + * All operations on runcur, runs, and stats require that lock be + * locked. Run allocation/deallocation are protected by the arena lock, + * which may be acquired while holding one or more bin locks, but not + * vise versa. + */ + malloc_mutex_t lock; + + /* + * Current run being used to service allocations of this bin's size + * class. + */ + arena_run_t *runcur; + + /* + * Tree of non-full runs. This tree is used when looking for an + * existing run when runcur is no longer usable. We choose the + * non-full run that is lowest in memory; this policy tends to keep + * objects packed well, and it can also help reduce the number of + * almost-empty chunks. + */ + arena_run_tree_t runs; + + /* Size of regions in a run for this bin's size class. */ + size_t reg_size; + + /* Total size of a run for this bin's size class. */ + size_t run_size; + + /* Total number of regions in a run for this bin's size class. */ + uint32_t nregs; + +#ifdef JEMALLOC_PROF + /* + * Offset of first (prof_cnt_t *) in a run header for this bin's size + * class, or 0 if (opt_prof == false). + */ + uint32_t cnt0_offset; +#endif + + /* Offset of first region in a run for this bin's size class. */ + uint32_t reg0_offset; + +#ifdef JEMALLOC_STATS + /* Bin statistics. */ + malloc_bin_stats_t stats; +#endif +}; + +struct arena_s { +#ifdef JEMALLOC_DEBUG + uint32_t magic; +# define ARENA_MAGIC 0x947d3d24 +#endif + + /* This arena's index within the arenas array. */ + unsigned ind; + + /* + * All non-bin-related operations on this arena require that lock be + * locked. + */ + malloc_mutex_t lock; + +#ifdef JEMALLOC_STATS + arena_stats_t stats; +# ifdef JEMALLOC_TCACHE + /* + * List of tcaches for extant threads associated with this arena. + * Stats from these are merged incrementally, and at exit. + */ + ql_head(tcache_t) tcache_ql; +# endif +#endif + +#ifdef JEMALLOC_PROF + uint64_t prof_accumbytes; +#endif + + /* List of dirty-page-containing chunks this arena manages. */ + ql_head(arena_chunk_t) chunks_dirty; + + /* + * In order to avoid rapid chunk allocation/deallocation when an arena + * oscillates right on the cusp of needing a new chunk, cache the most + * recently freed chunk. The spare is left in the arena's chunk trees + * until it is deleted. + * + * There is one spare chunk per arena, rather than one spare total, in + * order to avoid interactions between multiple threads that could make + * a single spare inadequate. + */ + arena_chunk_t *spare; + + /* Number of pages in active runs. */ + size_t nactive; + + /* + * Current count of pages within unused runs that are potentially + * dirty, and for which madvise(... MADV_DONTNEED) has not been called. + * By tracking this, we can institute a limit on how much dirty unused + * memory is mapped for each arena. + */ + size_t ndirty; + + /* + * Approximate number of pages being purged. It is possible for + * multiple threads to purge dirty pages concurrently, and they use + * npurgatory to indicate the total number of pages all threads are + * attempting to purge. + */ + size_t npurgatory; + + /* + * Size/address-ordered trees of this arena's available runs. The trees + * are used for first-best-fit run allocation. The dirty tree contains + * runs with dirty pages (i.e. very likely to have been touched and + * therefore have associated physical pages), whereas the clean tree + * contains runs with pages that either have no associated physical + * pages, or have pages that the kernel may recycle at any time due to + * previous madvise(2) calls. The dirty tree is used in preference to + * the clean tree for allocations, because using dirty pages reduces + * the amount of dirty purging necessary to keep the active:dirty page + * ratio below the purge threshold. + */ + arena_avail_tree_t runs_avail_clean; + arena_avail_tree_t runs_avail_dirty; + + /* + * bins is used to store trees of free regions of the following sizes, + * assuming a 16-byte quantum, 4 KiB page size, and default + * JEMALLOC_OPTIONS. + * + * bins[i] | size | + * --------+--------+ + * 0 | 2 | + * 1 | 4 | + * 2 | 8 | + * --------+--------+ + * 3 | 16 | + * 4 | 32 | + * 5 | 48 | + * : : + * 8 | 96 | + * 9 | 112 | + * 10 | 128 | + * --------+--------+ + * 11 | 192 | + * 12 | 256 | + * 13 | 320 | + * 14 | 384 | + * 15 | 448 | + * 16 | 512 | + * --------+--------+ + * 17 | 768 | + * 18 | 1024 | + * 19 | 1280 | + * : : + * 27 | 3328 | + * 28 | 3584 | + * 29 | 3840 | + * --------+--------+ + * 30 | 4 KiB | + * 31 | 6 KiB | + * 33 | 8 KiB | + * : : + * 43 | 28 KiB | + * 44 | 30 KiB | + * 45 | 32 KiB | + * --------+--------+ + */ + arena_bin_t bins[1]; /* Dynamically sized. */ +}; + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern size_t opt_lg_qspace_max; +extern size_t opt_lg_cspace_max; +extern ssize_t opt_lg_dirty_mult; +extern uint8_t const *small_size2bin; + +/* Various bin-related settings. */ +#ifdef JEMALLOC_TINY /* Number of (2^n)-spaced tiny bins. */ +# define ntbins ((unsigned)(LG_QUANTUM - LG_TINY_MIN)) +#else +# define ntbins 0 +#endif +extern unsigned nqbins; /* Number of quantum-spaced bins. */ +extern unsigned ncbins; /* Number of cacheline-spaced bins. */ +extern unsigned nsbins; /* Number of subpage-spaced bins. */ +extern unsigned nbins; +#ifdef JEMALLOC_TINY +# define tspace_max ((size_t)(QUANTUM >> 1)) +#endif +#define qspace_min QUANTUM +extern size_t qspace_max; +extern size_t cspace_min; +extern size_t cspace_max; +extern size_t sspace_min; +extern size_t sspace_max; +#define small_maxclass sspace_max + +#define nlclasses (chunk_npages - arena_chunk_header_npages) + +#ifdef JEMALLOC_TCACHE +void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, + size_t binind +# ifdef JEMALLOC_PROF + , uint64_t prof_accumbytes +# endif + ); +#endif +#ifdef JEMALLOC_PROF +void arena_prof_accum(arena_t *arena, uint64_t accumbytes); +#endif +void *arena_malloc_small(arena_t *arena, size_t size, bool zero); +void *arena_malloc_large(arena_t *arena, size_t size, bool zero); +void *arena_malloc(size_t size, bool zero); +void *arena_palloc(arena_t *arena, size_t alignment, size_t size, + size_t alloc_size); +size_t arena_salloc(const void *ptr); +#ifdef JEMALLOC_PROF +void arena_prof_promoted(const void *ptr, size_t size); +size_t arena_salloc_demote(const void *ptr); +prof_thr_cnt_t *arena_prof_cnt_get(const void *ptr); +void arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt); +#endif +void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, + arena_chunk_map_t *mapelm); +void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr); +#ifdef JEMALLOC_STATS +void arena_stats_merge(arena_t *arena, size_t *nactive, size_t *ndirty, + arena_stats_t *astats, malloc_bin_stats_t *bstats, + malloc_large_stats_t *lstats); +#endif +void *arena_ralloc(void *ptr, size_t size, size_t oldsize); +bool arena_new(arena_t *arena, unsigned ind); +bool arena_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#ifndef JEMALLOC_ENABLE_INLINE +void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_)) +JEMALLOC_INLINE void +arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr) +{ + size_t pageind; + arena_chunk_map_t *mapelm; + + assert(arena != NULL); + assert(arena->magic == ARENA_MAGIC); + assert(chunk->arena == arena); + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapelm = &chunk->map[pageind]; + assert((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0); + if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) { + /* Small allocation. */ +#ifdef JEMALLOC_TCACHE + tcache_t *tcache; + + if ((tcache = tcache_get()) != NULL) + tcache_dalloc_small(tcache, ptr); + else { +#endif + arena_run_t *run; + arena_bin_t *bin; + + run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - (mapelm->bits >> + PAGE_SHIFT)) << PAGE_SHIFT)); + assert(run->magic == ARENA_RUN_MAGIC); + assert(((uintptr_t)ptr - ((uintptr_t)run + + (uintptr_t)run->bin->reg0_offset)) % + run->bin->reg_size == 0); + bin = run->bin; + malloc_mutex_lock(&bin->lock); + arena_dalloc_bin(arena, chunk, ptr, mapelm); + malloc_mutex_unlock(&bin->lock); +#ifdef JEMALLOC_TCACHE + } +#endif + } else { +#ifdef JEMALLOC_TCACHE + size_t size = mapelm->bits & ~PAGE_MASK; + + assert(((uintptr_t)ptr & PAGE_MASK) == 0); + if (size <= tcache_maxclass) { + tcache_t *tcache; + + if ((tcache = tcache_get()) != NULL) + tcache_dalloc_large(tcache, ptr, size); + else { + malloc_mutex_lock(&arena->lock); + arena_dalloc_large(arena, chunk, ptr); + malloc_mutex_unlock(&arena->lock); + } + } else { + malloc_mutex_lock(&arena->lock); + arena_dalloc_large(arena, chunk, ptr); + malloc_mutex_unlock(&arena->lock); + } +#else + assert(((uintptr_t)ptr & PAGE_MASK) == 0); + malloc_mutex_lock(&arena->lock); + arena_dalloc_large(arena, chunk, ptr); + malloc_mutex_unlock(&arena->lock); +#endif + } +} +#endif + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/base.h b/externals/jemalloc/include/internal/base.h new file mode 100644 index 00000000000..e353f309bd2 --- /dev/null +++ b/externals/jemalloc/include/internal/base.h @@ -0,0 +1,24 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern malloc_mutex_t base_mtx; + +void *base_alloc(size_t size); +extent_node_t *base_node_alloc(void); +void base_node_dealloc(extent_node_t *node); +bool base_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/chunk.h b/externals/jemalloc/include/internal/chunk.h new file mode 100644 index 00000000000..1f6abf782f1 --- /dev/null +++ b/externals/jemalloc/include/internal/chunk.h @@ -0,0 +1,61 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +/* + * Size and alignment of memory chunks that are allocated by the OS's virtual + * memory system. + */ +#define LG_CHUNK_DEFAULT 22 + +/* Return the chunk address for allocation address a. */ +#define CHUNK_ADDR2BASE(a) \ + ((void *)((uintptr_t)(a) & ~chunksize_mask)) + +/* Return the chunk offset of address a. */ +#define CHUNK_ADDR2OFFSET(a) \ + ((size_t)((uintptr_t)(a) & chunksize_mask)) + +/* Return the smallest chunk multiple that is >= s. */ +#define CHUNK_CEILING(s) \ + (((s) + chunksize_mask) & ~chunksize_mask) + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern size_t opt_lg_chunk; +#ifdef JEMALLOC_SWAP +extern bool opt_overcommit; +#endif + +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) +/* Protects stats_chunks; currently not used for any other purpose. */ +extern malloc_mutex_t chunks_mtx; +/* Chunk statistics. */ +extern chunk_stats_t stats_chunks; +#endif + +extern size_t chunksize; +extern size_t chunksize_mask; /* (chunksize - 1). */ +extern size_t chunk_npages; +extern size_t arena_chunk_header_npages; +extern size_t arena_maxclass; /* Max size class for arenas. */ + +void *chunk_alloc(size_t size, bool *zero); +void chunk_dealloc(void *chunk, size_t size); +bool chunk_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ + +#include "jemalloc/internal/chunk_swap.h" +#include "jemalloc/internal/chunk_dss.h" +#include "jemalloc/internal/chunk_mmap.h" diff --git a/externals/jemalloc/include/internal/chunk_dss.h b/externals/jemalloc/include/internal/chunk_dss.h new file mode 100644 index 00000000000..6be4ad1f212 --- /dev/null +++ b/externals/jemalloc/include/internal/chunk_dss.h @@ -0,0 +1,29 @@ +#ifdef JEMALLOC_DSS +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +/* + * Protects sbrk() calls. This avoids malloc races among threads, though it + * does not protect against races with threads that call sbrk() directly. + */ +extern malloc_mutex_t dss_mtx; + +void *chunk_alloc_dss(size_t size, bool *zero); +bool chunk_dealloc_dss(void *chunk, size_t size); +bool chunk_dss_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ +#endif /* JEMALLOC_DSS */ diff --git a/externals/jemalloc/include/internal/chunk_mmap.h b/externals/jemalloc/include/internal/chunk_mmap.h new file mode 100644 index 00000000000..8fb90b77c9b --- /dev/null +++ b/externals/jemalloc/include/internal/chunk_mmap.h @@ -0,0 +1,20 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +void *chunk_alloc_mmap(size_t size); +void chunk_dealloc_mmap(void *chunk, size_t size); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/chunk_swap.h b/externals/jemalloc/include/internal/chunk_swap.h new file mode 100644 index 00000000000..d50cb197449 --- /dev/null +++ b/externals/jemalloc/include/internal/chunk_swap.h @@ -0,0 +1,33 @@ +#ifdef JEMALLOC_SWAP +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern malloc_mutex_t swap_mtx; +extern bool swap_enabled; +extern bool swap_prezeroed; +extern size_t swap_nfds; +extern int *swap_fds; +#ifdef JEMALLOC_STATS +extern size_t swap_avail; +#endif + +void *chunk_alloc_swap(size_t size, bool *zero); +bool chunk_dealloc_swap(void *chunk, size_t size); +bool chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed); +bool chunk_swap_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ +#endif /* JEMALLOC_SWAP */ diff --git a/externals/jemalloc/include/internal/ckh.h b/externals/jemalloc/include/internal/ckh.h new file mode 100644 index 00000000000..c39ea5c75ef --- /dev/null +++ b/externals/jemalloc/include/internal/ckh.h @@ -0,0 +1,95 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +typedef struct ckh_s ckh_t; +typedef struct ckhc_s ckhc_t; + +/* Typedefs to allow easy function pointer passing. */ +typedef void ckh_hash_t (const void *, unsigned, size_t *, size_t *); +typedef bool ckh_keycomp_t (const void *, const void *); + +/* Maintain counters used to get an idea of performance. */ +/* #define CKH_COUNT */ +/* Print counter values in ckh_delete() (requires CKH_COUNT). */ +/* #define CKH_VERBOSE */ + +/* + * There are 2^LG_CKH_BUCKET_CELLS cells in each hash table bucket. Try to fit + * one bucket per L1 cache line. + */ +#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1) + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +/* Hash table cell. */ +struct ckhc_s { + const void *key; + const void *data; +}; + +struct ckh_s { +#ifdef JEMALLOC_DEBUG +#define CKH_MAGIG 0x3af2489d + uint32_t magic; +#endif + +#ifdef CKH_COUNT + /* Counters used to get an idea of performance. */ + uint64_t ngrows; + uint64_t nshrinks; + uint64_t nshrinkfails; + uint64_t ninserts; + uint64_t nrelocs; +#endif + + /* Used for pseudo-random number generation. */ +#define CKH_A 12345 +#define CKH_C 12347 + uint32_t prn_state; + + /* Total number of items. */ + size_t count; + + /* + * Minimum and current number of hash table buckets. There are + * 2^LG_CKH_BUCKET_CELLS cells per bucket. + */ + unsigned lg_minbuckets; + unsigned lg_curbuckets; + + /* Hash and comparison functions. */ + ckh_hash_t *hash; + ckh_keycomp_t *keycomp; + + /* Hash table with 2^lg_curbuckets buckets. */ + ckhc_t *tab; +}; + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +bool ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, + ckh_keycomp_t *keycomp); +void ckh_delete(ckh_t *ckh); +size_t ckh_count(ckh_t *ckh); +bool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data); +bool ckh_insert(ckh_t *ckh, const void *key, const void *data); +bool ckh_remove(ckh_t *ckh, const void *searchkey, void **key, + void **data); +bool ckh_search(ckh_t *ckh, const void *seachkey, void **key, void **data); +void ckh_string_hash(const void *key, unsigned minbits, size_t *hash1, + size_t *hash2); +bool ckh_string_keycomp(const void *k1, const void *k2); +void ckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1, + size_t *hash2); +bool ckh_pointer_keycomp(const void *k1, const void *k2); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/ctl.h b/externals/jemalloc/include/internal/ctl.h new file mode 100644 index 00000000000..7bbf21e0e85 --- /dev/null +++ b/externals/jemalloc/include/internal/ctl.h @@ -0,0 +1,117 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +typedef struct ctl_node_s ctl_node_t; +typedef struct ctl_arena_stats_s ctl_arena_stats_t; +typedef struct ctl_stats_s ctl_stats_t; + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +struct ctl_node_s { + bool named; + union { + struct { + const char *name; + /* If (nchildren == 0), this is a terminal node. */ + unsigned nchildren; + const ctl_node_t *children; + } named; + struct { + const ctl_node_t *(*index)(const size_t *, size_t, + size_t); + } indexed; + } u; + int (*ctl)(const size_t *, size_t, void *, size_t *, void *, + size_t); +}; + +struct ctl_arena_stats_s { + bool initialized; + size_t pactive; + size_t pdirty; +#ifdef JEMALLOC_STATS + arena_stats_t astats; + + /* Aggregate stats for small size classes, based on bin stats. */ + size_t allocated_small; + uint64_t nmalloc_small; + uint64_t ndalloc_small; + uint64_t nrequests_small; + + malloc_bin_stats_t *bstats; /* nbins elements. */ + malloc_large_stats_t *lstats; /* nlclasses elements. */ +#endif +}; + +struct ctl_stats_s { +#ifdef JEMALLOC_STATS + size_t allocated; + size_t active; + size_t mapped; + struct { + size_t current; /* stats_chunks.curchunks */ + uint64_t total; /* stats_chunks.nchunks */ + size_t high; /* stats_chunks.highchunks */ + } chunks; + struct { + size_t allocated; /* huge_allocated */ + uint64_t nmalloc; /* huge_nmalloc */ + uint64_t ndalloc; /* huge_ndalloc */ + } huge; +#endif + ctl_arena_stats_t *arenas; /* (narenas + 1) elements. */ +#ifdef JEMALLOC_SWAP + size_t swap_avail; +#endif +}; + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +int ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp, + size_t newlen); +int ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp); + +int ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen); +bool ctl_boot(void); + +#define xmallctl(name, oldp, oldlenp, newp, newlen) do { \ + if (JEMALLOC_P(mallctl)(name, oldp, oldlenp, newp, newlen) \ + != 0) { \ + malloc_write("<jemalloc>: Invalid xmallctl(\""); \ + malloc_write(name); \ + malloc_write("\", ...) call\n"); \ + abort(); \ + } \ +} while (0) + +#define xmallctlnametomib(name, mibp, miblenp) do { \ + if (JEMALLOC_P(mallctlnametomib)(name, mibp, miblenp) != 0) { \ + malloc_write( \ + "<jemalloc>: Invalid xmallctlnametomib(\""); \ + malloc_write(name); \ + malloc_write("\", ...) call\n"); \ + abort(); \ + } \ +} while (0) + +#define xmallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) do { \ + if (JEMALLOC_P(mallctlbymib)(mib, miblen, oldp, oldlenp, newp, \ + newlen) != 0) { \ + malloc_write( \ + "<jemalloc>: Invalid xmallctlbymib() call\n"); \ + abort(); \ + } \ +} while (0) + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ + diff --git a/externals/jemalloc/include/internal/extent.h b/externals/jemalloc/include/internal/extent.h new file mode 100644 index 00000000000..33a4e9a3852 --- /dev/null +++ b/externals/jemalloc/include/internal/extent.h @@ -0,0 +1,49 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +typedef struct extent_node_s extent_node_t; + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +/* Tree of extents. */ +struct extent_node_s { +#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS)) + /* Linkage for the size/address-ordered tree. */ + rb_node(extent_node_t) link_szad; +#endif + + /* Linkage for the address-ordered tree. */ + rb_node(extent_node_t) link_ad; + +#ifdef JEMALLOC_PROF + /* Profile counters, used for huge objects. */ + prof_thr_cnt_t *prof_cnt; +#endif + + /* Pointer to the extent that this tree node is responsible for. */ + void *addr; + + /* Total region size. */ + size_t size; +}; +typedef rb_tree(extent_node_t) extent_tree_t; + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS)) +rb_proto(, extent_tree_szad_, extent_tree_t, extent_node_t) +#endif + +rb_proto(, extent_tree_ad_, extent_tree_t, extent_node_t) + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ + diff --git a/externals/jemalloc/include/internal/hash.h b/externals/jemalloc/include/internal/hash.h new file mode 100644 index 00000000000..d12cdb8359f --- /dev/null +++ b/externals/jemalloc/include/internal/hash.h @@ -0,0 +1,70 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#ifndef JEMALLOC_ENABLE_INLINE +uint64_t hash(const void *key, size_t len, uint64_t seed); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(HASH_C_)) +/* + * The following hash function is based on MurmurHash64A(), placed into the + * public domain by Austin Appleby. See http://murmurhash.googlepages.com/ for + * details. + */ +JEMALLOC_INLINE uint64_t +hash(const void *key, size_t len, uint64_t seed) +{ + const uint64_t m = 0xc6a4a7935bd1e995; + const int r = 47; + uint64_t h = seed ^ (len * m); + const uint64_t *data = (const uint64_t *)key; + const uint64_t *end = data + (len/8); + const unsigned char *data2; + + assert(((uintptr_t)key & 0x7) == 0); + + while(data != end) { + uint64_t k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + data2 = (const unsigned char *)data; + switch(len & 7) { + case 7: h ^= ((uint64_t)(data2[6])) << 48; + case 6: h ^= ((uint64_t)(data2[5])) << 40; + case 5: h ^= ((uint64_t)(data2[4])) << 32; + case 4: h ^= ((uint64_t)(data2[3])) << 24; + case 3: h ^= ((uint64_t)(data2[2])) << 16; + case 2: h ^= ((uint64_t)(data2[1])) << 8; + case 1: h ^= ((uint64_t)(data2[0])); + h *= m; + } + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; +} +#endif + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/huge.h b/externals/jemalloc/include/internal/huge.h new file mode 100644 index 00000000000..3cf32f7506d --- /dev/null +++ b/externals/jemalloc/include/internal/huge.h @@ -0,0 +1,38 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#ifdef JEMALLOC_STATS +/* Huge allocation statistics. */ +extern uint64_t huge_nmalloc; +extern uint64_t huge_ndalloc; +extern size_t huge_allocated; +#endif + +/* Protects chunk-related data structures. */ +extern malloc_mutex_t huge_mtx; + +void *huge_malloc(size_t size, bool zero); +void *huge_palloc(size_t alignment, size_t size); +void *huge_ralloc(void *ptr, size_t size, size_t oldsize); +void huge_dalloc(void *ptr); +size_t huge_salloc(const void *ptr); +#ifdef JEMALLOC_PROF +prof_thr_cnt_t *huge_prof_cnt_get(const void *ptr); +void huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt); +#endif +bool huge_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/jemalloc_internal.h b/externals/jemalloc/include/internal/jemalloc_internal.h new file mode 100644 index 00000000000..03782dd6690 --- /dev/null +++ b/externals/jemalloc/include/internal/jemalloc_internal.h @@ -0,0 +1,561 @@ +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/uio.h> + +#include <errno.h> +#include <limits.h> +#ifndef SIZE_T_MAX +# define SIZE_T_MAX SIZE_MAX +#endif +#include <pthread.h> +#include <sched.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> + +#define JEMALLOC_MANGLE +#include "../jemalloc.h" + +#ifdef JEMALLOC_LAZY_LOCK +#include <dlfcn.h> +#endif + +#define RB_COMPACT +#include "jemalloc/internal/rb.h" +#include "jemalloc/internal/qr.h" +#include "jemalloc/internal/ql.h" + +extern void (*JEMALLOC_P(malloc_message))(void *wcbopaque, const char *s); + +/* + * Define a custom assert() in order to reduce the chances of deadlock during + * assertion failure. + */ +#ifdef JEMALLOC_DEBUG +# define assert(e) do { \ + if (!(e)) { \ + char line_buf[UMAX2S_BUFSIZE]; \ + malloc_write("<jemalloc>: "); \ + malloc_write(__FILE__); \ + malloc_write(":"); \ + malloc_write(umax2s(__LINE__, 10, line_buf)); \ + malloc_write(": Failed assertion: "); \ + malloc_write("\""); \ + malloc_write(#e); \ + malloc_write("\"\n"); \ + abort(); \ + } \ +} while (0) +#else +#define assert(e) +#endif + +/* + * jemalloc can conceptually be broken into components (arena, tcache, etc.), + * but there are circular dependencies that cannot be broken without + * substantial performance degradation. In order to reduce the effect on + * visual code flow, read the header files in multiple passes, with one of the + * following cpp variables defined during each pass: + * + * JEMALLOC_H_TYPES : Preprocessor-defined constants and psuedo-opaque data + * types. + * JEMALLOC_H_STRUCTS : Data structures. + * JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes. + * JEMALLOC_H_INLINES : Inline functions. + */ +/******************************************************************************/ +#define JEMALLOC_H_TYPES + +#define ZU(z) ((size_t)z) + +#ifndef __DECONST +# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) +#endif + +#ifdef JEMALLOC_DEBUG + /* Disable inlining to make debugging easier. */ +# define JEMALLOC_INLINE +# define inline +#else +# define JEMALLOC_ENABLE_INLINE +# define JEMALLOC_INLINE static inline +#endif + +/* Size of stack-allocated buffer passed to strerror_r(). */ +#define STRERROR_BUF 64 + +/* Minimum alignment of allocations is 2^LG_QUANTUM bytes. */ +#ifdef __i386__ +# define LG_QUANTUM 4 +#endif +#ifdef __ia64__ +# define LG_QUANTUM 4 +#endif +#ifdef __alpha__ +# define LG_QUANTUM 4 +#endif +#ifdef __sparc64__ +# define LG_QUANTUM 4 +#endif +#if (defined(__amd64__) || defined(__x86_64__)) +# define LG_QUANTUM 4 +#endif +#ifdef __arm__ +# define LG_QUANTUM 3 +#endif +#ifdef __mips__ +# define LG_QUANTUM 3 +#endif +#ifdef __powerpc__ +# define LG_QUANTUM 4 +#endif +#ifdef __s390x__ +# define LG_QUANTUM 4 +#endif + +#define QUANTUM ((size_t)(1U << LG_QUANTUM)) +#define QUANTUM_MASK (QUANTUM - 1) + +/* Return the smallest quantum multiple that is >= a. */ +#define QUANTUM_CEILING(a) \ + (((a) + QUANTUM_MASK) & ~QUANTUM_MASK) + +#define SIZEOF_PTR (1U << LG_SIZEOF_PTR) + +/* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */ +#if (!defined(PIC) && !defined(NO_TLS)) +# define NO_TLS +#endif + +/* + * Maximum size of L1 cache line. This is used to avoid cache line aliasing. + * In addition, this controls the spacing of cacheline-spaced size classes. + */ +#define LG_CACHELINE 6 +#define CACHELINE ((size_t)(1U << LG_CACHELINE)) +#define CACHELINE_MASK (CACHELINE - 1) + +/* Return the smallest cacheline multiple that is >= s. */ +#define CACHELINE_CEILING(s) \ + (((s) + CACHELINE_MASK) & ~CACHELINE_MASK) + +/* + * Page size. STATIC_PAGE_SHIFT is determined by the configure script. If + * DYNAMIC_PAGE_SHIFT is enabled, only use the STATIC_PAGE_* macros where + * compile-time values are required for the purposes of defining data + * structures. + */ +#define STATIC_PAGE_SIZE ((size_t)(1U << STATIC_PAGE_SHIFT)) +#define STATIC_PAGE_MASK ((size_t)(STATIC_PAGE_SIZE - 1)) + +#ifdef DYNAMIC_PAGE_SHIFT +# define PAGE_SHIFT lg_pagesize +# define PAGE_SIZE pagesize +# define PAGE_MASK pagesize_mask +#else +# define PAGE_SHIFT STATIC_PAGE_SHIFT +# define PAGE_SIZE STATIC_PAGE_SIZE +# define PAGE_MASK STATIC_PAGE_MASK +#endif + +/* Return the smallest pagesize multiple that is >= s. */ +#define PAGE_CEILING(s) \ + (((s) + PAGE_MASK) & ~PAGE_MASK) + +#include "jemalloc/internal/totally_not_p_r_n.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#undef JEMALLOC_H_TYPES +/******************************************************************************/ +#define JEMALLOC_H_STRUCTS + +#include "jemalloc/internal/totally_not_p_r_n.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#undef JEMALLOC_H_STRUCTS +/******************************************************************************/ +#define JEMALLOC_H_EXTERNS + +extern bool opt_abort; +#ifdef JEMALLOC_FILL +extern bool opt_junk; +#endif +#ifdef JEMALLOC_SYSV +extern bool opt_sysv; +#endif +#ifdef JEMALLOC_XMALLOC +extern bool opt_xmalloc; +#endif +#ifdef JEMALLOC_FILL +extern bool opt_zero; +#endif + +#ifdef DYNAMIC_PAGE_SHIFT +extern size_t pagesize; +extern size_t pagesize_mask; +extern size_t lg_pagesize; +#endif + +/* Number of CPUs. */ +extern unsigned ncpus; + +extern malloc_mutex_t arenas_lock; /* Protects arenas initialization. */ +#ifndef NO_TLS +/* + * Map of pthread_self() --> arenas[???], used for selecting an arena to use + * for allocations. + */ +extern __thread arena_t *arenas_map JEMALLOC_ATTR(tls_model("initial-exec")); +#endif +/* + * Arenas that are used to service external requests. Not all elements of the + * arenas array are necessarily used; arenas are created lazily as needed. + */ +extern arena_t **arenas; +extern unsigned narenas; + +arena_t *arenas_extend(unsigned ind); +#ifndef NO_TLS +arena_t *choose_arena_hard(void); +#endif + +#include "jemalloc/internal/totally_not_p_r_n.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#undef JEMALLOC_H_EXTERNS +/******************************************************************************/ +#define JEMALLOC_H_INLINES + +#include "jemalloc/internal/totally_not_p_r_n.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" + +#ifndef JEMALLOC_ENABLE_INLINE +void malloc_write(const char *s); +arena_t *choose_arena(void); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) +/* + * Wrapper around malloc_message() that avoids the need for + * JEMALLOC_P(malloc_message)(...) throughout the code. + */ +JEMALLOC_INLINE void +malloc_write(const char *s) +{ + + JEMALLOC_P(malloc_message)(NULL, s); +} + +/* + * Choose an arena based on a per-thread value (fast-path code, calls slow-path + * code if necessary). + */ +JEMALLOC_INLINE arena_t * +choose_arena(void) +{ + arena_t *ret; + + /* + * We can only use TLS if this is a PIC library, since for the static + * library version, libc's malloc is used by TLS allocation, which + * introduces a bootstrapping issue. + */ +#ifndef NO_TLS + ret = arenas_map; + if (ret == NULL) { + ret = choose_arena_hard(); + assert(ret != NULL); + } +#else + if (isthreaded && narenas > 1) { + unsigned long ind; + + /* + * Hash pthread_self() to one of the arenas. There is a prime + * number of arenas, so this has a reasonable chance of + * working. Even so, the hashing can be easily thwarted by + * inconvenient pthread_self() values. Without specific + * knowledge of how pthread_self() calculates values, we can't + * easily do much better than this. + */ + ind = (unsigned long) pthread_self() % narenas; + + /* + * Optimistially assume that arenas[ind] has been initialized. + * At worst, we find out that some other thread has already + * done so, after acquiring the lock in preparation. Note that + * this lazy locking also has the effect of lazily forcing + * cache coherency; without the lock acquisition, there's no + * guarantee that modification of arenas[ind] by another thread + * would be seen on this CPU for an arbitrary amount of time. + * + * In general, this approach to modifying a synchronized value + * isn't a good idea, but in this case we only ever modify the + * value once, so things work out well. + */ + ret = arenas[ind]; + if (ret == NULL) { + /* + * Avoid races with another thread that may have already + * initialized arenas[ind]. + */ + malloc_mutex_lock(&arenas_lock); + if (arenas[ind] == NULL) + ret = arenas_extend((unsigned)ind); + else + ret = arenas[ind]; + malloc_mutex_unlock(&arenas_lock); + } + } else + ret = arenas[0]; +#endif + + assert(ret != NULL); + return (ret); +} +#endif + +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#ifndef JEMALLOC_ENABLE_INLINE +void *imalloc(size_t size); +void *icalloc(size_t size); +void *ipalloc(size_t alignment, size_t size); +size_t isalloc(const void *ptr); +void *iralloc(void *ptr, size_t size); +void idalloc(void *ptr); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) +JEMALLOC_INLINE void * +imalloc(size_t size) +{ + + assert(size != 0); + + if (size <= arena_maxclass) + return (arena_malloc(size, false)); + else + return (huge_malloc(size, false)); +} + +JEMALLOC_INLINE void * +icalloc(size_t size) +{ + + if (size <= arena_maxclass) + return (arena_malloc(size, true)); + else + return (huge_malloc(size, true)); +} + +JEMALLOC_INLINE void * +ipalloc(size_t alignment, size_t size) +{ + void *ret; + size_t ceil_size; + + /* + * Round size up to the nearest multiple of alignment. + * + * This done, we can take advantage of the fact that for each small + * size class, every object is aligned at the smallest power of two + * that is non-zero in the base two representation of the size. For + * example: + * + * Size | Base 2 | Minimum alignment + * -----+----------+------------------ + * 96 | 1100000 | 32 + * 144 | 10100000 | 32 + * 192 | 11000000 | 64 + * + * Depending on runtime settings, it is possible that arena_malloc() + * will further round up to a power of two, but that never causes + * correctness issues. + */ + ceil_size = (size + (alignment - 1)) & (-alignment); + /* + * (ceil_size < size) protects against the combination of maximal + * alignment and size greater than maximal alignment. + */ + if (ceil_size < size) { + /* size_t overflow. */ + return (NULL); + } + + if (ceil_size <= PAGE_SIZE || (alignment <= PAGE_SIZE + && ceil_size <= arena_maxclass)) + ret = arena_malloc(ceil_size, false); + else { + size_t run_size; + + /* + * We can't achieve subpage alignment, so round up alignment + * permanently; it makes later calculations simpler. + */ + alignment = PAGE_CEILING(alignment); + ceil_size = PAGE_CEILING(size); + /* + * (ceil_size < size) protects against very large sizes within + * PAGE_SIZE of SIZE_T_MAX. + * + * (ceil_size + alignment < ceil_size) protects against the + * combination of maximal alignment and ceil_size large enough + * to cause overflow. This is similar to the first overflow + * check above, but it needs to be repeated due to the new + * ceil_size value, which may now be *equal* to maximal + * alignment, whereas before we only detected overflow if the + * original size was *greater* than maximal alignment. + */ + if (ceil_size < size || ceil_size + alignment < ceil_size) { + /* size_t overflow. */ + return (NULL); + } + + /* + * Calculate the size of the over-size run that arena_palloc() + * would need to allocate in order to guarantee the alignment. + */ + if (ceil_size >= alignment) + run_size = ceil_size + alignment - PAGE_SIZE; + else { + /* + * It is possible that (alignment << 1) will cause + * overflow, but it doesn't matter because we also + * subtract PAGE_SIZE, which in the case of overflow + * leaves us with a very large run_size. That causes + * the first conditional below to fail, which means + * that the bogus run_size value never gets used for + * anything important. + */ + run_size = (alignment << 1) - PAGE_SIZE; + } + + if (run_size <= arena_maxclass) { + ret = arena_palloc(choose_arena(), alignment, ceil_size, + run_size); + } else if (alignment <= chunksize) + ret = huge_malloc(ceil_size, false); + else + ret = huge_palloc(alignment, ceil_size); + } + + assert(((uintptr_t)ret & (alignment - 1)) == 0); + return (ret); +} + +JEMALLOC_INLINE size_t +isalloc(const void *ptr) +{ + size_t ret; + arena_chunk_t *chunk; + + assert(ptr != NULL); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk != ptr) { + /* Region. */ + assert(chunk->arena->magic == ARENA_MAGIC); + +#ifdef JEMALLOC_PROF + ret = arena_salloc_demote(ptr); +#else + ret = arena_salloc(ptr); +#endif + } else + ret = huge_salloc(ptr); + + return (ret); +} + +JEMALLOC_INLINE void * +iralloc(void *ptr, size_t size) +{ + size_t oldsize; + + assert(ptr != NULL); + assert(size != 0); + + oldsize = isalloc(ptr); + + if (size <= arena_maxclass) + return (arena_ralloc(ptr, size, oldsize)); + else + return (huge_ralloc(ptr, size, oldsize)); +} + +JEMALLOC_INLINE void +idalloc(void *ptr) +{ + arena_chunk_t *chunk; + + assert(ptr != NULL); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk != ptr) + arena_dalloc(chunk->arena, chunk, ptr); + else + huge_dalloc(ptr); +} +#endif + +#undef JEMALLOC_H_INLINES +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/jemalloc_internal.h.in b/externals/jemalloc/include/internal/jemalloc_internal.h.in new file mode 100644 index 00000000000..2c3f32f126d --- /dev/null +++ b/externals/jemalloc/include/internal/jemalloc_internal.h.in @@ -0,0 +1,561 @@ +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/uio.h> + +#include <errno.h> +#include <limits.h> +#ifndef SIZE_T_MAX +# define SIZE_T_MAX SIZE_MAX +#endif +#include <pthread.h> +#include <sched.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> + +#define JEMALLOC_MANGLE +#include "../jemalloc@install_suffix@.h" + +#ifdef JEMALLOC_LAZY_LOCK +#include <dlfcn.h> +#endif + +#define RB_COMPACT +#include "jemalloc/internal/rb.h" +#include "jemalloc/internal/qr.h" +#include "jemalloc/internal/ql.h" + +extern void (*JEMALLOC_P(malloc_message))(void *wcbopaque, const char *s); + +/* + * Define a custom assert() in order to reduce the chances of deadlock during + * assertion failure. + */ +#ifdef JEMALLOC_DEBUG +# define assert(e) do { \ + if (!(e)) { \ + char line_buf[UMAX2S_BUFSIZE]; \ + malloc_write("<jemalloc>: "); \ + malloc_write(__FILE__); \ + malloc_write(":"); \ + malloc_write(umax2s(__LINE__, 10, line_buf)); \ + malloc_write(": Failed assertion: "); \ + malloc_write("\""); \ + malloc_write(#e); \ + malloc_write("\"\n"); \ + abort(); \ + } \ +} while (0) +#else +#define assert(e) +#endif + +/* + * jemalloc can conceptually be broken into components (arena, tcache, etc.), + * but there are circular dependencies that cannot be broken without + * substantial performance degradation. In order to reduce the effect on + * visual code flow, read the header files in multiple passes, with one of the + * following cpp variables defined during each pass: + * + * JEMALLOC_H_TYPES : Preprocessor-defined constants and psuedo-opaque data + * types. + * JEMALLOC_H_STRUCTS : Data structures. + * JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes. + * JEMALLOC_H_INLINES : Inline functions. + */ +/******************************************************************************/ +#define JEMALLOC_H_TYPES + +#define ZU(z) ((size_t)z) + +#ifndef __DECONST +# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) +#endif + +#ifdef JEMALLOC_DEBUG + /* Disable inlining to make debugging easier. */ +# define JEMALLOC_INLINE +# define inline +#else +# define JEMALLOC_ENABLE_INLINE +# define JEMALLOC_INLINE static inline +#endif + +/* Size of stack-allocated buffer passed to strerror_r(). */ +#define STRERROR_BUF 64 + +/* Minimum alignment of allocations is 2^LG_QUANTUM bytes. */ +#ifdef __i386__ +# define LG_QUANTUM 4 +#endif +#ifdef __ia64__ +# define LG_QUANTUM 4 +#endif +#ifdef __alpha__ +# define LG_QUANTUM 4 +#endif +#ifdef __sparc64__ +# define LG_QUANTUM 4 +#endif +#if (defined(__amd64__) || defined(__x86_64__)) +# define LG_QUANTUM 4 +#endif +#ifdef __arm__ +# define LG_QUANTUM 3 +#endif +#ifdef __mips__ +# define LG_QUANTUM 3 +#endif +#ifdef __powerpc__ +# define LG_QUANTUM 4 +#endif +#ifdef __s390x__ +# define LG_QUANTUM 4 +#endif + +#define QUANTUM ((size_t)(1U << LG_QUANTUM)) +#define QUANTUM_MASK (QUANTUM - 1) + +/* Return the smallest quantum multiple that is >= a. */ +#define QUANTUM_CEILING(a) \ + (((a) + QUANTUM_MASK) & ~QUANTUM_MASK) + +#define SIZEOF_PTR (1U << LG_SIZEOF_PTR) + +/* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */ +#if (!defined(PIC) && !defined(NO_TLS)) +# define NO_TLS +#endif + +/* + * Maximum size of L1 cache line. This is used to avoid cache line aliasing. + * In addition, this controls the spacing of cacheline-spaced size classes. + */ +#define LG_CACHELINE 6 +#define CACHELINE ((size_t)(1U << LG_CACHELINE)) +#define CACHELINE_MASK (CACHELINE - 1) + +/* Return the smallest cacheline multiple that is >= s. */ +#define CACHELINE_CEILING(s) \ + (((s) + CACHELINE_MASK) & ~CACHELINE_MASK) + +/* + * Page size. STATIC_PAGE_SHIFT is determined by the configure script. If + * DYNAMIC_PAGE_SHIFT is enabled, only use the STATIC_PAGE_* macros where + * compile-time values are required for the purposes of defining data + * structures. + */ +#define STATIC_PAGE_SIZE ((size_t)(1U << STATIC_PAGE_SHIFT)) +#define STATIC_PAGE_MASK ((size_t)(STATIC_PAGE_SIZE - 1)) + +#ifdef DYNAMIC_PAGE_SHIFT +# define PAGE_SHIFT lg_pagesize +# define PAGE_SIZE pagesize +# define PAGE_MASK pagesize_mask +#else +# define PAGE_SHIFT STATIC_PAGE_SHIFT +# define PAGE_SIZE STATIC_PAGE_SIZE +# define PAGE_MASK STATIC_PAGE_MASK +#endif + +/* Return the smallest pagesize multiple that is >= s. */ +#define PAGE_CEILING(s) \ + (((s) + PAGE_MASK) & ~PAGE_MASK) + +#include "jemalloc/internal/prn.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#undef JEMALLOC_H_TYPES +/******************************************************************************/ +#define JEMALLOC_H_STRUCTS + +#include "jemalloc/internal/prn.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#undef JEMALLOC_H_STRUCTS +/******************************************************************************/ +#define JEMALLOC_H_EXTERNS + +extern bool opt_abort; +#ifdef JEMALLOC_FILL +extern bool opt_junk; +#endif +#ifdef JEMALLOC_SYSV +extern bool opt_sysv; +#endif +#ifdef JEMALLOC_XMALLOC +extern bool opt_xmalloc; +#endif +#ifdef JEMALLOC_FILL +extern bool opt_zero; +#endif + +#ifdef DYNAMIC_PAGE_SHIFT +extern size_t pagesize; +extern size_t pagesize_mask; +extern size_t lg_pagesize; +#endif + +/* Number of CPUs. */ +extern unsigned ncpus; + +extern malloc_mutex_t arenas_lock; /* Protects arenas initialization. */ +#ifndef NO_TLS +/* + * Map of pthread_self() --> arenas[???], used for selecting an arena to use + * for allocations. + */ +extern __thread arena_t *arenas_map JEMALLOC_ATTR(tls_model("initial-exec")); +#endif +/* + * Arenas that are used to service external requests. Not all elements of the + * arenas array are necessarily used; arenas are created lazily as needed. + */ +extern arena_t **arenas; +extern unsigned narenas; + +arena_t *arenas_extend(unsigned ind); +#ifndef NO_TLS +arena_t *choose_arena_hard(void); +#endif + +#include "jemalloc/internal/prn.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#undef JEMALLOC_H_EXTERNS +/******************************************************************************/ +#define JEMALLOC_H_INLINES + +#include "jemalloc/internal/prn.h" +#include "jemalloc/internal/ckh.h" +#include "jemalloc/internal/stats.h" +#include "jemalloc/internal/ctl.h" +#include "jemalloc/internal/mutex.h" +#include "jemalloc/internal/mb.h" +#include "jemalloc/internal/extent.h" +#include "jemalloc/internal/base.h" +#include "jemalloc/internal/chunk.h" +#include "jemalloc/internal/huge.h" + +#ifndef JEMALLOC_ENABLE_INLINE +void malloc_write(const char *s); +arena_t *choose_arena(void); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) +/* + * Wrapper around malloc_message() that avoids the need for + * JEMALLOC_P(malloc_message)(...) throughout the code. + */ +JEMALLOC_INLINE void +malloc_write(const char *s) +{ + + JEMALLOC_P(malloc_message)(NULL, s); +} + +/* + * Choose an arena based on a per-thread value (fast-path code, calls slow-path + * code if necessary). + */ +JEMALLOC_INLINE arena_t * +choose_arena(void) +{ + arena_t *ret; + + /* + * We can only use TLS if this is a PIC library, since for the static + * library version, libc's malloc is used by TLS allocation, which + * introduces a bootstrapping issue. + */ +#ifndef NO_TLS + ret = arenas_map; + if (ret == NULL) { + ret = choose_arena_hard(); + assert(ret != NULL); + } +#else + if (isthreaded && narenas > 1) { + unsigned long ind; + + /* + * Hash pthread_self() to one of the arenas. There is a prime + * number of arenas, so this has a reasonable chance of + * working. Even so, the hashing can be easily thwarted by + * inconvenient pthread_self() values. Without specific + * knowledge of how pthread_self() calculates values, we can't + * easily do much better than this. + */ + ind = (unsigned long) pthread_self() % narenas; + + /* + * Optimistially assume that arenas[ind] has been initialized. + * At worst, we find out that some other thread has already + * done so, after acquiring the lock in preparation. Note that + * this lazy locking also has the effect of lazily forcing + * cache coherency; without the lock acquisition, there's no + * guarantee that modification of arenas[ind] by another thread + * would be seen on this CPU for an arbitrary amount of time. + * + * In general, this approach to modifying a synchronized value + * isn't a good idea, but in this case we only ever modify the + * value once, so things work out well. + */ + ret = arenas[ind]; + if (ret == NULL) { + /* + * Avoid races with another thread that may have already + * initialized arenas[ind]. + */ + malloc_mutex_lock(&arenas_lock); + if (arenas[ind] == NULL) + ret = arenas_extend((unsigned)ind); + else + ret = arenas[ind]; + malloc_mutex_unlock(&arenas_lock); + } + } else + ret = arenas[0]; +#endif + + assert(ret != NULL); + return (ret); +} +#endif + +#include "jemalloc/internal/tcache.h" +#include "jemalloc/internal/arena.h" +#include "jemalloc/internal/hash.h" +#include "jemalloc/internal/prof.h" + +#ifndef JEMALLOC_ENABLE_INLINE +void *imalloc(size_t size); +void *icalloc(size_t size); +void *ipalloc(size_t alignment, size_t size); +size_t isalloc(const void *ptr); +void *iralloc(void *ptr, size_t size); +void idalloc(void *ptr); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) +JEMALLOC_INLINE void * +imalloc(size_t size) +{ + + assert(size != 0); + + if (size <= arena_maxclass) + return (arena_malloc(size, false)); + else + return (huge_malloc(size, false)); +} + +JEMALLOC_INLINE void * +icalloc(size_t size) +{ + + if (size <= arena_maxclass) + return (arena_malloc(size, true)); + else + return (huge_malloc(size, true)); +} + +JEMALLOC_INLINE void * +ipalloc(size_t alignment, size_t size) +{ + void *ret; + size_t ceil_size; + + /* + * Round size up to the nearest multiple of alignment. + * + * This done, we can take advantage of the fact that for each small + * size class, every object is aligned at the smallest power of two + * that is non-zero in the base two representation of the size. For + * example: + * + * Size | Base 2 | Minimum alignment + * -----+----------+------------------ + * 96 | 1100000 | 32 + * 144 | 10100000 | 32 + * 192 | 11000000 | 64 + * + * Depending on runtime settings, it is possible that arena_malloc() + * will further round up to a power of two, but that never causes + * correctness issues. + */ + ceil_size = (size + (alignment - 1)) & (-alignment); + /* + * (ceil_size < size) protects against the combination of maximal + * alignment and size greater than maximal alignment. + */ + if (ceil_size < size) { + /* size_t overflow. */ + return (NULL); + } + + if (ceil_size <= PAGE_SIZE || (alignment <= PAGE_SIZE + && ceil_size <= arena_maxclass)) + ret = arena_malloc(ceil_size, false); + else { + size_t run_size; + + /* + * We can't achieve subpage alignment, so round up alignment + * permanently; it makes later calculations simpler. + */ + alignment = PAGE_CEILING(alignment); + ceil_size = PAGE_CEILING(size); + /* + * (ceil_size < size) protects against very large sizes within + * PAGE_SIZE of SIZE_T_MAX. + * + * (ceil_size + alignment < ceil_size) protects against the + * combination of maximal alignment and ceil_size large enough + * to cause overflow. This is similar to the first overflow + * check above, but it needs to be repeated due to the new + * ceil_size value, which may now be *equal* to maximal + * alignment, whereas before we only detected overflow if the + * original size was *greater* than maximal alignment. + */ + if (ceil_size < size || ceil_size + alignment < ceil_size) { + /* size_t overflow. */ + return (NULL); + } + + /* + * Calculate the size of the over-size run that arena_palloc() + * would need to allocate in order to guarantee the alignment. + */ + if (ceil_size >= alignment) + run_size = ceil_size + alignment - PAGE_SIZE; + else { + /* + * It is possible that (alignment << 1) will cause + * overflow, but it doesn't matter because we also + * subtract PAGE_SIZE, which in the case of overflow + * leaves us with a very large run_size. That causes + * the first conditional below to fail, which means + * that the bogus run_size value never gets used for + * anything important. + */ + run_size = (alignment << 1) - PAGE_SIZE; + } + + if (run_size <= arena_maxclass) { + ret = arena_palloc(choose_arena(), alignment, ceil_size, + run_size); + } else if (alignment <= chunksize) + ret = huge_malloc(ceil_size, false); + else + ret = huge_palloc(alignment, ceil_size); + } + + assert(((uintptr_t)ret & (alignment - 1)) == 0); + return (ret); +} + +JEMALLOC_INLINE size_t +isalloc(const void *ptr) +{ + size_t ret; + arena_chunk_t *chunk; + + assert(ptr != NULL); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk != ptr) { + /* Region. */ + assert(chunk->arena->magic == ARENA_MAGIC); + +#ifdef JEMALLOC_PROF + ret = arena_salloc_demote(ptr); +#else + ret = arena_salloc(ptr); +#endif + } else + ret = huge_salloc(ptr); + + return (ret); +} + +JEMALLOC_INLINE void * +iralloc(void *ptr, size_t size) +{ + size_t oldsize; + + assert(ptr != NULL); + assert(size != 0); + + oldsize = isalloc(ptr); + + if (size <= arena_maxclass) + return (arena_ralloc(ptr, size, oldsize)); + else + return (huge_ralloc(ptr, size, oldsize)); +} + +JEMALLOC_INLINE void +idalloc(void *ptr) +{ + arena_chunk_t *chunk; + + assert(ptr != NULL); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk != ptr) + arena_dalloc(chunk->arena, chunk, ptr); + else + huge_dalloc(ptr); +} +#endif + +#undef JEMALLOC_H_INLINES +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/mb.h b/externals/jemalloc/include/internal/mb.h new file mode 100644 index 00000000000..1707aa91d68 --- /dev/null +++ b/externals/jemalloc/include/internal/mb.h @@ -0,0 +1,108 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#ifndef JEMALLOC_ENABLE_INLINE +void mb_write(void); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(MB_C_)) +#ifdef __i386__ +/* + * According to the Intel Architecture Software Developer's Manual, current + * processors execute instructions in order from the perspective of other + * processors in a multiprocessor system, but 1) Intel reserves the right to + * change that, and 2) the compiler's optimizer could re-order instructions if + * there weren't some form of barrier. Therefore, even if running on an + * architecture that does not need memory barriers (everything through at least + * i686), an "optimizer barrier" is necessary. + */ +JEMALLOC_INLINE void +mb_write(void) +{ + +# if 0 + /* This is a true memory barrier. */ + asm volatile ("pusha;" + "xor %%eax,%%eax;" + "cpuid;" + "popa;" + : /* Outputs. */ + : /* Inputs. */ + : "memory" /* Clobbers. */ + ); +#else + /* + * This is hopefully enough to keep the compiler from reordering + * instructions around this one. + */ + asm volatile ("nop;" + : /* Outputs. */ + : /* Inputs. */ + : "memory" /* Clobbers. */ + ); +#endif +} +#elif (defined(__amd64_) || defined(__x86_64__)) +JEMALLOC_INLINE void +mb_write(void) +{ + + asm volatile ("sfence" + : /* Outputs. */ + : /* Inputs. */ + : "memory" /* Clobbers. */ + ); +} +#elif defined(__powerpc__) +JEMALLOC_INLINE void +mb_write(void) +{ + + asm volatile ("eieio" + : /* Outputs. */ + : /* Inputs. */ + : "memory" /* Clobbers. */ + ); +} +#elif defined(__sparc64__) +JEMALLOC_INLINE void +mb_write(void) +{ + + asm volatile ("membar #StoreStore" + : /* Outputs. */ + : /* Inputs. */ + : "memory" /* Clobbers. */ + ); +} +#else +/* + * This is much slower than a simple memory barrier, but the semantics of mutex + * unlock make this work. + */ +JEMALLOC_INLINE void +mb_write(void) +{ + malloc_mutex_t mtx; + + malloc_mutex_init(&mtx); + malloc_mutex_lock(&mtx); + malloc_mutex_unlock(&mtx); +} +#endif +#endif + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/mutex.h b/externals/jemalloc/include/internal/mutex.h new file mode 100644 index 00000000000..108bfa8abfd --- /dev/null +++ b/externals/jemalloc/include/internal/mutex.h @@ -0,0 +1,61 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +typedef pthread_mutex_t malloc_mutex_t; + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#ifdef JEMALLOC_LAZY_LOCK +extern bool isthreaded; +#else +# define isthreaded true +#endif + +bool malloc_mutex_init(malloc_mutex_t *mutex); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#ifndef JEMALLOC_ENABLE_INLINE +void malloc_mutex_lock(malloc_mutex_t *mutex); +bool malloc_mutex_trylock(malloc_mutex_t *mutex); +void malloc_mutex_unlock(malloc_mutex_t *mutex); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_MUTEX_C_)) +JEMALLOC_INLINE void +malloc_mutex_lock(malloc_mutex_t *mutex) +{ + + if (isthreaded) + pthread_mutex_lock(mutex); +} + +JEMALLOC_INLINE bool +malloc_mutex_trylock(malloc_mutex_t *mutex) +{ + + if (isthreaded) + return (pthread_mutex_trylock(mutex) != 0); + else + return (false); +} + +JEMALLOC_INLINE void +malloc_mutex_unlock(malloc_mutex_t *mutex) +{ + + if (isthreaded) + pthread_mutex_unlock(mutex); +} +#endif + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/prof.h b/externals/jemalloc/include/internal/prof.h new file mode 100644 index 00000000000..6e71552d85e --- /dev/null +++ b/externals/jemalloc/include/internal/prof.h @@ -0,0 +1,171 @@ +#ifdef JEMALLOC_PROF +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +typedef struct prof_bt_s prof_bt_t; +typedef struct prof_cnt_s prof_cnt_t; +typedef struct prof_thr_cnt_s prof_thr_cnt_t; +typedef struct prof_ctx_s prof_ctx_t; +typedef struct prof_s prof_t; + +/* Option defaults. */ +#define LG_PROF_BT_MAX_DEFAULT 2 +#define LG_PROF_SAMPLE_DEFAULT 0 +#define LG_PROF_INTERVAL_DEFAULT 30 + +/* + * Hard limit on stack backtrace depth. Note that the version of + * prof_backtrace() that is based on __builtin_return_address() necessarily has + * a hard-coded number of backtrace frame handlers, so increasing + * LG_PROF_BT_MAX requires changing prof_backtrace(). + */ +#define LG_PROF_BT_MAX 7 /* >= LG_PROF_BT_MAX_DEFAULT */ +#define PROF_BT_MAX (1U << LG_PROF_BT_MAX) + +/* Initial hash table size. */ +#define PROF_CKH_MINITEMS 64 + +/* Size of memory buffer to use when writing dump files. */ +#define PROF_DUMP_BUF_SIZE 65536 + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +struct prof_bt_s { + /* Backtrace, stored as len program counters. */ + void **vec; + unsigned len; +}; + +#ifdef JEMALLOC_PROF_LIBGCC +/* Data structure passed to libgcc _Unwind_Backtrace() callback functions. */ +typedef struct { + prof_bt_t *bt; + unsigned nignore; + unsigned max; +} prof_unwind_data_t; +#endif + +struct prof_cnt_s { + /* + * Profiling counters. An allocation/deallocation pair can operate on + * different prof_thr_cnt_t objects that are linked into the same + * prof_ctx_t sets_ql, so it is possible for the cur* counters to go + * negative. In principle it is possible for the *bytes counters to + * overflow/underflow, but a general solution would require some form + * of 128-bit counter solution; this implementation doesn't bother to + * solve that problem. + */ + int64_t curobjs; + int64_t curbytes; + uint64_t accumobjs; + uint64_t accumbytes; +}; + +struct prof_thr_cnt_s { + /* Linkage into prof_ctx_t's sets_ql. */ + ql_elm(prof_thr_cnt_t) link; + + /* + * Associated context. If a thread frees an object that it did not + * allocate, it is possible that the context is not cached in the + * thread's hash table, in which case it must be able to look up the + * context, insert a new prof_thr_cnt_t into the thread's hash table, + * and link it into the prof_ctx_t's sets_ql. + */ + prof_ctx_t *ctx; + + /* + * Threads use memory barriers to update the counters. Since there is + * only ever one writer, the only challenge is for the reader to get a + * consistent read of the counters. + * + * The writer uses this series of operations: + * + * 1) Increment epoch to an odd number. + * 2) Update counters. + * 3) Increment epoch to an even number. + * + * The reader must assure 1) that the epoch is even while it reads the + * counters, and 2) that the epoch doesn't change between the time it + * starts and finishes reading the counters. + */ + unsigned epoch; + + /* Profiling counters. */ + prof_cnt_t cnts; +}; + +struct prof_ctx_s { + /* Protects cnt_merged and sets_ql. */ + malloc_mutex_t lock; + + /* Temporary storage for aggregation during dump. */ + prof_cnt_t cnt_dump; + + /* When threads exit, they merge their stats into cnt_merged. */ + prof_cnt_t cnt_merged; + + /* + * List of profile counters, one for each thread that has allocated in + * this context. + */ + ql_head(prof_thr_cnt_t) cnts_ql; +}; + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern bool opt_prof; +/* + * Even if opt_prof is true, sampling can be temporarily disabled by setting + * opt_prof_active to false. No locking is used when updating opt_prof_active, + * so there are no guarantees regarding how long it will take for all threads + * to notice state changes. + */ +extern bool opt_prof_active; +extern size_t opt_lg_prof_bt_max; /* Maximum backtrace depth. */ +extern size_t opt_lg_prof_sample; /* Mean bytes between samples. */ +extern ssize_t opt_lg_prof_interval; /* lg(prof_interval). */ +extern bool opt_prof_udump; /* High-water memory dumping. */ +extern bool opt_prof_leak; /* Dump leak summary at exit. */ + +/* + * Profile dump interval, measured in bytes allocated. Each arena triggers a + * profile dump when it reaches this threshold. The effect is that the + * interval between profile dumps averages prof_interval, though the actual + * interval between dumps will tend to be sporadic, and the interval will be a + * maximum of approximately (prof_interval * narenas). + */ +extern uint64_t prof_interval; + +/* + * If true, promote small sampled objects to large objects, since small run + * headers do not have embedded profile context pointers. + */ +extern bool prof_promote; + +bool prof_init(prof_t *prof, bool master); +void prof_destroy(prof_t *prof); + +prof_thr_cnt_t *prof_alloc_prep(size_t size); +prof_thr_cnt_t *prof_cnt_get(const void *ptr); +void prof_malloc(const void *ptr, prof_thr_cnt_t *cnt); +void prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr, + size_t old_size, prof_thr_cnt_t *old_cnt); +void prof_free(const void *ptr); +void prof_idump(void); +bool prof_mdump(const char *filename); +void prof_udump(void); +void prof_boot0(void); +bool prof_boot1(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ +#endif /* JEMALLOC_PROF */ diff --git a/externals/jemalloc/include/internal/ql.h b/externals/jemalloc/include/internal/ql.h new file mode 100644 index 00000000000..a9ed2393f0c --- /dev/null +++ b/externals/jemalloc/include/internal/ql.h @@ -0,0 +1,83 @@ +/* + * List definitions. + */ +#define ql_head(a_type) \ +struct { \ + a_type *qlh_first; \ +} + +#define ql_head_initializer(a_head) {NULL} + +#define ql_elm(a_type) qr(a_type) + +/* List functions. */ +#define ql_new(a_head) do { \ + (a_head)->qlh_first = NULL; \ +} while (0) + +#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field) + +#define ql_first(a_head) ((a_head)->qlh_first) + +#define ql_last(a_head, a_field) \ + ((ql_first(a_head) != NULL) \ + ? qr_prev(ql_first(a_head), a_field) : NULL) + +#define ql_next(a_head, a_elm, a_field) \ + ((ql_last(a_head, a_field) != (a_elm)) \ + ? qr_next((a_elm), a_field) : NULL) + +#define ql_prev(a_head, a_elm, a_field) \ + ((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field) \ + : NULL) + +#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do { \ + qr_before_insert((a_qlelm), (a_elm), a_field); \ + if (ql_first(a_head) == (a_qlelm)) { \ + ql_first(a_head) = (a_elm); \ + } \ +} while (0) + +#define ql_after_insert(a_qlelm, a_elm, a_field) \ + qr_after_insert((a_qlelm), (a_elm), a_field) + +#define ql_head_insert(a_head, a_elm, a_field) do { \ + if (ql_first(a_head) != NULL) { \ + qr_before_insert(ql_first(a_head), (a_elm), a_field); \ + } \ + ql_first(a_head) = (a_elm); \ +} while (0) + +#define ql_tail_insert(a_head, a_elm, a_field) do { \ + if (ql_first(a_head) != NULL) { \ + qr_before_insert(ql_first(a_head), (a_elm), a_field); \ + } \ + ql_first(a_head) = qr_next((a_elm), a_field); \ +} while (0) + +#define ql_remove(a_head, a_elm, a_field) do { \ + if (ql_first(a_head) == (a_elm)) { \ + ql_first(a_head) = qr_next(ql_first(a_head), a_field); \ + } \ + if (ql_first(a_head) != (a_elm)) { \ + qr_remove((a_elm), a_field); \ + } else { \ + ql_first(a_head) = NULL; \ + } \ +} while (0) + +#define ql_head_remove(a_head, a_type, a_field) do { \ + a_type *t = ql_first(a_head); \ + ql_remove((a_head), t, a_field); \ +} while (0) + +#define ql_tail_remove(a_head, a_type, a_field) do { \ + a_type *t = ql_last(a_head, a_field); \ + ql_remove((a_head), t, a_field); \ +} while (0) + +#define ql_foreach(a_var, a_head, a_field) \ + qr_foreach((a_var), ql_first(a_head), a_field) + +#define ql_reverse_foreach(a_var, a_head, a_field) \ + qr_reverse_foreach((a_var), ql_first(a_head), a_field) diff --git a/externals/jemalloc/include/internal/qr.h b/externals/jemalloc/include/internal/qr.h new file mode 100644 index 00000000000..fe22352fedd --- /dev/null +++ b/externals/jemalloc/include/internal/qr.h @@ -0,0 +1,67 @@ +/* Ring definitions. */ +#define qr(a_type) \ +struct { \ + a_type *qre_next; \ + a_type *qre_prev; \ +} + +/* Ring functions. */ +#define qr_new(a_qr, a_field) do { \ + (a_qr)->a_field.qre_next = (a_qr); \ + (a_qr)->a_field.qre_prev = (a_qr); \ +} while (0) + +#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next) + +#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev) + +#define qr_before_insert(a_qrelm, a_qr, a_field) do { \ + (a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev; \ + (a_qr)->a_field.qre_next = (a_qrelm); \ + (a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr); \ + (a_qrelm)->a_field.qre_prev = (a_qr); \ +} while (0) + +#define qr_after_insert(a_qrelm, a_qr, a_field) \ + do \ + { \ + (a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next; \ + (a_qr)->a_field.qre_prev = (a_qrelm); \ + (a_qr)->a_field.qre_next->a_field.qre_prev = (a_qr); \ + (a_qrelm)->a_field.qre_next = (a_qr); \ + } while (0) + +#define qr_meld(a_qr_a, a_qr_b, a_field) do { \ + void *t; \ + (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b); \ + (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a); \ + t = (a_qr_a)->a_field.qre_prev; \ + (a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev; \ + (a_qr_b)->a_field.qre_prev = t; \ +} while (0) + +/* qr_meld() and qr_split() are functionally equivalent, so there's no need to + * have two copies of the code. */ +#define qr_split(a_qr_a, a_qr_b, a_field) \ + qr_meld((a_qr_a), (a_qr_b), a_field) + +#define qr_remove(a_qr, a_field) do { \ + (a_qr)->a_field.qre_prev->a_field.qre_next \ + = (a_qr)->a_field.qre_next; \ + (a_qr)->a_field.qre_next->a_field.qre_prev \ + = (a_qr)->a_field.qre_prev; \ + (a_qr)->a_field.qre_next = (a_qr); \ + (a_qr)->a_field.qre_prev = (a_qr); \ +} while (0) + +#define qr_foreach(var, a_qr, a_field) \ + for ((var) = (a_qr); \ + (var) != NULL; \ + (var) = (((var)->a_field.qre_next != (a_qr)) \ + ? (var)->a_field.qre_next : NULL)) + +#define qr_reverse_foreach(var, a_qr, a_field) \ + for ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL; \ + (var) != NULL; \ + (var) = (((var) != (a_qr)) \ + ? (var)->a_field.qre_prev : NULL)) diff --git a/externals/jemalloc/include/internal/rb.h b/externals/jemalloc/include/internal/rb.h new file mode 100644 index 00000000000..ee9b009d235 --- /dev/null +++ b/externals/jemalloc/include/internal/rb.h @@ -0,0 +1,973 @@ +/*- + ******************************************************************************* + * + * cpp macro implementation of left-leaning 2-3 red-black trees. Parent + * pointers are not used, and color bits are stored in the least significant + * bit of right-child pointers (if RB_COMPACT is defined), thus making node + * linkage as compact as is possible for red-black trees. + * + * Usage: + * + * #include <stdint.h> + * #include <stdbool.h> + * #define NDEBUG // (Optional, see assert(3).) + * #include <assert.h> + * #define RB_COMPACT // (Optional, embed color bits in right-child pointers.) + * #include <rb.h> + * ... + * + ******************************************************************************* + */ + +#ifndef RB_H_ +#define RB_H_ + +#if 0 +__FBSDID("$FreeBSD: head/lib/libc/stdlib/rb.h 204493 2010-02-28 22:57:13Z jasone $"); +#endif + +#ifdef RB_COMPACT +/* Node structure. */ +#define rb_node(a_type) \ +struct { \ + a_type *rbn_left; \ + a_type *rbn_right_red; \ +} +#else +#define rb_node(a_type) \ +struct { \ + a_type *rbn_left; \ + a_type *rbn_right; \ + bool rbn_red; \ +} +#endif + +/* Root structure. */ +#define rb_tree(a_type) \ +struct { \ + a_type *rbt_root; \ + a_type rbt_nil; \ +} + +/* Left accessors. */ +#define rbtn_left_get(a_type, a_field, a_node) \ + ((a_node)->a_field.rbn_left) +#define rbtn_left_set(a_type, a_field, a_node, a_left) do { \ + (a_node)->a_field.rbn_left = a_left; \ +} while (0) + +#ifdef RB_COMPACT +/* Right accessors. */ +#define rbtn_right_get(a_type, a_field, a_node) \ + ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red) \ + & ((ssize_t)-2))) +#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \ + (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right) \ + | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1))); \ +} while (0) + +/* Color accessors. */ +#define rbtn_red_get(a_type, a_field, a_node) \ + ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red) \ + & ((size_t)1))) +#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \ + (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t) \ + (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)) \ + | ((ssize_t)a_red)); \ +} while (0) +#define rbtn_red_set(a_type, a_field, a_node) do { \ + (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) \ + (a_node)->a_field.rbn_right_red) | ((size_t)1)); \ +} while (0) +#define rbtn_black_set(a_type, a_field, a_node) do { \ + (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t) \ + (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)); \ +} while (0) +#else +/* Right accessors. */ +#define rbtn_right_get(a_type, a_field, a_node) \ + ((a_node)->a_field.rbn_right) +#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \ + (a_node)->a_field.rbn_right = a_right; \ +} while (0) + +/* Color accessors. */ +#define rbtn_red_get(a_type, a_field, a_node) \ + ((a_node)->a_field.rbn_red) +#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \ + (a_node)->a_field.rbn_red = (a_red); \ +} while (0) +#define rbtn_red_set(a_type, a_field, a_node) do { \ + (a_node)->a_field.rbn_red = true; \ +} while (0) +#define rbtn_black_set(a_type, a_field, a_node) do { \ + (a_node)->a_field.rbn_red = false; \ +} while (0) +#endif + +/* Node initializer. */ +#define rbt_node_new(a_type, a_field, a_rbt, a_node) do { \ + rbtn_left_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \ + rbtn_right_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \ + rbtn_red_set(a_type, a_field, (a_node)); \ +} while (0) + +/* Tree initializer. */ +#define rb_new(a_type, a_field, a_rbt) do { \ + (a_rbt)->rbt_root = &(a_rbt)->rbt_nil; \ + rbt_node_new(a_type, a_field, a_rbt, &(a_rbt)->rbt_nil); \ + rbtn_black_set(a_type, a_field, &(a_rbt)->rbt_nil); \ +} while (0) + +/* Internal utility macros. */ +#define rbtn_first(a_type, a_field, a_rbt, a_root, r_node) do { \ + (r_node) = (a_root); \ + if ((r_node) != &(a_rbt)->rbt_nil) { \ + for (; \ + rbtn_left_get(a_type, a_field, (r_node)) != &(a_rbt)->rbt_nil;\ + (r_node) = rbtn_left_get(a_type, a_field, (r_node))) { \ + } \ + } \ +} while (0) + +#define rbtn_last(a_type, a_field, a_rbt, a_root, r_node) do { \ + (r_node) = (a_root); \ + if ((r_node) != &(a_rbt)->rbt_nil) { \ + for (; rbtn_right_get(a_type, a_field, (r_node)) != \ + &(a_rbt)->rbt_nil; (r_node) = rbtn_right_get(a_type, a_field, \ + (r_node))) { \ + } \ + } \ +} while (0) + +#define rbtn_rotate_left(a_type, a_field, a_node, r_node) do { \ + (r_node) = rbtn_right_get(a_type, a_field, (a_node)); \ + rbtn_right_set(a_type, a_field, (a_node), \ + rbtn_left_get(a_type, a_field, (r_node))); \ + rbtn_left_set(a_type, a_field, (r_node), (a_node)); \ +} while (0) + +#define rbtn_rotate_right(a_type, a_field, a_node, r_node) do { \ + (r_node) = rbtn_left_get(a_type, a_field, (a_node)); \ + rbtn_left_set(a_type, a_field, (a_node), \ + rbtn_right_get(a_type, a_field, (r_node))); \ + rbtn_right_set(a_type, a_field, (r_node), (a_node)); \ +} while (0) + +/* + * The rb_proto() macro generates function prototypes that correspond to the + * functions generated by an equivalently parameterized call to rb_gen(). + */ + +#define rb_proto(a_attr, a_prefix, a_rbt_type, a_type) \ +a_attr void \ +a_prefix##new(a_rbt_type *rbtree); \ +a_attr a_type * \ +a_prefix##first(a_rbt_type *rbtree); \ +a_attr a_type * \ +a_prefix##last(a_rbt_type *rbtree); \ +a_attr a_type * \ +a_prefix##next(a_rbt_type *rbtree, a_type *node); \ +a_attr a_type * \ +a_prefix##prev(a_rbt_type *rbtree, a_type *node); \ +a_attr a_type * \ +a_prefix##search(a_rbt_type *rbtree, a_type *key); \ +a_attr a_type * \ +a_prefix##nsearch(a_rbt_type *rbtree, a_type *key); \ +a_attr a_type * \ +a_prefix##psearch(a_rbt_type *rbtree, a_type *key); \ +a_attr void \ +a_prefix##insert(a_rbt_type *rbtree, a_type *node); \ +a_attr void \ +a_prefix##remove(a_rbt_type *rbtree, a_type *node); \ +a_attr a_type * \ +a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \ + a_rbt_type *, a_type *, void *), void *arg); \ +a_attr a_type * \ +a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg); + +/* + * The rb_gen() macro generates a type-specific red-black tree implementation, + * based on the above cpp macros. + * + * Arguments: + * + * a_attr : Function attribute for generated functions (ex: static). + * a_prefix : Prefix for generated functions (ex: ex_). + * a_rb_type : Type for red-black tree data structure (ex: ex_t). + * a_type : Type for red-black tree node data structure (ex: ex_node_t). + * a_field : Name of red-black tree node linkage (ex: ex_link). + * a_cmp : Node comparison function name, with the following prototype: + * int (a_cmp *)(a_type *a_node, a_type *a_other); + * ^^^^^^ + * or a_key + * Interpretation of comparision function return values: + * -1 : a_node < a_other + * 0 : a_node == a_other + * 1 : a_node > a_other + * In all cases, the a_node or a_key macro argument is the first + * argument to the comparison function, which makes it possible + * to write comparison functions that treat the first argument + * specially. + * + * Assuming the following setup: + * + * typedef struct ex_node_s ex_node_t; + * struct ex_node_s { + * rb_node(ex_node_t) ex_link; + * }; + * typedef rb_tree(ex_node_t) ex_t; + * rb_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp) + * + * The following API is generated: + * + * static void + * ex_new(ex_t *extree); + * Description: Initialize a red-black tree structure. + * Args: + * extree: Pointer to an uninitialized red-black tree object. + * + * static ex_node_t * + * ex_first(ex_t *extree); + * static ex_node_t * + * ex_last(ex_t *extree); + * Description: Get the first/last node in extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * Ret: First/last node in extree, or NULL if extree is empty. + * + * static ex_node_t * + * ex_next(ex_t *extree, ex_node_t *node); + * static ex_node_t * + * ex_prev(ex_t *extree, ex_node_t *node); + * Description: Get node's successor/predecessor. + * Args: + * extree: Pointer to an initialized red-black tree object. + * node : A node in extree. + * Ret: node's successor/predecessor in extree, or NULL if node is + * last/first. + * + * static ex_node_t * + * ex_search(ex_t *extree, ex_node_t *key); + * Description: Search for node that matches key. + * Args: + * extree: Pointer to an initialized red-black tree object. + * key : Search key. + * Ret: Node in extree that matches key, or NULL if no match. + * + * static ex_node_t * + * ex_nsearch(ex_t *extree, ex_node_t *key); + * static ex_node_t * + * ex_psearch(ex_t *extree, ex_node_t *key); + * Description: Search for node that matches key. If no match is found, + * return what would be key's successor/predecessor, were + * key in extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * key : Search key. + * Ret: Node in extree that matches key, or if no match, hypothetical + * node's successor/predecessor (NULL if no successor/predecessor). + * + * static void + * ex_insert(ex_t *extree, ex_node_t *node); + * Description: Insert node into extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * node : Node to be inserted into extree. + * + * static void + * ex_remove(ex_t *extree, ex_node_t *node); + * Description: Remove node from extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * node : Node in extree to be removed. + * + * static ex_node_t * + * ex_iter(ex_t *extree, ex_node_t *start, ex_node_t *(*cb)(ex_t *, + * ex_node_t *, void *), void *arg); + * static ex_node_t * + * ex_reverse_iter(ex_t *extree, ex_node_t *start, ex_node *(*cb)(ex_t *, + * ex_node_t *, void *), void *arg); + * Description: Iterate forward/backward over extree, starting at node. + * If extree is modified, iteration must be immediately + * terminated by the callback function that causes the + * modification. + * Args: + * extree: Pointer to an initialized red-black tree object. + * start : Node at which to start iteration, or NULL to start at + * first/last node. + * cb : Callback function, which is called for each node during + * iteration. Under normal circumstances the callback function + * should return NULL, which causes iteration to continue. If a + * callback function returns non-NULL, iteration is immediately + * terminated and the non-NULL return value is returned by the + * iterator. This is useful for re-starting iteration after + * modifying extree. + * arg : Opaque pointer passed to cb(). + * Ret: NULL if iteration completed, or the non-NULL callback return value + * that caused termination of the iteration. + */ +#define rb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp) \ +a_attr void \ +a_prefix##new(a_rbt_type *rbtree) { \ + rb_new(a_type, a_field, rbtree); \ +} \ +a_attr a_type * \ +a_prefix##first(a_rbt_type *rbtree) { \ + a_type *ret; \ + rbtn_first(a_type, a_field, rbtree, rbtree->rbt_root, ret); \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##last(a_rbt_type *rbtree) { \ + a_type *ret; \ + rbtn_last(a_type, a_field, rbtree, rbtree->rbt_root, ret); \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##next(a_rbt_type *rbtree, a_type *node) { \ + a_type *ret; \ + if (rbtn_right_get(a_type, a_field, node) != &rbtree->rbt_nil) { \ + rbtn_first(a_type, a_field, rbtree, rbtn_right_get(a_type, \ + a_field, node), ret); \ + } else { \ + a_type *tnode = rbtree->rbt_root; \ + assert(tnode != &rbtree->rbt_nil); \ + ret = &rbtree->rbt_nil; \ + while (true) { \ + int cmp = (a_cmp)(node, tnode); \ + if (cmp < 0) { \ + ret = tnode; \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ + } else { \ + break; \ + } \ + assert(tnode != &rbtree->rbt_nil); \ + } \ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##prev(a_rbt_type *rbtree, a_type *node) { \ + a_type *ret; \ + if (rbtn_left_get(a_type, a_field, node) != &rbtree->rbt_nil) { \ + rbtn_last(a_type, a_field, rbtree, rbtn_left_get(a_type, \ + a_field, node), ret); \ + } else { \ + a_type *tnode = rbtree->rbt_root; \ + assert(tnode != &rbtree->rbt_nil); \ + ret = &rbtree->rbt_nil; \ + while (true) { \ + int cmp = (a_cmp)(node, tnode); \ + if (cmp < 0) { \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + ret = tnode; \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ + } else { \ + break; \ + } \ + assert(tnode != &rbtree->rbt_nil); \ + } \ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##search(a_rbt_type *rbtree, a_type *key) { \ + a_type *ret; \ + int cmp; \ + ret = rbtree->rbt_root; \ + while (ret != &rbtree->rbt_nil \ + && (cmp = (a_cmp)(key, ret)) != 0) { \ + if (cmp < 0) { \ + ret = rbtn_left_get(a_type, a_field, ret); \ + } else { \ + ret = rbtn_right_get(a_type, a_field, ret); \ + } \ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##nsearch(a_rbt_type *rbtree, a_type *key) { \ + a_type *ret; \ + a_type *tnode = rbtree->rbt_root; \ + ret = &rbtree->rbt_nil; \ + while (tnode != &rbtree->rbt_nil) { \ + int cmp = (a_cmp)(key, tnode); \ + if (cmp < 0) { \ + ret = tnode; \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ + } else { \ + ret = tnode; \ + break; \ + } \ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##psearch(a_rbt_type *rbtree, a_type *key) { \ + a_type *ret; \ + a_type *tnode = rbtree->rbt_root; \ + ret = &rbtree->rbt_nil; \ + while (tnode != &rbtree->rbt_nil) { \ + int cmp = (a_cmp)(key, tnode); \ + if (cmp < 0) { \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + ret = tnode; \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ + } else { \ + ret = tnode; \ + break; \ + } \ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr void \ +a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \ + struct { \ + a_type *node; \ + int cmp; \ + } path[sizeof(void *) << 4], *pathp; \ + rbt_node_new(a_type, a_field, rbtree, node); \ + /* Wind. */ \ + path->node = rbtree->rbt_root; \ + for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \ + int cmp = pathp->cmp = a_cmp(node, pathp->node); \ + assert(cmp != 0); \ + if (cmp < 0) { \ + pathp[1].node = rbtn_left_get(a_type, a_field, \ + pathp->node); \ + } else { \ + pathp[1].node = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + } \ + } \ + pathp->node = node; \ + /* Unwind. */ \ + for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \ + a_type *cnode = pathp->node; \ + if (pathp->cmp < 0) { \ + a_type *left = pathp[1].node; \ + rbtn_left_set(a_type, a_field, cnode, left); \ + if (rbtn_red_get(a_type, a_field, left)) { \ + a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ + if (rbtn_red_get(a_type, a_field, leftleft)) { \ + /* Fix up 4-node. */ \ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, leftleft); \ + rbtn_rotate_right(a_type, a_field, cnode, tnode); \ + cnode = tnode; \ + } \ + } else { \ + return; \ + } \ + } else { \ + a_type *right = pathp[1].node; \ + rbtn_right_set(a_type, a_field, cnode, right); \ + if (rbtn_red_get(a_type, a_field, right)) { \ + a_type *left = rbtn_left_get(a_type, a_field, cnode); \ + if (rbtn_red_get(a_type, a_field, left)) { \ + /* Split 4-node. */ \ + rbtn_black_set(a_type, a_field, left); \ + rbtn_black_set(a_type, a_field, right); \ + rbtn_red_set(a_type, a_field, cnode); \ + } else { \ + /* Lean left. */ \ + a_type *tnode; \ + bool tred = rbtn_red_get(a_type, a_field, cnode); \ + rbtn_rotate_left(a_type, a_field, cnode, tnode); \ + rbtn_color_set(a_type, a_field, tnode, tred); \ + rbtn_red_set(a_type, a_field, cnode); \ + cnode = tnode; \ + } \ + } else { \ + return; \ + } \ + } \ + pathp->node = cnode; \ + } \ + /* Set root, and make it black. */ \ + rbtree->rbt_root = path->node; \ + rbtn_black_set(a_type, a_field, rbtree->rbt_root); \ +} \ +a_attr void \ +a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ + struct { \ + a_type *node; \ + int cmp; \ + } *pathp, *nodep, path[sizeof(void *) << 4]; \ + /* Wind. */ \ + nodep = NULL; /* Silence compiler warning. */ \ + path->node = rbtree->rbt_root; \ + for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \ + int cmp = pathp->cmp = a_cmp(node, pathp->node); \ + if (cmp < 0) { \ + pathp[1].node = rbtn_left_get(a_type, a_field, \ + pathp->node); \ + } else { \ + pathp[1].node = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + if (cmp == 0) { \ + /* Find node's successor, in preparation for swap. */ \ + pathp->cmp = 1; \ + nodep = pathp; \ + for (pathp++; pathp->node != &rbtree->rbt_nil; \ + pathp++) { \ + pathp->cmp = -1; \ + pathp[1].node = rbtn_left_get(a_type, a_field, \ + pathp->node); \ + } \ + break; \ + } \ + } \ + } \ + assert(nodep->node == node); \ + pathp--; \ + if (pathp->node != node) { \ + /* Swap node with its successor. */ \ + bool tred = rbtn_red_get(a_type, a_field, pathp->node); \ + rbtn_color_set(a_type, a_field, pathp->node, \ + rbtn_red_get(a_type, a_field, node)); \ + rbtn_left_set(a_type, a_field, pathp->node, \ + rbtn_left_get(a_type, a_field, node)); \ + /* If node's successor is its right child, the following code */\ + /* will do the wrong thing for the right child pointer. */\ + /* However, it doesn't matter, because the pointer will be */\ + /* properly set when the successor is pruned. */\ + rbtn_right_set(a_type, a_field, pathp->node, \ + rbtn_right_get(a_type, a_field, node)); \ + rbtn_color_set(a_type, a_field, node, tred); \ + /* The pruned leaf node's child pointers are never accessed */\ + /* again, so don't bother setting them to nil. */\ + nodep->node = pathp->node; \ + pathp->node = node; \ + if (nodep == path) { \ + rbtree->rbt_root = nodep->node; \ + } else { \ + if (nodep[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, nodep[-1].node, \ + nodep->node); \ + } else { \ + rbtn_right_set(a_type, a_field, nodep[-1].node, \ + nodep->node); \ + } \ + } \ + } else { \ + a_type *left = rbtn_left_get(a_type, a_field, node); \ + if (left != &rbtree->rbt_nil) { \ + /* node has no successor, but it has a left child. */\ + /* Splice node out, without losing the left child. */\ + assert(rbtn_red_get(a_type, a_field, node) == false); \ + assert(rbtn_red_get(a_type, a_field, left)); \ + rbtn_black_set(a_type, a_field, left); \ + if (pathp == path) { \ + rbtree->rbt_root = left; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + left); \ + } else { \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + left); \ + } \ + } \ + return; \ + } else if (pathp == path) { \ + /* The tree only contained one node. */ \ + rbtree->rbt_root = &rbtree->rbt_nil; \ + return; \ + } \ + } \ + if (rbtn_red_get(a_type, a_field, pathp->node)) { \ + /* Prune red node, which requires no fixup. */ \ + assert(pathp[-1].cmp < 0); \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + &rbtree->rbt_nil); \ + return; \ + } \ + /* The node to be pruned is black, so unwind until balance is */\ + /* restored. */\ + pathp->node = &rbtree->rbt_nil; \ + for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \ + assert(pathp->cmp != 0); \ + if (pathp->cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp->node, \ + pathp[1].node); \ + assert(rbtn_red_get(a_type, a_field, pathp[1].node) \ + == false); \ + if (rbtn_red_get(a_type, a_field, pathp->node)) { \ + a_type *right = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + a_type *rightleft = rbtn_left_get(a_type, a_field, \ + right); \ + a_type *tnode; \ + if (rbtn_red_get(a_type, a_field, rightleft)) { \ + /* In the following diagrams, ||, //, and \\ */\ + /* indicate the path to the removed node. */\ + /* */\ + /* || */\ + /* pathp(r) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + /* */\ + rbtn_black_set(a_type, a_field, pathp->node); \ + rbtn_rotate_right(a_type, a_field, right, tnode); \ + rbtn_right_set(a_type, a_field, pathp->node, tnode);\ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + } else { \ + /* || */\ + /* pathp(r) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + /* */\ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + } \ + /* Balance restored, but rotation modified subtree */\ + /* root. */\ + assert((uintptr_t)pathp > (uintptr_t)path); \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } \ + return; \ + } else { \ + a_type *right = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + a_type *rightleft = rbtn_left_get(a_type, a_field, \ + right); \ + if (rbtn_red_get(a_type, a_field, rightleft)) { \ + /* || */\ + /* pathp(b) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, rightleft); \ + rbtn_rotate_right(a_type, a_field, right, tnode); \ + rbtn_right_set(a_type, a_field, pathp->node, tnode);\ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + /* Balance restored, but rotation modified */\ + /* subree root, which may actually be the tree */\ + /* root. */\ + if (pathp == path) { \ + /* Set root. */ \ + rbtree->rbt_root = tnode; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, \ + pathp[-1].node, tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, \ + pathp[-1].node, tnode); \ + } \ + } \ + return; \ + } else { \ + /* || */\ + /* pathp(b) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + a_type *tnode; \ + rbtn_red_set(a_type, a_field, pathp->node); \ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + pathp->node = tnode; \ + } \ + } \ + } else { \ + a_type *left; \ + rbtn_right_set(a_type, a_field, pathp->node, \ + pathp[1].node); \ + left = rbtn_left_get(a_type, a_field, pathp->node); \ + if (rbtn_red_get(a_type, a_field, left)) { \ + a_type *tnode; \ + a_type *leftright = rbtn_right_get(a_type, a_field, \ + left); \ + a_type *leftrightleft = rbtn_left_get(a_type, a_field, \ + leftright); \ + if (rbtn_red_get(a_type, a_field, leftrightleft)) { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (r) (b) */\ + /* \ */\ + /* (b) */\ + /* / */\ + /* (r) */\ + a_type *unode; \ + rbtn_black_set(a_type, a_field, leftrightleft); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + unode); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + rbtn_right_set(a_type, a_field, unode, tnode); \ + rbtn_rotate_left(a_type, a_field, unode, tnode); \ + } else { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (r) (b) */\ + /* \ */\ + /* (b) */\ + /* / */\ + /* (b) */\ + assert(leftright != &rbtree->rbt_nil); \ + rbtn_red_set(a_type, a_field, leftright); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + rbtn_black_set(a_type, a_field, tnode); \ + } \ + /* Balance restored, but rotation modified subtree */\ + /* root, which may actually be the tree root. */\ + if (pathp == path) { \ + /* Set root. */ \ + rbtree->rbt_root = tnode; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } \ + } \ + return; \ + } else if (rbtn_red_get(a_type, a_field, pathp->node)) { \ + a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ + if (rbtn_red_get(a_type, a_field, leftleft)) { \ + /* || */\ + /* pathp(r) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, pathp->node); \ + rbtn_red_set(a_type, a_field, left); \ + rbtn_black_set(a_type, a_field, leftleft); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + /* Balance restored, but rotation modified */\ + /* subtree root. */\ + assert((uintptr_t)pathp > (uintptr_t)path); \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } \ + return; \ + } else { \ + /* || */\ + /* pathp(r) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + rbtn_red_set(a_type, a_field, left); \ + rbtn_black_set(a_type, a_field, pathp->node); \ + /* Balance restored. */ \ + return; \ + } \ + } else { \ + a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ + if (rbtn_red_get(a_type, a_field, leftleft)) { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, leftleft); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + /* Balance restored, but rotation modified */\ + /* subtree root, which may actually be the tree */\ + /* root. */\ + if (pathp == path) { \ + /* Set root. */ \ + rbtree->rbt_root = tnode; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, \ + pathp[-1].node, tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, \ + pathp[-1].node, tnode); \ + } \ + } \ + return; \ + } else { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + rbtn_red_set(a_type, a_field, left); \ + } \ + } \ + } \ + } \ + /* Set root. */ \ + rbtree->rbt_root = path->node; \ + assert(rbtn_red_get(a_type, a_field, rbtree->rbt_root) == false); \ +} \ +a_attr a_type * \ +a_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + if (node == &rbtree->rbt_nil) { \ + return (&rbtree->rbt_nil); \ + } else { \ + a_type *ret; \ + if ((ret = a_prefix##iter_recurse(rbtree, rbtn_left_get(a_type, \ + a_field, node), cb, arg)) != &rbtree->rbt_nil \ + || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ + a_field, node), cb, arg)); \ + } \ +} \ +a_attr a_type * \ +a_prefix##iter_start(a_rbt_type *rbtree, a_type *start, a_type *node, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + int cmp = a_cmp(start, node); \ + if (cmp < 0) { \ + a_type *ret; \ + if ((ret = a_prefix##iter_start(rbtree, start, \ + rbtn_left_get(a_type, a_field, node), cb, arg)) != \ + &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ + a_field, node), cb, arg)); \ + } else if (cmp > 0) { \ + return (a_prefix##iter_start(rbtree, start, \ + rbtn_right_get(a_type, a_field, node), cb, arg)); \ + } else { \ + a_type *ret; \ + if ((ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ + a_field, node), cb, arg)); \ + } \ +} \ +a_attr a_type * \ +a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \ + a_rbt_type *, a_type *, void *), void *arg) { \ + a_type *ret; \ + if (start != NULL) { \ + ret = a_prefix##iter_start(rbtree, start, rbtree->rbt_root, \ + cb, arg); \ + } else { \ + ret = a_prefix##iter_recurse(rbtree, rbtree->rbt_root, cb, arg);\ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##reverse_iter_recurse(a_rbt_type *rbtree, a_type *node, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + if (node == &rbtree->rbt_nil) { \ + return (&rbtree->rbt_nil); \ + } else { \ + a_type *ret; \ + if ((ret = a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_right_get(a_type, a_field, node), cb, arg)) != \ + &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } \ +} \ +a_attr a_type * \ +a_prefix##reverse_iter_start(a_rbt_type *rbtree, a_type *start, \ + a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \ + void *arg) { \ + int cmp = a_cmp(start, node); \ + if (cmp > 0) { \ + a_type *ret; \ + if ((ret = a_prefix##reverse_iter_start(rbtree, start, \ + rbtn_right_get(a_type, a_field, node), cb, arg)) != \ + &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } else if (cmp < 0) { \ + return (a_prefix##reverse_iter_start(rbtree, start, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } else { \ + a_type *ret; \ + if ((ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } \ +} \ +a_attr a_type * \ +a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + a_type *ret; \ + if (start != NULL) { \ + ret = a_prefix##reverse_iter_start(rbtree, start, \ + rbtree->rbt_root, cb, arg); \ + } else { \ + ret = a_prefix##reverse_iter_recurse(rbtree, rbtree->rbt_root, \ + cb, arg); \ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ + return (ret); \ +} + +#endif /* RB_H_ */ diff --git a/externals/jemalloc/include/internal/stats.h b/externals/jemalloc/include/internal/stats.h new file mode 100644 index 00000000000..cbf035ff2b9 --- /dev/null +++ b/externals/jemalloc/include/internal/stats.h @@ -0,0 +1,174 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#define UMAX2S_BUFSIZE 65 + +#ifdef JEMALLOC_STATS +typedef struct tcache_bin_stats_s tcache_bin_stats_t; +typedef struct malloc_bin_stats_s malloc_bin_stats_t; +typedef struct malloc_large_stats_s malloc_large_stats_t; +typedef struct arena_stats_s arena_stats_t; +#endif +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) +typedef struct chunk_stats_s chunk_stats_t; +#endif + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#ifdef JEMALLOC_STATS + +#ifdef JEMALLOC_TCACHE +struct tcache_bin_stats_s { + /* + * Number of allocation requests that corresponded to the size of this + * bin. + */ + uint64_t nrequests; +}; +#endif + +struct malloc_bin_stats_s { + /* + * Current number of bytes allocated, including objects currently + * cached by tcache. + */ + size_t allocated; + + /* + * Total number of allocation/deallocation requests served directly by + * the bin. Note that tcache may allocate an object, then recycle it + * many times, resulting many increments to nrequests, but only one + * each to nmalloc and ndalloc. + */ + uint64_t nmalloc; + uint64_t ndalloc; + + /* + * Number of allocation requests that correspond to the size of this + * bin. This includes requests served by tcache, though tcache only + * periodically merges into this counter. + */ + uint64_t nrequests; + +#ifdef JEMALLOC_TCACHE + /* Number of tcache fills from this bin. */ + uint64_t nfills; + + /* Number of tcache flushes to this bin. */ + uint64_t nflushes; +#endif + + /* Total number of runs created for this bin's size class. */ + uint64_t nruns; + + /* + * Total number of runs reused by extracting them from the runs tree for + * this bin's size class. + */ + uint64_t reruns; + + /* High-water mark for this bin. */ + size_t highruns; + + /* Current number of runs in this bin. */ + size_t curruns; +}; + +struct malloc_large_stats_s { + /* + * Total number of allocation/deallocation requests served directly by + * the arena. Note that tcache may allocate an object, then recycle it + * many times, resulting many increments to nrequests, but only one + * each to nmalloc and ndalloc. + */ + uint64_t nmalloc; + uint64_t ndalloc; + + /* + * Number of allocation requests that correspond to this size class. + * This includes requests served by tcache, though tcache only + * periodically merges into this counter. + */ + uint64_t nrequests; + + /* High-water mark for this size class. */ + size_t highruns; + + /* Current number of runs of this size class. */ + size_t curruns; +}; + +struct arena_stats_s { + /* Number of bytes currently mapped. */ + size_t mapped; + + /* + * Total number of purge sweeps, total number of madvise calls made, + * and total pages purged in order to keep dirty unused memory under + * control. + */ + uint64_t npurge; + uint64_t nmadvise; + uint64_t purged; + + /* Per-size-category statistics. */ + size_t allocated_large; + uint64_t nmalloc_large; + uint64_t ndalloc_large; + uint64_t nrequests_large; + + /* + * One element for each possible size class, including sizes that + * overlap with bin size classes. This is necessary because ipalloc() + * sometimes has to use such large objects in order to assure proper + * alignment. + */ + malloc_large_stats_t *lstats; +}; +#endif /* JEMALLOC_STATS */ + +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) +struct chunk_stats_s { +# ifdef JEMALLOC_STATS + /* Number of chunks that were allocated. */ + uint64_t nchunks; +# endif + + /* High-water mark for number of chunks allocated. */ + size_t highchunks; + + /* + * Current number of chunks allocated. This value isn't maintained for + * any other purpose, so keep track of it in order to be able to set + * highchunks. + */ + size_t curchunks; +}; +#endif /* JEMALLOC_STATS */ + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern bool opt_stats_print; + +char *umax2s(uintmax_t x, unsigned base, char *s); +#ifdef JEMALLOC_STATS +void malloc_cprintf(void (*write)(void *, const char *), void *cbopaque, + const char *format, ...) JEMALLOC_ATTR(format(printf, 3, 4)); +void malloc_printf(const char *format, ...) + JEMALLOC_ATTR(format(printf, 1, 2)); +#endif +void stats_print(void (*write)(void *, const char *), void *cbopaque, + const char *opts); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_STATS +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +#endif /* JEMALLOC_STATS */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/internal/tcache.h b/externals/jemalloc/include/internal/tcache.h new file mode 100644 index 00000000000..c76597fafab --- /dev/null +++ b/externals/jemalloc/include/internal/tcache.h @@ -0,0 +1,380 @@ +#ifdef JEMALLOC_TCACHE +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +typedef struct tcache_bin_s tcache_bin_t; +typedef struct tcache_s tcache_t; + +/* + * Absolute maximum number of cache slots for each small bin in the thread + * cache. This is an additional constraint beyond that imposed as: twice the + * number of regions per run for this size class. + * + * This constant must be an even number. + */ +#define TCACHE_NSLOTS_SMALL_MAX 200 + +/* Number of cache slots for large size classes. */ +#define TCACHE_NSLOTS_LARGE 20 + +/* (1U << opt_lg_tcache_maxclass) is used to compute tcache_maxclass. */ +#define LG_TCACHE_MAXCLASS_DEFAULT 15 + +/* + * (1U << opt_lg_tcache_gc_sweep) is the approximate number of allocation + * events between full GC sweeps (-1: disabled). Integer rounding may cause + * the actual number to be slightly higher, since GC is performed + * incrementally. + */ +#define LG_TCACHE_GC_SWEEP_DEFAULT 13 + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +struct tcache_bin_s { +# ifdef JEMALLOC_STATS + tcache_bin_stats_t tstats; +# endif + unsigned low_water; /* Min # cached since last GC. */ + unsigned high_water; /* Max # cached since last GC. */ + unsigned ncached; /* # of cached objects. */ + unsigned ncached_max; /* Upper limit on ncached. */ + void *avail; /* Chain of available objects. */ +}; + +struct tcache_s { +# ifdef JEMALLOC_STATS + ql_elm(tcache_t) link; /* Used for aggregating stats. */ +# endif +# ifdef JEMALLOC_PROF + uint64_t prof_accumbytes;/* Cleared after arena_prof_accum() */ +# endif + arena_t *arena; /* This thread's arena. */ + unsigned ev_cnt; /* Event count since incremental GC. */ + unsigned next_gc_bin; /* Next bin to GC. */ + tcache_bin_t tbins[1]; /* Dynamically sized. */ +}; + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +extern bool opt_tcache; +extern ssize_t opt_lg_tcache_maxclass; +extern ssize_t opt_lg_tcache_gc_sweep; + +/* Map of thread-specific caches. */ +extern __thread tcache_t *tcache_tls + JEMALLOC_ATTR(tls_model("initial-exec")); + +/* + * Number of tcache bins. There are nbins small-object bins, plus 0 or more + * large-object bins. + */ +extern size_t nhbins; + +/* Maximum cached size class. */ +extern size_t tcache_maxclass; + +/* Number of tcache allocation/deallocation events between incremental GCs. */ +extern unsigned tcache_gc_incr; + +void tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache_t *tcache +#endif + ); +void tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache_t *tcache +#endif + ); +tcache_t *tcache_create(arena_t *arena); +void *tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, + size_t binind); +void tcache_destroy(tcache_t *tcache); +#ifdef JEMALLOC_STATS +void tcache_stats_merge(tcache_t *tcache, arena_t *arena); +#endif +void tcache_boot(void); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#ifndef JEMALLOC_ENABLE_INLINE +void tcache_event(tcache_t *tcache); +tcache_t *tcache_get(void); +void *tcache_alloc_easy(tcache_bin_t *tbin); +void *tcache_alloc_small(tcache_t *tcache, size_t size, bool zero); +void *tcache_alloc_large(tcache_t *tcache, size_t size, bool zero); +void tcache_dalloc_small(tcache_t *tcache, void *ptr); +void tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_)) +JEMALLOC_INLINE tcache_t * +tcache_get(void) +{ + tcache_t *tcache; + + if ((isthreaded & opt_tcache) == false) + return (NULL); + + tcache = tcache_tls; + if ((uintptr_t)tcache <= (uintptr_t)1) { + if (tcache == NULL) { + tcache = tcache_create(choose_arena()); + if (tcache == NULL) + return (NULL); + } else + return (NULL); + } + + return (tcache); +} + +JEMALLOC_INLINE void +tcache_event(tcache_t *tcache) +{ + + if (tcache_gc_incr == 0) + return; + + tcache->ev_cnt++; + assert(tcache->ev_cnt <= tcache_gc_incr); + if (tcache->ev_cnt == tcache_gc_incr) { + size_t binind = tcache->next_gc_bin; + tcache_bin_t *tbin = &tcache->tbins[binind]; + + if (tbin->low_water > 0) { + /* + * Flush (ceiling) 3/4 of the objects below the low + * water mark. + */ + if (binind < nbins) { + tcache_bin_flush_small(tbin, binind, + tbin->ncached - tbin->low_water + + (tbin->low_water >> 2) +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache +#endif + ); + } else { + tcache_bin_flush_large(tbin, binind, + tbin->ncached - tbin->low_water + + (tbin->low_water >> 2) +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache +#endif + ); + } + } + tbin->low_water = tbin->ncached; + tbin->high_water = tbin->ncached; + + tcache->next_gc_bin++; + if (tcache->next_gc_bin == nhbins) + tcache->next_gc_bin = 0; + tcache->ev_cnt = 0; + } +} + +JEMALLOC_INLINE void * +tcache_alloc_easy(tcache_bin_t *tbin) +{ + void *ret; + + if (tbin->ncached == 0) + return (NULL); + tbin->ncached--; + if (tbin->ncached < tbin->low_water) + tbin->low_water = tbin->ncached; + ret = tbin->avail; + tbin->avail = *(void **)ret; + return (ret); +} + +JEMALLOC_INLINE void * +tcache_alloc_small(tcache_t *tcache, size_t size, bool zero) +{ + void *ret; + size_t binind; + tcache_bin_t *tbin; + + binind = small_size2bin[size]; + assert(binind < nbins); + tbin = &tcache->tbins[binind]; + ret = tcache_alloc_easy(tbin); + if (ret == NULL) { + ret = tcache_alloc_small_hard(tcache, tbin, binind); + if (ret == NULL) + return (NULL); + } + assert(arena_salloc(ret) == tcache->arena->bins[binind].reg_size); + + if (zero == false) { +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ret, 0xa5, size); + else if (opt_zero) + memset(ret, 0, size); +#endif + } else + memset(ret, 0, size); + +#ifdef JEMALLOC_STATS + tbin->tstats.nrequests++; +#endif +#ifdef JEMALLOC_PROF + tcache->prof_accumbytes += tcache->arena->bins[binind].reg_size; +#endif + tcache_event(tcache); + return (ret); +} + +JEMALLOC_INLINE void * +tcache_alloc_large(tcache_t *tcache, size_t size, bool zero) +{ + void *ret; + size_t binind; + tcache_bin_t *tbin; + + size = PAGE_CEILING(size); + assert(size <= tcache_maxclass); + binind = nbins + (size >> PAGE_SHIFT) - 1; + assert(binind < nhbins); + tbin = &tcache->tbins[binind]; + ret = tcache_alloc_easy(tbin); + if (ret == NULL) { + /* + * Only allocate one large object at a time, because it's quite + * expensive to create one and not use it. + */ + ret = arena_malloc_large(tcache->arena, size, zero); + if (ret == NULL) + return (NULL); + } else { +#ifdef JEMALLOC_PROF + arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret); + size_t pageind = (unsigned)(((uintptr_t)ret - (uintptr_t)chunk) + >> PAGE_SHIFT); + chunk->map[pageind].bits |= CHUNK_MAP_CLASS_MASK; +#endif + if (zero == false) { +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ret, 0xa5, size); + else if (opt_zero) + memset(ret, 0, size); +#endif + } else + memset(ret, 0, size); + +#ifdef JEMALLOC_STATS + tbin->tstats.nrequests++; +#endif +#ifdef JEMALLOC_PROF + tcache->prof_accumbytes += size; +#endif + } + + tcache_event(tcache); + return (ret); +} + +JEMALLOC_INLINE void +tcache_dalloc_small(tcache_t *tcache, void *ptr) +{ + arena_t *arena; + arena_chunk_t *chunk; + arena_run_t *run; + arena_bin_t *bin; + tcache_bin_t *tbin; + size_t pageind, binind; + arena_chunk_map_t *mapelm; + + assert(arena_salloc(ptr) <= small_maxclass); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + arena = chunk->arena; + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapelm = &chunk->map[pageind]; + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + (mapelm->bits >> PAGE_SHIFT)) << PAGE_SHIFT)); + assert(run->magic == ARENA_RUN_MAGIC); + bin = run->bin; + binind = ((uintptr_t)bin - (uintptr_t)&arena->bins) / + sizeof(arena_bin_t); + assert(binind < nbins); + +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ptr, 0x5a, bin->reg_size); +#endif + + tbin = &tcache->tbins[binind]; + if (tbin->ncached == tbin->ncached_max) { + tcache_bin_flush_small(tbin, binind, (tbin->ncached_max >> 1) +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache +#endif + ); + } + assert(tbin->ncached < tbin->ncached_max); + *(void **)ptr = tbin->avail; + tbin->avail = ptr; + tbin->ncached++; + if (tbin->ncached > tbin->high_water) + tbin->high_water = tbin->ncached; + + tcache_event(tcache); +} + +JEMALLOC_INLINE void +tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size) +{ + arena_t *arena; + arena_chunk_t *chunk; + size_t pageind, binind; + tcache_bin_t *tbin; + arena_chunk_map_t *mapelm; + + assert((size & PAGE_MASK) == 0); + assert(arena_salloc(ptr) > small_maxclass); + assert(arena_salloc(ptr) <= tcache_maxclass); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + arena = chunk->arena; + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapelm = &chunk->map[pageind]; + binind = nbins + (size >> PAGE_SHIFT) - 1; + +#ifdef JEMALLOC_FILL + if (opt_junk) + memset(ptr, 0x5a, bin->reg_size); +#endif + + tbin = &tcache->tbins[binind]; + if (tbin->ncached == tbin->ncached_max) { + tcache_bin_flush_large(tbin, binind, (tbin->ncached_max >> 1) +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache +#endif + ); + } + assert(tbin->ncached < tbin->ncached_max); + *(void **)ptr = tbin->avail; + tbin->avail = ptr; + tbin->ncached++; + if (tbin->ncached > tbin->high_water) + tbin->high_water = tbin->ncached; + + tcache_event(tcache); +} +#endif + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ +#endif /* JEMALLOC_TCACHE */ diff --git a/externals/jemalloc/include/internal/totally_not_p_r_n.h b/externals/jemalloc/include/internal/totally_not_p_r_n.h new file mode 100644 index 00000000000..0709d708012 --- /dev/null +++ b/externals/jemalloc/include/internal/totally_not_p_r_n.h @@ -0,0 +1,60 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +/* + * Simple linear congruential pseudo-random number generator: + * + * prn(y) = (a*x + c) % m + * + * where the following constants ensure maximal period: + * + * a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4. + * c == Odd number (relatively prime to 2^n). + * m == 2^32 + * + * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints. + * + * This choice of m has the disadvantage that the quality of the bits is + * proportional to bit position. For example. the lowest bit has a cycle of 2, + * the next has a cycle of 4, etc. For this reason, we prefer to use the upper + * bits. + * + * Macro parameters: + * uint32_t r : Result. + * unsigned lg_range : (0..32], number of least significant bits to return. + * uint32_t state : Seed value. + * const uint32_t a, c : See above discussion. + */ +#define prn32(r, lg_range, state, a, c) do { \ + assert(lg_range > 0); \ + assert(lg_range <= 32); \ + \ + r = (state * (a)) + (c); \ + state = r; \ + r >>= (32 - lg_range); \ +} while (false) + +/* Same as prn32(), but 64 bits of pseudo-randomness, using uint64_t. */ +#define prn64(r, lg_range, state, a, c) do { \ + assert(lg_range > 0); \ + assert(lg_range <= 64); \ + \ + r = (state * (a)) + (c); \ + state = r; \ + r >>= (64 - lg_range); \ +} while (false) + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ diff --git a/externals/jemalloc/include/jemalloc.h b/externals/jemalloc/include/jemalloc.h new file mode 100644 index 00000000000..d9bafbfff55 --- /dev/null +++ b/externals/jemalloc/include/jemalloc.h @@ -0,0 +1,42 @@ +#ifndef JEMALLOC_H_ +#define JEMALLOC_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#define JEMALLOC_VERSION "1.0.0-0-g5523399" +#define JEMALLOC_VERSION_MAJOR 1 +#define JEMALLOC_VERSION_MINOR 0 +#define JEMALLOC_VERSION_BUGFIX 0 +#define JEMALLOC_VERSION_NREV 0 +#define JEMALLOC_VERSION_GID "5523399" + +#include "jemalloc_defs.h" +#ifndef JEMALLOC_P +# define JEMALLOC_P(s) s +#endif + +extern const char *JEMALLOC_P(malloc_options); +extern void (*JEMALLOC_P(malloc_message))(void *, const char *); + +void *JEMALLOC_P(malloc)(size_t size) JEMALLOC_ATTR(malloc); +void *JEMALLOC_P(calloc)(size_t num, size_t size) JEMALLOC_ATTR(malloc); +int JEMALLOC_P(posix_memalign)(void **memptr, size_t alignment, size_t size) + JEMALLOC_ATTR(nonnull(1)); +void *JEMALLOC_P(realloc)(void *ptr, size_t size); +void JEMALLOC_P(free)(void *ptr); + +size_t JEMALLOC_P(malloc_usable_size)(const void *ptr); +void JEMALLOC_P(malloc_stats_print)(void (*write_cb)(void *, const char *), + void *cbopaque, const char *opts); +int JEMALLOC_P(mallctl)(const char *name, void *oldp, size_t *oldlenp, + void *newp, size_t newlen); +int JEMALLOC_P(mallctlnametomib)(const char *name, size_t *mibp, + size_t *miblenp); +int JEMALLOC_P(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen); + +#ifdef __cplusplus +}; +#endif +#endif /* JEMALLOC_H_ */ diff --git a/externals/jemalloc/include/jemalloc.h.in b/externals/jemalloc/include/jemalloc.h.in new file mode 100644 index 00000000000..8ef8183686e --- /dev/null +++ b/externals/jemalloc/include/jemalloc.h.in @@ -0,0 +1,42 @@ +#ifndef JEMALLOC_H_ +#define JEMALLOC_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#define JEMALLOC_VERSION "@jemalloc_version@" +#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@ +#define JEMALLOC_VERSION_MINOR @jemalloc_version_minor@ +#define JEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@ +#define JEMALLOC_VERSION_NREV @jemalloc_version_nrev@ +#define JEMALLOC_VERSION_GID "@jemalloc_version_gid@" + +#include "jemalloc_defs@install_suffix@.h" +#ifndef JEMALLOC_P +# define JEMALLOC_P(s) s +#endif + +extern const char *JEMALLOC_P(malloc_options); +extern void (*JEMALLOC_P(malloc_message))(void *, const char *); + +void *JEMALLOC_P(malloc)(size_t size) JEMALLOC_ATTR(malloc); +void *JEMALLOC_P(calloc)(size_t num, size_t size) JEMALLOC_ATTR(malloc); +int JEMALLOC_P(posix_memalign)(void **memptr, size_t alignment, size_t size) + JEMALLOC_ATTR(nonnull(1)); +void *JEMALLOC_P(realloc)(void *ptr, size_t size); +void JEMALLOC_P(free)(void *ptr); + +size_t JEMALLOC_P(malloc_usable_size)(const void *ptr); +void JEMALLOC_P(malloc_stats_print)(void (*write_cb)(void *, const char *), + void *cbopaque, const char *opts); +int JEMALLOC_P(mallctl)(const char *name, void *oldp, size_t *oldlenp, + void *newp, size_t newlen); +int JEMALLOC_P(mallctlnametomib)(const char *name, size_t *mibp, + size_t *miblenp); +int JEMALLOC_P(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen); + +#ifdef __cplusplus +}; +#endif +#endif /* JEMALLOC_H_ */ diff --git a/externals/jemalloc/include/jemalloc_defs.h b/externals/jemalloc/include/jemalloc_defs.h new file mode 100644 index 00000000000..e8acaed3abd --- /dev/null +++ b/externals/jemalloc/include/jemalloc_defs.h @@ -0,0 +1,102 @@ +/* include/jemalloc/jemalloc_defs.h. Generated from jemalloc_defs.h.in by configure. */ +#ifndef JEMALLOC_DEFS_H_ +#define JEMALLOC_DEFS_H_ + +/* + * If JEMALLOC_PREFIX is defined, it will cause all public APIs to be prefixed. + * This makes it possible, with some care, to use multiple allocators + * simultaneously. + * + * In many cases it is more convenient to manually prefix allocator function + * calls than to let macros do it automatically, particularly when using + * multiple allocators simultaneously. Define JEMALLOC_MANGLE before + * #include'ing jemalloc.h in order to cause name mangling that corresponds to + * the API prefixing. + */ +/* #undef JEMALLOC_PREFIX */ +#if (defined(JEMALLOC_PREFIX) && defined(JEMALLOC_MANGLE)) +/* #undef JEMALLOC_P */ +#endif + +/* + * Hyper-threaded CPUs may need a special instruction inside spin loops in + * order to yield to another virtual CPU. + */ +#define CPU_SPINWAIT __asm__ volatile("pause") + +/* Defined if __attribute__((...)) syntax is supported. */ +#define JEMALLOC_HAVE_ATTR +#ifdef JEMALLOC_HAVE_ATTR +# define JEMALLOC_ATTR(s) __attribute__((s)) +#else +# define JEMALLOC_ATTR(s) +#endif + +/* + * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables + * inline functions. + */ +/* #undef JEMALLOC_DEBUG */ + +/* JEMALLOC_STATS enables statistics calculation. */ +/* #undef JEMALLOC_STATS */ + +/* JEMALLOC_PROF enables allocation profiling. */ +/* #undef JEMALLOC_PROF */ + +/* Use libunwind for profile backtracing if defined. */ +/* #undef JEMALLOC_PROF_LIBUNWIND */ + +/* Use libgcc for profile backtracing if defined. */ +/* #undef JEMALLOC_PROF_LIBGCC */ + +/* + * JEMALLOC_TINY enables support for tiny objects, which are smaller than one + * quantum. + */ +#define JEMALLOC_TINY + +/* + * JEMALLOC_TCACHE enables a thread-specific caching layer for small objects. + * This makes it possible to allocate/deallocate objects without any locking + * when the cache is in the steady state. + */ +#define JEMALLOC_TCACHE + +/* + * JEMALLOC_DSS enables use of sbrk(2) to allocate chunks from the data storage + * segment (DSS). + */ +/* #undef JEMALLOC_DSS */ + +/* JEMALLOC_SWAP enables mmap()ed swap file support. */ +/* #undef JEMALLOC_SWAP */ + +/* Support memory filling (junk/zero). */ +/* #undef JEMALLOC_FILL */ + +/* Support optional abort() on OOM. */ +/* #undef JEMALLOC_XMALLOC */ + +/* Support SYSV semantics. */ +/* #undef JEMALLOC_SYSV */ + +/* Support lazy locking (avoid locking unless a second thread is launched). */ +#define JEMALLOC_LAZY_LOCK + +/* Determine page size at run time if defined. */ +/* #undef DYNAMIC_PAGE_SHIFT */ + +/* One page is 2^STATIC_PAGE_SHIFT bytes. */ +#define STATIC_PAGE_SHIFT 12 + +/* TLS is used to map arenas and magazine caches to threads. */ +/* #undef NO_TLS */ + +/* sizeof(void *) == 2^LG_SIZEOF_PTR. */ +#define LG_SIZEOF_PTR 3 + +/* sizeof(int) == 2^LG_SIZEOF_INT. */ +#define LG_SIZEOF_INT 2 + +#endif /* JEMALLOC_DEFS_H_ */ diff --git a/externals/jemalloc/include/jemalloc_defs.h.in b/externals/jemalloc/include/jemalloc_defs.h.in new file mode 100644 index 00000000000..8b98d670acc --- /dev/null +++ b/externals/jemalloc/include/jemalloc_defs.h.in @@ -0,0 +1,101 @@ +#ifndef JEMALLOC_DEFS_H_ +#define JEMALLOC_DEFS_H_ + +/* + * If JEMALLOC_PREFIX is defined, it will cause all public APIs to be prefixed. + * This makes it possible, with some care, to use multiple allocators + * simultaneously. + * + * In many cases it is more convenient to manually prefix allocator function + * calls than to let macros do it automatically, particularly when using + * multiple allocators simultaneously. Define JEMALLOC_MANGLE before + * #include'ing jemalloc.h in order to cause name mangling that corresponds to + * the API prefixing. + */ +#undef JEMALLOC_PREFIX +#if (defined(JEMALLOC_PREFIX) && defined(JEMALLOC_MANGLE)) +#undef JEMALLOC_P +#endif + +/* + * Hyper-threaded CPUs may need a special instruction inside spin loops in + * order to yield to another virtual CPU. + */ +#undef CPU_SPINWAIT + +/* Defined if __attribute__((...)) syntax is supported. */ +#undef JEMALLOC_HAVE_ATTR +#ifdef JEMALLOC_HAVE_ATTR +# define JEMALLOC_ATTR(s) __attribute__((s)) +#else +# define JEMALLOC_ATTR(s) +#endif + +/* + * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables + * inline functions. + */ +#undef JEMALLOC_DEBUG + +/* JEMALLOC_STATS enables statistics calculation. */ +#undef JEMALLOC_STATS + +/* JEMALLOC_PROF enables allocation profiling. */ +#undef JEMALLOC_PROF + +/* Use libunwind for profile backtracing if defined. */ +#undef JEMALLOC_PROF_LIBUNWIND + +/* Use libgcc for profile backtracing if defined. */ +#undef JEMALLOC_PROF_LIBGCC + +/* + * JEMALLOC_TINY enables support for tiny objects, which are smaller than one + * quantum. + */ +#undef JEMALLOC_TINY + +/* + * JEMALLOC_TCACHE enables a thread-specific caching layer for small objects. + * This makes it possible to allocate/deallocate objects without any locking + * when the cache is in the steady state. + */ +#undef JEMALLOC_TCACHE + +/* + * JEMALLOC_DSS enables use of sbrk(2) to allocate chunks from the data storage + * segment (DSS). + */ +#undef JEMALLOC_DSS + +/* JEMALLOC_SWAP enables mmap()ed swap file support. */ +#undef JEMALLOC_SWAP + +/* Support memory filling (junk/zero). */ +#undef JEMALLOC_FILL + +/* Support optional abort() on OOM. */ +#undef JEMALLOC_XMALLOC + +/* Support SYSV semantics. */ +#undef JEMALLOC_SYSV + +/* Support lazy locking (avoid locking unless a second thread is launched). */ +#undef JEMALLOC_LAZY_LOCK + +/* Determine page size at run time if defined. */ +#undef DYNAMIC_PAGE_SHIFT + +/* One page is 2^STATIC_PAGE_SHIFT bytes. */ +#undef STATIC_PAGE_SHIFT + +/* TLS is used to map arenas and magazine caches to threads. */ +#undef NO_TLS + +/* sizeof(void *) == 2^LG_SIZEOF_PTR. */ +#undef LG_SIZEOF_PTR + +/* sizeof(int) == 2^LG_SIZEOF_INT. */ +#undef LG_SIZEOF_INT + +#endif /* JEMALLOC_DEFS_H_ */ diff --git a/externals/jemalloc/jemalloc.c b/externals/jemalloc/jemalloc.c new file mode 100644 index 00000000000..e01de0d5066 --- /dev/null +++ b/externals/jemalloc/jemalloc.c @@ -0,0 +1,1349 @@ +/*- + * This allocator implementation is designed to provide scalable performance + * for multi-threaded programs on multi-processor systems. The following + * features are included for this purpose: + * + * + Multiple arenas are used if there are multiple CPUs, which reduces lock + * contention and cache sloshing. + * + * + Thread-specific caching is used if there are multiple threads, which + * reduces the amount of locking. + * + * + Cache line sharing between arenas is avoided for internal data + * structures. + * + * + Memory is managed in chunks and runs (chunks can be split into runs), + * rather than as individual pages. This provides a constant-time + * mechanism for associating allocations with particular arenas. + * + * Allocation requests are rounded up to the nearest size class, and no record + * of the original request size is maintained. Allocations are broken into + * categories according to size class. Assuming 1 MiB chunks, 4 KiB pages and + * a 16 byte quantum on a 32-bit system, the size classes in each category are + * as follows: + * + * |========================================| + * | Category | Subcategory | Size | + * |========================================| + * | Small | Tiny | 2 | + * | | | 4 | + * | | | 8 | + * | |------------------+----------| + * | | Quantum-spaced | 16 | + * | | | 32 | + * | | | 48 | + * | | | ... | + * | | | 96 | + * | | | 112 | + * | | | 128 | + * | |------------------+----------| + * | | Cacheline-spaced | 192 | + * | | | 256 | + * | | | 320 | + * | | | 384 | + * | | | 448 | + * | | | 512 | + * | |------------------+----------| + * | | Sub-page | 760 | + * | | | 1024 | + * | | | 1280 | + * | | | ... | + * | | | 3328 | + * | | | 3584 | + * | | | 3840 | + * |========================================| + * | Large | 4 KiB | + * | | 8 KiB | + * | | 12 KiB | + * | | ... | + * | | 1012 KiB | + * | | 1016 KiB | + * | | 1020 KiB | + * |========================================| + * | Huge | 1 MiB | + * | | 2 MiB | + * | | 3 MiB | + * | | ... | + * |========================================| + * + * Different mechanisms are used accoding to category: + * + * Small: Each size class is segregated into its own set of runs. Each run + * maintains a bitmap of which regions are free/allocated. + * + * Large : Each allocation is backed by a dedicated run. Metadata are stored + * in the associated arena chunk header maps. + * + * Huge : Each allocation is backed by a dedicated contiguous set of chunks. + * Metadata are stored in a separate red-black tree. + * + ******************************************************************************* + */ + +#define JEMALLOC_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +malloc_mutex_t arenas_lock; +arena_t **arenas; +unsigned narenas; +#ifndef NO_TLS +static unsigned next_arena; +#endif + +#ifndef NO_TLS +__thread arena_t *arenas_map JEMALLOC_ATTR(tls_model("initial-exec")); +#endif + +/* Set to true once the allocator has been initialized. */ +static bool malloc_initialized = false; + +/* Used to let the initializing thread recursively allocate. */ +static pthread_t malloc_initializer = (unsigned long)0; + +/* Used to avoid initialization races. */ +static malloc_mutex_t init_lock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; + +#ifdef DYNAMIC_PAGE_SHIFT +size_t pagesize; +size_t pagesize_mask; +size_t lg_pagesize; +#endif + +unsigned ncpus; + +/* Runtime configuration options. */ +const char *JEMALLOC_P(malloc_options) + JEMALLOC_ATTR(visibility("default")); +#ifdef JEMALLOC_DEBUG +bool opt_abort = true; +# ifdef JEMALLOC_FILL +bool opt_junk = true; +# endif +#else +bool opt_abort = false; +# ifdef JEMALLOC_FILL +bool opt_junk = false; +# endif +#endif +#ifdef JEMALLOC_SYSV +bool opt_sysv = false; +#endif +#ifdef JEMALLOC_XMALLOC +bool opt_xmalloc = false; +#endif +#ifdef JEMALLOC_FILL +bool opt_zero = false; +#endif +static int opt_narenas_lshift = 0; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static void wrtmessage(void *cbopaque, const char *s); +static void stats_print_atexit(void); +static unsigned malloc_ncpus(void); +static bool malloc_init_hard(void); +static void jemalloc_prefork(void); +static void jemalloc_postfork(void); + +/******************************************************************************/ +/* malloc_message() setup. */ + +#ifdef JEMALLOC_HAVE_ATTR +JEMALLOC_ATTR(visibility("hidden")) +#else +static +#endif +void +wrtmessage(void *cbopaque, const char *s) +{ + + write(STDERR_FILENO, s, strlen(s)); +} + +void (*JEMALLOC_P(malloc_message))(void *, const char *s) + JEMALLOC_ATTR(visibility("default")) = wrtmessage; + +/******************************************************************************/ +/* + * Begin miscellaneous support functions. + */ + +/* Create a new arena and insert it into the arenas array at index ind. */ +arena_t * +arenas_extend(unsigned ind) +{ + arena_t *ret; + + /* Allocate enough space for trailing bins. */ + ret = (arena_t *)base_alloc(sizeof(arena_t) + + (sizeof(arena_bin_t) * (nbins - 1))); + if (ret != NULL && arena_new(ret, ind) == false) { + arenas[ind] = ret; + return (ret); + } + /* Only reached if there is an OOM error. */ + + /* + * OOM here is quite inconvenient to propagate, since dealing with it + * would require a check for failure in the fast path. Instead, punt + * by using arenas[0]. In practice, this is an extremely unlikely + * failure. + */ + malloc_write("<jemalloc>: Error initializing arena\n"); + if (opt_abort) + abort(); + + return (arenas[0]); +} + +#ifndef NO_TLS +/* + * Choose an arena based on a per-thread value (slow-path code only, called + * only by choose_arena()). + */ +arena_t * +choose_arena_hard(void) +{ + arena_t *ret; + + if (narenas > 1) { + malloc_mutex_lock(&arenas_lock); + if ((ret = arenas[next_arena]) == NULL) + ret = arenas_extend(next_arena); + next_arena = (next_arena + 1) % narenas; + malloc_mutex_unlock(&arenas_lock); + } else + ret = arenas[0]; + + arenas_map = ret; + + return (ret); +} +#endif + +static void +stats_print_atexit(void) +{ + +#if (defined(JEMALLOC_TCACHE) && defined(JEMALLOC_STATS)) + unsigned i; + + /* + * Merge stats from extant threads. This is racy, since individual + * threads do not lock when recording tcache stats events. As a + * consequence, the final stats may be slightly out of date by the time + * they are reported, if other threads continue to allocate. + */ + for (i = 0; i < narenas; i++) { + arena_t *arena = arenas[i]; + if (arena != NULL) { + tcache_t *tcache; + + /* + * tcache_stats_merge() locks bins, so if any code is + * introduced that acquires both arena and bin locks in + * the opposite order, deadlocks may result. + */ + malloc_mutex_lock(&arena->lock); + ql_foreach(tcache, &arena->tcache_ql, link) { + tcache_stats_merge(tcache, arena); + } + malloc_mutex_unlock(&arena->lock); + } + } +#endif + JEMALLOC_P(malloc_stats_print)(NULL, NULL, NULL); +} + +/* + * End miscellaneous support functions. + */ +/******************************************************************************/ +/* + * Begin initialization functions. + */ + +static unsigned +malloc_ncpus(void) +{ + unsigned ret; + long result; + + result = sysconf(_SC_NPROCESSORS_ONLN); + if (result == -1) { + /* Error. */ + ret = 1; + } + ret = (unsigned)result; + + return (ret); +} + +/* + * FreeBSD's pthreads implementation calls malloc(3), so the malloc + * implementation has to take pains to avoid infinite recursion during + * initialization. + */ +static inline bool +malloc_init(void) +{ + + if (malloc_initialized == false) + return (malloc_init_hard()); + + return (false); +} + +static bool +malloc_init_hard(void) +{ + unsigned i; + int linklen; + char buf[PATH_MAX + 1]; + const char *opts; + arena_t *init_arenas[1]; + + malloc_mutex_lock(&init_lock); + if (malloc_initialized || malloc_initializer == pthread_self()) { + /* + * Another thread initialized the allocator before this one + * acquired init_lock, or this thread is the initializing + * thread, and it is recursively allocating. + */ + malloc_mutex_unlock(&init_lock); + return (false); + } + if (malloc_initializer != (unsigned long)0) { + /* Busy-wait until the initializing thread completes. */ + do { + malloc_mutex_unlock(&init_lock); + CPU_SPINWAIT; + malloc_mutex_lock(&init_lock); + } while (malloc_initialized == false); + return (false); + } + +#ifdef DYNAMIC_PAGE_SHIFT + /* Get page size. */ + { + long result; + + result = sysconf(_SC_PAGESIZE); + assert(result != -1); + pagesize = (unsigned)result; + + /* + * We assume that pagesize is a power of 2 when calculating + * pagesize_mask and lg_pagesize. + */ + assert(((result - 1) & result) == 0); + pagesize_mask = result - 1; + lg_pagesize = ffs((int)result) - 1; + } +#endif + + for (i = 0; i < 3; i++) { + unsigned j; + + /* Get runtime configuration. */ + switch (i) { + case 0: + if ((linklen = readlink("/etc/jemalloc.conf", buf, + sizeof(buf) - 1)) != -1) { + /* + * Use the contents of the "/etc/jemalloc.conf" + * symbolic link's name. + */ + buf[linklen] = '\0'; + opts = buf; + } else { + /* No configuration specified. */ + buf[0] = '\0'; + opts = buf; + } + break; + case 1: + if ((opts = getenv("JEMALLOC_OPTIONS")) != NULL) { + /* + * Do nothing; opts is already initialized to + * the value of the JEMALLOC_OPTIONS + * environment variable. + */ + } else { + /* No configuration specified. */ + buf[0] = '\0'; + opts = buf; + } + break; + case 2: + if (JEMALLOC_P(malloc_options) != NULL) { + /* + * Use options that were compiled into the + * program. + */ + opts = JEMALLOC_P(malloc_options); + } else { + /* No configuration specified. */ + buf[0] = '\0'; + opts = buf; + } + break; + default: + /* NOTREACHED */ + assert(false); + buf[0] = '\0'; + opts = buf; + } + + for (j = 0; opts[j] != '\0'; j++) { + unsigned k, nreps; + bool nseen; + + /* Parse repetition count, if any. */ + for (nreps = 0, nseen = false;; j++, nseen = true) { + switch (opts[j]) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + nreps *= 10; + nreps += opts[j] - '0'; + break; + default: + goto MALLOC_OUT; + } + } +MALLOC_OUT: + if (nseen == false) + nreps = 1; + + for (k = 0; k < nreps; k++) { + switch (opts[j]) { + case 'a': + opt_abort = false; + break; + case 'A': + opt_abort = true; + break; +#ifdef JEMALLOC_PROF + case 'b': + if (opt_lg_prof_bt_max > 0) + opt_lg_prof_bt_max--; + break; + case 'B': + if (opt_lg_prof_bt_max < LG_PROF_BT_MAX) + opt_lg_prof_bt_max++; + break; +#endif + case 'c': + if (opt_lg_cspace_max - 1 > + opt_lg_qspace_max && + opt_lg_cspace_max > + LG_CACHELINE) + opt_lg_cspace_max--; + break; + case 'C': + if (opt_lg_cspace_max < PAGE_SHIFT + - 1) + opt_lg_cspace_max++; + break; + case 'd': + if (opt_lg_dirty_mult + 1 < + (sizeof(size_t) << 3)) + opt_lg_dirty_mult++; + break; + case 'D': + if (opt_lg_dirty_mult >= 0) + opt_lg_dirty_mult--; + break; +#ifdef JEMALLOC_PROF + case 'e': + opt_prof_active = false; + break; + case 'E': + opt_prof_active = true; + break; + case 'f': + opt_prof = false; + break; + case 'F': + opt_prof = true; + break; +#endif +#ifdef JEMALLOC_TCACHE + case 'g': + if (opt_lg_tcache_gc_sweep >= 0) + opt_lg_tcache_gc_sweep--; + break; + case 'G': + if (opt_lg_tcache_gc_sweep + 1 < + (sizeof(size_t) << 3)) + opt_lg_tcache_gc_sweep++; + break; + case 'h': + opt_tcache = false; + break; + case 'H': + opt_tcache = true; + break; +#endif +#ifdef JEMALLOC_PROF + case 'i': + if (opt_lg_prof_interval >= 0) + opt_lg_prof_interval--; + break; + case 'I': + if (opt_lg_prof_interval + 1 < + (sizeof(uint64_t) << 3)) + opt_lg_prof_interval++; + break; +#endif +#ifdef JEMALLOC_FILL + case 'j': + opt_junk = false; + break; + case 'J': + opt_junk = true; + break; +#endif + case 'k': + /* + * Chunks always require at least one + * header page, plus one data page. + */ + if ((1U << (opt_lg_chunk - 1)) >= + (2U << PAGE_SHIFT)) + opt_lg_chunk--; + break; + case 'K': + if (opt_lg_chunk + 1 < + (sizeof(size_t) << 3)) + opt_lg_chunk++; + break; +#ifdef JEMALLOC_PROF + case 'l': + opt_prof_leak = false; + break; + case 'L': + opt_prof_leak = true; + break; +#endif +#ifdef JEMALLOC_TCACHE + case 'm': + if (opt_lg_tcache_maxclass >= 0) + opt_lg_tcache_maxclass--; + break; + case 'M': + if (opt_lg_tcache_maxclass + 1 < + (sizeof(size_t) << 3)) + opt_lg_tcache_maxclass++; + break; +#endif + case 'n': + opt_narenas_lshift--; + break; + case 'N': + opt_narenas_lshift++; + break; +#ifdef JEMALLOC_SWAP + case 'o': + opt_overcommit = false; + break; + case 'O': + opt_overcommit = true; + break; +#endif + case 'p': + opt_stats_print = false; + break; + case 'P': + opt_stats_print = true; + break; + case 'q': + if (opt_lg_qspace_max > LG_QUANTUM) + opt_lg_qspace_max--; + break; + case 'Q': + if (opt_lg_qspace_max + 1 < + opt_lg_cspace_max) + opt_lg_qspace_max++; + break; +#ifdef JEMALLOC_PROF + case 's': + if (opt_lg_prof_sample > 0) + opt_lg_prof_sample--; + break; + case 'S': + if (opt_lg_prof_sample + 1 < + (sizeof(uint64_t) << 3)) + opt_lg_prof_sample++; + break; + case 'u': + opt_prof_udump = false; + break; + case 'U': + opt_prof_udump = true; + break; +#endif +#ifdef JEMALLOC_SYSV + case 'v': + opt_sysv = false; + break; + case 'V': + opt_sysv = true; + break; +#endif +#ifdef JEMALLOC_XMALLOC + case 'x': + opt_xmalloc = false; + break; + case 'X': + opt_xmalloc = true; + break; +#endif +#ifdef JEMALLOC_FILL + case 'z': + opt_zero = false; + break; + case 'Z': + opt_zero = true; + break; +#endif + default: { + char cbuf[2]; + + cbuf[0] = opts[j]; + cbuf[1] = '\0'; + malloc_write( + "<jemalloc>: Unsupported character " + "in malloc options: '"); + malloc_write(cbuf); + malloc_write("'\n"); + } + } + } + } + } + + /* Register fork handlers. */ + if (pthread_atfork(jemalloc_prefork, jemalloc_postfork, + jemalloc_postfork) != 0) { + malloc_write("<jemalloc>: Error in pthread_atfork()\n"); + if (opt_abort) + abort(); + } + + if (ctl_boot()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + + if (opt_stats_print) { + /* Print statistics at exit. */ + if (atexit(stats_print_atexit) != 0) { + malloc_write("<jemalloc>: Error in atexit()\n"); + if (opt_abort) + abort(); + } + } + + if (chunk_boot()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + + if (base_boot()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + +#ifdef JEMALLOC_PROF + prof_boot0(); +#endif + + if (arena_boot()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + +#ifdef JEMALLOC_TCACHE + tcache_boot(); +#endif + + if (huge_boot()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + + /* + * Create enough scaffolding to allow recursive allocation in + * malloc_ncpus(). + */ + narenas = 1; + arenas = init_arenas; + memset(arenas, 0, sizeof(arena_t *) * narenas); + + /* + * Initialize one arena here. The rest are lazily created in + * choose_arena_hard(). + */ + arenas_extend(0); + if (arenas[0] == NULL) { + malloc_mutex_unlock(&init_lock); + return (true); + } + +#ifndef NO_TLS + /* + * Assign the initial arena to the initial thread, in order to avoid + * spurious creation of an extra arena if the application switches to + * threaded mode. + */ + arenas_map = arenas[0]; +#endif + + malloc_mutex_init(&arenas_lock); + +#ifdef JEMALLOC_PROF + if (prof_boot1()) { + malloc_mutex_unlock(&init_lock); + return (true); + } +#endif + + /* Get number of CPUs. */ + malloc_initializer = pthread_self(); + malloc_mutex_unlock(&init_lock); + ncpus = malloc_ncpus(); + malloc_mutex_lock(&init_lock); + + if (ncpus > 1) { + /* + * For SMP systems, create more than one arena per CPU by + * default. + */ + opt_narenas_lshift += 2; + } + + /* Determine how many arenas to use. */ + narenas = ncpus; + if (opt_narenas_lshift > 0) { + if ((narenas << opt_narenas_lshift) > narenas) + narenas <<= opt_narenas_lshift; + /* + * Make sure not to exceed the limits of what base_alloc() can + * handle. + */ + if (narenas * sizeof(arena_t *) > chunksize) + narenas = chunksize / sizeof(arena_t *); + } else if (opt_narenas_lshift < 0) { + if ((narenas >> -opt_narenas_lshift) < narenas) + narenas >>= -opt_narenas_lshift; + /* Make sure there is at least one arena. */ + if (narenas == 0) + narenas = 1; + } + +#ifdef NO_TLS + if (narenas > 1) { + static const unsigned primes[] = {1, 3, 5, 7, 11, 13, 17, 19, + 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, + 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, + 223, 227, 229, 233, 239, 241, 251, 257, 263}; + unsigned nprimes, parenas; + + /* + * Pick a prime number of hash arenas that is more than narenas + * so that direct hashing of pthread_self() pointers tends to + * spread allocations evenly among the arenas. + */ + assert((narenas & 1) == 0); /* narenas must be even. */ + nprimes = (sizeof(primes) >> LG_SIZEOF_INT); + parenas = primes[nprimes - 1]; /* In case not enough primes. */ + for (i = 1; i < nprimes; i++) { + if (primes[i] > narenas) { + parenas = primes[i]; + break; + } + } + narenas = parenas; + } +#endif + +#ifndef NO_TLS + next_arena = 0; +#endif + + /* Allocate and initialize arenas. */ + arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas); + if (arenas == NULL) { + malloc_mutex_unlock(&init_lock); + return (true); + } + /* + * Zero the array. In practice, this should always be pre-zeroed, + * since it was just mmap()ed, but let's be sure. + */ + memset(arenas, 0, sizeof(arena_t *) * narenas); + /* Copy the pointer to the one arena that was already initialized. */ + arenas[0] = init_arenas[0]; + + malloc_initialized = true; + malloc_mutex_unlock(&init_lock); + return (false); +} + +/* + * End initialization functions. + */ +/******************************************************************************/ +/* + * Begin malloc(3)-compatible functions. + */ + +JEMALLOC_ATTR(malloc) +JEMALLOC_ATTR(visibility("default")) +void * +JEMALLOC_P(malloc)(size_t size) +{ + void *ret; +#ifdef JEMALLOC_PROF + prof_thr_cnt_t *cnt; +#endif + + if (malloc_init()) { + ret = NULL; + goto OOM; + } + + if (size == 0) { +#ifdef JEMALLOC_SYSV + if (opt_sysv == false) +#endif + size = 1; +#ifdef JEMALLOC_SYSV + else { +# ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in malloc(): " + "invalid size 0\n"); + abort(); + } +# endif + ret = NULL; + goto RETURN; + } +#endif + } + +#ifdef JEMALLOC_PROF + if (opt_prof) { + if ((cnt = prof_alloc_prep(size)) == NULL) { + ret = NULL; + goto OOM; + } + if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && size <= + small_maxclass) { + ret = imalloc(small_maxclass+1); + if (ret != NULL) + arena_prof_promoted(ret, size); + } else + ret = imalloc(size); + } else +#endif + ret = imalloc(size); + +OOM: + if (ret == NULL) { +#ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in malloc(): " + "out of memory\n"); + abort(); + } +#endif + errno = ENOMEM; + } + +#ifdef JEMALLOC_SYSV +RETURN: +#endif +#ifdef JEMALLOC_PROF + if (opt_prof && ret != NULL) + prof_malloc(ret, cnt); +#endif + return (ret); +} + +JEMALLOC_ATTR(nonnull(1)) +JEMALLOC_ATTR(visibility("default")) +int +JEMALLOC_P(posix_memalign)(void **memptr, size_t alignment, size_t size) +{ + int ret; + void *result; +#ifdef JEMALLOC_PROF + prof_thr_cnt_t *cnt; +#endif + + if (malloc_init()) + result = NULL; + else { + if (size == 0) { +#ifdef JEMALLOC_SYSV + if (opt_sysv == false) +#endif + size = 1; +#ifdef JEMALLOC_SYSV + else { +# ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in " + "posix_memalign(): invalid size " + "0\n"); + abort(); + } +# endif + result = NULL; + *memptr = NULL; + ret = 0; + goto RETURN; + } +#endif + } + + /* Make sure that alignment is a large enough power of 2. */ + if (((alignment - 1) & alignment) != 0 + || alignment < sizeof(void *)) { +#ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in " + "posix_memalign(): invalid alignment\n"); + abort(); + } +#endif + result = NULL; + ret = EINVAL; + goto RETURN; + } + +#ifdef JEMALLOC_PROF + if (opt_prof) { + if ((cnt = prof_alloc_prep(size)) == NULL) { + result = NULL; + ret = EINVAL; + } else { + if (prof_promote && (uintptr_t)cnt != + (uintptr_t)1U && size <= small_maxclass) { + result = ipalloc(alignment, + small_maxclass+1); + if (result != NULL) { + arena_prof_promoted(result, + size); + } + } else + result = ipalloc(alignment, size); + } + } else +#endif + result = ipalloc(alignment, size); + } + + if (result == NULL) { +#ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in posix_memalign(): " + "out of memory\n"); + abort(); + } +#endif + ret = ENOMEM; + goto RETURN; + } + + *memptr = result; + ret = 0; + +RETURN: +#ifdef JEMALLOC_PROF + if (opt_prof && result != NULL) + prof_malloc(result, cnt); +#endif + return (ret); +} + +JEMALLOC_ATTR(malloc) +JEMALLOC_ATTR(visibility("default")) +void * +JEMALLOC_P(calloc)(size_t num, size_t size) +{ + void *ret; + size_t num_size; +#ifdef JEMALLOC_PROF + prof_thr_cnt_t *cnt; +#endif + + if (malloc_init()) { + num_size = 0; + ret = NULL; + goto RETURN; + } + + num_size = num * size; + if (num_size == 0) { +#ifdef JEMALLOC_SYSV + if ((opt_sysv == false) && ((num == 0) || (size == 0))) +#endif + num_size = 1; +#ifdef JEMALLOC_SYSV + else { + ret = NULL; + goto RETURN; + } +#endif + /* + * Try to avoid division here. We know that it isn't possible to + * overflow during multiplication if neither operand uses any of the + * most significant half of the bits in a size_t. + */ + } else if (((num | size) & (SIZE_T_MAX << (sizeof(size_t) << 2))) + && (num_size / size != num)) { + /* size_t overflow. */ + ret = NULL; + goto RETURN; + } + +#ifdef JEMALLOC_PROF + if (opt_prof) { + if ((cnt = prof_alloc_prep(num_size)) == NULL) { + ret = NULL; + goto RETURN; + } + if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && num_size + <= small_maxclass) { + ret = icalloc(small_maxclass+1); + if (ret != NULL) + arena_prof_promoted(ret, num_size); + } else + ret = icalloc(num_size); + } else +#endif + ret = icalloc(num_size); + +RETURN: + if (ret == NULL) { +#ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in calloc(): out of " + "memory\n"); + abort(); + } +#endif + errno = ENOMEM; + } + +#ifdef JEMALLOC_PROF + if (opt_prof && ret != NULL) + prof_malloc(ret, cnt); +#endif + return (ret); +} + +JEMALLOC_ATTR(visibility("default")) +void * +JEMALLOC_P(realloc)(void *ptr, size_t size) +{ + void *ret; +#ifdef JEMALLOC_PROF + size_t old_size; + prof_thr_cnt_t *cnt, *old_cnt; +#endif + + if (size == 0) { +#ifdef JEMALLOC_SYSV + if (opt_sysv == false) +#endif + size = 1; +#ifdef JEMALLOC_SYSV + else { + if (ptr != NULL) { +#ifdef JEMALLOC_PROF + if (opt_prof) { + old_size = isalloc(ptr); + old_cnt = prof_cnt_get(ptr); + cnt = NULL; + } +#endif + idalloc(ptr); + } +#ifdef JEMALLOC_PROF + else if (opt_prof) { + old_size = 0; + old_cnt = NULL; + cnt = NULL; + } +#endif + ret = NULL; + goto RETURN; + } +#endif + } + + if (ptr != NULL) { + assert(malloc_initialized || malloc_initializer == + pthread_self()); + +#ifdef JEMALLOC_PROF + if (opt_prof) { + old_size = isalloc(ptr); + old_cnt = prof_cnt_get(ptr); + if ((cnt = prof_alloc_prep(size)) == NULL) { + ret = NULL; + goto OOM; + } + if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && + size <= small_maxclass) { + ret = iralloc(ptr, small_maxclass+1); + if (ret != NULL) + arena_prof_promoted(ret, size); + } else + ret = iralloc(ptr, size); + } else +#endif + ret = iralloc(ptr, size); + +#ifdef JEMALLOC_PROF +OOM: +#endif + if (ret == NULL) { +#ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in realloc(): " + "out of memory\n"); + abort(); + } +#endif + errno = ENOMEM; + } + } else { +#ifdef JEMALLOC_PROF + if (opt_prof) { + old_size = 0; + old_cnt = NULL; + } +#endif + if (malloc_init()) { +#ifdef JEMALLOC_PROF + if (opt_prof) + cnt = NULL; +#endif + ret = NULL; + } else { +#ifdef JEMALLOC_PROF + if (opt_prof) { + if ((cnt = prof_alloc_prep(size)) == NULL) + ret = NULL; + else { + if (prof_promote && (uintptr_t)cnt != + (uintptr_t)1U && size <= + small_maxclass) { + ret = imalloc(small_maxclass+1); + if (ret != NULL) { + arena_prof_promoted(ret, + size); + } + } else + ret = imalloc(size); + } + } else +#endif + ret = imalloc(size); + } + + if (ret == NULL) { +#ifdef JEMALLOC_XMALLOC + if (opt_xmalloc) { + malloc_write("<jemalloc>: Error in realloc(): " + "out of memory\n"); + abort(); + } +#endif + errno = ENOMEM; + } + } + +#ifdef JEMALLOC_SYSV +RETURN: +#endif +#ifdef JEMALLOC_PROF + if (opt_prof) + prof_realloc(ret, cnt, ptr, old_size, old_cnt); +#endif + return (ret); +} + +JEMALLOC_ATTR(visibility("default")) +void +JEMALLOC_P(free)(void *ptr) +{ + + if (ptr != NULL) { + assert(malloc_initialized || malloc_initializer == + pthread_self()); + +#ifdef JEMALLOC_PROF + if (opt_prof) + prof_free(ptr); +#endif + idalloc(ptr); + } +} + +/* + * End malloc(3)-compatible functions. + */ +/******************************************************************************/ +/* + * Begin non-standard functions. + */ + +JEMALLOC_ATTR(visibility("default")) +size_t +JEMALLOC_P(malloc_usable_size)(const void *ptr) +{ + size_t ret; + + assert(ptr != NULL); + ret = isalloc(ptr); + + return (ret); +} + +#ifdef JEMALLOC_SWAP +JEMALLOC_ATTR(visibility("default")) +int +JEMALLOC_P(malloc_swap_enable)(const int *fds, unsigned nfds, int prezeroed) +{ + + /* + * Make sure malloc is initialized, because we need page size, chunk + * size, etc. + */ + if (malloc_init()) + return (-1); + + return (chunk_swap_enable(fds, nfds, (prezeroed != 0)) ? -1 : 0); +} +#endif + +JEMALLOC_ATTR(visibility("default")) +void +JEMALLOC_P(malloc_stats_print)(void (*write_cb)(void *, const char *), + void *cbopaque, const char *opts) +{ + + stats_print(write_cb, cbopaque, opts); +} + +JEMALLOC_ATTR(visibility("default")) +int +JEMALLOC_P(mallctl)(const char *name, void *oldp, size_t *oldlenp, void *newp, + size_t newlen) +{ + + if (malloc_init()) + return (EAGAIN); + + return (ctl_byname(name, oldp, oldlenp, newp, newlen)); +} + +JEMALLOC_ATTR(visibility("default")) +int +JEMALLOC_P(mallctlnametomib)(const char *name, size_t *mibp, size_t *miblenp) +{ + + if (malloc_init()) + return (EAGAIN); + + return (ctl_nametomib(name, mibp, miblenp)); +} + +JEMALLOC_ATTR(visibility("default")) +int +JEMALLOC_P(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + + if (malloc_init()) + return (EAGAIN); + + return (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen)); +} + +/* + * End non-standard functions. + */ +/******************************************************************************/ + +/* + * The following functions are used by threading libraries for protection of + * malloc during fork(). These functions are only called if the program is + * running in threaded mode, so there is no need to check whether the program + * is threaded here. + */ + +static void +jemalloc_prefork(void) +{ + unsigned i; + + /* Acquire all mutexes in a safe order. */ + + malloc_mutex_lock(&arenas_lock); + for (i = 0; i < narenas; i++) { + if (arenas[i] != NULL) + malloc_mutex_lock(&arenas[i]->lock); + } + + malloc_mutex_lock(&base_mtx); + + malloc_mutex_lock(&huge_mtx); + +#ifdef JEMALLOC_DSS + malloc_mutex_lock(&dss_mtx); +#endif + +#ifdef JEMALLOC_SWAP + malloc_mutex_lock(&swap_mtx); +#endif +} + +static void +jemalloc_postfork(void) +{ + unsigned i; + + /* Release all mutexes, now that fork() has completed. */ + +#ifdef JEMALLOC_SWAP + malloc_mutex_unlock(&swap_mtx); +#endif + +#ifdef JEMALLOC_DSS + malloc_mutex_unlock(&dss_mtx); +#endif + + malloc_mutex_unlock(&huge_mtx); + + malloc_mutex_unlock(&base_mtx); + + for (i = 0; i < narenas; i++) { + if (arenas[i] != NULL) + malloc_mutex_unlock(&arenas[i]->lock); + } + malloc_mutex_unlock(&arenas_lock); +} diff --git a/externals/jemalloc/mb.c b/externals/jemalloc/mb.c new file mode 100644 index 00000000000..30a1a2e997a --- /dev/null +++ b/externals/jemalloc/mb.c @@ -0,0 +1,2 @@ +#define MB_C_ +#include "jemalloc/internal/jemalloc_internal.h" diff --git a/externals/jemalloc/mutex.c b/externals/jemalloc/mutex.c new file mode 100644 index 00000000000..3b6081a4c4f --- /dev/null +++ b/externals/jemalloc/mutex.c @@ -0,0 +1,70 @@ +#define JEMALLOC_MUTEX_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ +/* Data. */ + +#ifdef JEMALLOC_LAZY_LOCK +bool isthreaded = false; +#endif + +#ifdef JEMALLOC_LAZY_LOCK +static void pthread_create_once(void); +#endif + +/******************************************************************************/ +/* + * We intercept pthread_create() calls in order to toggle isthreaded if the + * process goes multi-threaded. + */ + +#ifdef JEMALLOC_LAZY_LOCK +static int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *, + void *(*)(void *), void *__restrict); + +static void +pthread_create_once(void) +{ + + pthread_create_fptr = dlsym(RTLD_NEXT, "pthread_create"); + if (pthread_create_fptr == NULL) { + malloc_write("<jemalloc>: Error in dlsym(RTLD_NEXT, " + "\"pthread_create\")\n"); + abort(); + } + + isthreaded = true; +} + +JEMALLOC_ATTR(visibility("default")) +int +pthread_create(pthread_t *__restrict thread, + const pthread_attr_t *__restrict attr, void *(*start_routine)(void *), + void *__restrict arg) +{ + static pthread_once_t once_control = PTHREAD_ONCE_INIT; + + pthread_once(&once_control, pthread_create_once); + + return (pthread_create_fptr(thread, attr, start_routine, arg)); +} +#endif + +/******************************************************************************/ + +bool +malloc_mutex_init(malloc_mutex_t *mutex) +{ + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) + return (true); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + if (pthread_mutex_init(mutex, &attr) != 0) { + pthread_mutexattr_destroy(&attr); + return (true); + } + pthread_mutexattr_destroy(&attr); + + return (false); +} diff --git a/externals/jemalloc/prof.c b/externals/jemalloc/prof.c new file mode 100644 index 00000000000..6326188e50f --- /dev/null +++ b/externals/jemalloc/prof.c @@ -0,0 +1,1328 @@ +#define JEMALLOC_PROF_C_ +#include "jemalloc/internal/jemalloc_internal.h" +#ifdef JEMALLOC_PROF +/******************************************************************************/ + +#ifdef JEMALLOC_PROF_LIBGCC +#include <unwind.h> +#endif + +#ifdef JEMALLOC_PROF_LIBUNWIND +#define UNW_LOCAL_ONLY +#include <libunwind.h> +#endif + +#include <math.h> + +/******************************************************************************/ +/* Data. */ + +bool opt_prof = false; +bool opt_prof_active = true; +size_t opt_lg_prof_bt_max = LG_PROF_BT_MAX_DEFAULT; +size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT; +ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT; +bool opt_prof_udump = false; +bool opt_prof_leak = false; + +uint64_t prof_interval; +bool prof_promote; + +/* + * Global hash of (prof_bt_t *)-->(prof_ctx_t *). This is the master data + * structure that knows about all backtraces ever captured. + */ +static ckh_t bt2ctx; +static malloc_mutex_t bt2ctx_mtx; + +/* + * Thread-specific hash of (prof_bt_t *)-->(prof_thr_cnt_t *). Each thread + * keeps a cache of backtraces, with associated thread-specific prof_thr_cnt_t + * objects. Other threads may read the prof_thr_cnt_t contents, but no others + * will ever write them. + * + * Upon thread exit, the thread must merge all the prof_thr_cnt_t counter data + * into the associated prof_ctx_t objects, and unlink/free the prof_thr_cnt_t + * objects. + */ +static __thread ckh_t *bt2cnt_tls JEMALLOC_ATTR(tls_model("initial-exec")); + +/* + * Same contents as b2cnt, but initialized such that the TSD destructor is + * called when a thread exits, so that bt2cnt_tls contents can be merged, + * unlinked, and deallocated. + */ +static pthread_key_t bt2cnt_tsd; + +/* (1U << opt_lg_prof_bt_max). */ +static unsigned prof_bt_max; + +static __thread uint64_t prof_sample_prn_state + JEMALLOC_ATTR(tls_model("initial-exec")); +static __thread uint64_t prof_sample_threshold + JEMALLOC_ATTR(tls_model("initial-exec")); +static __thread uint64_t prof_sample_accum + JEMALLOC_ATTR(tls_model("initial-exec")); + +static malloc_mutex_t prof_dump_seq_mtx; +static uint64_t prof_dump_seq; +static uint64_t prof_dump_iseq; +static uint64_t prof_dump_mseq; +static uint64_t prof_dump_useq; + +/* + * This buffer is rather large for stack allocation, so use a single buffer for + * all profile dumps. The buffer is implicitly protected by bt2ctx_mtx, since + * it must be locked anyway during dumping. + */ +static char prof_dump_buf[PROF_DUMP_BUF_SIZE]; +static unsigned prof_dump_buf_end; +static int prof_dump_fd; + +/* Do not dump any profiles until bootstrapping is complete. */ +static bool prof_booted = false; + +static malloc_mutex_t enq_mtx; +static bool enq; +static bool enq_idump; +static bool enq_udump; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static prof_bt_t *bt_dup(prof_bt_t *bt); +static void bt_init(prof_bt_t *bt, void **vec); +#ifdef JEMALLOC_PROF_LIBGCC +static _Unwind_Reason_Code prof_unwind_init_callback( + struct _Unwind_Context *context, void *arg); +static _Unwind_Reason_Code prof_unwind_callback( + struct _Unwind_Context *context, void *arg); +#endif +static void prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max); +static prof_thr_cnt_t *prof_lookup(prof_bt_t *bt); +static void prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt); +static bool prof_flush(bool propagate_err); +static bool prof_write(const char *s, bool propagate_err); +static void prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all, + size_t *leak_nctx); +static bool prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt, + bool propagate_err); +static bool prof_dump_maps(bool propagate_err); +static bool prof_dump(const char *filename, bool leakcheck, + bool propagate_err); +static void prof_dump_filename(char *filename, char v, int64_t vseq); +static void prof_fdump(void); +static void prof_bt_hash(const void *key, unsigned minbits, size_t *hash1, + size_t *hash2); +static bool prof_bt_keycomp(const void *k1, const void *k2); +static void bt2cnt_thread_cleanup(void *arg); + +/******************************************************************************/ + +static void +bt_init(prof_bt_t *bt, void **vec) +{ + + bt->vec = vec; + bt->len = 0; +} + +static prof_bt_t * +bt_dup(prof_bt_t *bt) +{ + prof_bt_t *ret; + + /* + * Create a single allocation that has space for vec immediately + * following the prof_bt_t structure. The backtraces that get + * stored in the backtrace caches are copied from stack-allocated + * temporary variables, so size is known at creation time. Making this + * a contiguous object improves cache locality. + */ + ret = (prof_bt_t *)imalloc(QUANTUM_CEILING(sizeof(prof_bt_t)) + + (bt->len * sizeof(void *))); + if (ret == NULL) + return (NULL); + ret->vec = (void **)((uintptr_t)ret + + QUANTUM_CEILING(sizeof(prof_bt_t))); + memcpy(ret->vec, bt->vec, bt->len * sizeof(void *)); + ret->len = bt->len; + + return (ret); +} + +static inline void +prof_enter(void) +{ + + malloc_mutex_lock(&enq_mtx); + enq = true; + malloc_mutex_unlock(&enq_mtx); + + malloc_mutex_lock(&bt2ctx_mtx); +} + +static inline void +prof_leave(void) +{ + bool idump, udump; + + malloc_mutex_unlock(&bt2ctx_mtx); + + malloc_mutex_lock(&enq_mtx); + enq = false; + idump = enq_idump; + enq_idump = false; + udump = enq_udump; + enq_udump = false; + malloc_mutex_unlock(&enq_mtx); + + if (idump) + prof_idump(); + if (udump) + prof_udump(); +} + +#ifdef JEMALLOC_PROF_LIBGCC +static _Unwind_Reason_Code +prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) +{ + + return (_URC_NO_REASON); +} + +static _Unwind_Reason_Code +prof_unwind_callback(struct _Unwind_Context *context, void *arg) +{ + prof_unwind_data_t *data = (prof_unwind_data_t *)arg; + + if (data->nignore > 0) + data->nignore--; + else { + data->bt->vec[data->bt->len] = (void *)_Unwind_GetIP(context); + data->bt->len++; + if (data->bt->len == data->max) + return (_URC_END_OF_STACK); + } + + return (_URC_NO_REASON); +} + +static void +prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max) +{ + prof_unwind_data_t data = {bt, nignore, max}; + + _Unwind_Backtrace(prof_unwind_callback, &data); +} +#elif defined(JEMALLOC_PROF_LIBUNWIND) +static void +prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max) +{ + unw_context_t uc; + unw_cursor_t cursor; + unsigned i; + int err; + + assert(bt->len == 0); + assert(bt->vec != NULL); + assert(max <= (1U << opt_lg_prof_bt_max)); + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + + /* Throw away (nignore+1) stack frames, if that many exist. */ + for (i = 0; i < nignore + 1; i++) { + err = unw_step(&cursor); + if (err <= 0) + return; + } + + /* + * Iterate over stack frames until there are no more. Heap-allocate + * and iteratively grow a larger bt if necessary. + */ + for (i = 0; i < max; i++) { + unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)&bt->vec[i]); + err = unw_step(&cursor); + if (err <= 0) { + bt->len = i; + break; + } + } +} +#else +static void +prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max) +{ +#define NIGNORE 3 +#define BT_FRAME(i) \ + if ((i) < NIGNORE + max) { \ + void *p; \ + if (__builtin_frame_address(i) == 0) \ + return; \ + p = __builtin_return_address(i); \ + if (p == NULL) \ + return; \ + if (i >= NIGNORE) { \ + bt->vec[(i) - NIGNORE] = p; \ + bt->len = (i) - NIGNORE + 1; \ + } \ + } else \ + return; + + assert(max <= (1U << opt_lg_prof_bt_max)); + + /* + * Ignore the first three frames, since they are: + * + * 0: prof_backtrace() + * 1: prof_alloc_prep() + * 2: malloc(), calloc(), etc. + */ +#if 1 + assert(nignore + 1 == NIGNORE); +#else + BT_FRAME(0) + BT_FRAME(1) + BT_FRAME(2) +#endif + BT_FRAME(3) + BT_FRAME(4) + BT_FRAME(5) + BT_FRAME(6) + BT_FRAME(7) + BT_FRAME(8) + BT_FRAME(9) + + BT_FRAME(10) + BT_FRAME(11) + BT_FRAME(12) + BT_FRAME(13) + BT_FRAME(14) + BT_FRAME(15) + BT_FRAME(16) + BT_FRAME(17) + BT_FRAME(18) + BT_FRAME(19) + + BT_FRAME(20) + BT_FRAME(21) + BT_FRAME(22) + BT_FRAME(23) + BT_FRAME(24) + BT_FRAME(25) + BT_FRAME(26) + BT_FRAME(27) + BT_FRAME(28) + BT_FRAME(29) + + BT_FRAME(30) + BT_FRAME(31) + BT_FRAME(32) + BT_FRAME(33) + BT_FRAME(34) + BT_FRAME(35) + BT_FRAME(36) + BT_FRAME(37) + BT_FRAME(38) + BT_FRAME(39) + + BT_FRAME(40) + BT_FRAME(41) + BT_FRAME(42) + BT_FRAME(43) + BT_FRAME(44) + BT_FRAME(45) + BT_FRAME(46) + BT_FRAME(47) + BT_FRAME(48) + BT_FRAME(49) + + BT_FRAME(50) + BT_FRAME(51) + BT_FRAME(52) + BT_FRAME(53) + BT_FRAME(54) + BT_FRAME(55) + BT_FRAME(56) + BT_FRAME(57) + BT_FRAME(58) + BT_FRAME(59) + + BT_FRAME(60) + BT_FRAME(61) + BT_FRAME(62) + BT_FRAME(63) + BT_FRAME(64) + BT_FRAME(65) + BT_FRAME(66) + BT_FRAME(67) + BT_FRAME(68) + BT_FRAME(69) + + BT_FRAME(70) + BT_FRAME(71) + BT_FRAME(72) + BT_FRAME(73) + BT_FRAME(74) + BT_FRAME(75) + BT_FRAME(76) + BT_FRAME(77) + BT_FRAME(78) + BT_FRAME(79) + + BT_FRAME(80) + BT_FRAME(81) + BT_FRAME(82) + BT_FRAME(83) + BT_FRAME(84) + BT_FRAME(85) + BT_FRAME(86) + BT_FRAME(87) + BT_FRAME(88) + BT_FRAME(89) + + BT_FRAME(90) + BT_FRAME(91) + BT_FRAME(92) + BT_FRAME(93) + BT_FRAME(94) + BT_FRAME(95) + BT_FRAME(96) + BT_FRAME(97) + BT_FRAME(98) + BT_FRAME(99) + + BT_FRAME(100) + BT_FRAME(101) + BT_FRAME(102) + BT_FRAME(103) + BT_FRAME(104) + BT_FRAME(105) + BT_FRAME(106) + BT_FRAME(107) + BT_FRAME(108) + BT_FRAME(109) + + BT_FRAME(110) + BT_FRAME(111) + BT_FRAME(112) + BT_FRAME(113) + BT_FRAME(114) + BT_FRAME(115) + BT_FRAME(116) + BT_FRAME(117) + BT_FRAME(118) + BT_FRAME(119) + + BT_FRAME(120) + BT_FRAME(121) + BT_FRAME(122) + BT_FRAME(123) + BT_FRAME(124) + BT_FRAME(125) + BT_FRAME(126) + BT_FRAME(127) + + /* Extras to compensate for NIGNORE. */ + BT_FRAME(128) + BT_FRAME(129) + BT_FRAME(130) +#undef BT_FRAME +} +#endif + +static prof_thr_cnt_t * +prof_lookup(prof_bt_t *bt) +{ + prof_thr_cnt_t *ret; + ckh_t *bt2cnt = bt2cnt_tls; + + if (bt2cnt == NULL) { + /* Initialize an empty cache for this thread. */ + bt2cnt = (ckh_t *)imalloc(sizeof(ckh_t)); + if (bt2cnt == NULL) + return (NULL); + if (ckh_new(bt2cnt, PROF_CKH_MINITEMS, prof_bt_hash, + prof_bt_keycomp)) { + idalloc(bt2cnt); + return (NULL); + } + bt2cnt_tls = bt2cnt; + } + + if (ckh_search(bt2cnt, bt, NULL, (void **)&ret)) { + prof_bt_t *btkey; + prof_ctx_t *ctx; + + /* + * This thread's cache lacks bt. Look for it in the global + * cache. + */ + prof_enter(); + if (ckh_search(&bt2ctx, bt, (void **)&btkey, (void **)&ctx)) { + + /* bt has never been seen before. Insert it. */ + ctx = (prof_ctx_t *)imalloc(sizeof(prof_ctx_t)); + if (ctx == NULL) { + prof_leave(); + return (NULL); + } + btkey = bt_dup(bt); + if (btkey == NULL) { + prof_leave(); + idalloc(ctx); + return (NULL); + } + if (malloc_mutex_init(&ctx->lock)) { + prof_leave(); + idalloc(btkey); + idalloc(ctx); + return (NULL); + } + memset(&ctx->cnt_merged, 0, sizeof(prof_cnt_t)); + ql_new(&ctx->cnts_ql); + if (ckh_insert(&bt2ctx, btkey, ctx)) { + /* OOM. */ + prof_leave(); + idalloc(btkey); + idalloc(ctx); + return (NULL); + } + } + prof_leave(); + + /* Link a prof_thd_cnt_t into ctx for this thread. */ + ret = (prof_thr_cnt_t *)imalloc(sizeof(prof_thr_cnt_t)); + if (ret == NULL) + return (NULL); + ql_elm_new(ret, link); + ret->ctx = ctx; + ret->epoch = 0; + memset(&ret->cnts, 0, sizeof(prof_cnt_t)); + if (ckh_insert(bt2cnt, btkey, ret)) { + idalloc(ret); + return (NULL); + } + malloc_mutex_lock(&ctx->lock); + ql_tail_insert(&ctx->cnts_ql, ret, link); + malloc_mutex_unlock(&ctx->lock); + } + + return (ret); +} + +static inline void +prof_sample_threshold_update(void) +{ + uint64_t r; + double u; + + /* + * Compute prof_sample_threshold as a geometrically distributed random + * variable with mean (2^opt_lg_prof_sample). + */ + prn64(r, 53, prof_sample_prn_state, (uint64_t)1125899906842625LLU, + 1058392653243283975); + u = (double)r * (1.0/9007199254740992.0L); + prof_sample_threshold = (uint64_t)(log(u) / + log(1.0 - (1.0 / (double)((uint64_t)1U << opt_lg_prof_sample)))) + + (uint64_t)1U; +} + +prof_thr_cnt_t * +prof_alloc_prep(size_t size) +{ + prof_thr_cnt_t *ret; + void *vec[prof_bt_max]; + prof_bt_t bt; + + if (opt_prof_active == false) { + /* Sampling is currently inactive, so avoid sampling. */ + ret = (prof_thr_cnt_t *)(uintptr_t)1U; + } else if (opt_lg_prof_sample == 0) { + /* + * Don't bother with sampling logic, since sampling interval is + * 1. + */ + bt_init(&bt, vec); + prof_backtrace(&bt, 2, prof_bt_max); + ret = prof_lookup(&bt); + } else { + if (prof_sample_threshold == 0) { + /* + * Initialize. Seed the prng differently for each + * thread. + */ + prof_sample_prn_state = (uint64_t)(uintptr_t)&size; + prof_sample_threshold_update(); + } + + /* + * Determine whether to capture a backtrace based on whether + * size is enough for prof_accum to reach + * prof_sample_threshold. However, delay updating these + * variables until prof_{m,re}alloc(), because we don't know + * for sure that the allocation will succeed. + * + * Use subtraction rather than addition to avoid potential + * integer overflow. + */ + if (size >= prof_sample_threshold - prof_sample_accum) { + bt_init(&bt, vec); + prof_backtrace(&bt, 2, prof_bt_max); + ret = prof_lookup(&bt); + } else + ret = (prof_thr_cnt_t *)(uintptr_t)1U; + } + + return (ret); +} + +prof_thr_cnt_t * +prof_cnt_get(const void *ptr) +{ + prof_thr_cnt_t *ret; + arena_chunk_t *chunk; + + assert(ptr != NULL); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk != ptr) { + /* Region. */ + assert(chunk->arena->magic == ARENA_MAGIC); + + ret = arena_prof_cnt_get(ptr); + } else + ret = huge_prof_cnt_get(ptr); + + return (ret); +} + +static void +prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt) +{ + arena_chunk_t *chunk; + + assert(ptr != NULL); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk != ptr) { + /* Region. */ + assert(chunk->arena->magic == ARENA_MAGIC); + + arena_prof_cnt_set(ptr, cnt); + } else + huge_prof_cnt_set(ptr, cnt); +} + +static inline void +prof_sample_accum_update(size_t size) +{ + + if (opt_lg_prof_sample == 0) { + /* + * Don't bother with sampling logic, since sampling interval is + * 1. + */ + return; + } + + /* Take care to avoid integer overflow. */ + if (size >= prof_sample_threshold - prof_sample_accum) { + prof_sample_accum -= (prof_sample_threshold - size); + /* Compute new prof_sample_threshold. */ + prof_sample_threshold_update(); + while (prof_sample_accum >= prof_sample_threshold) { + prof_sample_accum -= prof_sample_threshold; + prof_sample_threshold_update(); + } + } else + prof_sample_accum += size; +} + +void +prof_malloc(const void *ptr, prof_thr_cnt_t *cnt) +{ + size_t size = isalloc(ptr); + + assert(ptr != NULL); + + prof_cnt_set(ptr, cnt); + prof_sample_accum_update(size); + + if ((uintptr_t)cnt > (uintptr_t)1U) { + cnt->epoch++; + /*********/ + mb_write(); + /*********/ + cnt->cnts.curobjs++; + cnt->cnts.curbytes += size; + cnt->cnts.accumobjs++; + cnt->cnts.accumbytes += size; + /*********/ + mb_write(); + /*********/ + cnt->epoch++; + /*********/ + mb_write(); + /*********/ + } +} + +void +prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr, + size_t old_size, prof_thr_cnt_t *old_cnt) +{ + size_t size = isalloc(ptr); + + if (ptr != NULL) { + prof_cnt_set(ptr, cnt); + prof_sample_accum_update(size); + } + + if ((uintptr_t)old_cnt > (uintptr_t)1U) + old_cnt->epoch++; + if ((uintptr_t)cnt > (uintptr_t)1U) + cnt->epoch++; + /*********/ + mb_write(); + /*********/ + if ((uintptr_t)old_cnt > (uintptr_t)1U) { + old_cnt->cnts.curobjs--; + old_cnt->cnts.curbytes -= old_size; + } + if ((uintptr_t)cnt > (uintptr_t)1U) { + cnt->cnts.curobjs++; + cnt->cnts.curbytes += size; + cnt->cnts.accumobjs++; + cnt->cnts.accumbytes += size; + } + /*********/ + mb_write(); + /*********/ + if ((uintptr_t)old_cnt > (uintptr_t)1U) + old_cnt->epoch++; + if ((uintptr_t)cnt > (uintptr_t)1U) + cnt->epoch++; + /*********/ + mb_write(); /* Not strictly necessary. */ +} + +void +prof_free(const void *ptr) +{ + prof_thr_cnt_t *cnt = prof_cnt_get(ptr); + + if ((uintptr_t)cnt > (uintptr_t)1) { + size_t size = isalloc(ptr); + + cnt->epoch++; + /*********/ + mb_write(); + /*********/ + cnt->cnts.curobjs--; + cnt->cnts.curbytes -= size; + /*********/ + mb_write(); + /*********/ + cnt->epoch++; + /*********/ + mb_write(); + /*********/ + } +} + +static bool +prof_flush(bool propagate_err) +{ + bool ret = false; + ssize_t err; + + err = write(prof_dump_fd, prof_dump_buf, prof_dump_buf_end); + if (err == -1) { + if (propagate_err == false) { + malloc_write("<jemalloc>: write() failed during heap " + "profile flush\n"); + if (opt_abort) + abort(); + } + ret = true; + } + prof_dump_buf_end = 0; + + return (ret); +} + +static bool +prof_write(const char *s, bool propagate_err) +{ + unsigned i, slen, n; + + i = 0; + slen = strlen(s); + while (i < slen) { + /* Flush the buffer if it is full. */ + if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE) + if (prof_flush(propagate_err) && propagate_err) + return (true); + + if (prof_dump_buf_end + slen <= PROF_DUMP_BUF_SIZE) { + /* Finish writing. */ + n = slen - i; + } else { + /* Write as much of s as will fit. */ + n = PROF_DUMP_BUF_SIZE - prof_dump_buf_end; + } + memcpy(&prof_dump_buf[prof_dump_buf_end], &s[i], n); + prof_dump_buf_end += n; + i += n; + } + + return (false); +} + +static void +prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx) +{ + prof_thr_cnt_t *thr_cnt; + prof_cnt_t tcnt; + + malloc_mutex_lock(&ctx->lock); + + memcpy(&ctx->cnt_dump, &ctx->cnt_merged, sizeof(prof_cnt_t)); + ql_foreach(thr_cnt, &ctx->cnts_ql, link) { + volatile unsigned *epoch = &thr_cnt->epoch; + + while (true) { + unsigned epoch0 = *epoch; + + /* Make sure epoch is even. */ + if (epoch0 & 1U) + continue; + + memcpy(&tcnt, &thr_cnt->cnts, sizeof(prof_cnt_t)); + + /* Terminate if epoch didn't change while reading. */ + if (*epoch == epoch0) + break; + } + + ctx->cnt_dump.curobjs += tcnt.curobjs; + ctx->cnt_dump.curbytes += tcnt.curbytes; + ctx->cnt_dump.accumobjs += tcnt.accumobjs; + ctx->cnt_dump.accumbytes += tcnt.accumbytes; + + if (tcnt.curobjs != 0) + (*leak_nctx)++; + } + + /* Merge into cnt_all. */ + cnt_all->curobjs += ctx->cnt_dump.curobjs; + cnt_all->curbytes += ctx->cnt_dump.curbytes; + cnt_all->accumobjs += ctx->cnt_dump.accumobjs; + cnt_all->accumbytes += ctx->cnt_dump.accumbytes; + + malloc_mutex_unlock(&ctx->lock); +} + +static bool +prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt, bool propagate_err) +{ + char buf[UMAX2S_BUFSIZE]; + unsigned i; + + if (prof_write(umax2s(ctx->cnt_dump.curobjs, 10, buf), propagate_err) + || prof_write(": ", propagate_err) + || prof_write(umax2s(ctx->cnt_dump.curbytes, 10, buf), + propagate_err) + || prof_write(" [", propagate_err) + || prof_write(umax2s(ctx->cnt_dump.accumobjs, 10, buf), + propagate_err) + || prof_write(": ", propagate_err) + || prof_write(umax2s(ctx->cnt_dump.accumbytes, 10, buf), + propagate_err) + || prof_write("] @", propagate_err)) + return (true); + + for (i = 0; i < bt->len; i++) { + if (prof_write(" 0x", propagate_err) + || prof_write(umax2s((uintptr_t)bt->vec[i], 16, buf), + propagate_err)) + return (true); + } + + if (prof_write("\n", propagate_err)) + return (true); + + return (false); +} + +static bool +prof_dump_maps(bool propagate_err) +{ + int mfd; + char buf[UMAX2S_BUFSIZE]; + char *s; + unsigned i, slen; + /* /proc/<pid>/maps\0 */ + char mpath[6 + UMAX2S_BUFSIZE + + 5 + 1]; + + i = 0; + + s = "/proc/"; + slen = strlen(s); + memcpy(&mpath[i], s, slen); + i += slen; + + s = umax2s(getpid(), 10, buf); + slen = strlen(s); + memcpy(&mpath[i], s, slen); + i += slen; + + s = "/maps"; + slen = strlen(s); + memcpy(&mpath[i], s, slen); + i += slen; + + mpath[i] = '\0'; + + mfd = open(mpath, O_RDONLY); + if (mfd != -1) { + ssize_t nread; + + if (prof_write("\nMAPPED_LIBRARIES:\n", propagate_err) && + propagate_err) + return (true); + nread = 0; + do { + prof_dump_buf_end += nread; + if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE) { + /* Make space in prof_dump_buf before read(). */ + if (prof_flush(propagate_err) && propagate_err) + return (true); + } + nread = read(mfd, &prof_dump_buf[prof_dump_buf_end], + PROF_DUMP_BUF_SIZE - prof_dump_buf_end); + } while (nread > 0); + close(mfd); + } else + return (true); + + return (false); +} + +static bool +prof_dump(const char *filename, bool leakcheck, bool propagate_err) +{ + prof_cnt_t cnt_all; + size_t tabind; + prof_bt_t *bt; + prof_ctx_t *ctx; + char buf[UMAX2S_BUFSIZE]; + size_t leak_nctx; + + prof_enter(); + prof_dump_fd = creat(filename, 0644); + if (prof_dump_fd == -1) { + if (propagate_err == false) { + malloc_write("<jemalloc>: creat(\""); + malloc_write(filename); + malloc_write("\", 0644) failed\n"); + if (opt_abort) + abort(); + } + goto ERROR; + } + + /* Merge per thread profile stats, and sum them in cnt_all. */ + memset(&cnt_all, 0, sizeof(prof_cnt_t)); + leak_nctx = 0; + for (tabind = 0; ckh_iter(&bt2ctx, &tabind, NULL, (void **)&ctx) + == false;) { + prof_ctx_merge(ctx, &cnt_all, &leak_nctx); + } + + /* Dump profile header. */ + if (prof_write("heap profile: ", propagate_err) + || prof_write(umax2s(cnt_all.curobjs, 10, buf), propagate_err) + || prof_write(": ", propagate_err) + || prof_write(umax2s(cnt_all.curbytes, 10, buf), propagate_err) + || prof_write(" [", propagate_err) + || prof_write(umax2s(cnt_all.accumobjs, 10, buf), propagate_err) + || prof_write(": ", propagate_err) + || prof_write(umax2s(cnt_all.accumbytes, 10, buf), propagate_err)) + goto ERROR; + + if (opt_lg_prof_sample == 0) { + if (prof_write("] @ heapprofile\n", propagate_err)) + goto ERROR; + } else { + if (prof_write("] @ heap_v2/", propagate_err) + || prof_write(umax2s((uint64_t)1U << opt_lg_prof_sample, 10, + buf), propagate_err) + || prof_write("\n", propagate_err)) + goto ERROR; + } + + /* Dump per ctx profile stats. */ + for (tabind = 0; ckh_iter(&bt2ctx, &tabind, (void **)&bt, (void **)&ctx) + == false;) { + if (prof_dump_ctx(ctx, bt, propagate_err)) + goto ERROR; + } + + /* Dump /proc/<pid>/maps if possible. */ + if (prof_dump_maps(propagate_err)) + goto ERROR; + + if (prof_flush(propagate_err)) + goto ERROR; + close(prof_dump_fd); + prof_leave(); + + if (leakcheck && cnt_all.curbytes != 0) { + malloc_write("<jemalloc>: Leak summary: "); + malloc_write(umax2s(cnt_all.curbytes, 10, buf)); + malloc_write((cnt_all.curbytes != 1) ? " bytes, " : " byte, "); + malloc_write(umax2s(cnt_all.curobjs, 10, buf)); + malloc_write((cnt_all.curobjs != 1) ? " objects, " : + " object, "); + malloc_write(umax2s(leak_nctx, 10, buf)); + malloc_write((leak_nctx != 1) ? " contexts\n" : " context\n"); + malloc_write("<jemalloc>: Run pprof on \""); + malloc_write(filename); + malloc_write("\" for leak detail\n"); + } + + return (false); +ERROR: + prof_leave(); + return (true); +} + +#define DUMP_FILENAME_BUFSIZE (PATH_MAX+ UMAX2S_BUFSIZE \ + + 1 \ + + UMAX2S_BUFSIZE \ + + 2 \ + + UMAX2S_BUFSIZE \ + + 5 + 1) +static void +prof_dump_filename(char *filename, char v, int64_t vseq) +{ + char buf[UMAX2S_BUFSIZE]; + char *s; + unsigned i, slen; + + /* + * Construct a filename of the form: + * + * <prefix>.<pid>.<seq>.v<vseq>.heap\0 + * or + * jeprof.<pid>.<seq>.v<vseq>.heap\0 + */ + + i = 0; + + /* + * Use JEMALLOC_PROF_PREFIX if it's set, and if it is short enough to + * avoid overflowing DUMP_FILENAME_BUFSIZE. The result may exceed + * PATH_MAX, but creat(2) will catch that problem. + */ + if ((s = getenv("JEMALLOC_PROF_PREFIX")) != NULL + && strlen(s) + (DUMP_FILENAME_BUFSIZE - PATH_MAX) <= PATH_MAX) { + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + s = "."; + } else + s = "jeprof."; + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + s = umax2s(getpid(), 10, buf); + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + s = "."; + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + s = umax2s(prof_dump_seq, 10, buf); + prof_dump_seq++; + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + s = "."; + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + filename[i] = v; + i++; + + if (vseq != 0xffffffffffffffffLLU) { + s = umax2s(vseq, 10, buf); + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + } + + s = ".heap"; + slen = strlen(s); + memcpy(&filename[i], s, slen); + i += slen; + + filename[i] = '\0'; +} + +static void +prof_fdump(void) +{ + char filename[DUMP_FILENAME_BUFSIZE]; + + if (prof_booted == false) + return; + + malloc_mutex_lock(&prof_dump_seq_mtx); + prof_dump_filename(filename, 'f', 0xffffffffffffffffLLU); + malloc_mutex_unlock(&prof_dump_seq_mtx); + prof_dump(filename, opt_prof_leak, false); +} + +void +prof_idump(void) +{ + char filename[DUMP_FILENAME_BUFSIZE]; + + if (prof_booted == false) + return; + malloc_mutex_lock(&enq_mtx); + if (enq) { + enq_idump = true; + malloc_mutex_unlock(&enq_mtx); + return; + } + malloc_mutex_unlock(&enq_mtx); + + malloc_mutex_lock(&prof_dump_seq_mtx); + prof_dump_filename(filename, 'i', prof_dump_iseq); + prof_dump_iseq++; + malloc_mutex_unlock(&prof_dump_seq_mtx); + prof_dump(filename, false, false); +} + +bool +prof_mdump(const char *filename) +{ + char filename_buf[DUMP_FILENAME_BUFSIZE]; + + if (opt_prof == false || prof_booted == false) + return (true); + + if (filename == NULL) { + /* No filename specified, so automatically generate one. */ + malloc_mutex_lock(&prof_dump_seq_mtx); + prof_dump_filename(filename_buf, 'm', prof_dump_mseq); + prof_dump_mseq++; + malloc_mutex_unlock(&prof_dump_seq_mtx); + filename = filename_buf; + } + return (prof_dump(filename, false, true)); +} + +void +prof_udump(void) +{ + char filename[DUMP_FILENAME_BUFSIZE]; + + if (prof_booted == false) + return; + malloc_mutex_lock(&enq_mtx); + if (enq) { + enq_udump = true; + malloc_mutex_unlock(&enq_mtx); + return; + } + malloc_mutex_unlock(&enq_mtx); + + malloc_mutex_lock(&prof_dump_seq_mtx); + prof_dump_filename(filename, 'u', prof_dump_useq); + prof_dump_useq++; + malloc_mutex_unlock(&prof_dump_seq_mtx); + prof_dump(filename, false, false); +} + +static void +prof_bt_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2) +{ + size_t ret1, ret2; + uint64_t h; + prof_bt_t *bt = (prof_bt_t *)key; + + assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64)); + assert(hash1 != NULL); + assert(hash2 != NULL); + + h = hash(bt->vec, bt->len * sizeof(void *), 0x94122f335b332aeaLLU); + if (minbits <= 32) { + /* + * Avoid doing multiple hashes, since a single hash provides + * enough bits. + */ + ret1 = h & ZU(0xffffffffU); + ret2 = h >> 32; + } else { + ret1 = h; + ret2 = hash(bt->vec, bt->len * sizeof(void *), + 0x8432a476666bbc13U); + } + + *hash1 = ret1; + *hash2 = ret2; +} + +static bool +prof_bt_keycomp(const void *k1, const void *k2) +{ + const prof_bt_t *bt1 = (prof_bt_t *)k1; + const prof_bt_t *bt2 = (prof_bt_t *)k2; + + if (bt1->len != bt2->len) + return (false); + return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0); +} + +static void +bt2cnt_thread_cleanup(void *arg) +{ + ckh_t *bt2cnt; + + bt2cnt = bt2cnt_tls; + if (bt2cnt != NULL) { + ql_head(prof_thr_cnt_t) cnts_ql; + size_t tabind; + prof_thr_cnt_t *cnt; + + /* Iteratively merge cnt's into the global stats. */ + ql_new(&cnts_ql); + tabind = 0; + while (ckh_iter(bt2cnt, &tabind, NULL, (void **)&cnt) == + false) { + prof_ctx_t *ctx = cnt->ctx; + /* Merge stats and detach from ctx. */ + malloc_mutex_lock(&ctx->lock); + ctx->cnt_merged.curobjs += cnt->cnts.curobjs; + ctx->cnt_merged.curbytes += cnt->cnts.curbytes; + ctx->cnt_merged.accumobjs += cnt->cnts.accumobjs; + ctx->cnt_merged.accumbytes += cnt->cnts.accumbytes; + ql_remove(&ctx->cnts_ql, cnt, link); + malloc_mutex_unlock(&ctx->lock); + + /* + * Stash cnt for deletion after finishing with + * ckh_iter(). + */ + ql_tail_insert(&cnts_ql, cnt, link); + } + + /* + * Delete the hash table now that cnts_ql has a list of all + * cnt's. + */ + ckh_delete(bt2cnt); + idalloc(bt2cnt); + bt2cnt_tls = NULL; + + /* Delete cnt's. */ + while ((cnt = ql_last(&cnts_ql, link)) != NULL) { + ql_remove(&cnts_ql, cnt, link); + idalloc(cnt); + } + } +} + +void +prof_boot0(void) +{ + + /* + * opt_prof and prof_promote must be in their final state before any + * arenas are initialized, so this function must be executed early. + */ + + if (opt_prof_leak && opt_prof == false) { + /* + * Enable opt_prof, but in such a way that profiles are never + * automatically dumped. + */ + opt_prof = true; + opt_prof_udump = false; + prof_interval = 0; + } else if (opt_prof) { + if (opt_lg_prof_interval >= 0) { + prof_interval = (((uint64_t)1U) << + opt_lg_prof_interval); + } else + prof_interval = 0; + } + + prof_promote = (opt_prof && opt_lg_prof_sample > PAGE_SHIFT); +} + +bool +prof_boot1(void) +{ + + if (opt_prof) { + if (ckh_new(&bt2ctx, PROF_CKH_MINITEMS, prof_bt_hash, + prof_bt_keycomp)) + return (true); + if (malloc_mutex_init(&bt2ctx_mtx)) + return (true); + if (pthread_key_create(&bt2cnt_tsd, bt2cnt_thread_cleanup) + != 0) { + malloc_write( + "<jemalloc>: Error in pthread_key_create()\n"); + abort(); + } + + prof_bt_max = (1U << opt_lg_prof_bt_max); + if (malloc_mutex_init(&prof_dump_seq_mtx)) + return (true); + + if (malloc_mutex_init(&enq_mtx)) + return (true); + enq = false; + enq_idump = false; + enq_udump = false; + + if (atexit(prof_fdump) != 0) { + malloc_write("<jemalloc>: Error in atexit()\n"); + if (opt_abort) + abort(); + } + } + +#ifdef JEMALLOC_PROF_LIBGCC + /* + * Cause the backtracing machinery to allocate its internal state + * before enabling profiling. + */ + _Unwind_Backtrace(prof_unwind_init_callback, NULL); +#endif + + prof_booted = true; + + return (false); +} + +/******************************************************************************/ +#endif /* JEMALLOC_PROF */ diff --git a/externals/jemalloc/stats.c b/externals/jemalloc/stats.c new file mode 100644 index 00000000000..9dc75293731 --- /dev/null +++ b/externals/jemalloc/stats.c @@ -0,0 +1,717 @@ +#define JEMALLOC_STATS_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +#define CTL_GET(n, v, t) do { \ + size_t sz = sizeof(t); \ + xmallctl(n, v, &sz, NULL, 0); \ +} while (0) + +#define CTL_I_GET(n, v, t) do { \ + size_t mib[6]; \ + size_t miblen = sizeof(mib) / sizeof(size_t); \ + size_t sz = sizeof(t); \ + xmallctlnametomib(n, mib, &miblen); \ + mib[2] = i; \ + xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \ +} while (0) + +#define CTL_J_GET(n, v, t) do { \ + size_t mib[6]; \ + size_t miblen = sizeof(mib) / sizeof(size_t); \ + size_t sz = sizeof(t); \ + xmallctlnametomib(n, mib, &miblen); \ + mib[2] = j; \ + xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \ +} while (0) + +#define CTL_IJ_GET(n, v, t) do { \ + size_t mib[6]; \ + size_t miblen = sizeof(mib) / sizeof(size_t); \ + size_t sz = sizeof(t); \ + xmallctlnametomib(n, mib, &miblen); \ + mib[2] = i; \ + mib[4] = j; \ + xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \ +} while (0) + +/******************************************************************************/ +/* Data. */ + +bool opt_stats_print = false; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +#ifdef JEMALLOC_STATS +static void malloc_vcprintf(void (*write_cb)(void *, const char *), + void *cbopaque, const char *format, va_list ap); +static void stats_arena_bins_print(void (*write_cb)(void *, const char *), + void *cbopaque, unsigned i); +static void stats_arena_lruns_print(void (*write_cb)(void *, const char *), + void *cbopaque, unsigned i); +static void stats_arena_print(void (*write_cb)(void *, const char *), + void *cbopaque, unsigned i); +#endif + +/******************************************************************************/ + +/* + * We don't want to depend on vsnprintf() for production builds, since that can + * cause unnecessary bloat for static binaries. umax2s() provides minimal + * integer printing functionality, so that malloc_printf() use can be limited to + * JEMALLOC_STATS code. + */ +char * +umax2s(uintmax_t x, unsigned base, char *s) +{ + unsigned i; + + i = UMAX2S_BUFSIZE - 1; + s[i] = '\0'; + switch (base) { + case 10: + do { + i--; + s[i] = "0123456789"[x % 10]; + x /= 10; + } while (x > 0); + break; + case 16: + do { + i--; + s[i] = "0123456789abcdef"[x & 0xf]; + x >>= 4; + } while (x > 0); + break; + default: + do { + i--; + s[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % base]; + x /= base; + } while (x > 0); + } + + return (&s[i]); +} + +#ifdef JEMALLOC_STATS +static void +malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque, + const char *format, va_list ap) +{ + char buf[4096]; + + if (write_cb == NULL) { + /* + * The caller did not provide an alternate write_cb callback + * function, so use the default one. malloc_write() is an + * inline function, so use malloc_message() directly here. + */ + write_cb = JEMALLOC_P(malloc_message); + cbopaque = NULL; + } + + vsnprintf(buf, sizeof(buf), format, ap); + write_cb(cbopaque, buf); +} + +/* + * Print to a callback function in such a way as to (hopefully) avoid memory + * allocation. + */ +JEMALLOC_ATTR(format(printf, 3, 4)) +void +malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque, + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + malloc_vcprintf(write_cb, cbopaque, format, ap); + va_end(ap); +} + +/* + * Print to stderr in such a way as to (hopefully) avoid memory allocation. + */ +JEMALLOC_ATTR(format(printf, 1, 2)) +void +malloc_printf(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + malloc_vcprintf(NULL, NULL, format, ap); + va_end(ap); +} +#endif + +#ifdef JEMALLOC_STATS +static void +stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque, + unsigned i) +{ + size_t pagesize; + bool config_tcache; + unsigned nbins, j, gap_start; + + CTL_GET("arenas.pagesize", &pagesize, size_t); + + CTL_GET("config.tcache", &config_tcache, bool); + if (config_tcache) { + malloc_cprintf(write_cb, cbopaque, + "bins: bin size regs pgs allocated nmalloc" + " ndalloc nrequests nfills nflushes" + " newruns reruns maxruns curruns\n"); + } else { + malloc_cprintf(write_cb, cbopaque, + "bins: bin size regs pgs allocated nmalloc" + " ndalloc newruns reruns maxruns" + " curruns\n"); + } + CTL_GET("arenas.nbins", &nbins, unsigned); + for (j = 0, gap_start = UINT_MAX; j < nbins; j++) { + uint64_t nruns; + + CTL_IJ_GET("stats.arenas.0.bins.0.nruns", &nruns, uint64_t); + if (nruns == 0) { + if (gap_start == UINT_MAX) + gap_start = j; + } else { + unsigned ntbins_, nqbins, ncbins, nsbins; + size_t reg_size, run_size, allocated; + uint32_t nregs; + uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes; + uint64_t reruns; + size_t highruns, curruns; + + if (gap_start != UINT_MAX) { + if (j > gap_start + 1) { + /* Gap of more than one size class. */ + malloc_cprintf(write_cb, cbopaque, + "[%u..%u]\n", gap_start, + j - 1); + } else { + /* Gap of one size class. */ + malloc_cprintf(write_cb, cbopaque, + "[%u]\n", gap_start); + } + gap_start = UINT_MAX; + } + CTL_GET("arenas.ntbins", &ntbins_, unsigned); + CTL_GET("arenas.nqbins", &nqbins, unsigned); + CTL_GET("arenas.ncbins", &ncbins, unsigned); + CTL_GET("arenas.nsbins", &nsbins, unsigned); + CTL_J_GET("arenas.bin.0.size", ®_size, size_t); + CTL_J_GET("arenas.bin.0.nregs", &nregs, uint32_t); + CTL_J_GET("arenas.bin.0.run_size", &run_size, size_t); + CTL_IJ_GET("stats.arenas.0.bins.0.allocated", + &allocated, size_t); + CTL_IJ_GET("stats.arenas.0.bins.0.nmalloc", + &nmalloc, uint64_t); + CTL_IJ_GET("stats.arenas.0.bins.0.ndalloc", + &ndalloc, uint64_t); + if (config_tcache) { + CTL_IJ_GET("stats.arenas.0.bins.0.nrequests", + &nrequests, uint64_t); + CTL_IJ_GET("stats.arenas.0.bins.0.nfills", + &nfills, uint64_t); + CTL_IJ_GET("stats.arenas.0.bins.0.nflushes", + &nflushes, uint64_t); + } + CTL_IJ_GET("stats.arenas.0.bins.0.nreruns", &reruns, + uint64_t); + CTL_IJ_GET("stats.arenas.0.bins.0.highruns", &highruns, + size_t); + CTL_IJ_GET("stats.arenas.0.bins.0.curruns", &curruns, + size_t); + if (config_tcache) { + malloc_cprintf(write_cb, cbopaque, + "%13u %1s %5zu %4u %3zu %12zu %12"PRIu64 + " %12"PRIu64" %12"PRIu64" %12"PRIu64 + " %12"PRIu64" %12"PRIu64" %12"PRIu64 + " %12zu %12zu\n", + j, + j < ntbins_ ? "T" : j < ntbins_ + nqbins ? + "Q" : j < ntbins_ + nqbins + ncbins ? "C" : + "S", + reg_size, nregs, run_size / pagesize, + allocated, nmalloc, ndalloc, nrequests, + nfills, nflushes, nruns, reruns, highruns, + curruns); + } else { + malloc_cprintf(write_cb, cbopaque, + "%13u %1s %5zu %4u %3zu %12zu %12"PRIu64 + " %12"PRIu64" %12"PRIu64" %12"PRIu64 + " %12zu %12zu\n", + j, + j < ntbins_ ? "T" : j < ntbins_ + nqbins ? + "Q" : j < ntbins_ + nqbins + ncbins ? "C" : + "S", + reg_size, nregs, run_size / pagesize, + allocated, nmalloc, ndalloc, nruns, reruns, + highruns, curruns); + } + } + } + if (gap_start != UINT_MAX) { + if (j > gap_start + 1) { + /* Gap of more than one size class. */ + malloc_cprintf(write_cb, cbopaque, "[%u..%u]\n", + gap_start, j - 1); + } else { + /* Gap of one size class. */ + malloc_cprintf(write_cb, cbopaque, "[%u]\n", gap_start); + } + } +} + +static void +stats_arena_lruns_print(void (*write_cb)(void *, const char *), void *cbopaque, + unsigned i) +{ + size_t pagesize, nlruns, j; + ssize_t gap_start; + + CTL_GET("arenas.pagesize", &pagesize, size_t); + + malloc_cprintf(write_cb, cbopaque, + "large: size pages nmalloc ndalloc nrequests" + " maxruns curruns\n"); + CTL_GET("arenas.nlruns", &nlruns, size_t); + for (j = 0, gap_start = -1; j < nlruns; j++) { + uint64_t nmalloc, ndalloc, nrequests; + size_t run_size, highruns, curruns; + + CTL_IJ_GET("stats.arenas.0.lruns.0.nmalloc", &nmalloc, + uint64_t); + CTL_IJ_GET("stats.arenas.0.lruns.0.ndalloc", &ndalloc, + uint64_t); + CTL_IJ_GET("stats.arenas.0.lruns.0.nrequests", &nrequests, + uint64_t); + if (nrequests == 0) { + if (gap_start == -1) + gap_start = j; + } else { + CTL_J_GET("arenas.lrun.0.size", &run_size, size_t); + CTL_IJ_GET("stats.arenas.0.lruns.0.highruns", &highruns, + size_t); + CTL_IJ_GET("stats.arenas.0.lruns.0.curruns", &curruns, + size_t); + if (gap_start != -1) { + malloc_cprintf(write_cb, cbopaque, "[%zu]\n", + j - gap_start); + gap_start = -1; + } + malloc_cprintf(write_cb, cbopaque, + "%13zu %5zu %12"PRIu64" %12"PRIu64" %12"PRIu64 + " %12zu %12zu\n", + run_size, run_size / pagesize, nmalloc, ndalloc, + nrequests, highruns, curruns); + } + } + if (gap_start != -1) + malloc_cprintf(write_cb, cbopaque, "[%zu]\n", j - gap_start); +} + +static void +stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque, + unsigned i) +{ + size_t pagesize, pactive, pdirty, mapped; + uint64_t npurge, nmadvise, purged; + size_t small_allocated; + uint64_t small_nmalloc, small_ndalloc, small_nrequests; + size_t large_allocated; + uint64_t large_nmalloc, large_ndalloc, large_nrequests; + + CTL_GET("arenas.pagesize", &pagesize, size_t); + + CTL_I_GET("stats.arenas.0.pactive", &pactive, size_t); + CTL_I_GET("stats.arenas.0.pdirty", &pdirty, size_t); + CTL_I_GET("stats.arenas.0.npurge", &npurge, uint64_t); + CTL_I_GET("stats.arenas.0.nmadvise", &nmadvise, uint64_t); + CTL_I_GET("stats.arenas.0.purged", &purged, uint64_t); + malloc_cprintf(write_cb, cbopaque, + "dirty pages: %zu:%zu active:dirty, %"PRIu64" sweep%s," + " %"PRIu64" madvise%s, %"PRIu64" purged\n", + pactive, pdirty, npurge, npurge == 1 ? "" : "s", + nmadvise, nmadvise == 1 ? "" : "s", purged); + + malloc_cprintf(write_cb, cbopaque, + " allocated nmalloc ndalloc nrequests\n"); + CTL_I_GET("stats.arenas.0.small.allocated", &small_allocated, size_t); + CTL_I_GET("stats.arenas.0.small.nmalloc", &small_nmalloc, uint64_t); + CTL_I_GET("stats.arenas.0.small.ndalloc", &small_ndalloc, uint64_t); + CTL_I_GET("stats.arenas.0.small.nrequests", &small_nrequests, uint64_t); + malloc_cprintf(write_cb, cbopaque, + "small: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n", + small_allocated, small_nmalloc, small_ndalloc, small_nrequests); + CTL_I_GET("stats.arenas.0.large.allocated", &large_allocated, size_t); + CTL_I_GET("stats.arenas.0.large.nmalloc", &large_nmalloc, uint64_t); + CTL_I_GET("stats.arenas.0.large.ndalloc", &large_ndalloc, uint64_t); + CTL_I_GET("stats.arenas.0.large.nrequests", &large_nrequests, uint64_t); + malloc_cprintf(write_cb, cbopaque, + "large: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n", + large_allocated, large_nmalloc, large_ndalloc, large_nrequests); + malloc_cprintf(write_cb, cbopaque, + "total: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n", + small_allocated + large_allocated, + small_nmalloc + large_nmalloc, + small_ndalloc + large_ndalloc, + small_nrequests + large_nrequests); + malloc_cprintf(write_cb, cbopaque, "active: %12zu\n", + pactive * pagesize ); + CTL_I_GET("stats.arenas.0.mapped", &mapped, size_t); + malloc_cprintf(write_cb, cbopaque, "mapped: %12zu\n", mapped); + + stats_arena_bins_print(write_cb, cbopaque, i); + stats_arena_lruns_print(write_cb, cbopaque, i); +} +#endif + +void +stats_print(void (*write_cb)(void *, const char *), void *cbopaque, + const char *opts) +{ + uint64_t epoch; + size_t u64sz; + char s[UMAX2S_BUFSIZE]; + bool general = true; + bool merged = true; + bool unmerged = true; + bool bins = true; + bool large = true; + + /* Refresh stats, in case mallctl() was called by the application. */ + epoch = 1; + u64sz = sizeof(uint64_t); + xmallctl("epoch", &epoch, &u64sz, &epoch, sizeof(uint64_t)); + + if (write_cb == NULL) { + /* + * The caller did not provide an alternate write_cb callback + * function, so use the default one. malloc_write() is an + * inline function, so use malloc_message() directly here. + */ + write_cb = JEMALLOC_P(malloc_message); + cbopaque = NULL; + } + + if (opts != NULL) { + unsigned i; + + for (i = 0; opts[i] != '\0'; i++) { + switch (opts[i]) { + case 'g': + general = false; + break; + case 'm': + merged = false; + break; + case 'a': + unmerged = false; + break; + case 'b': + bins = false; + break; + case 'l': + large = false; + break; + default:; + } + } + } + + write_cb(cbopaque, "___ Begin jemalloc statistics ___\n"); + if (general) { + int err; + const char *cpv; + bool bv; + unsigned uv; + ssize_t ssv; + size_t sv, bsz, ssz; + + bsz = sizeof(bool); + ssz = sizeof(size_t); + + CTL_GET("version", &cpv, const char *); + write_cb(cbopaque, "Version: "); + write_cb(cbopaque, cpv); + write_cb(cbopaque, "\n"); + CTL_GET("config.debug", &bv, bool); + write_cb(cbopaque, "Assertions "); + write_cb(cbopaque, bv ? "enabled" : "disabled"); + write_cb(cbopaque, "\n"); + + write_cb(cbopaque, "Boolean JEMALLOC_OPTIONS: "); + if ((err = JEMALLOC_P(mallctl)("opt.abort", &bv, &bsz, NULL, 0)) + == 0) + write_cb(cbopaque, bv ? "A" : "a"); + if ((err = JEMALLOC_P(mallctl)("prof.active", &bv, &bsz, + NULL, 0)) == 0) + write_cb(cbopaque, bv ? "E" : "e"); + if ((err = JEMALLOC_P(mallctl)("opt.prof", &bv, &bsz, NULL, 0)) + == 0) + write_cb(cbopaque, bv ? "F" : "f"); + if ((err = JEMALLOC_P(mallctl)("opt.tcache", &bv, &bsz, NULL, + 0)) == 0) + write_cb(cbopaque, bv ? "H" : "h"); + if ((err = JEMALLOC_P(mallctl)("opt.junk", &bv, &bsz, NULL, 0)) + == 0) + write_cb(cbopaque, bv ? "J" : "j"); + if ((err = JEMALLOC_P(mallctl)("opt.prof_leak", &bv, &bsz, NULL, + 0)) == 0) + write_cb(cbopaque, bv ? "L" : "l"); + if ((err = JEMALLOC_P(mallctl)("opt.overcommit", &bv, &bsz, + NULL, 0)) == 0) + write_cb(cbopaque, bv ? "O" : "o"); + if ((err = JEMALLOC_P(mallctl)("opt.stats_print", &bv, &bsz, + NULL, 0)) == 0) + write_cb(cbopaque, bv ? "P" : "p"); + if ((err = JEMALLOC_P(mallctl)("opt.prof_udump", &bv, &bsz, + NULL, 0)) == 0) + write_cb(cbopaque, bv ? "U" : "u"); + if ((err = JEMALLOC_P(mallctl)("opt.sysv", &bv, &bsz, NULL, 0)) + == 0) + write_cb(cbopaque, bv ? "V" : "v"); + if ((err = JEMALLOC_P(mallctl)("opt.xmalloc", &bv, &bsz, NULL, + 0)) == 0) + write_cb(cbopaque, bv ? "X" : "x"); + if ((err = JEMALLOC_P(mallctl)("opt.zero", &bv, &bsz, NULL, 0)) + == 0) + write_cb(cbopaque, bv ? "Z" : "z"); + write_cb(cbopaque, "\n"); + + write_cb(cbopaque, "CPUs: "); + write_cb(cbopaque, umax2s(ncpus, 10, s)); + write_cb(cbopaque, "\n"); + + CTL_GET("arenas.narenas", &uv, unsigned); + write_cb(cbopaque, "Max arenas: "); + write_cb(cbopaque, umax2s(uv, 10, s)); + write_cb(cbopaque, "\n"); + + write_cb(cbopaque, "Pointer size: "); + write_cb(cbopaque, umax2s(sizeof(void *), 10, s)); + write_cb(cbopaque, "\n"); + + CTL_GET("arenas.quantum", &sv, size_t); + write_cb(cbopaque, "Quantum size: "); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "\n"); + + CTL_GET("arenas.cacheline", &sv, size_t); + write_cb(cbopaque, "Cacheline size (assumed): "); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "\n"); + + CTL_GET("arenas.subpage", &sv, size_t); + write_cb(cbopaque, "Subpage spacing: "); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "\n"); + + if ((err = JEMALLOC_P(mallctl)("arenas.tspace_min", &sv, &ssz, + NULL, 0)) == 0) { + write_cb(cbopaque, "Tiny 2^n-spaced sizes: ["); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, ".."); + + CTL_GET("arenas.tspace_max", &sv, size_t); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "]\n"); + } + + CTL_GET("arenas.qspace_min", &sv, size_t); + write_cb(cbopaque, "Quantum-spaced sizes: ["); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, ".."); + CTL_GET("arenas.qspace_max", &sv, size_t); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "]\n"); + + CTL_GET("arenas.cspace_min", &sv, size_t); + write_cb(cbopaque, "Cacheline-spaced sizes: ["); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, ".."); + CTL_GET("arenas.cspace_max", &sv, size_t); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "]\n"); + + CTL_GET("arenas.sspace_min", &sv, size_t); + write_cb(cbopaque, "Subpage-spaced sizes: ["); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, ".."); + CTL_GET("arenas.sspace_max", &sv, size_t); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "]\n"); + + CTL_GET("opt.lg_dirty_mult", &ssv, ssize_t); + if (ssv >= 0) { + write_cb(cbopaque, + "Min active:dirty page ratio per arena: "); + write_cb(cbopaque, umax2s((1U << ssv), 10, s)); + write_cb(cbopaque, ":1\n"); + } else { + write_cb(cbopaque, + "Min active:dirty page ratio per arena: N/A\n"); + } + if ((err = JEMALLOC_P(mallctl)("arenas.tcache_max", &sv, + &ssz, NULL, 0)) == 0) { + write_cb(cbopaque, + "Maximum thread-cached size class: "); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, "\n"); + } + if ((err = JEMALLOC_P(mallctl)("opt.lg_tcache_gc_sweep", &ssv, + &ssz, NULL, 0)) == 0) { + size_t tcache_gc_sweep = (1U << ssv); + bool tcache_enabled; + CTL_GET("opt.tcache", &tcache_enabled, bool); + write_cb(cbopaque, "Thread cache GC sweep interval: "); + write_cb(cbopaque, tcache_enabled && ssv >= 0 ? + umax2s(tcache_gc_sweep, 10, s) : "N/A"); + write_cb(cbopaque, "\n"); + } + if ((err = JEMALLOC_P(mallctl)("opt.prof", &bv, &bsz, NULL, 0)) + == 0 && bv) { + CTL_GET("opt.lg_prof_bt_max", &sv, size_t); + write_cb(cbopaque, "Maximum profile backtrace depth: "); + write_cb(cbopaque, umax2s((1U << sv), 10, s)); + write_cb(cbopaque, "\n"); + + CTL_GET("opt.lg_prof_sample", &sv, size_t); + write_cb(cbopaque, "Average profile sample interval: "); + write_cb(cbopaque, umax2s((1U << sv), 10, s)); + write_cb(cbopaque, " (2^"); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, ")\n"); + + CTL_GET("opt.lg_prof_interval", &ssv, ssize_t); + write_cb(cbopaque, "Average profile dump interval: "); + if (ssv >= 0) { + write_cb(cbopaque, umax2s((1U << ssv), 10, s)); + write_cb(cbopaque, " (2^"); + write_cb(cbopaque, umax2s(ssv, 10, s)); + write_cb(cbopaque, ")\n"); + } else + write_cb(cbopaque, "N/A\n"); + } + CTL_GET("arenas.chunksize", &sv, size_t); + write_cb(cbopaque, "Chunk size: "); + write_cb(cbopaque, umax2s(sv, 10, s)); + CTL_GET("opt.lg_chunk", &sv, size_t); + write_cb(cbopaque, " (2^"); + write_cb(cbopaque, umax2s(sv, 10, s)); + write_cb(cbopaque, ")\n"); + } + +#ifdef JEMALLOC_STATS + { + int err; + size_t ssz; + size_t allocated, active, mapped; + size_t chunks_current, chunks_high, swap_avail; + uint64_t chunks_total; + size_t huge_allocated; + uint64_t huge_nmalloc, huge_ndalloc; + + ssz = sizeof(size_t); + + CTL_GET("stats.allocated", &allocated, size_t); + CTL_GET("stats.active", &active, size_t); + CTL_GET("stats.mapped", &mapped, size_t); + malloc_cprintf(write_cb, cbopaque, + "Allocated: %zu, active: %zu, mapped: %zu\n", allocated, + active, mapped); + + /* Print chunk stats. */ + CTL_GET("stats.chunks.total", &chunks_total, uint64_t); + CTL_GET("stats.chunks.high", &chunks_high, size_t); + CTL_GET("stats.chunks.current", &chunks_current, size_t); + if ((err = JEMALLOC_P(mallctl)("swap.avail", &swap_avail, &ssz, + NULL, 0)) == 0) { + size_t lg_chunk; + + malloc_cprintf(write_cb, cbopaque, "chunks: nchunks " + "highchunks curchunks swap_avail\n"); + CTL_GET("opt.lg_chunk", &lg_chunk, size_t); + malloc_cprintf(write_cb, cbopaque, + " %13"PRIu64"%13zu%13zu%13zu\n", + chunks_total, chunks_high, chunks_current, + swap_avail << lg_chunk); + } else { + malloc_cprintf(write_cb, cbopaque, "chunks: nchunks " + "highchunks curchunks\n"); + malloc_cprintf(write_cb, cbopaque, + " %13"PRIu64"%13zu%13zu\n", + chunks_total, chunks_high, chunks_current); + } + + /* Print huge stats. */ + CTL_GET("stats.huge.nmalloc", &huge_nmalloc, uint64_t); + CTL_GET("stats.huge.ndalloc", &huge_ndalloc, uint64_t); + CTL_GET("stats.huge.allocated", &huge_allocated, size_t); + malloc_cprintf(write_cb, cbopaque, + "huge: nmalloc ndalloc allocated\n"); + malloc_cprintf(write_cb, cbopaque, + " %12"PRIu64" %12"PRIu64" %12zu\n", + huge_nmalloc, huge_ndalloc, huge_allocated); + + if (merged) { + unsigned narenas; + + CTL_GET("arenas.narenas", &narenas, unsigned); + { + bool initialized[narenas]; + size_t isz; + unsigned i, ninitialized; + + isz = sizeof(initialized); + xmallctl("arenas.initialized", initialized, + &isz, NULL, 0); + for (i = ninitialized = 0; i < narenas; i++) { + if (initialized[i]) + ninitialized++; + } + + if (ninitialized > 1) { + /* Print merged arena stats. */ + malloc_cprintf(write_cb, cbopaque, + "\nMerged arenas stats:\n"); + stats_arena_print(write_cb, cbopaque, + narenas); + } + } + } + + if (unmerged) { + unsigned narenas; + + /* Print stats for each arena. */ + + CTL_GET("arenas.narenas", &narenas, unsigned); + { + bool initialized[narenas]; + size_t isz; + unsigned i; + + isz = sizeof(initialized); + xmallctl("arenas.initialized", initialized, + &isz, NULL, 0); + + for (i = 0; i < narenas; i++) { + if (initialized[i]) { + malloc_cprintf(write_cb, + cbopaque, + "\narenas[%u]:\n", i); + stats_arena_print(write_cb, + cbopaque, i); + } + } + } + } + } +#endif /* #ifdef JEMALLOC_STATS */ + write_cb(cbopaque, "--- End jemalloc statistics ---\n"); +} diff --git a/externals/jemalloc/tcache.c b/externals/jemalloc/tcache.c new file mode 100644 index 00000000000..ce6ec996159 --- /dev/null +++ b/externals/jemalloc/tcache.c @@ -0,0 +1,403 @@ +#define JEMALLOC_TCACHE_C_ +#include "jemalloc/internal/jemalloc_internal.h" +#ifdef JEMALLOC_TCACHE +/******************************************************************************/ +/* Data. */ + +bool opt_tcache = true; +ssize_t opt_lg_tcache_maxclass = LG_TCACHE_MAXCLASS_DEFAULT; +ssize_t opt_lg_tcache_gc_sweep = LG_TCACHE_GC_SWEEP_DEFAULT; + +/* Map of thread-specific caches. */ +__thread tcache_t *tcache_tls JEMALLOC_ATTR(tls_model("initial-exec")); + +/* + * Same contents as tcache, but initialized such that the TSD destructor is + * called when a thread exits, so that the cache can be cleaned up. + */ +static pthread_key_t tcache_tsd; + +size_t nhbins; +size_t tcache_maxclass; +unsigned tcache_gc_incr; + +/******************************************************************************/ +/* Function prototypes for non-inline static functions. */ + +static void tcache_thread_cleanup(void *arg); + +/******************************************************************************/ + +void * +tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) +{ + void *ret; + + arena_tcache_fill_small(tcache->arena, tbin, binind +#ifdef JEMALLOC_PROF + , tcache->prof_accumbytes +#endif + ); +#ifdef JEMALLOC_PROF + tcache->prof_accumbytes = 0; +#endif + ret = tcache_alloc_easy(tbin); + + return (ret); +} + +void +tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache_t *tcache +#endif + ) +{ + void *flush, *deferred, *ptr; + unsigned i, nflush, ndeferred; + + assert(binind < nbins); + assert(rem <= tbin->ncached); + + for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL; + flush = deferred, nflush = ndeferred) { + /* Lock the arena bin associated with the first object. */ + arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush); + arena_t *arena = chunk->arena; + arena_bin_t *bin = &arena->bins[binind]; + +#ifdef JEMALLOC_PROF + if (arena == tcache->arena) { + malloc_mutex_lock(&arena->lock); + arena_prof_accum(arena, tcache->prof_accumbytes); + malloc_mutex_unlock(&arena->lock); + tcache->prof_accumbytes = 0; + } +#endif + + malloc_mutex_lock(&bin->lock); +#ifdef JEMALLOC_STATS + if (arena == tcache->arena) { + bin->stats.nflushes++; + bin->stats.nrequests += tbin->tstats.nrequests; + tbin->tstats.nrequests = 0; + } +#endif + deferred = NULL; + ndeferred = 0; + for (i = 0; i < nflush; i++) { + ptr = flush; + assert(ptr != NULL); + flush = *(void **)ptr; + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk->arena == arena) { + size_t pageind = (((uintptr_t)ptr - + (uintptr_t)chunk) >> PAGE_SHIFT); + arena_chunk_map_t *mapelm = + &chunk->map[pageind]; + arena_dalloc_bin(arena, chunk, ptr, mapelm); + } else { + /* + * This object was allocated via a different + * arena bin than the one that is currently + * locked. Stash the object, so that it can be + * handled in a future pass. + */ + *(void **)ptr = deferred; + deferred = ptr; + ndeferred++; + } + } + malloc_mutex_unlock(&bin->lock); + + if (flush != NULL) { + /* + * This was the first pass, and rem cached objects + * remain. + */ + tbin->avail = flush; + } + } + + tbin->ncached = rem; + if (tbin->ncached < tbin->low_water) + tbin->low_water = tbin->ncached; +} + +void +tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache_t *tcache +#endif + ) +{ + void *flush, *deferred, *ptr; + unsigned i, nflush, ndeferred; + + assert(binind < nhbins); + assert(rem <= tbin->ncached); + + for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL; + flush = deferred, nflush = ndeferred) { + /* Lock the arena associated with the first object. */ + arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush); + arena_t *arena = chunk->arena; + + malloc_mutex_lock(&arena->lock); +#if (defined(JEMALLOC_PROF) || defined(JEMALLOC_STATS)) + if (arena == tcache->arena) { +#endif +#ifdef JEMALLOC_PROF + arena_prof_accum(arena, tcache->prof_accumbytes); + tcache->prof_accumbytes = 0; +#endif +#ifdef JEMALLOC_STATS + arena->stats.nrequests_large += tbin->tstats.nrequests; + arena->stats.lstats[binind - nbins].nrequests += + tbin->tstats.nrequests; + tbin->tstats.nrequests = 0; +#endif +#if (defined(JEMALLOC_PROF) || defined(JEMALLOC_STATS)) + } +#endif + deferred = NULL; + ndeferred = 0; + for (i = 0; i < nflush; i++) { + ptr = flush; + assert(ptr != NULL); + flush = *(void **)ptr; + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (chunk->arena == arena) + arena_dalloc_large(arena, chunk, ptr); + else { + /* + * This object was allocated via a different + * arena than the one that is currently locked. + * Stash the object, so that it can be handled + * in a future pass. + */ + *(void **)ptr = deferred; + deferred = ptr; + ndeferred++; + } + } + malloc_mutex_unlock(&arena->lock); + + if (flush != NULL) { + /* + * This was the first pass, and rem cached objects + * remain. + */ + tbin->avail = flush; + } + } + + tbin->ncached = rem; + if (tbin->ncached < tbin->low_water) + tbin->low_water = tbin->ncached; +} + +tcache_t * +tcache_create(arena_t *arena) +{ + tcache_t *tcache; + size_t size; + unsigned i; + + size = sizeof(tcache_t) + (sizeof(tcache_bin_t) * (nhbins - 1)); + /* + * Round up to the nearest multiple of the cacheline size, in order to + * avoid the possibility of false cacheline sharing. + * + * That this works relies on the same logic as in ipalloc(). + */ + size = (size + CACHELINE_MASK) & (-CACHELINE); + + if (size <= small_maxclass) + tcache = (tcache_t *)arena_malloc_small(arena, size, true); + else + tcache = (tcache_t *)icalloc(size); + + if (tcache == NULL) + return (NULL); + +#ifdef JEMALLOC_STATS + /* Link into list of extant tcaches. */ + malloc_mutex_lock(&arena->lock); + ql_elm_new(tcache, link); + ql_tail_insert(&arena->tcache_ql, tcache, link); + malloc_mutex_unlock(&arena->lock); +#endif + + tcache->arena = arena; + assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0); + for (i = 0; i < nbins; i++) { + if ((arena->bins[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) { + tcache->tbins[i].ncached_max = (arena->bins[i].nregs << + 1); + } else + tcache->tbins[i].ncached_max = TCACHE_NSLOTS_SMALL_MAX; + } + for (; i < nhbins; i++) + tcache->tbins[i].ncached_max = TCACHE_NSLOTS_LARGE; + + tcache_tls = tcache; + pthread_setspecific(tcache_tsd, tcache); + + return (tcache); +} + +void +tcache_destroy(tcache_t *tcache) +{ + unsigned i; + +#ifdef JEMALLOC_STATS + /* Unlink from list of extant tcaches. */ + malloc_mutex_lock(&tcache->arena->lock); + ql_remove(&tcache->arena->tcache_ql, tcache, link); + malloc_mutex_unlock(&tcache->arena->lock); + tcache_stats_merge(tcache, tcache->arena); +#endif + + for (i = 0; i < nbins; i++) { + tcache_bin_t *tbin = &tcache->tbins[i]; + tcache_bin_flush_small(tbin, i, 0 +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache +#endif + ); + +#ifdef JEMALLOC_STATS + if (tbin->tstats.nrequests != 0) { + arena_t *arena = tcache->arena; + arena_bin_t *bin = &arena->bins[i]; + malloc_mutex_lock(&bin->lock); + bin->stats.nrequests += tbin->tstats.nrequests; + malloc_mutex_unlock(&bin->lock); + } +#endif + } + + for (; i < nhbins; i++) { + tcache_bin_t *tbin = &tcache->tbins[i]; + tcache_bin_flush_large(tbin, i, 0 +#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) + , tcache +#endif + ); + +#ifdef JEMALLOC_STATS + if (tbin->tstats.nrequests != 0) { + arena_t *arena = tcache->arena; + malloc_mutex_lock(&arena->lock); + arena->stats.nrequests_large += tbin->tstats.nrequests; + arena->stats.lstats[i - nbins].nrequests += + tbin->tstats.nrequests; + malloc_mutex_unlock(&arena->lock); + } +#endif + } + +#ifdef JEMALLOC_PROF + if (tcache->prof_accumbytes > 0) { + malloc_mutex_lock(&tcache->arena->lock); + arena_prof_accum(tcache->arena, tcache->prof_accumbytes); + malloc_mutex_unlock(&tcache->arena->lock); + } +#endif + + if (arena_salloc(tcache) <= small_maxclass) { + arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); + arena_t *arena = chunk->arena; + size_t pageind = (((uintptr_t)tcache - (uintptr_t)chunk) >> + PAGE_SHIFT); + arena_chunk_map_t *mapelm = &chunk->map[pageind]; + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - (mapelm->bits >> PAGE_SHIFT)) << + PAGE_SHIFT)); + arena_bin_t *bin = run->bin; + + malloc_mutex_lock(&bin->lock); + arena_dalloc_bin(arena, chunk, tcache, mapelm); + malloc_mutex_unlock(&bin->lock); + } else + idalloc(tcache); +} + +static void +tcache_thread_cleanup(void *arg) +{ + tcache_t *tcache = (tcache_t *)arg; + + assert(tcache == tcache_tls); + if (tcache != NULL) { + assert(tcache != (void *)(uintptr_t)1); + tcache_destroy(tcache); + tcache_tls = (void *)(uintptr_t)1; + } +} + +#ifdef JEMALLOC_STATS +void +tcache_stats_merge(tcache_t *tcache, arena_t *arena) +{ + unsigned i; + + /* Merge and reset tcache stats. */ + for (i = 0; i < nbins; i++) { + arena_bin_t *bin = &arena->bins[i]; + tcache_bin_t *tbin = &tcache->tbins[i]; + malloc_mutex_lock(&bin->lock); + bin->stats.nrequests += tbin->tstats.nrequests; + malloc_mutex_unlock(&bin->lock); + tbin->tstats.nrequests = 0; + } + + for (; i < nhbins; i++) { + malloc_large_stats_t *lstats = &arena->stats.lstats[i - nbins]; + tcache_bin_t *tbin = &tcache->tbins[i]; + arena->stats.nrequests_large += tbin->tstats.nrequests; + lstats->nrequests += tbin->tstats.nrequests; + tbin->tstats.nrequests = 0; + } +} +#endif + +void +tcache_boot(void) +{ + + if (opt_tcache) { + /* + * If necessary, clamp opt_lg_tcache_maxclass, now that + * small_maxclass and arena_maxclass are known. + */ + if (opt_lg_tcache_maxclass < 0 || (1U << + opt_lg_tcache_maxclass) < small_maxclass) + tcache_maxclass = small_maxclass; + else if ((1U << opt_lg_tcache_maxclass) > arena_maxclass) + tcache_maxclass = arena_maxclass; + else + tcache_maxclass = (1U << opt_lg_tcache_maxclass); + + nhbins = nbins + (tcache_maxclass >> PAGE_SHIFT); + + /* Compute incremental GC event threshold. */ + if (opt_lg_tcache_gc_sweep >= 0) { + tcache_gc_incr = ((1U << opt_lg_tcache_gc_sweep) / + nbins) + (((1U << opt_lg_tcache_gc_sweep) % nbins == + 0) ? 0 : 1); + } else + tcache_gc_incr = 0; + + if (pthread_key_create(&tcache_tsd, tcache_thread_cleanup) != + 0) { + malloc_write( + "<jemalloc>: Error in pthread_key_create()\n"); + abort(); + } + } +} +/******************************************************************************/ +#endif /* JEMALLOC_TCACHE */ diff --git a/externals/sockets/Base64.cpp b/externals/sockets/Base64.cpp new file mode 100644 index 00000000000..f7f12f5edff --- /dev/null +++ b/externals/sockets/Base64.cpp @@ -0,0 +1,262 @@ +/** \file Base64.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Base64.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +const char *Base64::bstr = + "ABCDEFGHIJKLMNOPQ" + "RSTUVWXYZabcdefgh" + "ijklmnopqrstuvwxy" + "z0123456789+/"; + +const char Base64::rstr[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0}; + +Base64::Base64() +{ +} + +void Base64::encode(FILE *fil, std::string& output, bool add_crlf) +{ + size_t remain; + size_t i = 0; + size_t o = 0; + char input[4]; + + output = ""; + remain = fread(input,1,3,fil); + while (remain > 0) + { + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + // + remain = fread(input,1,3,fil); + } +} + +void Base64::encode(const std::string& str_in, std::string& str_out, bool add_crlf) +{ + encode(str_in.c_str(), str_in.size(), str_out, add_crlf); +} + +void Base64::encode(const char* input,size_t l,std::string& output, bool add_crlf) +{ + size_t i = 0; + size_t o = 0; + + output = ""; + while (i < l) + { + size_t remain = l - i; + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + i += 3; + } +} + +void Base64::encode(const unsigned char* input,size_t l,std::string& output,bool add_crlf) +{ + size_t i = 0; + size_t o = 0; + + output = ""; + while (i < l) + { + size_t remain = l - i; + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + i += 3; + } +} + +void Base64::decode(const std::string& input,std::string& output) +{ + size_t i = 0; + size_t l = input.size(); + + output = ""; + while (i < l) + { + while (i < l && (input[i] == 13 || input[i] == 10)) + i++; + if (i < l) + { + char b1 = (char)((rstr[(int)input[i]] << 2 & 0xfc) + + (rstr[(int)input[i + 1]] >> 4 & 0x03)); + output += b1; + if (input[i + 2] != '=') + { + char b2 = (char)((rstr[(int)input[i + 1]] << 4 & 0xf0) + + (rstr[(int)input[i + 2]] >> 2 & 0x0f)); + output += b2; + } + if (input[i + 3] != '=') + { + char b3 = (char)((rstr[(int)input[i + 2]] << 6 & 0xc0) + + rstr[(int)input[i + 3]]); + output += b3; + } + i += 4; + } + } +} + +void Base64::decode(const std::string& input, unsigned char *output, size_t& sz) +{ + size_t i = 0; + size_t l = input.size(); + size_t j = 0; + + while (i < l) + { + while (i < l && (input[i] == 13 || input[i] == 10)) + i++; + if (i < l) + { + unsigned char b1 = (unsigned char)((rstr[(int)input[i]] << 2 & 0xfc) + + (rstr[(int)input[i + 1]] >> 4 & 0x03)); + if (output) + { + output[j] = b1; + } + j++; + if (input[i + 2] != '=') + { + unsigned char b2 = (unsigned char)((rstr[(int)input[i + 1]] << 4 & 0xf0) + + (rstr[(int)input[i + 2]] >> 2 & 0x0f)); + if (output) + { + output[j] = b2; + } + j++; + } + if (input[i + 3] != '=') + { + unsigned char b3 = (unsigned char)((rstr[(int)input[i + 2]] << 6 & 0xc0) + + rstr[(int)input[i + 3]]); + if (output) + { + output[j] = b3; + } + j++; + } + i += 4; + } + } + sz = j; +} + +size_t Base64::decode_length(const std::string& str64) +{ + if (str64.empty() || str64.size() % 4) + return 0; + size_t l = 3 * (str64.size() / 4 - 1) + 1; + if (str64[str64.size() - 2] != '=') + l++; + if (str64[str64.size() - 1] != '=') + l++; + return l; +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/CMakeLists.txt b/externals/sockets/CMakeLists.txt new file mode 100644 index 00000000000..a47c8dee75f --- /dev/null +++ b/externals/sockets/CMakeLists.txt @@ -0,0 +1,26 @@ +SET(trinitysockets_STAT_SRCS + Base64.cpp + Exception.cpp + Ipv4Address.cpp + Ipv6Address.cpp + Lock.cpp + Mutex.cpp + Parse.cpp + ResolvServer.cpp + ResolvSocket.cpp + Socket.cpp + SocketHandler.cpp + StdoutLog.cpp + StreamSocket.cpp + TcpSocket.cpp + Thread.cpp + UdpSocket.cpp + Utility.cpp + socket_include.cpp +) + +include_directories( + ${CMAKE_SOURCE_DIR}/dep/include/sockets +) + +add_library(trinitysockets STATIC ${trinitysockets_STAT_SRCS}) diff --git a/externals/sockets/Exception.cpp b/externals/sockets/Exception.cpp new file mode 100644 index 00000000000..4d79aeef813 --- /dev/null +++ b/externals/sockets/Exception.cpp @@ -0,0 +1,45 @@ +/** + ** \file Exception.cpp + ** \date 2007-09-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "Exception.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +Exception::Exception(const std::string& description) : m_description(description) +{ +} + +const std::string Exception::ToString() const +{ + return m_description; +} + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/externals/sockets/Ipv4Address.cpp b/externals/sockets/Ipv4Address.cpp new file mode 100644 index 00000000000..03935038951 --- /dev/null +++ b/externals/sockets/Ipv4Address.cpp @@ -0,0 +1,192 @@ +/** + ** \file Ipv4Address.cpp + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Ipv4Address.h" +#include "Utility.h" +#include "Parse.h" +#ifndef _WIN32 +#include <netdb.h> +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +Ipv4Address::Ipv4Address(port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); +} + +Ipv4Address::Ipv4Address(ipaddr_t a,port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); + memcpy(&m_addr.sin_addr, &a, sizeof(struct in_addr)); +} + +Ipv4Address::Ipv4Address(struct in_addr& a,port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); + m_addr.sin_addr = a; +} + +Ipv4Address::Ipv4Address(const std::string& host,port_t port) : m_valid(false) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); + { + ipaddr_t a; + if (Utility::u2ip(host, a)) + { + memcpy(&m_addr.sin_addr, &a, sizeof(struct in_addr)); + m_valid = true; + } + } +} + +Ipv4Address::Ipv4Address(struct sockaddr_in& sa) +{ + m_addr = sa; + m_valid = sa.sin_family == AF_INET; +} + +Ipv4Address::~Ipv4Address() +{ +} + +Ipv4Address::operator struct sockaddr *() +{ + return (struct sockaddr *)&m_addr; +} + +Ipv4Address::operator socklen_t() +{ + return sizeof(struct sockaddr_in); +} + +void Ipv4Address::SetPort(port_t port) +{ + m_addr.sin_port = htons( port ); +} + +port_t Ipv4Address::GetPort() +{ + return ntohs( m_addr.sin_port ); +} + +bool Ipv4Address::Resolve(const std::string& hostname,struct in_addr& a) +{ + struct sockaddr_in sa; + memset(&a, 0, sizeof(a)); + if (Utility::isipv4(hostname)) + { + if (!Utility::u2ip(hostname, sa, AI_NUMERICHOST)) + return false; + a = sa.sin_addr; + return true; + } + if (!Utility::u2ip(hostname, sa)) + return false; + a = sa.sin_addr; + return true; +} + +bool Ipv4Address::Reverse(struct in_addr& a,std::string& name) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = a; + return Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name); +} + +std::string Ipv4Address::Convert(bool include_port) +{ + if (include_port) + return Convert(m_addr.sin_addr) + ":" + Utility::l2string(GetPort()); + return Convert(m_addr.sin_addr); +} + +std::string Ipv4Address::Convert(struct in_addr& a) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = a; + std::string name; + Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name, NI_NUMERICHOST); + return name; +} + +void Ipv4Address::SetAddress(struct sockaddr *sa) +{ + memcpy(&m_addr, sa, sizeof(struct sockaddr_in)); +} + +int Ipv4Address::GetFamily() +{ + return m_addr.sin_family; +} + +bool Ipv4Address::IsValid() +{ + return m_valid; +} + +bool Ipv4Address::operator==(SocketAddress& a) +{ + if (a.GetFamily() != GetFamily()) + return false; + if ((socklen_t)a != sizeof(m_addr)) + return false; + struct sockaddr *sa = a; + struct sockaddr_in *p = (struct sockaddr_in *)sa; + if (p -> sin_port != m_addr.sin_port) + return false; + if (memcmp(&p -> sin_addr, &m_addr.sin_addr, 4)) + return false; + return true; +} + +std::auto_ptr<SocketAddress> Ipv4Address::GetCopy() +{ + return std::auto_ptr<SocketAddress>(new Ipv4Address(m_addr)); +} + +std::string Ipv4Address::Reverse() +{ + std::string tmp; + Reverse(m_addr.sin_addr, tmp); + return tmp; +} + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/externals/sockets/Ipv6Address.cpp b/externals/sockets/Ipv6Address.cpp new file mode 100644 index 00000000000..3208b5098fa --- /dev/null +++ b/externals/sockets/Ipv6Address.cpp @@ -0,0 +1,247 @@ +/** + ** \file Ipv6Address.cpp + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Ipv6Address.h" +#ifdef ENABLE_IPV6 + +#include "Utility.h" +#include "Parse.h" +#ifndef _WIN32 +#include <netdb.h> +#endif +#ifdef IPPROTO_IPV6 + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +Ipv6Address::Ipv6Address(port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin6_family = AF_INET6; + m_addr.sin6_port = htons( port ); +} + +Ipv6Address::Ipv6Address(struct in6_addr& a,port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin6_family = AF_INET6; + m_addr.sin6_port = htons( port ); + m_addr.sin6_addr = a; +} + +Ipv6Address::Ipv6Address(const std::string& host,port_t port) : m_valid(false) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin6_family = AF_INET6; + m_addr.sin6_port = htons( port ); + { + struct in6_addr a; + if (Utility::u2ip(host, a)) + { + m_addr.sin6_addr = a; + m_valid = true; + } + } +} + +Ipv6Address::Ipv6Address(struct sockaddr_in6& sa) +{ + m_addr = sa; + m_valid = sa.sin6_family == AF_INET6; +} + +Ipv6Address::~Ipv6Address() +{ +} + +Ipv6Address::operator struct sockaddr *() +{ + return (struct sockaddr *)&m_addr; +} + +Ipv6Address::operator socklen_t() +{ + return sizeof(struct sockaddr_in6); +} + +void Ipv6Address::SetPort(port_t port) +{ + m_addr.sin6_port = htons( port ); +} + +port_t Ipv6Address::GetPort() +{ + return ntohs( m_addr.sin6_port ); +} + +bool Ipv6Address::Resolve(const std::string& hostname,struct in6_addr& a) +{ + struct sockaddr_in6 sa; + memset(&a, 0, sizeof(a)); + if (Utility::isipv6(hostname)) + { + if (!Utility::u2ip(hostname, sa, AI_NUMERICHOST)) + return false; + a = sa.sin6_addr; + return true; + } + if (!Utility::u2ip(hostname, sa)) + return false; + a = sa.sin6_addr; + return true; +} + +bool Ipv6Address::Reverse(struct in6_addr& a,std::string& name) +{ + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_addr = a; + return Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name); +} + +std::string Ipv6Address::Convert(bool include_port) +{ + if (include_port) + return Convert(m_addr.sin6_addr) + ":" + Utility::l2string(GetPort()); + return Convert(m_addr.sin6_addr); +} + +std::string Ipv6Address::Convert(struct in6_addr& a,bool mixed) +{ + char slask[100]; // l2ip temporary + *slask = 0; + unsigned int prev = 0; + bool skipped = false; + bool ok_to_skip = true; + if (mixed) + { + unsigned short x; + unsigned short addr16[8]; + memcpy(addr16, &a, sizeof(addr16)); + for (size_t i = 0; i < 6; i++) + { + x = ntohs(addr16[i]); + if (*slask && (x || !ok_to_skip || prev)) + strcat(slask,":"); + if (x || !ok_to_skip) + { + sprintf(slask + strlen(slask),"%x", x); + if (x && skipped) + ok_to_skip = false; + } + else + { + skipped = true; + } + prev = x; + } + x = ntohs(addr16[6]); + sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255); + x = ntohs(addr16[7]); + sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255); + } + else + { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_addr = a; + std::string name; + Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name, NI_NUMERICHOST); + return name; + } + return slask; +} + +void Ipv6Address::SetAddress(struct sockaddr *sa) +{ + memcpy(&m_addr, sa, sizeof(struct sockaddr_in6)); +} + +int Ipv6Address::GetFamily() +{ + return m_addr.sin6_family; +} + +void Ipv6Address::SetFlowinfo(uint32_t x) +{ + m_addr.sin6_flowinfo = x; +} + +uint32_t Ipv6Address::GetFlowinfo() +{ + return m_addr.sin6_flowinfo; +} + +#ifndef _WIN32 +void Ipv6Address::SetScopeId(uint32_t x) +{ + m_addr.sin6_scope_id = x; +} + +uint32_t Ipv6Address::GetScopeId() +{ + return m_addr.sin6_scope_id; +} +#endif + +bool Ipv6Address::IsValid() +{ + return m_valid; +} + +bool Ipv6Address::operator==(SocketAddress& a) +{ + if (a.GetFamily() != GetFamily()) + return false; + if ((socklen_t)a != sizeof(m_addr)) + return false; + struct sockaddr *sa = a; + struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa; + if (p -> sin6_port != m_addr.sin6_port) + return false; + if (memcmp(&p -> sin6_addr, &m_addr.sin6_addr, sizeof(struct in6_addr))) + return false; + return true; +} + +std::auto_ptr<SocketAddress> Ipv6Address::GetCopy() +{ + return std::auto_ptr<SocketAddress>(new Ipv6Address(m_addr)); +} + +std::string Ipv6Address::Reverse() +{ + std::string tmp; + Reverse(m_addr.sin6_addr, tmp); + return tmp; +} + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // IPPROTO_IPV6 +#endif // ENABLE_IPV6 + + diff --git a/externals/sockets/Lock.cpp b/externals/sockets/Lock.cpp new file mode 100644 index 00000000000..b75664cfbdc --- /dev/null +++ b/externals/sockets/Lock.cpp @@ -0,0 +1,52 @@ +/** \file Lock.cpp + ** \date 2005-08-22 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Mutex.h" +#include "Lock.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +Lock::Lock(Mutex& m) : m_mutex(m) +{ + m_mutex.Lock(); +} + +Lock::~Lock() +{ + m_mutex.Unlock(); +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/Mutex.cpp b/externals/sockets/Mutex.cpp new file mode 100644 index 00000000000..681e85cee5b --- /dev/null +++ b/externals/sockets/Mutex.cpp @@ -0,0 +1,77 @@ +/** \file Mutex.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Mutex.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +Mutex::Mutex() +{ +#ifdef _WIN32 + m_mutex = ::CreateMutex(NULL, FALSE, NULL); +#else + pthread_mutex_init(&m_mutex, NULL); +#endif +} + +Mutex::~Mutex() +{ +#ifdef _WIN32 + ::CloseHandle(m_mutex); +#else + pthread_mutex_destroy(&m_mutex); +#endif +} + +void Mutex::Lock() +{ +#ifdef _WIN32 + /*DWORD d =*/ WaitForSingleObject(m_mutex, INFINITE); + /// \todo check 'd' for result +#else + pthread_mutex_lock(&m_mutex); +#endif +} + +void Mutex::Unlock() +{ +#ifdef _WIN32 + ::ReleaseMutex(m_mutex); +#else + pthread_mutex_unlock(&m_mutex); +#endif +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/Parse.cpp b/externals/sockets/Parse.cpp new file mode 100644 index 00000000000..2967859f23d --- /dev/null +++ b/externals/sockets/Parse.cpp @@ -0,0 +1,318 @@ +/** \file Parse.cpp - parse a string + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include <stdlib.h> +#include <string.h> + +#include "Parse.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/* implementation of class Parse */ + +Parse::Parse() +:pa_the_str("") +,pa_splits("") +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s) +:pa_the_str(s) +,pa_splits("") +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s,const std::string&sp) +:pa_the_str(s) +,pa_splits(sp) +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s,const std::string&sp,short /*nospace*/) +:pa_the_str(s) +,pa_splits(sp) +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(1) +,pa_quote(false) +{ +} + +Parse::~Parse() +{ +} + +#define C ((pa_the_ptr<pa_the_str.size()) ? pa_the_str[pa_the_ptr] : 0) + +short Parse::issplit(const char c) +{ + for (size_t i = 0; i < pa_splits.size(); i++) + if (pa_splits[i] == c) + return 1; + return 0; +} + +void Parse::getsplit() +{ + size_t x; + + if (C == '=') + { + x = pa_the_ptr++; + } else + { + while (C && (issplit(C))) + pa_the_ptr++; + x = pa_the_ptr; + while (C && !issplit(C) && C != '=') + pa_the_ptr++; + } + if (x == pa_the_ptr && C == '=') + pa_the_ptr++; + pa_ord = (x < pa_the_str.size()) ? pa_the_str.substr(x,pa_the_ptr - x) : ""; +} + +std::string Parse::getword() +{ + size_t x; + int disabled = 0; + int quote = 0; + int rem = 0; + + if (pa_nospace) + { + while (C && issplit(C)) + pa_the_ptr++; + x = pa_the_ptr; + while (C && !issplit(C) && (C != pa_breakchar || !pa_breakchar || disabled)) + { + if (pa_breakchar && C == pa_disable) + disabled = 1; + if (pa_breakchar && C == pa_enable) + disabled = 0; + if (pa_quote && C == '"') + quote = 1; + pa_the_ptr++; + while (quote && C && C != '"') + { + pa_the_ptr++; + } + if (pa_quote && C == '"') + { + pa_the_ptr++; + } + quote = 0; + } + } else + { + if (C == pa_breakchar && pa_breakchar) + { + x = pa_the_ptr++; + rem = 1; + } else + { + while (C && (C == ' ' || C == 9 || C == 13 || C == 10 || issplit(C))) + pa_the_ptr++; + x = pa_the_ptr; + while (C && C != ' ' && C != 9 && C != 13 && C != 10 && !issplit(C) && + (C != pa_breakchar || !pa_breakchar || disabled)) + { + if (pa_breakchar && C == pa_disable) + disabled = 1; + if (pa_breakchar && C == pa_enable) + disabled = 0; + if (pa_quote && C == '"') + { + quote = 1; + pa_the_ptr++; + while (quote && C && C != '"') + { + pa_the_ptr++; + } + if (pa_quote && C == '"') + { + pa_the_ptr++; + } + } + else + pa_the_ptr++; + quote = 0; + } + pa_the_ptr++; + rem = 1; + } + if (x == pa_the_ptr && C == pa_breakchar && pa_breakchar) + pa_the_ptr++; + } + if (x < pa_the_str.size()) + { + pa_ord = pa_the_str.substr(x,pa_the_ptr - x - rem); + } + else + { + pa_ord = ""; + } + return pa_ord; +} + +void Parse::getword(std::string&s) +{ + s = Parse::getword(); +} + +void Parse::getsplit(std::string&s) +{ + Parse::getsplit(); + s = pa_ord; +} + +void Parse::getword(std::string&s,std::string&fill,int l) +{ + Parse::getword(); + s = ""; + while (s.size() + pa_ord.size() < (size_t)l) + s += fill; + s += pa_ord; +} + +std::string Parse::getrest() +{ + std::string s; + while (C && (C == ' ' || C == 9 || issplit(C))) + pa_the_ptr++; + s = (pa_the_ptr < pa_the_str.size()) ? pa_the_str.substr(pa_the_ptr) : ""; + return s; +} + +void Parse::getrest(std::string&s) +{ + while (C && (C == ' ' || C == 9 || issplit(C))) + pa_the_ptr++; + s = (pa_the_ptr < pa_the_str.size()) ? pa_the_str.substr(pa_the_ptr) : ""; +} + +long Parse::getvalue() +{ + Parse::getword(); + return atol(pa_ord.c_str()); +} + +void Parse::setbreak(const char c) +{ + pa_breakchar = c; +} + +int Parse::getwordlen() +{ + size_t x,y = pa_the_ptr,len; + + if (C == pa_breakchar && pa_breakchar) + { + x = pa_the_ptr++; + } else + { + while (C && (C == ' ' || C == 9 || C == 13 || C == 10 || issplit(C))) + pa_the_ptr++; + x = pa_the_ptr; + while (C && C != ' ' && C != 9 && C != 13 && C != 10 && !issplit(C) && (C != pa_breakchar || !pa_breakchar)) + pa_the_ptr++; + } + if (x == pa_the_ptr && C == pa_breakchar && pa_breakchar) + pa_the_ptr++; + len = pa_the_ptr - x; + pa_the_ptr = y; + return (int)len; +} + +int Parse::getrestlen() +{ + size_t y = pa_the_ptr; + size_t len; + + while (C && (C == ' ' || C == 9 || issplit(C))) + pa_the_ptr++; + len = strlen(pa_the_str.c_str() + pa_the_ptr); + pa_the_ptr = y; + return (int)len; +} + +void Parse::getline() +{ + size_t x; + + x = pa_the_ptr; + while (C && C != 13 && C != 10) + pa_the_ptr++; + pa_ord = (x < pa_the_str.size()) ? pa_the_str.substr(x,pa_the_ptr - x) : ""; + if (C == 13) + pa_the_ptr++; + if (C == 10) + pa_the_ptr++; +} + +void Parse::getline(std::string&s) +{ + getline(); + s = pa_ord; +} + +/* end of implementation of class Parse */ +/***************************************************/ +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/ResolvServer.cpp b/externals/sockets/ResolvServer.cpp new file mode 100644 index 00000000000..3c8a7de6bc0 --- /dev/null +++ b/externals/sockets/ResolvServer.cpp @@ -0,0 +1,92 @@ +/** \file ResolvServer.cpp + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "ResolvServer.h" +#ifdef ENABLE_RESOLVER +#include "StdoutLog.h" +#include "ListenSocket.h" +#include "ResolvSocket.h" +#include "SocketHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +ResolvServer::ResolvServer(port_t port) +:Thread() +,m_quit(false) +,m_port(port) +,m_ready(false) +{ +} + +ResolvServer::~ResolvServer() +{ +} + +void ResolvServer::Run() +{ +// StdoutLog log; + SocketHandler h; + ListenSocket<ResolvSocket> l(h); + + if (l.Bind("127.0.0.1", m_port)) + { + return; + } + h.Add(&l); + + m_ready = true; + while (!m_quit && IsRunning() ) + { + h.Select(0, 500000); + } + SetRunning(false); +} + +void ResolvServer::Quit() +{ + m_quit = true; +} + +bool ResolvServer::Ready() +{ + return m_ready; +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER + + diff --git a/externals/sockets/ResolvSocket.cpp b/externals/sockets/ResolvSocket.cpp new file mode 100644 index 00000000000..636de276426 --- /dev/null +++ b/externals/sockets/ResolvSocket.cpp @@ -0,0 +1,426 @@ +/** \file ResolvSocket.cpp + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif +#else +#include <netdb.h> +#endif +#include "ResolvSocket.h" +#ifdef ENABLE_RESOLVER +#include "Utility.h" +#include "Parse.h" +#include "ISocketHandler.h" +#include "Lock.h" +#include "Mutex.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +//#ifdef _DEBUG +//#define DEB(x) x +//#else +#define DEB(x) +//#endif + +// static +ResolvSocket::cache_t ResolvSocket::m_cache; +ResolvSocket::timeout_t ResolvSocket::m_cache_to; +Mutex ResolvSocket::m_cache_mutex; + +ResolvSocket::ResolvSocket(ISocketHandler& h) +:TcpSocket(h) +,m_bServer(false) +,m_parent(NULL) +#ifdef ENABLE_IPV6 +,m_resolve_ipv6(false) +#endif +,m_cached(false) +{ + SetLineProtocol(); +} + +ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, const std::string& host, port_t port, bool ipv6) +:TcpSocket(h) +,m_bServer(false) +,m_parent(parent) +,m_resolv_host(host) +,m_resolv_port(port) +#ifdef ENABLE_IPV6 +,m_resolve_ipv6(ipv6) +#endif +,m_cached(false) +{ + SetLineProtocol(); +} + +ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, ipaddr_t a) +:TcpSocket(h) +,m_bServer(false) +,m_parent(parent) +,m_resolv_port(0) +,m_resolv_address(a) +#ifdef ENABLE_IPV6 +,m_resolve_ipv6(false) +#endif +,m_cached(false) +{ + SetLineProtocol(); +} + +#ifdef ENABLE_IPV6 +ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, in6_addr& a) +:TcpSocket(h) +,m_bServer(false) +,m_parent(parent) +,m_resolv_port(0) +,m_resolve_ipv6(true) +,m_resolv_address6(a) +,m_cached(false) +{ + SetLineProtocol(); +} +#endif + +ResolvSocket::~ResolvSocket() +{ +} + +void ResolvSocket::OnLine(const std::string& line) +{ + Parse pa(line, ":"); + if (m_bServer) + { + m_query = pa.getword(); + m_data = pa.getrest(); +DEB( fprintf(stderr, " *** ResolvSocket server; query=%s, data=%s\n", m_query.c_str(), m_data.c_str());) + // %! check cache + { + Lock lock(m_cache_mutex); + if (m_cache[m_query].find(m_data) != m_cache[m_query].end()) + { + if (time(NULL) - m_cache_to[m_query][m_data] < 3600) // ttl + { + std::string result = m_cache[m_query][m_data]; +DEB(fprintf(stderr, " *** Returning cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), result.c_str());) + Send("Cached\n"); + if (!result.size()) /* failed */ + { + Send("Failed\n\n"); + SetCloseAndDelete(); + return; + } + else + if (m_query == "gethostbyname") + { + Send("A: " + result + "\n\n"); + SetCloseAndDelete(); + return; + } + else +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (m_query == "gethostbyname2") + { + Send("AAAA: " + result + "\n\n"); + SetCloseAndDelete(); + return; + } + else +#endif +#endif + if (m_query == "gethostbyaddr") + { + Send("Name: " + result + "\n\n"); + SetCloseAndDelete(); + return; + } + } + } + } + if (!Detach()) // detach failed? + { + SetCloseAndDelete(); + } + return; + } + std::string key = pa.getword(); + std::string value = pa.getrest(); +DEB( fprintf(stderr, " *** ResolvSocket response; %s: %s\n", key.c_str(), value.c_str());) + + if (key == "Cached") + { + m_cached = true; + } + else + if (key == "Failed" && m_parent) + { +DEB( fprintf(stderr, " ************ Resolve failed\n");) + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + m_parent -> OnResolveFailed(m_resolv_id); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } + else + if (key == "Name" && !m_resolv_host.size() && m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + m_parent -> OnReverseResolved(m_resolv_id, value); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } + else + if (key == "A" && m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + ipaddr_t l; + Utility::u2ip(value, l); // ip2ipaddr_t + m_parent -> OnResolved(m_resolv_id, l, m_resolv_port); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; // always use first ip in case there are several + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + else + if (key == "AAAA" && m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + in6_addr a; + Utility::u2ip(value, a); + m_parent -> OnResolved(m_resolv_id, a, m_resolv_port); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } +#endif +#endif +} + +void ResolvSocket::OnDetached() +{ +DEB( fprintf(stderr, " *** ResolvSocket::OnDetached(); query=%s, data=%s\n", m_query.c_str(), m_data.c_str());) + if (m_query == "gethostbyname") + { + struct sockaddr_in sa; + if (Utility::u2ip(m_data, sa)) + { + std::string ip; + Utility::l2ip(sa.sin_addr, ip); + Send("A: " + ip + "\n"); + } + else + { + Send("Failed\n"); + } + Send("\n"); + } + else +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (m_query == "gethostbyname2") + { + struct sockaddr_in6 sa; + if (Utility::u2ip(m_data, sa)) + { + std::string ip; + Utility::l2ip(sa.sin6_addr, ip); + Send("AAAA: " + ip + "\n"); + } + else + { + Send("Failed\n"); + } + Send("\n"); + } + else +#endif +#endif + if (m_query == "gethostbyaddr") + { + if (Utility::isipv4( m_data )) + { + struct sockaddr_in sa; + if (!Utility::u2ip(m_data, sa, AI_NUMERICHOST)) + { + Send("Failed: convert to sockaddr_in failed\n"); + } + else + { + std::string name; + if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), name)) + { + Send("Failed: ipv4 reverse lookup of " + m_data + "\n"); + } + else + { + Send("Name: " + name + "\n"); + } + } + } + else +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (Utility::isipv6( m_data )) + { + struct sockaddr_in6 sa; + if (!Utility::u2ip(m_data, sa, AI_NUMERICHOST)) + { + Send("Failed: convert to sockaddr_in6 failed\n"); + } + else + { + std::string name; + if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), name)) + { + Send("Failed: ipv6 reverse lookup of " + m_data + "\n"); + } + else + { + Send("Name: " + name + "\n"); + } + } + } + else +#endif +#endif + { + Send("Failed: malformed address\n"); + } + Send("\n"); + } + else + { + std::string msg = "Unknown query type: " + m_query; + Handler().LogError(this, "OnDetached", 0, msg); + Send("Unknown\n\n"); + } + SetCloseAndDelete(); +} + +void ResolvSocket::OnConnect() +{ + if (!m_resolv_host.empty()) + { +#ifdef ENABLE_IPV6 + std::string msg = (m_resolve_ipv6 ? "gethostbyname2 " : "gethostbyname ") + m_resolv_host + "\n"; + m_query = m_resolve_ipv6 ? "gethostbyname2" : "gethostbyname"; +#else + std::string msg = "gethostbyname " + m_resolv_host + "\n"; + m_query = "gethostbyname"; +#endif + m_data = m_resolv_host; + Send( msg ); + return; + } +#ifdef ENABLE_IPV6 + if (m_resolve_ipv6) + { + std::string tmp; + Utility::l2ip(m_resolv_address6, tmp); + m_query = "gethostbyaddr"; + m_data = tmp; + std::string msg = "gethostbyaddr " + tmp + "\n"; + Send( msg ); + } +#endif + std::string tmp; + Utility::l2ip(m_resolv_address, tmp); + m_query = "gethostbyaddr"; + m_data = tmp; + std::string msg = "gethostbyaddr " + tmp + "\n"; + Send( msg ); +} + +void ResolvSocket::OnDelete() +{ + if (m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + m_parent -> OnResolveFailed(m_resolv_id); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); + std::string value; +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER + + diff --git a/externals/sockets/Socket.cpp b/externals/sockets/Socket.cpp new file mode 100644 index 00000000000..f53cd27621e --- /dev/null +++ b/externals/sockets/Socket.cpp @@ -0,0 +1,1726 @@ +/** \file Socket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Socket.h" +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include <stdlib.h> +#else +#include <errno.h> +#include <netdb.h> +#endif +#include <ctype.h> +#include <fcntl.h> + +#include "ISocketHandler.h" +#include "Utility.h" + +#include "SocketAddress.h" +#include "SocketHandler.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif +#include "Ipv4Address.h" + +//#ifdef _DEBUG +//#define DEB(x) x; fflush(stderr); +//#else +#define DEB(x) +//#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +// statics +#ifdef _WIN32 +WSAInitializer Socket::m_winsock_init; +#endif + +Socket::Socket(ISocketHandler& h) +//:m_flags(0) +:m_handler(h) +,m_socket( INVALID_SOCKET ) +,m_bDel(false) +,m_bClose(false) +,m_tCreate(time(NULL)) +,m_parent(NULL) +,m_b_disable_read(false) +,m_connected(false) +,m_b_erased_by_handler(false) +,m_tClose(0) +,m_client_remote_address(NULL) +,m_remote_address(NULL) +,m_traffic_monitor(NULL) +,m_bLost(false) +#ifdef HAVE_OPENSSL +,m_b_enable_ssl(false) +,m_b_ssl(false) +,m_b_ssl_server(false) +#endif +#ifdef ENABLE_IPV6 +,m_ipv6(false) +#endif +#ifdef ENABLE_POOL +,m_socket_type(0) +,m_bClient(false) +,m_bRetain(false) +#endif +#ifdef ENABLE_SOCKS4 +,m_bSocks4(false) +,m_socks4_host(h.GetSocks4Host()) +,m_socks4_port(h.GetSocks4Port()) +,m_socks4_userid(h.GetSocks4Userid()) +#endif +#ifdef ENABLE_DETACH +,m_detach(false) +,m_detached(false) +,m_pThread(NULL) +,m_slave_handler(NULL) +#endif +{ +} + +Socket::~Socket() +{ + Handler().Remove(this); + if (m_socket != INVALID_SOCKET +#ifdef ENABLE_POOL + && !m_bRetain +#endif + ) + { + Close(); + } +} + +void Socket::Init() +{ +} + +void Socket::OnRead() +{ +} + +void Socket::OnWrite() +{ +} + +void Socket::OnException() +{ + // %! exception doesn't always mean something bad happened, this code should be reworked + // errno valid here? + int err = SoError(); + Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} + +void Socket::OnDelete() +{ +} + +void Socket::OnConnect() +{ +} + +void Socket::OnAccept() +{ +} + +int Socket::Close() +{ + if (m_socket == INVALID_SOCKET) // this could happen + { + Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING); + return 0; + } + int n; + if ((n = closesocket(m_socket)) == -1) + { + // failed... + Handler().LogError(this, "close", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + Handler().Set(m_socket, false, false, false); // remove from fd_set's + Handler().AddList(m_socket, LIST_CALLONCONNECT, false); +#ifdef ENABLE_DETACH + Handler().AddList(m_socket, LIST_DETACH, false); +#endif + Handler().AddList(m_socket, LIST_TIMEOUT, false); + Handler().AddList(m_socket, LIST_RETRY, false); + Handler().AddList(m_socket, LIST_CLOSE, false); + m_socket = INVALID_SOCKET; + return n; +} + +SOCKET Socket::CreateSocket(int af,int type, const std::string& protocol) +{ + struct protoent *p = NULL; + SOCKET s; + +#ifdef ENABLE_POOL + m_socket_type = type; + m_socket_protocol = protocol; +#endif + if (!protocol.empty()) + { + p = getprotobyname( protocol.c_str() ); + if (!p) + { + Handler().LogError(this, "getprotobyname", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +#ifdef ENABLE_EXCEPTIONS + throw Exception(std::string("getprotobyname() failed: ") + StrError(Errno)); +#endif + return INVALID_SOCKET; + } + } + int protno = p ? p -> p_proto : 0; + + s = socket(af, type, protno); + if (s == INVALID_SOCKET) + { + Handler().LogError(this, "socket", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +#ifdef ENABLE_EXCEPTIONS + throw Exception(std::string("socket() failed: ") + StrError(Errno)); +#endif + return INVALID_SOCKET; + } + Attach(s); + OnOptions(af, type, protno, s); + Attach(INVALID_SOCKET); + return s; +} + +void Socket::Attach(SOCKET s) +{ + m_socket = s; +} + +SOCKET Socket::GetSocket() +{ + return m_socket; +} + +void Socket::SetDeleteByHandler(bool x) +{ + m_bDel = x; +} + +bool Socket::DeleteByHandler() +{ + return m_bDel; +} + +void Socket::SetCloseAndDelete(bool x) +{ + if (x != m_bClose) + { + Handler().AddList(m_socket, LIST_CLOSE, x); + m_bClose = x; + if (x) + { + m_tClose = time(NULL); + } + } +} + +bool Socket::CloseAndDelete() +{ + return m_bClose; +} + +void Socket::SetRemoteAddress(SocketAddress& ad) //struct sockaddr* sa, socklen_t l) +{ + m_remote_address = ad.GetCopy(); +} + +std::auto_ptr<SocketAddress> Socket::GetRemoteSocketAddress() +{ + return m_remote_address -> GetCopy(); +} + +ISocketHandler& Socket::Handler() const +{ +#ifdef ENABLE_DETACH + if (IsDetached()) + return *m_slave_handler; +#endif + return m_handler; +} + +ISocketHandler& Socket::MasterHandler() const +{ + return m_handler; +} + +ipaddr_t Socket::GetRemoteIP4() +{ + ipaddr_t l = 0; +#ifdef ENABLE_IPV6 + if (m_ipv6) + { + Handler().LogError(this, "GetRemoteIP4", 0, "get ipv4 address for ipv6 socket", LOG_LEVEL_WARNING); + } +#endif + if (m_remote_address.get() != NULL) + { + struct sockaddr *p = *m_remote_address; + struct sockaddr_in *sa = (struct sockaddr_in *)p; + memcpy(&l, &sa -> sin_addr, sizeof(struct in_addr)); + } + return l; +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +struct in6_addr Socket::GetRemoteIP6() +{ + if (!m_ipv6) + { + Handler().LogError(this, "GetRemoteIP6", 0, "get ipv6 address for ipv4 socket", LOG_LEVEL_WARNING); + } + struct sockaddr_in6 fail; + if (m_remote_address.get() != NULL) + { + struct sockaddr *p = *m_remote_address; + memcpy(&fail, p, sizeof(struct sockaddr_in6)); + } + else + { + memset(&fail, 0, sizeof(struct sockaddr_in6)); + } + return fail.sin6_addr; +} +#endif +#endif + +port_t Socket::GetRemotePort() +{ + if (!m_remote_address.get()) + { + return 0; + } + return m_remote_address -> GetPort(); +} + +std::string Socket::GetRemoteAddress() +{ + if (!m_remote_address.get()) + { + return ""; + } + return m_remote_address -> Convert(false); +} + +std::string Socket::GetRemoteHostname() +{ + if (!m_remote_address.get()) + { + return ""; + } + return m_remote_address -> Reverse(); +} + +bool Socket::SetNonblocking(bool bNb) +{ +#ifdef _WIN32 + unsigned long l = bNb ? 1 : 0; + int n = ioctlsocket(m_socket, FIONBIO, &l); + if (n != 0) + { + Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, ""); + return false; + } + return true; +#else + if (bNb) + { + if (fcntl(m_socket, F_SETFL, O_NONBLOCK) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + else + { + if (fcntl(m_socket, F_SETFL, 0) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + return true; +#endif +} + +bool Socket::SetNonblocking(bool bNb, SOCKET s) +{ +#ifdef _WIN32 + unsigned long l = bNb ? 1 : 0; + int n = ioctlsocket(s, FIONBIO, &l); + if (n != 0) + { + Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, ""); + return false; + } + return true; +#else + if (bNb) + { + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + else + { + if (fcntl(s, F_SETFL, 0) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + return true; +#endif +} + +void Socket::Set(bool bRead, bool bWrite, bool bException) +{ + Handler().Set(m_socket, bRead, bWrite, bException); +} + +bool Socket::Ready() +{ + if (m_socket != INVALID_SOCKET && !CloseAndDelete()) + return true; + return false; +} + +void Socket::OnLine(const std::string& ) +{ +} + +void Socket::OnConnectFailed() +{ +} + +Socket *Socket::GetParent() +{ + return m_parent; +} + +void Socket::SetParent(Socket *x) +{ + m_parent = x; +} + +port_t Socket::GetPort() +{ + Handler().LogError(this, "GetPort", 0, "GetPort only implemented for ListenSocket", LOG_LEVEL_WARNING); + return 0; +} + +bool Socket::OnConnectRetry() +{ + return true; +} + +#ifdef ENABLE_RECONNECT +void Socket::OnReconnect() +{ +} +#endif + +time_t Socket::Uptime() +{ + return time(NULL) - m_tCreate; +} + +#ifdef ENABLE_IPV6 +void Socket::SetIpv6(bool x) +{ + m_ipv6 = x; +} + +bool Socket::IsIpv6() +{ + return m_ipv6; +} +#endif + +void Socket::DisableRead(bool x) +{ + m_b_disable_read = x; +} + +bool Socket::IsDisableRead() +{ + return m_b_disable_read; +} + +void Socket::SendBuf(const char *,size_t,int) +{ +} + +void Socket::Send(const std::string&,int) +{ +} + +void Socket::SetConnected(bool x) +{ + m_connected = x; +} + +bool Socket::IsConnected() +{ + return m_connected; +} + +void Socket::OnDisconnect() +{ +} + +void Socket::SetLost() +{ + m_bLost = true; +} + +bool Socket::Lost() +{ + return m_bLost; +} + +void Socket::SetErasedByHandler(bool x) +{ + m_b_erased_by_handler = x; +} + +bool Socket::ErasedByHandler() +{ + return m_b_erased_by_handler; +} + +time_t Socket::TimeSinceClose() +{ + return time(NULL) - m_tClose; +} + +void Socket::SetClientRemoteAddress(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SetClientRemoteAddress", 0, "remote address not valid", LOG_LEVEL_ERROR); + } + m_client_remote_address = ad.GetCopy(); +} + +std::auto_ptr<SocketAddress> Socket::GetClientRemoteAddress() +{ + if (!m_client_remote_address.get()) + { + Handler().LogError(this, "GetClientRemoteAddress", 0, "remote address not yet set", LOG_LEVEL_ERROR); + } + return m_client_remote_address -> GetCopy(); +} + +uint64_t Socket::GetBytesSent(bool) +{ + return 0; +} + +uint64_t Socket::GetBytesReceived(bool) +{ + return 0; +} + +#ifdef HAVE_OPENSSL +void Socket::OnSSLConnect() +{ +} + +void Socket::OnSSLAccept() +{ +} + +bool Socket::SSLNegotiate() +{ + return false; +} + +bool Socket::IsSSL() +{ + return m_b_enable_ssl; +} + +void Socket::EnableSSL(bool x) +{ + m_b_enable_ssl = x; +} + +bool Socket::IsSSLNegotiate() +{ + return m_b_ssl; +} + +void Socket::SetSSLNegotiate(bool x) +{ + m_b_ssl = x; +} + +bool Socket::IsSSLServer() +{ + return m_b_ssl_server; +} + +void Socket::SetSSLServer(bool x) +{ + m_b_ssl_server = x; +} + +void Socket::OnSSLConnectFailed() +{ +} + +void Socket::OnSSLAcceptFailed() +{ +} +#endif // HAVE_OPENSSL + +#ifdef ENABLE_POOL +void Socket::CopyConnection(Socket *sock) +{ + Attach( sock -> GetSocket() ); +#ifdef ENABLE_IPV6 + SetIpv6( sock -> IsIpv6() ); +#endif + SetSocketType( sock -> GetSocketType() ); + SetSocketProtocol( sock -> GetSocketProtocol() ); + + SetClientRemoteAddress( *sock -> GetClientRemoteAddress() ); + SetRemoteAddress( *sock -> GetRemoteSocketAddress() ); +} + +void Socket::SetIsClient() +{ + m_bClient = true; +} + +void Socket::SetSocketType(int x) +{ + m_socket_type = x; +} + +int Socket::GetSocketType() +{ + return m_socket_type; +} + +void Socket::SetSocketProtocol(const std::string& x) +{ + m_socket_protocol = x; +} + +const std::string& Socket::GetSocketProtocol() +{ + return m_socket_protocol; +} + +void Socket::SetRetain() +{ + if (m_bClient) m_bRetain = true; +} + +bool Socket::Retain() +{ + return m_bRetain; +} + +#endif // ENABLE_POOL + +#ifdef ENABLE_SOCKS4 +void Socket::OnSocks4Connect() +{ + Handler().LogError(this, "OnSocks4Connect", 0, "Use with TcpSocket only"); +} + +void Socket::OnSocks4ConnectFailed() +{ + Handler().LogError(this, "OnSocks4ConnectFailed", 0, "Use with TcpSocket only"); +} + +bool Socket::OnSocks4Read() +{ + Handler().LogError(this, "OnSocks4Read", 0, "Use with TcpSocket only"); + return true; +} + +void Socket::SetSocks4Host(const std::string& host) +{ + Utility::u2ip(host, m_socks4_host); +} + +bool Socket::Socks4() +{ + return m_bSocks4; +} + +void Socket::SetSocks4(bool x) +{ + m_bSocks4 = x; +} + +void Socket::SetSocks4Host(ipaddr_t a) +{ + m_socks4_host = a; +} + +void Socket::SetSocks4Port(port_t p) +{ + m_socks4_port = p; +} + +void Socket::SetSocks4Userid(const std::string& x) +{ + m_socks4_userid = x; +} + +ipaddr_t Socket::GetSocks4Host() +{ + return m_socks4_host; +} + +port_t Socket::GetSocks4Port() +{ + return m_socks4_port; +} + +const std::string& Socket::GetSocks4Userid() +{ + return m_socks4_userid; +} +#endif // ENABLE_SOCKS4 + +#ifdef ENABLE_DETACH +bool Socket::Detach() +{ + if (!DeleteByHandler()) + return false; + if (m_pThread) + return false; + if (m_detached) + return false; + SetDetach(); + return true; +} + +void Socket::DetachSocket() +{ + SetDetached(); + m_pThread = new SocketThread(this); + m_pThread -> SetRelease(true); +} + +void Socket::OnDetached() +{ +} + +void Socket::SetDetach(bool x) +{ + Handler().AddList(m_socket, LIST_DETACH, x); + m_detach = x; +} + +bool Socket::IsDetach() +{ + return m_detach; +} + +void Socket::SetDetached(bool x) +{ + m_detached = x; +} + +const bool Socket::IsDetached() const +{ + return m_detached; +} + +void Socket::SetSlaveHandler(ISocketHandler *p) +{ + m_slave_handler = p; +} + +Socket::SocketThread::SocketThread(Socket *p) +:Thread(false) +,m_socket(p) +{ + // Creator will release +} + +Socket::SocketThread::~SocketThread() +{ + if (IsRunning()) + { + SetRelease(true); + SetRunning(false); +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } +} + +void Socket::SocketThread::Run() +{ + SocketHandler h; + h.SetSlave(); + h.Add(m_socket); + m_socket -> SetSlaveHandler(&h); + m_socket -> OnDetached(); + while (h.GetCount() && IsRunning()) + { + h.Select(0, 500000); + } + // m_socket now deleted oops + // yeah oops m_socket delete its socket thread, that means this + // so Socket will no longer delete its socket thread, instead we do this: + SetDeleteOnExit(); +} +#endif // ENABLE_DETACH + +#ifdef ENABLE_RESOLVER +int Socket::Resolve(const std::string& host,port_t port) +{ + return Handler().Resolve(this, host, port); +} + +#ifdef ENABLE_IPV6 +int Socket::Resolve6(const std::string& host,port_t port) +{ + return Handler().Resolve6(this, host, port); +} +#endif + +int Socket::Resolve(ipaddr_t a) +{ + return Handler().Resolve(this, a); +} + +#ifdef ENABLE_IPV6 +int Socket::Resolve(in6_addr& a) +{ + return Handler().Resolve(this, a); +} +#endif + +void Socket::OnResolved(int,ipaddr_t,port_t) +{ +} + +#ifdef ENABLE_IPV6 +void Socket::OnResolved(int,in6_addr&,port_t) +{ +} +#endif + +void Socket::OnReverseResolved(int,const std::string&) +{ +} + +void Socket::OnResolveFailed(int) +{ +} +#endif // ENABLE_RESOLVER + +/* IP options */ + +bool Socket::SetIpOptions(const void *p, socklen_t len) +{ +#ifdef IP_OPTIONS + if (setsockopt(GetSocket(), IPPROTO_IP, IP_OPTIONS, (char *)p, len) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_OPTIONS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_OPTIONS", LOG_LEVEL_INFO); + return false; +#endif +} + +#ifdef IP_PKTINFO +bool Socket::SetIpPktinfo(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_PKTINFO, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_PKTINFO)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef IP_RECVTOS +bool Socket::SetIpRecvTOS(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVTOS, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVTOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef IP_RECVTTL +bool Socket::SetIpRecvTTL(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVTTL, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVTTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef IP_RECVOPTS +bool Socket::SetIpRecvopts(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVOPTS, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVOPTS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef IP_RETOPTS +bool Socket::SetIpRetopts(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RETOPTS, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RETOPTS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +bool Socket::SetIpTOS(unsigned char tos) +{ +#ifdef IP_TOS + if (setsockopt(GetSocket(), IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_TOS", LOG_LEVEL_INFO); + return false; +#endif +} + +unsigned char Socket::IpTOS() +{ + unsigned char tos = 0; +#ifdef IP_TOS + socklen_t len = sizeof(tos); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_TOS, (char *)&tos, &len) == -1) + { + Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_TOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "ip option not available", 0, "IP_TOS", LOG_LEVEL_INFO); +#endif + return tos; +} + +bool Socket::SetIpTTL(int ttl) +{ +#ifdef IP_TTL + if (setsockopt(GetSocket(), IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_TTL", LOG_LEVEL_INFO); + return false; +#endif +} + +int Socket::IpTTL() +{ + int ttl = 0; +#ifdef IP_TTL + socklen_t len = sizeof(ttl); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_TTL, (char *)&ttl, &len) == -1) + { + Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "ip option not available", 0, "IP_TTL", LOG_LEVEL_INFO); +#endif + return ttl; +} + +bool Socket::SetIpHdrincl(bool x) +{ +#ifdef IP_HDRINCL + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_HDRINCL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_HDRINCL", LOG_LEVEL_INFO); + return false; +#endif +} + +#ifdef IP_RECVERR +bool Socket::SetIpRecverr(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVERR, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVERR)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef IP_MTU_DISCOVER +bool Socket::SetIpMtudiscover(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_MTU_DISCOVER, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MTU_DISCOVER)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef IP_MTU +int Socket::IpMtu() +{ + int mtu = 0; + socklen_t len = sizeof(mtu); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_MTU, (char *)&mtu, &len) == -1) + { + Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_MTU)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } + return mtu; +} +#endif + +#ifdef IP_ROUTER_ALERT +bool Socket::SetIpRouterAlert(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_ROUTER_ALERT, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ROUTER_ALERT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +bool Socket::SetIpMulticastTTL(int ttl) +{ +#ifdef IP_MULTICAST_TTL + if (setsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_TTL", LOG_LEVEL_INFO); + return false; +#endif +} + +int Socket::IpMulticastTTL() +{ + int ttl = 0; +#ifdef IP_MULTICAST_TTL + socklen_t len = sizeof(ttl); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, &len) == -1) + { + Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_MULTICAST_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_TTL", LOG_LEVEL_INFO); +#endif + return ttl; +} + +bool Socket::SetMulticastLoop(bool x) +{ +#ifdef IP_MULTICAST_LOOP + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_LOOP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_LOOP", LOG_LEVEL_INFO); + return false; +#endif +} + +#ifdef LINUX +bool Socket::IpAddMembership(struct ip_mreqn& ref) +{ +#ifdef IP_ADD_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreqn)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_ADD_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} +#endif + +bool Socket::IpAddMembership(struct ip_mreq& ref) +{ +#ifdef IP_ADD_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_ADD_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} + +#ifdef LINUX +bool Socket::IpDropMembership(struct ip_mreqn& ref) +{ +#ifdef IP_DROP_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreqn)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_DROP_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} +#endif + +bool Socket::IpDropMembership(struct ip_mreq& ref) +{ +#ifdef IP_DROP_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_DROP_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} + +/* SOCKET options */ + +bool Socket::SetSoReuseaddr(bool x) +{ +#ifdef SO_REUSEADDR + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_REUSEADDR)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_REUSEADDR", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoKeepalive(bool x) +{ +#ifdef SO_KEEPALIVE + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_KEEPALIVE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_KEEPALIVE", LOG_LEVEL_INFO); + return false; +#endif +} + +#ifdef SO_NOSIGPIPE +bool Socket::SetSoNosigpipe(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_NOSIGPIPE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +bool Socket::SoAcceptconn() +{ + int value = 0; +#ifdef SO_ACCEPTCONN + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_ACCEPTCONN, (char *)&value, &len) == -1) + { + Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_ACCEPTCONN)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_ACCEPTCONN", LOG_LEVEL_INFO); +#endif + return value ? true : false; +} + +#ifdef SO_BSDCOMPAT +bool Socket::SetSoBsdcompat(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BSDCOMPAT, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BSDCOMPAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef SO_BINDTODEVICE +bool Socket::SetSoBindtodevice(const std::string& intf) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BINDTODEVICE, (char *)intf.c_str(), intf.size()) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BINDTODEVICE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +bool Socket::SetSoBroadcast(bool x) +{ +#ifdef SO_BROADCAST + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BROADCAST)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_BROADCAST", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoDebug(bool x) +{ +#ifdef SO_DEBUG + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_DEBUG, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_DEBUG)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_DEBUG", LOG_LEVEL_INFO); + return false; +#endif +} + +int Socket::SoError() +{ + int value = 0; +#ifdef SO_ERROR + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_ERROR, (char *)&value, &len) == -1) + { + Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_ERROR)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_ERROR", LOG_LEVEL_INFO); +#endif + return value; +} + +bool Socket::SetSoDontroute(bool x) +{ +#ifdef SO_DONTROUTE + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_DONTROUTE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_DONTROUTE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_DONTROUTE", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoLinger(int onoff, int linger) +{ +#ifdef SO_LINGER + struct linger stl; + stl.l_onoff = onoff; + stl.l_linger = linger; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_LINGER, (char *)&stl, sizeof(stl)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_LINGER)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_LINGER", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoOobinline(bool x) +{ +#ifdef SO_OOBINLINE + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_OOBINLINE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_OOBINLINE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_OOBINLINE", LOG_LEVEL_INFO); + return false; +#endif +} + +#ifdef SO_PASSCRED +bool Socket::SetSoPasscred(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_PASSCRED, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PASSCRED)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef SO_PEERCRED +bool Socket::SoPeercred(struct ucred& ucr) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_PEERCRED, (char *)&ucr, sizeof(ucr)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PEERCRED)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef SO_PRIORITY +bool Socket::SetSoPriority(int x) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_PRIORITY, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PRIORITY)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +bool Socket::SetSoRcvlowat(int x) +{ +#ifdef SO_RCVLOWAT + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVLOWAT, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVLOWAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVLOWAT", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoSndlowat(int x) +{ +#ifdef SO_SNDLOWAT + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDLOWAT, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDLOWAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDLOWAT", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoRcvtimeo(struct timeval& tv) +{ +#ifdef SO_RCVTIMEO + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVTIMEO)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVTIMEO", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoSndtimeo(struct timeval& tv) +{ +#ifdef SO_SNDTIMEO + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDTIMEO)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDTIMEO", LOG_LEVEL_INFO); + return false; +#endif +} + +bool Socket::SetSoRcvbuf(int x) +{ +#ifdef SO_RCVBUF + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUF, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVBUF", LOG_LEVEL_INFO); + return false; +#endif +} + +int Socket::SoRcvbuf() +{ + int value = 0; +#ifdef SO_RCVBUF + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUF, (char *)&value, &len) == -1) + { + Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_RCVBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVBUF", LOG_LEVEL_INFO); +#endif + return value; +} + +#ifdef SO_RCVBUFFORCE +bool Socket::SetSoRcvbufforce(int x) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUFFORCE, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUFFORCE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +bool Socket::SetSoSndbuf(int x) +{ +#ifdef SO_SNDBUF + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUF, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDBUF", LOG_LEVEL_INFO); + return false; +#endif +} + +int Socket::SoSndbuf() +{ + int value = 0; +#ifdef SO_SNDBUF + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUF, (char *)&value, &len) == -1) + { + Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_SNDBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDBUF", LOG_LEVEL_INFO); +#endif + return value; +} + +#ifdef SO_SNDBUFFORCE +bool Socket::SetSoSndbufforce(int x) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUFFORCE, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUFFORCE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +#ifdef SO_TIMESTAMP +bool Socket::SetSoTimestamp(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_TIMESTAMP, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_TIMESTAMP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + +int Socket::SoType() +{ + int value = 0; +#ifdef SO_TYPE + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_TYPE, (char *)&value, &len) == -1) + { + Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_TYPE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_TYPE", LOG_LEVEL_INFO); +#endif + return value; +} + +#ifdef ENABLE_TRIGGERS +void Socket::Subscribe(int id) +{ + Handler().Subscribe(id, this); +} + +void Socket::Unsubscribe(int id) +{ + Handler().Unsubscribe(id, this); +} + +void Socket::OnTrigger(int, const TriggerData&) +{ +} + +void Socket::OnCancelled(int) +{ +} +#endif + +void Socket::SetTimeout(time_t secs) +{ + if (!secs) + { + Handler().AddList(m_socket, LIST_TIMEOUT, false); + return; + } + Handler().AddList(m_socket, LIST_TIMEOUT, true); + m_timeout_start = time(NULL); + m_timeout_limit = secs; +} + +void Socket::OnTimeout() +{ +} + +void Socket::OnConnectTimeout() +{ +} + +bool Socket::Timeout(time_t tnow) +{ + if (tnow - m_timeout_start > m_timeout_limit) + return true; + return false; +} + +/** Returns local port number for bound socket file descriptor. */ +port_t Socket::GetSockPort() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in6); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + return ntohs(sa.sin6_port); + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + return ntohs(sa.sin_port); +} + +/** Returns local ipv4 address for bound socket file descriptor. */ +ipaddr_t Socket::GetSockIP4() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + return 0; + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + ipaddr_t a; + memcpy(&a, &sa.sin_addr, 4); + return a; +} + +/** Returns local ipv4 address as text for bound socket file descriptor. */ +std::string Socket::GetSockAddress() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + return ""; + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + Ipv4Address addr( sa ); + return addr.Convert(); +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +/** Returns local ipv6 address for bound socket file descriptor. */ +struct in6_addr Socket::GetSockIP6() +{ + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in6); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + return sa.sin6_addr; + } + struct in6_addr a; + memset(&a, 0, sizeof(a)); + return a; +} + +/** Returns local ipv6 address as text for bound socket file descriptor. */ +std::string Socket::GetSockAddress6() +{ + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in6); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + Ipv6Address addr( sa ); + return addr.Convert(); + } + return ""; +} +#endif +#endif + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/SocketHandler.cpp b/externals/sockets/SocketHandler.cpp new file mode 100644 index 00000000000..acf71fb2efa --- /dev/null +++ b/externals/sockets/SocketHandler.cpp @@ -0,0 +1,1377 @@ +/** \file SocketHandler.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#endif +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <cstdio> + +#include "SocketHandler.h" +#include "UdpSocket.h" +#include "ResolvSocket.h" +#include "ResolvServer.h" +#include "TcpSocket.h" +#include "Mutex.h" +#include "Utility.h" +#include "SocketAddress.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +//#ifdef _DEBUG +//#define DEB(x) x; fflush(stderr); +//#else +#define DEB(x) +//#endif + +SocketHandler::SocketHandler(StdLog *p) +:m_stdlog(p) +,m_mutex(m_mutex) +,m_b_use_mutex(false) +,m_maxsock(0) +,m_preverror(-1) +,m_errcnt(0) +,m_tlast(0) +#ifdef ENABLE_SOCKS4 +,m_socks4_host(0) +,m_socks4_port(0) +,m_bTryDirect(false) +#endif +#ifdef ENABLE_RESOLVER +,m_resolv_id(0) +,m_resolver(NULL) +#endif +#ifdef ENABLE_POOL +,m_b_enable_pool(false) +#endif +#ifdef ENABLE_TRIGGERS +,m_next_trigger_id(0) +#endif +#ifdef ENABLE_DETACH +,m_slave(false) +#endif +{ + FD_ZERO(&m_rfds); + FD_ZERO(&m_wfds); + FD_ZERO(&m_efds); +} + +SocketHandler::SocketHandler(Mutex& mutex,StdLog *p) +:m_stdlog(p) +,m_mutex(mutex) +,m_b_use_mutex(true) +,m_maxsock(0) +,m_preverror(-1) +,m_errcnt(0) +,m_tlast(0) +#ifdef ENABLE_SOCKS4 +,m_socks4_host(0) +,m_socks4_port(0) +,m_bTryDirect(false) +#endif +#ifdef ENABLE_RESOLVER +,m_resolv_id(0) +,m_resolver(NULL) +#endif +#ifdef ENABLE_POOL +,m_b_enable_pool(false) +#endif +#ifdef ENABLE_TRIGGERS +,m_next_trigger_id(0) +#endif +#ifdef ENABLE_DETACH +,m_slave(false) +#endif +{ + m_mutex.Lock(); + FD_ZERO(&m_rfds); + FD_ZERO(&m_wfds); + FD_ZERO(&m_efds); +} + +SocketHandler::~SocketHandler() +{ +#ifdef ENABLE_RESOLVER + if (m_resolver) + { + m_resolver -> Quit(); + } +#endif + { + while (m_sockets.size()) + { +DEB( fprintf(stderr, "Emptying sockets list in SocketHandler destructor, %d instances\n", (int)m_sockets.size());) + socket_m::iterator it = m_sockets.begin(); + Socket *p = it -> second; + if (p) + { +DEB( fprintf(stderr, " fd %d\n", p -> GetSocket());) + p -> Close(); +DEB( fprintf(stderr, " fd closed %d\n", p -> GetSocket());) +// p -> OnDelete(); // hey, I turn this back on. what's the worst that could happen??!! + // MinionSocket breaks, calling MinderHandler methods in OnDelete - + // MinderHandler is already gone when that happens... + + // only delete socket when controlled + // ie master sockethandler can delete non-detached sockets + // and a slave sockethandler can only delete a detach socket + if (p -> DeleteByHandler() +#ifdef ENABLE_DETACH + && !(m_slave ^ p -> IsDetached()) +#endif + ) + { + p -> SetErasedByHandler(); + delete p; + } + m_sockets.erase(it); + } + else + { + m_sockets.erase(it); + } +DEB( fprintf(stderr, "next\n");) + } +DEB( fprintf(stderr, "/Emptying sockets list in SocketHandler destructor, %d instances\n", (int)m_sockets.size());) + } +#ifdef ENABLE_RESOLVER + if (m_resolver) + { + delete m_resolver; + } +#endif + if (m_b_use_mutex) + { + m_mutex.Unlock(); + } +} + +Mutex& SocketHandler::GetMutex() const +{ + return m_mutex; +} + +#ifdef ENABLE_DETACH +void SocketHandler::SetSlave(bool x) +{ + m_slave = x; +} + +bool SocketHandler::IsSlave() +{ + return m_slave; +} +#endif + +void SocketHandler::RegStdLog(StdLog *log) +{ + m_stdlog = log; +} + +void SocketHandler::LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t) +{ + if (m_stdlog) + { + m_stdlog -> error(this, p, user_text, err, sys_err, t); + } +} + +void SocketHandler::Add(Socket *p) +{ + if (p -> GetSocket() == INVALID_SOCKET) + { + LogError(p, "Add", -1, "Invalid socket", LOG_LEVEL_WARNING); + if (p -> CloseAndDelete()) + { + m_delete.push_back(p); + } + return; + } + if (m_add.find(p -> GetSocket()) != m_add.end()) + { + LogError(p, "Add", (int)p -> GetSocket(), "Attempt to add socket already in add queue", LOG_LEVEL_FATAL); + m_delete.push_back(p); + return; + } + m_add[p -> GetSocket()] = p; +} + +void SocketHandler::Get(SOCKET s,bool& r,bool& w,bool& e) +{ + if (s >= 0) + { + r = FD_ISSET(s, &m_rfds) ? true : false; + w = FD_ISSET(s, &m_wfds) ? true : false; + e = FD_ISSET(s, &m_efds) ? true : false; + } +} + +void SocketHandler::Set(SOCKET s,bool bRead,bool bWrite,bool bException) +{ +DEB( fprintf(stderr, "Set(%d, %s, %s, %s)\n", s, bRead ? "true" : "false", bWrite ? "true" : "false", bException ? "true" : "false");) + if (s >= 0) + { + if (bRead) + { + if (!FD_ISSET(s, &m_rfds)) + { + FD_SET(s, &m_rfds); + } + } + else + { + FD_CLR(s, &m_rfds); + } + if (bWrite) + { + if (!FD_ISSET(s, &m_wfds)) + { + FD_SET(s, &m_wfds); + } + } + else + { + FD_CLR(s, &m_wfds); + } + if (bException) + { + if (!FD_ISSET(s, &m_efds)) + { + FD_SET(s, &m_efds); + } + } + else + { + FD_CLR(s, &m_efds); + } + } +} + +int SocketHandler::Select(long sec,long usec) +{ + struct timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + return Select(&tv); +} + +int SocketHandler::Select() +{ + if (!m_fds_callonconnect.empty() || +#ifdef ENABLE_DETACH + (!m_slave && !m_fds_detach.empty()) || +#endif + !m_fds_timeout.empty() || + !m_fds_retry.empty() || + !m_fds_close.empty() || + !m_fds_erase.empty()) + { + return Select(0, 200000); + } + return Select(NULL); +} + +int SocketHandler::Select(struct timeval *tsel) +{ + size_t ignore = 0; + while (m_add.size() > ignore) + { + if (m_sockets.size() >= FD_SETSIZE) + { + LogError(NULL, "Select", (int)m_sockets.size(), "FD_SETSIZE reached", LOG_LEVEL_WARNING); + break; + } + socket_m::iterator it = m_add.begin(); + SOCKET s = it -> first; + Socket *p = it -> second; +DEB( fprintf(stderr, "Trying to add fd %d, m_add.size() %d, ignore %d\n", (int)s, (int)m_add.size(), (int)ignore);) + // + if (m_sockets.find(p -> GetSocket()) != m_sockets.end()) + { + LogError(p, "Add", (int)p -> GetSocket(), "Attempt to add socket already in controlled queue", LOG_LEVEL_FATAL); + // %! it's a dup, don't add to delete queue, just ignore it + m_delete.push_back(p); + m_add.erase(it); +// ignore++; + continue; + } + if (!p -> CloseAndDelete()) + { + StreamSocket *scp = dynamic_cast<StreamSocket *>(p); + if (scp && scp -> Connecting()) // 'Open' called before adding socket + { + Set(s,false,true); + } + else + { + TcpSocket *tcp = dynamic_cast<TcpSocket *>(p); + bool bWrite = tcp ? tcp -> GetOutputLength() != 0 : false; + if (p -> IsDisableRead()) + { + Set(s, false, bWrite); + } + else + { + Set(s, true, bWrite); + } + } + m_maxsock = (s > m_maxsock) ? s : m_maxsock; + } + else + { + LogError(p, "Add", (int)p -> GetSocket(), "Trying to add socket with SetCloseAndDelete() true", LOG_LEVEL_WARNING); + } + // only add to m_fds (process fd_set events) if + // slave handler and detached/detaching socket + // master handler and non-detached socket +#ifdef ENABLE_DETACH + if (!(m_slave ^ p -> IsDetach())) +#endif + { + m_fds.push_back(s); + } + m_sockets[s] = p; + // + m_add.erase(it); + } +#ifdef MACOSX + fd_set rfds; + fd_set wfds; + fd_set efds; + FD_COPY(&m_rfds, &rfds); + FD_COPY(&m_wfds, &wfds); + FD_COPY(&m_efds, &efds); +#else + fd_set rfds = m_rfds; + fd_set wfds = m_wfds; + fd_set efds = m_efds; +#endif + int n; + if (m_b_use_mutex) + { + m_mutex.Unlock(); + n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel); + m_mutex.Lock(); + } + else + { + n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel); + } + if (n == -1) + { + /* + EBADF An invalid file descriptor was given in one of the sets. + EINTR A non blocked signal was caught. + EINVAL n is negative. Or struct timeval contains bad time values (<0). + ENOMEM select was unable to allocate memory for internal tables. + */ + if (Errno != m_preverror || m_errcnt++ % 10000 == 0) + { + LogError(NULL, "select", Errno, StrError(Errno)); +DEB( fprintf(stderr, "m_maxsock: %d\n", m_maxsock); + fprintf(stderr, "%s\n", Errno == EINVAL ? "EINVAL" : + Errno == EINTR ? "EINTR" : + Errno == EBADF ? "EBADF" : + Errno == ENOMEM ? "ENOMEM" : "<another>"); + // test bad fd + for (SOCKET i = 0; i <= m_maxsock; i++) + { + bool t = false; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + if (FD_ISSET(i, &m_rfds)) + { + FD_SET(i, &rfds); + t = true; + } + if (FD_ISSET(i, &m_wfds)) + { + FD_SET(i, &wfds); + t = true; + } + if (FD_ISSET(i, &m_efds)) + { + FD_SET(i, &efds); + t = true; + } + if (t && m_sockets.find(i) == m_sockets.end()) + { + fprintf(stderr, "Bad fd in fd_set: %d\n", i); + } + } +) // DEB + m_preverror = Errno; + } + /// \todo rebuild fd_set's from active sockets list (m_sockets) here + } + else + if (!n) + { + m_preverror = -1; + } + else + if (n > 0) + { + for (socket_v::iterator it2 = m_fds.begin(); it2 != m_fds.end() && n; it2++) + { + SOCKET i = *it2; + if (FD_ISSET(i, &rfds)) + { + socket_m::iterator itmp = m_sockets.find(i); + if (itmp != m_sockets.end()) // found + { + Socket *p = itmp -> second; + // new SSL negotiate method +#ifdef HAVE_OPENSSL + if (p -> IsSSLNegotiate()) + { + p -> SSLNegotiate(); + } + else +#endif + { + p -> OnRead(); + } + } + else + { + LogError(NULL, "GetSocket/handler/1", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + n--; + } + if (FD_ISSET(i, &wfds)) + { + socket_m::iterator itmp = m_sockets.find(i); + if (itmp != m_sockets.end()) // found + { + Socket *p = itmp -> second; + // new SSL negotiate method +#ifdef HAVE_OPENSSL + if (p -> IsSSLNegotiate()) + { + p -> SSLNegotiate(); + } + else +#endif + { + p -> OnWrite(); + } + } + else + { + LogError(NULL, "GetSocket/handler/2", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + n--; + } + if (FD_ISSET(i, &efds)) + { + socket_m::iterator itmp = m_sockets.find(i); + if (itmp != m_sockets.end()) // found + { + Socket *p = itmp -> second; + p -> OnException(); + } + else + { + LogError(NULL, "GetSocket/handler/3", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + n--; + } + } // m_fds loop + m_preverror = -1; + } // if (n > 0) + + // check CallOnConnect - EVENT + if (!m_fds_callonconnect.empty()) + { + socket_v tmp = m_fds_callonconnect; + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/4", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + if (p) + { +// if (p -> CallOnConnect() && p -> Ready() ) + { + p -> SetConnected(); // moved here from inside if (tcp) check below +#ifdef HAVE_OPENSSL + if (p -> IsSSL()) // SSL Enabled socket + p -> OnSSLConnect(); + else +#endif +#ifdef ENABLE_SOCKS4 + if (p -> Socks4()) + p -> OnSocks4Connect(); + else +#endif + { + TcpSocket *tcp = dynamic_cast<TcpSocket *>(p); + if (tcp) + { + if (tcp -> GetOutputLength()) + { + p -> OnWrite(); + } + } +#ifdef ENABLE_RECONNECT + if (tcp && tcp -> IsReconnect()) + p -> OnReconnect(); + else +#endif + { +// LogError(p, "Calling OnConnect", 0, "Because CallOnConnect", LOG_LEVEL_INFO); + p -> OnConnect(); + } + } +// p -> SetCallOnConnect( false ); + AddList(p -> GetSocket(), LIST_CALLONCONNECT, false); + } + } + } + } +#ifdef ENABLE_DETACH + // check detach of socket if master handler - EVENT + if (!m_slave && !m_fds_detach.empty()) + { + // %! why not using tmp list here??!? + for (socket_v::iterator it = m_fds_detach.begin(); it != m_fds_detach.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/5", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + if (p) + { +// if (p -> IsDetach()) + { + Set(p -> GetSocket(), false, false, false); + // After DetachSocket(), all calls to Handler() will return a reference + // to the new slave SocketHandler running in the new thread. + p -> DetachSocket(); + // Adding the file descriptor to m_fds_erase will now also remove the + // socket from the detach queue - tnx knightmad + m_fds_erase.push_back(p -> GetSocket()); + } + } + } + } +#endif + // check Connecting - connection timeout - conditional event + if (m_fds_timeout.size()) + { + time_t tnow = time(NULL); + if (tnow != m_tlast) + { + socket_v tmp = m_fds_timeout; +DEB( fprintf(stderr, "Checking %d socket(s) for timeout\n", tmp.size());) + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + itmp = m_add.find(*it); + if (itmp != m_add.end()) + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/6", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + } + if (p) + { + if (p -> Timeout(tnow)) + { + StreamSocket *scp = dynamic_cast<StreamSocket *>(p); + if (scp && scp -> Connecting()) + p -> OnConnectTimeout(); + else + p -> OnTimeout(); + p -> SetTimeout(0); + } + } + } + m_tlast = tnow; + } // tnow != tlast + } + // check retry client connect - EVENT + if (!m_fds_retry.empty()) + { + socket_v tmp = m_fds_retry; + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/7", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + if (p) + { +// if (p -> RetryClientConnect()) + { + TcpSocket *tcp = dynamic_cast<TcpSocket *>(p); + SOCKET nn = *it; //(*it3).first; + tcp -> SetRetryClientConnect(false); +DEB( fprintf(stderr, "Close() before retry client connect\n");) + p -> Close(); // removes from m_fds_retry + std::auto_ptr<SocketAddress> ad = p -> GetClientRemoteAddress(); + if (ad.get()) + { + tcp -> Open(*ad); + } + else + { + LogError(p, "RetryClientConnect", 0, "no address", LOG_LEVEL_ERROR); + } + Add(p); + m_fds_erase.push_back(nn); + } + } + } + } + // check close and delete - conditional event + if (!m_fds_close.empty()) + { + socket_v tmp = m_fds_close; +DEB( fprintf(stderr, "m_fds_close.size() == %d\n", (int)m_fds_close.size());) + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + itmp = m_add.find(*it); + if (itmp != m_add.end()) + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/8", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + } + if (p) + { +// if (p -> CloseAndDelete() ) + { + TcpSocket *tcp = dynamic_cast<TcpSocket *>(p); + // new graceful tcp - flush and close timeout 5s + if (tcp && p -> IsConnected() && tcp -> GetFlushBeforeClose() && +#ifdef HAVE_OPENSSL + !tcp -> IsSSL() && +#endif + p -> TimeSinceClose() < 5) + { +DEB( fprintf(stderr, " close(1)\n");) + if (tcp -> GetOutputLength()) + { + LogError(p, "Closing", (int)tcp -> GetOutputLength(), "Sending all data before closing", LOG_LEVEL_INFO); + } + else // shutdown write when output buffer is empty + if (!(tcp -> GetShutdown() & SHUT_WR)) + { + SOCKET nn = *it; + if (nn != INVALID_SOCKET && shutdown(nn, SHUT_WR) == -1) + { + LogError(p, "graceful shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + tcp -> SetShutdown(SHUT_WR); + } + } + else +#ifdef ENABLE_RECONNECT + if (tcp && p -> IsConnected() && tcp -> Reconnect()) + { + SOCKET nn = *it; //(*it3).first; +DEB( fprintf(stderr, " close(2) fd %d\n", nn);) + p -> SetCloseAndDelete(false); + tcp -> SetIsReconnect(); + p -> SetConnected(false); +DEB( fprintf(stderr, "Close() before reconnect\n");) + p -> Close(); // dispose of old file descriptor (Open creates a new) + p -> OnDisconnect(); + std::auto_ptr<SocketAddress> ad = p -> GetClientRemoteAddress(); + if (ad.get()) + { + tcp -> Open(*ad); + } + else + { + LogError(p, "Reconnect", 0, "no address", LOG_LEVEL_ERROR); + } + tcp -> ResetConnectionRetries(); + Add(p); + m_fds_erase.push_back(nn); + } + else +#endif + { + SOCKET nn = *it; //(*it3).first; +DEB( fprintf(stderr, " close(3) fd %d GetSocket() %d\n", nn, p -> GetSocket());) + if (tcp && p -> IsConnected() && tcp -> GetOutputLength()) + { + LogError(p, "Closing", (int)tcp -> GetOutputLength(), "Closing socket while data still left to send", LOG_LEVEL_WARNING); + } +#ifdef ENABLE_POOL + if (p -> Retain() && !p -> Lost()) + { + PoolSocket *p2 = new PoolSocket(*this, p); + p2 -> SetDeleteByHandler(); + Add(p2); + // + p -> SetCloseAndDelete(false); // added - remove from m_fds_close + } + else +#endif // ENABLE_POOL + { + Set(p -> GetSocket(),false,false,false); +DEB( fprintf(stderr, "Close() before OnDelete\n");) + p -> Close(); + } + p -> OnDelete(); + if (p -> DeleteByHandler()) + { + p -> SetErasedByHandler(); + } + m_fds_erase.push_back(nn); + } + } + } + } + } + + // check erased sockets + bool check_max_fd = false; + while (!m_fds_erase.empty()) + { + socket_v::iterator it = m_fds_erase.begin(); + SOCKET nn = *it; +#ifdef ENABLE_DETACH + { + for (socket_v::iterator it = m_fds_detach.begin(); it != m_fds_detach.end(); it++) + { + if (*it == nn) + { + m_fds_detach.erase(it); + break; + } + } + } +#endif + { + for (socket_v::iterator it = m_fds.begin(); it != m_fds.end(); it++) + { + if (*it == nn) + { + m_fds.erase(it); + break; + } + } + } + { + socket_m::iterator it = m_sockets.find(nn); + if (it != m_sockets.end()) + { + Socket *p = it -> second; + /* Sometimes a SocketThread class can finish its run before the master + sockethandler gets here. In that case, the SocketThread has set the + 'ErasedByHandler' flag on the socket which will make us end up with a + double delete on the socket instance. + The fix is to make sure that the master sockethandler only can delete + non-detached sockets, and a slave sockethandler only can delete + detach sockets. */ + if (p -> ErasedByHandler() +#ifdef ENABLE_DETACH + && !(m_slave ^ p -> IsDetached()) +#endif + ) + { +#ifdef ENABLE_TRIGGERS + bool again = false; + do + { + again = false; + for (std::map<int, Socket *>::iterator it = m_trigger_src.begin(); it != m_trigger_src.end(); it++) + { + int id = it -> first; + Socket *src = it -> second; + if (src == p) + { + for (std::map<Socket *, bool>::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++) + { + Socket *dst = it -> first; + if (Valid(dst)) + { + dst -> OnCancelled(id); + } + } + m_trigger_src.erase(m_trigger_src.find(id)); + m_trigger_dst.erase(m_trigger_dst.find(id)); + again = true; + break; + } + } + } while (again); +#endif + delete p; + } + m_sockets.erase(it); + } + } + m_fds_erase.erase(it); + check_max_fd = true; + } + // calculate max file descriptor for select() call + if (check_max_fd) + { + m_maxsock = 0; + for (socket_v::iterator it = m_fds.begin(); it != m_fds.end(); it++) + { + SOCKET s = *it; + m_maxsock = s > m_maxsock ? s : m_maxsock; + } + } + // remove Add's that fizzed + while (!m_delete.empty()) + { + std::list<Socket *>::iterator it = m_delete.begin(); + Socket *p = *it; + p -> OnDelete(); + m_delete.erase(it); + if (p -> DeleteByHandler() +#ifdef ENABLE_DETACH + && !(m_slave ^ p -> IsDetached()) +#endif + ) + { + p -> SetErasedByHandler(); +#ifdef ENABLE_TRIGGERS + bool again = false; + do + { + again = false; + for (std::map<int, Socket *>::iterator it = m_trigger_src.begin(); it != m_trigger_src.end(); it++) + { + int id = it -> first; + Socket *src = it -> second; + if (src == p) + { + for (std::map<Socket *, bool>::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++) + { + Socket *dst = it -> first; + if (Valid(dst)) + { + dst -> OnCancelled(id); + } + } + m_trigger_src.erase(m_trigger_src.find(id)); + m_trigger_dst.erase(m_trigger_dst.find(id)); + again = true; + break; + } + } + } while (again); +#endif + delete p; + } + } + return n; +} + +#ifdef ENABLE_RESOLVER +bool SocketHandler::Resolving(Socket *p0) +{ + std::map<Socket *, bool>::iterator it = m_resolve_q.find(p0); + return it != m_resolve_q.end(); +} +#endif + +bool SocketHandler::Valid(Socket *p0) +{ + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + Socket *p = it -> second; + if (p0 == p) + return true; + } + return false; +} + +bool SocketHandler::OkToAccept(Socket *) +{ + return true; +} + +size_t SocketHandler::GetCount() +{ +/* +printf(" m_sockets : %d\n", m_sockets.size()); +printf(" m_add : %d\n", m_add.size()); +printf(" m_delete : %d\n", m_delete.size()); +*/ + return m_sockets.size() + m_add.size() + m_delete.size(); +} + +#ifdef ENABLE_SOCKS4 +void SocketHandler::SetSocks4Host(ipaddr_t a) +{ + m_socks4_host = a; +} + +void SocketHandler::SetSocks4Host(const std::string& host) +{ + Utility::u2ip(host, m_socks4_host); +} + +void SocketHandler::SetSocks4Port(port_t port) +{ + m_socks4_port = port; +} + +void SocketHandler::SetSocks4Userid(const std::string& id) +{ + m_socks4_userid = id; +} +#endif + +#ifdef ENABLE_RESOLVER +int SocketHandler::Resolve(Socket *p,const std::string& host,port_t port) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, host, port); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; +DEB( fprintf(stderr, " *** Resolve '%s:%d' id#%d m_resolve_q size: %d p: %p\n", host.c_str(), port, resolv -> GetId(), m_resolve_q.size(), p);) + return resolv -> GetId(); +} + +#ifdef ENABLE_IPV6 +int SocketHandler::Resolve6(Socket *p,const std::string& host,port_t port) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, host, port, true); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; + return resolv -> GetId(); +} +#endif + +int SocketHandler::Resolve(Socket *p,ipaddr_t a) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, a); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; + return resolv -> GetId(); +} + +#ifdef ENABLE_IPV6 +int SocketHandler::Resolve(Socket *p,in6_addr& a) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, a); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; + return resolv -> GetId(); +} +#endif + +void SocketHandler::EnableResolver(port_t port) +{ + if (!m_resolver) + { + m_resolver_port = port; + m_resolver = new ResolvServer(port); + } +} + +bool SocketHandler::ResolverReady() +{ + return m_resolver ? m_resolver -> Ready() : false; +} +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_SOCKS4 +void SocketHandler::SetSocks4TryDirect(bool x) +{ + m_bTryDirect = x; +} + +ipaddr_t SocketHandler::GetSocks4Host() +{ + return m_socks4_host; +} + +port_t SocketHandler::GetSocks4Port() +{ + return m_socks4_port; +} + +const std::string& SocketHandler::GetSocks4Userid() +{ + return m_socks4_userid; +} + +bool SocketHandler::Socks4TryDirect() +{ + return m_bTryDirect; +} +#endif + +#ifdef ENABLE_RESOLVER +bool SocketHandler::ResolverEnabled() +{ + return m_resolver ? true : false; +} + +port_t SocketHandler::GetResolverPort() +{ + return m_resolver_port; +} +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_POOL +ISocketHandler::PoolSocket *SocketHandler::FindConnection(int type,const std::string& protocol,SocketAddress& ad) +{ + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end() && !m_sockets.empty(); it++) + { + PoolSocket *pools = dynamic_cast<PoolSocket *>(it -> second); + if (pools) + { + if (pools -> GetSocketType() == type && + pools -> GetSocketProtocol() == protocol && +// %! pools -> GetClientRemoteAddress() && + *pools -> GetClientRemoteAddress() == ad) + { + m_sockets.erase(it); + pools -> SetRetain(); // avoid Close in Socket destructor + return pools; // Caller is responsible that this socket is deleted + } + } + } + return NULL; +} + +void SocketHandler::EnablePool(bool x) +{ + m_b_enable_pool = x; +} + +bool SocketHandler::PoolEnabled() +{ + return m_b_enable_pool; +} +#endif + +void SocketHandler::Remove(Socket *p) +{ +#ifdef ENABLE_RESOLVER + std::map<Socket *, bool>::iterator it4 = m_resolve_q.find(p); + if (it4 != m_resolve_q.end()) + m_resolve_q.erase(it4); +#endif + if (p -> ErasedByHandler()) + { + return; + } + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + if (it -> second == p) + { + LogError(p, "Remove", -1, "Socket destructor called while still in use", LOG_LEVEL_WARNING); + m_sockets.erase(it); + return; + } + } + for (socket_m::iterator it2 = m_add.begin(); it2 != m_add.end(); it2++) + { + if ((*it2).second == p) + { + LogError(p, "Remove", -2, "Socket destructor called while still in use", LOG_LEVEL_WARNING); + m_add.erase(it2); + return; + } + } + for (std::list<Socket *>::iterator it3 = m_delete.begin(); it3 != m_delete.end(); it3++) + { + if (*it3 == p) + { + LogError(p, "Remove", -3, "Socket destructor called while still in use", LOG_LEVEL_WARNING); + m_delete.erase(it3); + return; + } + } +} + +void SocketHandler::CheckSanity() +{ + CheckList(m_fds, "active sockets"); // active sockets + CheckList(m_fds_erase, "sockets to be erased"); // should always be empty anyway + CheckList(m_fds_callonconnect, "checklist CallOnConnect"); +#ifdef ENABLE_DETACH + CheckList(m_fds_detach, "checklist Detach"); +#endif + CheckList(m_fds_timeout, "checklist Timeout"); + CheckList(m_fds_retry, "checklist retry client connect"); + CheckList(m_fds_close, "checklist close and delete"); +} + +void SocketHandler::CheckList(socket_v& ref,const std::string& listname) +{ + for (socket_v::iterator it = ref.begin(); it != ref.end(); it++) + { + SOCKET s = *it; + if (m_sockets.find(s) != m_sockets.end()) + continue; + if (m_add.find(s) != m_add.end()) + continue; + bool found = false; + for (std::list<Socket *>::iterator it = m_delete.begin(); it != m_delete.end(); it++) + { + Socket *p = *it; + if (p -> GetSocket() == s) + { + found = true; + break; + } + } + if (!found) + { + fprintf(stderr, "CheckList failed for \"%s\": fd %d\n", listname.c_str(), s); + } + } +} + +void SocketHandler::AddList(SOCKET s,list_t which_one,bool add) +{ + if (s == INVALID_SOCKET) + { +DEB( fprintf(stderr, "AddList: invalid_socket\n");) + return; + } + socket_v& ref = + (which_one == LIST_CALLONCONNECT) ? m_fds_callonconnect : +#ifdef ENABLE_DETACH + (which_one == LIST_DETACH) ? m_fds_detach : +#endif + (which_one == LIST_TIMEOUT) ? m_fds_timeout : + (which_one == LIST_RETRY) ? m_fds_retry : + (which_one == LIST_CLOSE) ? m_fds_close : m_fds_close; + if (add) + { +#ifdef ENABLE_DETACH +DEB( fprintf(stderr, "AddList; %5d: %s: %s\n", s, (which_one == LIST_CALLONCONNECT) ? "CallOnConnect" : + (which_one == LIST_DETACH) ? "Detach" : + (which_one == LIST_TIMEOUT) ? "Timeout" : + (which_one == LIST_RETRY) ? "Retry" : + (which_one == LIST_CLOSE) ? "Close" : "<undef>", + add ? "Add" : "Remove");) +#else +DEB( fprintf(stderr, "AddList; %5d: %s: %s\n", s, (which_one == LIST_CALLONCONNECT) ? "CallOnConnect" : + (which_one == LIST_TIMEOUT) ? "Timeout" : + (which_one == LIST_RETRY) ? "Retry" : + (which_one == LIST_CLOSE) ? "Close" : "<undef>", + add ? "Add" : "Remove");) +#endif + } + if (add) + { + for (socket_v::iterator it = ref.begin(); it != ref.end(); it++) + { + if (*it == s) // already there + { + return; + } + } + ref.push_back(s); + return; + } + // remove + for (socket_v::iterator it = ref.begin(); it != ref.end(); it++) + { + if (*it == s) + { + ref.erase(it); + break; + } + } +//DEB( fprintf(stderr, "/AddList\n");) +} + +#ifdef ENABLE_TRIGGERS +int SocketHandler::TriggerID(Socket *src) +{ + int id = m_next_trigger_id++; + m_trigger_src[id] = src; + return id; +} + +bool SocketHandler::Subscribe(int id, Socket *dst) +{ + if (m_trigger_src.find(id) != m_trigger_src.end()) + { + std::map<Socket *, bool>::iterator it = m_trigger_dst[id].find(dst); + if (it != m_trigger_dst[id].end()) + { + m_trigger_dst[id][dst] = true; + return true; + } + LogError(dst, "Subscribe", id, "Already subscribed", LOG_LEVEL_INFO); + return false; + } + LogError(dst, "Subscribe", id, "Trigger id not found", LOG_LEVEL_INFO); + return false; +} + +bool SocketHandler::Unsubscribe(int id, Socket *dst) +{ + if (m_trigger_src.find(id) != m_trigger_src.end()) + { + std::map<Socket *, bool>::iterator it = m_trigger_dst[id].find(dst); + if (it != m_trigger_dst[id].end()) + { + m_trigger_dst[id].erase(it); + return true; + } + LogError(dst, "Unsubscribe", id, "Not subscribed", LOG_LEVEL_INFO); + return false; + } + LogError(dst, "Unsubscribe", id, "Trigger id not found", LOG_LEVEL_INFO); + return false; +} + +void SocketHandler::Trigger(int id, Socket::TriggerData& data, bool erase) +{ + if (m_trigger_src.find(id) != m_trigger_src.end()) + { + data.SetSource( m_trigger_src[id] ); + for (std::map<Socket *, bool>::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++) + { + Socket *dst = it -> first; + if (Valid(dst)) + { + dst -> OnTrigger(id, data); + } + } + if (erase) + { + m_trigger_src.erase(m_trigger_src.find(id)); + m_trigger_dst.erase(m_trigger_dst.find(id)); + } + } + else + { + LogError(NULL, "Trigger", id, "Trigger id not found", LOG_LEVEL_INFO); + } +} +#endif // ENABLE_TRIGGERS + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/StdoutLog.cpp b/externals/sockets/StdoutLog.cpp new file mode 100644 index 00000000000..e745a6d3358 --- /dev/null +++ b/externals/sockets/StdoutLog.cpp @@ -0,0 +1,98 @@ +/** \file StdoutLog.cpp + ** \date 2004-06-01 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include <stdio.h> + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif + +#include <cstdio> + +#include "ISocketHandler.h" +#include "Socket.h" +#include "StdoutLog.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +void StdoutLog::error(ISocketHandler *,Socket *sock,const std::string& call,int err,const std::string& sys_err,loglevel_t lvl) +{ + time_t t = time(NULL); + struct tm tp; +#ifdef _WIN32 + memcpy(&tp, localtime(&t), sizeof(tp)); +#else + localtime_r(&t, &tp); +#endif + std::string level; + + switch (lvl) + { + case LOG_LEVEL_WARNING: + level = "Warning"; + break; + case LOG_LEVEL_ERROR: + level = "Error"; + break; + case LOG_LEVEL_FATAL: + level = "Fatal"; + break; + case LOG_LEVEL_INFO: + level = "Info"; + break; + } + if (sock) + { + printf("%d-%02d-%02d %02d:%02d:%02d :: fd %d :: %s: %d %s (%s)\n", + tp.tm_year + 1900, + tp.tm_mon + 1, + tp.tm_mday, + tp.tm_hour,tp.tm_min,tp.tm_sec, + sock -> GetSocket(), + call.c_str(),err,sys_err.c_str(),level.c_str()); + } + else + { + printf("%d-%02d-%02d %02d:%02d:%02d :: %s: %d %s (%s)\n", + tp.tm_year + 1900, + tp.tm_mon + 1, + tp.tm_mday, + tp.tm_hour,tp.tm_min,tp.tm_sec, + call.c_str(),err,sys_err.c_str(),level.c_str()); + } +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/StreamSocket.cpp b/externals/sockets/StreamSocket.cpp new file mode 100644 index 00000000000..009abadad8f --- /dev/null +++ b/externals/sockets/StreamSocket.cpp @@ -0,0 +1,145 @@ +#include "StreamSocket.h" +#include "ISocketHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +StreamSocket::StreamSocket(ISocketHandler& h) : Socket(h) +,m_bConnecting(false) +,m_connect_timeout(5) +,m_flush_before_close(true) +,m_connection_retry(0) +,m_retries(0) +,m_call_on_connect(false) +,m_b_retry_connect(false) +,m_line_protocol(false) +,m_shutdown(0) +{ +} + +StreamSocket::~StreamSocket() +{ +} + +void StreamSocket::SetConnecting(bool x) +{ + if (x != m_bConnecting) + { + m_bConnecting = x; + if (x) + { + SetTimeout( GetConnectTimeout() ); + } + else + { + SetTimeout( 0 ); + } + } +} + +bool StreamSocket::Connecting() +{ + return m_bConnecting; +} + +bool StreamSocket::Ready() +{ + if (GetSocket() != INVALID_SOCKET && !Connecting() && !CloseAndDelete()) + return true; + return false; +} + +void StreamSocket::SetConnectTimeout(int x) +{ + m_connect_timeout = x; +} + +int StreamSocket::GetConnectTimeout() +{ + return m_connect_timeout; +} + +void StreamSocket::SetFlushBeforeClose(bool x) +{ + m_flush_before_close = x; +} + +bool StreamSocket::GetFlushBeforeClose() +{ + return m_flush_before_close; +} + +int StreamSocket::GetConnectionRetry() +{ + return m_connection_retry; +} + +void StreamSocket::SetConnectionRetry(int x) +{ + m_connection_retry = x; +} + +int StreamSocket::GetConnectionRetries() +{ + return m_retries; +} + +void StreamSocket::IncreaseConnectionRetries() +{ + m_retries++; +} + +void StreamSocket::ResetConnectionRetries() +{ + m_retries = 0; +} + +void StreamSocket::SetCallOnConnect(bool x) +{ + Handler().AddList(GetSocket(), LIST_CALLONCONNECT, x); + m_call_on_connect = x; +} + +bool StreamSocket::CallOnConnect() +{ + return m_call_on_connect; +} + +void StreamSocket::SetRetryClientConnect(bool x) +{ + Handler().AddList(GetSocket(), LIST_RETRY, x); + m_b_retry_connect = x; +} + +bool StreamSocket::RetryClientConnect() +{ + return m_b_retry_connect; +} + +void StreamSocket::SetLineProtocol(bool x) +{ + m_line_protocol = x; +} + +bool StreamSocket::LineProtocol() +{ + return m_line_protocol; +} + +void StreamSocket::SetShutdown(int x) +{ + m_shutdown = x; +} + +int StreamSocket::GetShutdown() +{ + return m_shutdown; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/externals/sockets/TcpSocket.cpp b/externals/sockets/TcpSocket.cpp new file mode 100644 index 00000000000..5f067b53124 --- /dev/null +++ b/externals/sockets/TcpSocket.cpp @@ -0,0 +1,1681 @@ +/** \file TcpSocket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include <stdlib.h> +#else +#include <errno.h> +#endif +#include "ISocketHandler.h" +#include <fcntl.h> +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#ifdef HAVE_OPENSSL +#include <openssl/rand.h> +#include <openssl/err.h> +#endif +#include <map> +#include <cstdio> + +#include "TcpSocket.h" +#include "Utility.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#include "Mutex.h" +#include "IFile.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +//#ifdef _DEBUG +//#define DEB(x) x +//#else +#define DEB(x) +//#endif + +// statics +#ifdef HAVE_OPENSSL +SSLInitializer TcpSocket::m_ssl_init; +#endif + +// thanks, q +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif +TcpSocket::TcpSocket(ISocketHandler& h) : StreamSocket(h) +,ibuf(TCP_BUFSIZE_READ) +,m_b_input_buffer_disabled(false) +,m_bytes_sent(0) +,m_bytes_received(0) +,m_skip_c(false) +#ifdef SOCKETS_DYNAMIC_TEMP +,m_buf(new char[TCP_BUFSIZE_READ + 1]) +#endif +,m_obuf_top(NULL) +,m_transfer_limit(0) +,m_output_length(0) +#ifdef HAVE_OPENSSL +,m_ssl_ctx(NULL) +,m_ssl(NULL) +,m_sbio(NULL) +#endif +#ifdef ENABLE_SOCKS4 +,m_socks4_state(0) +#endif +#ifdef ENABLE_RESOLVER +,m_resolver_id(0) +#endif +#ifdef ENABLE_RECONNECT +,m_b_reconnect(false) +,m_b_is_reconnect(false) +#endif +{ +} +#ifdef _MSC_VER +#pragma warning(default:4355) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif +TcpSocket::TcpSocket(ISocketHandler& h,size_t isize,size_t osize) : StreamSocket(h) +,ibuf(isize) +,m_b_input_buffer_disabled(false) +,m_bytes_sent(0) +,m_bytes_received(0) +,m_skip_c(false) +#ifdef SOCKETS_DYNAMIC_TEMP +,m_buf(new char[TCP_BUFSIZE_READ + 1]) +#endif +,m_obuf_top(NULL) +,m_transfer_limit(0) +,m_output_length(0) +#ifdef HAVE_OPENSSL +,m_ssl_ctx(NULL) +,m_ssl(NULL) +,m_sbio(NULL) +#endif +#ifdef ENABLE_SOCKS4 +,m_socks4_state(0) +#endif +#ifdef ENABLE_RESOLVER +,m_resolver_id(0) +#endif +#ifdef ENABLE_RECONNECT +,m_b_reconnect(false) +,m_b_is_reconnect(false) +#endif +{ +} +#ifdef _MSC_VER +#pragma warning(default:4355) +#endif + +TcpSocket::~TcpSocket() +{ +#ifdef SOCKETS_DYNAMIC_TEMP + delete[] m_buf; +#endif + // %! empty m_obuf + while (m_obuf.size()) + { + output_l::iterator it = m_obuf.begin(); + OUTPUT *p = *it; + delete p; + m_obuf.erase(it); + } +#ifdef HAVE_OPENSSL + if (m_ssl) + { + SSL_free(m_ssl); + } +#endif +} + +bool TcpSocket::Open(ipaddr_t ip,port_t port,bool skip_socks) +{ + Ipv4Address ad(ip, port); + Ipv4Address local; + return Open(ad, local, skip_socks); +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool TcpSocket::Open(in6_addr ip,port_t port,bool skip_socks) +{ + Ipv6Address ad(ip, port); + return Open(ad, skip_socks); +} +#endif +#endif + +bool TcpSocket::Open(SocketAddress& ad,bool skip_socks) +{ + Ipv4Address bind_ad("0.0.0.0", 0); + return Open(ad, bind_ad, skip_socks); +} + +bool TcpSocket::Open(SocketAddress& ad,SocketAddress& bind_ad,bool skip_socks) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "Open", 0, "Invalid SocketAddress", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + return false; + } + if (Handler().GetCount() >= FD_SETSIZE) + { + Handler().LogError(this, "Open", 0, "no space left in fd_set", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + return false; + } + SetConnecting(false); +#ifdef ENABLE_SOCKS4 + SetSocks4(false); +#endif + // check for pooling +#ifdef ENABLE_POOL + if (Handler().PoolEnabled()) + { + ISocketHandler::PoolSocket *pools = Handler().FindConnection(SOCK_STREAM, "tcp", ad); + if (pools) + { + CopyConnection( pools ); + delete pools; + + SetIsClient(); + SetCallOnConnect(); // ISocketHandler must call OnConnect + Handler().LogError(this, "SetCallOnConnect", 0, "Found pooled connection", LOG_LEVEL_INFO); + return true; + } + } +#endif + // if not, create new connection + SOCKET s = CreateSocket(ad.GetFamily(), SOCK_STREAM, "tcp"); + if (s == INVALID_SOCKET) + { + return false; + } + // socket must be nonblocking for async connect + if (!SetNonblocking(true, s)) + { + SetCloseAndDelete(); + closesocket(s); + return false; + } +#ifdef ENABLE_POOL + SetIsClient(); // client because we connect +#endif + SetClientRemoteAddress(ad); + int n = 0; + if (bind_ad.GetPort() != 0) + { + bind(s, bind_ad, bind_ad); + } +#ifdef ENABLE_SOCKS4 + if (!skip_socks && GetSocks4Host() && GetSocks4Port()) + { + Ipv4Address sa(GetSocks4Host(), GetSocks4Port()); + { + std::string sockshost; + Utility::l2ip(GetSocks4Host(), sockshost); + Handler().LogError(this, "Open", 0, "Connecting to socks4 server @ " + sockshost + ":" + + Utility::l2string(GetSocks4Port()), LOG_LEVEL_INFO); + } + SetSocks4(); + n = connect(s, sa, sa); + SetRemoteAddress(sa); + } + else +#endif + { + n = connect(s, ad, ad); + SetRemoteAddress(ad); + } + if (n == -1) + { + // check error code that means a connect is in progress +#ifdef _WIN32 + if (Errno == WSAEWOULDBLOCK) +#else + if (Errno == EINPROGRESS) +#endif + { + Attach(s); + SetConnecting( true ); // this flag will control fd_set's + } + else +#ifdef ENABLE_SOCKS4 + if (Socks4() && Handler().Socks4TryDirect() ) // retry + { + closesocket(s); + return Open(ad, true); + } + else +#endif +#ifdef ENABLE_RECONNECT + if (Reconnect()) + { + Handler().LogError(this, "connect: failed, reconnect pending", Errno, StrError(Errno), LOG_LEVEL_INFO); + Attach(s); + SetConnecting( true ); // this flag will control fd_set's + } + else +#endif + { + Handler().LogError(this, "connect: failed", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + closesocket(s); + return false; + } + } + else + { + Attach(s); + SetCallOnConnect(); // ISocketHandler must call OnConnect + } + + // 'true' means connected or connecting(not yet connected) + // 'false' means something failed + return true; //!Connecting(); +} + +bool TcpSocket::Open(const std::string &host,port_t port) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { +#ifdef ENABLE_RESOLVER + if (!Handler().ResolverEnabled() || Utility::isipv6(host) ) + { +#endif + in6_addr a; + if (!Utility::u2ip(host, a)) + { + SetCloseAndDelete(); + return false; + } + Ipv6Address ad(a, port); + Ipv6Address local; + return Open(ad, local); +#ifdef ENABLE_RESOLVER + } + m_resolver_id = Resolve6(host, port); + return true; +#endif + } +#endif +#endif +#ifdef ENABLE_RESOLVER + if (!Handler().ResolverEnabled() || Utility::isipv4(host) ) + { +#endif + ipaddr_t l; + if (!Utility::u2ip(host,l)) + { + SetCloseAndDelete(); + return false; + } + Ipv4Address ad(l, port); + Ipv4Address local; + return Open(ad, local); +#ifdef ENABLE_RESOLVER + } + // resolve using async resolver thread + m_resolver_id = Resolve(host, port); + return true; +#endif +} + +#ifdef ENABLE_RESOLVER +void TcpSocket::OnResolved(int id,ipaddr_t a,port_t port) +{ +DEB( fprintf(stderr, "TcpSocket::OnResolved id %d addr %x port %d\n", id, a, port);) + if (id == m_resolver_id) + { + if (a && port) + { + Ipv4Address ad(a, port); + Ipv4Address local; + if (Open(ad, local)) + { + if (!Handler().Valid(this)) + { + Handler().Add(this); + } + } + } + else + { + Handler().LogError(this, "OnResolved", 0, "Resolver failed", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + } + else + { + Handler().LogError(this, "OnResolved", id, "Resolver returned wrong job id", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} + +#ifdef ENABLE_IPV6 +void TcpSocket::OnResolved(int id,in6_addr& a,port_t port) +{ + if (id == m_resolver_id) + { + Ipv6Address ad(a, port); + if (ad.IsValid()) + { + Ipv6Address local; + if (Open(ad, local)) + { + if (!Handler().Valid(this)) + { + Handler().Add(this); + } + } + } + } + else + { + Handler().LogError(this, "OnResolved", id, "Resolver returned wrong job id", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} +#endif +#endif + +void TcpSocket::OnRead() +{ + int n = 0; +#ifdef SOCKETS_DYNAMIC_TEMP + char *buf = m_buf; +#else + char buf[TCP_BUFSIZE_READ]; +#endif +#ifdef HAVE_OPENSSL + if (IsSSL()) + { + if (!Ready()) + return; + n = SSL_read(m_ssl, buf, TCP_BUFSIZE_READ); + if (n == -1) + { + n = SSL_get_error(m_ssl, n); + switch (n) + { + case SSL_ERROR_NONE: + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + case SSL_ERROR_ZERO_RETURN: +DEB( fprintf(stderr, "SSL_read() returns zero - closing socket\n");) + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + break; + default: +DEB( fprintf(stderr, "SSL read problem, errcode = %d\n",n);) + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + } + return; + } + else + if (!n) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + SetShutdown(SHUT_WR); + return; + } + else + if (n > 0 && n <= TCP_BUFSIZE_READ) + { + m_bytes_received += n; + if (GetTrafficMonitor()) + { + GetTrafficMonitor() -> fwrite(buf, 1, n); + } + if (!m_b_input_buffer_disabled && !ibuf.Write(buf,n)) + { + Handler().LogError(this, "OnRead(ssl)", 0, "ibuf overflow", LOG_LEVEL_WARNING); + } + } + else + { + Handler().LogError(this, "OnRead(ssl)", n, "abnormal value from SSL_read", LOG_LEVEL_ERROR); + } + } + else +#endif // HAVE_OPENSSL + { + n = recv(GetSocket(), buf, TCP_BUFSIZE_READ, MSG_NOSIGNAL); + if (n == -1) + { + Handler().LogError(this, "read", Errno, StrError(Errno), LOG_LEVEL_FATAL); + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + return; + } + else + if (!n) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + SetShutdown(SHUT_WR); + return; + } + else + if (n > 0 && n <= TCP_BUFSIZE_READ) + { + m_bytes_received += n; + if (GetTrafficMonitor()) + { + GetTrafficMonitor() -> fwrite(buf, 1, n); + } + if (!m_b_input_buffer_disabled && !ibuf.Write(buf,n)) + { + Handler().LogError(this, "OnRead", 0, "ibuf overflow", LOG_LEVEL_WARNING); + } + } + else + { + Handler().LogError(this, "OnRead", n, "abnormal value from recv", LOG_LEVEL_ERROR); + } + } + // + OnRead( buf, n ); +} + +void TcpSocket::OnRead( char *buf, size_t n ) +{ + // unbuffered + if (n > 0 && n <= TCP_BUFSIZE_READ) + { + if (LineProtocol()) + { + buf[n] = 0; + size_t i = 0; + if (m_skip_c && (buf[i] == 13 || buf[i] == 10) && buf[i] != m_c) + { + m_skip_c = false; + i++; + } + size_t x = i; + for (; i < n && LineProtocol(); i++) + { + while ((buf[i] == 13 || buf[i] == 10) && LineProtocol()) + { + char c = buf[i]; + buf[i] = 0; + if (buf[x]) + { + m_line += (buf + x); + } + OnLine( m_line ); + i++; + m_skip_c = true; + m_c = c; + if (i < n && (buf[i] == 13 || buf[i] == 10) && buf[i] != c) + { + m_skip_c = false; + i++; + } + x = i; + m_line = ""; + } + if (!LineProtocol()) + { + break; + } + } + if (!LineProtocol()) + { + if (i < n) + { + OnRawData(buf + i, n - i); + } + } + else + if (buf[x]) + { + m_line += (buf + x); + } + } + else + { + OnRawData(buf, n); + } + } + if (m_b_input_buffer_disabled) + { + return; + } + // further processing: socks4 +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + bool need_more = false; + while (GetInputLength() && !need_more && !CloseAndDelete()) + { + need_more = OnSocks4Read(); + } + } +#endif +} + +void TcpSocket::OnWriteComplete() +{ +} + +void TcpSocket::OnWrite() +{ + if (Connecting()) + { + int err = SoError(); + + // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on + if (!err) // ok + { + Set(!IsDisableRead(), false); + SetConnecting(false); + SetCallOnConnect(); + return; + } + Handler().LogError(this, "tcp: connect failed", err, StrError(err), LOG_LEVEL_FATAL); + Set(false, false); // no more monitoring because connection failed + + // failed +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + // %! leave 'Connecting' flag set? + OnSocks4ConnectFailed(); + return; + } +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + // even though the connection failed at once, only retry after + // the connection timeout. + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + return; + } + SetConnecting(false); + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + return; + } + // try send next block in buffer + // if full block is sent, repeat + // if all blocks are sent, reset m_wfds + + bool repeat = false; + size_t sz = m_transfer_limit ? GetOutputLength() : 0; + do + { + output_l::iterator it = m_obuf.begin(); + OUTPUT *p = *it; + repeat = false; + int n = TryWrite(p -> Buf(), p -> Len()); + if (n > 0) + { + size_t left = p -> Remove(n); + m_output_length -= n; + if (!left) + { + delete p; + m_obuf.erase(it); + if (!m_obuf.size()) + { + m_obuf_top = NULL; + OnWriteComplete(); + } + else + { + repeat = true; + } + } + } + } while (repeat); + + if (m_transfer_limit && sz > m_transfer_limit && GetOutputLength() < m_transfer_limit) + { + OnTransferLimit(); + } + + // check output buffer set, set/reset m_wfds accordingly + { + bool br; + bool bw; + bool bx; + Handler().Get(GetSocket(), br, bw, bx); + if (m_obuf.size()) + Set(br, true); + else + Set(br, false); + } +} + +int TcpSocket::TryWrite(const char *buf, size_t len) +{ + int n = 0; +#ifdef HAVE_OPENSSL + if (IsSSL()) + { + n = SSL_write(m_ssl, buf, (int)len); + if (n == -1) + { + int errnr = SSL_get_error(m_ssl, n); + if ( errnr != SSL_ERROR_WANT_READ && errnr != SSL_ERROR_WANT_WRITE ) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + const char *errbuf = ERR_error_string(errnr, NULL); + Handler().LogError(this, "OnWrite/SSL_write", errnr, errbuf, LOG_LEVEL_FATAL); + } + return 0; + } + else + if (!n) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); +DEB( int errnr = SSL_get_error(m_ssl, n); + const char *errbuf = ERR_error_string(errnr, NULL); + fprintf(stderr, "SSL_write() returns 0: %d : %s\n",errnr, errbuf);) + } + } + else +#endif // HAVE_OPENSSL + { + n = send(GetSocket(), buf, (int)len, MSG_NOSIGNAL); + if (n == -1) + { + // normal error codes: + // WSAEWOULDBLOCK + // EAGAIN or EWOULDBLOCK +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + { + Handler().LogError(this, "send", Errno, StrError(Errno), LOG_LEVEL_FATAL); + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); + SetLost(); + } + return 0; + } + } + if (n > 0) + { + m_bytes_sent += n; + if (GetTrafficMonitor()) + { + GetTrafficMonitor() -> fwrite(buf, 1, n); + } + } + return n; +} + +void TcpSocket::Buffer(const char *buf, size_t len) +{ + size_t ptr = 0; + m_output_length += len; + while (ptr < len) + { + // buf/len => pbuf/sz + size_t space = 0; + if (m_obuf_top && (space = m_obuf_top -> Space()) > 0) + { + const char *pbuf = buf + ptr; + size_t sz = len - ptr; + if (space >= sz) + { + m_obuf_top -> Add(pbuf, sz); + ptr += sz; + } + else + { + m_obuf_top -> Add(pbuf, space); + ptr += space; + } + } + else + { + m_obuf_top = new OUTPUT; + m_obuf.push_back( m_obuf_top ); + } + } +} + +void TcpSocket::Send(const std::string &str,int i) +{ + SendBuf(str.c_str(),str.size(),i); +} + +void TcpSocket::SendBuf(const char *buf,size_t len,int) +{ + if (!Ready() && !Connecting()) + { + Handler().LogError(this, "SendBuf", -1, "Attempt to write to a non-ready socket" ); // warning + if (GetSocket() == INVALID_SOCKET) + Handler().LogError(this, "SendBuf", 0, " * GetSocket() == INVALID_SOCKET", LOG_LEVEL_INFO); + if (Connecting()) + Handler().LogError(this, "SendBuf", 0, " * Connecting()", LOG_LEVEL_INFO); + if (CloseAndDelete()) + Handler().LogError(this, "SendBuf", 0, " * CloseAndDelete()", LOG_LEVEL_INFO); + return; + } + if (!IsConnected()) + { + Handler().LogError(this, "SendBuf", -1, "Attempt to write to a non-connected socket, will be sent on connect" ); // warning + Buffer(buf, len); + return; + } + if (m_obuf_top) + { + Buffer(buf, len); + return; + } + int n = TryWrite(buf, len); + if (n >= 0 && n < (int)len) + { + Buffer(buf + n, len - n); + } + // if ( data in buffer || !IsConnected ) + // { + // add to buffer + // } + // else + // try_send + // if any data is unsent, buffer it and set m_wfds + + // check output buffer set, set/reset m_wfds accordingly + { + bool br; + bool bw; + bool bx; + Handler().Get(GetSocket(), br, bw, bx); + if (m_obuf.size()) + Set(br, true); + else + Set(br, false); + } +} + +void TcpSocket::OnLine(const std::string& ) +{ +} + +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif +TcpSocket::TcpSocket(const TcpSocket& s) +:StreamSocket(s) +,ibuf(0) +{ +} +#ifdef _MSC_VER +#pragma warning(default:4355) +#endif + +#ifdef ENABLE_SOCKS4 +void TcpSocket::OnSocks4Connect() +{ + char request[1000]; + memset(request, 0, sizeof(request)); + request[0] = 4; // socks v4 + request[1] = 1; // command code: CONNECT + { + std::auto_ptr<SocketAddress> ad = GetClientRemoteAddress(); + if (ad.get()) + { + struct sockaddr *p0 = (struct sockaddr *)*ad; + struct sockaddr_in *p = (struct sockaddr_in *)p0; + if (p -> sin_family == AF_INET) + { + memcpy(request + 2, &p -> sin_port, 2); // nwbo is ok here + memcpy(request + 4, &p -> sin_addr, sizeof(struct in_addr)); + } + else + { + /// \todo warn + } + } + else + { + /// \todo warn + } + } + strcpy(request + 8, GetSocks4Userid().c_str()); + size_t length = GetSocks4Userid().size() + 8 + 1; + SendBuf(request, length); + m_socks4_state = 0; +} + +void TcpSocket::OnSocks4ConnectFailed() +{ + Handler().LogError(this,"OnSocks4ConnectFailed",0,"connection to socks4 server failed, trying direct connection",LOG_LEVEL_WARNING); + if (!Handler().Socks4TryDirect()) + { + SetConnecting(false); + SetCloseAndDelete(); + OnConnectFailed(); // just in case + } + else + { + SetRetryClientConnect(); + } +} + +bool TcpSocket::OnSocks4Read() +{ + switch (m_socks4_state) + { + case 0: + ibuf.Read(&m_socks4_vn, 1); + m_socks4_state = 1; + break; + case 1: + ibuf.Read(&m_socks4_cd, 1); + m_socks4_state = 2; + break; + case 2: + if (GetInputLength() > 1) + { + ibuf.Read( (char *)&m_socks4_dstport, 2); + m_socks4_state = 3; + } + else + { + return true; + } + break; + case 3: + if (GetInputLength() > 3) + { + ibuf.Read( (char *)&m_socks4_dstip, 4); + SetSocks4(false); + + switch (m_socks4_cd) + { + case 90: + OnConnect(); + Handler().LogError(this, "OnSocks4Read", 0, "Connection established", LOG_LEVEL_INFO); + break; + case 91: + case 92: + case 93: + Handler().LogError(this,"OnSocks4Read",m_socks4_cd,"socks4 server reports connect failed",LOG_LEVEL_FATAL); + SetConnecting(false); + SetCloseAndDelete(); + OnConnectFailed(); + break; + default: + Handler().LogError(this,"OnSocks4Read",m_socks4_cd,"socks4 server unrecognized response",LOG_LEVEL_FATAL); + SetCloseAndDelete(); + break; + } + } + else + { + return true; + } + break; + } + return false; +} +#endif + +void TcpSocket::Sendf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char slask[5000]; // vsprintf / vsnprintf temporary +#ifdef _WIN32 + vsprintf(slask, format, ap); +#else + vsnprintf(slask, 5000, format, ap); +#endif + va_end(ap); + Send( slask ); +} + +#ifdef HAVE_OPENSSL +void TcpSocket::OnSSLConnect() +{ + SetNonblocking(true); + { + if (m_ssl_ctx) + { +DEB( fprintf(stderr, "SSL Context already initialized - closing socket\n");) + SetCloseAndDelete(true); + return; + } + InitSSLClient(); + } + if (m_ssl_ctx) + { + /* Connect the SSL socket */ + m_ssl = SSL_new(m_ssl_ctx); + if (!m_ssl) + { +DEB( fprintf(stderr, " m_ssl is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY); + m_sbio = BIO_new_socket((int)GetSocket(), BIO_NOCLOSE); + if (!m_sbio) + { +DEB( fprintf(stderr, " m_sbio is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_bio(m_ssl, m_sbio, m_sbio); + if (!SSLNegotiate()) + { + SetSSLNegotiate(); + } + } + else + { + SetCloseAndDelete(); + } +} + +void TcpSocket::OnSSLAccept() +{ + SetNonblocking(true); + { + if (m_ssl_ctx) + { +DEB( fprintf(stderr, "SSL Context already initialized - closing socket\n");) + SetCloseAndDelete(true); + return; + } + InitSSLServer(); + SetSSLServer(); + } + if (m_ssl_ctx) + { + m_ssl = SSL_new(m_ssl_ctx); + if (!m_ssl) + { +DEB( fprintf(stderr, " m_ssl is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY); + m_sbio = BIO_new_socket((int)GetSocket(), BIO_NOCLOSE); + if (!m_sbio) + { +DEB( fprintf(stderr, " m_sbio is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_bio(m_ssl, m_sbio, m_sbio); +// if (!SSLNegotiate()) + { + SetSSLNegotiate(); + } + } +} + +bool TcpSocket::SSLNegotiate() +{ + if (!IsSSLServer()) // client + { + int r = SSL_connect(m_ssl); + if (r > 0) + { + SetSSLNegotiate(false); + /// \todo: resurrect certificate check... client +// CheckCertificateChain( "");//ServerHOST); + SetNonblocking(false); + // + { + SetConnected(); + if (GetOutputLength()) + { + OnWrite(); + } + } +#ifdef ENABLE_RECONNECT + if (IsReconnect()) + OnReconnect(); + else +#endif + { + OnConnect(); + } + Handler().LogError(this, "SSLNegotiate/SSL_connect", 0, "Connection established", LOG_LEVEL_INFO); + return true; + } + else + if (!r) + { + Handler().LogError(this, "SSLNegotiate/SSL_connect", 0, "Connection failed", LOG_LEVEL_INFO); + SetSSLNegotiate(false); + SetCloseAndDelete(); + OnSSLConnectFailed(); + } + else + { + r = SSL_get_error(m_ssl, r); + if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) + { + Handler().LogError(this, "SSLNegotiate/SSL_connect", -1, "Connection failed", LOG_LEVEL_INFO); +DEB( fprintf(stderr, "SSL_connect() failed - closing socket, return code: %d\n",r);) + SetSSLNegotiate(false); + SetCloseAndDelete(true); + OnSSLConnectFailed(); + } + } + } + else // server + { + int r = SSL_accept(m_ssl); + if (r > 0) + { + SetSSLNegotiate(false); + /// \todo: resurrect certificate check... server +// CheckCertificateChain( "");//ClientHOST); + SetNonblocking(false); + // + { + SetConnected(); + if (GetOutputLength()) + { + OnWrite(); + } + } + OnAccept(); + Handler().LogError(this, "SSLNegotiate/SSL_accept", 0, "Connection established", LOG_LEVEL_INFO); + return true; + } + else + if (!r) + { + Handler().LogError(this, "SSLNegotiate/SSL_accept", 0, "Connection failed", LOG_LEVEL_INFO); + SetSSLNegotiate(false); + SetCloseAndDelete(); + OnSSLAcceptFailed(); + } + else + { + r = SSL_get_error(m_ssl, r); + if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) + { + Handler().LogError(this, "SSLNegotiate/SSL_accept", -1, "Connection failed", LOG_LEVEL_INFO); +DEB( fprintf(stderr, "SSL_accept() failed - closing socket, return code: %d\n",r);) + SetSSLNegotiate(false); + SetCloseAndDelete(true); + OnSSLAcceptFailed(); + } + } + } + return false; +} + +void TcpSocket::InitSSLClient() +{ + InitializeContext("", SSLv23_method()); +} + +void TcpSocket::InitSSLServer() +{ + Handler().LogError(this, "InitSSLServer", 0, "You MUST implement your own InitSSLServer method", LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} + +void TcpSocket::InitializeContext(const std::string& context, SSL_METHOD *meth_in) +{ + /* Create our context*/ + static std::map<std::string, SSL_CTX *> client_contexts; + if (client_contexts.find(context) == client_contexts.end()) + { + SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method(); + m_ssl_ctx = client_contexts[context] = SSL_CTX_new(meth); + SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY); + } + else + { + m_ssl_ctx = client_contexts[context]; + } +} + +void TcpSocket::InitializeContext(const std::string& context,const std::string& keyfile,const std::string& password,SSL_METHOD *meth_in) +{ + /* Create our context*/ + static std::map<std::string, SSL_CTX *> server_contexts; + if (server_contexts.find(context) == server_contexts.end()) + { + SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method(); + m_ssl_ctx = server_contexts[context] = SSL_CTX_new(meth); + SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY); + // session id + if (!context.empty()) + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)context.c_str(), (unsigned int)context.size()); + else + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)"--empty--", 9); + } + else + { + m_ssl_ctx = server_contexts[context]; + } + + /* Load our keys and certificates*/ + if (!(SSL_CTX_use_certificate_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read certificate file " + keyfile, LOG_LEVEL_FATAL); + } + + m_password = password; + SSL_CTX_set_default_passwd_cb(m_ssl_ctx, SSL_password_cb); + SSL_CTX_set_default_passwd_cb_userdata(m_ssl_ctx, this); + if (!(SSL_CTX_use_PrivateKey_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read private key file " + keyfile, LOG_LEVEL_FATAL); + } +} + +void TcpSocket::InitializeContext(const std::string& context,const std::string& certfile,const std::string& keyfile,const std::string& password,SSL_METHOD *meth_in) +{ + /* Create our context*/ + static std::map<std::string, SSL_CTX *> server_contexts; + if (server_contexts.find(context) == server_contexts.end()) + { + SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method(); + m_ssl_ctx = server_contexts[context] = SSL_CTX_new(meth); + SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY); + // session id + if (context.size()) + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)context.c_str(), (unsigned int)context.size()); + else + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)"--empty--", 9); + } + else + { + m_ssl_ctx = server_contexts[context]; + } + + /* Load our keys and certificates*/ + if (!(SSL_CTX_use_certificate_file(m_ssl_ctx, certfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read certificate file " + keyfile, LOG_LEVEL_FATAL); + } + + m_password = password; + SSL_CTX_set_default_passwd_cb(m_ssl_ctx, SSL_password_cb); + SSL_CTX_set_default_passwd_cb_userdata(m_ssl_ctx, this); + if (!(SSL_CTX_use_PrivateKey_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read private key file " + keyfile, LOG_LEVEL_FATAL); + } +} + +int TcpSocket::SSL_password_cb(char *buf,int num,int rwflag,void *userdata) +{ + Socket *p0 = static_cast<Socket *>(userdata); + TcpSocket *p = dynamic_cast<TcpSocket *>(p0); + std::string pw = p ? p -> GetPassword() : ""; + if ( (size_t)num < pw.size() + 1) + { + return 0; + } + strcpy(buf,pw.c_str()); + return (int)pw.size(); +} +#endif // HAVE_OPENSSL + +int TcpSocket::Close() +{ + if (GetSocket() == INVALID_SOCKET) // this could happen + { + Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING); + return 0; + } + int n; + SetNonblocking(true); + if (!Lost() && IsConnected() && !(GetShutdown() & SHUT_WR)) + { + if (shutdown(GetSocket(), SHUT_WR) == -1) + { + // failed... + Handler().LogError(this, "shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + } + // + char tmp[1000]; + if (!Lost() && (n = recv(GetSocket(),tmp,1000,0)) >= 0) + { + if (n) + { + Handler().LogError(this, "read() after shutdown", n, "bytes read", LOG_LEVEL_WARNING); + } + } +#ifdef HAVE_OPENSSL + if (IsSSL() && m_ssl) + SSL_shutdown(m_ssl); + if (m_ssl) + { + SSL_free(m_ssl); + m_ssl = NULL; + } +#endif + return Socket::Close(); +} + +#ifdef HAVE_OPENSSL +SSL_CTX *TcpSocket::GetSslContext() +{ + if (!m_ssl_ctx) + Handler().LogError(this, "GetSslContext", 0, "SSL Context is NULL; check InitSSLServer/InitSSLClient", LOG_LEVEL_WARNING); + return m_ssl_ctx; +} + +SSL *TcpSocket::GetSsl() +{ + if (!m_ssl) + Handler().LogError(this, "GetSsl", 0, "SSL is NULL; check InitSSLServer/InitSSLClient", LOG_LEVEL_WARNING); + return m_ssl; +} +#endif + +#ifdef ENABLE_RECONNECT +void TcpSocket::SetReconnect(bool x) +{ + m_b_reconnect = x; +} +#endif + +void TcpSocket::OnRawData(const char *buf_in,size_t len) +{ +} + +size_t TcpSocket::GetInputLength() +{ + return ibuf.GetLength(); +} + +size_t TcpSocket::GetOutputLength() +{ + return m_output_length; +} + +uint64_t TcpSocket::GetBytesReceived(bool clear) +{ + uint64_t z = m_bytes_received; + if (clear) + m_bytes_received = 0; + return z; +} + +uint64_t TcpSocket::GetBytesSent(bool clear) +{ + uint64_t z = m_bytes_sent; + if (clear) + m_bytes_sent = 0; + return z; +} + +#ifdef ENABLE_RECONNECT +bool TcpSocket::Reconnect() +{ + return m_b_reconnect; +} + +void TcpSocket::SetIsReconnect(bool x) +{ + m_b_is_reconnect = x; +} + +bool TcpSocket::IsReconnect() +{ + return m_b_is_reconnect; +} +#endif + +#ifdef HAVE_OPENSSL +const std::string& TcpSocket::GetPassword() +{ + return m_password; +} +#endif + +void TcpSocket::DisableInputBuffer(bool x) +{ + m_b_input_buffer_disabled = x; +} + +void TcpSocket::OnOptions(int family,int type,int protocol,SOCKET s) +{ +DEB( fprintf(stderr, "Socket::OnOptions()\n");) +#ifdef SO_NOSIGPIPE + SetSoNosigpipe(true); +#endif + SetSoReuseaddr(true); + SetSoKeepalive(true); +} + +void TcpSocket::SetLineProtocol(bool x) +{ + StreamSocket::SetLineProtocol(x); + DisableInputBuffer(x); +} + +bool TcpSocket::SetTcpNodelay(bool x) +{ +#ifdef TCP_NODELAY + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_TCP, TCP_NODELAY)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "TCP_NODELAY", LOG_LEVEL_INFO); + return false; +#endif +} + +TcpSocket::CircularBuffer::CircularBuffer(size_t size) +:buf(new char[2 * size]) +,m_max(size) +,m_q(0) +,m_b(0) +,m_t(0) +,m_count(0) +{ +} + +TcpSocket::CircularBuffer::~CircularBuffer() +{ + delete[] buf; +} + +bool TcpSocket::CircularBuffer::Write(const char *s,size_t l) +{ + if (m_q + l > m_max) + { + return false; // overflow + } + m_count += (unsigned long)l; + if (m_t + l > m_max) // block crosses circular border + { + size_t l1 = m_max - m_t; // size left until circular border crossing + // always copy full block to buffer(buf) + top pointer(m_t) + // because we have doubled the buffer size for performance reasons + memcpy(buf + m_t, s, l); + memcpy(buf, s + l1, l - l1); + m_t = l - l1; + m_q += l; + } + else + { + memcpy(buf + m_t, s, l); + memcpy(buf + m_max + m_t, s, l); + m_t += l; + if (m_t >= m_max) + m_t -= m_max; + m_q += l; + } + return true; +} + +bool TcpSocket::CircularBuffer::Read(char *s,size_t l) +{ + if (l > m_q) + { + return false; // not enough chars + } + if (m_b + l > m_max) // block crosses circular border + { + size_t l1 = m_max - m_b; + if (s) + { + memcpy(s, buf + m_b, l1); + memcpy(s + l1, buf, l - l1); + } + m_b = l - l1; + m_q -= l; + } + else + { + if (s) + { + memcpy(s, buf + m_b, l); + } + m_b += l; + if (m_b >= m_max) + m_b -= m_max; + m_q -= l; + } + if (!m_q) + { + m_b = m_t = 0; + } + return true; +} + +bool TcpSocket::CircularBuffer::SoftRead(char *s, size_t l) +{ + if (l > m_q) + { + return false; + } + if (m_b + l > m_max) // block crosses circular border + { + size_t l1 = m_max - m_b; + if (s) + { + memcpy(s, buf + m_b, l1); + memcpy(s + l1, buf, l - l1); + } + } + else + { + if (s) + { + memcpy(s, buf + m_b, l); + } + } + return true; +} + +bool TcpSocket::CircularBuffer::Remove(size_t l) +{ + return Read(NULL, l); +} + +size_t TcpSocket::CircularBuffer::GetLength() +{ + return m_q; +} + +const char *TcpSocket::CircularBuffer::GetStart() +{ + return buf + m_b; +} + +size_t TcpSocket::CircularBuffer::GetL() +{ + return (m_b + m_q > m_max) ? m_max - m_b : m_q; +} + +size_t TcpSocket::CircularBuffer::Space() +{ + return m_max - m_q; +} + +unsigned long TcpSocket::CircularBuffer::ByteCounter(bool clear) +{ + if (clear) + { + unsigned long x = m_count; + m_count = 0; + return x; + } + return m_count; +} + +std::string TcpSocket::CircularBuffer::ReadString(size_t l) +{ + char *sz = new char[l + 1]; + if (!Read(sz, l)) // failed, debug printout in Read() method + { + delete[] sz; + return ""; + } + sz[l] = 0; + std::string tmp = sz; + delete[] sz; + return tmp; +} + +void TcpSocket::OnConnectTimeout() +{ + Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL); +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + OnSocks4ConnectFailed(); + // retry direct connection + } + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + IncreaseConnectionRetries(); + // ask socket via OnConnectRetry callback if we should continue trying + if (OnConnectRetry()) + { + SetRetryClientConnect(); + } + else + { + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + } + } + else + { + SetCloseAndDelete(true); + /// \todo state reason why connect failed + OnConnectFailed(); + } + // + SetConnecting(false); +} + +#ifdef _WIN32 +void TcpSocket::OnException() +{ + if (Connecting()) + { +#ifdef ENABLE_SOCKS4 + if (Socks4()) + OnSocks4ConnectFailed(); + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && + GetConnectionRetries() < GetConnectionRetry() )) + { + // even though the connection failed at once, only retry after + // the connection timeout + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + } + else + { + SetConnecting(false); // tnx snibbe + SetCloseAndDelete(); + OnConnectFailed(); + } + return; + } + // %! exception doesn't always mean something bad happened, this code should be reworked + // errno valid here? + int err = SoError(); + Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} +#endif // _WIN32 + +int TcpSocket::Protocol() +{ + return IPPROTO_TCP; +} + +void TcpSocket::SetTransferLimit(size_t sz) +{ + m_transfer_limit = sz; +} + +void TcpSocket::OnTransferLimit() +{ +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/Thread.cpp b/externals/sockets/Thread.cpp new file mode 100644 index 00000000000..773e9f214fa --- /dev/null +++ b/externals/sockets/Thread.cpp @@ -0,0 +1,154 @@ +/** \file Thread.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include <stdio.h> +#ifdef _WIN32 +#include <process.h> +#include "socket_include.h" +#else +#include <unistd.h> +#endif + +#include "Thread.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +Thread::Thread(bool release) +:m_thread(0) +,m_running(true) +,m_release(false) +,m_b_delete_on_exit(false) +,m_b_destructor(false) +{ +#ifdef _WIN32 +// m_thread = ::CreateThread(NULL, 0, StartThread, this, 0, &m_dwThreadId); + m_thread = (HANDLE)_beginthreadex(NULL, 0, &StartThread, this, 0, &m_dwThreadId); +#else + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + if (pthread_create(&m_thread,&attr, StartThread,this) == -1) + { + perror("Thread: create failed"); + SetRunning(false); + } +// pthread_attr_destroy(&attr); +#endif + m_release = release; +} + +Thread::~Thread() +{ + m_b_destructor = true; + if (m_running) + { + SetRelease(true); + SetRunning(false); +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } +#ifdef _WIN32 + if (m_thread) + ::CloseHandle(m_thread); +#endif +} + +threadfunc_t STDPREFIX Thread::StartThread(threadparam_t zz) +{ + Thread *p = (Thread *)zz; + + while (p -> m_running && !p -> m_release) + { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + if (p -> m_running) + { + p -> Run(); + } + p -> SetRunning(false); // if return + if (p -> DeleteOnExit() && !p -> IsDestructor()) + { + delete p; + } +#ifdef _WIN32 + _endthreadex(0); +#endif + return (threadfunc_t)NULL; +} + +bool Thread::IsRunning() +{ + return m_running; +} + +void Thread::SetRunning(bool x) +{ + m_running = x; +} + +bool Thread::IsReleased() +{ + return m_release; +} + +void Thread::SetRelease(bool x) +{ + m_release = x; +} + +bool Thread::DeleteOnExit() +{ + return m_b_delete_on_exit; +} + +void Thread::SetDeleteOnExit(bool x) +{ + m_b_delete_on_exit = x; +} + +bool Thread::IsDestructor() +{ + return m_b_destructor; +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/UdpSocket.cpp b/externals/sockets/UdpSocket.cpp new file mode 100644 index 00000000000..a3d393c00e2 --- /dev/null +++ b/externals/sockets/UdpSocket.cpp @@ -0,0 +1,810 @@ +/** \file UdpSocket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include <stdlib.h> +#else +#include <errno.h> +#endif + +#include "ISocketHandler.h" +#include "UdpSocket.h" +#include "Utility.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif +// include this to see strange sights +//#include <linux/in6.h> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +UdpSocket::UdpSocket(ISocketHandler& h, int ibufsz, bool ipv6, int retries) : Socket(h) +, m_ibuf(new char[ibufsz]) +, m_ibufsz(ibufsz) +, m_bind_ok(false) +, m_port(0) +, m_last_size_written(-1) +, m_retries(retries) +, m_b_read_ts(false) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + SetIpv6(ipv6); +#endif +#endif +} + +UdpSocket::~UdpSocket() +{ + Close(); + delete[] m_ibuf; +} + +int UdpSocket::Bind(port_t &port, int range) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(port); + return Bind(ad, range); + } +#endif +#endif + Ipv4Address ad(port); + return Bind(ad, range); +} + +int UdpSocket::Bind(const std::string& intf, port_t &port, int range) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, range); + } + SetCloseAndDelete(); + return -1; + } +#endif +#endif + Ipv4Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, range); + } + SetCloseAndDelete(); + return -1; +} + +int UdpSocket::Bind(ipaddr_t a, port_t &port, int range) +{ + Ipv4Address ad(a, port); + return Bind(ad, range); +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +int UdpSocket::Bind(in6_addr a, port_t &port, int range) +{ + Ipv6Address ad(a, port); + return Bind(ad, range); +} +#endif +#endif + +int UdpSocket::Bind(SocketAddress& ad, int range) +{ + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp")); + } + if (GetSocket() != INVALID_SOCKET) + { + SetNonblocking(true); + int n = bind(GetSocket(), ad, ad); + int tries = range; + while (n == -1 && tries--) + { + ad.SetPort(ad.GetPort() + 1); + n = bind(GetSocket(), ad, ad); + } + if (n == -1) + { + Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +#ifdef ENABLE_EXCEPTIONS + throw Exception("bind() failed for UdpSocket, port:range: " + Utility::l2string(ad.GetPort()) + ":" + Utility::l2string(range)); +#endif + return -1; + } + m_bind_ok = true; + m_port = ad.GetPort(); + return 0; + } + return -1; +} + +/** if you wish to use Send, first Open a connection */ +bool UdpSocket::Open(ipaddr_t l, port_t port) +{ + Ipv4Address ad(l, port); + return Open(ad); +} + +bool UdpSocket::Open(const std::string& host, port_t port) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(host, port); + if (ad.IsValid()) + { + return Open(ad); + } + return false; + } +#endif +#endif + Ipv4Address ad(host, port); + if (ad.IsValid()) + { + return Open(ad); + } + return false; +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool UdpSocket::Open(struct in6_addr& a, port_t port) +{ + Ipv6Address ad(a, port); + return Open(ad); +} +#endif +#endif + +bool UdpSocket::Open(SocketAddress& ad) +{ + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp")); + } + if (GetSocket() != INVALID_SOCKET) + { + SetNonblocking(true); + if (connect(GetSocket(), ad, ad) == -1) + { + Handler().LogError(this, "connect", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + return false; + } + SetConnected(); + return true; + } + return false; +} + +void UdpSocket::CreateConnection() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + if (GetSocket() == INVALID_SOCKET) + { + SOCKET s = CreateSocket(AF_INET6, SOCK_DGRAM, "udp"); + if (s == INVALID_SOCKET) + { + return; + } + SetNonblocking(true, s); + Attach(s); + } + return; + } +#endif +#endif + if (GetSocket() == INVALID_SOCKET) + { + SOCKET s = CreateSocket(AF_INET, SOCK_DGRAM, "udp"); + if (s == INVALID_SOCKET) + { + return; + } + SetNonblocking(true, s); + Attach(s); + } +} + +/** send to specified address */ +void UdpSocket::SendToBuf(const std::string& h, port_t p, const char *data, int len, int flags) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(h, p); + if (ad.IsValid()) + { + SendToBuf(ad, data, len, flags); + } + return; + } +#endif +#endif + Ipv4Address ad(h, p); + if (ad.IsValid()) + { + SendToBuf(ad, data, len, flags); + } +} + +/** send to specified address */ +void UdpSocket::SendToBuf(ipaddr_t a, port_t p, const char *data, int len, int flags) +{ + Ipv4Address ad(a, p); + SendToBuf(ad, data, len, flags); +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void UdpSocket::SendToBuf(in6_addr a, port_t p, const char *data, int len, int flags) +{ + Ipv6Address ad(a, p); + SendToBuf(ad, data, len, flags); +} +#endif +#endif + +void UdpSocket::SendToBuf(SocketAddress& ad, const char *data, int len, int flags) +{ + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp")); + } + if (GetSocket() != INVALID_SOCKET) + { + SetNonblocking(true); + if ((m_last_size_written = sendto(GetSocket(), data, len, flags, ad, ad)) == -1) + { + Handler().LogError(this, "sendto", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + } +} + +void UdpSocket::SendTo(const std::string& a, port_t p, const std::string& str, int flags) +{ + SendToBuf(a, p, str.c_str(), (int)str.size(), flags); +} + +void UdpSocket::SendTo(ipaddr_t a, port_t p, const std::string& str, int flags) +{ + SendToBuf(a, p, str.c_str(), (int)str.size(), flags); +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void UdpSocket::SendTo(in6_addr a, port_t p, const std::string& str, int flags) +{ + SendToBuf(a, p, str.c_str(), (int)str.size(), flags); +} +#endif +#endif + +void UdpSocket::SendTo(SocketAddress& ad, const std::string& str, int flags) +{ + SendToBuf(ad, str.c_str(), (int)str.size(), flags); +} + +/** send to connected address */ +void UdpSocket::SendBuf(const char *data, size_t len, int flags) +{ + if (!IsConnected()) + { + Handler().LogError(this, "SendBuf", 0, "not connected", LOG_LEVEL_ERROR); + return; + } + if ((m_last_size_written = send(GetSocket(), data, (int)len, flags)) == -1) + { + Handler().LogError(this, "send", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } +} + +void UdpSocket::Send(const std::string& str, int flags) +{ + SendBuf(str.c_str(), (int)str.size(), flags); +} + +#if defined(LINUX) || defined(MACOSX) +int UdpSocket::ReadTS(char *ioBuf, int inBufSize, struct sockaddr *from, socklen_t fromlen, struct timeval *ts) +{ + struct msghdr msg; + struct iovec vec[1]; + union { + struct cmsghdr cm; +#ifdef MACOSX +#ifdef __DARWIN_UNIX03 +#define ALIGNBYTES __DARWIN_ALIGNBYTES +#endif +#define myALIGN(p) (((unsigned int)(p) + ALIGNBYTES) &~ ALIGNBYTES) +#define myCMSG_SPACE(l) (myALIGN(sizeof(struct cmsghdr)) + myALIGN(l)) + char data[ myCMSG_SPACE(sizeof(struct timeval)) ]; +#else + char data[ CMSG_SPACE(sizeof(struct timeval)) ]; +#endif + } cmsg_un; + struct cmsghdr *cmsg; + struct timeval *tv; + + vec[0].iov_base = ioBuf; + vec[0].iov_len = inBufSize; + + memset(&msg, 0, sizeof(msg)); + memset(from, 0, fromlen); + memset(ioBuf, 0, inBufSize); + memset(&cmsg_un, 0, sizeof(cmsg_un)); + + msg.msg_name = (caddr_t)from; + msg.msg_namelen = fromlen; + msg.msg_iov = vec; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_un.data; + msg.msg_controllen = sizeof(cmsg_un.data); + msg.msg_flags = 0; + + // Original version - for reference only + //int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + + int n = recvmsg(GetSocket(), &msg, MSG_DONTWAIT); + + // now ioBuf will contain the data, as if we used recvfrom + + // Now get the time + if(n != -1 && msg.msg_controllen >= sizeof(struct cmsghdr) && !(msg.msg_flags & MSG_CTRUNC)) + { + tv = 0; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) + { + tv = (struct timeval *)CMSG_DATA(cmsg); + } + } + if (tv) + { + memcpy(ts, tv, sizeof(struct timeval)); + } + } + // The address is in network order, but that's OK right now + return n; +} +#endif + +void UdpSocket::OnRead() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sa_len = sizeof(sa); + if (m_b_read_ts) + { + struct timeval ts; + Utility::GetTime(&ts); +#if !defined(LINUX) && !defined(MACOSX) + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); +#else + int n = ReadTS(m_ibuf, m_ibufsz, (struct sockaddr *)&sa, sa_len, &ts); +#endif + if (n > 0) + { + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len, &ts); + } + else + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + return; + } + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + int q = m_retries; // receive max 10 at one cycle + while (n > 0) + { + if (sa_len != sizeof(sa)) + { + Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING); + } + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len); + if (!q--) + break; + // + n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + } + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + return; + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + if (m_b_read_ts) + { + struct timeval ts; + Utility::GetTime(&ts); +#if !defined(LINUX) && !defined(MACOSX) + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); +#else + int n = ReadTS(m_ibuf, m_ibufsz, (struct sockaddr *)&sa, sa_len, &ts); +#endif + if (n > 0) + { + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len, &ts); + } + else + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + return; + } + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + int q = m_retries; + while (n > 0) + { + if (sa_len != sizeof(sa)) + { + Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING); + } + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len); + if (!q--) + break; + // + n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + } + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } +} + +void UdpSocket::SetBroadcast(bool b) +{ + int one = 1; + int zero = 0; + + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (b) + { + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof(one)) == -1) + { + Handler().LogError(this, "SetBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } + else + { + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *) &zero, sizeof(zero)) == -1) + { + Handler().LogError(this, "SetBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } +} + +bool UdpSocket::IsBroadcast() +{ + int is_broadcast = 0; + socklen_t size; + + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (getsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *)&is_broadcast, &size) == -1) + { + Handler().LogError(this, "IsBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return is_broadcast != 0; +} + +void UdpSocket::SetMulticastTTL(int ttl) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (setsockopt(GetSocket(), SOL_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastTTL", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } +} + +int UdpSocket::GetMulticastTTL() +{ + int ttl = 0; + socklen_t size = sizeof(int); + + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (getsockopt(GetSocket(), SOL_IP, IP_MULTICAST_TTL, (char *)&ttl, &size) == -1) + { + Handler().LogError(this, "GetMulticastTTL", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return ttl; +} + +void UdpSocket::SetMulticastLoop(bool x) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + int val = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&val, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return; + } +#endif +#endif + int val = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_IP, IP_MULTICAST_LOOP, (char *)&val, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } +} + +bool UdpSocket::IsMulticastLoop() +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + int is_loop = 0; + socklen_t size = sizeof(int); + if (getsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&is_loop, &size) == -1) + { + Handler().LogError(this, "IsMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return is_loop ? true : false; + } +#endif +#endif + int is_loop = 0; + socklen_t size = sizeof(int); + if (getsockopt(GetSocket(), SOL_IP, IP_MULTICAST_LOOP, (char *)&is_loop, &size) == -1) + { + Handler().LogError(this, "IsMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return is_loop ? true : false; +} + +void UdpSocket::AddMulticastMembership(const std::string& group, const std::string& local_if, int if_index) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct ipv6_mreq x; + struct in6_addr addr; + if (Utility::u2ip( group, addr )) + { + x.ipv6mr_multiaddr = addr; + x.ipv6mr_interface = if_index; + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&x, sizeof(struct ipv6_mreq)) == -1) + { + Handler().LogError(this, "AddMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } + return; + } +#endif +#endif + struct ip_mreq x; // ip_mreqn + ipaddr_t addr; + if (Utility::u2ip( group, addr )) + { + memcpy(&x.imr_multiaddr.s_addr, &addr, sizeof(addr)); + Utility::u2ip( local_if, addr); + memcpy(&x.imr_interface.s_addr, &addr, sizeof(addr)); +// x.imr_ifindex = if_index; + if (setsockopt(GetSocket(), SOL_IP, IP_ADD_MEMBERSHIP, (char *)&x, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "AddMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } +} + +void UdpSocket::DropMulticastMembership(const std::string& group, const std::string& local_if, int if_index) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct ipv6_mreq x; + struct in6_addr addr; + if (Utility::u2ip( group, addr )) + { + x.ipv6mr_multiaddr = addr; + x.ipv6mr_interface = if_index; + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (char *)&x, sizeof(struct ipv6_mreq)) == -1) + { + Handler().LogError(this, "DropMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } + return; + } +#endif +#endif + struct ip_mreq x; // ip_mreqn + ipaddr_t addr; + if (Utility::u2ip( group, addr )) + { + memcpy(&x.imr_multiaddr.s_addr, &addr, sizeof(addr)); + Utility::u2ip( local_if, addr); + memcpy(&x.imr_interface.s_addr, &addr, sizeof(addr)); +// x.imr_ifindex = if_index; + if (setsockopt(GetSocket(), SOL_IP, IP_DROP_MEMBERSHIP, (char *)&x, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "DropMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void UdpSocket::SetMulticastHops(int hops) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (!IsIpv6()) + { + Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR); + return; + } + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastHops", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } +} + +int UdpSocket::GetMulticastHops() +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (!IsIpv6()) + { + Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR); + return -1; + } + int hops = 0; + socklen_t size = sizeof(int); + if (getsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, &size) == -1) + { + Handler().LogError(this, "GetMulticastHops", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return hops; +} +#endif // IPPROTO_IPV6 +#endif + +bool UdpSocket::IsBound() +{ + return m_bind_ok; +} + +void UdpSocket::OnRawData(const char *buf, size_t len, struct sockaddr *sa, socklen_t sa_len) +{ +} + +void UdpSocket::OnRawData(const char *buf, size_t len, struct sockaddr *sa, socklen_t sa_len, struct timeval *ts) +{ +} + +port_t UdpSocket::GetPort() +{ + return m_port; +} + +int UdpSocket::GetLastSizeWritten() +{ + return m_last_size_written; +} + +void UdpSocket::SetTimestamp(bool x) +{ + m_b_read_ts = x; +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/Utility.cpp b/externals/sockets/Utility.cpp new file mode 100644 index 00000000000..7c093fc0832 --- /dev/null +++ b/externals/sockets/Utility.cpp @@ -0,0 +1,960 @@ +/** \file Utility.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Utility.h" +#include "Parse.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#include "Base64.h" +#include <vector> +#ifdef _WIN32 +#include <time.h> +#else +#include <netdb.h> +#include <pthread.h> +#endif +#include <map> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +// defines for the random number generator +#define TWIST_IA 397 +#define TWIST_IB (TWIST_LEN - TWIST_IA) +#define UMASK 0x80000000 +#define LMASK 0x7FFFFFFF +#define MATRIX_A 0x9908B0DF +#define TWIST(b,i,j) ((b)[i] & UMASK) | ((b)[j] & LMASK) +#define MAGIC_TWIST(s) (((s) & 1) * MATRIX_A) + +// statics +std::string Utility::m_host; +bool Utility::m_local_resolved = false; +ipaddr_t Utility::m_ip = 0; +std::string Utility::m_addr; +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +struct in6_addr Utility::m_local_ip6; +std::string Utility::m_local_addr6; +#endif +#endif + +std::string Utility::base64(const std::string& str_in) +{ + std::string str; + Base64 m_b; + m_b.encode(str_in, str, false); // , false == do not add cr/lf + return str; +} + +std::string Utility::base64d(const std::string& str_in) +{ + std::string str; + Base64 m_b; + m_b.decode(str_in, str); + return str; +} + +std::string Utility::l2string(long l) +{ + std::string str; + char tmp[100]; + sprintf(tmp,"%ld",l); + str = tmp; + return str; +} + +std::string Utility::bigint2string(uint64_t l) +{ + std::string str; + uint64_t tmp = l; + while (tmp) + { + uint64_t a = tmp % 10; + str = (char)(a + 48) + str; + tmp /= 10; + } + if (str.empty()) + { + str = "0"; + } + return str; +} + +uint64_t Utility::atoi64(const std::string& str) +{ + uint64_t l = 0; + for (size_t i = 0; i < str.size(); i++) + { + l = l * 10 + str[i] - 48; + } + return l; +} + +unsigned int Utility::hex2unsigned(const std::string& str) +{ + unsigned int r = 0; + for (size_t i = 0; i < str.size(); i++) + { + r = r * 16 + str[i] - 48 - ((str[i] >= 'A') ? 7 : 0) - ((str[i] >= 'a') ? 32 : 0); + } + return r; +} + +/* +* Encode string per RFC1738 URL encoding rules +* tnx rstaveley +*/ +std::string Utility::rfc1738_encode(const std::string& src) +{ +static char hex[] = "0123456789ABCDEF"; + std::string dst; + for (size_t i = 0; i < src.size(); i++) + { + if (isalnum(src[i])) + { + dst += src[i]; + } + else + if (src[i] == ' ') + { + dst += '+'; + } + else + { + unsigned char c = static_cast<unsigned char>(src[i]); + dst += '%'; + dst += hex[c / 16]; + dst += hex[c % 16]; + } + } + return dst; +} // rfc1738_encode + +/* +* Decode string per RFC1738 URL encoding rules +* tnx rstaveley +*/ +std::string Utility::rfc1738_decode(const std::string& src) +{ + std::string dst; + for (size_t i = 0; i < src.size(); i++) + { + if (src[i] == '%' && isxdigit(src[i + 1]) && isxdigit(src[i + 2])) + { + char c1 = src[++i]; + char c2 = src[++i]; + c1 = c1 - 48 - ((c1 >= 'A') ? 7 : 0) - ((c1 >= 'a') ? 32 : 0); + c2 = c2 - 48 - ((c2 >= 'A') ? 7 : 0) - ((c2 >= 'a') ? 32 : 0); + dst += (char)(c1 * 16 + c2); + } + else + if (src[i] == '+') + { + dst += ' '; + } + else + { + dst += src[i]; + } + } + return dst; +} // rfc1738_decode + +bool Utility::isipv4(const std::string& str) +{ + int dots = 0; + // %! ignore :port? + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '.') + dots++; + else + if (!isdigit(str[i])) + return false; + } + if (dots != 3) + return false; + return true; +} + +bool Utility::isipv6(const std::string& str) +{ + size_t qc = 0; + size_t qd = 0; + for (size_t i = 0; i < str.size(); i++) + { + qc += (str[i] == ':') ? 1 : 0; + qd += (str[i] == '.') ? 1 : 0; + } + if (qc > 7) + { + return false; + } + if (qd && qd != 3) + { + return false; + } + Parse pa(str,":."); + std::string tmp = pa.getword(); + while (!tmp.empty()) + { + if (tmp.size() > 4) + { + return false; + } + for (size_t i = 0; i < tmp.size(); i++) + { + if (tmp[i] < '0' || (tmp[i] > '9' && tmp[i] < 'A') || + (tmp[i] > 'F' && tmp[i] < 'a') || tmp[i] > 'f') + { + return false; + } + } + // + tmp = pa.getword(); + } + return true; +} + +bool Utility::u2ip(const std::string& str, ipaddr_t& l) +{ + struct sockaddr_in sa; + bool r = Utility::u2ip(str, sa); + memcpy(&l, &sa.sin_addr, sizeof(l)); + return r; +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool Utility::u2ip(const std::string& str, struct in6_addr& l) +{ + struct sockaddr_in6 sa; + bool r = Utility::u2ip(str, sa); + l = sa.sin6_addr; + return r; +} +#endif +#endif + +void Utility::l2ip(const ipaddr_t ip, std::string& str) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, &ip, sizeof(sa.sin_addr)); + Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); +} + +void Utility::l2ip(const in_addr& ip, std::string& str) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = ip; + Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void Utility::l2ip(const struct in6_addr& ip, std::string& str,bool mixed) +{ + char slask[100]; // l2ip temporary + *slask = 0; + unsigned int prev = 0; + bool skipped = false; + bool ok_to_skip = true; + if (mixed) + { + unsigned short x; + unsigned short addr16[8]; + memcpy(addr16, &ip, sizeof(addr16)); + for (size_t i = 0; i < 6; i++) + { + x = ntohs(addr16[i]); + if (*slask && (x || !ok_to_skip || prev)) + strcat(slask,":"); + if (x || !ok_to_skip) + { + sprintf(slask + strlen(slask),"%x", x); + if (x && skipped) + ok_to_skip = false; + } + else + { + skipped = true; + } + prev = x; + } + x = ntohs(addr16[6]); + sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255); + x = ntohs(addr16[7]); + sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255); + } + else + { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_addr = ip; + Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); + return; + } + str = slask; +} + +int Utility::in6_addr_compare(in6_addr a,in6_addr b) +{ + for (size_t i = 0; i < 16; i++) + { + if (a.s6_addr[i] < b.s6_addr[i]) + return -1; + if (a.s6_addr[i] > b.s6_addr[i]) + return 1; + } + return 0; +} +#endif +#endif + +void Utility::ResolveLocal() +{ + char h[256]; + + // get local hostname and translate into ip-address + *h = 0; + gethostname(h,255); + { + if (Utility::u2ip(h, m_ip)) + { + Utility::l2ip(m_ip, m_addr); + } + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + memset(&m_local_ip6, 0, sizeof(m_local_ip6)); + { + if (Utility::u2ip(h, m_local_ip6)) + { + Utility::l2ip(m_local_ip6, m_local_addr6); + } + } +#endif +#endif + m_host = h; + m_local_resolved = true; +} + +const std::string& Utility::GetLocalHostname() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_host; +} + +ipaddr_t Utility::GetLocalIP() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_ip; +} + +const std::string& Utility::GetLocalAddress() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_addr; +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +const struct in6_addr& Utility::GetLocalIP6() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_local_ip6; +} + +const std::string& Utility::GetLocalAddress6() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_local_addr6; +} +#endif +#endif + +void Utility::SetEnv(const std::string& var,const std::string& value) +{ +#if (defined(SOLARIS8) || defined(SOLARIS)) + { + static std::map<std::string, char *> vmap; + if (vmap.find(var) != vmap.end()) + { + delete[] vmap[var]; + } + vmap[var] = new char[var.size() + 1 + value.size() + 1]; + sprintf(vmap[var], "%s=%s", var.c_str(), value.c_str()); + putenv( vmap[var] ); + } +#elif defined _WIN32 + { + std::string slask = var + "=" + value; + _putenv( (char *)slask.c_str()); + } +#else + setenv(var.c_str(), value.c_str(), 1); +#endif +} + +std::string Utility::Sa2String(struct sockaddr *sa) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (sa -> sa_family == AF_INET6) + { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + std::string tmp; + Utility::l2ip(sa6 -> sin6_addr, tmp); + return tmp + ":" + Utility::l2string(ntohs(sa6 -> sin6_port)); + } +#endif +#endif + if (sa -> sa_family == AF_INET) + { + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + ipaddr_t a; + memcpy(&a, &sa4 -> sin_addr, 4); + std::string tmp; + Utility::l2ip(a, tmp); + return tmp + ":" + Utility::l2string(ntohs(sa4 -> sin_port)); + } + return ""; +} + +void Utility::GetTime(struct timeval *p) +{ +#ifdef _WIN32 + FILETIME ft; // Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). + GetSystemTimeAsFileTime(&ft); + uint64_t tt; + memcpy(&tt, &ft, sizeof(tt)); + tt /= 10; // make it usecs + p->tv_sec = (long)tt / 1000000; + p->tv_usec = (long)tt % 1000000; +#else + gettimeofday(p, NULL); +#endif +} + +std::auto_ptr<SocketAddress> Utility::CreateAddress(struct sockaddr *sa,socklen_t sa_len) +{ + switch (sa -> sa_family) + { + case AF_INET: + if (sa_len == sizeof(struct sockaddr_in)) + { + struct sockaddr_in *p = (struct sockaddr_in *)sa; + return std::auto_ptr<SocketAddress>(new Ipv4Address(*p)); + } + break; +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + case AF_INET6: + if (sa_len == sizeof(struct sockaddr_in6)) + { + struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa; + return std::auto_ptr<SocketAddress>(new Ipv6Address(*p)); + } + break; +#endif +#endif + } + return std::auto_ptr<SocketAddress>(NULL); +} + +bool Utility::u2ip(const std::string& host, struct sockaddr_in& sa, int ai_flags) +{ + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; +#ifdef NO_GETADDRINFO + if ((ai_flags & AI_NUMERICHOST) != 0 || isipv4(host)) + { + Parse pa((char *)host.c_str(), "."); + union { + struct { + unsigned char b1; + unsigned char b2; + unsigned char b3; + unsigned char b4; + } a; + ipaddr_t l; + } u; + u.a.b1 = static_cast<unsigned char>(pa.getvalue()); + u.a.b2 = static_cast<unsigned char>(pa.getvalue()); + u.a.b3 = static_cast<unsigned char>(pa.getvalue()); + u.a.b4 = static_cast<unsigned char>(pa.getvalue()); + memcpy(&sa.sin_addr, &u.l, sizeof(sa.sin_addr)); + return true; + } +#ifndef LINUX + struct hostent *he = gethostbyname( host.c_str() ); + if (!he) + { + return false; + } + memcpy(&sa.sin_addr, he -> h_addr, sizeof(sa.sin_addr)); +#else + struct hostent he; + struct hostent *result = NULL; + int myerrno = 0; + char buf[2000]; + int n = gethostbyname_r(host.c_str(), &he, buf, sizeof(buf), &result, &myerrno); + if (n || !result) + { + return false; + } + if (he.h_addr_list && he.h_addr_list[0]) + memcpy(&sa.sin_addr, he.h_addr, 4); + else + return false; +#endif + return true; +#else + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + // AI_NUMERICHOST + // AI_CANONNAME + // AI_PASSIVE - server + // AI_ADDRCONFIG + // AI_V4MAPPED + // AI_ALL + // AI_NUMERICSERV + hints.ai_flags = ai_flags; + hints.ai_family = AF_INET; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + struct addrinfo *res; + if (Utility::isipv4(host)) + hints.ai_flags |= AI_NUMERICHOST; + int n = getaddrinfo(host.c_str(), NULL, &hints, &res); + if (!n) + { + std::vector<struct addrinfo *> vec; + struct addrinfo *ai = res; + while (ai) + { + if (ai -> ai_addrlen == sizeof(sa)) + vec.push_back( ai ); + ai = ai -> ai_next; + } + if (vec.empty()) + return false; + ai = vec[Utility::Rnd() % vec.size()]; + { + memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen); + } + freeaddrinfo(res); + return true; + } + std::string error = "Error: "; +#ifndef __CYGWIN__ + error += gai_strerror(n); +#endif + return false; +#endif // NO_GETADDRINFO +} + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool Utility::u2ip(const std::string& host, struct sockaddr_in6& sa, int ai_flags) +{ + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; +#ifdef NO_GETADDRINFO + if ((ai_flags & AI_NUMERICHOST) != 0 || isipv6(host)) + { + std::list<std::string> vec; + size_t x = 0; + for (size_t i = 0; i <= host.size(); i++) + { + if (i == host.size() || host[i] == ':') + { + std::string s = host.substr(x, i - x); + // + if (strstr(s.c_str(),".")) // x.x.x.x + { + Parse pa(s,"."); + char slask[100]; // u2ip temporary hex2string conversion + unsigned long b0 = static_cast<unsigned long>(pa.getvalue()); + unsigned long b1 = static_cast<unsigned long>(pa.getvalue()); + unsigned long b2 = static_cast<unsigned long>(pa.getvalue()); + unsigned long b3 = static_cast<unsigned long>(pa.getvalue()); + sprintf(slask,"%lx",b0 * 256 + b1); + vec.push_back(slask); + sprintf(slask,"%lx",b2 * 256 + b3); + vec.push_back(slask); + } + else + { + vec.push_back(s); + } + // + x = i + 1; + } + } + size_t sz = vec.size(); // number of byte pairs + size_t i = 0; // index in in6_addr.in6_u.u6_addr16[] ( 0 .. 7 ) + unsigned short addr16[8]; + for (std::list<std::string>::iterator it = vec.begin(); it != vec.end(); it++) + { + std::string bytepair = *it; + if (!bytepair.empty()) + { + addr16[i++] = htons(Utility::hex2unsigned(bytepair)); + } + else + { + addr16[i++] = 0; + while (sz++ < 8) + { + addr16[i++] = 0; + } + } + } + memcpy(&sa.sin6_addr, addr16, sizeof(addr16)); + return true; + } +#ifdef SOLARIS + int errnum = 0; + struct hostent *he = getipnodebyname( host.c_str(), AF_INET6, 0, &errnum ); +#else + struct hostent *he = gethostbyname2( host.c_str(), AF_INET6 ); +#endif + if (!he) + { + return false; + } + memcpy(&sa.sin6_addr,he -> h_addr_list[0],he -> h_length); +#ifdef SOLARIS + free(he); +#endif + return true; +#else + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = ai_flags; + hints.ai_family = AF_INET6; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + struct addrinfo *res; + if (Utility::isipv6(host)) + hints.ai_flags |= AI_NUMERICHOST; + int n = getaddrinfo(host.c_str(), NULL, &hints, &res); + if (!n) + { + std::vector<struct addrinfo *> vec; + struct addrinfo *ai = res; + while (ai) + { + if (ai -> ai_addrlen == sizeof(sa)) + vec.push_back( ai ); + ai = ai -> ai_next; + } + if (vec.empty()) + return false; + ai = vec[Utility::Rnd() % vec.size()]; + { + memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen); + } + freeaddrinfo(res); + return true; + } + std::string error = "Error: "; +#ifndef __CYGWIN__ + error += gai_strerror(n); +#endif + return false; +#endif // NO_GETADDRINFO +} +#endif // IPPROTO_IPV6 +#endif // ENABLE_IPV6 + +bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, int flags) +{ + std::string service; + return Utility::reverse(sa, sa_len, hostname, service, flags); +} + +bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags) +{ + hostname = ""; + service = ""; +#ifdef NO_GETADDRINFO + switch (sa -> sa_family) + { + case AF_INET: + if (flags & NI_NUMERICHOST) + { + union { + struct { + unsigned char b1; + unsigned char b2; + unsigned char b3; + unsigned char b4; + } a; + ipaddr_t l; + } u; + struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; + memcpy(&u.l, &sa_in -> sin_addr, sizeof(u.l)); + char tmp[100]; + sprintf(tmp, "%u.%u.%u.%u", u.a.b1, u.a.b2, u.a.b3, u.a.b4); + hostname = tmp; + return true; + } + else + { + struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; + struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin_addr, sizeof(sa_in -> sin_addr), AF_INET); + if (h) + { + hostname = h -> h_name; + return true; + } + } + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + if (flags & NI_NUMERICHOST) + { + char slask[100]; // l2ip temporary + *slask = 0; + unsigned int prev = 0; + bool skipped = false; + bool ok_to_skip = true; + { + unsigned short addr16[8]; + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; + memcpy(addr16, &sa_in6 -> sin6_addr, sizeof(addr16)); + for (size_t i = 0; i < 8; i++) + { + unsigned short x = ntohs(addr16[i]); + if (*slask && (x || !ok_to_skip || prev)) + strcat(slask,":"); + if (x || !ok_to_skip) + { + sprintf(slask + strlen(slask),"%x", x); + if (x && skipped) + ok_to_skip = false; + } + else + { + skipped = true; + } + prev = x; + } + } + if (!*slask) + strcpy(slask, "::"); + hostname = slask; + return true; + } + else + { + // %! TODO: ipv6 reverse lookup + struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)sa; + struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin6_addr, sizeof(sa_in -> sin6_addr), AF_INET6); + if (h) + { + hostname = h -> h_name; + return true; + } + } + break; +#endif + } + return false; +#else + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + // NI_NOFQDN + // NI_NUMERICHOST + // NI_NAMEREQD + // NI_NUMERICSERV + // NI_DGRAM + int n = getnameinfo(sa, sa_len, host, sizeof(host), serv, sizeof(serv), flags); + if (n) + { + // EAI_AGAIN + // EAI_BADFLAGS + // EAI_FAIL + // EAI_FAMILY + // EAI_MEMORY + // EAI_NONAME + // EAI_OVERFLOW + // EAI_SYSTEM + return false; + } + hostname = host; + service = serv; + return true; +#endif // NO_GETADDRINFO +} + +bool Utility::u2service(const std::string& name, int& service, int ai_flags) +{ +#ifdef NO_GETADDRINFO + // %! + return false; +#else + struct addrinfo hints; + service = 0; + memset(&hints, 0, sizeof(hints)); + // AI_NUMERICHOST + // AI_CANONNAME + // AI_PASSIVE - server + // AI_ADDRCONFIG + // AI_V4MAPPED + // AI_ALL + // AI_NUMERICSERV + hints.ai_flags = ai_flags; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + struct addrinfo *res; + int n = getaddrinfo(NULL, name.c_str(), &hints, &res); + if (!n) + { + service = res -> ai_protocol; + freeaddrinfo(res); + return true; + } + return false; +#endif // NO_GETADDRINFO +} + +unsigned long Utility::ThreadID() +{ +#ifdef _WIN32 + return GetCurrentThreadId(); +#else + return (unsigned long)pthread_self(); +#endif +} + +std::string Utility::ToLower(const std::string& str) +{ + std::string r; + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] >= 'A' && str[i] <= 'Z') + r += str[i] | 32; + else + r += str[i]; + } + return r; +} + +std::string Utility::ToUpper(const std::string& str) +{ + std::string r; + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] >= 'a' && str[i] <= 'z') + r += (char)(str[i] - 32); + else + r += str[i]; + } + return r; +} + +std::string Utility::ToString(double d) +{ + char tmp[100]; + sprintf(tmp, "%f", d); + return tmp; +} + +unsigned long Utility::Rnd() +{ +static Utility::Rng generator( (unsigned long)time(NULL) ); + return generator.Get(); +} + +Utility::Rng::Rng(unsigned long seed) : m_value( 0 ) +{ + m_tmp[0]= seed & 0xffffffffUL; + for (int i = 1; i < TWIST_LEN; i++) + { + m_tmp[i] = (1812433253UL * (m_tmp[i - 1] ^ (m_tmp[i - 1] >> 30)) + i); + } +} + +unsigned long Utility::Rng::Get() +{ + unsigned long val = m_tmp[m_value]; + ++m_value; + if (m_value == TWIST_LEN) + { + for (int i = 0; i < TWIST_IB; ++i) + { + unsigned long s = TWIST(m_tmp, i, i + 1); + m_tmp[i] = m_tmp[i + TWIST_IA] ^ (s >> 1) ^ MAGIC_TWIST(s); + } + { + for (int i = 0; i < TWIST_LEN - 1; ++i) + { + unsigned long s = TWIST(m_tmp, i, i + 1); + m_tmp[i] = m_tmp[i - TWIST_IB] ^ (s >> 1) ^ MAGIC_TWIST(s); + } + } + unsigned long s = TWIST(m_tmp, TWIST_LEN - 1, 0); + m_tmp[TWIST_LEN - 1] = m_tmp[TWIST_IA - 1] ^ (s >> 1) ^ MAGIC_TWIST(s); + + m_value = 0; + } + return val; +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/externals/sockets/delme b/externals/sockets/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/sockets/delme +++ /dev/null diff --git a/externals/sockets/include/Base64.h b/externals/sockets/include/Base64.h new file mode 100644 index 00000000000..d4323aaa019 --- /dev/null +++ b/externals/sockets/include/Base64.h @@ -0,0 +1,77 @@ +/** \file Base64.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Base64_H +#define _SOCKETS_Base64_H + +#include "sockets-config.h" +#ifdef _MSC_VER +#pragma warning(disable:4514) +#endif + +#include <stdio.h> +#include <string> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup util Utilities */ + +/** Base64 encode/decode. + \ingroup util */ +class Base64 +{ +public: + Base64(); + + void encode(FILE *, std::string& , bool add_crlf = true); + void encode(const std::string&, std::string& , bool add_crlf = true); + void encode(const char *, size_t, std::string& , bool add_crlf = true); + void encode(const unsigned char *, size_t, std::string& , bool add_crlf = true); + + void decode(const std::string&, std::string& ); + void decode(const std::string&, unsigned char *, size_t&); + + size_t decode_length(const std::string& ); + +private: + Base64(const Base64& ) {} + Base64& operator=(const Base64& ) { return *this; } +static const char *bstr; +static const char rstr[128]; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Base64_H + + diff --git a/externals/sockets/include/Exception.h b/externals/sockets/include/Exception.h new file mode 100644 index 00000000000..bb881b2d74f --- /dev/null +++ b/externals/sockets/include/Exception.h @@ -0,0 +1,55 @@ +/** + ** \file Exception.h + ** \date 2007-09-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _Sockets_Exception_H +#define _Sockets_Exception_H + +#include <string> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Exception +{ +public: + Exception(const std::string& description); + virtual ~Exception() {} + + virtual const std::string ToString() const; + + Exception(const Exception& ) {} // copy constructor + + Exception& operator=(const Exception& ) { return *this; } // assignment operator + +private: + std::string m_description; + +}; + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _Sockets_Exception_H + + diff --git a/externals/sockets/include/File.h b/externals/sockets/include/File.h new file mode 100644 index 00000000000..ed322efa2d8 --- /dev/null +++ b/externals/sockets/include/File.h @@ -0,0 +1,82 @@ +/** \file File.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_File_H +#define _SOCKETS_File_H + +#include "sockets-config.h" +#include "IFile.h" +#include <stdio.h> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** IFile implementation of a disk file. + \ingroup file */ +class File : public IFile +{ +public: + File(); + ~File(); + + bool fopen(const std::string&, const std::string&); + void fclose(); + + size_t fread(char *, size_t, size_t) const; + size_t fwrite(const char *, size_t, size_t); + + char *fgets(char *, int) const; + void fprintf(const char *format, ...); + + off_t size() const; + bool eof() const; + + void reset_read() const; + void reset_write(); + +private: + File(const File& ) {} // copy constructor + File& operator=(const File& ) { return *this; } // assignment operator + + std::string m_path; + std::string m_mode; + FILE *m_fil; + mutable long m_rptr; + long m_wptr; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_File_H + + diff --git a/externals/sockets/include/IFile.h b/externals/sockets/include/IFile.h new file mode 100644 index 00000000000..657c8a4b1d9 --- /dev/null +++ b/externals/sockets/include/IFile.h @@ -0,0 +1,71 @@ +/** \file IFile.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_IFile_H +#define _SOCKETS_IFile_H + +#include "sockets-config.h" +#include <string> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup file File handling */ +/** Pure virtual file I/O interface. + \ingroup file */ +class IFile +{ +public: + virtual ~IFile() {} + + virtual bool fopen(const std::string&, const std::string&) = 0; + virtual void fclose() = 0; + + virtual size_t fread(char *, size_t, size_t) const = 0; + virtual size_t fwrite(const char *, size_t, size_t) = 0; + + virtual char *fgets(char *, int) const = 0; + virtual void fprintf(const char *format, ...) = 0; + + virtual off_t size() const = 0; + virtual bool eof() const = 0; + + virtual void reset_read() const = 0; + virtual void reset_write() = 0; + +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_IFile_H + + diff --git a/externals/sockets/include/ISocketHandler.h b/externals/sockets/include/ISocketHandler.h new file mode 100644 index 00000000000..940783c104b --- /dev/null +++ b/externals/sockets/include/ISocketHandler.h @@ -0,0 +1,231 @@ +/** \file ISocketHandler.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ISocketHandler_H +#define _SOCKETS_ISocketHandler_H +#include "sockets-config.h" + +#include <list> + +#include "socket_include.h" +#include "Socket.h" +#include "StdLog.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef enum { + LIST_CALLONCONNECT = 0, +#ifdef ENABLE_DETACH + LIST_DETACH, +#endif + LIST_TIMEOUT, + LIST_RETRY, + LIST_CLOSE +} list_t; + +class SocketAddress; +class Mutex; + +/** Socket container class, event generator. + \ingroup basic */ +class ISocketHandler +{ + friend class Socket; + +public: + /** Connection pool class for internal use by the ISocketHandler. + \ingroup internal */ +#ifdef ENABLE_POOL + class PoolSocket : public Socket + { + public: + PoolSocket(ISocketHandler& h,Socket *src) : Socket(h) { + CopyConnection( src ); + SetIsClient(); + } + + void OnRead() { + Handler().LogError(this, "OnRead", 0, "data on hibernating socket", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + void OnOptions(int,int,int,SOCKET) {} + + }; +#endif + +public: + virtual ~ISocketHandler() {} + + /** Get mutex reference for threadsafe operations. */ + virtual Mutex& GetMutex() const = 0; + + /** Register StdLog object for error callback. + \param log Pointer to log class */ + virtual void RegStdLog(StdLog *log) = 0; + + /** Log error to log class for print out / storage. */ + virtual void LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t = LOG_LEVEL_WARNING) = 0; + + // ------------------------------------------------------------------------- + // Socket stuff + // ------------------------------------------------------------------------- + /** Add socket instance to socket map. Removal is always automatic. */ + virtual void Add(Socket *) = 0; +private: + /** Remove socket from socket map, used by Socket class. */ + virtual void Remove(Socket *) = 0; +public: + /** Get status of read/write/exception file descriptor set for a socket. */ + virtual void Get(SOCKET s,bool& r,bool& w,bool& e) = 0; + /** Set read/write/exception file descriptor sets (fd_set). */ + virtual void Set(SOCKET s,bool bRead,bool bWrite,bool bException = true) = 0; + + /** Wait for events, generate callbacks. */ + virtual int Select(long sec,long usec) = 0; + /** This method will not return until an event has been detected. */ + virtual int Select() = 0; + /** Wait for events, generate callbacks. */ + virtual int Select(struct timeval *tsel) = 0; + + /** Check that a socket really is handled by this socket handler. */ + virtual bool Valid(Socket *) = 0; + /** Return number of sockets handled by this handler. */ + virtual size_t GetCount() = 0; + + /** Override and return false to deny all incoming connections. + \param p ListenSocket class pointer (use GetPort to identify which one) */ + virtual bool OkToAccept(Socket *p) = 0; + + /** Called by Socket when a socket changes state. */ + virtual void AddList(SOCKET s,list_t which_one,bool add) = 0; + + // ------------------------------------------------------------------------- + // Connection pool + // ------------------------------------------------------------------------- +#ifdef ENABLE_POOL + /** Find available open connection (used by connection pool). */ + virtual ISocketHandler::PoolSocket *FindConnection(int type,const std::string& protocol,SocketAddress&) = 0; + /** Enable connection pool (by default disabled). */ + virtual void EnablePool(bool = true) = 0; + /** Check pool status. + \return true if connection pool is enabled */ + virtual bool PoolEnabled() = 0; +#endif // ENABLE_POOL + + // ------------------------------------------------------------------------- + // Socks4 + // ------------------------------------------------------------------------- +#ifdef ENABLE_SOCKS4 + /** Set socks4 server ip that all new tcp sockets should use. */ + virtual void SetSocks4Host(ipaddr_t) = 0; + /** Set socks4 server hostname that all new tcp sockets should use. */ + virtual void SetSocks4Host(const std::string& ) = 0; + /** Set socks4 server port number that all new tcp sockets should use. */ + virtual void SetSocks4Port(port_t) = 0; + /** Set optional socks4 userid. */ + virtual void SetSocks4Userid(const std::string& ) = 0; + /** If connection to socks4 server fails, immediately try direct connection to final host. */ + virtual void SetSocks4TryDirect(bool = true) = 0; + /** Get socks4 server ip. + \return socks4 server ip */ + virtual ipaddr_t GetSocks4Host() = 0; + /** Get socks4 port number. + \return socks4 port number */ + virtual port_t GetSocks4Port() = 0; + /** Get socks4 userid (optional). + \return socks4 userid */ + virtual const std::string& GetSocks4Userid() = 0; + /** Check status of socks4 try direct flag. + \return true if direct connection should be tried if connection to socks4 server fails */ + virtual bool Socks4TryDirect() = 0; +#endif // ENABLE_SOCKS4 + + // ------------------------------------------------------------------------- + // DNS resolve server + // ------------------------------------------------------------------------- +#ifdef ENABLE_RESOLVER + /** Enable asynchronous DNS. + \param port Listen port of asynchronous dns server */ + virtual void EnableResolver(port_t = 16667) = 0; + /** Check resolver status. + \return true if resolver is enabled */ + virtual bool ResolverEnabled() = 0; + /** Queue a dns request. + \param host Hostname to be resolved + \param port Port number will be echoed in Socket::OnResolved callback */ + virtual int Resolve(Socket *,const std::string& host,port_t port) = 0; +#ifdef ENABLE_IPV6 + virtual int Resolve6(Socket *,const std::string& host,port_t port) = 0; +#endif + /** Do a reverse dns lookup. */ + virtual int Resolve(Socket *,ipaddr_t a) = 0; +#ifdef ENABLE_IPV6 + virtual int Resolve(Socket *,in6_addr& a) = 0; +#endif + /** Get listen port of asynchronous dns server. */ + virtual port_t GetResolverPort() = 0; + /** Resolver thread ready for queries. */ + virtual bool ResolverReady() = 0; + /** Returns true if socket waiting for a resolve event. */ + virtual bool Resolving(Socket *) = 0; +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_TRIGGERS + /** Fetch unique trigger id. */ + virtual int TriggerID(Socket *src) = 0; + /** Subscribe socket to trigger id. */ + virtual bool Subscribe(int id, Socket *dst) = 0; + /** Unsubscribe socket from trigger id. */ + virtual bool Unsubscribe(int id, Socket *dst) = 0; + /** Execute OnTrigger for subscribed sockets. + \param id Trigger ID + \param data Data passed from source to destination + \param erase Empty trigger id source and destination maps if 'true', + Leave them in place if 'false' - if a trigger should be called many times */ + virtual void Trigger(int id, Socket::TriggerData& data, bool erase = true) = 0; +#endif // ENABLE_TRIGGERS + +#ifdef ENABLE_DETACH + /** Indicates that the handler runs under SocketThread. */ + virtual void SetSlave(bool x = true) = 0; + /** Indicates that the handler runs under SocketThread. */ + virtual bool IsSlave() = 0; +#endif // ENABLE_DETACH + +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_ISocketHandler_H + + diff --git a/externals/sockets/include/Ipv4Address.h b/externals/sockets/include/Ipv4Address.h new file mode 100644 index 00000000000..71d925254e9 --- /dev/null +++ b/externals/sockets/include/Ipv4Address.h @@ -0,0 +1,95 @@ +/** + ** \file Ipv4Address.h + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Ipv4Address_H +#define _SOCKETS_Ipv4Address_H + +#include "sockets-config.h" +#include "SocketAddress.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/* Ipv4 address implementation. + \ingroup basic */ +class Ipv4Address : public SocketAddress +{ +public: + /** Create empty Ipv4 address structure. + \param port Port number */ + Ipv4Address(port_t port = 0); + /** Create Ipv4 address structure. + \param a Socket address in network byte order (as returned by Utility::u2ip) + \param port Port number in host byte order */ + Ipv4Address(ipaddr_t a,port_t port); + /** Create Ipv4 address structure. + \param a Socket address in network byte order + \param port Port number in host byte order */ + Ipv4Address(struct in_addr& a,port_t port); + /** Create Ipv4 address structure. + \param host Hostname to be resolved + \param port Port number in host byte order */ + Ipv4Address(const std::string& host,port_t port); + Ipv4Address(struct sockaddr_in&); + ~Ipv4Address(); + + // SocketAddress implementation + + operator struct sockaddr *(); + operator socklen_t(); + bool operator==(SocketAddress&); + + void SetPort(port_t port); + port_t GetPort(); + + void SetAddress(struct sockaddr *sa); + int GetFamily(); + + bool IsValid(); + std::auto_ptr<SocketAddress> GetCopy(); + + /** Convert address struct to text. */ + std::string Convert(bool include_port = false); + std::string Reverse(); + + /** Resolve hostname. */ +static bool Resolve(const std::string& hostname,struct in_addr& a); + /** Reverse resolve (IP to hostname). */ +static bool Reverse(struct in_addr& a,std::string& name); + /** Convert address struct to text. */ +static std::string Convert(struct in_addr& a); + +private: + Ipv4Address(const Ipv4Address& ) {} // copy constructor + Ipv4Address& operator=(const Ipv4Address& ) { return *this; } // assignment operator + struct sockaddr_in m_addr; + bool m_valid; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // _SOCKETS_Ipv4Address_H + + diff --git a/externals/sockets/include/Ipv6Address.h b/externals/sockets/include/Ipv6Address.h new file mode 100644 index 00000000000..20c68d8c92d --- /dev/null +++ b/externals/sockets/include/Ipv6Address.h @@ -0,0 +1,105 @@ +/** + ** \file Ipv6Address.h + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Ipv6Address_H +#define _SOCKETS_Ipv6Address_H +#include "sockets-config.h" +#ifdef ENABLE_IPV6 + +#include "SocketAddress.h" +#ifdef IPPROTO_IPV6 +#if defined( _WIN32) && !defined(__CYGWIN__) +typedef unsigned __int32 uint32_t; +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Ipv6 address implementation. + \ingroup basic */ +class Ipv6Address : public SocketAddress +{ +public: + /** Create empty Ipv6 address structure. + \param port Port number */ + Ipv6Address(port_t port = 0); + /** Create Ipv6 address structure. + \param a Socket address in network byte order + \param port Port number in host byte order */ + Ipv6Address(struct in6_addr& a,port_t port); + /** Create Ipv6 address structure. + \param host Hostname to be resolved + \param port Port number in host byte order */ + Ipv6Address(const std::string& host,port_t port); + Ipv6Address(struct sockaddr_in6&); + ~Ipv6Address(); + + // SocketAddress implementation + + operator struct sockaddr *(); + operator socklen_t(); + bool operator==(SocketAddress&); + + void SetPort(port_t port); + port_t GetPort(); + + void SetAddress(struct sockaddr *sa); + int GetFamily(); + + bool IsValid(); + std::auto_ptr<SocketAddress> GetCopy(); + + /** Convert address struct to text. */ + std::string Convert(bool include_port = false); + std::string Reverse(); + + /** Resolve hostname. */ +static bool Resolve(const std::string& hostname,struct in6_addr& a); + /** Reverse resolve (IP to hostname). */ +static bool Reverse(struct in6_addr& a,std::string& name); + /** Convert address struct to text. */ +static std::string Convert(struct in6_addr& a,bool mixed = false); + + void SetFlowinfo(uint32_t); + uint32_t GetFlowinfo(); +#ifndef _WIN32 + void SetScopeId(uint32_t); + uint32_t GetScopeId(); +#endif + +private: + Ipv6Address(const Ipv6Address& ) {} // copy constructor + Ipv6Address& operator=(const Ipv6Address& ) { return *this; } // assignment operator + struct sockaddr_in6 m_addr; + bool m_valid; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // IPPROTO_IPV6 +#endif // ENABLE_IPV6 +#endif // _SOCKETS_Ipv6Address_H + + diff --git a/externals/sockets/include/ListenSocket.h b/externals/sockets/include/ListenSocket.h new file mode 100644 index 00000000000..8934a809d0e --- /dev/null +++ b/externals/sockets/include/ListenSocket.h @@ -0,0 +1,418 @@ +/** \file ListenSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ListenSocket_H +#define _SOCKETS_ListenSocket_H +#include "sockets-config.h" + +#ifdef _WIN32 +#include <stdlib.h> +#else +#include <errno.h> +#endif + +#include "ISocketHandler.h" +#include "Socket.h" +#include "Utility.h" +#include "SctpSocket.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Binds incoming port number to new Socket class X. + \ingroup basic */ +template <class X> +class ListenSocket : public Socket +{ +public: + /** Constructor. + \param h ISocketHandler reference + \param use_creator Optional use of creator (default true) */ + ListenSocket(ISocketHandler& h,bool use_creator = true) : Socket(h), m_depth(0), m_creator(NULL) + ,m_bHasCreate(false) + { + if (use_creator) + { + m_creator = new X(h); + Socket *tmp = m_creator -> Create(); + if (tmp && dynamic_cast<X *>(tmp)) + { + m_bHasCreate = true; + } + if (tmp) + { + delete tmp; + } + } + } + ~ListenSocket() { + if (m_creator) + { + delete m_creator; + } + } + + /** Close file descriptor. */ + int Close() { + if (GetSocket() != INVALID_SOCKET) + { + closesocket(GetSocket()); + } + return 0; + } + + /** Bind and listen to any interface. + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(port_t port,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(port); + return Bind(ad, depth); + } + else +#endif +#endif + { + Ipv4Address ad(port); + return Bind(ad, depth); + } + } + + int Bind(SocketAddress& ad,int depth) { +#ifdef USE_SCTP + if (dynamic_cast<SctpSocket *>(m_creator)) + { + return Bind(ad, "sctp", depth); + } +#endif + return Bind(ad, "tcp", depth); + } + + /** Bind and listen to any interface, with optional protocol. + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(port_t port,const std::string& protocol,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(port); + return Bind(ad, protocol, depth); + } + else +#endif +#endif + { + Ipv4Address ad(port); + return Bind(ad, protocol, depth); + } + } + + /** Bind and listen to specific interface. + \param intf Interface hostname + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(const std::string& intf,port_t port,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + else +#endif +#endif + { + Ipv4Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + } + + /** Bind and listen to specific interface. + \param intf Interface hostname + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(const std::string& intf,port_t port,const std::string& protocol,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, protocol, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + else +#endif +#endif + { + Ipv4Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, protocol, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + } + + /** Bind and listen to ipv4 interface. + \param a Ipv4 interface address + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(ipaddr_t a,port_t port,int depth = 20) { + Ipv4Address ad(a, port); +#ifdef USE_SCTP + if (dynamic_cast<SctpSocket *>(m_creator)) + { + return Bind(ad, "sctp", depth); + } +#endif + return Bind(ad, "tcp", depth); + } + /** Bind and listen to ipv4 interface. + \param a Ipv4 interface address + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(ipaddr_t a,port_t port,const std::string& protocol,int depth) { + Ipv4Address ad(a, port); + return Bind(ad, protocol, depth); + } + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Bind and listen to ipv6 interface. + \param a Ipv6 interface address + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(in6_addr a,port_t port,int depth = 20) { + Ipv6Address ad(a, port); +#ifdef USE_SCTP + if (dynamic_cast<SctpSocket *>(m_creator)) + { + return Bind(ad, "sctp", depth); + } +#endif + return Bind(ad, "tcp", depth); + } + /** Bind and listen to ipv6 interface. + \param a Ipv6 interface address + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(in6_addr a,port_t port,const std::string& protocol,int depth) { + Ipv6Address ad(a, port); + return Bind(ad, protocol, depth); + } +#endif +#endif + + /** Bind and listen to network interface. + \param ad Interface address + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(SocketAddress& ad,const std::string& protocol,int depth) { + SOCKET s; + if ( (s = CreateSocket(ad.GetFamily(), SOCK_STREAM, protocol)) == INVALID_SOCKET) + { + return -1; + } + if (bind(s, ad, ad) == -1) + { + Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL); + closesocket(s); +#ifdef ENABLE_EXCEPTIONS + throw Exception("bind() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno)); +#endif + return -1; + } + if (listen(s, depth) == -1) + { + Handler().LogError(this, "listen", Errno, StrError(Errno), LOG_LEVEL_FATAL); + closesocket(s); +#ifdef ENABLE_EXCEPTIONS + throw Exception("listen() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno)); +#endif + return -1; + } + m_depth = depth; + Attach(s); + return 0; + } + + /** Return assigned port number. */ + port_t GetPort() + { + return GetSockPort(); + } + + /** Return listen queue depth. */ + int GetDepth() + { + return m_depth; + } + + /** OnRead on a ListenSocket receives an incoming connection. */ + void OnRead() + { + struct sockaddr sa; + socklen_t sa_len = sizeof(struct sockaddr); + SOCKET a_s = accept(GetSocket(), &sa, &sa_len); + + if (a_s == INVALID_SOCKET) + { + Handler().LogError(this, "accept", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return; + } + if (!Handler().OkToAccept(this)) + { + Handler().LogError(this, "accept", -1, "Not OK to accept", LOG_LEVEL_WARNING); + closesocket(a_s); + return; + } + if (Handler().GetCount() >= FD_SETSIZE) + { + Handler().LogError(this, "accept", (int)Handler().GetCount(), "ISocketHandler fd_set limit reached", LOG_LEVEL_FATAL); + closesocket(a_s); + return; + } + Socket *tmp = m_bHasCreate ? m_creator -> Create() : new X(Handler()); +#ifdef ENABLE_IPV6 + tmp -> SetIpv6( IsIpv6() ); +#endif + tmp -> SetParent(this); + tmp -> Attach(a_s); + tmp -> SetNonblocking(true); + { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (sa_len == sizeof(struct sockaddr_in6)) + { + struct sockaddr_in6 *p = (struct sockaddr_in6 *)&sa; + if (p -> sin6_family == AF_INET6) + { + Ipv6Address ad(p -> sin6_addr,ntohs(p -> sin6_port)); + ad.SetFlowinfo(p -> sin6_flowinfo); +#ifndef _WIN32 + ad.SetScopeId(p -> sin6_scope_id); +#endif + tmp -> SetRemoteAddress(ad); + } + } +#endif +#endif + if (sa_len == sizeof(struct sockaddr_in)) + { + struct sockaddr_in *p = (struct sockaddr_in *)&sa; + if (p -> sin_family == AF_INET) + { + Ipv4Address ad(p -> sin_addr,ntohs(p -> sin_port)); + tmp -> SetRemoteAddress(ad); + } + } + } + tmp -> SetConnected(true); + tmp -> Init(); + tmp -> SetDeleteByHandler(true); + Handler().Add(tmp); +#ifdef HAVE_OPENSSL + if (tmp -> IsSSL()) // SSL Enabled socket + { + // %! OnSSLAccept calls SSLNegotiate that can finish in this one call. + // %! If that happens and negotiation fails, the 'tmp' instance is + // %! still added to the list of active sockets in the sockethandler. + // %! See bugfix for this in SocketHandler::Select - don't Set rwx + // %! flags if CloseAndDelete() flag is true. + // %! An even better fugbix (see TcpSocket::OnSSLAccept) now avoids + // %! the Add problem altogether, so ignore the above. + // %! (OnSSLAccept does no longer call SSLNegotiate().) + tmp -> OnSSLAccept(); + } + else +#endif + { + tmp -> OnAccept(); + } + } + + /** Please don't use this method. + "accept()" is handled automatically in the OnRead() method. */ + virtual SOCKET Accept(SOCKET socket, struct sockaddr *saptr, socklen_t *lenptr) + { + return accept(socket, saptr, lenptr); + } + + bool HasCreator() { return m_bHasCreate; } + + void OnOptions(int,int,int,SOCKET) { + SetSoReuseaddr(true); + } + +protected: + ListenSocket(const ListenSocket& s) : Socket(s) {} +private: + ListenSocket& operator=(const ListenSocket& ) { return *this; } + int m_depth; + X *m_creator; + bool m_bHasCreate; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_ListenSocket_H + + diff --git a/externals/sockets/include/Lock.h b/externals/sockets/include/Lock.h new file mode 100644 index 00000000000..f3bb9273920 --- /dev/null +++ b/externals/sockets/include/Lock.h @@ -0,0 +1,58 @@ +/** \file Lock.h + ** \date 2005-08-22 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Lock_H +#define _SOCKETS_Lock_H + +#include "sockets-config.h" +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Mutex; + +/** Mutex encapsulation class. + \ingroup threading */ +class Lock +{ +public: + Lock(Mutex&); + ~Lock(); + +private: + Mutex& m_mutex; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif +#endif // _SOCKETS_Lock_H + + diff --git a/externals/sockets/include/Mutex.h b/externals/sockets/include/Mutex.h new file mode 100644 index 00000000000..e42a57c3262 --- /dev/null +++ b/externals/sockets/include/Mutex.h @@ -0,0 +1,68 @@ +/** \file Mutex.h + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Mutex_H +#define _SOCKETS_Mutex_H + +#include "sockets-config.h" +#ifndef _WIN32 +#include <pthread.h> +#else +#include <windows.h> +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Mutex container class, used by Lock. + \ingroup threading */ +class Mutex +{ + friend class Lock; +public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); +private: +#ifdef _WIN32 + HANDLE m_mutex; +#else + pthread_mutex_t m_mutex; +#endif +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif +#endif // _SOCKETS_Mutex_H + + diff --git a/externals/sockets/include/Parse.h b/externals/sockets/include/Parse.h new file mode 100644 index 00000000000..52bd9327e28 --- /dev/null +++ b/externals/sockets/include/Parse.h @@ -0,0 +1,100 @@ +/** \file Parse.h - parse a string + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _SOCKETS_Parse_H +#define _SOCKETS_Parse_H + +#include "sockets-config.h" +#ifdef _MSC_VER +#pragma warning(disable:4514) +#endif + +#include <string> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/***************************************************/ +/* interface of class Parse */ + +/** Splits a string whatever way you want. + \ingroup util */ +class Parse +{ +public: + Parse(); + Parse(const std::string&); + Parse(const std::string&,const std::string&); + Parse(const std::string&,const std::string&,short); + ~Parse(); + short issplit(const char); + void getsplit(); + void getsplit(std::string&); + std::string getword(); + void getword(std::string&); + void getword(std::string&,std::string&,int); + std::string getrest(); + void getrest(std::string&); + long getvalue(); + void setbreak(const char); + int getwordlen(); + int getrestlen(); + void enablebreak(const char c) { + pa_enable = c; + } + void disablebreak(const char c) { + pa_disable = c; + } + void getline(); + void getline(std::string&); + size_t getptr() { return pa_the_ptr; } + void EnableQuote(bool b) { pa_quote = b; } + +private: + std::string pa_the_str; + std::string pa_splits; + std::string pa_ord; + size_t pa_the_ptr; + char pa_breakchar; + char pa_enable; + char pa_disable; + short pa_nospace; + bool pa_quote; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Parse_H + + diff --git a/externals/sockets/include/ResolvServer.h b/externals/sockets/include/ResolvServer.h new file mode 100644 index 00000000000..409c9b7a619 --- /dev/null +++ b/externals/sockets/include/ResolvServer.h @@ -0,0 +1,72 @@ +/** \file ResolvServer.h + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ResolvServer_H +#define _SOCKETS_ResolvServer_H +#include "sockets-config.h" +#ifdef ENABLE_RESOLVER +#include "socket_include.h" +#include "Thread.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup async Asynchronous DNS */ +/** Async DNS resolver thread. + \ingroup async */ +class ResolvServer : public Thread +{ +public: + ResolvServer(port_t); + ~ResolvServer(); + + void Run(); + void Quit(); + + bool Ready(); + +private: + ResolvServer(const ResolvServer& ) {} // copy constructor + ResolvServer& operator=(const ResolvServer& ) { return *this; } // assignment operator + + bool m_quit; + port_t m_port; + bool m_ready; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER +#endif // _SOCKETS_ResolvServer_H + + diff --git a/externals/sockets/include/ResolvSocket.h b/externals/sockets/include/ResolvSocket.h new file mode 100644 index 00000000000..60743736e08 --- /dev/null +++ b/externals/sockets/include/ResolvSocket.h @@ -0,0 +1,105 @@ +/** \file ResolvSocket.h + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ResolvSocket_H +#define _SOCKETS_ResolvSocket_H +#include "sockets-config.h" +#ifdef ENABLE_RESOLVER +#include "TcpSocket.h" +#include <map> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Mutex; + +/** Async DNS resolver socket. + \ingroup async */ +class ResolvSocket : public TcpSocket +{ + typedef std::map<std::string, /* type */ + std::map<std::string, std::string> > cache_t; /* host, result */ + typedef std::map<std::string, /* type */ + std::map<std::string, time_t> > timeout_t; /* host, time */ + +public: + ResolvSocket(ISocketHandler&); + ResolvSocket(ISocketHandler&, Socket *parent, const std::string& host, port_t port, bool ipv6 = false); + ResolvSocket(ISocketHandler&, Socket *parent, ipaddr_t); +#ifdef ENABLE_IPV6 + ResolvSocket(ISocketHandler&, Socket *parent, in6_addr&); +#endif + ~ResolvSocket(); + + void OnAccept() { m_bServer = true; } + void OnLine(const std::string& line); + void OnDetached(); + void OnDelete(); + + void SetId(int x) { m_resolv_id = x; } + int GetId() { return m_resolv_id; } + + void OnConnect(); + +#ifdef ENABLE_IPV6 + void SetResolveIpv6(bool x = true) { m_resolve_ipv6 = x; } +#endif + +private: + ResolvSocket(const ResolvSocket& s) : TcpSocket(s) {} // copy constructor + ResolvSocket& operator=(const ResolvSocket& ) { return *this; } // assignment operator + + std::string m_query; + std::string m_data; + bool m_bServer; + Socket *m_parent; + int m_resolv_id; + std::string m_resolv_host; + port_t m_resolv_port; + ipaddr_t m_resolv_address; +#ifdef ENABLE_IPV6 + bool m_resolve_ipv6; + in6_addr m_resolv_address6; +#endif + static cache_t m_cache; + static timeout_t m_cache_to; + static Mutex m_cache_mutex; + bool m_cached; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER +#endif // _SOCKETS_ResolvSocket_H + + diff --git a/externals/sockets/include/SctpSocket.h b/externals/sockets/include/SctpSocket.h new file mode 100644 index 00000000000..ed507fb1880 --- /dev/null +++ b/externals/sockets/include/SctpSocket.h @@ -0,0 +1,108 @@ +/** + ** \file SctpSocket.h + ** \date 2006-09-04 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SctpSocket_H +#define _SOCKETS_SctpSocket_H +#include "sockets-config.h" + +#include "StreamSocket.h" +#ifdef USE_SCTP +#include <netinet/sctp.h> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define SCTP_BUFSIZE_READ 16400 + +class SocketAddress; + +class SctpSocket : public StreamSocket +{ +public: + /** SctpSocket constructor. + \param h Owner + \param type SCTP_STREAM or SCTP_SEQPACKET */ + SctpSocket(ISocketHandler& h,int type); + ~SctpSocket(); + + /** bind() */ + int Bind(const std::string&,port_t); + int Bind(SocketAddress&); + /** sctp_bindx() */ + int AddAddress(const std::string&,port_t); + int AddAddress(SocketAddress&); + /** sctp_bindx() */ + int RemoveAddress(const std::string&,port_t); + int RemoveAddress(SocketAddress&); + + /** connect() */ + int Open(const std::string&,port_t); + int Open(SocketAddress&); + + /** Connect timeout callback. */ + void OnConnectTimeout(); +#ifdef _WIN32 + /** Connection failed reported as exception on win32 */ + void OnException(); +#endif + +#ifndef SOLARIS + /** sctp_connectx() */ + int AddConnection(const std::string&,port_t); + int AddConnection(SocketAddress&); +#endif + + /** Get peer addresses of an association. */ + int getpaddrs(sctp_assoc_t id,std::list<std::string>&); + /** Get all bound addresses of an association. */ + int getladdrs(sctp_assoc_t id,std::list<std::string>&); + + /** sctp_peeloff */ + int PeelOff(sctp_assoc_t id); + + /** recvmsg callback */ + virtual void OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags) = 0; + + void OnOptions(int,int,int,SOCKET) {} + + virtual int Protocol(); + +protected: + SctpSocket(const SctpSocket& s) : StreamSocket(s) {} + void OnRead(); + void OnWrite(); + +private: + SctpSocket& operator=(const SctpSocket& s) { return *this; } + int m_type; ///< SCTP_STREAM or SCTP_SEQPACKET + char *m_buf; ///< Temporary receive buffer +}; + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE +#endif + +#endif // USE_SCTP +#endif // _SOCKETS_SctpSocket_H + + diff --git a/externals/sockets/include/Socket.h b/externals/sockets/include/Socket.h new file mode 100644 index 00000000000..23a806b5ea1 --- /dev/null +++ b/externals/sockets/include/Socket.h @@ -0,0 +1,735 @@ +/** \file Socket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This software is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Socket_H +#define _SOCKETS_Socket_H +#include "sockets-config.h" + +#include <string> +#include <vector> +#include <list> +#ifdef HAVE_OPENSSL +#include <openssl/ssl.h> +#endif + +#include "socket_include.h" +#include <time.h> +#include "SocketAddress.h" +#include "Thread.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class ISocketHandler; +class SocketAddress; +class IFile; + +/** \defgroup basic Basic sockets */ +/** Socket base class. + \ingroup basic */ +class Socket +{ + friend class ISocketHandler; +#ifdef ENABLE_DETACH + /** Detached socket run thread. + \ingroup internal */ + class SocketThread : public Thread + { + public: + SocketThread(Socket *p); + ~SocketThread(); + + void Run(); + + private: + Socket *GetSocket() const { return m_socket; } + SocketThread(const SocketThread& s) : m_socket(s.GetSocket()) {} + SocketThread& operator=(const SocketThread& ) { return *this; } + Socket *m_socket; + }; +#endif // ENABLE_DETACH + +#ifdef ENABLE_TRIGGERS +public: + /** Data pass class from source to destination. */ + class TriggerData + { + public: + TriggerData() : m_src(NULL) {} + virtual ~TriggerData() {} + + Socket *GetSource() const { return m_src; } + void SetSource(Socket *x) { m_src = x; } + + private: + Socket *m_src; + }; +#endif // ENABLE_TRIGGERS + + /** Socket mode flags. */ +/* + enum { + // Socket + SOCK_DEL = 0x01, ///< Delete by handler flag + SOCK_CLOSE = 0x02, ///< Close and delete flag + SOCK_DISABLE_READ = 0x04, ///< Disable checking for read events + SOCK_CONNECTED = 0x08, ///< Socket is connected (tcp/udp) + + SOCK_ERASED_BY_HANDLER = 0x10, ///< Set by handler before delete + // HAVE_OPENSSL + SOCK_ENABLE_SSL = 0x20, ///< Enable SSL for this TcpSocket + SOCK_SSL = 0x40, ///< ssl negotiation mode (TcpSocket) + SOCK_SSL_SERVER = 0x80, ///< True if this is an incoming ssl TcpSocket connection + + // ENABLE_IPV6 + SOCK_IPV6 = 0x0100, ///< This is an ipv6 socket if this one is true + // ENABLE_POOL + SOCK_CLIENT = 0x0200, ///< only client connections are pooled + SOCK_RETAIN = 0x0400, ///< keep connection on close + SOCK_LOST = 0x0800, ///< connection lost + + // ENABLE_SOCKS4 + SOCK_SOCKS4 = 0x1000, ///< socks4 negotiation mode (TcpSocket) + // ENABLE_DETACH + SOCK_DETACH = 0x2000, ///< Socket ordered to detach flag + SOCK_DETACHED = 0x4000, ///< Socket has been detached + // StreamSocket + STREAMSOCK_CONNECTING = 0x8000, ///< Flag indicating connection in progress + + STREAMSOCK_FLUSH_BEFORE_CLOSE = 0x010000L, ///< Send all data before closing (default true) + STREAMSOCK_CALL_ON_CONNECT = 0x020000L, ///< OnConnect will be called next ISocketHandler cycle if true + STREAMSOCK_RETRY_CONNECT = 0x040000L, ///< Try another connection attempt next ISocketHandler cycle + STREAMSOCK_LINE_PROTOCOL = 0x080000L, ///< Line protocol mode flag + + }; +*/ + +public: + /** "Default" constructor */ + Socket(ISocketHandler&); + + virtual ~Socket(); + + /** Socket class instantiation method. Used when a "non-standard" constructor + * needs to be used for the socket class. Note: the socket class still needs + * the "default" constructor with one ISocketHandler& as input parameter. + */ + virtual Socket *Create() { return NULL; } + + /** Returns reference to sockethandler that owns the socket. + If the socket is detached, this is a reference to the slave sockethandler. + */ + ISocketHandler& Handler() const; + + /** Returns reference to sockethandler that owns the socket. + This one always returns the reference to the original sockethandler, + even if the socket is detached. + */ + ISocketHandler& MasterHandler() const; + + /** Called by ListenSocket after accept but before socket is added to handler. + * CTcpSocket uses this to create its ICrypt member variable. + * The ICrypt member variable is created by a virtual method, therefore + * it can't be called directly from the CTcpSocket constructor. + * Also used to determine if incoming HTTP connection is normal (port 80) + * or ssl (port 443). + */ + virtual void Init(); + + /** Create a socket file descriptor. + \param af Address family AF_INET / AF_INET6 / ... + \param type SOCK_STREAM / SOCK_DGRAM / ... + \param protocol "tcp" / "udp" / ... */ + SOCKET CreateSocket(int af,int type,const std::string& protocol = ""); + + /** Assign this socket a file descriptor created + by a call to socket() or otherwise. */ + void Attach(SOCKET s); + + /** Return file descriptor assigned to this socket. */ + SOCKET GetSocket(); + + /** Close connection immediately - internal use. + \sa SetCloseAndDelete */ + virtual int Close(); + + /** Add file descriptor to sockethandler fd_set's. */ + void Set(bool bRead,bool bWrite,bool bException = true); + + /** Returns true when socket file descriptor is valid + and socket is not about to be closed. */ + virtual bool Ready(); + + /** Returns pointer to ListenSocket that created this instance + * on an incoming connection. */ + Socket *GetParent(); + + /** Used by ListenSocket to set parent pointer of newly created + * socket instance. */ + void SetParent(Socket *); + + /** Get listening port from ListenSocket<>. */ + virtual port_t GetPort(); + + /** Set socket non-block operation. */ + bool SetNonblocking(bool); + + /** Set socket non-block operation. */ + bool SetNonblocking(bool, SOCKET); + + /** Total lifetime of instance. */ + time_t Uptime(); + + /** Set address/port of last connect() call. */ + void SetClientRemoteAddress(SocketAddress&); + + /** Get address/port of last connect() call. */ + std::auto_ptr<SocketAddress> GetClientRemoteAddress(); + + /** Common interface for SendBuf used by Tcp and Udp sockets. */ + virtual void SendBuf(const char *,size_t,int = 0); + + /** Common interface for Send used by Tcp and Udp sockets. */ + virtual void Send(const std::string&,int = 0); + + /** Outgoing traffic counter. */ + virtual uint64_t GetBytesSent(bool clear = false); + + /** Incoming traffic counter. */ + virtual uint64_t GetBytesReceived(bool clear = false); + + // LIST_TIMEOUT + + /** Enable timeout control. 0=disable timeout check. */ + void SetTimeout(time_t secs); + + /** Check timeout. \return true if time limit reached */ + bool Timeout(time_t tnow); + + /** Used by ListenSocket. ipv4 and ipv6 */ + void SetRemoteAddress(SocketAddress&); + + /** \name Event callbacks */ + //@{ + + /** Called when there is something to be read from the file descriptor. */ + virtual void OnRead(); + /** Called when there is room for another write on the file descriptor. */ + virtual void OnWrite(); + /** Called on socket exception. */ + virtual void OnException(); + /** Called before a socket class is deleted by the ISocketHandler. */ + virtual void OnDelete(); + /** Called when a connection has completed. */ + virtual void OnConnect(); + /** Called when an incoming connection has been completed. */ + virtual void OnAccept(); + /** Called when a complete line has been read and the socket is in + * line protocol mode. */ + virtual void OnLine(const std::string& ); + /** Called on connect timeout (5s). */ + virtual void OnConnectFailed(); + /** Called when a client socket is created, to set socket options. + \param family AF_INET, AF_INET6, etc + \param type SOCK_STREAM, SOCK_DGRAM, etc + \param protocol Protocol number (tcp, udp, sctp, etc) + \param s Socket file descriptor + */ + virtual void OnOptions(int family,int type,int protocol,SOCKET s) = 0; + /** Connection retry callback - return false to abort connection attempts */ + virtual bool OnConnectRetry(); +#ifdef ENABLE_RECONNECT + /** a reconnect has been made */ + virtual void OnReconnect(); +#endif + /** TcpSocket: When a disconnect has been detected (recv/SSL_read returns 0 bytes). */ + virtual void OnDisconnect(); + /** Timeout callback. */ + virtual void OnTimeout(); + /** Connection timeout. */ + virtual void OnConnectTimeout(); + //@} + + /** \name Socket mode flags, set/reset */ + //@{ + /** Set delete by handler true when you want the sockethandler to + delete the socket instance after use. */ + void SetDeleteByHandler(bool = true); + /** Check delete by handler flag. + \return true if this instance should be deleted by the sockethandler */ + bool DeleteByHandler(); + + // LIST_CLOSE - conditional event queue + + /** Set close and delete to terminate the connection. */ + void SetCloseAndDelete(bool = true); + /** Check close and delete flag. + \return true if this socket should be closed and the instance removed */ + bool CloseAndDelete(); + + /** Return number of seconds since socket was ordered to close. \sa SetCloseAndDelete */ + time_t TimeSinceClose(); + + /** Ignore read events for an output only socket. */ + void DisableRead(bool x = true); + /** Check ignore read events flag. + \return true if read events should be ignored */ + bool IsDisableRead(); + + /** Set connected status. */ + void SetConnected(bool = true); + /** Check connected status. + \return true if connected */ + bool IsConnected(); + + /** Connection lost - error while reading/writing from a socket - TcpSocket only. */ + void SetLost(); + /** Check connection lost status flag, used by TcpSocket only. + \return true if there was an error while r/w causing the socket to close */ + bool Lost(); + + /** Set flag indicating the socket is being actively deleted by the sockethandler. */ + void SetErasedByHandler(bool x = true); + /** Get value of flag indicating socket is deleted by sockethandler. */ + bool ErasedByHandler(); + + //@} + + /** \name Information about remote connection */ + //@{ + /** Returns address of remote end. */ + std::auto_ptr<SocketAddress> GetRemoteSocketAddress(); + /** Returns address of remote end: ipv4. */ + ipaddr_t GetRemoteIP4(); +#ifdef ENABLE_IPV6 + /** Returns address of remote end: ipv6. */ +#ifdef IPPROTO_IPV6 + struct in6_addr GetRemoteIP6(); +#endif +#endif + /** Returns remote port number: ipv4 and ipv6. */ + port_t GetRemotePort(); + /** Returns remote ip as string? ipv4 and ipv6. */ + std::string GetRemoteAddress(); + /** ipv4 and ipv6(not implemented) */ + std::string GetRemoteHostname(); + //@} + + /** Returns local port number for bound socket file descriptor. */ + port_t GetSockPort(); + /** Returns local ipv4 address for bound socket file descriptor. */ + ipaddr_t GetSockIP4(); + /** Returns local ipv4 address as text for bound socket file descriptor. */ + std::string GetSockAddress(); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Returns local ipv6 address for bound socket file descriptor. */ + struct in6_addr GetSockIP6(); + /** Returns local ipv6 address as text for bound socket file descriptor. */ + std::string GetSockAddress6(); +#endif +#endif + // -------------------------------------------------------------------------- + /** @name IP options + When an ip or socket option is available on all of the operating systems + I'm testing on (linux 2.4.x, _win32, macosx, solaris9 intel) they are not + checked with an #ifdef below. + This might cause a compile error on other operating systems. */ + // -------------------------------------------------------------------------- + + // IP options + //@{ + + bool SetIpOptions(const void *p, socklen_t len); + bool SetIpTOS(unsigned char tos); + unsigned char IpTOS(); + bool SetIpTTL(int ttl); + int IpTTL(); + bool SetIpHdrincl(bool x = true); + bool SetIpMulticastTTL(int); + int IpMulticastTTL(); + bool SetMulticastLoop(bool x = true); + bool IpAddMembership(struct ip_mreq&); + bool IpDropMembership(struct ip_mreq&); + +#ifdef IP_PKTINFO + bool SetIpPktinfo(bool x = true); +#endif +#ifdef IP_RECVTOS + bool SetIpRecvTOS(bool x = true); +#endif +#ifdef IP_RECVTTL + bool SetIpRecvTTL(bool x = true); +#endif +#ifdef IP_RECVOPTS + bool SetIpRecvopts(bool x = true); +#endif +#ifdef IP_RETOPTS + bool SetIpRetopts(bool x = true); +#endif +#ifdef IP_RECVERR + bool SetIpRecverr(bool x = true); +#endif +#ifdef IP_MTU_DISCOVER + bool SetIpMtudiscover(bool x = true); +#endif +#ifdef IP_MTU + int IpMtu(); +#endif +#ifdef IP_ROUTER_ALERT + bool SetIpRouterAlert(bool x = true); +#endif +#ifdef LINUX + bool IpAddMembership(struct ip_mreqn&); +#endif +#ifdef LINUX + bool IpDropMembership(struct ip_mreqn&); +#endif + //@} + + // SOCKET options + /** @name Socket Options */ + //@{ + + bool SoAcceptconn(); + bool SetSoBroadcast(bool x = true); + bool SetSoDebug(bool x = true); + int SoError(); + bool SetSoDontroute(bool x = true); + bool SetSoLinger(int onoff, int linger); + bool SetSoOobinline(bool x = true); + bool SetSoRcvlowat(int); + bool SetSoSndlowat(int); + bool SetSoRcvtimeo(struct timeval&); + bool SetSoSndtimeo(struct timeval&); + bool SetSoRcvbuf(int); + int SoRcvbuf(); + bool SetSoSndbuf(int); + int SoSndbuf(); + int SoType(); + bool SetSoReuseaddr(bool x = true); + bool SetSoKeepalive(bool x = true); + +#ifdef SO_BSDCOMPAT + bool SetSoBsdcompat(bool x = true); +#endif +#ifdef SO_BINDTODEVICE + bool SetSoBindtodevice(const std::string& intf); +#endif +#ifdef SO_PASSCRED + bool SetSoPasscred(bool x = true); +#endif +#ifdef SO_PEERCRED + bool SoPeercred(struct ucred& ); +#endif +#ifdef SO_PRIORITY + bool SetSoPriority(int); +#endif +#ifdef SO_RCVBUFFORCE + bool SetSoRcvbufforce(int); +#endif +#ifdef SO_SNDBUFFORCE + bool SetSoSndbufforce(int); +#endif +#ifdef SO_TIMESTAMP + bool SetSoTimestamp(bool x = true); +#endif +#ifdef SO_NOSIGPIPE + bool SetSoNosigpipe(bool x = true); +#endif + //@} + + // TCP options in TcpSocket.h/TcpSocket.cpp + +#ifdef HAVE_OPENSSL + /** @name SSL Support */ + //@{ + /** SSL client/server support - internal use. \sa TcpSocket */ + virtual void OnSSLConnect(); + /** SSL client/server support - internal use. \sa TcpSocket */ + virtual void OnSSLAccept(); + /** SSL negotiation failed for client connect. */ + virtual void OnSSLConnectFailed(); + /** SSL negotiation failed for server accept. */ + virtual void OnSSLAcceptFailed(); + /** new SSL support */ + virtual bool SSLNegotiate(); + /** Check if SSL is Enabled for this TcpSocket. + \return true if this is a TcpSocket with SSL enabled */ + bool IsSSL(); + /** Enable SSL operation for a TcpSocket. */ + void EnableSSL(bool x = true); + /** Still negotiating ssl connection. + \return true if ssl negotiating is still in progress */ + bool IsSSLNegotiate(); + /** Set flag indicating ssl handshaking still in progress. */ + void SetSSLNegotiate(bool x = true); + /** OnAccept called with SSL Enabled. + \return true if this is a TcpSocket with an incoming SSL connection */ + bool IsSSLServer(); + /** Set flag indicating that this is a TcpSocket with incoming SSL connection. */ + void SetSSLServer(bool x = true); + /** SSL; Get pointer to ssl context structure. */ + virtual SSL_CTX *GetSslContext() { return NULL; } + /** SSL; Get pointer to ssl structure. */ + virtual SSL *GetSsl() { return NULL; } + //@} +#endif // HAVE_OPENSSL + +#ifdef ENABLE_IPV6 + /** Enable ipv6 for this socket. */ + void SetIpv6(bool x = true); + /** Check ipv6 socket. + \return true if this is an ipv6 socket */ + bool IsIpv6(); +#endif + +#ifdef ENABLE_POOL + /** @name Connection Pool */ + //@{ + /** Client = connecting TcpSocket. */ + void SetIsClient(); + /** Socket type from socket() call. */ + void SetSocketType(int x); + /** Socket type from socket() call. */ + int GetSocketType(); + /** Protocol type from socket() call. */ + void SetSocketProtocol(const std::string& x); + /** Protocol type from socket() call. */ + const std::string& GetSocketProtocol(); + /** Instruct a client socket to stay open in the connection pool after use. + If you have connected to a server using tcp, you can call SetRetain + to leave the connection open after your socket instance has been deleted. + The next connection you make to the same server will reuse the already + opened connection, if it is still available. + */ + void SetRetain(); + /** Check retain flag. + \return true if the socket should be moved to connection pool after use */ + bool Retain(); + /** Copy connection parameters from sock. */ + void CopyConnection(Socket *sock); + //@} +#endif // ENABLE_POOL + +#ifdef ENABLE_SOCKS4 + /** \name Socks4 support */ + //@{ + /** Socks4 client support internal use. \sa TcpSocket */ + virtual void OnSocks4Connect(); + /** Socks4 client support internal use. \sa TcpSocket */ + virtual void OnSocks4ConnectFailed(); + /** Socks4 client support internal use. \sa TcpSocket */ + virtual bool OnSocks4Read(); + /** Called when the last write caused the tcp output buffer to + * become empty. */ + /** socket still in socks4 negotiation mode */ + bool Socks4(); + /** Set flag indicating Socks4 handshaking in progress */ + void SetSocks4(bool x = true); + + /** Set socks4 server host address to use */ + void SetSocks4Host(ipaddr_t a); + /** Set socks4 server hostname to use. */ + void SetSocks4Host(const std::string& ); + /** Socks4 server port to use. */ + void SetSocks4Port(port_t p); + /** Provide a socks4 userid if required by the socks4 server. */ + void SetSocks4Userid(const std::string& x); + /** Get the ip address of socks4 server to use. + \return socks4 server host address */ + ipaddr_t GetSocks4Host(); + /** Get the socks4 server port to use. + \return socks4 server port */ + port_t GetSocks4Port(); + /** Get socks4 userid. + \return Socks4 userid */ + const std::string& GetSocks4Userid(); + //@} +#endif // ENABLE_SOCKS4 + +#ifdef ENABLE_RESOLVER + /** \name Asynchronous Resolver */ + //@{ + /** Request an asynchronous dns resolution. + \param host hostname to be resolved + \param port port number passed along for the ride + \return Resolve ID */ + int Resolve(const std::string& host,port_t port = 0); +#ifdef ENABLE_IPV6 + int Resolve6(const std::string& host, port_t port = 0); +#endif + /** Callback returning a resolved address. + \param id Resolve ID from Resolve call + \param a resolved ip address + \param port port number passed to Resolve */ + virtual void OnResolved(int id,ipaddr_t a,port_t port); +#ifdef ENABLE_IPV6 + virtual void OnResolved(int id,in6_addr& a,port_t port); +#endif + /** Request asynchronous reverse dns lookup. + \param a in_addr to be translated */ + int Resolve(ipaddr_t a); +#ifdef ENABLE_IPV6 + int Resolve(in6_addr& a); +#endif + /** Callback returning reverse resolve results. + \param id Resolve ID + \param name Resolved hostname */ + virtual void OnReverseResolved(int id,const std::string& name); + /** Callback indicating failed dns lookup. + \param id Resolve ID */ + virtual void OnResolveFailed(int id); + //@} +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_DETACH + /** \name Thread Support */ + //@{ + /** Callback fires when a new socket thread has started and this + socket is ready for operation again. + \sa ResolvSocket */ + virtual void OnDetached(); + + // LIST_DETACH + + /** Internal use. */ + void SetDetach(bool x = true); + /** Check detach flag. + \return true if the socket should detach to its own thread */ + bool IsDetach(); + + /** Internal use. */ + void SetDetached(bool x = true); + /** Check detached flag. + \return true if the socket runs in its own thread. */ + const bool IsDetached() const; + /** Order this socket to start its own thread and call OnDetached + when ready for operation. */ + bool Detach(); + /** Store the slave sockethandler pointer. */ + void SetSlaveHandler(ISocketHandler *); + /** Create new thread for this socket to run detached in. */ + void DetachSocket(); + //@} +#endif // ENABLE_DETACH + + /** Write traffic to an IFile. Socket will not delete this object. */ + void SetTrafficMonitor(IFile *p) { m_traffic_monitor = p; } + +#ifdef ENABLE_TRIGGERS + /** \name Triggers */ + //@{ + /** Subscribe to trigger id. */ + void Subscribe(int id); + /** Unsubscribe from trigger id. */ + void Unsubscribe(int id); + /** Trigger callback, with data passed from source to destination. */ + virtual void OnTrigger(int id, const TriggerData& data); + /** Trigger cancelled because source has been deleted (as in delete). */ + virtual void OnCancelled(int id); + //@} +#endif + +protected: + /** default constructor not available */ + Socket() : m_handler(m_handler) {} + /** copy constructor not available */ + Socket(const Socket& s) : m_handler(s.m_handler) {} + + /** assignment operator not available. */ + Socket& operator=(const Socket& ) { return *this; } + + /** All traffic will be written to this IFile, if set. */ + IFile *GetTrafficMonitor() { return m_traffic_monitor; } + +// unsigned long m_flags; ///< boolean flags, replacing old 'bool' members + +private: + ISocketHandler& m_handler; ///< Reference of ISocketHandler in control of this socket + SOCKET m_socket; ///< File descriptor + bool m_bDel; ///< Delete by handler flag + bool m_bClose; ///< Close and delete flag + time_t m_tCreate; ///< Time in seconds when this socket was created + Socket *m_parent; ///< Pointer to ListenSocket class, valid for incoming sockets + bool m_b_disable_read; ///< Disable checking for read events + bool m_connected; ///< Socket is connected (tcp/udp) + bool m_b_erased_by_handler; ///< Set by handler before delete + time_t m_tClose; ///< Time in seconds when ordered to close + std::auto_ptr<SocketAddress> m_client_remote_address; ///< Address of last connect() + std::auto_ptr<SocketAddress> m_remote_address; ///< Remote end address + IFile *m_traffic_monitor; + time_t m_timeout_start; ///< Set by SetTimeout + time_t m_timeout_limit; ///< Defined by SetTimeout + bool m_bLost; ///< connection lost + +#ifdef _WIN32 +static WSAInitializer m_winsock_init; ///< Winsock initialization singleton class +#endif + +#ifdef HAVE_OPENSSL + bool m_b_enable_ssl; ///< Enable SSL for this TcpSocket + bool m_b_ssl; ///< ssl negotiation mode (TcpSocket) + bool m_b_ssl_server; ///< True if this is an incoming ssl TcpSocket connection +#endif + +#ifdef ENABLE_IPV6 + bool m_ipv6; ///< This is an ipv6 socket if this one is true +#endif + +#ifdef ENABLE_POOL + int m_socket_type; ///< Type of socket, from socket() call + std::string m_socket_protocol; ///< Protocol, from socket() call + bool m_bClient; ///< only client connections are pooled + bool m_bRetain; ///< keep connection on close +#endif + +#ifdef ENABLE_SOCKS4 + bool m_bSocks4; ///< socks4 negotiation mode (TcpSocket) + ipaddr_t m_socks4_host; ///< socks4 server address + port_t m_socks4_port; ///< socks4 server port number + std::string m_socks4_userid; ///< socks4 server usedid +#endif + +#ifdef ENABLE_DETACH + bool m_detach; ///< Socket ordered to detach flag + bool m_detached; ///< Socket has been detached + SocketThread *m_pThread; ///< Detach socket thread class pointer + ISocketHandler *m_slave_handler; ///< Actual sockethandler while detached +#endif +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Socket_H + + diff --git a/externals/sockets/include/SocketAddress.h b/externals/sockets/include/SocketAddress.h new file mode 100644 index 00000000000..abdbbfd2cf6 --- /dev/null +++ b/externals/sockets/include/SocketAddress.h @@ -0,0 +1,93 @@ +/** + ** \file SocketAddress.h + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SocketAddress_H +#define _SOCKETS_SocketAddress_H + +#include "sockets-config.h" +#include <string> +#include <memory> +#include "socket_include.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** + This class and its subclasses is intended to be used as replacement + for the internal data type 'ipaddr_t' and various implementations of + IPv6 addressing found throughout the library. + 'ipaddr_t' is an IPv4 address in network byte order. + 'port_t' is the portnumber in host byte order. + 'struct in6_addr' is an IPv6 address. + 'struct in_addr' is an IPv4 address. + \ingroup basic +*/ +class SocketAddress +{ +public: + virtual ~SocketAddress() {} + + /** Get a pointer to the address struct. */ + virtual operator struct sockaddr *() = 0; + + /** Get length of address struct. */ + virtual operator socklen_t() = 0; + + /** Compare two addresses. */ + virtual bool operator==(SocketAddress&) = 0; + + /** Set port number. + \param port Port number in host byte order */ + virtual void SetPort(port_t port) = 0; + + /** Get port number. + \return Port number in host byte order. */ + virtual port_t GetPort() = 0; + + /** Set socket address. + \param sa Pointer to either 'struct sockaddr_in' or 'struct sockaddr_in6'. */ + virtual void SetAddress(struct sockaddr *sa) = 0; + + /** Convert address to text. */ + virtual std::string Convert(bool include_port) = 0; + + /** Reverse lookup of address. */ + virtual std::string Reverse() = 0; + + /** Get address family. */ + virtual int GetFamily() = 0; + + /** Address structure is valid. */ + virtual bool IsValid() = 0; + + /** Get a copy of this SocketAddress object. */ + virtual std::auto_ptr<SocketAddress> GetCopy() = 0; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // _SOCKETS_SocketAddress_H + + diff --git a/externals/sockets/include/SocketHandler.h b/externals/sockets/include/SocketHandler.h new file mode 100644 index 00000000000..5598ec4249b --- /dev/null +++ b/externals/sockets/include/SocketHandler.h @@ -0,0 +1,265 @@ +/** \file SocketHandler.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SocketHandler_H +#define _SOCKETS_SocketHandler_H + +#include "sockets-config.h" +#include <map> +#include <list> + +#include "socket_include.h" +#include "ISocketHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Socket; +#ifdef ENABLE_RESOLVER +class ResolvServer; +#endif +class Mutex; + +/** Socket container class, event generator. + \ingroup basic */ +class SocketHandler : public ISocketHandler +{ +protected: + /** Map type for holding file descriptors/socket object pointers. */ + typedef std::map<SOCKET,Socket *> socket_m; + +public: + /** SocketHandler constructor. + \param log Optional log class pointer */ + SocketHandler(StdLog *log = NULL); + + /** SocketHandler threadsafe constructor. + \param mutex Externally declared mutex variable + \param log Optional log class pointer */ + SocketHandler(Mutex& mutex,StdLog *log = NULL); + + ~SocketHandler(); + + /** Get mutex reference for threadsafe operations. */ + Mutex& GetMutex() const; + + /** Register StdLog object for error callback. + \param log Pointer to log class */ + void RegStdLog(StdLog *log); + + /** Log error to log class for print out / storage. */ + void LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t = LOG_LEVEL_WARNING); + + /** Add socket instance to socket map. Removal is always automatic. */ + void Add(Socket *); + + /** Get status of read/write/exception file descriptor set for a socket. */ + void Get(SOCKET s,bool& r,bool& w,bool& e); + + /** Set read/write/exception file descriptor sets (fd_set). */ + void Set(SOCKET s,bool bRead,bool bWrite,bool bException = true); + + /** Wait for events, generate callbacks. */ + int Select(long sec,long usec); + + /** This method will not return until an event has been detected. */ + int Select(); + + /** Wait for events, generate callbacks. */ + int Select(struct timeval *tsel); + + /** Check that a socket really is handled by this socket handler. */ + bool Valid(Socket *); + + /** Return number of sockets handled by this handler. */ + size_t GetCount(); + + /** Override and return false to deny all incoming connections. + \param p ListenSocket class pointer (use GetPort to identify which one) */ + bool OkToAccept(Socket *p); + + /** Called by Socket when a socket changes state. */ + void AddList(SOCKET s,list_t which_one,bool add); + + // Connection pool +#ifdef ENABLE_POOL + /** Find available open connection (used by connection pool). */ + ISocketHandler::PoolSocket *FindConnection(int type,const std::string& protocol,SocketAddress&); + /** Enable connection pool (by default disabled). */ + void EnablePool(bool x = true); + /** Check pool status. + \return true if connection pool is enabled */ + bool PoolEnabled(); +#endif // ENABLE_POOL + + // Socks4 +#ifdef ENABLE_SOCKS4 + /** Set socks4 server ip that all new tcp sockets should use. */ + void SetSocks4Host(ipaddr_t); + /** Set socks4 server hostname that all new tcp sockets should use. */ + void SetSocks4Host(const std::string& ); + /** Set socks4 server port number that all new tcp sockets should use. */ + void SetSocks4Port(port_t); + /** Set optional socks4 userid. */ + void SetSocks4Userid(const std::string& ); + /** If connection to socks4 server fails, immediately try direct connection to final host. */ + void SetSocks4TryDirect(bool x = true); + /** Get socks4 server ip. + \return socks4 server ip */ + ipaddr_t GetSocks4Host(); + /** Get socks4 port number. + \return socks4 port number */ + port_t GetSocks4Port(); + /** Get socks4 userid (optional). + \return socks4 userid */ + const std::string& GetSocks4Userid(); + /** Check status of socks4 try direct flag. + \return true if direct connection should be tried if connection to socks4 server fails */ + bool Socks4TryDirect(); +#endif // ENABLE_SOCKS4 + + // DNS resolve server +#ifdef ENABLE_RESOLVER + /** Enable asynchronous DNS. + \param port Listen port of asynchronous dns server */ + void EnableResolver(port_t port = 16667); + /** Check resolver status. + \return true if resolver is enabled */ + bool ResolverEnabled(); + /** Queue a dns request. + \param host Hostname to be resolved + \param port Port number will be echoed in Socket::OnResolved callback */ + int Resolve(Socket *,const std::string& host,port_t port); +#ifdef ENABLE_IPV6 + int Resolve6(Socket *,const std::string& host,port_t port); +#endif + /** Do a reverse dns lookup. */ + int Resolve(Socket *,ipaddr_t a); +#ifdef ENABLE_IPV6 + int Resolve(Socket *,in6_addr& a); +#endif + /** Get listen port of asynchronous dns server. */ + port_t GetResolverPort(); + /** Resolver thread ready for queries. */ + bool ResolverReady(); + /** Returns true if the socket is waiting for a resolve event. */ + bool Resolving(Socket *); +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_TRIGGERS + /** Fetch unique trigger id. */ + int TriggerID(Socket *src); + /** Subscribe socket to trigger id. */ + bool Subscribe(int id, Socket *dst); + /** Unsubscribe socket from trigger id. */ + bool Unsubscribe(int id, Socket *dst); + /** Execute OnTrigger for subscribed sockets. + \param id Trigger ID + \param data Data passed from source to destination + \param erase Empty trigger id source and destination maps if 'true', + Leave them in place if 'false' - if a trigger should be called many times */ + void Trigger(int id, Socket::TriggerData& data, bool erase = true); +#endif // ENABLE_TRIGGERS + +#ifdef ENABLE_DETACH + /** Indicates that the handler runs under SocketThread. */ + void SetSlave(bool x = true); + /** Indicates that the handler runs under SocketThread. */ + bool IsSlave(); +#endif + + /** Sanity check of those accursed lists. */ + void CheckSanity(); + +protected: + socket_m m_sockets; ///< Active sockets map + socket_m m_add; ///< Sockets to be added to sockets map + std::list<Socket *> m_delete; ///< Sockets to be deleted (failed when Add) + +protected: + StdLog *m_stdlog; ///< Registered log class, or NULL + Mutex& m_mutex; ///< Thread safety mutex + bool m_b_use_mutex; ///< Mutex correctly initialized + +private: + void CheckList(socket_v&,const std::string&); ///< Used by CheckSanity + /** Remove socket from socket map, used by Socket class. */ + void Remove(Socket *); + SOCKET m_maxsock; ///< Highest file descriptor + 1 in active sockets list + fd_set m_rfds; ///< file descriptor set monitored for read events + fd_set m_wfds; ///< file descriptor set monitored for write events + fd_set m_efds; ///< file descriptor set monitored for exceptions + int m_preverror; ///< debug select() error + int m_errcnt; ///< debug select() error + time_t m_tlast; ///< timeout control + + // state lists + socket_v m_fds; ///< Active file descriptor list + socket_v m_fds_erase; ///< File descriptors that are to be erased from m_sockets + socket_v m_fds_callonconnect; ///< checklist CallOnConnect +#ifdef ENABLE_DETACH + socket_v m_fds_detach; ///< checklist Detach +#endif + socket_v m_fds_timeout; ///< checklist timeout + socket_v m_fds_retry; ///< checklist retry client connect + socket_v m_fds_close; ///< checklist close and delete + +#ifdef ENABLE_SOCKS4 + ipaddr_t m_socks4_host; ///< Socks4 server host ip + port_t m_socks4_port; ///< Socks4 server port number + std::string m_socks4_userid; ///< Socks4 userid + bool m_bTryDirect; ///< Try direct connection if socks4 server fails +#endif +#ifdef ENABLE_RESOLVER + int m_resolv_id; ///< Resolver id counter + ResolvServer *m_resolver; ///< Resolver thread pointer + port_t m_resolver_port; ///< Resolver listen port + std::map<Socket *, bool> m_resolve_q; ///< resolve queue +#endif +#ifdef ENABLE_POOL + bool m_b_enable_pool; ///< Connection pool enabled if true +#endif +#ifdef ENABLE_TRIGGERS + int m_next_trigger_id; ///< Unique trigger id counter + std::map<int, Socket *> m_trigger_src; ///< mapping trigger id to source socket + std::map<int, std::map<Socket *, bool> > m_trigger_dst; ///< mapping trigger id to destination sockets +#endif +#ifdef ENABLE_DETACH + bool m_slave; ///< Indicates that this is a ISocketHandler run in SocketThread +#endif +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_SocketHandler_H + + diff --git a/externals/sockets/include/StdLog.h b/externals/sockets/include/StdLog.h new file mode 100644 index 00000000000..3ff68d6e9ea --- /dev/null +++ b/externals/sockets/include/StdLog.h @@ -0,0 +1,73 @@ +/** \file StdLog.h + ** \date 2004-06-01 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_StdLog_H +#define _SOCKETS_StdLog_H + +#include "sockets-config.h" +#include <string> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** error level enum. */ +typedef enum +{ + LOG_LEVEL_WARNING = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_FATAL, + LOG_LEVEL_INFO +} loglevel_t; + +class ISocketHandler; +class Socket; + +/** \defgroup logging Log help classes */ +/** Log class interface. + \ingroup logging */ +class StdLog +{ +public: + virtual ~StdLog() {} + + virtual void error(ISocketHandler *,Socket *, + const std::string& user_text, + int err, + const std::string& sys_err, + loglevel_t = LOG_LEVEL_WARNING) = 0; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_StdLog_H + + diff --git a/externals/sockets/include/StdoutLog.h b/externals/sockets/include/StdoutLog.h new file mode 100644 index 00000000000..aeb25b3e6e6 --- /dev/null +++ b/externals/sockets/include/StdoutLog.h @@ -0,0 +1,55 @@ +/** \file StdoutLog.h + ** \date 2004-06-01 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_StdoutLog_H +#define _SOCKETS_StdoutLog_H + +#include "sockets-config.h" +#include "StdLog.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** StdLog implementation, logs to stdout. + \ingroup logging */ +class StdoutLog : public StdLog +{ +public: + void error(ISocketHandler *,Socket *,const std::string& call,int err,const std::string& sys_err,loglevel_t); +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_StdoutLog_H + + diff --git a/externals/sockets/include/StreamSocket.h b/externals/sockets/include/StreamSocket.h new file mode 100644 index 00000000000..bcce10ffbc5 --- /dev/null +++ b/externals/sockets/include/StreamSocket.h @@ -0,0 +1,124 @@ +#ifndef _StreamSocket_H +#define _StreamSocket_H + +#include "Socket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** SOCK_STREAM Socket base class. + \ingroup basic */ +class StreamSocket : public Socket +{ +public: + StreamSocket(ISocketHandler& ); + ~StreamSocket(); + + /** Socket should Check Connect on next write event from select(). */ + void SetConnecting(bool = true); + + /** Check connecting flag. + \return true if the socket is still trying to connect */ + bool Connecting(); + + /** Returns true when socket file descriptor is valid, + socket connection is established, and socket is not about to + be closed. */ + bool Ready(); + + /** Set timeout to use for connection attempt. + \param x Timeout in seconds */ + void SetConnectTimeout(int x); + + /** Return number of seconds to wait for a connection. + \return Connection timeout (seconds) */ + int GetConnectTimeout(); + + /** Set flush before close to make a tcp socket completely empty its + output buffer before closing the connection. */ + void SetFlushBeforeClose(bool = true); + + /** Check flush before status. + \return true if the socket should send all data before closing */ + bool GetFlushBeforeClose(); + + /** Define number of connection retries (tcp only). + n = 0 - no retry + n > 0 - number of retries + n = -1 - unlimited retries */ + void SetConnectionRetry(int n); + + /** Get number of maximum connection retries (tcp only). */ + int GetConnectionRetry(); + + /** Increase number of actual connection retries (tcp only). */ + void IncreaseConnectionRetries(); + + /** Get number of actual connection retries (tcp only). */ + int GetConnectionRetries(); + + /** Reset actual connection retries (tcp only). */ + void ResetConnectionRetries(); + + // LIST_CALLONCONNECT + + /** Instruct socket to call OnConnect callback next sockethandler cycle. */ + void SetCallOnConnect(bool x = true); + + /** Check call on connect flag. + \return true if OnConnect() should be called a.s.a.p */ + bool CallOnConnect(); + + // LIST_RETRY + + /** Set flag to initiate a connection attempt after a connection timeout. */ + void SetRetryClientConnect(bool x = true); + + /** Check if a connection attempt should be made. + \return true when another attempt should be made */ + bool RetryClientConnect(); + + /** Called after OnRead if socket is in line protocol mode. + \sa SetLineProtocol */ + /** Enable the OnLine callback. Do not create your own OnRead + * callback when using this. */ + virtual void SetLineProtocol(bool = true); + + /** Check line protocol mode. + \return true if socket is in line protocol mode */ + bool LineProtocol(); + + /** Set shutdown status. */ + void SetShutdown(int); + + /** Get shutdown status. */ + int GetShutdown(); + + /** Returns IPPROTO_TCP or IPPROTO_SCTP */ + virtual int Protocol() = 0; + +protected: + StreamSocket(const StreamSocket& ) {} // copy constructor + +private: + StreamSocket& operator=(const StreamSocket& ) { return *this; } // assignment operator + + bool m_bConnecting; ///< Flag indicating connection in progress + int m_connect_timeout; ///< Connection timeout (seconds) + bool m_flush_before_close; ///< Send all data before closing (default true) + int m_connection_retry; ///< Maximum connection retries (tcp) + int m_retries; ///< Actual number of connection retries (tcp) + bool m_call_on_connect; ///< OnConnect will be called next ISocketHandler cycle if true + bool m_b_retry_connect; ///< Try another connection attempt next ISocketHandler cycle + bool m_line_protocol; ///< Line protocol mode flag + int m_shutdown; ///< Shutdown status +}; + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _StreamSocket_H + + diff --git a/externals/sockets/include/TcpSocket.h b/externals/sockets/include/TcpSocket.h new file mode 100644 index 00000000000..de1be8bd8b9 --- /dev/null +++ b/externals/sockets/include/TcpSocket.h @@ -0,0 +1,356 @@ +/** \file TcpSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_TcpSocket_H +#define _SOCKETS_TcpSocket_H +#include "sockets-config.h" +#include "StreamSocket.h" +#ifdef HAVE_OPENSSL +#include <openssl/ssl.h> +#include "SSLInitializer.h" +#endif + +#include <string.h> + +#define TCP_BUFSIZE_READ 16400 +#define TCP_OUTPUT_CAPACITY 1024000 + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class SocketAddress; + +/** Socket implementation for TCP. + \ingroup basic */ +class TcpSocket : public StreamSocket +{ + /** \defgroup internal Internal utility */ +protected: + /** Buffer class containing one read/write circular buffer. + \ingroup internal */ + class CircularBuffer + { + public: + CircularBuffer(size_t size); + ~CircularBuffer(); + + /** append l bytes from p to buffer */ + bool Write(const char *p,size_t l); + /** copy l bytes from buffer to dest */ + bool Read(char *dest,size_t l); + /** copy l bytes from buffer to dest, dont touch buffer pointers */ + bool SoftRead(char *dest, size_t l); + /** skip l bytes from buffer */ + bool Remove(size_t l); + /** read l bytes from buffer, returns as string. */ + std::string ReadString(size_t l); + + /** total buffer length */ + size_t GetLength(); + /** pointer to circular buffer beginning */ + const char *GetStart(); + /** return number of bytes from circular buffer beginning to buffer physical end */ + size_t GetL(); + /** return free space in buffer, number of bytes until buffer overrun */ + size_t Space(); + + /** return total number of bytes written to this buffer, ever */ + unsigned long ByteCounter(bool clear = false); + + private: + CircularBuffer(const CircularBuffer& /*s*/) {} + CircularBuffer& operator=(const CircularBuffer& ) { return *this; } + char *buf; + size_t m_max; + size_t m_q; + size_t m_b; + size_t m_t; + unsigned long m_count; + }; + /** Output buffer struct. + \ingroup internal */ + struct OUTPUT { + OUTPUT() : _b(0), _t(0), _q(0) {} + OUTPUT(const char *buf, size_t len) : _b(0), _t(len), _q(len) { + memcpy(_buf, buf, len); + } + size_t Space() { + return TCP_OUTPUT_CAPACITY - _t; + } + void Add(const char *buf, size_t len) { + memcpy(_buf + _t, buf, len); + _t += len; + _q += len; + } + size_t Remove(size_t len) { + _b += len; + _q -= len; + return _q; + } + const char *Buf() { + return _buf + _b; + } + size_t Len() { + return _q; + } + size_t _b; + size_t _t; + size_t _q; + char _buf[TCP_OUTPUT_CAPACITY]; + }; + typedef std::list<OUTPUT *> output_l; + +public: + /** Constructor with standard values on input/output buffers. */ + TcpSocket(ISocketHandler& ); + /** Constructor with custom values for i/o buffer. + \param h ISocketHandler reference + \param isize Input buffer size + \param osize Output buffer size */ + TcpSocket(ISocketHandler& h,size_t isize,size_t osize); + ~TcpSocket(); + + /** Open a connection to a remote server. + If you want your socket to connect to a server, + always call Open before Add'ing a socket to the sockethandler. + If not, the connection attempt will not be monitored by the + socket handler... + \param ip IP address + \param port Port number + \param skip_socks Do not use socks4 even if configured */ + bool Open(ipaddr_t ip,port_t port,bool skip_socks = false); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Open connection. + \param ip Ipv6 address + \param port Port number + \param skip_socks Do not use socks4 even if configured */ + bool Open(in6_addr ip,port_t port,bool skip_socks = false); +#endif +#endif + bool Open(SocketAddress&,bool skip_socks = false); + bool Open(SocketAddress&,SocketAddress& bind_address,bool skip_socks = false); + /** Open connection. + \param host Hostname + \param port Port number */ + bool Open(const std::string &host,port_t port); + + /** Connect timeout callback. */ + void OnConnectTimeout(); +#ifdef _WIN32 + /** Connection failed reported as exception on win32 */ + void OnException(); +#endif + + /** Close file descriptor - internal use only. + \sa SetCloseAndDelete */ + int Close(); + + /** Send a string. + \param s String to send + \param f Dummy flags -- not used */ + void Send(const std::string &s,int f = 0); + /** Send string using printf formatting. */ + void Sendf(const char *format, ...); + /** Send buffer of bytes. + \param buf Buffer pointer + \param len Length of data + \param f Dummy flags -- not used */ + void SendBuf(const char *buf,size_t len,int f = 0); + /** This callback is executed after a successful read from the socket. + \param buf Pointer to the data + \param len Length of the data */ + virtual void OnRawData(const char *buf,size_t len); + + /** Called when output buffer has been sent. + Note: Will only be called IF the output buffer has been used. + Send's that was successful without needing the output buffer + will not generate a call to this method. */ + virtual void OnWriteComplete(); + /** Number of bytes in input buffer. */ + size_t GetInputLength(); + /** Number of bytes in output buffer. */ + size_t GetOutputLength(); + + /** Callback fires when a socket in line protocol has read one full line. + \param line Line read */ + void OnLine(const std::string& line); + /** Get counter of number of bytes received. */ + uint64_t GetBytesReceived(bool clear = false); + /** Get counter of number of bytes sent. */ + uint64_t GetBytesSent(bool clear = false); + + /** Socks4 specific callback. */ + void OnSocks4Connect(); + /** Socks4 specific callback. */ + void OnSocks4ConnectFailed(); + /** Socks4 specific callback. + \return 'need_more' */ + bool OnSocks4Read(); + +#ifdef ENABLE_RESOLVER + /** Callback executed when resolver thread has finished a resolve request. */ + void OnResolved(int id,ipaddr_t a,port_t port); +#ifdef ENABLE_IPV6 + void OnResolved(int id,in6_addr& a,port_t port); +#endif +#endif +#ifdef HAVE_OPENSSL + /** Callback for 'New' ssl support - replaces SSLSocket. Internal use. */ + void OnSSLConnect(); + /** Callback for 'New' ssl support - replaces SSLSocket. Internal use. */ + void OnSSLAccept(); + /** This method must be implemented to initialize + the ssl context for an outgoing connection. */ + virtual void InitSSLClient(); + /** This method must be implemented to initialize + the ssl context for an incoming connection. */ + virtual void InitSSLServer(); +#endif + +#ifdef ENABLE_RECONNECT + /** Flag that says a broken connection will try to reconnect. */ + void SetReconnect(bool = true); + /** Check reconnect on lost connection flag status. */ + bool Reconnect(); + /** Flag to determine if a reconnect is in progress. */ + void SetIsReconnect(bool x = true); + /** Socket is reconnecting. */ + bool IsReconnect(); +#endif + + void DisableInputBuffer(bool = true); + + void OnOptions(int,int,int,SOCKET); + + void SetLineProtocol(bool = true); + + // TCP options + bool SetTcpNodelay(bool = true); + + virtual int Protocol(); + + /** Trigger limit for callback OnTransferLimit. */ + void SetTransferLimit(size_t sz); + /** This callback fires when the output buffer drops below the value + set by SetTransferLimit. Default: 0 (disabled). */ + virtual void OnTransferLimit(); + +protected: + TcpSocket(const TcpSocket& ); + void OnRead(); + void OnRead( char *buf, size_t n ); + void OnWrite(); +#ifdef HAVE_OPENSSL + /** SSL; Initialize ssl context for a client socket. + \param meth_in SSL method */ + void InitializeContext(const std::string& context, SSL_METHOD *meth_in = NULL); + /** SSL; Initialize ssl context for a server socket. + \param keyfile Combined private key/certificate file + \param password Password for private key + \param meth_in SSL method */ + void InitializeContext(const std::string& context, const std::string& keyfile, const std::string& password, SSL_METHOD *meth_in = NULL); + /** SSL; Initialize ssl context for a server socket. + \param certfile Separate certificate file + \param keyfile Combined private key/certificate file + \param password Password for private key + \param meth_in SSL method */ + void InitializeContext(const std::string& context, const std::string& certfile, const std::string& keyfile, const std::string& password, SSL_METHOD *meth_in = NULL); + /** SSL; Password callback method. */ +static int SSL_password_cb(char *buf,int num,int rwflag,void *userdata); + /** SSL; Get pointer to ssl context structure. */ + virtual SSL_CTX *GetSslContext(); + /** SSL; Get pointer to ssl structure. */ + virtual SSL *GetSsl(); + /** ssl; still negotiating connection. */ + bool SSLNegotiate(); + /** SSL; Get ssl password. */ + const std::string& GetPassword(); +#endif + + CircularBuffer ibuf; ///< Circular input buffer + +private: + TcpSocket& operator=(const TcpSocket& ) { return *this; } + + /** the actual send() */ + int TryWrite(const char *buf, size_t len); + /** add data to output buffer top */ + void Buffer(const char *buf, size_t len); + + // + bool m_b_input_buffer_disabled; + uint64_t m_bytes_sent; + uint64_t m_bytes_received; + bool m_skip_c; ///< Skip second char of CRLF or LFCR sequence in OnRead + char m_c; ///< First char in CRLF or LFCR sequence + std::string m_line; ///< Current line in line protocol mode +#ifdef SOCKETS_DYNAMIC_TEMP + char *m_buf; ///< temporary read buffer +#endif + output_l m_obuf; ///< output buffer + OUTPUT *m_obuf_top; ///< output buffer on top + size_t m_transfer_limit; + size_t m_output_length; + +#ifdef HAVE_OPENSSL +static SSLInitializer m_ssl_init; + SSL_CTX *m_ssl_ctx; ///< ssl context + SSL *m_ssl; ///< ssl 'socket' + BIO *m_sbio; ///< ssl bio + std::string m_password; ///< ssl password +#endif + +#ifdef ENABLE_SOCKS4 + int m_socks4_state; ///< socks4 support + char m_socks4_vn; ///< socks4 support, temporary variable + char m_socks4_cd; ///< socks4 support, temporary variable + unsigned short m_socks4_dstport; ///< socks4 support + unsigned long m_socks4_dstip; ///< socks4 support +#endif + +#ifdef ENABLE_RESOLVER + int m_resolver_id; ///< Resolver id (if any) for current Open call +#endif + +#ifdef ENABLE_RECONNECT + bool m_b_reconnect; ///< Reconnect on lost connection flag + bool m_b_is_reconnect; ///< Trying to reconnect +#endif + +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_TcpSocket_H + + diff --git a/externals/sockets/include/Thread.h b/externals/sockets/include/Thread.h new file mode 100644 index 00000000000..efb766e9ee6 --- /dev/null +++ b/externals/sockets/include/Thread.h @@ -0,0 +1,100 @@ +/** \file Thread.h + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Thread_H +#define _SOCKETS_Thread_H + +#include "sockets-config.h" +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _WIN32 +// to be +//typedef DWORD threadfunc_t; +//typedef LPVOID threadparam_t; +//#define STDPREFIX WINAPI +typedef unsigned threadfunc_t; +typedef void * threadparam_t; +#define STDPREFIX __stdcall +#else +#include <pthread.h> + +typedef void * threadfunc_t; +typedef void * threadparam_t; +#define STDPREFIX +#endif + +/** \defgroup threading Threading */ +/** Thread base class. +The Thread class is used by the resolver (ResolvServer) and running a detached socket (SocketThread). +When you know some processing will take a long time and will freeze up a socket, there is always the +possibility to call Detach() on that socket before starting the processing. +When the OnDetached() callback is later called the processing can continue, now in its own thread. + \ingroup threading */ +class Thread +{ +public: + Thread(bool release = true); + virtual ~Thread(); + + static threadfunc_t STDPREFIX StartThread(threadparam_t); + + virtual void Run() = 0; + + bool IsRunning(); + void SetRunning(bool x); + bool IsReleased(); + void SetRelease(bool x); + bool DeleteOnExit(); + void SetDeleteOnExit(bool x = true); + bool IsDestructor(); + +private: + Thread(const Thread& ) {} + Thread& operator=(const Thread& ) { return *this; } +#ifdef _WIN32 + HANDLE m_thread; + unsigned m_dwThreadId; +#else + pthread_t m_thread; +#endif + bool m_running; + bool m_release; + bool m_b_delete_on_exit; + bool m_b_destructor; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Thread_H + + diff --git a/externals/sockets/include/UdpSocket.h b/externals/sockets/include/UdpSocket.h new file mode 100644 index 00000000000..3b06c6955bd --- /dev/null +++ b/externals/sockets/include/UdpSocket.h @@ -0,0 +1,215 @@ +/** \file UdpSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_UdpSocket_H +#define _SOCKETS_UdpSocket_H + +#include "sockets-config.h" +#include "Socket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Socket implementation for UDP. + \ingroup basic */ +class UdpSocket : public Socket +{ +public: + /** Constructor. + \param h ISocketHandler reference + \param ibufsz Maximum size of receive message (extra bytes will be truncated) + \param ipv6 'true' if this is an ipv6 socket */ + UdpSocket(ISocketHandler& h,int ibufsz = 16384,bool ipv6 = false, int retries = 0); + ~UdpSocket(); + + /** Called when incoming data has been received. + \param buf Pointer to data + \param len Length of data + \param sa Pointer to sockaddr struct of sender + \param sa_len Length of sockaddr struct */ + virtual void OnRawData(const char *buf,size_t len,struct sockaddr *sa,socklen_t sa_len); + + /** Called when incoming data has been received and read timestamp is enabled. + \param buf Pointer to data + \param len Length of data + \param sa Pointer to sockaddr struct of sender + \param sa_len Length of sockaddr struct + \param ts Timestamp from message */ + virtual void OnRawData(const char *buf,size_t len,struct sockaddr *sa,socklen_t sa_len,struct timeval *ts); + + /** To receive incoming data, call Bind to setup an incoming port. + \param port Incoming port number + \param range Port range to try if ports already in use + \return 0 if bind succeeded */ + int Bind(port_t& port,int range = 1); + /** To receive data on a specific interface:port, use this. + \param intf Interface ip/hostname + \param port Port number + \param range Port range + \return 0 if bind succeeded */ + int Bind(const std::string& intf,port_t& port,int range = 1); + /** To receive data on a specific interface:port, use this. + \param a Ip address + \param port Port number + \param range Port range + \return 0 if bind succeeded */ + int Bind(ipaddr_t a,port_t& port,int range = 1); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** To receive data on a specific interface:port, use this. + \param a Ipv6 address + \param port Port number + \param range Port range + \return 0 if bind succeeded */ + int Bind(in6_addr a,port_t& port,int range = 1); +#endif +#endif + /** To receive data on a specific interface:port, use this. + \param ad Socket address + \param range Port range + \return 0 if bind succeeded */ + int Bind(SocketAddress& ad,int range = 1); + + /** Define remote host. + \param l Address of remote host + \param port Port of remote host + \return true if successful */ + bool Open(ipaddr_t l,port_t port); + /** Define remote host. + \param host Hostname + \param port Port number + \return true if successful */ + bool Open(const std::string& host,port_t port); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Define remote host. + \param a Address of remote host, ipv6 + \param port Port of remote host + \return true if successful */ + bool Open(struct in6_addr& a,port_t port); +#endif +#endif + /** Define remote host. + \param ad Socket address + \return true if successful */ + bool Open(SocketAddress& ad); + + /** Send to specified host */ + void SendToBuf(const std::string& ,port_t,const char *data,int len,int flags = 0); + /** Send to specified address */ + void SendToBuf(ipaddr_t,port_t,const char *data,int len,int flags = 0); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Send to specified ipv6 address */ + void SendToBuf(in6_addr,port_t,const char *data,int len,int flags = 0); +#endif +#endif + /** Send to specified socket address */ + void SendToBuf(SocketAddress& ad,const char *data,int len,int flags = 0); + + /** Send string to specified host */ + void SendTo(const std::string&,port_t,const std::string&,int flags = 0); + /** Send string to specified address */ + void SendTo(ipaddr_t,port_t,const std::string&,int flags = 0); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Send string to specified ipv6 address */ + void SendTo(in6_addr,port_t,const std::string&,int flags = 0); +#endif +#endif + /** Send string to specified socket address */ + void SendTo(SocketAddress& ad,const std::string&,int flags = 0); + + /** Send to connected address */ + void SendBuf(const char *data,size_t,int flags = 0); + /** Send string to connected address. */ + void Send(const std::string& ,int flags = 0); + + /** Set broadcast */ + void SetBroadcast(bool b = true); + /** Check broadcast flag. + \return true broadcast is enabled. */ + bool IsBroadcast(); + + /** multicast */ + void SetMulticastTTL(int ttl = 1); + int GetMulticastTTL(); + void SetMulticastLoop(bool = true); + bool IsMulticastLoop(); + void AddMulticastMembership(const std::string& group,const std::string& intf = "0.0.0.0",int if_index = 0); + void DropMulticastMembership(const std::string& group,const std::string& intf = "0.0.0.0",int if_index = 0); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** multicast, ipv6 only */ + void SetMulticastHops(int = -1); + /** multicast, ipv6 only */ + int GetMulticastHops(); +#endif +#endif + /** Returns true if Bind succeeded. */ + bool IsBound(); + /** Return Bind port number */ + port_t GetPort(); + + void OnOptions(int,int,int,SOCKET) {} + + int GetLastSizeWritten(); + + /** Also read timestamp information from incoming message */ + void SetTimestamp(bool = true); + +protected: + UdpSocket(const UdpSocket& s) : Socket(s) {} + void OnRead(); +#if defined(LINUX) || defined(MACOSX) + /** This method emulates socket recvfrom, but uses messages so we can get the timestamp */ + int ReadTS(char *ioBuf, int inBufSize, struct sockaddr *from, socklen_t fromlen, struct timeval *ts); +#endif + +private: + UdpSocket& operator=(const UdpSocket& ) { return *this; } + /** create before using sendto methods */ + void CreateConnection(); + char *m_ibuf; ///< Input buffer + int m_ibufsz; ///< Size of input buffer + bool m_bind_ok; ///< Bind completed successfully + port_t m_port; ///< Bind port number + int m_last_size_written; + int m_retries; + bool m_b_read_ts; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_UdpSocket_H + + diff --git a/externals/sockets/include/Utility.h b/externals/sockets/include/Utility.h new file mode 100644 index 00000000000..724a94e4b32 --- /dev/null +++ b/externals/sockets/include/Utility.h @@ -0,0 +1,186 @@ +/** \file Utility.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Utility_H +#define _SOCKETS_Utility_H + +#include "sockets-config.h" +#include <ctype.h> +#include <string.h> +#include <memory> +#include "socket_include.h" +#include <map> +#include <string> + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define TWIST_LEN 624 + +class SocketAddress; + +/** Conversion utilities. + \ingroup util */ +class Utility +{ + /** + The Mersenne Twister + http://www.math.keio.ac.jp/~matumoto/emt.html + */ + class Rng { + public: + Rng(unsigned long seed); + + unsigned long Get(); + + private: + int m_value; + unsigned long m_tmp[TWIST_LEN]; + }; + class ncmap_compare { + public: + bool operator()(const std::string& x, const std::string& y) const { + return strcasecmp(x.c_str(), y.c_str()) < 0; + } + }; +public: + template<typename Y> class ncmap : public std::map<std::string, Y, ncmap_compare> { + public: + ncmap() {} + }; +public: + static std::string base64(const std::string& str_in); + static std::string base64d(const std::string& str_in); + static std::string l2string(long l); + static std::string bigint2string(uint64_t l); + static uint64_t atoi64(const std::string& str); + static unsigned int hex2unsigned(const std::string& str); + static std::string rfc1738_encode(const std::string& src); + static std::string rfc1738_decode(const std::string& src); + + /** Checks whether a string is a valid ipv4/ipv6 ip number. */ + static bool isipv4(const std::string&); + /** Checks whether a string is a valid ipv4/ipv6 ip number. */ + static bool isipv6(const std::string&); + + /** Hostname to ip resolution ipv4, not asynchronous. */ + static bool u2ip(const std::string&, ipaddr_t&); + static bool u2ip(const std::string&, struct sockaddr_in& sa, int ai_flags = 0); + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Hostname to ip resolution ipv6, not asynchronous. */ + static bool u2ip(const std::string&, struct in6_addr&); + static bool u2ip(const std::string&, struct sockaddr_in6& sa, int ai_flags = 0); +#endif +#endif + + /** Reverse lookup of address to hostname */ + static bool reverse(struct sockaddr *sa, socklen_t sa_len, std::string&, int flags = 0); + static bool reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags = 0); + + static bool u2service(const std::string& name, int& service, int ai_flags = 0); + + /** Convert binary ip address to string: ipv4. */ + static void l2ip(const ipaddr_t,std::string& ); + static void l2ip(const in_addr&,std::string& ); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Convert binary ip address to string: ipv6. */ + static void l2ip(const struct in6_addr&,std::string& ,bool mixed = false); + + /** ipv6 address compare. */ + static int in6_addr_compare(in6_addr,in6_addr); +#endif +#endif + /** ResolveLocal (hostname) - call once before calling any GetLocal method. */ + static void ResolveLocal(); + /** Returns local hostname, ResolveLocal must be called once before using. + \sa ResolveLocal */ + static const std::string& GetLocalHostname(); + /** Returns local ip, ResolveLocal must be called once before using. + \sa ResolveLocal */ + static ipaddr_t GetLocalIP(); + /** Returns local ip number as string. + \sa ResolveLocal */ + static const std::string& GetLocalAddress(); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Returns local ipv6 ip. + \sa ResolveLocal */ + static const struct in6_addr& GetLocalIP6(); + /** Returns local ipv6 address. + \sa ResolveLocal */ + static const std::string& GetLocalAddress6(); +#endif +#endif + /** Set environment variable. + \param var Name of variable to set + \param value Value */ + static void SetEnv(const std::string& var,const std::string& value); + /** Convert sockaddr struct to human readable string. + \param sa Ptr to sockaddr struct */ + static std::string Sa2String(struct sockaddr *sa); + + /** Get current time in sec/microseconds. */ + static void GetTime(struct timeval *); + + static std::auto_ptr<SocketAddress> CreateAddress(struct sockaddr *,socklen_t); + + static unsigned long ThreadID(); + + static std::string ToLower(const std::string& str); + static std::string ToUpper(const std::string& str); + + static std::string ToString(double d); + + /** Returns a random 32-bit integer */ + static unsigned long Rnd(); + +private: + static std::string m_host; ///< local hostname + static ipaddr_t m_ip; ///< local ip address + static std::string m_addr; ///< local ip address in string format +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + static struct in6_addr m_local_ip6; ///< local ipv6 address +#endif + static std::string m_local_addr6; ///< local ipv6 address in string format +#endif + static bool m_local_resolved; ///< ResolveLocal has been called if true +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Utility_H + + diff --git a/externals/sockets/include/socket_include.h b/externals/sockets/include/socket_include.h new file mode 100644 index 00000000000..89855a54108 --- /dev/null +++ b/externals/sockets/include/socket_include.h @@ -0,0 +1,290 @@ +/** \file socket_include.h + ** \date 2005-04-12 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_socket_include_H +#define _SOCKETS_socket_include_H +#include "sockets-config.h" + +#ifdef _MSC_VER +#pragma warning(disable:4514) +#endif + +// common defines affecting library and applications using library + +/* Define SOCKETS_DYNAMIC_TEMP to use dynamically allocated buffers + in read operations - helps on ECOS */ +#define SOCKETS_DYNAMIC_TEMP + +// platform specific stuff +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include <sys/param.h> +#endif +#include <list> + +// int64 +#ifdef _WIN32 +typedef unsigned __int64 uint64_t; +#else +#include <stdlib.h> +#ifdef SOLARIS +# include <sys/types.h> +#else +# include <stdint.h> +#endif +#endif + +#ifndef _WIN32 +// ---------------------------------------- +// common unix includes / defines +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +//#include <netdb.h> + +// all typedefs in this file will be declared outside the sockets namespace, +// because some os's will already have one or more of the type defined. +typedef int SOCKET; +#define Errno errno +#define StrError strerror + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +// WIN32 adapt +#define closesocket close +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif // INADDR_NONE + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // !_WIN32 + +// ---------------------------------------- +// Generic +#ifndef SOL_IP +#define SOL_IP IPPROTO_IP +#endif + +// ---------------------------------------- +// OS specific adaptions + +#ifdef SOLARIS +// ---------------------------------------- +// Solaris +typedef unsigned short port_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define s6_addr16 _S6_un._S6_u8 +#define MSG_NOSIGNAL 0 + +#elif defined __FreeBSD__ +// ---------------------------------------- +// FreeBSD +# if __FreeBSD_version >= 400014 +# define s6_addr16 __u6_addr.__u6_addr16 +# if !defined(MSG_NOSIGNAL) +# define MSG_NOSIGNAL 0 +# endif +# include <netinet/in.h> +typedef in_addr_t ipaddr_t; +typedef in_port_t port_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +# else +# error FreeBSD versions prior to 400014 does not support ipv6 +# endif + +#elif defined (__NetBSD__) || defined (__OpenBSD__) +# if !defined(MSG_NOSIGNAL) +# define MSG_NOSIGNAL 0 +# endif +# include <netinet/in.h> +typedef in_addr_t ipaddr_t; +typedef in_port_t port_t; +#elif defined MACOSX +// ---------------------------------------- +// Mac OS X +#include <string.h> +#ifdef __DARWIN_UNIX03 +typedef unsigned short port_t; +#else +#include <mach/port.h> +#endif // __DARWIN_UNIX03 +typedef unsigned long ipaddr_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define s6_addr16 __u6_addr.__u6_addr16 +#define MSG_NOSIGNAL 0 // oops - thanks Derek +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP + +#elif defined _WIN32 +// ---------------------------------------- +// Win32 +#ifdef _MSC_VER +#pragma comment(lib, "wsock32.lib") +#endif +#define strcasecmp _stricmp + +typedef unsigned long ipaddr_t; +typedef unsigned short port_t; +typedef int socklen_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +// 1.8.6: define FD_SETSIZE to something bigger than 64 if there are a lot of +// simultaneous connections (must be done before including winsock.h) +#define FD_SETSIZE 1024 + +// windows 2000 with ipv6 preview installed: +// http://msdn.microsoft.com/downloads/sdks/platform/tpipv6.asp +// see the FAQ on how to install +#define WIN32_LEAN_AND_MEAN +#include <winsock2.h> +#include <ws2tcpip.h> +#if _MSC_VER < 1200 +#ifndef __CYGWIN__ +#ifdef ENABLE_IPV6 +#include <tpipv6.h> // For IPv6 Tech Preview. +#endif +#endif +#endif // _MSC_VER < 1200 + +#define MSG_NOSIGNAL 0 +//#define SHUT_RDWR 2 +#define SHUT_WR 1 + +#define Errno WSAGetLastError() +const char *StrError(int x); + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +// class WSAInitializer is a part of the Socket class (on win32) +// as a static instance - so whenever an application uses a Socket, +// winsock is initialized +class WSAInitializer // Winsock Initializer +{ +public: + WSAInitializer() { + if (WSAStartup(0x101,&m_wsadata)) + { + exit(-1); + } + } + ~WSAInitializer() { + WSACleanup(); + } +private: + WSADATA m_wsadata; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#else +// ---------------------------------------- +// LINUX +typedef unsigned long ipaddr_t; +typedef unsigned short port_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + /** List type containing file descriptors. */ + typedef std::list<SOCKET> socket_v; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +// getaddrinfo / getnameinfo replacements +#ifdef NO_GETADDRINFO +#ifndef AI_NUMERICHOST +#define AI_NUMERICHOST 1 +#endif +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 1 +#endif +#endif + +#endif // _SOCKETS_socket_include_H + + diff --git a/externals/sockets/include/sockets-config.h b/externals/sockets/include/sockets-config.h new file mode 100644 index 00000000000..1c8dc439092 --- /dev/null +++ b/externals/sockets/include/sockets-config.h @@ -0,0 +1,90 @@ +/** + ** \file sockets-config.h + ** \date 2007-04-14 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_CONFIG_H +#define _SOCKETS_CONFIG_H + +#ifndef _RUN_DP +/* First undefine symbols if already defined. */ +#undef HAVE_OPENSSL +#undef ENABLE_IPV6 +#undef USE_SCTP +#undef NO_GETADDRINFO +#undef ENABLE_POOL +#undef ENABLE_SOCKS4 +#undef ENABLE_RESOLVER +#undef ENABLE_RECONNECT +#undef ENABLE_DETACH +#undef ENABLE_TRIGGERS +#undef ENABLE_EXCEPTIONS +#endif // _RUN_DP + +// define MACOSX for internal socket library checks +#if defined(__APPLE__) && defined(__MACH__) && !defined(MACOSX) +#define MACOSX +#endif + +/* OpenSSL support. */ +//#define HAVE_OPENSSL + +/* Ipv6 support. */ +//#define ENABLE_IPV6 + +/* SCTP support. */ +//#define USE_SCTP + +/* Define NO_GETADDRINFO if your operating system does not support + the "getaddrinfo" and "getnameinfo" function calls. */ +#define NO_GETADDRINFO + +/* Connection pool support. */ +#define ENABLE_POOL + +/* Socks4 client support. */ +//#define ENABLE_SOCKS4 + +/* Asynchronous resolver. */ +#define ENABLE_RESOLVER + +/* Enable TCP reconnect on lost connection. + Socket::OnReconnect + Socket::OnDisconnect +*/ +#define ENABLE_RECONNECT + +/* Enable socket thread detach functionality. */ +#define ENABLE_DETACH + +/* Enable socket to socket triggers. Not yet in use. */ +//#define ENABLE_TRIGGERS + +/* Enabled exceptions. */ +//#define ENABLE_EXCEPTIONS + +/* Resolver uses the detach function so either enable both or disable both. */ +#ifndef ENABLE_DETACH +#undef ENABLE_RESOLVER +#endif + +#endif // _SOCKETS_CONFIG_H + + diff --git a/externals/sockets/network_kist.txt b/externals/sockets/network_kist.txt new file mode 100644 index 00000000000..f6597bf9c77 --- /dev/null +++ b/externals/sockets/network_kist.txt @@ -0,0 +1,20 @@ +The following are the only .cpp files used from the new network library (v2.2.8) This file is just for future reference. + +Base64.cpp +Exception.cpp +Ipv4Address.cpp +Ipv6Address.cpp +Lock.cpp +Mutex.cpp +Parse.cpp +ResolvServer.cpp +ResolvSocket.cpp +Socket.cpp +SocketHandler.cpp +socket_include.cpp +StdoutLog.cpp +StreamSocket.cpp +TcpSocket.cpp +Thread.cpp +UdpSocket.cpp +Utility.cpp diff --git a/externals/sockets/socket_include.cpp b/externals/sockets/socket_include.cpp new file mode 100644 index 00000000000..290602c1b52 --- /dev/null +++ b/externals/sockets/socket_include.cpp @@ -0,0 +1,89 @@ +/** \file socket_include.cpp + ** \date 2004-11-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include <stdio.h> + +// only to be included in win32 projects +const char *StrError(int x) +{ +static char tmp[100]; + switch (x) + { + case 10004: return "Interrupted function call."; + case 10013: return "Permission denied."; + case 10014: return "Bad address."; + case 10022: return "Invalid argument."; + case 10024: return "Too many open files."; + case 10035: return "Resource temporarily unavailable."; + case 10036: return "Operation now in progress."; + case 10037: return "Operation already in progress."; + case 10038: return "Socket operation on nonsocket."; + case 10039: return "Destination address required."; + case 10040: return "Message too long."; + case 10041: return "Protocol wrong type for socket."; + case 10042: return "Bad protocol option."; + case 10043: return "Protocol not supported."; + case 10044: return "Socket type not supported."; + case 10045: return "Operation not supported."; + case 10046: return "Protocol family not supported."; + case 10047: return "Address family not supported by protocol family."; + case 10048: return "Address already in use."; + case 10049: return "Cannot assign requested address."; + case 10050: return "Network is down."; + case 10051: return "Network is unreachable."; + case 10052: return "Network dropped connection on reset."; + case 10053: return "Software caused connection abort."; + case 10054: return "Connection reset by peer."; + case 10055: return "No buffer space available."; + case 10056: return "Socket is already connected."; + case 10057: return "Socket is not connected."; + case 10058: return "Cannot send after socket shutdown."; + case 10060: return "Connection timed out."; + case 10061: return "Connection refused."; + case 10064: return "Host is down."; + case 10065: return "No route to host."; + case 10067: return "Too many processes."; + case 10091: return "Network subsystem is unavailable."; + case 10092: return "Winsock.dll version out of range."; + case 10093: return "Successful WSAStartup not yet performed."; + case 10101: return "Graceful shutdown in progress."; + case 10109: return "Class type not found."; + case 11001: return "Host not found."; + case 11002: return "Nonauthoritative host not found."; + case 11003: return "This is a nonrecoverable error."; + case 11004: return "Valid name, no data record of requested type."; + + default: + break; + } + sprintf(tmp, "Winsock error code: %d", x); + return tmp; +} + + diff --git a/externals/utf8cpp/delme b/externals/utf8cpp/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/utf8cpp/delme +++ /dev/null diff --git a/externals/utf8cpp/include/doc/ReleaseNotes b/externals/utf8cpp/include/doc/ReleaseNotes new file mode 100644 index 00000000000..8541c7a6031 --- /dev/null +++ b/externals/utf8cpp/include/doc/ReleaseNotes @@ -0,0 +1,9 @@ +utf8 cpp library +Release 2.1 + +This is a minor feature release - added the function peek_next. + +Changes from version 2.o +- Implemented feature request [ 1770746 ] "Provide a const version of next() (some sort of a peek() ) + +Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes diff --git a/externals/utf8cpp/include/doc/utf8cpp.html b/externals/utf8cpp/include/doc/utf8cpp.html new file mode 100644 index 00000000000..4ad7e1002a9 --- /dev/null +++ b/externals/utf8cpp/include/doc/utf8cpp.html @@ -0,0 +1,1574 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> + <head> + <meta name="generator" content= + "HTML Tidy for Linux/x86 (vers 1st November 2002), see www.w3.org"> + <meta name="description" content= + "A simple, portable and lightweigt C++ library for easy handling of UTF-8 encoded strings"> + <meta name="keywords" content="UTF-8 C++ portable utf8 unicode generic templates"> + <meta name="author" content="Nemanja Trifunovic"> + <title> + UTF8-CPP: UTF-8 with C++ in a Portable Way + </title> + <style type="text/css"> + <!-- + span.return_value { + color: brown; + } + span.keyword { + color: blue; + } + span.preprocessor { + color: navy; + } + span.literal { + color: olive; + } + span.comment { + color: green; + } + code { + font-weight: bold; + } + ul.toc { + list-style-type: none; + } + p.version { + font-size: small; + font-style: italic; + } + --> + </style> + </head> + <body> + <h1> + UTF8-CPP: UTF-8 with C++ in a Portable Way + </h1> + <p> + <a href="https://sourceforge.net/projects/utfcpp">The Sourceforge project page</a> + </p> + <div id="toc"> + <h2> + Table of Contents + </h2> + <ul class="toc"> + <li> + <a href="#introduction">Introduction</a> + </li> + <li> + <a href="#examples">Examples of Use</a> + </li> + <li> + <a href="#reference">Reference</a> + <ul class="toc"> + <li> + <a href="#funutf8">Functions From utf8 Namespace </a> + </li> + <li> + <a href="#typesutf8">Types From utf8 Namespace </a> + </li> + <li> + <a href="#fununchecked">Functions From utf8::unchecked Namespace </a> + </li> + <li> + <a href="#typesunchecked">Types From utf8::unchecked Namespace </a> + </li> + </ul> + </li> + <li> + <a href="#points">Points of Interest</a> + </li> + <li> + <a href="#conclusion">Conclusion</a> + </li> + <li> + <a href="#links">Links</a> + </li> + </ul> + </div> + <h2 id="introduction"> + Introduction + </h2> + <p> + Many C++ developers miss an easy and portable way of handling Unicode encoded + strings. C++ Standard is currently Unicode agnostic, and while some work is being + done to introduce Unicode to the next incarnation called C++0x, for the moment + nothing of the sort is available. In the meantime, developers use 3rd party + libraries like ICU, OS specific capabilities, or simply roll out their own + solutions. + </p> + <p> + In order to easily handle UTF-8 encoded Unicode strings, I have come up with a small + generic library. For anybody used to work with STL algorithms and iterators, it should be + easy and natural to use. The code is freely available for any purpose - check out + the license at the beginning of the utf8.h file. If you run into + bugs or performance issues, please let me know and I'll do my best to address them. + </p> + <p> + The purpose of this article is not to offer an introduction to Unicode in general, + and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out + <a href="http://www.unicode.org/">Unicode Home Page</a> or some other source of + information for Unicode. Also, it is not my aim to advocate the use of UTF-8 + encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from + C++, I am sure you have good reasons for it. + </p> + <h2 id="examples"> + Examples of use + </h2> + <p> + To illustrate the use of this utf8 library, we shall open a file containing UTF-8 + encoded text, check whether it starts with a byte order mark, read each line into a + <code>std::string</code>, check it for validity, convert the text to UTF-16, and + back to UTF-8: + </p> +<pre> +<span class="preprocessor">#include <fstream></span> +<span class="preprocessor">#include <iostream></span> +<span class="preprocessor">#include <string></span> +<span class="preprocessor">#include <vector></span> +<span class="preprocessor">#include "utf8.h"</span> +<span class="keyword">using namespace</span> std; +<span class="keyword">int</span> main() +{ + <span class="keyword">if</span> (argc != <span class="literal">2</span>) { + cout << <span class="literal">"\nUsage: docsample filename\n"</span>; + <span class="keyword">return</span> <span class="literal">0</span>; + } + <span class="keyword">const char</span>* test_file_path = argv[1]; + <span class="comment">// Open the test file (must be UTF-8 encoded)</span> + ifstream fs8(test_file_path); + <span class="keyword">if</span> (!fs8.is_open()) { + cout << <span class= +"literal">"Could not open "</span> << test_file_path << endl; + <span class="keyword">return</span> <span class="literal">0</span>; + } + <span class="comment">// Read the first line of the file</span> + <span class="keyword">unsigned</span> line_count = <span class="literal">1</span>; + string line; + <span class="keyword">if</span> (!getline(fs8, line)) + <span class="keyword">return</span> <span class="literal">0</span>; + <span class="comment">// Look for utf-8 byte-order mark at the beginning</span> + <span class="keyword">if</span> (line.size() > <span class="literal">2</span>) { + <span class="keyword">if</span> (utf8::is_bom(line.c_str())) + cout << <span class= +"literal">"There is a byte order mark at the beginning of the file\n"</span>; + } + <span class="comment">// Play with all the lines in the file</span> + <span class="keyword">do</span> { + <span class="comment">// check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)</span> + string::iterator end_it = utf8::find_invalid(line.begin(), line.end()); + <span class="keyword">if</span> (end_it != line.end()) { + cout << <span class= +"literal">"Invalid UTF-8 encoding detected at line "</span> << line_count << <span + class="literal">"\n"</span>; + cout << <span class= +"literal">"This part is fine: "</span> << string(line.begin(), end_it) << <span + class="literal">"\n"</span>; + } + <span class="comment">// Get the line length (at least for the valid part)</span> + <span class="keyword">int</span> length = utf8::distance(line.begin(), end_it); + cout << <span class= +"literal">"Length of line "</span> << line_count << <span class= +"literal">" is "</span> << length << <span class="literal">"\n"</span>; + <span class="comment">// Convert it to utf-16</span> + vector<unsigned short> utf16line; + utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line)); + <span class="comment">// And back to utf-8</span> + string utf8line; + utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line)); + <span class="comment">// Confirm that the conversion went OK:</span> + <span class="keyword">if</span> (utf8line != string(line.begin(), end_it)) + cout << <span class= +"literal">"Error in UTF-16 conversion at line: "</span> << line_count << <span + class="literal">"\n"</span>; + getline(fs8, line); + line_count++; + } <span class="keyword">while</span> (!fs8.eof()); + <span class="keyword">return</span> <span class="literal">0</span>; +} +</pre> + <p> + In the previous code sample, we have seen the use of the following functions from + <code>utf8</code> namespace: first we used <code>is_bom</code> function to detect + UTF-8 byte order mark at the beginning of the file; then for each line we performed + a detection of invalid UTF-8 sequences with <code>find_invalid</code>; the number + of characters (more precisely - the number of Unicode code points) in each line was + determined with a use of <code>utf8::distance</code>; finally, we have converted + each line to UTF-16 encoding with <code>utf8to16</code> and back to UTF-8 with + <code>utf16to8</code>. + </p> + <h2 id="reference"> + Reference + </h2> + <h3 id="funutf8"> + Functions From utf8 Namespace + </h3> + <h4> + utf8::append + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +octet_iterator append(uint32_t cp, octet_iterator result); + +</pre> + <p> + <code>cp</code>: A 32 bit integer representing a code point to append to the + sequence.<br> + <code>result</code>: An output iterator to the place in the sequence where to + append the code point.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the newly appended sequence. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span +class="literal">0</span>,<span class="literal">0</span>,<span class= +"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>}; +<span class="keyword">unsigned char</span>* end = append(<span class= +"literal">0x0448</span>, u); +assert (u[<span class="literal">0</span>] == <span class= +"literal">0xd1</span> && u[<span class="literal">1</span>] == <span class= +"literal">0x88</span> && u[<span class="literal">2</span>] == <span class= +"literal">0</span> && u[<span class="literal">3</span>] == <span class= +"literal">0</span> && u[<span class="literal">4</span>] == <span class= +"literal">0</span>); +</pre> + <p> + Note that <code>append</code> does not allocate any memory - it is the burden of + the caller to make sure there is enough memory allocated for the operation. To make + things more interesting, <code>append</code> can add anywhere between 1 and 4 + octets to the sequence. In practice, you would most often want to use + <code>std::back_inserter</code> to ensure that the necessary memory is allocated. + </p> + <p> + In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception + is thrown. + </p> + <h4> + utf8::next + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point and moves the iterator to the next position. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t next(octet_iterator& it, octet_iterator end); + +</pre> + <p> + <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.<br> + <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code> + gets equal to <code>end</code> during the extraction of a code point, an + <code>utf8::not_enough_room</code> exception is thrown.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + processed UTF-8 code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars; +<span class="keyword">int</span> cp = next(w, twochars + <span class="literal">6</span>); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars + <span class="literal">3</span>); +</pre> + <p> + This function is typically used to iterate through a UTF-8 encoded string. + </p> + <p> + In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is + thrown. + </p> + <h4> + utf8::peek_next + </h4> + <p class="version"> + Available in version 2.1 and later. + </p> + <p> + Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point for the following sequence without changing the value of the iterator. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t peek_next(octet_iterator it, octet_iterator end); + +</pre> + <p> + <code>it</code>: an iterator pointing to the beginning of an UTF-8 + encoded code point.<br> + <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code> + gets equal to <code>end</code> during the extraction of a code point, an + <code>utf8::not_enough_room</code> exception is thrown.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + processed UTF-8 code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars; +<span class="keyword">int</span> cp = peek_next(w, twochars + <span class="literal">6</span>); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars); +</pre> + <p> + In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is + thrown. + </p> + <h4> + utf8::prior + </h4> + <p class="version"> + Available in version 1.02 and later. + </p> + <p> + Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t prior(octet_iterator& it, octet_iterator start); + +</pre> + <p> + <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.<br> + <code>start</code>: an iterator to the beginning of the sequence where the search + for the beginning of a code point is performed. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + previous code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">unsigned char</span>* w = twochars + <span class= +"literal">3</span>; +<span class="keyword">int</span> cp = prior (w, twochars); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars); +</pre> + <p> + This function has two purposes: one is two iterate backwards through a UTF-8 + encoded string. Note that it is usually a better idea to iterate forward instead, + since <code>utf8::next</code> is faster. The second purpose is to find a beginning + of a UTF-8 sequence if we have a random position within a string. + </p> + <p> + <code>it</code> will typically point to the beginning of + a code point, and <code>start</code> will point to the + beginning of the string to ensure we don't go backwards too far. <code>it</code> is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. + </p> + <p> + In case <code>pass_end</code> is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code> + exception is thrown. + </p> + <h4> + utf8::previous + </h4> + <p class="version"> + Deprecated in version 1.02 and later. + </p> + <p> + Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t previous(octet_iterator& it, octet_iterator pass_start); + +</pre> + <p> + <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.<br> + <code>pass_start</code>: an iterator to the point in the sequence where the search + for the beginning of a code point is aborted if no result was reached. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + previous code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">unsigned char</span>* w = twochars + <span class= +"literal">3</span>; +<span class="keyword">int</span> cp = previous (w, twochars - <span class= +"literal">1</span>); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars); +</pre> + <p> + <code>utf8::previous</code> is deprecated, and <code>utf8::prior</code> should + be used instead, although the existing code can continue using this function. + The problem is the parameter <code>pass_start</code> that points to the position + just before the beginning of the sequence. Standard containers don't have the + concept of "pass start" and the function can not be used with their iterators. + </p> + <p> + <code>it</code> will typically point to the beginning of + a code point, and <code>pass_start</code> will point to the octet just before the + beginning of the string to ensure we don't go backwards too far. <code>it</code> is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. + </p> + <p> + In case <code>pass_end</code> is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code> + exception is thrown + </p> + <h4> + utf8::advance + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Advances an iterator by the specified number of code points within an UTF-8 + sequence. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, typename distance_type> +<span class= +"keyword">void</span> advance (octet_iterator& it, distance_type n, octet_iterator end); + +</pre> + <p> + <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.<br> + <code>n</code>: a positive integer that shows how many code points we want to + advance.<br> + <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code> + gets equal to <code>end</code> during the extraction of a code point, an + <code>utf8::not_enough_room</code> exception is thrown.<br> + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">unsigned char</span>* w = twochars; +advance (w, <span class="literal">2</span>, twochars + <span class="literal">6</span>); +assert (w == twochars + <span class="literal">5</span>); +</pre> + <p> + This function works only "forward". In case of a negative <code>n</code>, there is + no effect. + </p> + <p> + In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception + is thrown. + </p> + <h4> + utf8::distance + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +<span class= +"keyword">typename</span> std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last); + +</pre> + <p> + <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br> + <code>last</code>: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.<br> + <span class="return_value">Return value</span> the distance between the iterators, + in code points. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +size_t dist = utf8::distance(twochars, twochars + <span class="literal">5</span>); +assert (dist == <span class="literal">2</span>); +</pre> + <p> + This function is used to find the length (in code points) of a UTF-8 encoded + string. The reason it is called <em>distance</em>, rather than, say, + <em>length</em> is mainly because developers are used that <em>length</em> is an + O(1) function. Computing the length of an UTF-8 string is a linear operation, and + it looked better to model it after <code>std::distance</code> algorithm. + </p> + <p> + In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is + thrown. If <code>last</code> does not point to the past-of-end of a UTF-8 seqence, + a <code>utf8::not_enough_room</code> exception is thrown. + </p> + <h4> + utf8::utf16to8 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts a UTF-16 encoded string to UTF-8. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> u16bit_iterator, <span class= +"keyword">typename</span> octet_iterator> +octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-8 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-8 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">unsigned short</span> utf16string[] = {<span class= +"literal">0x41</span>, <span class="literal">0x0448</span>, <span class= +"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class= +"literal">0xdd1e</span>}; +vector<<span class="keyword">unsigned char</span>> utf8result; +utf16to8(utf16string, utf16string + <span class= +"literal">5</span>, back_inserter(utf8result)); +assert (utf8result.size() == <span class="literal">10</span>); +</pre> + <p> + In case of invalid UTF-16 sequence, a <code>utf8::invalid_utf16</code> exception is + thrown. + </p> + <h4> + utf8::utf8to16 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts an UTF-8 encoded string to UTF-16 + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> u16bit_iterator, typename octet_iterator> +u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> <code>end</code>: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-16 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-16 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span> utf8_with_surrogates[] = <span class= +"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>; +vector <<span class="keyword">unsigned short</span>> utf16result; +utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class= +"literal">9</span>, back_inserter(utf16result)); +assert (utf16result.size() == <span class="literal">4</span>); +assert (utf16result[<span class="literal">2</span>] == <span class= +"literal">0xd834</span>); +assert (utf16result[<span class="literal">3</span>] == <span class= +"literal">0xdd1e</span>); +</pre> + <p> + In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is + thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a + <code>utf8::not_enough_room</code> exception is thrown. + </p> + <h4> + utf8::utf32to8 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts a UTF-32 encoded string to UTF-8. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, typename u32bit_iterator> +octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-8 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-8 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">int</span> utf32string[] = {<span class= +"literal">0x448</span>, <span class="literal">0x65E5</span>, <span class= +"literal">0x10346</span>, <span class="literal">0</span>}; +vector<<span class="keyword">unsigned char</span>> utf8result; +utf32to8(utf32string, utf32string + <span class= +"literal">3</span>, back_inserter(utf8result)); +assert (utf8result.size() == <span class="literal">9</span>); +</pre> + <p> + In case of invalid UTF-32 string, a <code>utf8::invalid_code_point</code> exception + is thrown. + </p> + <h4> + utf8::utf8to32 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts a UTF-8 encoded string to UTF-32. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, <span class= +"keyword">typename</span> u32bit_iterator> +u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-32 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-32 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +vector<<span class="keyword">int</span>> utf32result; +utf8to32(twochars, twochars + <span class= +"literal">5</span>, back_inserter(utf32result)); +assert (utf32result.size() == <span class="literal">2</span>); +</pre> + <p> + In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is + thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a + <code>utf8::not_enough_room</code> exception is thrown. + </p> + <h4> + utf8::find_invalid + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Detects an invalid sequence within a UTF-8 string. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +octet_iterator find_invalid(octet_iterator start, octet_iterator end); +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to + test for validity.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.<br> + <span class="return_value">Return value</span>: an iterator pointing to the first + invalid octet in the UTF-8 string. In case none were found, equals + <code>end</code>. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span> utf_invalid[] = <span class= +"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>; +<span class= +"keyword">char</span>* invalid = find_invalid(utf_invalid, utf_invalid + <span class= +"literal">6</span>); +assert (invalid == utf_invalid + <span class="literal">5</span>); +</pre> + <p> + This function is typically used to make sure a UTF-8 string is valid before + processing it with other functions. It is especially important to call it if before + doing any of the <em>unchecked</em> operations on it. + </p> + <h4> + utf8::is_valid + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Checks whether a sequence of octets is a valid UTF-8 string. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +<span class="keyword">bool</span> is_valid(octet_iterator start, octet_iterator end); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to + test for validity.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.<br> + <span class="return_value">Return value</span>: <code>true</code> if the sequence + is a valid UTF-8 string; <code>false</code> if not. + </p> + Example of use: +<pre> +<span class="keyword">char</span> utf_invalid[] = <span class= +"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>; +<span class="keyword">bool</span> bvalid = is_valid(utf_invalid, utf_invalid + <span +class="literal">6</span>); +assert (bvalid == false); +</pre> + <p> + <code>is_valid</code> is a shorthand for <code>find_invalid(start, end) == + end;</code>. You may want to use it to make sure that a byte seqence is a valid + UTF-8 string without the need to know where it fails if it is not valid. + </p> + <h4> + utf8::replace_invalid + </h4> + <p class="version"> + Available in version 2.0 and later. + </p> + <p> + Replaces all invalid UTF-8 sequences within a string with a replacement marker. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, <span class= +"keyword">typename</span> output_iterator> +output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement); +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, <span class= +"keyword">typename</span> output_iterator> +output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to + look for invalid UTF-8 sequences.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to look + for invalid UTF-8 sequences.<br> + <code>out</code>: An output iterator to the range where the result of replacement + is stored.<br> + <code>replacement</code>: A Unicode code point for the replacement marker. The + version without this parameter assumes the value <code>0xfffd</code><br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the UTF-8 string with replaced invalid sequences. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span> invalid_sequence[] = <span class= +"literal">"a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"</span>; +vector<<span class="keyword">char</span>> replace_invalid_result; +replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), <span + class="literal">'?'</span>); +bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end()); +assert (bvalid); +<span class="keyword">char</span>* fixed_invalid_sequence = <span class= +"literal">"a????z"</span>; +assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence)); +</pre> + <p> + <code>replace_invalid</code> does not perform in-place replacement of invalid + sequences. Rather, it produces a copy of the original string with the invalid + sequences replaced with a replacement marker. Therefore, <code>out</code> must not + be in the <code>[start, end]</code> range. + </p> + <p> + If <code>end</code> does not point to the past-of-end of a UTF-8 sequence, a + <code>utf8::not_enough_room</code> exception is thrown. + </p> + <h4> + utf8::is_bom + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM) + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +<span class="keyword">bool</span> is_bom (octet_iterator it); +</pre> + <p> + <code>it</code>: beginning of the 3-octet sequence to check<br> + <span class="return_value">Return value</span>: <code>true</code> if the sequence + is UTF-8 byte order mark; <code>false</code> if not. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">unsigned char</span> byte_order_mark[] = {<span class= +"literal">0xef</span>, <span class="literal">0xbb</span>, <span class= +"literal">0xbf</span>}; +<span class="keyword">bool</span> bbom = is_bom(byte_order_mark); +assert (bbom == <span class="literal">true</span>); +</pre> + <p> + The typical use of this function is to check the first three bytes of a file. If + they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 + encoded text. + </p> + <h3 id="typesutf8"> + Types From utf8 Namespace + </h3> + <h4> + utf8::iterator + </h4> + <p class="version"> + Available in version 2.0 and later. + </p> + <p> + Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. + </p> +<pre> +<span class="keyword">template</span> <<span class="keyword">typename</span> octet_iterator> +<span class="keyword">class</span> iterator; +</pre> + + <h5>Member functions</h5> + <dl> + <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is + constructed with its default constructor. + <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end);</code> <dd> a constructor + that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code> + and sets the range in which the iterator is considered valid. + <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the + underlying <code>octet_iterator</code>. + <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence + the underlying <code>octet_iterator</code> is pointing to and returns the code point. + <dt><code><span class="keyword">bool operator</span> == (const iterator& rhs) + <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span> + if the two underlaying iterators are equal. + <dt><code><span class="keyword">bool operator</span> != (const iterator& rhs) + <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span> + if the two underlaying iterators are not equal. + <dt><code>iterator& <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves + the iterator to the next UTF-8 encoded code point. + <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd> + the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. + <dt><code>iterator& <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. + <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd> + the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. + </dl> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>; +utf8::iterator<<span class="keyword">char</span>*> it(threechars, threechars, threechars + <span class="literal">9</span>); +utf8::iterator<<span class="keyword">char</span>*> it2 = it; +assert (it2 == it); +assert (*it == <span class="literal">0x10346</span>); +assert (*(++it) == <span class="literal">0x65e5</span>); +assert ((*it++) == <span class="literal">0x65e5</span>); +assert (*it == <span class="literal">0x0448</span>); +assert (it != it2); +utf8::iterator<<span class="keyword">char</span>*> endit (threechars + <span class="literal">9</span>, threechars, threechars + <span class="literal">9</span>); +assert (++it == endit); +assert (*(--it) == <span class="literal">0x0448</span>); +assert ((*it--) == <span class="literal">0x0448</span>); +assert (*it == <span class="literal">0x65e5</span>); +assert (--it == utf8::iterator<<span class="keyword">char</span>*>(threechars, threechars, threechars + <span class="literal">9</span>)); +assert (*it == <span class="literal">0x10346</span>); +</pre> + <p> + The purpose of <code>utf8::iterator</code> adapter is to enable easy iteration as well as the use of STL + algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of + <code>utf8::next()</code> and <code>utf8::prior()</code> functions. + </p> + <p> + Note that <code>utf8::iterator</code> adapter is a checked iterator. It operates on the range specified in + the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators + require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically, + the range will be determined by sequence container functions <code>begin</code> and <code>end</code>, i.e.: + </p> +<pre> +std::string s = <span class="literal">"example"</span>; +utf8::iterator i (s.begin(), s.begin(), s.end()); +</pre> + <h3 id="fununchecked"> + Functions From utf8::unchecked Namespace + </h3> + <h4> + utf8::unchecked::append + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +octet_iterator append(uint32_t cp, octet_iterator result); + +</pre> + <p> + <code>cp</code>: A 32 bit integer representing a code point to append to the + sequence.<br> + <code>result</code>: An output iterator to the place in the sequence where to + append the code point.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the newly appended sequence. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span +class="literal">0</span>,<span class="literal">0</span>,<span class= +"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>}; +<span class="keyword">unsigned char</span>* end = unchecked::append(<span class= +"literal">0x0448</span>, u); +assert (u[<span class="literal">0</span>] == <span class= +"literal">0xd1</span> && u[<span class="literal">1</span>] == <span class= +"literal">0x88</span> && u[<span class="literal">2</span>] == <span class= +"literal">0</span> && u[<span class="literal">3</span>] == <span class= +"literal">0</span> && u[<span class="literal">4</span>] == <span class= +"literal">0</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::append</code>. It does not + check for validity of the supplied code point, and may produce an invalid UTF-8 + sequence. + </p> + <h4> + utf8::unchecked::next + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Given the iterator to the beginning of a UTF-8 sequence, it returns the code point + and moves the iterator to the next position. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t next(octet_iterator& it); + +</pre> + <p> + <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + processed UTF-8 code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars; +<span class="keyword">int</span> cp = unchecked::next(w); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars + <span class="literal">3</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::next</code>. It does not + check for validity of the supplied UTF-8 sequence. + </p> + <h4> + utf8::unchecked::peek_next + </h4> + <p class="version"> + Available in version 2.1 and later. + </p> + <p> + Given the iterator to the beginning of a UTF-8 sequence, it returns the code point. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t peek_next(octet_iterator it); + +</pre> + <p> + <code>it</code>: an iterator pointing to the beginning of an UTF-8 + encoded code point.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + processed UTF-8 code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars; +<span class="keyword">int</span> cp = unchecked::peek_next(w); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars); +</pre> + <p> + This is a faster but less safe version of <code>utf8::peek_next</code>. It does not + check for validity of the supplied UTF-8 sequence. + </p> + <h4> + utf8::unchecked::prior + </h4> + <p class="version"> + Available in version 1.02 and later. + </p> + <p> + Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t prior(octet_iterator& it); + +</pre> + <p> + <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + previous code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>; +<span class="keyword">int</span> cp = unchecked::prior (w); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars); +</pre> + <p> + This is a faster but less safe version of <code>utf8::prior</code>. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. + </p> + <h4> + utf8::unchecked::previous (deprecated, see utf8::unchecked::prior) + </h4> + <p class="version"> + Deprecated in version 1.02 and later. + </p> + <p> + Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +uint32_t previous(octet_iterator& it); + +</pre> + <p> + <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.<br> + <span class="return_value">Return value</span>: the 32 bit representation of the + previous code point. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>; +<span class="keyword">int</span> cp = unchecked::previous (w); +assert (cp == <span class="literal">0x65e5</span>); +assert (w == twochars); +</pre> + <p> + The reason this function is deprecated is just the consistency with the "checked" + versions, where <code>prior</code> should be used instead of <code>previous</code>. + In fact, <code>unchecked::previous</code> behaves exactly the same as <code> + unchecked::prior</code> + </p> + <p> + This is a faster but less safe version of <code>utf8::previous</code>. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. + </p> + <h4> + utf8::unchecked::advance + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Advances an iterator by the specified number of code points within an UTF-8 + sequence. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, typename distance_type> +<span class="keyword">void</span> advance (octet_iterator& it, distance_type n); + +</pre> + <p> + <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.<br> + <code>n</code>: a positive integer that shows how many code points we want to + advance.<br> + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +<span class="keyword">char</span>* w = twochars; +unchecked::advance (w, <span class="literal">2</span>); +assert (w == twochars + <span class="literal">5</span>); +</pre> + <p> + This function works only "forward". In case of a negative <code>n</code>, there is + no effect. + </p> + <p> + This is a faster but less safe version of <code>utf8::advance</code>. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. + </p> + <h4> + utf8::unchecked::distance + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator> +<span class= +"keyword">typename</span> std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last); +</pre> + <p> + <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br> + <code>last</code>: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.<br> + <span class="return_value">Return value</span> the distance between the iterators, + in code points. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +size_t dist = utf8::unchecked::distance(twochars, twochars + <span class= +"literal">5</span>); +assert (dist == <span class="literal">2</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::distance</code>. It does not + check for validity of the supplied UTF-8 sequence. + </p> + <h4> + utf8::unchecked::utf16to8 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts a UTF-16 encoded string to UTF-8. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> u16bit_iterator, <span class= +"keyword">typename</span> octet_iterator> +octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-8 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-8 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">unsigned short</span> utf16string[] = {<span class= +"literal">0x41</span>, <span class="literal">0x0448</span>, <span class= +"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class= +"literal">0xdd1e</span>}; +vector<<span class="keyword">unsigned char</span>> utf8result; +unchecked::utf16to8(utf16string, utf16string + <span class= +"literal">5</span>, back_inserter(utf8result)); +assert (utf8result.size() == <span class="literal">10</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::utf16to8</code>. It does not + check for validity of the supplied UTF-16 sequence. + </p> + <h4> + utf8::unchecked::utf8to16 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts an UTF-8 encoded string to UTF-16 + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> u16bit_iterator, typename octet_iterator> +u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> <code>end</code>: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-16 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-16 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span> utf8_with_surrogates[] = <span class= +"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>; +vector <<span class="keyword">unsigned short</span>> utf16result; +unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class= +"literal">9</span>, back_inserter(utf16result)); +assert (utf16result.size() == <span class="literal">4</span>); +assert (utf16result[<span class="literal">2</span>] == <span class= +"literal">0xd834</span>); +assert (utf16result[<span class="literal">3</span>] == <span class= +"literal">0xdd1e</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::utf8to16</code>. It does not + check for validity of the supplied UTF-8 sequence. + </p> + <h4> + utf8::unchecked::utf32to8 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts a UTF-32 encoded string to UTF-8. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, <span class= +"keyword">typename</span> u32bit_iterator> +octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-8 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-8 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">int</span> utf32string[] = {<span class= +"literal">0x448</span>, <span class="literal">0x65e5</span>, <span class= +"literal">0x10346</span>, <span class="literal">0</span>}; +vector<<span class="keyword">unsigned char</span>> utf8result; +utf32to8(utf32string, utf32string + <span class= +"literal">3</span>, back_inserter(utf8result)); +assert (utf8result.size() == <span class="literal">9</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::utf32to8</code>. It does not + check for validity of the supplied UTF-32 sequence. + </p> + <h4> + utf8::unchecked::utf8to32 + </h4> + <p class="version"> + Available in version 1.0 and later. + </p> + <p> + Converts a UTF-8 encoded string to UTF-32. + </p> +<pre> +<span class="keyword">template</span> <<span class= +"keyword">typename</span> octet_iterator, typename u32bit_iterator> +u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result); + +</pre> + <p> + <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.<br> + <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.<br> + <code>result</code>: an output iterator to the place in the UTF-32 string where to + append the result of conversion.<br> + <span class="return_value">Return value</span>: An iterator pointing to the place + after the appended UTF-32 string. + </p> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* twochars = <span class= +"literal">"\xe6\x97\xa5\xd1\x88"</span>; +vector<<span class="keyword">int</span>> utf32result; +unchecked::utf8to32(twochars, twochars + <span class= +"literal">5</span>, back_inserter(utf32result)); +assert (utf32result.size() == <span class="literal">2</span>); +</pre> + <p> + This is a faster but less safe version of <code>utf8::utf8to32</code>. It does not + check for validity of the supplied UTF-8 sequence. + </p> + <h3 id="typesunchecked"> + Types From utf8::unchecked Namespace + </h3> + <h4> + utf8::iterator + </h4> + <p class="version"> + Available in version 2.0 and later. + </p> + <p> + Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. + </p> +<pre> +<span class="keyword">template</span> <<span class="keyword">typename</span> octet_iterator> +<span class="keyword">class</span> iterator; +</pre> + + <h5>Member functions</h5> + <dl> + <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is + constructed with its default constructor. + <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator& octet_it); + </code> <dd> a constructor + that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code> + <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the + underlying <code>octet_iterator</code>. + <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence + the underlying <code>octet_iterator</code> is pointing to and returns the code point. + <dt><code><span class="keyword">bool operator</span> == (const iterator& rhs) + <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span> + if the two underlaying iterators are equal. + <dt><code><span class="keyword">bool operator</span> != (const iterator& rhs) + <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span> + if the two underlaying iterators are not equal. + <dt><code>iterator& <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves + the iterator to the next UTF-8 encoded code point. + <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd> + the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. + <dt><code>iterator& <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. + <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd> + the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. + </dl> + <p> + Example of use: + </p> +<pre> +<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>; +utf8::unchecked::iterator<<span class="keyword">char</span>*> un_it(threechars); +utf8::unchecked::iterator<<span class="keyword">char</span>*> un_it2 = un_it; +assert (un_it2 == un_it); +assert (*un_it == <span class="literal">0x10346</span>); +assert (*(++un_it) == <span class="literal">0x65e5</span>); +assert ((*un_it++) == <span class="literal">0x65e5</span>); +assert (*un_it == <span class="literal">0x0448</span>); +assert (un_it != un_it2); +utf8::::unchecked::iterator<<span class="keyword">char</span>*> un_endit (threechars + <span class="literal">9</span>); +assert (++un_it == un_endit); +assert (*(--un_it) == <span class="literal">0x0448</span>); +assert ((*un_it--) == <span class="literal">0x0448</span>); +assert (*un_it == <span class="literal">0x65e5</span>); +assert (--un_it == utf8::unchecked::iterator<<span class="keyword">char</span>*>(threechars)); +assert (*un_it == <span class="literal">0x10346</span>); +</pre> + <p> + This is an unchecked version of <code>utf8::iterator</code>. It is faster in many cases, but offers + no validity or range checks. + </p> + <h2 id="points"> + Points of interest + </h2> + <h4> + Design goals and decisions + </h4> + <p> + The library was designed to be: + </p> + <ol> + <li> + Generic: for better or worse, there are many C++ string classes out there, and + the library should work with as many of them as possible. + </li> + <li> + Portable: the library should be portable both accross different platforms and + compilers. The only non-portable code is a small section that declares unsigned + integers of different sizes: three typedefs. They can be changed by the users of + the library if they don't match their platform. The default setting should work + for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. + </li> + <li> + Lightweight: follow the "pay only for what you use" guidline. + </li> + <li> + Unintrusive: avoid forcing any particular design or even programming style on the + user. This is a library, not a framework. + </li> + </ol> + <h4> + Alternatives + </h4> + <p> + In case you want to look into other means of working with UTF-8 strings from C++, + here is the list of solutions I am aware of: + </p> + <ol> + <li> + <a href="http://icu.sourceforge.net/">ICU Library</a>. It is very powerful, + complete, feature-rich, mature, and widely used. Also big, intrusive, + non-generic, and doesn't play well with the Standard Library. I definitelly + recommend looking at ICU even if you don't plan to use it. + </li> + <li> + <a href= + "http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html">Glib::ustring</a>. + A class specifically made to work with UTF-8 strings, and also feel like + <code>std::string</code>. If you prefer to have yet another string class in your + code, it may be worth a look. Be aware of the licensing issues, though. + </li> + <li> + Platform dependent solutions: Windows and POSIX have functions to convert strings + from one encoding to another. That is only a subset of what my library offers, + but if that is all you need it may be good enough, especially given the fact that + these functions are mature and tested in production. + </li> + </ol> + <h2 id="conclusion"> + Conclusion + </h2> + <p> + Until Unicode becomes officially recognized by the C++ Standard Library, we need to + use other means to work with UTF-8 strings. Template functions I describe in this + article may be a good step in this direction. + </p> + <h2 id="links"> + Links + </h2> + <ol> + <li> + <a href="http://www.unicode.org/">The Unicode Consortium</a>. + </li> + <li> + <a href="http://icu.sourceforge.net/">ICU Library</a>. + </li> + <li> + <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8 at Wikipedia</a> + </li> + <li> + <a href="http://www.cl.cam.ac.uk/~mgk25/unicode.html">UTF-8 and Unicode FAQ for + Unix/Linux</a> + </li> + </ol> + </body> +</html> diff --git a/externals/utf8cpp/include/utf8.h b/externals/utf8cpp/include/utf8.h new file mode 100644 index 00000000000..cc463cb82d5 --- /dev/null +++ b/externals/utf8cpp/include/utf8.h @@ -0,0 +1,35 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "utf8/checked.h" +#include "utf8/unchecked.h" + +#endif // header guard + diff --git a/externals/utf8cpp/include/utf8/checked.h b/externals/utf8cpp/include/utf8/checked.h new file mode 100644 index 00000000000..86204eae3ea --- /dev/null +++ b/externals/utf8cpp/include/utf8/checked.h @@ -0,0 +1,319 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" +#include <stdexcept> + +namespace utf8 +{ + // Exceptions that may be thrown from the library functions. + class invalid_code_point : public std::exception { + uint32_t cp; + public: + invalid_code_point(uint32_t cp) : cp(cp) {} + virtual const char* what() const throw() { return "Invalid code point"; } + uint32_t code_point() const {return cp;} + }; + + class invalid_utf8 : public std::exception { + uint8_t u8; + public: + invalid_utf8 (uint8_t u) : u8(u) {} + virtual const char* what() const throw() { return "Invalid UTF-8"; } + uint8_t utf8_octet() const {return u8;} + }; + + class invalid_utf16 : public std::exception { + uint16_t u16; + public: + invalid_utf16 (uint16_t u) : u16(u) {} + virtual const char* what() const throw() { return "Invalid UTF-16"; } + uint16_t utf16_word() const {return u16;} + }; + + class not_enough_room : public std::exception { + public: + virtual const char* what() const throw() { return "Not enough space"; } + }; + + /// The library API - functions intended to be called by the users + + template <typename octet_iterator, typename output_iterator> + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = internal::validate_next(start, end); + switch (err_code) { + case internal::OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: + append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (internal::is_trail(*start) && start != end) + ++start; + break; + } + } + return out; + } + + template <typename octet_iterator, typename output_iterator> + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = internal::mask16(0xfffd); + return replace_invalid(start, end, out, replacement_marker); + } + + template <typename octet_iterator> + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (!internal::is_code_point_valid(cp)) + throw invalid_code_point(cp); + + if (cp < 0x80) // one octet + *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp <= internal::CODE_POINT_MAX) { // four octets + *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0); + *(result++) = static_cast<uint8_t>(((cp >> 12)& 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else + throw invalid_code_point(cp); + + return result; + } + + template <typename octet_iterator> + uint32_t next(octet_iterator& it, octet_iterator end) + { + uint32_t cp = 0; + internal::utf_error err_code = internal::validate_next(it, end, &cp); + switch (err_code) { + case internal::OK : + break; + case internal::NOT_ENOUGH_ROOM : + throw not_enough_room(); + case internal::INVALID_LEAD : + case internal::INCOMPLETE_SEQUENCE : + case internal::OVERLONG_SEQUENCE : + throw invalid_utf8(*it); + case internal::INVALID_CODE_POINT : + throw invalid_code_point(cp); + } + return cp; + } + + template <typename octet_iterator> + uint32_t peek_next(octet_iterator it, octet_iterator end) + { + return next(it, end); + } + + template <typename octet_iterator> + uint32_t prior(octet_iterator& it, octet_iterator start) + { + octet_iterator end = it; + while (internal::is_trail(*(--it))) + if (it < start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return next(temp, end); + } + + /// Deprecated in versions that include "prior" + template <typename octet_iterator> + uint32_t previous(octet_iterator& it, octet_iterator pass_start) + { + octet_iterator end = it; + while (internal::is_trail(*(--it))) + if (it == pass_start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return next(temp, end); + } + + template <typename octet_iterator, typename distance_type> + void advance (octet_iterator& it, distance_type n, octet_iterator end) + { + for (distance_type i = 0; i < n; ++i) + next(it, end); + } + + template <typename octet_iterator> + typename std::iterator_traits<octet_iterator>::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits<octet_iterator>::difference_type dist; + for (dist = 0; first < last; ++dist) + next(first, last); + return dist; + } + + template <typename u16bit_iterator, typename octet_iterator> + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = internal::mask16(*start++); + // Take care of surrogate pairs first + if (internal::is_surrogate(cp)) { + if (start != end) { + uint32_t trail_surrogate = internal::mask16(*start++); + if (trail_surrogate >= internal::TRAIL_SURROGATE_MIN && trail_surrogate <= internal::TRAIL_SURROGATE_MAX) + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + else + throw invalid_utf16(static_cast<uint16_t>(trail_surrogate)); + } + else + throw invalid_utf16(static_cast<uint16_t>(*start)); + + } + result = append(cp, result); + } + return result; + } + + template <typename u16bit_iterator, typename octet_iterator> + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = next(start, end); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast<uint16_t>(cp); + } + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = append(*(start++), result); + + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = next(start, end); + + return result; + } + + // The iterator class + template <typename octet_iterator> + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: + iterator () {}; + explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end) : + it(octet_it), range_start(range_start), range_end(range_end) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return next(temp, range_end); + } + bool operator == (const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + next(it, range_end); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + next(it, range_end); + return temp; + } + iterator& operator -- () + { + prior(it, range_start); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + prior(it, range_start); + return temp; + } + }; // class iterator + +} // namespace utf8 + +#endif //header guard + + + diff --git a/externals/utf8cpp/include/utf8/core.h b/externals/utf8cpp/include/utf8/core.h new file mode 100644 index 00000000000..389dd3e8ca5 --- /dev/null +++ b/externals/utf8cpp/include/utf8/core.h @@ -0,0 +1,269 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include <iterator> + +// use Trinity core types +#include "Platform/Define.h" + +namespace utf8 +{ + // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers + // You may need to change them to match your system. + // These typedefs have the same names as ones from cstdint, or boost/cstdint + + /* use Trinity alternatives + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + */ + typedef uint8 uint8_t; + typedef uint16 uint16_t; + typedef uint32 uint32_t; + +// Helper code - not intended to be directly called by the library users. May be changed at any time +namespace internal +{ + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); + const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template<typename octet_type> + inline uint8_t mask8(octet_type oc) + { + return static_cast<uint8_t>(0xff & oc); + } + template<typename u16_type> + inline uint16_t mask16(u16_type oc) + { + return static_cast<uint16_t>(0xffff & oc); + } + template<typename octet_type> + inline bool is_trail(octet_type oc) + { + return ((mask8(oc) >> 6) == 0x2); + } + + template <typename u16> + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template <typename u32> + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !is_surrogate(cp) && cp != 0xfffe && cp != 0xffff); + } + + template <typename octet_iterator> + inline typename std::iterator_traits<octet_iterator>::difference_type + sequence_length(octet_iterator lead_it) + { + uint8_t lead = mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; + } + + enum utf_error {OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + + template <typename octet_iterator> + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t* code_point) + { + uint32_t cp = mask8(*it); + // Check the lead octet + typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type; + octet_difference_type length = sequence_length(it); + + // "Shortcut" for ASCII characters + if (length == 1) { + if (end - it > 0) { + if (code_point) + *code_point = cp; + ++it; + return OK; + } + else + return NOT_ENOUGH_ROOM; + } + + // Do we have enough memory? + if (std::distance(it, end) < length) + return NOT_ENOUGH_ROOM; + + // Check trail octets and calculate the code point + switch (length) { + case 0: + return INVALID_LEAD; + break; + case 2: + if (is_trail(*(++it))) { + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + } + else { + --it; + return INCOMPLETE_SEQUENCE; + } + break; + case 3: + if (is_trail(*(++it))) { + cp = ((cp << 12) & 0xffff) + ((mask8(*it) << 6) & 0xfff); + if (is_trail(*(++it))) { + cp += (*it) & 0x3f; + } + else { + std::advance(it, -2); + return INCOMPLETE_SEQUENCE; + } + } + else { + --it; + return INCOMPLETE_SEQUENCE; + } + break; + case 4: + if (is_trail(*(++it))) { + cp = ((cp << 18) & 0x1fffff) + ((mask8(*it) << 12) & 0x3ffff); + if (is_trail(*(++it))) { + cp += (mask8(*it) << 6) & 0xfff; + if (is_trail(*(++it))) { + cp += (*it) & 0x3f; + } + else { + std::advance(it, -3); + return INCOMPLETE_SEQUENCE; + } + } + else { + std::advance(it, -2); + return INCOMPLETE_SEQUENCE; + } + } + else { + --it; + return INCOMPLETE_SEQUENCE; + } + break; + } + // Is the code point valid? + if (!is_code_point_valid(cp)) { + for (octet_difference_type i = 0; i < length - 1; ++i) + --it; + return INVALID_CODE_POINT; + } + + if (code_point) + *code_point = cp; + + if (cp < 0x80) { + if (length != 1) { + std::advance(it, -(length-1)); + return OVERLONG_SEQUENCE; + } + } + else if (cp < 0x800) { + if (length != 2) { + std::advance(it, -(length-1)); + return OVERLONG_SEQUENCE; + } + } + else if (cp < 0x10000) { + if (length != 3) { + std::advance(it, -(length-1)); + return OVERLONG_SEQUENCE; + } + } + + ++it; + return OK; + } + + template <typename octet_iterator> + inline utf_error validate_next(octet_iterator& it, octet_iterator end) { + return validate_next(it, end, 0); + } + +} // namespace internal + + /// The library API - functions intended to be called by the users + + // Byte order mark + const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + + template <typename octet_iterator> + octet_iterator find_invalid(octet_iterator start, octet_iterator end) + { + octet_iterator result = start; + while (result != end) { + internal::utf_error err_code = internal::validate_next(result, end); + if (err_code != internal::OK) + return result; + } + return result; + } + + template <typename octet_iterator> + inline bool is_valid(octet_iterator start, octet_iterator end) + { + return (find_invalid(start, end) == end); + } + + template <typename octet_iterator> + inline bool is_bom (octet_iterator it) + { + return ( + (internal::mask8(*it++)) == bom[0] && + (internal::mask8(*it++)) == bom[1] && + (internal::mask8(*it)) == bom[2] + ); + } +} // namespace utf8 + +#endif // header guard + + + diff --git a/externals/utf8cpp/include/utf8/unchecked.h b/externals/utf8cpp/include/utf8/unchecked.h new file mode 100644 index 00000000000..fc7267d1b98 --- /dev/null +++ b/externals/utf8cpp/include/utf8/unchecked.h @@ -0,0 +1,229 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" + +namespace utf8 +{ + namespace unchecked + { + template <typename octet_iterator> + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (cp < 0x80) // one octet + *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0); + *(result++) = static_cast<uint8_t>((cp >> 6) & 0x3f | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0); + *(result++) = static_cast<uint8_t>((cp >> 12)& 0x3f | 0x80); + *(result++) = static_cast<uint8_t>((cp >> 6) & 0x3f | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + return result; + } + + template <typename octet_iterator> + uint32_t next(octet_iterator& it) + { + uint32_t cp = internal::mask8(*it); + typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it); + switch (length) { + case 1: + break; + case 2: + it++; + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + break; + case 3: + ++it; + cp = ((cp << 12) & 0xffff) + ((internal::mask8(*it) << 6) & 0xfff); + ++it; + cp += (*it) & 0x3f; + break; + case 4: + ++it; + cp = ((cp << 18) & 0x1fffff) + ((internal::mask8(*it) << 12) & 0x3ffff); + ++it; + cp += (internal::mask8(*it) << 6) & 0xfff; + ++it; + cp += (*it) & 0x3f; + break; + } + ++it; + return cp; + } + + template <typename octet_iterator> + uint32_t peek_next(octet_iterator it) + { + return next(it); + } + + template <typename octet_iterator> + uint32_t prior(octet_iterator& it) + { + while (internal::is_trail(*(--it))) ; + octet_iterator temp = it; + return next(temp); + } + + // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) + template <typename octet_iterator> + inline uint32_t previous(octet_iterator& it) + { + return prior(it); + } + + template <typename octet_iterator, typename distance_type> + void advance (octet_iterator& it, distance_type n) + { + for (distance_type i = 0; i < n; ++i) + next(it); + } + + template <typename octet_iterator> + typename std::iterator_traits<octet_iterator>::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits<octet_iterator>::difference_type dist; + for (dist = 0; first < last; ++dist) + next(first); + return dist; + } + + template <typename u16bit_iterator, typename octet_iterator> + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = internal::mask16(*start++); + // Take care of surrogate pairs first + if (internal::is_surrogate(cp)) { + uint32_t trail_surrogate = internal::mask16(*start++); + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + } + result = append(cp, result); + } + return result; + } + + template <typename u16bit_iterator, typename octet_iterator> + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = next(start); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast<uint16_t>(cp); + } + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = append(*(start++), result); + + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = next(start); + + return result; + } + + // The iterator class + template <typename octet_iterator> + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it; + public: + iterator () {}; + explicit iterator (const octet_iterator& octet_it): it(octet_it) {} + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return next(temp); + } + bool operator == (const iterator& rhs) const + { + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + std::advance(it, internal::sequence_length(it)); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + std::advance(it, internal::sequence_length(it)); + return temp; + } + iterator& operator -- () + { + prior(it); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + prior(it); + return temp; + } + }; // class iterator + + } // namespace utf8::unchecked +} // namespace utf8 + + +#endif // header guard + + diff --git a/externals/vld/delme b/externals/vld/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/vld/delme +++ /dev/null diff --git a/externals/vld/vld.h b/externals/vld/vld.h new file mode 100644 index 00000000000..72bebd8c4f6 --- /dev/null +++ b/externals/vld/vld.h @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Id: vld.h,v 1.27 2006/11/12 18:09:20 dmouldin Exp $ +// +// Visual Leak Detector (Version 1.9d) - Import Library Header +// Copyright (c) 2006 Dan Moulding +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// +// See COPYING.txt for the full terms of the GNU Lesser General Public License. +// +//////////////////////////////////////////////////////////////////////////////// + +//#pragma once +#ifndef _VLD_H_ +#define _VLD_H_ + +#ifdef _DEBUG + +#pragma comment(lib, "vld.lib") + +// Force a symbolic reference to the global VisualLeakDetector class object from +// the DLL. This enusres that the DLL is loaded and linked with the program, +// even if no code otherwise imports any of the DLL's exports. +#pragma comment(linker, "/include:__imp_?vld@@3VVisualLeakDetector@@A") + +//////////////////////////////////////////////////////////////////////////////// +// +// Visual Leak Detector APIs +// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// VLDDisable - Disables Visual Leak Detector's memory leak detection at +// runtime. If memory leak detection is already disabled, then calling this +// function has no effect. +// +// Note: In multithreaded programs, this function operates on a per-thread +// basis. In other words, if you call this function from one thread, then +// memory leak detection is only disabled for that thread. If memory leak +// detection is enabled for other threads, then it will remain enabled for +// those other threads. It was designed to work this way to insulate you, +// the programmer, from having to ensure thread synchronization when calling +// VLDEnable() and VLDDisable(). Without this, calling these two functions +// unsychronized could result in unpredictable and unintended behavior. +// But this also means that if you want to disable memory leak detection +// process-wide, then you need to call this function from every thread in +// the process. +// +// Return Value: +// +// None. +// + +__declspec(dllimport) void VLDDisable (); + +// VLDEnable - Enables Visual Leak Detector's memory leak detection at runtime. +// If memory leak detection is already enabled, which it is by default, then +// calling this function has no effect. +// +// Note: In multithreaded programs, this function operates on a per-thread +// basis. In other words, if you call this function from one thread, then +// memory leak detection is only enabled for that thread. If memory leak +// detection is disabled for other threads, then it will remain disabled for +// those other threads. It was designed to work this way to insulate you, +// the programmer, from having to ensure thread synchronization when calling +// VLDEnable() and VLDDisable(). Without this, calling these two functions +// unsychronized could result in unpredictable and unintended behavior. +// But this also means that if you want to enable memory leak detection +// process-wide, then you need to call this function from every thread in +// the process. +// +// Return Value: +// +// None. +// + +__declspec(dllimport) void VLDEnable (); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#else // !_DEBUG + +#define VLDEnable() +#define VLDDisable() + +#endif // _DEBUG + +#endif // _VLD_H_ + diff --git a/externals/zlib/win/VC90/zlib.vcproj b/externals/zlib/win/VC90/zlib.vcproj index 8e89623915c..1b63254aa23 100644 --- a/externals/zlib/win/VC90/zlib.vcproj +++ b/externals/zlib/win/VC90/zlib.vcproj @@ -22,7 +22,7 @@ <Configuration Name="Debug|Win32" OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" - IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)" ConfigurationType="4" CharacterSet="2" > @@ -90,7 +90,7 @@ <Configuration Name="Debug|x64" OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" - IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)" ConfigurationType="4" CharacterSet="2" > @@ -159,7 +159,7 @@ <Configuration Name="Release|Win32" OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" - IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)" ConfigurationType="4" CharacterSet="2" > @@ -225,7 +225,7 @@ <Configuration Name="Release|x64" OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)" - IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)" + IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)" ConfigurationType="4" CharacterSet="2" > |
