forked from aya/aya
Initial commit
This commit is contained in:
161
engine/3d/CMakeLists.txt
Normal file
161
engine/3d/CMakeLists.txt
Normal file
@@ -0,0 +1,161 @@
|
||||
add_library(3D STATIC)
|
||||
|
||||
set(SOURCES
|
||||
src/AABox.cpp
|
||||
src/AABox.hpp
|
||||
src/AABSPTree.hpp
|
||||
src/Array.hpp
|
||||
src/BinaryFormat.hpp
|
||||
src/BinaryInput.cpp
|
||||
src/BinaryInput.hpp
|
||||
src/BinaryOutput.cpp
|
||||
src/BinaryOutput.hpp
|
||||
src/BoundsTrait.hpp
|
||||
src/Box.cpp
|
||||
src/Box.hpp
|
||||
src/BumpMapPreprocess.hpp
|
||||
src/Capsule.cpp
|
||||
src/Capsule.hpp
|
||||
src/CollisionDetection.cpp
|
||||
src/CollisionDetection.hpp
|
||||
src/Color1.hpp
|
||||
src/Color1uint8.hpp
|
||||
src/Color3.cpp
|
||||
src/Color3.hpp
|
||||
src/Color3uint8.cpp
|
||||
src/Color3uint8.hpp
|
||||
src/Color4.cpp
|
||||
src/Color4.hpp
|
||||
src/Color4uint8.cpp
|
||||
src/Color4uint8.hpp
|
||||
src/CompactCFrame.hpp
|
||||
src/Cone.hpp
|
||||
src/constants.hpp
|
||||
src/ConvexPolyhedron.hpp
|
||||
src/CoordinateFrame.cpp
|
||||
src/CoordinateFrame.hpp
|
||||
src/Crypto.cpp
|
||||
src/Crypto.hpp
|
||||
src/Cylinder.cpp
|
||||
src/Cylinder.hpp
|
||||
src/G3DDebug.hpp
|
||||
src/debugAssert.cpp
|
||||
src/debugAssert.hpp
|
||||
src/debugPrintf.hpp
|
||||
src/Draw.cpp
|
||||
src/Draw.hpp
|
||||
src/DrawAdorn.cpp
|
||||
src/DrawAdorn.hpp
|
||||
src/DrawPrimitives.hpp
|
||||
src/enumclass.hpp
|
||||
src/EqualsTrait.hpp
|
||||
src/fileutils.cpp
|
||||
src/fileutils.hpp
|
||||
src/format.cpp
|
||||
src/format.hpp
|
||||
src/Frustum.cpp
|
||||
src/Frustum.hpp
|
||||
src/g3derror.hpp
|
||||
src/g3dfnmatch.cpp
|
||||
src/g3dfnmatch.hpp
|
||||
src/G3DGameUnits.hpp
|
||||
src/g3dmath.cpp
|
||||
src/g3dmath.hpp
|
||||
src/GCamera.cpp
|
||||
src/GCamera.hpp
|
||||
src/GImage_bayer.cpp
|
||||
src/GImage_bmp.cpp
|
||||
src/GImage_jpeg.cpp
|
||||
src/GImage_jxl.cpp
|
||||
src/GImage_png.cpp
|
||||
src/GImage_tga.cpp
|
||||
src/GImage.cpp
|
||||
src/GImage.hpp
|
||||
src/GLight.cpp
|
||||
src/GLight.hpp
|
||||
src/HandleType.hpp
|
||||
src/HashTrait.hpp
|
||||
src/HitTest.cpp
|
||||
src/HitTest.hpp
|
||||
src/Image1.hpp
|
||||
src/LightingParameters.cpp
|
||||
src/LightingParameters.hpp
|
||||
src/Line.cpp
|
||||
src/Line.hpp
|
||||
src/LineSegment.cpp
|
||||
src/LineSegment.hpp
|
||||
src/Map2D.hpp
|
||||
src/Matrix2.hpp
|
||||
src/Matrix3.cpp
|
||||
src/Matrix3.hpp
|
||||
src/Matrix4.cpp
|
||||
src/Matrix4.hpp
|
||||
src/MemoryManager.cpp
|
||||
src/MemoryManager.hpp
|
||||
src/MeshAlg.hpp
|
||||
src/MeshBuilder.hpp
|
||||
src/ParseError.hpp
|
||||
src/PhysicsFrame.cpp
|
||||
src/PhysicsFrame.hpp
|
||||
src/Plane.cpp
|
||||
src/Plane.hpp
|
||||
src/platform.hpp
|
||||
src/PositionTrait.hpp
|
||||
src/Quat.cpp
|
||||
src/Quat.hpp
|
||||
src/Random.cpp
|
||||
src/Random.hpp
|
||||
src/Ray.cpp
|
||||
src/Ray.hpp
|
||||
src/RbxCamera.cpp
|
||||
src/RbxCamera.hpp
|
||||
src/RbxRay.cpp
|
||||
src/RbxRay.hpp
|
||||
src/RbxTime.hpp
|
||||
src/Rect2D.hpp
|
||||
src/Set.hpp
|
||||
src/SmallArray.hpp
|
||||
src/Sphere.cpp
|
||||
src/Sphere.hpp
|
||||
src/spline.hpp
|
||||
src/stringutils.cpp
|
||||
src/stringutils.hpp
|
||||
src/System.cpp
|
||||
src/System.hpp
|
||||
src/Table.hpp
|
||||
src/Triangle.cpp
|
||||
src/Triangle.hpp
|
||||
src/uint128.cpp
|
||||
src/uint128.hpp
|
||||
src/units.hpp
|
||||
src/UprightFrame.cpp
|
||||
src/UprightFrame.hpp
|
||||
src/Vector2.cpp
|
||||
src/Vector2.hpp
|
||||
src/Vector2int16.cpp
|
||||
src/Vector2int16.hpp
|
||||
src/Vector3.cpp
|
||||
src/Vector3.hpp
|
||||
src/Vector3int16.cpp
|
||||
src/Vector3int16.hpp
|
||||
src/Vector3int32.hpp
|
||||
src/Vector4.cpp
|
||||
src/Vector4.hpp
|
||||
src/Vector4int8.hpp
|
||||
src/vectorMath.hpp
|
||||
src/WrapMode.hpp
|
||||
)
|
||||
|
||||
target_sources(3D PRIVATE ${SOURCES})
|
||||
|
||||
target_include_directories(3D
|
||||
PUBLIC
|
||||
src
|
||||
PRIVATE
|
||||
${THIRD_PARTY_DIR}/BulletPhysics/src
|
||||
${ENGINE_DIR}/app/src
|
||||
${ENGINE_DIR}/core/src
|
||||
${ENGINE_DIR}/gfx/src
|
||||
)
|
||||
|
||||
target_link_libraries(3D $<TARGET_OBJECTS:Core>)
|
||||
1708
engine/3d/src/AABSPTree.hpp
Normal file
1708
engine/3d/src/AABSPTree.hpp
Normal file
File diff suppressed because it is too large
Load Diff
355
engine/3d/src/AABox.cpp
Normal file
355
engine/3d/src/AABox.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
@file AABox.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2004-01-10
|
||||
@edited 2006-01-11
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "AABox.hpp"
|
||||
#include "Box.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "Sphere.hpp"
|
||||
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
const AABox& AABox::maxFinite()
|
||||
{
|
||||
static const AABox b = AABox(Vector3::minFinite(), Vector3::maxFinite());
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
const AABox& AABox::large()
|
||||
{
|
||||
static const AABox b = AABox(Vector3::minFinite() * 0.5f, Vector3::maxFinite() * 0.5f);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
const AABox& AABox::inf()
|
||||
{
|
||||
static const AABox b = AABox(-Vector3::inf(), Vector3::inf());
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
const AABox& AABox::zero()
|
||||
{
|
||||
static const AABox b = AABox(Vector3::zero(), Vector3::zero());
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void AABox::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;
|
||||
debugAssert(plane.size() < 31);
|
||||
|
||||
childMask = 0;
|
||||
|
||||
const bool finite = (abs(lo.x) < G3D::finf()) && (abs(hi.x) < G3D::finf()) && (abs(lo.y) < G3D::finf()) && (abs(hi.y) < G3D::finf()) &&
|
||||
(abs(lo.z) < G3D::finf()) && (abs(hi.z) < G3D::finf());
|
||||
|
||||
// See if there is one plane for which all of the
|
||||
// vertices are in the negative half space.
|
||||
for (int p = 0; p < 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;
|
||||
debugAssert(plane.size() < 31);
|
||||
|
||||
const bool finite = (abs(lo.x) < G3D::finf()) && (abs(hi.x) < G3D::finf()) && (abs(lo.y) < G3D::finf()) && (abs(hi.y) < G3D::finf()) &&
|
||||
(abs(lo.z) < G3D::finf()) && (abs(hi.z) < G3D::finf());
|
||||
|
||||
// See if there is one plane for which all of the
|
||||
// vertices are in the negative half space.
|
||||
for (int p = 0; p < 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;
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
302
engine/3d/src/AABox.hpp
Normal file
302
engine/3d/src/AABox.hpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
@file AABox.h
|
||||
|
||||
Axis-aligned box class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2004-01-10
|
||||
@edited 2009-02-10
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_AABOX_H
|
||||
#define G3D_AABOX_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "G3DDebug.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Plane.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
An axis-aligned box.
|
||||
*/
|
||||
class AABox
|
||||
{
|
||||
private:
|
||||
friend class Intersect;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
inline bool isFinite() const
|
||||
{
|
||||
return lo.isFinite() && hi.isFinite();
|
||||
}
|
||||
|
||||
inline const Vector3& low() const
|
||||
{
|
||||
return lo;
|
||||
}
|
||||
|
||||
inline const Vector3& high() const
|
||||
{
|
||||
return hi;
|
||||
}
|
||||
|
||||
/**
|
||||
The largest possible finite box.
|
||||
*/
|
||||
static const AABox& maxFinite();
|
||||
|
||||
/** A large finite box. This is smaller than FLT_MAX
|
||||
because it leaves room to add boxes together. */
|
||||
static const AABox& large();
|
||||
|
||||
static const AABox& inf();
|
||||
|
||||
static const AABox& zero();
|
||||
|
||||
/**
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// Adapted from geometrictools.com implementation for AABbox
|
||||
// Real DistPoint3Box3<Real>::GetSquared
|
||||
|
||||
inline float ClosestSqDistanceToAABB(const Vector3& point, const Vector3& center, const Vector3& extents)
|
||||
{
|
||||
Vector3 diff = point - center;
|
||||
|
||||
float sqDistance = 0;
|
||||
float delta;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
float axisdistance = diff[i]; // closest[i] = diff.Dot(mBox->Axis[i]); with axis aligned;
|
||||
if (axisdistance < -extents[i])
|
||||
{
|
||||
delta = axisdistance + extents[i];
|
||||
sqDistance += delta * delta;
|
||||
}
|
||||
|
||||
if (axisdistance > extents[i])
|
||||
{
|
||||
delta = axisdistance - extents[i];
|
||||
sqDistance += delta * delta;
|
||||
}
|
||||
}
|
||||
|
||||
return sqDistance;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::AABox>
|
||||
{
|
||||
static size_t hashCode(const G3D::AABox& key)
|
||||
{
|
||||
return key.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
1434
engine/3d/src/Array.hpp
Normal file
1434
engine/3d/src/Array.hpp
Normal file
File diff suppressed because it is too large
Load Diff
176
engine/3d/src/BinaryFormat.hpp
Normal file
176
engine/3d/src/BinaryFormat.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
@file BinaryFormat.h
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@author 2005-06-03
|
||||
@edited 2005-06-03
|
||||
|
||||
Copyright 2000-2005, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_BINARYFORMAT_H
|
||||
#define G3D_BINARYFORMAT_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
/** 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;
|
||||
}
|
||||
};
|
||||
} // namespace _internal
|
||||
} // namespace G3D
|
||||
|
||||
|
||||
/**
|
||||
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);
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
500
engine/3d/src/BinaryInput.cpp
Normal file
500
engine/3d/src/BinaryInput.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
/**
|
||||
@file BinaryInput.cpp
|
||||
|
||||
@author Morgan McGuire, graphics3d.com
|
||||
Copyright 2001-2007, Morgan McGuire. All rights reserved.
|
||||
|
||||
@created 2001-08-09
|
||||
@edited 2010-03-05
|
||||
|
||||
|
||||
<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 "platform.hpp"
|
||||
#include "BinaryInput.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "fileutils.hpp"
|
||||
#include <zlib.h>
|
||||
#include <stdexcept>
|
||||
#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)
|
||||
{
|
||||
assert(false); // should not be using this
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
setEndian(dataEndian);
|
||||
m_freeBuffer = copyMemory || compressed;
|
||||
|
||||
if (compressed)
|
||||
{
|
||||
// Read the decompressed size from the first 4 bytes
|
||||
m_length = G3D::readUInt32(data, m_swapBytes);
|
||||
|
||||
debugAssert(m_freeBuffer);
|
||||
m_buffer = (uint8*)malloc(m_length); // was: alignedMalloc()
|
||||
|
||||
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*)malloc(m_length); // was: alignedMalloc()
|
||||
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)
|
||||
{
|
||||
|
||||
|
||||
assert(false); // we should not be using this
|
||||
}
|
||||
|
||||
void BinaryInput::decompress()
|
||||
{
|
||||
// Decompress
|
||||
// Use the existing buffer as the source, allocate
|
||||
// a new buffer to use as the destination.
|
||||
assert(false); // we should not be using this
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
free(m_buffer); // was: alignedFree()
|
||||
}
|
||||
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)
|
||||
{
|
||||
assert(false); // we should not be using this
|
||||
std::string dummystring;
|
||||
return dummystring;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static bool isNewline(char c)
|
||||
{
|
||||
return c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
std::string BinaryInput::readStringNewline()
|
||||
{
|
||||
int64 n = 0;
|
||||
|
||||
if ((m_pos + m_alreadyRead + n) < (m_length - 1))
|
||||
{
|
||||
prepareToRead(1);
|
||||
}
|
||||
|
||||
if (((m_pos + m_alreadyRead + n) < (m_length - 1)) && !isNewline(m_buffer[m_pos + n]))
|
||||
{
|
||||
|
||||
++n;
|
||||
while (((m_pos + m_alreadyRead + n) < (m_length - 1)) && !isNewline(m_buffer[m_pos + n]))
|
||||
{
|
||||
|
||||
prepareToRead(1);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string s = readString(n);
|
||||
|
||||
// Consume the newline
|
||||
char firstNLChar = readUInt8();
|
||||
|
||||
// Consume the 2nd newline
|
||||
if (isNewline(m_buffer[m_pos + 1]) && (m_buffer[m_pos + 1] != firstNLChar))
|
||||
{
|
||||
readUInt8();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::string BinaryInput::readStringEven()
|
||||
{
|
||||
std::string x = readString();
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
472
engine/3d/src/BinaryInput.hpp
Normal file
472
engine/3d/src/BinaryInput.hpp
Normal file
@@ -0,0 +1,472 @@
|
||||
/**
|
||||
@file BinaryInput.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-08-09
|
||||
@edited 2010-03-19
|
||||
|
||||
Copyright 2000-2010, 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 "platform.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Color4.hpp"
|
||||
#include "Color3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "G3DDebug.hpp"
|
||||
#include "System.hpp"
|
||||
|
||||
|
||||
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 \r, \r\n, \n\r, \n or the end of the file is encountered. Consumes the newline.*/
|
||||
std::string readStringNewline();
|
||||
|
||||
/**
|
||||
Reads until NULL or the end of the file is encountered.
|
||||
If the string has odd length (including NULL), reads
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
502
engine/3d/src/BinaryOutput.cpp
Normal file
502
engine/3d/src/BinaryOutput.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
/**
|
||||
@file BinaryOutput.cpp
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
Copyright 2002-2010, Morgan McGuire, All rights reserved.
|
||||
|
||||
@created 2002-02-20
|
||||
@edited 2010-03-17
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "BinaryOutput.hpp"
|
||||
#include "fileutils.hpp"
|
||||
#include "stringutils.hpp"
|
||||
#include "Array.hpp"
|
||||
#include <zlib.h>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(G3D_LINUX) || defined(G3D_IOS) || defined(G3D_ANDROID) // Aya
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
// Largest memory buffer that the system will use for writing to
|
||||
// disk. After this (or if the system runs out of memory)
|
||||
// chunks of the file will be dumped to disk.
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
assert(false); // we should not be using this
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
assert(false); // we should not be using this
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
448
engine/3d/src/BinaryOutput.hpp
Normal file
448
engine/3d/src/BinaryOutput.hpp
Normal file
@@ -0,0 +1,448 @@
|
||||
/**
|
||||
@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 "platform.hpp"
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include "Color4.hpp"
|
||||
#include "Color3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "G3DDebug.hpp"
|
||||
#include "BinaryInput.hpp"
|
||||
#include "System.hpp"
|
||||
|
||||
#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
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
21
engine/3d/src/BoundsTrait.hpp
Normal file
21
engine/3d/src/BoundsTrait.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
@file BoundsTrait.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2008-10-01
|
||||
@edited 2008-10-01
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_BOUNDSTRAIT_H
|
||||
#define G3D_BOUNDSTRAIT_H
|
||||
|
||||
#include "platform.hpp"
|
||||
|
||||
template<typename Value>
|
||||
struct BoundsTrait
|
||||
{
|
||||
};
|
||||
|
||||
#endif
|
||||
368
engine/3d/src/Box.cpp
Normal file
368
engine/3d/src/Box.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
@file Box.cpp
|
||||
Box class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2006-02-05
|
||||
*/
|
||||
|
||||
#include "Box.hpp"
|
||||
#include "G3DDebug.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "AABox.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
|
||||
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(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::finf();
|
||||
}
|
||||
|
||||
debugAssert(!isNaN(_extent.x));
|
||||
|
||||
_area = 2 * (_extent.x * _extent.y + _extent.y * _extent.z + _extent.z * _extent.x);
|
||||
|
||||
_center = (max + min) * 0.5f;
|
||||
|
||||
// If the extent is infinite along an axis, make the center zero to avoid NaNs
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (!G3D::isFinite(_extent[i]))
|
||||
{
|
||||
_center[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float Box::volume() const
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
|
||||
|
||||
float Box::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 G3D
|
||||
177
engine/3d/src/Box.hpp
Normal file
177
engine/3d/src/Box.hpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
@file Box.h
|
||||
|
||||
Box class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
|
||||
@created 2001-06-02
|
||||
@edited 2007-06-05
|
||||
|
||||
Copyright 2000-2006, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_BOX_H
|
||||
#define G3D_BOX_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Plane.hpp"
|
||||
|
||||
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(const class AABox& 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::ignore()) const;
|
||||
|
||||
/**
|
||||
Uniformly distributed on the interior (includes surface)
|
||||
*/
|
||||
Vector3 randomInteriorPoint() const;
|
||||
|
||||
void getBounds(class AABox&) const;
|
||||
|
||||
bool isFinite() const
|
||||
{
|
||||
return G3D::isFinite(_volume);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
60
engine/3d/src/BumpMapPreprocess.hpp
Normal file
60
engine/3d/src/BumpMapPreprocess.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
\file BumpMapPreprocess.h
|
||||
|
||||
\maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
\created 2010-01-28
|
||||
\edited 2010-01-28
|
||||
|
||||
Copyright 2000-2010, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_BumpMapPreprocess_h
|
||||
#define G3D_BumpMapPreprocess_h
|
||||
|
||||
#include "platform.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Not in the BumpMap class to avoid a circular dependency between Texture and BumpMap.
|
||||
G3D::GImage::computeNormalMap().
|
||||
*/
|
||||
class BumpMapPreprocess
|
||||
{
|
||||
public:
|
||||
/** If true, the elevations are box filtered after computing normals
|
||||
and before uploading, which produces better results for parallax offset mapping
|
||||
Defaults to false. */
|
||||
bool lowPassFilter;
|
||||
|
||||
/** Height of the maximum ("white") value, in pixels, for the purpose of computing normals.
|
||||
A value of 255 means that a 255 x 255 bump image with a full black-to-white gradient
|
||||
will produce a 45-degree ramp (this also results in "cubic" voxels).
|
||||
A negative value means to set zExtentPixels to -zExtentPixels * max(width, height).
|
||||
The default is -0.02.
|
||||
*/
|
||||
float zExtentPixels;
|
||||
|
||||
/** After computing normals, scale the height by |N.z|, a trick that reduces texture swim in steep areas for parallax offset
|
||||
mapping. Defaults to false.*/
|
||||
bool scaleZByNz;
|
||||
|
||||
BumpMapPreprocess()
|
||||
: lowPassFilter(false)
|
||||
, zExtentPixels(-0.02f)
|
||||
, scaleZByNz(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const BumpMapPreprocess& other) const
|
||||
{
|
||||
return (lowPassFilter == other.lowPassFilter) && (zExtentPixels == other.zExtentPixels) && (scaleZByNz == other.scaleZByNz);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
172
engine/3d/src/Capsule.cpp
Normal file
172
engine/3d/src/Capsule.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
@file Capsule.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-07
|
||||
@edited 2005-08-18
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#include "Capsule.hpp"
|
||||
#include "LineSegment.hpp"
|
||||
#include "Sphere.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "AABox.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
Capsule::Capsule() {}
|
||||
|
||||
|
||||
Capsule::Capsule(const Vector3& _p1, const Vector3& _p2, float _r)
|
||||
: p1(_p1)
|
||||
, p2(_p2)
|
||||
, _radius(_r)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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 G3D
|
||||
93
engine/3d/src/Capsule.hpp
Normal file
93
engine/3d/src/Capsule.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
@file Capsule.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-07
|
||||
@edited 2005-08-20
|
||||
|
||||
Copyright 2000-2006, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_CAPSULE_H
|
||||
#define G3D_CAPSULE_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
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(const Vector3& _p1, const Vector3& _p2, float _r);
|
||||
|
||||
|
||||
/** 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 G3D
|
||||
|
||||
#endif
|
||||
2617
engine/3d/src/CollisionDetection.cpp
Normal file
2617
engine/3d/src/CollisionDetection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1025
engine/3d/src/CollisionDetection.hpp
Normal file
1025
engine/3d/src/CollisionDetection.hpp
Normal file
File diff suppressed because it is too large
Load Diff
164
engine/3d/src/Color1.hpp
Normal file
164
engine/3d/src/Color1.hpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
@file Color1.h
|
||||
|
||||
Monochrome Color class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2007-01-31
|
||||
@edited 2009-03-20
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_COLOR1_H
|
||||
#define G3D_COLOR1_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "HashTrait.hpp"
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
inline explicit Color1(float v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool isZero() const
|
||||
{
|
||||
return value == 0.0f;
|
||||
}
|
||||
|
||||
inline bool isOne() const
|
||||
{
|
||||
return value == 1.0f;
|
||||
}
|
||||
|
||||
static const Color1& one();
|
||||
|
||||
static const Color1& zero();
|
||||
|
||||
/** Returns the value three times */
|
||||
class Color3 rgb() const;
|
||||
|
||||
Color1(const class Color1uint8& other);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::Color1>
|
||||
{
|
||||
static size_t hashCode(const G3D::Color1& key)
|
||||
{
|
||||
return key.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
94
engine/3d/src/Color1uint8.hpp
Normal file
94
engine/3d/src/Color1uint8.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
@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 "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Represents a Color1 as a packed integer. Convenient
|
||||
for creating unsigned int vertex arrays.
|
||||
|
||||
<B>WARNING</B>: Integer color formats are different than
|
||||
integer vertex formats. The color channels are automatically
|
||||
scaled by 255 (because OpenGL automatically scales integer
|
||||
colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255)
|
||||
but Vector3(1,1,1) == Vector3int16(1,1,1).
|
||||
|
||||
<B>Note</B>:
|
||||
Conversion of a float32 to uint8 is accomplished by min(iFloor(f * 256)) and
|
||||
back to float32 by u / 255.0f. This gives equal size intervals.
|
||||
Consider a number line from 0 to 1 and a corresponding one from 0 to 255. If we use iRound(x * 255), then the mapping for three critical intervals
|
||||
are:
|
||||
|
||||
<pre>
|
||||
let s = 0.5/255
|
||||
float int size
|
||||
[0, s) -> 0 s
|
||||
[s, s * 3) -> 1 2*s
|
||||
(1 - s, 1] -> 255 s
|
||||
</pre>
|
||||
|
||||
If we use max(floor(x * 256), 255), then we get:
|
||||
|
||||
<pre>
|
||||
let s = 1/256
|
||||
float int size
|
||||
[0, s) -> 0 s
|
||||
[s, 2 * s) -> 1 s
|
||||
(1 - s, 1] -> 255 s
|
||||
</PRE>
|
||||
and the intervals are all the same size, thus giving equal precision to all values.
|
||||
*/
|
||||
G3D_BEGIN_PACKED_CLASS(1)
|
||||
class Color1uint8
|
||||
{
|
||||
private:
|
||||
// Hidden operators
|
||||
bool operator<(const Color1uint8&) const;
|
||||
bool operator>(const Color1uint8&) const;
|
||||
bool operator<=(const Color1uint8&) const;
|
||||
bool operator>=(const Color1uint8&) const;
|
||||
|
||||
public:
|
||||
uint8 value;
|
||||
|
||||
Color1uint8()
|
||||
: value(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Color1uint8(const uint8 _v)
|
||||
: value(_v)
|
||||
{
|
||||
}
|
||||
|
||||
Color1uint8(const class Color1& c);
|
||||
|
||||
inline bool operator==(const Color1uint8& other) const
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Color1uint8& other) const
|
||||
{
|
||||
return value != other.value;
|
||||
}
|
||||
|
||||
} G3D_END_PACKED_CLASS(1)
|
||||
} // namespace G3D
|
||||
#endif
|
||||
345
engine/3d/src/Color3.cpp
Normal file
345
engine/3d/src/Color3.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
@file Color3.cpp
|
||||
|
||||
Color class.
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2010-01-28
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include <stdlib.h>
|
||||
#include "Color3.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Color3uint8.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
Color3 Color3::ansiMap(uint32 i)
|
||||
{
|
||||
static const Color3 map[] = {Color3::black(), Color3::red() * 0.75f, Color3::green() * 0.75f, Color3::yellow() * 0.75f, Color3::blue() * 0.75f,
|
||||
Color3::purple() * 0.75f, Color3::cyan() * 0.75f, Color3::white() * 0.75f, Color3::white() * 0.90f, Color3::red(), Color3::green(),
|
||||
Color3::yellow(), Color3::blue(), Color3::purple(), Color3::cyan(), Color3::white()};
|
||||
|
||||
return map[i & 15];
|
||||
}
|
||||
|
||||
|
||||
Color3 Color3::pastelMap(uint32 i)
|
||||
{
|
||||
uint32 x = Crypto::crc32(&i, sizeof(uint32));
|
||||
// Create fairly bright, saturated colors
|
||||
Vector3 v(((x >> 22) & 1023) / 1023.0f, (((x >> 11) & 2047) / 2047.0f) * 0.5f + 0.25f, ((x & 2047) / 2047.0f) * 0.75f + 0.25f);
|
||||
return Color3::fromHSV(v);
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::red()
|
||||
{
|
||||
static Color3 c(1.0f, 0.0f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::green()
|
||||
{
|
||||
static Color3 c(0.0f, 1.0f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::blue()
|
||||
{
|
||||
static Color3 c(0.0f, 0.0f, 1.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::purple()
|
||||
{
|
||||
static Color3 c(0.7f, 0.0f, 1.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::cyan()
|
||||
{
|
||||
static Color3 c(0.0f, 0.7f, 1.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::yellow()
|
||||
{
|
||||
static Color3 c(1.0f, 1.0f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::brown()
|
||||
{
|
||||
static Color3 c(0.5f, 0.5f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::orange()
|
||||
{
|
||||
static Color3 c(1.0f, 0.5f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::black()
|
||||
{
|
||||
static Color3 c(0.0f, 0.0f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
const Color3& Color3::zero()
|
||||
{
|
||||
static Color3 c(0.0f, 0.0f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::one()
|
||||
{
|
||||
static Color3 c(1.0f, 1.0f, 1.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::gray()
|
||||
{
|
||||
static Color3 c(0.7f, 0.7f, 0.7f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::white()
|
||||
{
|
||||
static Color3 c(1, 1, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
bool Color3::isFinite() const
|
||||
{
|
||||
return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b);
|
||||
}
|
||||
|
||||
|
||||
const Color3& Color3::wheelRandom()
|
||||
{
|
||||
static const Color3 colorArray[8] = {
|
||||
Color3::blue(), Color3::red(), Color3::green(), Color3::orange(), Color3::yellow(), Color3::cyan(), Color3::purple(), Color3::brown()};
|
||||
|
||||
return colorArray[iRandom(0, 7)];
|
||||
}
|
||||
|
||||
|
||||
size_t Color3::hashCode() const
|
||||
{
|
||||
unsigned int rhash = (*(int*)(void*)(&r));
|
||||
unsigned int ghash = (*(int*)(void*)(&g));
|
||||
unsigned int bhash = (*(int*)(void*)(&b));
|
||||
|
||||
return rhash + (ghash * 37) + (bhash * 101);
|
||||
}
|
||||
|
||||
|
||||
Color3::Color3(const Vector3& v)
|
||||
{
|
||||
r = v.x;
|
||||
g = v.y;
|
||||
b = v.z;
|
||||
}
|
||||
|
||||
|
||||
Color3::Color3(const class Color3uint8& other)
|
||||
{
|
||||
r = other.r / 255.0f;
|
||||
g = other.g / 255.0f;
|
||||
b = other.b / 255.0f;
|
||||
}
|
||||
|
||||
|
||||
Color3 Color3::fromARGB(uint32 x)
|
||||
{
|
||||
return Color3((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF)) / 255.0f;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
Color3 Color3::random()
|
||||
{
|
||||
return Color3(uniformRandom(), uniformRandom(), uniformRandom()).direction();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Color3& Color3::operator/=(float fScalar)
|
||||
{
|
||||
if (fScalar != 0.0f)
|
||||
{
|
||||
float fInvScalar = 1.0f / fScalar;
|
||||
r *= fInvScalar;
|
||||
g *= fInvScalar;
|
||||
b *= fInvScalar;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = (float)G3D::finf();
|
||||
g = (float)G3D::finf();
|
||||
b = (float)G3D::finf();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
float Color3::unitize(float fTolerance)
|
||||
{
|
||||
float fLength = length();
|
||||
|
||||
if (fLength > fTolerance)
|
||||
{
|
||||
float fInvLength = 1.0f / fLength;
|
||||
r *= fInvLength;
|
||||
g *= fInvLength;
|
||||
b *= fInvLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
fLength = 0.0f;
|
||||
}
|
||||
|
||||
return fLength;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Color3 Color3::fromHSV(const Vector3& _hsv)
|
||||
{
|
||||
debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f) && (_hsv.y <= 1.0f && _hsv.y >= 0.0f) && (_hsv.z <= 1.0f && _hsv.z >= 0.0f),
|
||||
"H,S,V must be between [0,1]");
|
||||
const int i = iMin(5, G3D::iFloor(6.0 * _hsv.x));
|
||||
const float f = 6.0f * _hsv.x - i;
|
||||
const float m = _hsv.z * (1.0f - (_hsv.y));
|
||||
const float n = _hsv.z * (1.0f - (_hsv.y * f));
|
||||
const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f)));
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return Color3(_hsv.z, k, m);
|
||||
|
||||
case 1:
|
||||
return Color3(n, _hsv.z, m);
|
||||
|
||||
case 2:
|
||||
return Color3(m, _hsv.z, k);
|
||||
|
||||
case 3:
|
||||
return Color3(m, n, _hsv.z);
|
||||
|
||||
case 4:
|
||||
return Color3(k, m, _hsv.z);
|
||||
|
||||
case 5:
|
||||
return Color3(_hsv.z, m, n);
|
||||
|
||||
default:
|
||||
debugAssertM(false, "fell through switch..");
|
||||
}
|
||||
return Color3::black();
|
||||
}
|
||||
|
||||
|
||||
Vector3 Color3::toHSV(const Color3& _rgb)
|
||||
{
|
||||
debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f) && (_rgb.g <= 1.0f && _rgb.g >= 0.0f) && (_rgb.b <= 1.0f && _rgb.b >= 0.0f),
|
||||
"R,G,B must be between [0,1]");
|
||||
Vector3 hsv = Vector3::zero();
|
||||
hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b);
|
||||
if (G3D::fuzzyEq(hsv.z, 0.0f))
|
||||
{
|
||||
return hsv;
|
||||
}
|
||||
|
||||
const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b);
|
||||
hsv.y = (hsv.z - x) / hsv.z;
|
||||
|
||||
if (G3D::fuzzyEq(hsv.y, 0.0f))
|
||||
{
|
||||
return hsv;
|
||||
}
|
||||
|
||||
Vector3 rgbN;
|
||||
rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x);
|
||||
rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x);
|
||||
rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x);
|
||||
|
||||
if (_rgb.r == hsv.z)
|
||||
{ // note from the max we know that it exactly equals one of the three.
|
||||
hsv.x = (_rgb.g == x) ? 5.0f + rgbN.z : 1.0f - rgbN.y;
|
||||
}
|
||||
else if (_rgb.g == hsv.z)
|
||||
{
|
||||
hsv.x = (_rgb.b == x) ? 1.0f + rgbN.x : 3.0f - rgbN.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
hsv.x = (_rgb.r == x) ? 3.0f + rgbN.y : 5.0f - rgbN.x;
|
||||
}
|
||||
|
||||
hsv.x /= 6.0f;
|
||||
|
||||
return hsv;
|
||||
}
|
||||
|
||||
Color3 Color3::jetColorMap(const float& val)
|
||||
{
|
||||
debugAssertM(val <= 1.0f && val >= 0.0f, "value should be in [0,1]");
|
||||
|
||||
// truncated triangles where sides have slope 4
|
||||
Color3 jet;
|
||||
|
||||
jet.r = G3D::min(4.0f * val - 1.5f, -4.0f * val + 4.5f);
|
||||
jet.g = G3D::min(4.0f * val - 0.5f, -4.0f * val + 3.5f);
|
||||
jet.b = G3D::min(4.0f * val + 0.5f, -4.0f * val + 2.5f);
|
||||
|
||||
|
||||
jet.r = G3D::clamp(jet.r, 0.0f, 1.0f);
|
||||
jet.g = G3D::clamp(jet.g, 0.0f, 1.0f);
|
||||
jet.b = G3D::clamp(jet.b, 0.0f, 1.0f);
|
||||
|
||||
return jet;
|
||||
}
|
||||
|
||||
// Aya - Added for boost App.Unit Test
|
||||
std::ostream& operator<<(std::ostream& os, const Color3& c)
|
||||
{
|
||||
return os << c.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 G3D
|
||||
467
engine/3d/src/Color3.hpp
Normal file
467
engine/3d/src/Color3.hpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/**
|
||||
@file Color3.h
|
||||
|
||||
Color class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@cite Portions based on Dave Eberly's Magic Software Library
|
||||
at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2009-04-28
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_Color3_h
|
||||
#define G3D_Color3_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "HashTrait.hpp"
|
||||
#include "Color1.hpp"
|
||||
#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();
|
||||
|
||||
Color3(float r, float g, float b);
|
||||
explicit Color3(float v)
|
||||
: r(v)
|
||||
, g(v)
|
||||
, b(v)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Color3(const class Vector3& v);
|
||||
|
||||
explicit Color3(const float value[3]);
|
||||
|
||||
/** Returns this color */
|
||||
const Color3& rgb() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
Initialize from another color.
|
||||
*/
|
||||
Color3(const Color3& other);
|
||||
|
||||
Color3(const class Color3uint8& other);
|
||||
|
||||
inline bool isZero() const
|
||||
{
|
||||
return (r == 0.0f) && (g == 0.0f) && (b == 0.0f);
|
||||
}
|
||||
|
||||
inline bool isOne() const
|
||||
{
|
||||
return (r == 1.0f) && (g == 1.0f) && (b == 1.0f);
|
||||
}
|
||||
|
||||
bool isFinite() const;
|
||||
|
||||
/**
|
||||
Initialize from an HTML-style color (e.g. 0xFF0000 == RED)
|
||||
*/
|
||||
static Color3 fromARGB(uint32);
|
||||
|
||||
/** Returns one of the color wheel colors (e.g. RED, GREEN, CYAN).
|
||||
Does not include white, black, or gray. */
|
||||
static const Color3& wheelRandom();
|
||||
|
||||
/** Generate colors according to the ANSI color set, mod 16.
|
||||
\sa pastelMap */
|
||||
static Color3 ansiMap(uint32 i);
|
||||
|
||||
/**
|
||||
Generate colors using a hash such that adjacent values
|
||||
are unlikely to have similar colors.
|
||||
|
||||
Useful for rendering with
|
||||
stable but arbitrary colors, e.g., when debugging a mesh
|
||||
algorithm.
|
||||
|
||||
\sa ansiMap
|
||||
*/
|
||||
static Color3 pastelMap(uint32 i);
|
||||
|
||||
/**
|
||||
* Channel value.
|
||||
*/
|
||||
float r, g, b;
|
||||
|
||||
// access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
|
||||
//
|
||||
// WARNING. These member functions rely on
|
||||
// (1) Color3 not having virtual functions
|
||||
// (2) the data packed in a 3*sizeof(float) memory block
|
||||
const float& operator[](int i) const;
|
||||
float& operator[](int i);
|
||||
|
||||
// assignment and comparison
|
||||
Color3& operator=(const Color3& rkVector);
|
||||
bool operator==(const Color3& rkVector) const;
|
||||
bool operator!=(const Color3& rkVector) const;
|
||||
size_t hashCode() const;
|
||||
|
||||
// arithmetic operations
|
||||
Color3 operator+(const Color3& rkVector) const;
|
||||
Color3 operator-(const Color3& rkVector) const;
|
||||
inline Color3 operator*(float s) const
|
||||
{
|
||||
return Color3(r * s, g * s, b * s);
|
||||
}
|
||||
Color3 operator*(const Color3& rkVector) const;
|
||||
inline Color3 operator/(float fScalar) const
|
||||
{
|
||||
return (*this) * (1.0f / fScalar);
|
||||
}
|
||||
Color3 operator-() const;
|
||||
|
||||
// arithmetic updates
|
||||
Color3& operator+=(const Color3& rkVector);
|
||||
Color3& operator-=(const Color3& rkVector);
|
||||
Color3& operator*=(const Color3& rkVector);
|
||||
Color3& operator*=(float fScalar);
|
||||
Color3& operator/=(float fScalar);
|
||||
|
||||
bool fuzzyEq(const Color3& other) const;
|
||||
bool fuzzyNe(const Color3& other) const;
|
||||
|
||||
// vector operations
|
||||
float length() const;
|
||||
Color3 direction() const;
|
||||
float squaredLength() const;
|
||||
float dot(const Color3& rkVector) const;
|
||||
float unitize(float fTolerance = 1e-06);
|
||||
Color3 cross(const Color3& rkVector) const;
|
||||
Color3 unitCross(const Color3& rkVector) const;
|
||||
|
||||
inline Color3 pow(const Color3& other) const
|
||||
{
|
||||
return Color3(::pow(r, other.r), ::pow(g, other.g), ::pow(b, other.b));
|
||||
}
|
||||
|
||||
inline Color3 pow(float other) const
|
||||
{
|
||||
return Color3(::pow(r, other), ::pow(g, other), ::pow(b, other));
|
||||
}
|
||||
|
||||
inline Color3 max(const Color3& other) const
|
||||
{
|
||||
return Color3(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b));
|
||||
}
|
||||
|
||||
inline Color3 min(const Color3& other) const
|
||||
{
|
||||
return Color3(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b));
|
||||
}
|
||||
|
||||
/** Smallest element */
|
||||
inline float min() const
|
||||
{
|
||||
return G3D::min(G3D::min(r, g), b);
|
||||
}
|
||||
|
||||
/** Largest element */
|
||||
inline float max() const
|
||||
{
|
||||
return G3D::max(G3D::max(r, g), b);
|
||||
}
|
||||
|
||||
inline Color3 lerp(const Color3& other, float a) const
|
||||
{
|
||||
return (*this) + (other - *this) * a;
|
||||
}
|
||||
|
||||
inline float sum() const
|
||||
{
|
||||
return r + g + b;
|
||||
}
|
||||
|
||||
inline float average() const
|
||||
{
|
||||
return sum() / 3.0f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts from HSV to RGB , note: toHSV(fromHSV(_hsv)) may not be _hsv, if it is at a grey point or black point.
|
||||
* The components of _hsv should lie in the unit interval.
|
||||
* @cite Alvy Ray Smith SIGGRAPH 1978 "Color Gamut Transform Pairs"
|
||||
**/
|
||||
static Color3 fromHSV(const Vector3& _hsv);
|
||||
static Vector3 toHSV(const Color3& _rgb);
|
||||
|
||||
/** Duplicates the matlab jet colormap maps [0,1] --> (r,g,b) where blue is close to 0 and red is close to 1. */
|
||||
static Color3 jetColorMap(const float& val);
|
||||
|
||||
/** Returns colors with maximum saturation and value @param hue [0, 1]*/
|
||||
static Color3 rainbowColorMap(float hue);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
/** Random unit vector */
|
||||
static Color3 random();
|
||||
|
||||
// Special values.
|
||||
// Intentionally not inlined: see Matrix3::identity() for details.
|
||||
static const Color3& red();
|
||||
static const Color3& green();
|
||||
static const Color3& blue();
|
||||
static const Color3& purple();
|
||||
static const Color3& cyan();
|
||||
static const Color3& yellow();
|
||||
static const Color3& brown();
|
||||
static const Color3& orange();
|
||||
static const Color3& black();
|
||||
static const Color3& gray();
|
||||
static const Color3& white();
|
||||
|
||||
static const Color3& zero();
|
||||
static const Color3& one();
|
||||
|
||||
inline Color3 bgr() const
|
||||
{
|
||||
return Color3(b, g, r);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline G3D::Color3 operator*(float s, const G3D::Color3& c)
|
||||
{
|
||||
return c * s;
|
||||
}
|
||||
|
||||
inline G3D::Color3 operator*(G3D::Color1& s, const G3D::Color3& c)
|
||||
{
|
||||
return c * s.value;
|
||||
}
|
||||
|
||||
inline G3D::Color3 operator*(const G3D::Color3& c, G3D::Color1& s)
|
||||
{
|
||||
return c * s.value;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Color3&); // Aya - Added for boost App.Unit Test
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3::Color3() {}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color3::Color3(float fX, float fY, float fZ)
|
||||
{
|
||||
r = fX;
|
||||
g = fY;
|
||||
b = fZ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3::Color3(const float afCoordinate[3])
|
||||
{
|
||||
r = afCoordinate[0];
|
||||
g = afCoordinate[1];
|
||||
b = afCoordinate[2];
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3::Color3(const Color3& rkVector)
|
||||
{
|
||||
r = rkVector.r;
|
||||
g = rkVector.g;
|
||||
b = rkVector.b;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline float& Color3::operator[](int i)
|
||||
{
|
||||
return ((float*)this)[i];
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline const float& Color3::operator[](int i) const
|
||||
{
|
||||
return ((float*)this)[i];
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool Color3::fuzzyEq(const Color3& other) const
|
||||
{
|
||||
return G3D::fuzzyEq((*this - other).squaredLength(), 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool Color3::fuzzyNe(const Color3& other) const
|
||||
{
|
||||
return G3D::fuzzyNe((*this - other).squaredLength(), 0);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3& Color3::operator=(const Color3& rkVector)
|
||||
{
|
||||
r = rkVector.r;
|
||||
g = rkVector.g;
|
||||
b = rkVector.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline bool Color3::operator==(const Color3& rkVector) const
|
||||
{
|
||||
return (r == rkVector.r && g == rkVector.g && b == rkVector.b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline bool Color3::operator!=(const Color3& rkVector) const
|
||||
{
|
||||
return (r != rkVector.r || g != rkVector.g || b != rkVector.b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::operator+(const Color3& rkVector) const
|
||||
{
|
||||
return Color3(r + rkVector.r, g + rkVector.g, b + rkVector.b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::operator-(const Color3& rkVector) const
|
||||
{
|
||||
return Color3(r - rkVector.r, g - rkVector.g, b - rkVector.b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::operator*(const Color3& rkVector) const
|
||||
{
|
||||
return Color3(r * rkVector.r, g * rkVector.g, b * rkVector.b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::operator-() const
|
||||
{
|
||||
return Color3(-r, -g, -b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3& Color3::operator+=(const Color3& rkVector)
|
||||
{
|
||||
r += rkVector.r;
|
||||
g += rkVector.g;
|
||||
b += rkVector.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3& Color3::operator-=(const Color3& rkVector)
|
||||
{
|
||||
r -= rkVector.r;
|
||||
g -= rkVector.g;
|
||||
b -= rkVector.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3& Color3::operator*=(float fScalar)
|
||||
{
|
||||
r *= fScalar;
|
||||
g *= fScalar;
|
||||
b *= fScalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3& Color3::operator*=(const Color3& rkVector)
|
||||
{
|
||||
r *= rkVector.r;
|
||||
g *= rkVector.g;
|
||||
b *= rkVector.b;
|
||||
return *this;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline float Color3::squaredLength() const
|
||||
{
|
||||
return r * r + g * g + b * b;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline float Color3::length() const
|
||||
{
|
||||
return sqrtf(r * r + g * g + b * b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::direction() const
|
||||
{
|
||||
float lenSquared = r * r + g * g + b * b;
|
||||
|
||||
if (lenSquared != 1.0f)
|
||||
{
|
||||
return *this / sqrtf(lenSquared);
|
||||
}
|
||||
else
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline float Color3::dot(const Color3& rkVector) const
|
||||
{
|
||||
return r * rkVector.r + g * rkVector.g + b * rkVector.b;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::cross(const Color3& rkVector) const
|
||||
{
|
||||
return Color3(g * rkVector.b - b * rkVector.g, b * rkVector.r - r * rkVector.b, r * rkVector.g - g * rkVector.r);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color3 Color3::unitCross(const Color3& rkVector) const
|
||||
{
|
||||
Color3 kCross(g * rkVector.b - b * rkVector.g, b * rkVector.r - r * rkVector.b, r * rkVector.g - g * rkVector.r);
|
||||
kCross.unitize();
|
||||
return kCross;
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::Color3>
|
||||
{
|
||||
static size_t hashCode(const G3D::Color3& key)
|
||||
{
|
||||
return key.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
26
engine/3d/src/Color3uint8.cpp
Normal file
26
engine/3d/src/Color3uint8.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
@file Color3uint8.cpp
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-04-07
|
||||
@edited 2006-01-07
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Color3uint8.hpp"
|
||||
#include "Color3.hpp"
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
140
engine/3d/src/Color3uint8.hpp
Normal file
140
engine/3d/src/Color3uint8.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
@file Color3uint8.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-04-07
|
||||
@edited 2010-03-24
|
||||
|
||||
Copyright 2000-2010, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_Color3uint8_h
|
||||
#define G3D_Color3uint8_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Represents a Color3 as a packed integer. Convenient
|
||||
for creating unsigned int vertex arrays. Used by
|
||||
G3D::GImage as the underlying format.
|
||||
|
||||
<B>WARNING</B>: Integer color formats are different than
|
||||
integer vertex formats. The color channels are automatically
|
||||
scaled by 255 (because OpenGL automatically scales integer
|
||||
colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255)
|
||||
but Vector3(1,1,1) == Vector3int16(1,1,1).
|
||||
*/
|
||||
|
||||
G3D_BEGIN_PACKED_CLASS(1)
|
||||
|
||||
class Color3uint8
|
||||
{
|
||||
private:
|
||||
// Hidden operators
|
||||
bool operator<(const Color3uint8&) const;
|
||||
bool operator>(const Color3uint8&) const;
|
||||
bool operator<=(const Color3uint8&) const;
|
||||
bool operator>=(const Color3uint8&) const;
|
||||
|
||||
public:
|
||||
uint8 r;
|
||||
uint8 g;
|
||||
uint8 b;
|
||||
|
||||
Color3uint8()
|
||||
: r(0)
|
||||
, g(0)
|
||||
, b(0)
|
||||
{
|
||||
}
|
||||
|
||||
Color3uint8(const uint8 _r, const uint8 _g, const uint8 _b)
|
||||
: r(_r)
|
||||
, g(_g)
|
||||
, b(_b)
|
||||
{
|
||||
}
|
||||
|
||||
Color3uint8(const class Color3& c);
|
||||
|
||||
static Color3uint8 fromARGB(uint32 i)
|
||||
{
|
||||
Color3uint8 c;
|
||||
c.r = (i >> 16) & 0xFF;
|
||||
c.g = (i >> 8) & 0xFF;
|
||||
c.b = i & 0xFF;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color3uint8 bgr() const
|
||||
{
|
||||
return Color3uint8(b, g, r);
|
||||
}
|
||||
|
||||
Color3uint8 max(const Color3uint8 x) const
|
||||
{
|
||||
return Color3uint8(G3D::max(r, x.r), G3D::max(g, x.g), G3D::max(b, x.b));
|
||||
}
|
||||
|
||||
Color3uint8 min(const Color3uint8 x) const
|
||||
{
|
||||
return Color3uint8(G3D::min(r, x.r), G3D::min(g, x.g), G3D::min(b, x.b));
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the color packed into a uint32
|
||||
(the upper byte is 0xFF)
|
||||
*/
|
||||
uint32 asUInt32() const
|
||||
{
|
||||
return (0xFF << 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) Color3 not having virtual functions
|
||||
// (2) the data packed in a 3*sizeof(uint8) memory block
|
||||
uint8& operator[](int i) const
|
||||
{
|
||||
debugAssert((unsigned int)i < 3);
|
||||
return ((uint8*)this)[i];
|
||||
}
|
||||
|
||||
operator uint8*()
|
||||
{
|
||||
return (G3D::uint8*)this;
|
||||
}
|
||||
|
||||
operator const uint8*() const
|
||||
{
|
||||
return (uint8*)this;
|
||||
}
|
||||
|
||||
bool operator==(const Color3uint8 other) const
|
||||
{
|
||||
return (other.r == r) && (other.g == g) && (other.b == b);
|
||||
}
|
||||
|
||||
bool operator!=(const Color3uint8 other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
} G3D_END_PACKED_CLASS(1)
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
147
engine/3d/src/Color4.cpp
Normal file
147
engine/3d/src/Color4.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
@file Color4.cpp
|
||||
|
||||
Color class.
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@cite Portions by Laura Wollstadt, graphics3d.com
|
||||
@cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
|
||||
|
||||
|
||||
@created 2002-06-25
|
||||
@edited 2009-11-10
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "Color4.hpp"
|
||||
#include "Color4uint8.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "format.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
const Color4& Color4::one()
|
||||
{
|
||||
const static Color4 x(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
const Color4& Color4::zero()
|
||||
{
|
||||
static Color4 c(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color4& Color4::inf()
|
||||
{
|
||||
static Color4 c((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf());
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color4& Color4::nan()
|
||||
{
|
||||
static Color4 c((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan());
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const Color4& Color4::clear()
|
||||
{
|
||||
return Color4::zero();
|
||||
}
|
||||
|
||||
|
||||
Color4::Color4(const Vector4& v)
|
||||
{
|
||||
r = v.x;
|
||||
g = v.y;
|
||||
b = v.z;
|
||||
a = v.w;
|
||||
}
|
||||
|
||||
|
||||
Color4::Color4(const Color4uint8& c)
|
||||
: r(c.r)
|
||||
, g(c.g)
|
||||
, b(c.b)
|
||||
, a(c.a)
|
||||
{
|
||||
*this /= 255.0f;
|
||||
}
|
||||
|
||||
size_t Color4::hashCode() const
|
||||
{
|
||||
unsigned int rhash = (*(int*)(void*)(&r));
|
||||
unsigned int ghash = (*(int*)(void*)(&g));
|
||||
unsigned int bhash = (*(int*)(void*)(&b));
|
||||
unsigned int ahash = (*(int*)(void*)(&a));
|
||||
|
||||
return rhash + (ghash * 37) + (bhash * 101) + (ahash * 241);
|
||||
}
|
||||
|
||||
Color4 Color4::fromARGB(uint32 x)
|
||||
{
|
||||
return Color4((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF), (float)((x >> 24) & 0xFF)) / 255.0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Color4 Color4::operator/(float fScalar) const
|
||||
{
|
||||
Color4 kQuot;
|
||||
|
||||
if (fScalar != 0.0f)
|
||||
{
|
||||
float fInvScalar = 1.0f / fScalar;
|
||||
kQuot.r = fInvScalar * r;
|
||||
kQuot.g = fInvScalar * g;
|
||||
kQuot.b = fInvScalar * b;
|
||||
kQuot.a = fInvScalar * a;
|
||||
return kQuot;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return Color4::inf();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Color4& Color4::operator/=(float fScalar)
|
||||
{
|
||||
if (fScalar != 0.0f)
|
||||
{
|
||||
float fInvScalar = 1.0f / fScalar;
|
||||
r *= fInvScalar;
|
||||
g *= fInvScalar;
|
||||
b *= fInvScalar;
|
||||
a *= fInvScalar;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = (float)G3D::finf();
|
||||
g = (float)G3D::finf();
|
||||
b = (float)G3D::finf();
|
||||
a = (float)G3D::finf();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
std::string Color4::toString() const
|
||||
{
|
||||
return G3D::format("(%g, %g, %g, %g)", r, g, b, a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
}; // namespace G3D
|
||||
365
engine/3d/src/Color4.hpp
Normal file
365
engine/3d/src/Color4.hpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/**
|
||||
@file Color4.h
|
||||
|
||||
Color class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@cite Portions based on Dave Eberly's Magic Software Library
|
||||
at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
|
||||
|
||||
@created 2002-06-25
|
||||
@edited 2009-11-15
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_Color4_h
|
||||
#define G3D_Color4_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Color3.hpp"
|
||||
#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:
|
||||
/** \param any Must be in one of the following forms:
|
||||
- Color4(#, #, #, #)
|
||||
- Color4::fromARGB(#)
|
||||
- Color4{r = #, g = #, b = #, a = #)
|
||||
*/
|
||||
/**
|
||||
* Does not initialize fields.
|
||||
*/
|
||||
Color4();
|
||||
|
||||
Color4(const Color3& c3, float a = 1.0);
|
||||
|
||||
Color4(const class Color4uint8& c);
|
||||
|
||||
Color4(const class Vector4& v);
|
||||
|
||||
Color4(float r, float g, float b, float a = 1.0);
|
||||
|
||||
static const Color4& one();
|
||||
|
||||
Color4(float value[4]);
|
||||
|
||||
/**
|
||||
* Initialize from another color.
|
||||
*/
|
||||
Color4(const Color4& other);
|
||||
|
||||
|
||||
inline bool isZero() const
|
||||
{
|
||||
return (r == 0.0f) && (g == 0.0f) && (b == 0.0f) && (a == 0.0f);
|
||||
}
|
||||
|
||||
inline bool isOne() const
|
||||
{
|
||||
return (r == 1.0f) && (g == 1.0f) && (b == 1.0f) && (a == 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
Initialize from an HTML-style color (e.g. 0xFFFF0000 == RED)
|
||||
*/
|
||||
static Color4 fromARGB(uint32);
|
||||
|
||||
/**
|
||||
* Channel values.
|
||||
*/
|
||||
float r, g, b, a;
|
||||
|
||||
inline Color3 rgb() const
|
||||
{
|
||||
return Color3(r, g, b);
|
||||
}
|
||||
|
||||
// access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b, v[3] = V.a
|
||||
//
|
||||
// WARNING. These member functions rely on
|
||||
// (1) Color4 not having virtual functions
|
||||
// (2) the data packed in a 3*sizeof(float) memory block
|
||||
float& operator[](int i) const;
|
||||
|
||||
// assignment and comparison
|
||||
Color4& operator=(const Color4& rkVector);
|
||||
bool operator==(const Color4& rkVector) const;
|
||||
bool operator!=(const Color4& rkVector) const;
|
||||
size_t hashCode() const;
|
||||
|
||||
// arithmetic operations
|
||||
Color4 operator+(const Color4& rkVector) const;
|
||||
Color4 operator-(const Color4& rkVector) const;
|
||||
Color4 operator*(float fScalar) const;
|
||||
Color4 operator*(const Color4& k) const
|
||||
{
|
||||
return Color4(r * k.r, g * k.g, b * k.b, a * k.a);
|
||||
}
|
||||
|
||||
Color4& operator*=(const Color4& c)
|
||||
{
|
||||
r *= c.r;
|
||||
g *= c.g;
|
||||
b *= c.b;
|
||||
a *= c.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color4 operator/(float fScalar) const;
|
||||
Color4 operator-() const;
|
||||
friend Color4 operator*(double fScalar, const Color4& rkVector);
|
||||
|
||||
// arithmetic updates
|
||||
Color4& operator+=(const Color4& rkVector);
|
||||
Color4& operator-=(const Color4& rkVector);
|
||||
Color4& operator*=(float fScalar);
|
||||
Color4& operator/=(float fScalar);
|
||||
|
||||
bool fuzzyEq(const Color4& other) const;
|
||||
bool fuzzyNe(const Color4& other) const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
inline Color4 max(const Color4& other) const
|
||||
{
|
||||
return Color4(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b), G3D::max(a, other.a));
|
||||
}
|
||||
|
||||
inline Color4 min(const Color4& other) const
|
||||
{
|
||||
return Color4(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b), G3D::min(a, other.a));
|
||||
}
|
||||
|
||||
/** r + g + b + a */
|
||||
inline float sum() const
|
||||
{
|
||||
return r + g + b + a;
|
||||
}
|
||||
|
||||
inline Color4 lerp(const Color4& other, float a) const
|
||||
{
|
||||
return (*this) + (other - *this) * a;
|
||||
}
|
||||
|
||||
// Special values.
|
||||
// Intentionally not inlined: see Matrix3::identity() for details.
|
||||
static const Color4& zero();
|
||||
static const Color4& clear();
|
||||
|
||||
static const Color4& inf();
|
||||
static const Color4& nan();
|
||||
|
||||
inline bool isFinite() const
|
||||
{
|
||||
return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b) && G3D::isFinite(a);
|
||||
}
|
||||
|
||||
inline Color3 bgr() const
|
||||
{
|
||||
return Color3(b, g, r);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Extends the c3 with alpha = 1.0
|
||||
*/
|
||||
Color4 operator*(const Color3& c3, const Color4& c4);
|
||||
|
||||
|
||||
inline Color4 operator*(const Color3& c3, const Color4& c4)
|
||||
{
|
||||
return Color4(c3.r * c4.r, c3.g * c4.g, c3.b * c4.b, c4.a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4::Color4()
|
||||
{
|
||||
// For efficiency in construction of large arrays of vectors, the
|
||||
// default constructor does not initialize the vector.
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4::Color4(const Color3& c3, float a)
|
||||
{
|
||||
r = c3.r;
|
||||
g = c3.g;
|
||||
b = c3.b;
|
||||
this->a = a;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4::Color4(float r, float g, float b, float a)
|
||||
: r(r)
|
||||
, g(g)
|
||||
, b(b)
|
||||
, a(a)
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color4::Color4(float afCoordinate[4])
|
||||
{
|
||||
r = afCoordinate[0];
|
||||
g = afCoordinate[1];
|
||||
b = afCoordinate[2];
|
||||
a = afCoordinate[3];
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4::Color4(const Color4& other)
|
||||
{
|
||||
|
||||
r = other.r;
|
||||
g = other.g;
|
||||
b = other.b;
|
||||
a = other.a;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline float& Color4::operator[](int i) const
|
||||
{
|
||||
return ((float*)this)[i];
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool Color4::fuzzyEq(const Color4& other) const
|
||||
{
|
||||
Color4 dif = (*this - other);
|
||||
return G3D::fuzzyEq(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool Color4::fuzzyNe(const Color4& other) const
|
||||
{
|
||||
Color4 dif = (*this - other);
|
||||
return G3D::fuzzyNe(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color4& Color4::operator=(const Color4& other)
|
||||
{
|
||||
r = other.r;
|
||||
g = other.g;
|
||||
b = other.b;
|
||||
a = other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool Color4::operator==(const Color4& other) const
|
||||
{
|
||||
return (r == other.r && g == other.g && b == other.b && a == other.a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool Color4::operator!=(const Color4& other) const
|
||||
{
|
||||
return (r != other.r || g != other.g || b != other.b || a != other.a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color4 Color4::operator+(const Color4& other) const
|
||||
{
|
||||
return Color4(r + other.r, g + other.g, b + other.b, a + other.a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
inline Color4 Color4::operator-(const Color4& other) const
|
||||
{
|
||||
return Color4(r - other.r, g - other.g, b - other.b, a - other.a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4 Color4::operator*(float fScalar) const
|
||||
{
|
||||
return Color4(fScalar * r, fScalar * g, fScalar * b, fScalar * a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4 Color4::operator-() const
|
||||
{
|
||||
return Color4(-r, -g, -b, -a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4 operator*(float fScalar, const Color4& other)
|
||||
{
|
||||
return Color4(fScalar * other.r, fScalar * other.g, fScalar * other.b, fScalar * other.a);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4& Color4::operator+=(const Color4& other)
|
||||
{
|
||||
r += other.r;
|
||||
g += other.g;
|
||||
b += other.b;
|
||||
a += other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4& Color4::operator-=(const Color4& other)
|
||||
{
|
||||
r -= other.r;
|
||||
g -= other.g;
|
||||
b -= other.b;
|
||||
a -= other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline Color4& Color4::operator*=(float fScalar)
|
||||
{
|
||||
r *= fScalar;
|
||||
g *= fScalar;
|
||||
b *= fScalar;
|
||||
a *= fScalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::Color4>
|
||||
{
|
||||
static size_t hashCode(const G3D::Color4& key)
|
||||
{
|
||||
return key.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
26
engine/3d/src/Color4uint8.cpp
Normal file
26
engine/3d/src/Color4uint8.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
@file Color4uint8.cpp
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-04-07
|
||||
@edited 2006-01-07
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Color4uint8.hpp"
|
||||
#include "Color4.hpp"
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
147
engine/3d/src/Color4uint8.hpp
Normal file
147
engine/3d/src/Color4uint8.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
@file Color4uint8.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-04-07
|
||||
@edited 2010-03-24
|
||||
|
||||
Copyright 2000-2010, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef COLOR4UINT8_H
|
||||
#define COLOR4UINT8_H
|
||||
|
||||
#include "g3dmath.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "Color3uint8.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Represents a Color4 as a packed integer. Convenient
|
||||
for creating unsigned int vertex arrays. Used by
|
||||
G3D::GImage as the underlying format.
|
||||
|
||||
<B>WARNING</B>: Integer color formats are different than
|
||||
integer vertex formats. The color channels are automatically
|
||||
scaled by 255 (because OpenGL automatically scales integer
|
||||
colors back by this factor). So Color4(1,1,1) == Color4uint8(255,255,255)
|
||||
but Vector3(1,1,1) == Vector3int16(1,1,1).
|
||||
|
||||
*/
|
||||
G3D_BEGIN_PACKED_CLASS(1)
|
||||
class Color4uint8
|
||||
{
|
||||
private:
|
||||
// Hidden operators
|
||||
bool operator<(const Color4uint8&) const;
|
||||
bool operator>(const Color4uint8&) const;
|
||||
bool operator<=(const Color4uint8&) const;
|
||||
bool operator>=(const Color4uint8&) const;
|
||||
|
||||
public:
|
||||
uint8 r;
|
||||
uint8 g;
|
||||
uint8 b;
|
||||
uint8 a;
|
||||
|
||||
Color4uint8()
|
||||
: r(0)
|
||||
, g(0)
|
||||
, b(0)
|
||||
, a(0)
|
||||
{
|
||||
}
|
||||
|
||||
Color4uint8(const class Color4& c);
|
||||
|
||||
Color4uint8 max(const Color4uint8 x) const
|
||||
{
|
||||
return Color4uint8(G3D::max(r, x.r), G3D::max(g, x.g), G3D::max(b, x.b), G3D::max(a, x.a));
|
||||
}
|
||||
|
||||
Color4uint8 min(const Color4uint8 x) const
|
||||
{
|
||||
return Color4uint8(G3D::min(r, x.r), G3D::min(g, x.g), G3D::min(b, x.b), G3D::min(a, x.a));
|
||||
}
|
||||
|
||||
Color4uint8(const uint8 _r, const uint8 _g, const uint8 _b, const uint8 _a)
|
||||
: r(_r)
|
||||
, g(_g)
|
||||
, b(_b)
|
||||
, a(_a)
|
||||
{
|
||||
}
|
||||
|
||||
Color4uint8(const Color3uint8& c, const uint8 _a)
|
||||
: r(c.r)
|
||||
, g(c.g)
|
||||
, b(c.b)
|
||||
, a(_a)
|
||||
{
|
||||
}
|
||||
|
||||
inline static Color4uint8 fromARGB(uint32 i)
|
||||
{
|
||||
Color4uint8 c;
|
||||
c.a = (i >> 24) & 0xFF;
|
||||
c.r = (i >> 16) & 0xFF;
|
||||
c.g = (i >> 8) & 0xFF;
|
||||
c.b = i & 0xFF;
|
||||
return c;
|
||||
}
|
||||
|
||||
inline uint32 asUInt32() const
|
||||
{
|
||||
return ((uint32)a << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b;
|
||||
}
|
||||
|
||||
// access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
|
||||
//
|
||||
// WARNING. These member functions rely on
|
||||
// (1) Color4uint8 not having virtual functions
|
||||
// (2) the data packed in a 3*sizeof(uint8) memory block
|
||||
uint8& operator[](int i) const
|
||||
{
|
||||
return ((uint8*)this)[i];
|
||||
}
|
||||
|
||||
operator uint8*()
|
||||
{
|
||||
return (uint8*)this;
|
||||
}
|
||||
|
||||
operator const uint8*() const
|
||||
{
|
||||
return (uint8*)this;
|
||||
}
|
||||
|
||||
|
||||
inline Color3uint8 bgr() const
|
||||
{
|
||||
return Color3uint8(b, g, r);
|
||||
}
|
||||
|
||||
inline Color3uint8 rgb() const
|
||||
{
|
||||
return Color3uint8(r, g, b);
|
||||
}
|
||||
|
||||
bool operator==(const Color4uint8& other) const
|
||||
{
|
||||
return *reinterpret_cast<const uint32*>(this) == *reinterpret_cast<const uint32*>(&other);
|
||||
}
|
||||
|
||||
bool operator!=(const Color4uint8& other) const
|
||||
{
|
||||
return *reinterpret_cast<const uint32*>(this) != *reinterpret_cast<const uint32*>(&other);
|
||||
}
|
||||
|
||||
} G3D_END_PACKED_CLASS(1)
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
81
engine/3d/src/CompactCFrame.hpp
Normal file
81
engine/3d/src/CompactCFrame.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utility/Math.hpp"
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class CompactCFrame
|
||||
{
|
||||
Vector3 rotationaxis;
|
||||
float rotationangle; // angle is always positive.
|
||||
public:
|
||||
Vector3 translation;
|
||||
|
||||
CompactCFrame()
|
||||
: rotationangle(0)
|
||||
{
|
||||
}
|
||||
CompactCFrame(const CoordinateFrame& cframe)
|
||||
: translation(cframe.translation)
|
||||
{
|
||||
cframe.rotation.toAxisAngle(rotationaxis, rotationangle);
|
||||
AYAASSERT(!Math::hasNanOrInf(rotationaxis));
|
||||
AYAASSERT(!Math::isNanInf(rotationangle));
|
||||
}
|
||||
|
||||
CompactCFrame(const Vector3& translation, const Vector3& axisAngle)
|
||||
: translation(translation)
|
||||
, rotationaxis(axisAngle)
|
||||
{
|
||||
rotationangle = rotationaxis.unitize();
|
||||
AYAASSERT(!Math::hasNanOrInf(rotationaxis));
|
||||
AYAASSERT(!Math::isNanInf(rotationangle));
|
||||
}
|
||||
|
||||
CompactCFrame(const Vector3& translation, const Vector3& axis, float angle)
|
||||
: translation(translation)
|
||||
{
|
||||
setAxisAngle(axis, angle);
|
||||
AYAASSERT(!Math::hasNanOrInf(rotationaxis));
|
||||
AYAASSERT(!Math::isNanInf(rotationangle));
|
||||
}
|
||||
|
||||
void setAxisAngle(const Vector3& axis, float angle)
|
||||
{
|
||||
// enforce angle > 0.
|
||||
if (angle >= 0)
|
||||
{
|
||||
rotationaxis = axis;
|
||||
rotationangle = angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
rotationaxis = axis * -1.0f;
|
||||
rotationangle = -angle;
|
||||
}
|
||||
}
|
||||
|
||||
CoordinateFrame getCFrame() const
|
||||
{
|
||||
CoordinateFrame answer(Matrix3::fromAxisAngleFast(rotationaxis, rotationangle), translation);
|
||||
AYAASSERT(!Math::hasNanOrInf(answer));
|
||||
return answer;
|
||||
}
|
||||
|
||||
Vector3 getAxisAngle() const
|
||||
{
|
||||
return rotationaxis * rotationangle;
|
||||
}
|
||||
|
||||
const Vector3& getAxis() const
|
||||
{
|
||||
return rotationaxis;
|
||||
}
|
||||
|
||||
float getAngle() const
|
||||
{
|
||||
return rotationangle;
|
||||
}
|
||||
};
|
||||
} // namespace Aya
|
||||
69
engine/3d/src/Cone.hpp
Normal file
69
engine/3d/src/Cone.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
@file Cone.h
|
||||
|
||||
Cone class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2006-02-23
|
||||
|
||||
Copyright 2000-2006, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_CONE_H
|
||||
#define G3D_CONE_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
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 G3D
|
||||
|
||||
#endif
|
||||
188
engine/3d/src/ConvexPolyhedron.hpp
Normal file
188
engine/3d/src/ConvexPolyhedron.hpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
@file ConvexPolyhedron.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-11-11
|
||||
@edited 2006-04-10
|
||||
|
||||
Copyright 2000-2006, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_CONVEXPOLYHEDRON_H
|
||||
#define G3D_CONVEXPOLYHEDRON_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Array.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
class DirectedEdge
|
||||
{
|
||||
public:
|
||||
Vector3 start;
|
||||
Vector3 stop;
|
||||
};
|
||||
|
||||
class ConvexPolygon
|
||||
{
|
||||
private:
|
||||
friend class ConvexPolyhedron;
|
||||
|
||||
Array<Vector3> _vertex;
|
||||
|
||||
public:
|
||||
ConvexPolygon() {}
|
||||
ConvexPolygon(const Vector3& v0, const Vector3& v1, const Vector3& v2);
|
||||
ConvexPolygon(const Array<Vector3>& __vertex);
|
||||
virtual ~ConvexPolygon() {}
|
||||
|
||||
/**
|
||||
Counter clockwise winding order.
|
||||
*/
|
||||
inline const Vector3& vertex(int i) const
|
||||
{
|
||||
return _vertex[i];
|
||||
}
|
||||
|
||||
inline void setVertex(int i, const Vector3& v)
|
||||
{
|
||||
_vertex[i] = v;
|
||||
}
|
||||
|
||||
/**
|
||||
Zero vertices indicates an empty polygon (zero area).
|
||||
*/
|
||||
inline int numVertices() const
|
||||
{
|
||||
return _vertex.size();
|
||||
}
|
||||
|
||||
inline void setNumVertices(int n)
|
||||
{
|
||||
_vertex.resize(n);
|
||||
}
|
||||
|
||||
/**
|
||||
O(n) in the number of edges
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
/**
|
||||
Cuts the polygon at the plane. If the polygon is entirely above or below
|
||||
the plane, one of the returned polygons will be empty.
|
||||
|
||||
@param above The part of the polygon above (on the side the
|
||||
normal points to or in the plane) the plane
|
||||
@param below The part of the polygon below the plane.
|
||||
@param newEdge If a new edge was introduced, this is that edge (on the above portion; the below portion is the opposite winding.
|
||||
*/
|
||||
void cut(const Plane& plane, ConvexPolygon& above, ConvexPolygon& below, DirectedEdge& newEdge);
|
||||
void cut(const Plane& plane, ConvexPolygon& above, ConvexPolygon& below);
|
||||
|
||||
/**
|
||||
When a cut plane grazes a vertex in the polygon, two near-identical vertices may be created.
|
||||
The closeness of these two points can cause a number of problems, such as ConvexPolygon::normal()
|
||||
returning an infinite vector. It should be noted, however, that not all applications are
|
||||
sensitive to near-identical vertices.
|
||||
|
||||
removeDuplicateVertices() detects and eliminates redundant vertices.
|
||||
*/
|
||||
void removeDuplicateVertices();
|
||||
|
||||
/**
|
||||
O(n) in the number of edges
|
||||
*/
|
||||
float getArea() const;
|
||||
|
||||
inline Vector3 normal() const
|
||||
{
|
||||
debugAssert(_vertex.length() >= 3);
|
||||
return (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).direction();
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the same polygon with inverse winding.
|
||||
*/
|
||||
ConvexPolygon inverse() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ConvexPolyhedron
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Zero faces indicates an empty polyhedron
|
||||
*/
|
||||
Array<ConvexPolygon> face;
|
||||
|
||||
ConvexPolyhedron() {}
|
||||
ConvexPolyhedron(const Array<ConvexPolygon>& _face);
|
||||
|
||||
/**
|
||||
O(n) in the number of edges
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
/**
|
||||
O(n) in the number of edges
|
||||
*/
|
||||
float getVolume() const;
|
||||
|
||||
/**
|
||||
Cuts the polyhedron at the plane. If the polyhedron is entirely above or below
|
||||
the plane, one of the returned polyhedra will be empty.
|
||||
|
||||
@param above The part of the polyhedron above (on the side the
|
||||
normal points to or in the plane) the plane
|
||||
@param below The part of the polyhedron below the plane.
|
||||
*/
|
||||
void cut(const Plane& plane, ConvexPolyhedron& above, ConvexPolyhedron& below);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
class ConvexPolygon2D
|
||||
{
|
||||
private:
|
||||
Array<Vector2> m_vertex;
|
||||
|
||||
public:
|
||||
ConvexPolygon2D() {}
|
||||
|
||||
/**
|
||||
Points are counter-clockwise in a Y = down, X = right coordinate
|
||||
system.
|
||||
|
||||
@param reverse If true, the points are reversed (i.e. winding direction is changed)
|
||||
before the polygon is created.
|
||||
*/
|
||||
ConvexPolygon2D(const Array<Vector2>& pts, bool reverse = false);
|
||||
|
||||
inline int numVertices() const
|
||||
{
|
||||
return m_vertex.size();
|
||||
}
|
||||
|
||||
inline const Vector2& vertex(int index) const
|
||||
{
|
||||
debugAssert((index >= 0) && (index <= m_vertex.size()));
|
||||
return m_vertex[index];
|
||||
}
|
||||
|
||||
/** @param reverseWinding If true, the winding direction of the polygon is reversed for this test.*/
|
||||
bool contains(const Vector2& p, bool reverseWinding = false) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
#endif
|
||||
506
engine/3d/src/CoordinateFrame.cpp
Normal file
506
engine/3d/src/CoordinateFrame.cpp
Normal file
@@ -0,0 +1,506 @@
|
||||
/**
|
||||
@file CoordinateFrame.cpp
|
||||
|
||||
Coordinate frame class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2010-03-13
|
||||
|
||||
Copyright 2000-2010, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Quat.hpp"
|
||||
#include "Matrix4.hpp"
|
||||
#include "Box.hpp"
|
||||
#include "AABox.hpp"
|
||||
#include "Sphere.hpp"
|
||||
#include "Triangle.hpp"
|
||||
#include "Ray.hpp"
|
||||
#include "Capsule.hpp"
|
||||
#include "Cylinder.hpp"
|
||||
#include "UprightFrame.hpp"
|
||||
#include "stringutils.hpp"
|
||||
#include "PhysicsFrame.hpp"
|
||||
#include "UprightFrame.hpp"
|
||||
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
|
||||
std::string CoordinateFrame::toXYZYPRDegreesString() const
|
||||
{
|
||||
UprightFrame uframe(*this);
|
||||
|
||||
return format("CFrame::fromXYZYPRDegrees(% 5.1ff, % 5.1ff, % 5.1ff, % 5.1ff, % 5.1ff, % 5.1ff)", uframe.translation.x, uframe.translation.y,
|
||||
uframe.translation.z, toDegrees(uframe.yaw), toDegrees(uframe.pitch), 0.0f);
|
||||
}
|
||||
|
||||
CoordinateFrame CoordinateFrame::fromXYZYPRRadians(float x, float y, float z, float yaw, float pitch, float roll)
|
||||
{
|
||||
Matrix3 rotation = Matrix3::fromAxisAngleFast(Vector3::unitY(), yaw);
|
||||
|
||||
rotation = Matrix3::fromAxisAngleFast(rotation.column(0), pitch) * rotation;
|
||||
rotation = Matrix3::fromAxisAngleFast(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;
|
||||
}
|
||||
|
||||
// Aya
|
||||
bool CoordinateFrame::fuzzyEq(const CoordinateFrame& other, double absepsilon) 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], absepsilon))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!G3D::fuzzyEq(translation[c], other.translation[c], absepsilon))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Aya
|
||||
#if 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);
|
||||
}
|
||||
#else
|
||||
Plane CoordinateFrame::toObjectSpace(const Plane& p) const
|
||||
{
|
||||
return Plane(normalToObjectSpace(p.normal()), pointToObjectSpace(p.normal() * p.distance()));
|
||||
}
|
||||
|
||||
|
||||
Plane CoordinateFrame::toWorldSpace(const Plane& p) const
|
||||
{
|
||||
return Plane(normalToWorldSpace(p.normal()), pointToWorldSpace(p.normal() * p.distance()));
|
||||
}
|
||||
#endif
|
||||
// =============
|
||||
|
||||
Triangle CoordinateFrame::toObjectSpace(const Triangle& t) const
|
||||
{
|
||||
return Triangle(pointToObjectSpace(t.vertex(0)), pointToObjectSpace(t.vertex(1)), pointToObjectSpace(t.vertex(2)));
|
||||
}
|
||||
|
||||
// Aya
|
||||
Line CoordinateFrame::toWorldSpace(const Line& l) const
|
||||
{
|
||||
return Line::fromPointAndUnitDirection(pointToWorldSpace(l.point()), vectorToWorldSpace(l.direction()));
|
||||
}
|
||||
//========
|
||||
|
||||
Triangle CoordinateFrame::toWorldSpace(const Triangle& t) const
|
||||
{
|
||||
return Triangle(pointToWorldSpace(t.vertex(0)), pointToWorldSpace(t.vertex(1)), pointToWorldSpace(t.vertex(2)));
|
||||
}
|
||||
|
||||
|
||||
Cylinder CoordinateFrame::toWorldSpace(const Cylinder& c) const
|
||||
{
|
||||
return Cylinder(pointToWorldSpace(c.point(0)), pointToWorldSpace(c.point(1)), c.radius());
|
||||
}
|
||||
|
||||
|
||||
Capsule CoordinateFrame::toWorldSpace(const Capsule& c) const
|
||||
{
|
||||
return Capsule(pointToWorldSpace(c.point(0)), pointToWorldSpace(c.point(1)), c.radius());
|
||||
}
|
||||
|
||||
|
||||
Box CoordinateFrame::toWorldSpace(const AABox& b) const
|
||||
{
|
||||
Box b2(b);
|
||||
return toWorldSpace(b2);
|
||||
}
|
||||
|
||||
|
||||
Box CoordinateFrame::toWorldSpace(const Box& b) const
|
||||
{
|
||||
Box out(b);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
out._corner[i] = pointToWorldSpace(b._corner[i]);
|
||||
debugAssert(!isNaN(out._corner[i].x));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
out._axis[i] = vectorToWorldSpace(b._axis[i]);
|
||||
}
|
||||
|
||||
out._center = pointToWorldSpace(b._center);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Box CoordinateFrame::toObjectSpace(const Box& b) const
|
||||
{
|
||||
return inverse().toWorldSpace(b);
|
||||
}
|
||||
|
||||
|
||||
Box CoordinateFrame::toObjectSpace(const AABox& b) const
|
||||
{
|
||||
return toObjectSpace(Box(b));
|
||||
}
|
||||
|
||||
AABox CoordinateFrame::AABBtoWorldSpace(const AABox& b) const
|
||||
{
|
||||
Vector3 center = b.center();
|
||||
Vector3 halfSize = b.extent() * 0.5f;
|
||||
|
||||
Vector3 newCenter = pointToWorldSpace(center);
|
||||
Vector3 newHalfSize = Vector3(fabs(rotation[0][0]) * halfSize[0] + fabs(rotation[0][1]) * halfSize[1] + fabs(rotation[0][2]) * halfSize[2],
|
||||
fabs(rotation[1][0]) * halfSize[0] + fabs(rotation[1][1]) * halfSize[1] + fabs(rotation[1][2]) * halfSize[2],
|
||||
fabs(rotation[2][0]) * halfSize[0] + fabs(rotation[2][1]) * halfSize[1] + fabs(rotation[2][2]) * halfSize[2]);
|
||||
|
||||
return AABox(newCenter - newHalfSize, newCenter + newHalfSize);
|
||||
}
|
||||
|
||||
AABox CoordinateFrame::AABBtoObjectSpace(const AABox& b) const
|
||||
{
|
||||
Vector3 center = b.center();
|
||||
Vector3 halfSize = b.extent() * 0.5f;
|
||||
|
||||
Vector3 newCenter = pointToObjectSpace(center);
|
||||
Vector3 newHalfSize = Vector3(fabs(rotation[0][0]) * halfSize[0] + fabs(rotation[1][0]) * halfSize[1] + fabs(rotation[2][0]) * halfSize[2],
|
||||
fabs(rotation[0][1]) * halfSize[0] + fabs(rotation[1][1]) * halfSize[1] + fabs(rotation[2][1]) * halfSize[2],
|
||||
fabs(rotation[0][2]) * halfSize[0] + fabs(rotation[1][2]) * halfSize[1] + fabs(rotation[2][2]) * halfSize[2]);
|
||||
|
||||
return AABox(newCenter - newHalfSize, newCenter + newHalfSize);
|
||||
}
|
||||
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
Aya::RbxRay CoordinateFrame::toObjectSpace(const Aya::RbxRay& r) const
|
||||
{
|
||||
return Aya::RbxRay::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
|
||||
{
|
||||
const Quat q1(this->rotation);
|
||||
const Quat q2(other.rotation);
|
||||
|
||||
return CoordinateFrame(q1.slerp(q2, alpha).toRotationMatrix(), translation * (1 - alpha) + other.translation * alpha);
|
||||
}
|
||||
}
|
||||
|
||||
CoordinateFrame CoordinateFrame::nlerp(const CoordinateFrame& other, float alpha) const
|
||||
{
|
||||
if (alpha == 1.0f)
|
||||
{
|
||||
return other;
|
||||
}
|
||||
else if (alpha == 0.0f)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Quat q1(this->rotation);
|
||||
const Quat q2(other.rotation);
|
||||
|
||||
return CoordinateFrame(q1.nlerp(q2, alpha).toRotationMatrix(), translation * (1 - alpha) + other.translation * alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const
|
||||
{
|
||||
vout.resize(v.size());
|
||||
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
{
|
||||
vout[i] = pointToWorldSpace(v[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CoordinateFrame::normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const
|
||||
{
|
||||
vout.resize(v.size());
|
||||
|
||||
for (int i = 0; i < v.size(); ++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]);
|
||||
}
|
||||
}
|
||||
|
||||
btTransform CoordinateFrame::transformFromCFrame() const
|
||||
{
|
||||
btMatrix3x3 rot(rotation[0][0], rotation[0][1], rotation[0][2], rotation[1][0], rotation[1][1], rotation[1][2], rotation[2][0], rotation[2][1],
|
||||
rotation[2][2]);
|
||||
|
||||
return btTransform(rot, btVector3(translation.x, translation.y, translation.z));
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
372
engine/3d/src/CoordinateFrame.hpp
Normal file
372
engine/3d/src/CoordinateFrame.hpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
@file CoordinateFrame.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-03-04
|
||||
@edited 2009-04-29
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_CFrame_h
|
||||
#define G3D_CFrame_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "Matrix3.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Line.hpp"
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <assert.h>
|
||||
|
||||
// Aya
|
||||
#include "RbxRay.hpp"
|
||||
// ====
|
||||
|
||||
// Bullet
|
||||
#include "LinearMath/btTransform.hpp"
|
||||
// ====
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Turn off "conditional expression is constant" warning; MSVC generates this
|
||||
// for debug assertions in inlined methods.
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
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;
|
||||
// Aya
|
||||
bool fuzzyEq(const CoordinateFrame& other, double absepsilon) const;
|
||||
// =====
|
||||
|
||||
bool fuzzyIsIdentity() const;
|
||||
|
||||
bool isIdentity() const;
|
||||
|
||||
/**
|
||||
Initializes to the identity coordinate frame.
|
||||
*/
|
||||
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())
|
||||
{
|
||||
}
|
||||
|
||||
static CoordinateFrame fromXYZYPRRadians(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
|
||||
|
||||
std::string toXYZYPRDegreesString() const;
|
||||
|
||||
/** Construct a coordinate frame from translation = (x,y,z) and
|
||||
rotations (in that order) about Y, object space X, object space
|
||||
Z. Note that because object-space axes are used, these are not
|
||||
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);
|
||||
|
||||
/**
|
||||
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.column(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);
|
||||
}
|
||||
|
||||
// Aya
|
||||
Line toWorldSpace(const Line& l) const;
|
||||
//======
|
||||
|
||||
/**
|
||||
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;
|
||||
|
||||
// Aya
|
||||
Aya::RbxRay toObjectSpace(const Aya::RbxRay& 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;
|
||||
|
||||
class AABox AABBtoWorldSpace(const AABox& b) const;
|
||||
|
||||
class AABox AABBtoObjectSpace(const AABox& b) 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));
|
||||
}
|
||||
|
||||
// Aya Added by DB
|
||||
inline static void mul(const CoordinateFrame& a, const CoordinateFrame& b, CoordinateFrame& answer)
|
||||
{
|
||||
Matrix3::fastMul(a.rotation, b.rotation, answer.rotation);
|
||||
answer.translation = a.pointToWorldSpace(b.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.column(2);
|
||||
}
|
||||
|
||||
/** Returns the ray starting at the camera origin travelling in direction CoordinateFrame::lookVector. */
|
||||
class Ray lookRay() const;
|
||||
|
||||
/** Up direction for this camera (its y axis). */
|
||||
inline Vector3 upVector() const
|
||||
{
|
||||
return rotation.column(1);
|
||||
}
|
||||
|
||||
inline Vector3 rightVector() const
|
||||
{
|
||||
return rotation.column(0);
|
||||
}
|
||||
|
||||
/**
|
||||
If a viewer looks along the look vector, this is the viewer's "left".
|
||||
Useful for strafing motions and building alternative coordinate frames.
|
||||
*/
|
||||
inline Vector3 leftVector() const
|
||||
{
|
||||
return -rotation.column(0);
|
||||
}
|
||||
|
||||
/**
|
||||
Linearly interpolates between two coordinate frames, using
|
||||
Quat::slerp for the rotations.
|
||||
*/
|
||||
CoordinateFrame lerp(const CoordinateFrame& other, float alpha) const;
|
||||
|
||||
// Roblox
|
||||
CoordinateFrame nlerp(const CoordinateFrame& other, float alpha) const;
|
||||
////
|
||||
|
||||
btTransform transformFromCFrame() const;
|
||||
};
|
||||
|
||||
typedef CoordinateFrame CFrame;
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
54
engine/3d/src/Crypto.cpp
Normal file
54
engine/3d/src/Crypto.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
@file Crypto.cpp
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
|
||||
@created 2006-03-28
|
||||
@edited 2006-04-06
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Crypto.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#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);
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
97
engine/3d/src/Crypto.hpp
Normal file
97
engine/3d/src/Crypto.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
@file Crypto.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
|
||||
@created 2006-03-29
|
||||
@edited 2006-04-06
|
||||
*/
|
||||
|
||||
#ifndef G3D_CRYPTO_H
|
||||
#define G3D_CRYPTO_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** 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();
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
170
engine/3d/src/Cylinder.cpp
Normal file
170
engine/3d/src/Cylinder.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
@file Cylinder.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-07
|
||||
@edited 2006-02-18
|
||||
|
||||
Copyright 2000-2006, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Cylinder.hpp"
|
||||
#include "LineSegment.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "AABox.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
Cylinder::Cylinder() {}
|
||||
|
||||
|
||||
Cylinder::Cylinder(const Vector3& _p1, const Vector3& _p2, float _r)
|
||||
: p1(_p1)
|
||||
, p2(_p2)
|
||||
, mRadius(_r)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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 G3D
|
||||
94
engine/3d/src/Cylinder.hpp
Normal file
94
engine/3d/src/Cylinder.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
@file Cylinder.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-07
|
||||
@edited 2005-09-26
|
||||
|
||||
Copyright 2000-2005, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_Cylinder_H
|
||||
#define G3D_Cylinder_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
class Line;
|
||||
class AABox;
|
||||
/**
|
||||
Right cylinder
|
||||
*/
|
||||
class Cylinder
|
||||
{
|
||||
private:
|
||||
Vector3 p1;
|
||||
Vector3 p2;
|
||||
|
||||
float mRadius;
|
||||
|
||||
public:
|
||||
/** Uninitialized */
|
||||
Cylinder();
|
||||
Cylinder(const Vector3& _p1, const Vector3& _p2, float _r);
|
||||
|
||||
|
||||
/** 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 G3D
|
||||
|
||||
#endif
|
||||
173
engine/3d/src/Draw.cpp
Normal file
173
engine/3d/src/Draw.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
|
||||
|
||||
#include "Draw.hpp"
|
||||
#include "DrawPrimitives.hpp"
|
||||
#include "DrawAdorn.hpp"
|
||||
#include "Base/Part.hpp"
|
||||
#include "Base/Adorn.hpp"
|
||||
#include "Utility/Math.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
static const int SelectionBoxLineThreshold = 1500; //!< tweakable threshold before boxes switch to lines
|
||||
|
||||
bool Draw::m_showHoverOver = true;
|
||||
G3D::Color4 Draw::m_hoverOverColor(0.7f, 0.9f, 1.0f, 1.0f); // 178, 229, 255
|
||||
G3D::Color4 Draw::m_selectColor(0.1f, 0.6f, 1.0f, 1.0f); // 25, 153, 256
|
||||
|
||||
void Draw::partAdorn(const Part& part, Adorn* adorn, const G3D::Color3& controllerColor)
|
||||
{
|
||||
adornSurfaces(part, adorn, controllerColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color used for the selection box when a part is selected.
|
||||
*/
|
||||
void Draw::setSelectColor(const G3D::Color4& Color)
|
||||
{
|
||||
m_selectColor = Color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color used for the selection box when a part is mouse hover over.
|
||||
*/
|
||||
void Draw::setHoverOverColor(const G3D::Color4& color)
|
||||
{
|
||||
m_hoverOverColor = color;
|
||||
}
|
||||
|
||||
void Draw::selectionBox(const Part& part, Adorn* adorn, SelectState selectState, float lineThickness)
|
||||
{
|
||||
if (selectState == SELECT_HOVER && m_showHoverOver)
|
||||
{
|
||||
adorn->setObjectToWorldMatrix(part.coordinateFrame);
|
||||
selectionBox(part, adorn, m_hoverOverColor, lineThickness * 2);
|
||||
}
|
||||
else if (selectState != SELECT_HOVER)
|
||||
{
|
||||
selectionBox(part, adorn, (selectState == SELECT_NORMAL) ? selectColor() : Color3::orange(), lineThickness);
|
||||
}
|
||||
}
|
||||
|
||||
void Draw::selectionBox(ModelInstance& model, Adorn* adorn, const G3D::Color4& selectColor, float lineThickness)
|
||||
{
|
||||
selectionBox(model.computePart(), adorn, selectColor, lineThickness);
|
||||
}
|
||||
|
||||
void Draw::adornSurfaces(const Part& part, Adorn* adorn, const G3D::Color3& controllerColor)
|
||||
{
|
||||
for (int face = 0; face < 6; ++face)
|
||||
{
|
||||
switch (part.surfaceType[face])
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case ROTATE:
|
||||
case ROTATE_P:
|
||||
case ROTATE_V:
|
||||
Draw::constraint(part, adorn, face, controllerColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Draw::constraint(const Part& part, Adorn* adorn, int face, const G3D::Color3& controllerColor)
|
||||
{
|
||||
SurfaceType surfaceType = part.surfaceType[face];
|
||||
|
||||
debugAssert((surfaceType == ROTATE) || (surfaceType == ROTATE_P) || (surfaceType == ROTATE_V));
|
||||
|
||||
Vector3 halfSize = part.gridSize * 0.5;
|
||||
|
||||
const Matrix3& relativeRotation = Math::getAxisRotationMatrix(face);
|
||||
Vector3 relativeTranslation;
|
||||
|
||||
int normal = face % 3;
|
||||
float posNeg = face > 2 ? -1.0f : 1.0f;
|
||||
relativeTranslation[normal] = halfSize[normal] * posNeg;
|
||||
|
||||
CoordinateFrame translation(relativeRotation, relativeTranslation);
|
||||
CoordinateFrame newObject = part.coordinateFrame * translation;
|
||||
|
||||
adorn->setObjectToWorldMatrix(newObject);
|
||||
|
||||
float axis = 1.0f;
|
||||
float radius = 0.2f;
|
||||
adorn->cylinderAlongX(radius, axis, Color3::yellow());
|
||||
|
||||
if ((surfaceType == ROTATE_V) || (surfaceType == ROTATE_P))
|
||||
{
|
||||
float axis = 0.25f;
|
||||
float radius = 0.4f;
|
||||
adorn->cylinderAlongX(radius, axis, controllerColor);
|
||||
}
|
||||
}
|
||||
|
||||
void Draw::selectionBox(const Part& part, Adorn* adorn, const G3D::Color4& selectColor, float lineThickness)
|
||||
{
|
||||
if (adorn->getCamera() != NULL)
|
||||
{
|
||||
// determine distance from camera to object
|
||||
const Vector3 camera_pos = adorn->getCamera()->getCameraCoordinateFrame().translation;
|
||||
const Vector3 delta = part.coordinateFrame.translation - camera_pos;
|
||||
const float delta_squared = delta.squaredLength();
|
||||
|
||||
// determine size of object by using longest axis
|
||||
const float grid_size = part.gridSize[part.gridSize.primaryAxis()];
|
||||
const float grid_size_squared = grid_size * grid_size;
|
||||
|
||||
// make sure the distance is greater than the size
|
||||
if (delta_squared > grid_size_squared)
|
||||
{
|
||||
// adjust distance based on thickness of lines
|
||||
const float threshold_dist = SelectionBoxLineThreshold * lineThickness;
|
||||
const float threshold_dist_squared = threshold_dist * threshold_dist;
|
||||
|
||||
// subtract the size from the distance and compare to the threshold
|
||||
if (delta_squared - grid_size_squared > threshold_dist_squared)
|
||||
{
|
||||
// switch to drawing lines
|
||||
const Vector3 half_size = part.gridSize / 2;
|
||||
adorn->setObjectToWorldMatrix(part.coordinateFrame);
|
||||
DrawAdorn::outlineBox(adorn, Extents(-half_size, half_size), selectColor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adorn->setObjectToWorldMatrix(part.coordinateFrame);
|
||||
|
||||
Vector3 halfSize = 0.5f * part.gridSize;
|
||||
|
||||
float highlight = lineThickness;
|
||||
|
||||
for (int c1 = 0; c1 < 3; ++c1)
|
||||
{ // x, y, z
|
||||
int c2 = (c1 + 1) % 3; // y
|
||||
int c3 = (c1 + 2) % 3; // z
|
||||
for (int d2 = -1; d2 < 2; d2 += 2)
|
||||
{
|
||||
for (int d3 = -1; d3 < 2; d3 += 2)
|
||||
{
|
||||
|
||||
// for c1, shorten the length of the edge segment by highlight size
|
||||
// on both ends so that the edge segments do not overlap on the
|
||||
// corners
|
||||
Vector3 p0, p1;
|
||||
p0[c1] = -halfSize[c1] - (c1 == 0 ? highlight : -highlight);
|
||||
p0[c2] = d2 * halfSize[c2] - highlight;
|
||||
p0[c3] = d3 * halfSize[c3] - highlight;
|
||||
p1[c1] = halfSize[c1] + (c1 == 0 ? highlight : -highlight);
|
||||
p1[c2] = d2 * halfSize[c2] + highlight;
|
||||
p1[c3] = d3 * halfSize[c3] + highlight;
|
||||
|
||||
if (p0.x <= p1.x && p0.y <= p1.y && p0.z <= p1.z)
|
||||
adorn->box(AABox(p0, p1), selectColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
74
engine/3d/src/Draw.hpp
Normal file
74
engine/3d/src/Draw.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SelectState.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class Rect2D;
|
||||
class CoordinateFrame;
|
||||
class Ray;
|
||||
class RenderDevice;
|
||||
class Color4;
|
||||
} // namespace G3D
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Part;
|
||||
class Details;
|
||||
class Adorn;
|
||||
|
||||
class Draw
|
||||
{
|
||||
private:
|
||||
// generic
|
||||
static void adornSurfaces(const Part& part, Adorn* adorn, const G3D::Color3& controllerColor);
|
||||
static void frameBox(const Part& part, Adorn* adorn, const Color4& color);
|
||||
static void constraint(const Part& part, Adorn* adorn, int face, const G3D::Color3& controllerColor);
|
||||
|
||||
static bool m_showHoverOver;
|
||||
static G3D::Color4 m_hoverOverColor;
|
||||
static G3D::Color4 m_selectColor;
|
||||
static G3D::Color4 m_ayaSelectColor;
|
||||
public:
|
||||
static const G3D::Color4& selectColor()
|
||||
{
|
||||
return m_selectColor;
|
||||
}
|
||||
|
||||
static void setSelectColor(const G3D::Color4& Color);
|
||||
|
||||
static const G3D::Color4& hoverOverColor()
|
||||
{
|
||||
return m_hoverOverColor;
|
||||
}
|
||||
static void setHoverOverColor(const G3D::Color4& color);
|
||||
|
||||
// DRAWING - assumes 2D mode
|
||||
static void selectionSquare(const G3D::Rect2D& rect, float thick);
|
||||
|
||||
static void partAdorn(const Part& part, Adorn* adorn, const G3D::Color3& controllerColor);
|
||||
|
||||
static void selectionBox(const Part& part, Adorn* adorn, const G3D::Color4& selectColor, float lineThickness = 0.15f);
|
||||
|
||||
static void selectionBox(ModelInstance& model, Adorn* adorn, const G3D::Color4& selectColor, float lineThickness = 0.15f);
|
||||
|
||||
static void selectionBox(const Part& part, Adorn* adorn, SelectState selectState, float lineThickness = 0.15f);
|
||||
|
||||
static void showHoverOver(bool Show)
|
||||
{
|
||||
m_showHoverOver = Show;
|
||||
}
|
||||
static bool isHoverOver()
|
||||
{
|
||||
return m_showHoverOver;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
981
engine/3d/src/DrawAdorn.cpp
Normal file
981
engine/3d/src/DrawAdorn.cpp
Normal file
@@ -0,0 +1,981 @@
|
||||
|
||||
|
||||
#include "DrawAdorn.hpp"
|
||||
#include "DrawPrimitives.hpp"
|
||||
#include "Base/Part.hpp"
|
||||
#include "Base/Adorn.hpp"
|
||||
#include "HitTest.hpp"
|
||||
#include "Utility/Math.hpp"
|
||||
#include "Utility/Rect.hpp"
|
||||
#include "Utility/Extents.hpp"
|
||||
#include "Utility/HitTest.hpp"
|
||||
#include "Utility/Face.hpp"
|
||||
#include "Base/MeshGen.hpp"
|
||||
#include "DataModel/Camera.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "Color3uint8.hpp"
|
||||
|
||||
FASTFLAGVARIABLE(Studio3DGridUseAALines, true)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
static const float ZEROPLANE_GRID_SIZE_BASE = 400.0f;
|
||||
static const float ZEROPLANE_GRID_FACTOR = 8.0f;
|
||||
static const float HandleOffset = 0.5f;
|
||||
static const float RotationGridLineThickness = 0.045f;
|
||||
static const float RotationGridLineMinAngle = Math::degreesToRadians(2.5f);
|
||||
static const float TorusThickness = 0.025f;
|
||||
static const float TorusThicknessMinAngle = Math::degreesToRadians(2.5f);
|
||||
static const float SphereRadius = 0.5f;
|
||||
static const float SphereMinAngle = Math::degreesToRadians(2.0f);
|
||||
static const float ArrowLength = 3.0f;
|
||||
static const float ArrowMinAngle = Math::degreesToRadians(3.5f);
|
||||
static const float HandleTransparency = 0.65f;
|
||||
static const float AxisTransparency = 0.28f;
|
||||
|
||||
const Color3 DrawAdorn::axisColors[3] = {Color3::red(), Color3::green(), Color3::blue()};
|
||||
|
||||
// common colors
|
||||
const Color3 DrawAdorn::beige(G3D::Color3uint8(0xF5, 0xF5, 0xDC));
|
||||
const Color3 DrawAdorn::darkblue(G3D::Color3uint8(0x00, 0x00, 0x8B));
|
||||
const Color3 DrawAdorn::powderblue(G3D::Color3uint8(0xB0, 0xE0, 0xE6));
|
||||
const Color3 DrawAdorn::skyblue(G3D::Color3uint8(0x87, 0xCE, 0xEB));
|
||||
const Color3 DrawAdorn::violet(G3D::Color3uint8(0xEE, 0x82, 0xEE));
|
||||
const Color3 DrawAdorn::slategray(G3D::Color3uint8(0x70, 0x80, 0x90));
|
||||
const Color3 DrawAdorn::aqua(G3D::Color3uint8(0x00, 0xFF, 0xFF));
|
||||
const Color3 DrawAdorn::tan(G3D::Color3uint8(0xD2, 0xB4, 0x8C));
|
||||
const Color3 DrawAdorn::wheat(G3D::Color3uint8(0xF5, 0xDE, 0xB3));
|
||||
const Color3 DrawAdorn::cornflowerblue(G3D::Color3uint8(0x64, 0x95, 0xED));
|
||||
const Color3 DrawAdorn::limegreen(G3D::Color3uint8(0x32, 0xCD, 0x32));
|
||||
const Color3 DrawAdorn::magenta(G3D::Color3uint8(0xFF, 0x00, 0xFF));
|
||||
const Color3 DrawAdorn::pink(G3D::Color3uint8(0xFF, 0xC0, 0xCB));
|
||||
const Color3 DrawAdorn::silver(G3D::Color3uint8(0xC0, 0xC0, 0xC0));
|
||||
|
||||
void DrawAdorn::cylinder(Adorn* adorn, const CoordinateFrame& worldC, int axis, float length, float radius, const Color4& color, bool cap)
|
||||
{
|
||||
const Matrix3& relativeRotation = Math::getAxisRotationMatrix(axis);
|
||||
CoordinateFrame rotatedWorldC(worldC.rotation * relativeRotation, worldC.translation);
|
||||
|
||||
adorn->setObjectToWorldMatrix(rotatedWorldC);
|
||||
adorn->cylinderAlongX(radius, length, color, cap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DrawAdorn::faceInWorld(Adorn* adorn, const Face& face, float thickness, const Color4& color)
|
||||
{
|
||||
Vector3 x = (face[1] - face[0]);
|
||||
Vector3 y = (face[3] - face[0]);
|
||||
Vector3 z = x.cross(y);
|
||||
Vector3 center = (face[0] + face[2]) * 0.5;
|
||||
|
||||
CoordinateFrame c(center);
|
||||
|
||||
c.rotation.setColumn(0, x.unit());
|
||||
c.rotation.setColumn(1, y.unit());
|
||||
c.rotation.setColumn(2, z.unit());
|
||||
|
||||
adorn->setObjectToWorldMatrix(c);
|
||||
|
||||
float dx = x.magnitude() * 0.5f;
|
||||
float dy = y.magnitude() * 0.5f;
|
||||
|
||||
|
||||
Extents extents(Vector3(-dx, -dy, -thickness), Vector3(dx, dy, thickness));
|
||||
adorn->box(extents, color);
|
||||
}
|
||||
|
||||
|
||||
void DrawAdorn::surfaceBorder(Adorn* adorn, const Vector3& halfRealSize, float highlight, int surfaceId, const Color4& color)
|
||||
{
|
||||
int c1 = surfaceId % 3; // x, y, or z - primary
|
||||
float cZ = halfRealSize[c1] * ((surfaceId < 3) ? 1 : -1);
|
||||
Vector3 p0, p1;
|
||||
p0[c1] = cZ - highlight;
|
||||
p1[c1] = cZ + highlight;
|
||||
|
||||
int c2 = (c1 + 1) % 3; // y
|
||||
int c3 = (c1 + 2) % 3; // z
|
||||
for (int direction = 0; direction < 2; ++direction)
|
||||
{
|
||||
int cY = direction ? c2 : c3;
|
||||
int cZ = direction ? c3 : c2;
|
||||
for (int polarity = -1; polarity <= 1; polarity += 2)
|
||||
{
|
||||
p0[cY] = polarity * halfRealSize[cY] - highlight;
|
||||
p1[cY] = polarity * halfRealSize[cY] + highlight;
|
||||
p0[cZ] = -halfRealSize[cZ] - highlight;
|
||||
p1[cZ] = halfRealSize[cZ] + highlight;
|
||||
|
||||
AABox(p0, p1);
|
||||
|
||||
adorn->box(AABox(p0, p1), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAdorn::surfaceGridOnFace(const Primitive& prim, Adorn* adorn, int surfaceId, const Color4& color, int boxesPerStud)
|
||||
{
|
||||
CoordinateFrame bodyCoord = prim.getCoordinateFrame();
|
||||
CoordinateFrame faceCoord = bodyCoord * prim.getConstGeometry()->getSurfaceCoordInBody(surfaceId);
|
||||
|
||||
Vector4 bounds(-1e10f, -1e10f, 1e10f, 1e10f);
|
||||
for (int i = 0; i < prim.getConstGeometry()->getNumVertsInSurface(surfaceId); i++)
|
||||
{
|
||||
Vector3 vertInBody = prim.getConstGeometry()->getSurfaceVertInBody(surfaceId, i);
|
||||
Vector3 vertInWorld = bodyCoord.pointToWorldSpace(vertInBody);
|
||||
Vector3 vertInFace = faceCoord.pointToObjectSpace(vertInWorld);
|
||||
|
||||
// max vector
|
||||
if (vertInFace.x > bounds.x)
|
||||
bounds.x = vertInFace.x;
|
||||
if (vertInFace.y > bounds.y)
|
||||
bounds.y = vertInFace.y;
|
||||
|
||||
// Min vector
|
||||
if (vertInFace.x < bounds.z)
|
||||
bounds.z = vertInFace.x;
|
||||
if (vertInFace.y < bounds.w)
|
||||
bounds.w = vertInFace.y;
|
||||
}
|
||||
surfaceGridAtCoord(adorn, faceCoord, bounds, Vector3::unitX(), Vector3::unitY(), color, boxesPerStud);
|
||||
}
|
||||
|
||||
|
||||
void DrawAdorn::zeroPlaneGrid(Adorn* adorn, const Aya::Camera& camera, const int studsPerBox, const int yLevel, const G3D::Color4& smallGridColor,
|
||||
const G3D::Color4& largeGridColor)
|
||||
{
|
||||
const Vector3 cameraPos = camera.getCameraCoordinateFrame().translation;
|
||||
const Vector3 zeroPlaneCenter = G3D::Plane(Vector3(0, 1, 0), Vector3(0, yLevel, 0)).closestPoint(cameraPos);
|
||||
const int x = (int)(Math::iRound((zeroPlaneCenter.x + studsPerBox / 2.0f) / studsPerBox) * studsPerBox);
|
||||
const int z = (int)(Math::iRound((zeroPlaneCenter.z + studsPerBox / 2.0f) / studsPerBox) * studsPerBox);
|
||||
const Vector3 zeroPlaneCenterOnGrid = Vector3(x, zeroPlaneCenter.y, z);
|
||||
const float distFromPlane = (cameraPos - zeroPlaneCenter).magnitude();
|
||||
const float lineThickness = 2;
|
||||
|
||||
const int largeGridStep = studsPerBox * ZEROPLANE_GRID_FACTOR;
|
||||
const int lineSegmentStep = largeGridStep;
|
||||
|
||||
adorn->setObjectToWorldMatrix(Vector3::zero());
|
||||
|
||||
// if we are close enough to the zero plane, lets render the smaller grid
|
||||
if (distFromPlane < ZEROPLANE_GRID_SIZE_BASE)
|
||||
{
|
||||
const float zEnd = ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.z;
|
||||
const float zStart = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.z;
|
||||
const float xEnd = ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.x;
|
||||
const float xStart = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.x;
|
||||
|
||||
const int zeroPlaneSmallCenterX = Math::iRound(zeroPlaneCenterOnGrid.x / studsPerBox) * studsPerBox;
|
||||
const int zeroPlaneSmallCenterZ = Math::iRound(zeroPlaneCenterOnGrid.z / studsPerBox) * studsPerBox;
|
||||
|
||||
for (int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterX; i < (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterX); i += studsPerBox)
|
||||
{
|
||||
float lastStep = zStart;
|
||||
for (int j = zStart + lineSegmentStep; j <= zEnd + lineSegmentStep; j += lineSegmentStep)
|
||||
{
|
||||
const float cameraToPlaneDistance = (Vector3(i, zeroPlaneCenterOnGrid.y, j + (j - lastStep) / 2.0f) - cameraPos).magnitude();
|
||||
const float alpha = std::max(1.0f - (cameraToPlaneDistance / ZEROPLANE_GRID_SIZE_BASE), 0.0f);
|
||||
if (alpha > 0.0f)
|
||||
{
|
||||
if (FFlag::Studio3DGridUseAALines)
|
||||
adorn->line3dAA(Vector3(i, zeroPlaneCenterOnGrid.y, lastStep), Vector3(i, zeroPlaneCenterOnGrid.y, j),
|
||||
Aya::Color4(smallGridColor.rgb(), alpha), lineThickness, 0, false);
|
||||
else
|
||||
adorn->line3d(Vector3(i, zeroPlaneCenterOnGrid.y, lastStep), Vector3(i, zeroPlaneCenterOnGrid.y, j),
|
||||
Aya::Color4(smallGridColor.rgb(), alpha));
|
||||
}
|
||||
lastStep = j;
|
||||
}
|
||||
}
|
||||
for (int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterZ; i < (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterZ); i += studsPerBox)
|
||||
{
|
||||
float lastStep = xStart;
|
||||
for (int j = xStart + lineSegmentStep; j <= xEnd + lineSegmentStep; j += lineSegmentStep)
|
||||
{
|
||||
const float cameraToPlaneDistance = (Vector3(j + (j - lastStep) / 2.0f, zeroPlaneCenterOnGrid.y, i) - cameraPos).magnitude();
|
||||
const float alpha = std::max(1.0f - (cameraToPlaneDistance / ZEROPLANE_GRID_SIZE_BASE), 0.0f);
|
||||
if (alpha > 0.0f)
|
||||
{
|
||||
if (FFlag::Studio3DGridUseAALines)
|
||||
adorn->line3dAA(Vector3(lastStep, zeroPlaneCenterOnGrid.y, i), Vector3(j, zeroPlaneCenterOnGrid.y, i),
|
||||
Aya::Color4(smallGridColor.rgb(), alpha), lineThickness, 0, false);
|
||||
else
|
||||
adorn->line3d(Vector3(lastStep, zeroPlaneCenterOnGrid.y, i), Vector3(j, zeroPlaneCenterOnGrid.y, i),
|
||||
Aya::Color4(smallGridColor.rgb(), alpha));
|
||||
}
|
||||
lastStep = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now render the large grid
|
||||
const int zeroPlaneLargeCenterX = Math::iRound(zeroPlaneCenterOnGrid.x / largeGridStep) * largeGridStep;
|
||||
const int zeroPlaneLargeCenterZ = Math::iRound(zeroPlaneCenterOnGrid.z / largeGridStep) * largeGridStep;
|
||||
|
||||
for (int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX; i <= (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX); i += largeGridStep)
|
||||
{
|
||||
const Vector3 lineStart = Vector3(i, zeroPlaneCenterOnGrid.y, -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
||||
const Vector3 lineEnd = Vector3(i, zeroPlaneCenterOnGrid.y, ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
||||
const Vector3 midPoint = lineEnd + ((lineEnd - lineStart) * 0.5f);
|
||||
const float distFromPlane = (cameraPos - midPoint).magnitude();
|
||||
|
||||
const float largeGridAlpha = std::max(1.0f - ((distFromPlane * 1.5f) / (ZEROPLANE_GRID_SIZE_BASE)), 0.25f);
|
||||
if (FFlag::Studio3DGridUseAALines)
|
||||
adorn->line3dAA(lineStart, lineEnd, Aya::Color4(largeGridColor.rgb(), largeGridAlpha), lineThickness, 0, false);
|
||||
else
|
||||
adorn->line3d(lineStart, lineEnd, Aya::Color4(largeGridColor.rgb(), largeGridAlpha));
|
||||
}
|
||||
for (int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ; i <= (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ); i += largeGridStep)
|
||||
{
|
||||
const Vector3 lineStart = Vector3(i, zeroPlaneCenterOnGrid.y, -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
||||
const Vector3 lineEnd = Vector3(i, zeroPlaneCenterOnGrid.y, ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
||||
const Vector3 midPoint = lineEnd + ((lineEnd - lineStart) * 0.5f);
|
||||
const float distFromPlane = (cameraPos - midPoint).magnitude();
|
||||
|
||||
const float largeGridAlpha = std::max(1.0f - ((distFromPlane * 1.5f) / (ZEROPLANE_GRID_SIZE_BASE)), 0.25f);
|
||||
if (FFlag::Studio3DGridUseAALines)
|
||||
adorn->line3dAA(Vector3(-ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX, zeroPlaneCenterOnGrid.y, i),
|
||||
Vector3(ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX, zeroPlaneCenterOnGrid.y, i),
|
||||
Aya::Color4(largeGridColor.rgb(), largeGridAlpha), lineThickness, 0, false);
|
||||
else
|
||||
adorn->line3d(Vector3(-ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX, zeroPlaneCenterOnGrid.y, i),
|
||||
Vector3(ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX, zeroPlaneCenterOnGrid.y, i),
|
||||
Aya::Color4(largeGridColor.rgb(), largeGridAlpha));
|
||||
}
|
||||
|
||||
// last, we render the axes rays at the origin
|
||||
static const float ArrowSize = 4.0f;
|
||||
if (camera.frustum().intersectsSphere(Vector3::zero(), ArrowSize))
|
||||
{
|
||||
adorn->setMaterial(Adorn::Material_SelfLitHighlight);
|
||||
adorn->setObjectToWorldMatrix(CoordinateFrame(Vector3(0, 0, 0)));
|
||||
adorn->ray(Aya::RbxRay(Vector3(0, 0, 0), Vector3(ArrowSize, 0, 0)), axisColors[0]);
|
||||
adorn->ray(Aya::RbxRay(Vector3(0, 0, 0), Vector3(0, ArrowSize, 0)), axisColors[1]);
|
||||
adorn->ray(Aya::RbxRay(Vector3(0, 0, 0), Vector3(0, 0, ArrowSize)), axisColors[2]);
|
||||
adorn->setMaterial(Adorn::Material_Default);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAdorn::surfaceGridAtCoord(Adorn* adorn, CoordinateFrame& cF, const Vector4& bounds, const Vector3& gridXDir, const Vector3& gridYDir,
|
||||
const G3D::Color4& color, int boxesPerStud)
|
||||
{
|
||||
int direction1 = 1;
|
||||
int direction2 = 0;
|
||||
int xMaxInt = Math::iRound(bounds.x + 1);
|
||||
int yMaxInt = Math::iRound(bounds.y + 1);
|
||||
int xMinInt = Math::iRound(bounds.z - 1);
|
||||
int yMinInt = Math::iRound(bounds.w - 1);
|
||||
|
||||
Matrix3 planeCoordRot =
|
||||
Math::fromDirectionCosines(gridXDir, gridYDir, gridXDir.cross(gridYDir), Vector3::unitX(), Vector3::unitY(), Vector3::unitZ());
|
||||
|
||||
CoordinateFrame planeCoord = cF;
|
||||
planeCoord.rotation *= planeCoordRot;
|
||||
CoordinateFrame adornCoord = planeCoord;
|
||||
|
||||
Vector3 ptXDir = Vector3::zero();
|
||||
ptXDir.y = 0.5f * (yMaxInt + yMinInt);
|
||||
for (int i = xMinInt; i <= xMaxInt; i++)
|
||||
{
|
||||
if (boxesPerStud == 0 && i > xMinInt)
|
||||
i = xMaxInt;
|
||||
ptXDir.x = (float)i;
|
||||
Vector3 ptInWorld = planeCoord.pointToWorldSpace(ptXDir);
|
||||
adornCoord.translation = ptInWorld;
|
||||
cylinder(adorn, adornCoord, direction1, (float)(yMaxInt - yMinInt), 0.03, color);
|
||||
if (i < xMaxInt)
|
||||
{
|
||||
for (int j = 1; j < boxesPerStud; j++)
|
||||
{
|
||||
ptXDir.x = (float)i + (1.0f / (float)boxesPerStud) * (float)j;
|
||||
ptInWorld = planeCoord.pointToWorldSpace(ptXDir);
|
||||
adornCoord.translation = ptInWorld;
|
||||
cylinder(adorn, adornCoord, direction1, (float)(yMaxInt - yMinInt), 0.01, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 ptYDir = Vector3::zero();
|
||||
ptYDir.x = 0.5f * (xMaxInt + xMinInt);
|
||||
for (int i = yMinInt; i <= yMaxInt; i++)
|
||||
{
|
||||
if (boxesPerStud == 0 && i > yMinInt)
|
||||
i = yMaxInt;
|
||||
ptYDir.y = (float)i;
|
||||
Vector3 ptInWorld = planeCoord.pointToWorldSpace(ptYDir);
|
||||
adornCoord.translation = ptInWorld;
|
||||
cylinder(adorn, adornCoord, direction2, (float)(xMaxInt - xMinInt), 0.03, color);
|
||||
if (i < yMaxInt)
|
||||
{
|
||||
for (int j = 1; j < boxesPerStud; j++)
|
||||
{
|
||||
ptYDir.y = (float)i + (1.0f / (float)boxesPerStud) * (float)j;
|
||||
ptInWorld = planeCoord.pointToWorldSpace(ptYDir);
|
||||
adornCoord.translation = ptInWorld;
|
||||
cylinder(adorn, adornCoord, direction2, (float)(xMaxInt - xMinInt), 0.01, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DrawAdorn::axisWidget(Adorn* adorn, const Aya::Camera& camera)
|
||||
{
|
||||
Rect2D viewport = adorn->getViewport();
|
||||
|
||||
float sizeOfWidget = 0.1f;
|
||||
Vector3 offset = Vector3((viewport.width() / 2) - 50, (-viewport.height() / 2) + 50, 0);
|
||||
Vector3 widgetOrigin3dPos = camera.getRenderingCoordinateFrame().translation + (camera.getRenderingCoordinateFrame().lookVector() * 2);
|
||||
Vector3 widgetOrigin2dPos = camera.project(widgetOrigin3dPos) - offset;
|
||||
|
||||
Vector3 widgetXPos = camera.project(widgetOrigin3dPos + Vector3::unitX() * sizeOfWidget) - offset;
|
||||
adorn->line2d(Vector2(widgetOrigin2dPos.x, widgetOrigin2dPos.y), Vector2(widgetXPos.x, widgetXPos.y), axisColors[0]);
|
||||
adorn->drawFont2D("X", Vector2(widgetXPos.x, widgetXPos.y), 12.0f, false, axisColors[0], Aya::Color4::clear(), Aya::Text::FONT_ARIAL);
|
||||
|
||||
Vector3 widgetYPos = camera.project(widgetOrigin3dPos + Vector3::unitY() * sizeOfWidget) - offset;
|
||||
adorn->line2d(Vector2(widgetOrigin2dPos.x, widgetOrigin2dPos.y), Vector2(widgetYPos.x, widgetYPos.y), axisColors[1]);
|
||||
adorn->drawFont2D("Y", Vector2(widgetYPos.x + 3.0f, widgetYPos.y), 12.0f, false, axisColors[1], Aya::Color4::clear(), Aya::Text::FONT_ARIAL);
|
||||
|
||||
Vector3 widgetZPos = camera.project(widgetOrigin3dPos + Vector3::unitZ() * sizeOfWidget) - offset;
|
||||
adorn->line2d(Vector2(widgetOrigin2dPos.x, widgetOrigin2dPos.y), Vector2(widgetZPos.x, widgetZPos.y), axisColors[2]);
|
||||
adorn->drawFont2D("Z", Vector2(widgetZPos.x, widgetZPos.y), 12.0f, false, axisColors[2], Aya::Color4::clear(), Aya::Text::FONT_ARIAL);
|
||||
}
|
||||
|
||||
void DrawAdorn::circularGridAtCoord(Adorn* adorn, const CoordinateFrame& coordFrame, const G3D::Vector3& size, const G3D::Vector3& cameraPos,
|
||||
NormalId normalId, const Color4& color, int boxesPerStud)
|
||||
{
|
||||
const Vector3 halfSize = size / 2;
|
||||
const Extents localExtents = Extents(-halfSize, halfSize);
|
||||
const Vector3 handlePos = handlePosInObject(cameraPos, localExtents, HANDLE_ROTATE, normalId);
|
||||
const Vector3 handlePosInWorld = coordFrame.pointToWorldSpace(handlePos);
|
||||
const Vector3 delta = coordFrame.translation - handlePosInWorld;
|
||||
const float radius = delta.length();
|
||||
const Vector3 normal = normalIdToVector3(normalId);
|
||||
const int direction = (normalId == NORM_X || normalId == NORM_X_NEG) ? 1 : 0;
|
||||
const float rotationAngle = Math::pif() / boxesPerStud;
|
||||
const Matrix3 rotation_increment = Matrix3::fromAxisAngle(normal, rotationAngle);
|
||||
const float line_thickness = scaleRelativeToCamera(cameraPos, coordFrame.translation, RotationGridLineMinAngle, RotationGridLineThickness);
|
||||
|
||||
adorn->setMaterial(Adorn::Material_SelfLit);
|
||||
|
||||
CoordinateFrame frame = coordFrame;
|
||||
for (int i = 0; i < boxesPerStud; ++i)
|
||||
{
|
||||
frame.rotation *= rotation_increment;
|
||||
cylinder(adorn, frame, direction, radius * 2, line_thickness, color);
|
||||
}
|
||||
|
||||
adorn->setMaterial(Adorn::Material_Default);
|
||||
}
|
||||
|
||||
void DrawAdorn::lineSegmentRelativeToCoord(
|
||||
Adorn* adorn, const CoordinateFrame& cF, const Vector3& pt0, const Vector3& pt1, const G3D::Color3& color, float lineThickness)
|
||||
{
|
||||
CoordinateFrame lineCf;
|
||||
|
||||
Vector3 lineVector = pt1 - pt0;
|
||||
lineCf.translation = pt0 + lineVector * 0.5f;
|
||||
float lineLength = lineVector.magnitude();
|
||||
lineCf.rotation = Math::getWellFormedRotForZVector(lineVector / lineLength);
|
||||
|
||||
DrawAdorn::cylinder(adorn, cF * lineCf, 2, lineLength, lineThickness, color);
|
||||
}
|
||||
|
||||
void DrawAdorn::verticalLineSegmentSplit(Adorn* adorn, const CoordinateFrame& cF, const Vector3& pt0, const Vector3& pt1, const float& delta,
|
||||
const float& dropFactor, const short& level, const G3D::Color3& color, float lineThickness)
|
||||
{
|
||||
short maxLevels = 3;
|
||||
float recursiveDropFactor = 0.25f;
|
||||
Vector3 splitPoint = 0.5 * (pt1 - pt0) + pt0;
|
||||
splitPoint.y -= dropFactor * delta;
|
||||
|
||||
if (level < maxLevels)
|
||||
{
|
||||
verticalLineSegmentSplit(adorn, cF, pt0, splitPoint, delta, recursiveDropFactor * dropFactor, level + 1, color, lineThickness);
|
||||
verticalLineSegmentSplit(adorn, cF, splitPoint, pt1, delta, recursiveDropFactor * dropFactor, level + 1, color, lineThickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
lineSegmentRelativeToCoord(adorn, cF, pt0, splitPoint, color, lineThickness);
|
||||
lineSegmentRelativeToCoord(adorn, cF, pt1, splitPoint, color, lineThickness);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAdorn::surfacePolygon(Adorn* adorn, PartInstance& partInst, int surfaceId, const G3D::Color3& color, float lineThickness)
|
||||
{
|
||||
std::vector<Vector3> polygonInPart;
|
||||
for (int i = 0; i < PartInstance::getConstPrimitive(&partInst)->getConstGeometry()->getNumVertsInSurface(surfaceId); i++)
|
||||
polygonInPart.push_back(PartInstance::getConstPrimitive(&partInst)->getConstGeometry()->getSurfaceVertInBody(surfaceId, i));
|
||||
|
||||
DrawAdorn::polygonRelativeToCoord(adorn, PartInstance::getConstPrimitive(&partInst)->getCoordinateFrame(), polygonInPart, color, lineThickness);
|
||||
}
|
||||
|
||||
void DrawAdorn::polygonRelativeToCoord(
|
||||
Adorn* adorn, const CoordinateFrame& cF, std::vector<Vector3>& vertices, const G3D::Color4& color, float lineThickness)
|
||||
{
|
||||
CoordinateFrame lineCf;
|
||||
|
||||
for (unsigned int i = 0; i < vertices.size(); i++)
|
||||
{
|
||||
int next = i < vertices.size() - 1 ? i + 1 : 0;
|
||||
Vector3 lineVector = vertices[next] - vertices[i];
|
||||
lineCf.translation = vertices[i] + lineVector * 0.5f;
|
||||
float lineLength = lineVector.magnitude();
|
||||
lineCf.rotation = Math::getWellFormedRotForZVector(lineVector / lineLength);
|
||||
|
||||
cylinder(adorn, cF * lineCf, 2, lineLength, lineThickness, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DrawAdorn::partSurface(const Part& part, int surfaceId, Adorn* adorn, const Color4& color, float thickness)
|
||||
{
|
||||
adorn->setObjectToWorldMatrix(part.coordinateFrame);
|
||||
|
||||
Vector3 halfRealSize = 0.5f * part.gridSize;
|
||||
surfaceBorder(adorn, halfRealSize, thickness, surfaceId, color);
|
||||
}
|
||||
|
||||
Vector3 DrawAdorn::handlePosInObject(const Vector3& cameraPos, const Extents& localExtents, HandleType handleType, NormalId normalId)
|
||||
{
|
||||
const Vector3 size = localExtents.size();
|
||||
const Vector3 halfSize = size / 2;
|
||||
|
||||
switch (handleType)
|
||||
{
|
||||
case HANDLE_ROTATE:
|
||||
{
|
||||
const float halfdiagonal = halfSize.length();
|
||||
const float default_radius = halfdiagonal + HandleOffset * 4;
|
||||
|
||||
// TODO fix this so we can have a nice scaled circle far away
|
||||
// use the default radius because if we scale it's gonna get huge
|
||||
// need to determine circle size on screen and make sure it's larger
|
||||
// than the minimum size on screen
|
||||
const float radius = default_radius;
|
||||
// const float radius = scaleRelativeToCamera(
|
||||
// cameraPos,
|
||||
// localExtents.center(),
|
||||
// TorusMinAngle,
|
||||
// default_radius );
|
||||
|
||||
const Vector3 axis = normalIdToVector3(normalIdToU(normalId));
|
||||
return localExtents.center() + axis * radius;
|
||||
}
|
||||
case HANDLE_RESIZE:
|
||||
{
|
||||
const Vector3 axis = normalIdToVector3(normalId);
|
||||
const Vector3 start_offset(HandleOffset, HandleOffset, HandleOffset);
|
||||
return localExtents.center() + axis * (halfSize + start_offset * 4);
|
||||
}
|
||||
case HANDLE_VELOCITY:
|
||||
case HANDLE_MOVE:
|
||||
{
|
||||
const Vector3 axis = normalIdToVector3(normalId);
|
||||
const Vector3 start_offset(HandleOffset, HandleOffset, HandleOffset);
|
||||
return localExtents.center() + axis * (halfSize + start_offset);
|
||||
}
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
return Vector3::zero();
|
||||
}
|
||||
}
|
||||
|
||||
float DrawAdorn::scaleHandleRelativeToCamera(const Vector3& cameraPos, HandleType handleType, const Vector3& handlePos)
|
||||
{
|
||||
float min_angle;
|
||||
float expected_radius;
|
||||
|
||||
switch (handleType)
|
||||
{
|
||||
case HANDLE_RESIZE:
|
||||
min_angle = SphereMinAngle;
|
||||
expected_radius = SphereRadius;
|
||||
break;
|
||||
case HANDLE_MOVE:
|
||||
min_angle = ArrowMinAngle;
|
||||
expected_radius = ArrowLength;
|
||||
break;
|
||||
case HANDLE_ROTATE:
|
||||
min_angle = SphereMinAngle;
|
||||
expected_radius = SphereRadius;
|
||||
break;
|
||||
case HANDLE_VELOCITY:
|
||||
min_angle = SphereMinAngle;
|
||||
expected_radius = SphereRadius;
|
||||
break;
|
||||
}
|
||||
|
||||
return scaleRelativeToCamera(cameraPos, handlePos, min_angle, expected_radius);
|
||||
}
|
||||
|
||||
// visual angle = radius/distance
|
||||
float DrawAdorn::scaleRelativeToCamera(const Vector3& cameraPos, const Vector3& handlePos, float minAngle, float expectedSize)
|
||||
{
|
||||
const float distance = (cameraPos - handlePos).magnitude();
|
||||
|
||||
float scale = 1.0f;
|
||||
if (distance > (1.0f / minAngle))
|
||||
scale = distance * minAngle;
|
||||
|
||||
const float radius = scale * expectedSize;
|
||||
return radius;
|
||||
}
|
||||
|
||||
void DrawAdorn::handles2d(const G3D::Vector3& size, const G3D::CoordinateFrame& position, const Aya::Camera& camera, Adorn* adorn,
|
||||
HandleType handleType, const G3D::Color4& color, bool useAxisColor, int normalIdMask)
|
||||
{
|
||||
Vector3 halfSize = size / 2;
|
||||
Aya::Extents localExtents(-halfSize, halfSize);
|
||||
|
||||
for (int posNeg = 0; posNeg < 2; ++posNeg)
|
||||
{
|
||||
for (int xyz = 0; xyz < 3; ++xyz)
|
||||
{
|
||||
if (normalIdMask & (1 << (xyz + (posNeg * 3))))
|
||||
{
|
||||
NormalId normalId = (NormalId)(posNeg * 3 + xyz);
|
||||
Vector3 handlePos = handlePosInObject(camera.coordinateFrame().translation, localExtents, handleType, normalId);
|
||||
|
||||
Vector3 handlePosInWorld = position.pointToWorldSpace(handlePos);
|
||||
|
||||
if (handleType == HANDLE_MOVE)
|
||||
{
|
||||
// compute relative size on screen based on distance
|
||||
const float handleRadius =
|
||||
scaleRelativeToCamera(camera.coordinateFrame().translation, handlePosInWorld, ArrowMinAngle, ArrowLength - 0.5f);
|
||||
|
||||
Vector3 correctedHandlePos = normalIdToVector3(normalId) * handleRadius;
|
||||
|
||||
// transform point back to world
|
||||
CoordinateFrame frame(position.rotation, handlePosInWorld);
|
||||
handlePosInWorld = frame.pointToWorldSpace(correctedHandlePos);
|
||||
}
|
||||
|
||||
Vector3 screenPos = camera.project(handlePosInWorld);
|
||||
|
||||
if (screenPos.z == std::numeric_limits<float>::infinity())
|
||||
return;
|
||||
|
||||
adorn->rect2d(Rect::fromCenterSize(screenPos.xy(), 6.0f).toRect2D(), useAxisColor ? axisColors[xyz] : color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAdorn::partInfoText2D(const G3D::Vector3& size, const G3D::CoordinateFrame& position, const Aya::Camera& camera, Adorn* adorn,
|
||||
const std::string& text, const G3D::Color4& color, float fontSize, int normalIdMask)
|
||||
{
|
||||
Vector3 halfSize = size / 2;
|
||||
Aya::Extents localExtents(-halfSize, halfSize);
|
||||
|
||||
const Vector3 max = localExtents.max();
|
||||
const Vector3 min = localExtents.min();
|
||||
|
||||
Vector3 points[8];
|
||||
points[0] = Vector3(min.x, min.y, min.z);
|
||||
points[1] = Vector3(min.x, max.y, min.z);
|
||||
points[2] = Vector3(min.x, max.y, max.z);
|
||||
points[3] = Vector3(min.x, min.y, max.z);
|
||||
|
||||
points[4] = Vector3(max.x, min.y, min.z);
|
||||
points[5] = Vector3(max.x, max.y, min.z);
|
||||
points[6] = Vector3(max.x, max.y, max.z);
|
||||
points[7] = Vector3(max.x, min.y, max.z);
|
||||
|
||||
Vector2 lowestRightCornerMax = Vector2(0.0f, 0.0f);
|
||||
float minDistanceToOptimal = FLT_MAX;
|
||||
Vector2 lowestRightCorner;
|
||||
|
||||
// In projection space, find the optimal point, the lowest rightest maximum of all the points.
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
const Vector3 worldPos = position.pointToWorldSpace(points[i]);
|
||||
const Vector3 projPos = camera.project(worldPos);
|
||||
|
||||
points[i] = projPos;
|
||||
|
||||
if (projPos.z == std::numeric_limits<float>::infinity())
|
||||
continue;
|
||||
|
||||
if (projPos.y > lowestRightCornerMax.y)
|
||||
{
|
||||
lowestRightCornerMax.y = projPos.y;
|
||||
}
|
||||
|
||||
if (projPos.x > lowestRightCornerMax.x)
|
||||
{
|
||||
lowestRightCornerMax.x = projPos.x;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the point closest to lowest rightest maximum.
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
const Vector3 projPos = points[i];
|
||||
|
||||
if (projPos.z == std::numeric_limits<float>::infinity())
|
||||
continue;
|
||||
|
||||
float distanceToOptimal = (projPos.xy() - lowestRightCornerMax).length();
|
||||
|
||||
if (minDistanceToOptimal > distanceToOptimal)
|
||||
{
|
||||
minDistanceToOptimal = distanceToOptimal;
|
||||
lowestRightCorner = projPos.xy();
|
||||
}
|
||||
}
|
||||
|
||||
adorn->drawFont2D(text, lowestRightCorner, fontSize, false, color, Aya::Color4::clear(), Aya::Text::FONT_ARIAL);
|
||||
}
|
||||
|
||||
void DrawAdorn::handles3d(const G3D::Vector3& size, const G3D::CoordinateFrame& position, Adorn* adorn, HandleType handleType,
|
||||
const Vector3& cameraPos, const G3D::Color4& color, bool useAxisColor, int normalIdMask, NormalId normalIdTohighlight,
|
||||
const G3D::Color4& highlightColor)
|
||||
{
|
||||
const Vector3 halfSize = size / 2;
|
||||
const Extents localExtents(-halfSize, halfSize);
|
||||
|
||||
int usedaxes = 0; // for rotation handles, prevent use of NORM_X and NORM_X_NEG at the same time.
|
||||
for (int posNeg = 0; posNeg < 2; ++posNeg)
|
||||
{
|
||||
for (int xyz = 0; xyz < 3; ++xyz)
|
||||
{
|
||||
if (normalIdMask & (1 << (xyz + (posNeg * 3))))
|
||||
{
|
||||
NormalId normalId = intToNormalId(posNeg * 3 + xyz);
|
||||
|
||||
// determine where the handle should be located
|
||||
const Vector3 handlePos = handlePosInObject(cameraPos, localExtents, handleType, normalId);
|
||||
const Vector3 handlePosInWorld = position.pointToWorldSpace(handlePos);
|
||||
|
||||
// compute relative size on screen based on distance
|
||||
const float handleRadius = scaleHandleRelativeToCamera(cameraPos, handleType, handlePosInWorld);
|
||||
|
||||
const CoordinateFrame frame(position.rotation, handlePosInWorld);
|
||||
adorn->setObjectToWorldMatrix(frame);
|
||||
|
||||
if (normalIdTohighlight == normalId)
|
||||
adorn->setMaterial(Adorn::Material_SelfLitHighlight);
|
||||
else
|
||||
adorn->setMaterial(Adorn::Material_SelfLit);
|
||||
|
||||
const int axis_index = normalId % 3;
|
||||
Color4 handleColor = useAxisColor ? axisColors[axis_index] : color;
|
||||
|
||||
if (normalIdTohighlight != normalId)
|
||||
handleColor.a *= HandleTransparency;
|
||||
|
||||
switch (handleType)
|
||||
{
|
||||
case HANDLE_RESIZE:
|
||||
case HANDLE_VELOCITY:
|
||||
{
|
||||
if (normalIdTohighlight == normalId)
|
||||
adorn->sphere(Sphere(Vector3::zero(), handleRadius + 0.08), handleColor);
|
||||
else
|
||||
adorn->sphere(Sphere(Vector3::zero(), handleRadius), handleColor);
|
||||
break;
|
||||
}
|
||||
case HANDLE_MOVE:
|
||||
{
|
||||
Vector3 direction = normalIdToVector3(normalId) * handleRadius;
|
||||
RbxRay rayInPart = RbxRay::fromOriginAndDirection(Vector3::zero(), direction);
|
||||
adorn->ray(rayInPart, handleColor);
|
||||
break;
|
||||
}
|
||||
case HANDLE_ROTATE:
|
||||
{
|
||||
const int axis_flag = normalIdToMask(normalId);
|
||||
|
||||
// only allow if positive orientation wasn't already specified.
|
||||
if ((usedaxes & axis_flag) == 0)
|
||||
{
|
||||
const Vector3 delta = position.translation - handlePosInWorld;
|
||||
const float torus_radius = delta.length();
|
||||
|
||||
// compute relative size thickness based on distance
|
||||
const float thickness = scaleRelativeToCamera(cameraPos, position.translation, TorusThicknessMinAngle, TorusThickness);
|
||||
|
||||
Color4 axisColor(handleColor);
|
||||
|
||||
if (normalIdTohighlight != normalId)
|
||||
axisColor.a = AxisTransparency;
|
||||
|
||||
torus(adorn, position, normalId, torus_radius, thickness, axisColor);
|
||||
|
||||
usedaxes |= axis_flag;
|
||||
}
|
||||
|
||||
// need to rest the matrix because torus changed it
|
||||
adorn->setObjectToWorldMatrix(handlePosInWorld);
|
||||
if (normalIdTohighlight == normalId)
|
||||
adorn->sphere(Sphere(Vector3::zero(), handleRadius + 0.08), handleColor);
|
||||
else
|
||||
adorn->sphere(
|
||||
Sphere(Vector3::zero(), handleRadius), Color3(handleColor.r - 0.02, handleColor.g - 0.02, handleColor.b - 0.02));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adorn->setMaterial(Adorn::Material_Default);
|
||||
}
|
||||
|
||||
// circle, with normal of linespace parallel to radius.
|
||||
class CircleRadialNormal : public I3DLinearFunc
|
||||
{
|
||||
Vector3 x;
|
||||
Vector3 y;
|
||||
Vector3 z;
|
||||
float r;
|
||||
NormalId axis;
|
||||
|
||||
public:
|
||||
CircleRadialNormal(float radius, NormalId axis)
|
||||
: r(radius)
|
||||
, axis(axis)
|
||||
{
|
||||
x = uvwToObject(Vector3::unitX(), axis);
|
||||
y = uvwToObject(Vector3::unitY(), axis);
|
||||
z = uvwToObject(Vector3::unitZ(), axis);
|
||||
}
|
||||
Vector3 eval(float t)
|
||||
{
|
||||
double theta = t * 2 * G3D::pi();
|
||||
return x * (float)(cos(theta) * r) + y * (float)(sin(theta) * r);
|
||||
}
|
||||
// first derivative.
|
||||
Vector3 evalTangent(float t)
|
||||
{
|
||||
double theta = t * 2 * G3D::pi();
|
||||
return y * (float)cos(theta) - x * (float)sin(theta);
|
||||
}
|
||||
Vector3 evalNormal(float t)
|
||||
{
|
||||
double theta = t * 2 * G3D::pi();
|
||||
return x * (float)cos(theta) + y * (float)sin(theta);
|
||||
}
|
||||
Vector3 evalBinormal(float t)
|
||||
{
|
||||
return -z;
|
||||
}
|
||||
|
||||
// string that encodes this function in a unique way.
|
||||
std::string hashString()
|
||||
{
|
||||
std::ostringstream hash;
|
||||
hash << "CircleRN(" << r << "," << axis << ")";
|
||||
return hash.str();
|
||||
}
|
||||
};
|
||||
|
||||
void DrawAdorn::chatBubble2d(Adorn* adorn, const Rect2D& rect, const G3D::Vector2 pointer, float cornerradius, const float linewidth,
|
||||
const int quarterdivs, const Color4& color)
|
||||
{
|
||||
cornerradius = std::min(cornerradius, std::min(rect.width(), rect.height()));
|
||||
const float pointerwidth = std::min(cornerradius * 1.5f, (rect.width() - cornerradius * 2.0f));
|
||||
|
||||
std::vector<G3D::Vector2> verts;
|
||||
std::vector<G3D::Vector2> vertsBorder;
|
||||
|
||||
// start at base, right side of pointer.
|
||||
// go counterclockwise.
|
||||
// ui space: math space:
|
||||
// (0,0)
|
||||
// /--------------\ n
|
||||
// | | \
|
||||
// | | n-1\ \ 0 1
|
||||
// \-----/ /-----/ /------\ \-----\
|
||||
// n-1/ / 0 1 | |
|
||||
// / | |
|
||||
// n \---------------/
|
||||
// (0,0)
|
||||
|
||||
// y1 should be bottom.
|
||||
Vector2 p0(rect.center().x + pointerwidth / 2, rect.y1());
|
||||
Vector2 p1(rect.center().x - pointerwidth / 2, rect.y1());
|
||||
verts.push_back(p0);
|
||||
vertsBorder.push_back(p0 + Vector2(linewidth, linewidth));
|
||||
|
||||
|
||||
AYAASSERT(rect.wh().x >= cornerradius * 2);
|
||||
AYAASSERT(rect.wh().y >= cornerradius * 2);
|
||||
|
||||
Rect2D innerrect = rect.border(cornerradius);
|
||||
|
||||
double angleinc = (quarterdivs > 0) ? (-(Math::pi() * 0.5) / quarterdivs) : 0;
|
||||
for (int i = 0; i <= quarterdivs; ++i)
|
||||
{
|
||||
double angle = (quarterdivs > 0) ? (Math::pi() * 0.5 + angleinc * i) : (Math::pi() * 0.25);
|
||||
Vector2 r = Vector2((float)cos(angle), (quarterdivs > 0) ? (float)sin(angle) : 1.0f);
|
||||
verts.push_back(innerrect.x1y1() + r * cornerradius);
|
||||
vertsBorder.push_back(innerrect.x1y1() + r * (cornerradius + linewidth));
|
||||
}
|
||||
for (int i = 0; i <= quarterdivs; ++i)
|
||||
{
|
||||
double angle = (quarterdivs > 0) ? (Math::pi() * 0.0 + angleinc * i) : (Math::pi() * 1.75);
|
||||
Vector2 r = Vector2((float)cos(angle), (float)sin(angle));
|
||||
verts.push_back(innerrect.x1y0() + r * cornerradius);
|
||||
vertsBorder.push_back(innerrect.x1y0() + r * (cornerradius + linewidth));
|
||||
}
|
||||
for (int i = 0; i <= quarterdivs; ++i)
|
||||
{
|
||||
double angle = (quarterdivs > 0) ? (Math::pi() * 1.5 + angleinc * i) : (Math::pi() * 1.25);
|
||||
Vector2 r = Vector2((float)cos(angle), (float)sin(angle));
|
||||
verts.push_back(innerrect.x0y0() + r * cornerradius);
|
||||
vertsBorder.push_back(innerrect.x0y0() + r * (cornerradius + linewidth));
|
||||
}
|
||||
for (int i = 0; i <= quarterdivs; ++i)
|
||||
{
|
||||
double angle = (quarterdivs > 0) ? (Math::pi() * 1.0 + angleinc * i) : (Math::pi() * .75);
|
||||
Vector2 r = Vector2((float)cos(angle), (quarterdivs > 0) ? (float)sin(angle) : 1.0f);
|
||||
verts.push_back(innerrect.x0y1() + r * cornerradius);
|
||||
vertsBorder.push_back(innerrect.x0y1() + r * (cornerradius + linewidth));
|
||||
}
|
||||
verts.push_back(p1);
|
||||
vertsBorder.push_back(p1 + Vector2(-linewidth, linewidth));
|
||||
verts.push_back(pointer);
|
||||
Vector2 pointerdir = pointer - (p0 + p1) * 0.5f;
|
||||
pointerdir.unitize();
|
||||
|
||||
vertsBorder.push_back(pointer + pointerdir * linewidth * 3); // todo: not correct.
|
||||
|
||||
adorn->convexPolygon2d(&vertsBorder[0], vertsBorder.size(), G3D::Color4(Color3::black()));
|
||||
adorn->convexPolygon2d(&verts[0], verts.size(), G3D::Color4(color));
|
||||
}
|
||||
|
||||
void DrawAdorn::torus(Adorn* adorn, const CoordinateFrame& position, NormalId axis, float radius, float thicknessradius, const Color4& color)
|
||||
{
|
||||
CircleRadialNormal trajectory(radius, axis);
|
||||
CircleRadialNormal profile(thicknessradius, NORM_Z);
|
||||
|
||||
adorn->setObjectToWorldMatrix(position);
|
||||
adorn->extrusion(&trajectory, 32, &profile, 8, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a wire star made up of 3 line segments.
|
||||
*
|
||||
* @param adorn object used to generate the geometry
|
||||
* @param center world space center coordinates
|
||||
* @param size lines are -size to +size on the axis
|
||||
* @param colorX color of x axis line
|
||||
* @param colorY color of y axis line
|
||||
* @param colorZ color of z axis line
|
||||
*/
|
||||
void DrawAdorn::star(Adorn* adorn, const Vector3& center, float size, const Color4& colorX, const Color4& colorY, const Color4& colorZ)
|
||||
{
|
||||
size /= 2.0f;
|
||||
|
||||
adorn->line3d(center - Vector3::unitX() * size, center + Vector3::unitX() * size, colorX);
|
||||
adorn->line3d(center - Vector3::unitY() * size, center + Vector3::unitY() * size, colorY);
|
||||
adorn->line3d(center - Vector3::unitZ() * size, center + Vector3::unitZ() * size, colorZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw an outline (wireframe) box in 3D.
|
||||
*
|
||||
* @param adorn adornment to use for drawing
|
||||
* @param box box to draw
|
||||
* @param color color of lines
|
||||
*/
|
||||
void DrawAdorn::outlineBox(Adorn* adorn, const AABox& box, const Color4& color)
|
||||
{
|
||||
const Vector3& p0 = box.low();
|
||||
const Vector3& p1 = box.high();
|
||||
|
||||
const float X0 = p0.x;
|
||||
const float Y0 = p0.y;
|
||||
const float Z0 = p0.z;
|
||||
const float X1 = p1.x;
|
||||
const float Y1 = p1.y;
|
||||
const float Z1 = p1.z;
|
||||
|
||||
adorn->line3d(Vector3(X0, Y0, Z0), Vector3(X1, Y0, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z0), Vector3(X1, Y1, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y0, Z1), Vector3(X1, Y0, Z1), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z1), Vector3(X1, Y1, Z1), color);
|
||||
|
||||
adorn->line3d(Vector3(X0, Y0, Z0), Vector3(X0, Y1, Z0), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z0), Vector3(X1, Y1, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y0, Z1), Vector3(X0, Y1, Z1), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z1), Vector3(X1, Y1, Z1), color);
|
||||
|
||||
adorn->line3d(Vector3(X0, Y0, Z0), Vector3(X0, Y0, Z1), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z0), Vector3(X1, Y0, Z1), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z0), Vector3(X0, Y1, Z1), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z0), Vector3(X1, Y1, Z1), color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw select indicator around a box similar to 3DS.
|
||||
*
|
||||
* @param adorn adornment to use for drawing
|
||||
* @param box box to draw
|
||||
* @param color color of lines
|
||||
*/
|
||||
void DrawAdorn::selectionBox(Adorn* adorn, const AABox& box, const Color4& color)
|
||||
{
|
||||
const Vector3& P0 = box.low();
|
||||
const Vector3& P1 = box.high();
|
||||
|
||||
float X0 = P0.x;
|
||||
float Y0 = P0.y;
|
||||
float Z0 = P0.z;
|
||||
float X1 = P1.x;
|
||||
float Y1 = P1.y;
|
||||
float Z1 = P1.z;
|
||||
|
||||
float s0 = X0 + (X1 - X0) / 4;
|
||||
float s1 = X1 + (X0 - X1) / 4;
|
||||
float s2 = Y0 + (Y1 - Y0) / 4;
|
||||
float s3 = Y1 + (Y0 - Y1) / 4;
|
||||
float s4 = Z0 + (Z1 - Z0) / 4;
|
||||
float s5 = Z1 + (Z0 - Z1) / 4;
|
||||
|
||||
// side0
|
||||
adorn->line3d(Vector3(X0, Y0, Z0), Vector3(s0, Y0, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z0), Vector3(s0, Y1, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y0, Z1), Vector3(s0, Y0, Z1), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z1), Vector3(s0, Y1, Z1), color);
|
||||
|
||||
// side1
|
||||
adorn->line3d(Vector3(X1, Y0, Z0), Vector3(s1, Y0, Z0), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z0), Vector3(s1, Y1, Z0), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z1), Vector3(s1, Y0, Z1), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z1), Vector3(s1, Y1, Z1), color);
|
||||
|
||||
// side2
|
||||
adorn->line3d(Vector3(X0, Y0, Z0), Vector3(X0, s2, Z0), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z0), Vector3(X1, s2, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y0, Z1), Vector3(X0, s2, Z1), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z1), Vector3(X1, s2, Z1), color);
|
||||
|
||||
// side3
|
||||
adorn->line3d(Vector3(X0, Y1, Z0), Vector3(X0, s3, Z0), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z0), Vector3(X1, s3, Z0), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z1), Vector3(X0, s3, Z1), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z1), Vector3(X1, s3, Z1), color);
|
||||
|
||||
// side4
|
||||
adorn->line3d(Vector3(X0, Y0, Z0), Vector3(X0, Y0, s4), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z0), Vector3(X1, Y0, s4), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z0), Vector3(X0, Y1, s4), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z0), Vector3(X1, Y1, s4), color);
|
||||
|
||||
// side5
|
||||
adorn->line3d(Vector3(X0, Y0, Z1), Vector3(X0, Y0, s5), color);
|
||||
adorn->line3d(Vector3(X1, Y0, Z1), Vector3(X1, Y0, s5), color);
|
||||
adorn->line3d(Vector3(X0, Y1, Z1), Vector3(X0, Y1, s5), color);
|
||||
adorn->line3d(Vector3(X1, Y1, Z1), Vector3(X1, Y1, s5), color);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
164
engine/3d/src/DrawAdorn.hpp
Normal file
164
engine/3d/src/DrawAdorn.hpp
Normal file
@@ -0,0 +1,164 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SelectState.hpp"
|
||||
#include "HandleType.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "Utility/NormalId.hpp"
|
||||
#include <vector>
|
||||
#include "World/Primitive.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "Tool/DragTypes.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Part;
|
||||
class Adorn;
|
||||
class Face;
|
||||
class Camera;
|
||||
|
||||
class DrawAdorn
|
||||
{
|
||||
private:
|
||||
static void surfaceBorder(Adorn* adorn, const Vector3& halfRealSize, float highlight, int surfaceId, const Color4& color);
|
||||
|
||||
public:
|
||||
static const Color3& resizeColor()
|
||||
{
|
||||
static Color3 c(0.1f, 0.6f, 1.0f);
|
||||
return c;
|
||||
}
|
||||
|
||||
static float scaleHandleRelativeToCamera(const Vector3& cameraPos, HandleType handleType, const Vector3& handlePos);
|
||||
|
||||
static Vector3 handlePosInObject(const Vector3& cameraPos, const Extents& localExtents, HandleType handleType, NormalId normalId);
|
||||
|
||||
static void cylinder(Adorn* adorn, const CoordinateFrame& worldC, int axis, float length, float radius, const Color4& color, bool cap = true);
|
||||
|
||||
static void faceInWorld(Adorn* adorn, const Face& face, float thickness, const Color4& color);
|
||||
|
||||
static void partSurface(
|
||||
const Part& part, int surfaceId, Adorn* adorn, const G3D::Color4& color = G3D::Color4(G3D::Color3::orange()), float thickness = 0.2f);
|
||||
|
||||
static void surfaceGridOnFace(const Primitive& prim, Adorn* adorn, int surfaceId, const G3D::Color4& color, int boxesPerStud);
|
||||
|
||||
static void zeroPlaneGrid(Adorn* adorn, const Aya::Camera& camera, const int studsPerBox, const int yLevel, const G3D::Color4& smallGridColor,
|
||||
const G3D::Color4& largeGridColor);
|
||||
|
||||
static void surfaceGridAtCoord(Adorn* adorn, CoordinateFrame& cF, const Vector4& bounds, const Vector3& gridXDir, const Vector3& gridYDir,
|
||||
const G3D::Color4& color, int boxesPerStud);
|
||||
|
||||
static void axisWidget(Adorn* adorn, const Aya::Camera& camera);
|
||||
|
||||
static void circularGridAtCoord(Adorn* adorn, const CoordinateFrame& coordFrame, const G3D::Vector3& size, const G3D::Vector3& cameraPos,
|
||||
NormalId normalId, const Color4& color, int boxesPerStud);
|
||||
|
||||
static void surfacePolygon(Adorn* adorn, PartInstance& partInst, int surfaceId, const G3D::Color3& color, float lineThickness);
|
||||
|
||||
static void polygonRelativeToCoord(
|
||||
Adorn* adorn, const CoordinateFrame& cF, std::vector<Vector3>& vertices, const G3D::Color4& color, float lineThickness);
|
||||
|
||||
static void lineSegmentRelativeToCoord(
|
||||
Adorn* adorn, const CoordinateFrame& cF, const Vector3& pt0, const Vector3& pt1, const G3D::Color3& color, float lineThickness);
|
||||
|
||||
static void verticalLineSegmentSplit(Adorn* adorn, const CoordinateFrame& cF, const Vector3& pt0, const Vector3& pt1, const float& delta,
|
||||
const float& magicParam, const short& level, const G3D::Color3& color, float lineThickness);
|
||||
|
||||
static void handles3d(const G3D::Vector3& size, const G3D::CoordinateFrame& position, Adorn* adorn, HandleType handleType,
|
||||
const G3D::Vector3& cameraPos, const G3D::Color4& color, bool useAxisColor = true, int normalIdMask = NORM_ALL_MASK,
|
||||
NormalId normalIdTohighlight = NORM_UNDEFINED, const G3D::Color4& highlightColor = cornflowerblue);
|
||||
|
||||
// Must be in 2d Mode
|
||||
static void handles2d(const G3D::Vector3& size, const G3D::CoordinateFrame& position, const Aya::Camera& camera, Adorn* adorn,
|
||||
HandleType handleType, const G3D::Color4& color, bool useAxisColor = true, int normalIdMask = NORM_ALL_MASK);
|
||||
|
||||
static void partInfoText2D(const G3D::Vector3& size, const G3D::CoordinateFrame& position, const Aya::Camera& camera, Adorn* adorn,
|
||||
const std::string& text, const G3D::Color4& color, float fontSize = 18.0f, int normalIdMask = NORM_ALL_MASK);
|
||||
|
||||
static void chatBubble2d(Adorn* adorn, const Rect2D& rect, const G3D::Vector2 pointer, float cornerradius, const float linewidth,
|
||||
const int quarterdivs, const Color4& color);
|
||||
|
||||
static void torus(Adorn* adorn, const CoordinateFrame& worldC, NormalId axis, float radius, float thicknessradius, const Color4& color);
|
||||
|
||||
/**
|
||||
* Draws a wire star made up of 3 line segments.
|
||||
*
|
||||
* @param adorn object used to generate the geometry
|
||||
* @param center world space center coordinates
|
||||
* @param size lines are -size to +size on the axis
|
||||
* @param colorX color of x axis line
|
||||
* @param colorY color of y axis line
|
||||
* @param colorZ color of z axis line
|
||||
*/
|
||||
static void star(Adorn* adorn, const Vector3& center, float size = 1.0f, const Color4& colorX = Color3::white(),
|
||||
const Color4& colorY = Color3::white(), const Color4& colorZ = Color3::white());
|
||||
|
||||
/**
|
||||
* Draw an outline (wireframe) box in 3D.
|
||||
*
|
||||
* @param adorn adornment to use for drawing
|
||||
* @param box box to draw
|
||||
* @param color color of lines
|
||||
*/
|
||||
static void outlineBox(Adorn* adorn, const AABox& box, const Color4& color);
|
||||
|
||||
/**
|
||||
* Draw an outline (wireframe) box in 3D.
|
||||
*
|
||||
* @param adorn adornment to use for drawing
|
||||
* @param extents box dimensions
|
||||
* @param color color of lines
|
||||
*/
|
||||
static void outlineBox(Adorn* adorn, const Extents& extents, const Color4& color)
|
||||
{
|
||||
AABox aa_box(extents.min(), extents.max());
|
||||
outlineBox(adorn, aa_box, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw select indicator around a box similar to 3DS.
|
||||
*
|
||||
* @param adorn adornment to use for drawing
|
||||
* @param box box to draw
|
||||
* @param color color of lines
|
||||
*/
|
||||
static void selectionBox(Adorn* adorn, const AABox& box, const Color4& color);
|
||||
|
||||
/**
|
||||
* Draw select indicator around a box similar to 3DS.
|
||||
*
|
||||
* @param adorn adornment to use for drawing
|
||||
* @param extents box dimensions
|
||||
* @param color color of lines
|
||||
*/
|
||||
static void selectionBox(Adorn* adorn, const Extents& extents, const Color4& color)
|
||||
{
|
||||
AABox aa_box(extents.min(), extents.max());
|
||||
selectionBox(adorn, aa_box, color);
|
||||
}
|
||||
|
||||
// common colors
|
||||
static const Color3 beige;
|
||||
static const Color3 darkblue;
|
||||
static const Color3 powderblue;
|
||||
static const Color3 skyblue;
|
||||
static const Color3 violet;
|
||||
static const Color3 slategray;
|
||||
static const Color3 aqua;
|
||||
static const Color3 tan;
|
||||
static const Color3 wheat;
|
||||
static const Color3 cornflowerblue;
|
||||
static const Color3 limegreen;
|
||||
static const Color3 magenta;
|
||||
static const Color3 pink;
|
||||
static const Color3 silver;
|
||||
|
||||
// Color values for each axis (x, y, z).
|
||||
static const Color3 axisColors[3];
|
||||
|
||||
private:
|
||||
static float scaleRelativeToCamera(const Vector3& cameraPos, const Vector3& handlePos, float minAngle, float expectedRadius);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
31
engine/3d/src/DrawPrimitives.hpp
Normal file
31
engine/3d/src/DrawPrimitives.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/G3DCore.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class RenderDevice;
|
||||
}
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class Rect;
|
||||
|
||||
class DrawPrimitives
|
||||
{
|
||||
public:
|
||||
static void rawBox(const AABox& box, G3D::RenderDevice* rd);
|
||||
|
||||
static void rawSphere(float radius, G3D::RenderDevice* rd);
|
||||
|
||||
static void rawCylinderAlongX(float radius, float axis, G3D::RenderDevice* rd, bool cap);
|
||||
|
||||
// generic 2d - must be in 2d mode
|
||||
static void rect2d(const Aya::Rect& rect, G3D::RenderDevice* rd, const G3D::Color4& color = G3D::Color3::white());
|
||||
|
||||
static void line2d(const G3D::Vector2& p0, const G3D::Vector2& p1, G3D::RenderDevice* rd, const G3D::Color4& color = G3D::Color3::white());
|
||||
|
||||
static void outlineRect2d(const Aya::Rect& rect, float thick, G3D::RenderDevice* rd, const G3D::Color4& color = G3D::Color3::blue());
|
||||
};
|
||||
} // namespace Aya
|
||||
28
engine/3d/src/EqualsTrait.hpp
Normal file
28
engine/3d/src/EqualsTrait.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
@file EqualsTrait.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2008-10-01
|
||||
@edited 2008-10-01
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_EQUALSTRAIT_H
|
||||
#define G3D_EQUALSTRAIT_H
|
||||
|
||||
#include "platform.hpp"
|
||||
|
||||
/** 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
|
||||
152
engine/3d/src/Frustum.cpp
Normal file
152
engine/3d/src/Frustum.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "Frustum.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "Utility/Math.hpp"
|
||||
#include "Utility/Extents.hpp"
|
||||
|
||||
using G3D::Plane;
|
||||
using G3D::Vector3;
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
// creates a frustum in a local coordinate space with the apex at the origin
|
||||
// translates the frustum into the space defined by apex, dir and up
|
||||
// performing the floating point operations in a local space helps reduce numerical errors when all of the points are far from the origin
|
||||
Frustum::Frustum(
|
||||
const Vector3& apex, const Vector3& dir, const Vector3& up, const float nearDist, const float farDist, const float fovx, const float fovy)
|
||||
{
|
||||
Matrix3 rotation;
|
||||
|
||||
// up and dir should already be normalized and perpendicular to each other
|
||||
AYAASSERT(dir.isUnit());
|
||||
AYAASSERT(up.isUnit());
|
||||
|
||||
// due to rounding errors the cross product may not be a unit vector even though it should be.
|
||||
const Vector3 right = up.cross(dir).unit();
|
||||
|
||||
rotation.setColumn(0, right);
|
||||
rotation.setColumn(1, up);
|
||||
rotation.setColumn(2, -dir);
|
||||
|
||||
// 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.
|
||||
faceArray.append(Plane(Vector3(0.0f, 0.0f, -1.0f), Vector3(0, 0, -nearDist)));
|
||||
|
||||
// Right plane
|
||||
faceArray.append(Plane(Vector3(-cosf(fovx / 2.0f), 0.0f, -sinf(fovx / 2.0f)), Vector3::zero()));
|
||||
|
||||
// Left plane
|
||||
faceArray.append(Plane(Vector3(-faceArray.last().normal().x, 0.0f, faceArray.last().normal().z), Vector3::zero()));
|
||||
|
||||
// Bottom plane
|
||||
faceArray.append(Plane(Vector3(0.0f, cosf(fovy / 2.0f), -sinf(fovy / 2.0f)), Vector3::zero()));
|
||||
|
||||
// Top plane
|
||||
faceArray.append(Plane(Vector3(0.0f, -faceArray.last().normal().y, faceArray.last().normal().z), Vector3::zero()));
|
||||
|
||||
// Far plane
|
||||
if (farDist < inf())
|
||||
{
|
||||
faceArray.append(Plane(Vector3(0.0f, 0.0f, 1.0f), Vector3(0.0f, 0.0f, -farDist)));
|
||||
}
|
||||
|
||||
// Transform planes to world space
|
||||
for (int face = 0; face < faceArray.size(); ++face)
|
||||
{
|
||||
// Since there is no scale factor, we don't have to
|
||||
// worry about the inverse transpose of the normal.
|
||||
Vector3 normal;
|
||||
float d;
|
||||
|
||||
faceArray[face].getEquation(normal, d);
|
||||
|
||||
Vector3 newNormal = rotation * normal;
|
||||
|
||||
if (G3D::isFinite(d))
|
||||
{
|
||||
d = (newNormal * -d + apex).dot(newNormal);
|
||||
faceArray[face] = Plane(newNormal, newNormal * d);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When d is infinite, we can't multiply 0's by it without
|
||||
// generating NaNs.
|
||||
faceArray[face] = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Frustum::containsAABB(const Extents& aabb) const
|
||||
{
|
||||
for (int ii = 0; ii < 8; ++ii)
|
||||
{
|
||||
Vector3 corner = aabb.getCorner(ii);
|
||||
if (!containsPoint(corner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::intersectsAABB(const Aya::Extents& aabb, const G3D::CoordinateFrame& extentsFrame) const
|
||||
{
|
||||
Aya::Extents worldBox = aabb.toWorldSpace(extentsFrame);
|
||||
Vector3 extent = worldBox.size() * 0.5f;
|
||||
Vector3 center = worldBox.center();
|
||||
|
||||
for (int ii = 0; ii < 6; ++ii)
|
||||
{
|
||||
const Plane& plane = faceArray[ii];
|
||||
|
||||
float d = plane.normal().dot(center) - plane.distance();
|
||||
float r = Vector3(fabsf(plane.normal().x), fabsf(plane.normal().y), fabsf(plane.normal().z)).dot(extent);
|
||||
|
||||
// box is in negative half-space => outside the frustum
|
||||
if (d + r < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::containsAABB(const Extents& aabb, const G3D::CoordinateFrame& extentsFrame) const
|
||||
{
|
||||
for (int ii = 0; ii < 8; ++ii)
|
||||
{
|
||||
Vector3 corner = aabb.getCorner(ii);
|
||||
Vector3 world = extentsFrame.pointToWorldSpace(corner);
|
||||
if (!containsPoint(world))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::containsPoint(const Vector3& point) const
|
||||
{
|
||||
for (int ii = 0; ii < faceArray.size(); ++ii)
|
||||
{
|
||||
const Plane& plane = faceArray[ii];
|
||||
if (!plane.halfSpaceContains(point))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Frustum::intersectsSphere(const Vector3& center, float radius) const
|
||||
{
|
||||
for (int ii = 0; ii < faceArray.size(); ++ii)
|
||||
{
|
||||
const Plane& p = faceArray[ii];
|
||||
Plane offsetplane(p.normal(), p.distance() - radius);
|
||||
if (!offsetplane.halfSpaceContains(center))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
53
engine/3d/src/Frustum.hpp
Normal file
53
engine/3d/src/Frustum.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef RBXG3D_FRUSTUM_H
|
||||
#define RBXG3D_FRUSTUM_H
|
||||
|
||||
#include "Plane.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class CoordinateFrame;
|
||||
}
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Extents;
|
||||
|
||||
class Frustum
|
||||
{
|
||||
public:
|
||||
Frustum(){};
|
||||
|
||||
// fovx, fovy are in radians
|
||||
Frustum(const G3D::Vector3& apex, const G3D::Vector3& dir, const G3D::Vector3& up, float nearDist, float farDist, float fovx, float fovy);
|
||||
|
||||
enum FrustumPlane
|
||||
{
|
||||
kPlaneNear = 0,
|
||||
kPlaneRight,
|
||||
kPlaneLeft,
|
||||
kPlaneBottom,
|
||||
kPlaneTop,
|
||||
kPlaneFar,
|
||||
kPlaneInvalid,
|
||||
};
|
||||
|
||||
/** 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].
|
||||
*/
|
||||
G3D::Array<G3D::Plane> faceArray;
|
||||
|
||||
bool containsPoint(const G3D::Vector3& point) const;
|
||||
bool intersectsSphere(const G3D::Vector3& center, float radius) const;
|
||||
|
||||
bool containsAABB(const Aya::Extents& aabb) const;
|
||||
bool intersectsAABB(const Aya::Extents& aabb, const G3D::CoordinateFrame& extentsFrame) const;
|
||||
bool containsAABB(const Aya::Extents& aabb, const G3D::CoordinateFrame& extentsFrame) const;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
#endif
|
||||
68
engine/3d/src/G3DDebug.hpp
Normal file
68
engine/3d/src/G3DDebug.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
@file debug.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@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 "platform.hpp"
|
||||
#ifdef _MSC_VER
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#include "debugPrintf.hpp"
|
||||
#include "debugAssert.hpp"
|
||||
|
||||
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
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
58
engine/3d/src/G3DGameUnits.hpp
Normal file
58
engine/3d/src/G3DGameUnits.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
@file G3DGameUnits.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-10-05
|
||||
@edited 2006-11-10
|
||||
*/
|
||||
|
||||
#ifndef G3D_GAMEUNITS_H
|
||||
#define G3D_GAMEUNITS_H
|
||||
|
||||
#include "platform.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
/**
|
||||
Time, in seconds.
|
||||
*/
|
||||
typedef double GameTime;
|
||||
typedef double SimTime;
|
||||
|
||||
/**
|
||||
Actual wall clock time in seconds.
|
||||
*/
|
||||
typedef double RealTime;
|
||||
|
||||
enum AMPM
|
||||
{
|
||||
AM,
|
||||
PM
|
||||
};
|
||||
|
||||
/** \deprecated */
|
||||
enum
|
||||
{
|
||||
SECOND = 1,
|
||||
MINUTE = 60,
|
||||
HOUR = 60 * 60,
|
||||
DAY = 24 * 60 * 60,
|
||||
SUNRISE = 24 * 60 * 60 / 4,
|
||||
SUNSET = 24 * 60 * 60 * 3 / 4,
|
||||
MIDNIGHT = 0,
|
||||
METER = 1,
|
||||
KILOMETER = 1000
|
||||
};
|
||||
|
||||
/**
|
||||
Converts a 12 hour clock time into the number of seconds since
|
||||
midnight. Note that 12:00 PM is noon and 12:00 AM is midnight.
|
||||
|
||||
Example: <CODE>toSeconds(10, 00, AM)</CODE>
|
||||
*/
|
||||
SimTime toSeconds(int hour, int minute, double seconds, AMPM ap);
|
||||
SimTime toSeconds(int hour, int minute, AMPM ap);
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
453
engine/3d/src/GCamera.cpp
Normal file
453
engine/3d/src/GCamera.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
/**
|
||||
Aya - We are not using the G3D 8.0 G3D::GCamera, intstead we are using the Aya:Rbx::Camera from old G3D.
|
||||
@file GCamera.cpp
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@author Jeff Marsceill, 08jcm@williams.edu
|
||||
|
||||
@created 2005-07-20
|
||||
@edited 2010-02-22
|
||||
*/
|
||||
#include "GCamera.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "Rect2D.hpp"
|
||||
#include "Ray.hpp"
|
||||
#include "Matrix4.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
GCamera::GCamera()
|
||||
{
|
||||
debugAssert(1); /// Aya - Do not use, intead use Aya::RbxCamera
|
||||
setNearPlaneZ(-0.2f);
|
||||
setFarPlaneZ(-150.0f);
|
||||
setFieldOfView((float)toRadians(90.0f), HORIZONTAL);
|
||||
}
|
||||
|
||||
|
||||
GCamera::GCamera(const Matrix4& proj, const CFrame& frame)
|
||||
{
|
||||
debugAssert(1); /// Aya - Do not use, intead use Aya::RbxCamera
|
||||
float left, right, bottom, top, nearval, farval;
|
||||
proj.getPerspectiveProjectionParameters(left, right, bottom, top, nearval, farval);
|
||||
setNearPlaneZ(-nearval);
|
||||
setFarPlaneZ(-farval);
|
||||
float x = right;
|
||||
|
||||
// Assume horizontal field of view
|
||||
setFieldOfView(atan2(x, -m_nearPlaneZ) * 2.0f, HORIZONTAL);
|
||||
setCoordinateFrame(frame);
|
||||
}
|
||||
|
||||
|
||||
GCamera::~GCamera() {}
|
||||
|
||||
|
||||
void GCamera::getCoordinateFrame(CoordinateFrame& c) const
|
||||
{
|
||||
c = m_cframe;
|
||||
}
|
||||
|
||||
|
||||
void GCamera::setCoordinateFrame(const CoordinateFrame& c)
|
||||
{
|
||||
m_cframe = c;
|
||||
}
|
||||
|
||||
|
||||
void GCamera::setFieldOfView(float angle, FOVDirection dir)
|
||||
{
|
||||
debugAssert((angle < pi()) && (angle > 0));
|
||||
|
||||
m_fieldOfView = angle;
|
||||
m_direction = dir;
|
||||
}
|
||||
|
||||
|
||||
float GCamera::imagePlaneDepth() const
|
||||
{
|
||||
return -m_nearPlaneZ;
|
||||
}
|
||||
|
||||
float GCamera::viewportWidth(const Rect2D& viewport) const
|
||||
{
|
||||
// Compute the side of a square at the near plane based on our field of view
|
||||
float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
|
||||
|
||||
if (m_direction == VERTICAL)
|
||||
{
|
||||
s *= viewport.width() / viewport.height();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
float GCamera::viewportHeight(const Rect2D& viewport) const
|
||||
{
|
||||
// Compute the side of a square at the near plane based on our field of view
|
||||
float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
|
||||
|
||||
debugAssert(m_fieldOfView < toRadians(180));
|
||||
if (m_direction == HORIZONTAL)
|
||||
{
|
||||
s *= viewport.height() / viewport.width();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Ray GCamera::worldRay(float x, float y, const Rect2D& viewport) const
|
||||
{
|
||||
|
||||
int screenWidth = iFloor(viewport.width());
|
||||
int screenHeight = iFloor(viewport.height());
|
||||
|
||||
Vector3 origin = m_cframe.translation;
|
||||
|
||||
float cx = screenWidth / 2.0f;
|
||||
float cy = screenHeight / 2.0f;
|
||||
|
||||
float vw = viewportWidth(viewport);
|
||||
float vh = viewportHeight(viewport);
|
||||
|
||||
Vector3 direction = Vector3((x - cx) * vw / screenWidth, -(y - cy) * vh / screenHeight, m_nearPlaneZ);
|
||||
|
||||
direction = m_cframe.vectorToWorldSpace(direction);
|
||||
|
||||
// Normalize the direction (we didn't do it before)
|
||||
direction = direction.direction();
|
||||
|
||||
return Ray::fromOriginAndDirection(origin, direction);
|
||||
}
|
||||
|
||||
|
||||
void GCamera::getProjectPixelMatrix(const Rect2D& viewport, Matrix4& P) const
|
||||
{
|
||||
getProjectUnitMatrix(viewport, P);
|
||||
float screenWidth = viewport.width();
|
||||
float screenHeight = viewport.height();
|
||||
|
||||
float sx = screenWidth / 2.0;
|
||||
float sy = screenHeight / 2.0;
|
||||
|
||||
P = Matrix4(sx, 0, 0, sx + viewport.x0() - m_pixelOffset.x, 0, -sy, 0, sy + viewport.y0() + m_pixelOffset.y, 0, 0, 1, 0, 0, 0, 0, 1) * P;
|
||||
}
|
||||
|
||||
|
||||
void GCamera::getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const
|
||||
{
|
||||
|
||||
float screenWidth = viewport.width();
|
||||
float screenHeight = viewport.height();
|
||||
|
||||
float r, l, t, b, n, f, x, y;
|
||||
|
||||
float s = 1.0f;
|
||||
if (m_direction == VERTICAL)
|
||||
{
|
||||
y = -m_nearPlaneZ * tan(m_fieldOfView / 2);
|
||||
x = y * (screenWidth / screenHeight);
|
||||
s = screenHeight;
|
||||
}
|
||||
else
|
||||
{ // m_direction == HORIZONTAL
|
||||
x = -m_nearPlaneZ * tan(m_fieldOfView / 2);
|
||||
y = x * (screenHeight / screenWidth);
|
||||
s = screenWidth;
|
||||
}
|
||||
|
||||
n = -m_nearPlaneZ;
|
||||
f = -m_farPlaneZ;
|
||||
r = x - m_pixelOffset.x / s;
|
||||
l = -x - m_pixelOffset.x / s;
|
||||
t = y + m_pixelOffset.y / s;
|
||||
b = -y + m_pixelOffset.y / s;
|
||||
|
||||
P = Matrix4::perspectiveProjection(l, r, b, t, n, f);
|
||||
}
|
||||
|
||||
|
||||
Vector3 GCamera::projectUnit(const Vector3& point, const Rect2D& viewport) const
|
||||
{
|
||||
Matrix4 M;
|
||||
getProjectUnitMatrix(viewport, M);
|
||||
|
||||
Vector4 cameraSpacePoint(coordinateFrame().pointToObjectSpace(point), 1.0f);
|
||||
const Vector4& screenSpacePoint = M * cameraSpacePoint;
|
||||
|
||||
return Vector3(screenSpacePoint.xyz() / screenSpacePoint.w);
|
||||
}
|
||||
|
||||
Vector3 GCamera::project(const Vector3& point, const Rect2D& viewport) const
|
||||
{
|
||||
|
||||
// Find the point in the homogeneous cube
|
||||
const Vector3& cube = projectUnit(point, viewport);
|
||||
|
||||
return convertFromUnitToNormal(cube, viewport);
|
||||
}
|
||||
|
||||
Vector3 GCamera::unprojectUnit(const Vector3& v, const Rect2D& viewport) const
|
||||
{
|
||||
|
||||
const Vector3& projectedPoint = convertFromUnitToNormal(v, viewport);
|
||||
|
||||
return unproject(projectedPoint, viewport);
|
||||
}
|
||||
|
||||
|
||||
Vector3 GCamera::unproject(const Vector3& v, const Rect2D& viewport) const
|
||||
{
|
||||
|
||||
const float n = m_nearPlaneZ;
|
||||
const float f = m_farPlaneZ;
|
||||
|
||||
float z;
|
||||
|
||||
if (-f >= finf())
|
||||
{
|
||||
// Infinite far plane
|
||||
z = 1.0f / (((-1.0f / n) * v.z) + 1.0f / n);
|
||||
}
|
||||
else
|
||||
{
|
||||
z = 1.0f / ((((1.0f / f) - (1.0f / n)) * v.z) + 1.0f / n);
|
||||
}
|
||||
|
||||
const Ray& ray = worldRay(v.x - m_pixelOffset.x, v.y - m_pixelOffset.y, viewport);
|
||||
|
||||
// Find out where the ray reaches the specified depth.
|
||||
const Vector3& out = ray.origin() + ray.direction() * -z / (ray.direction().dot(m_cframe.lookVector()));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
float GCamera::worldToScreenSpaceArea(float area, float z, const Rect2D& viewport) const
|
||||
{
|
||||
(void)viewport;
|
||||
if (z >= 0)
|
||||
{
|
||||
return finf();
|
||||
}
|
||||
return area * (float)square(imagePlaneDepth() / z);
|
||||
}
|
||||
|
||||
|
||||
void GCamera::getClipPlanes(const Rect2D& viewport, Array<Plane>& clip) const
|
||||
{
|
||||
|
||||
Frustum fr;
|
||||
frustum(viewport, fr);
|
||||
clip.resize(fr.faceArray.size(), DONT_SHRINK_UNDERLYING_ARRAY);
|
||||
for (int f = 0; f < clip.size(); ++f)
|
||||
{
|
||||
clip[f] = fr.faceArray[f].plane;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GCamera::Frustum GCamera::frustum(const Rect2D& viewport) const
|
||||
{
|
||||
Frustum f;
|
||||
frustum(viewport, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
void GCamera::frustum(const Rect2D& viewport, Frustum& fr) const
|
||||
{
|
||||
|
||||
// The volume is the convex hull of the vertices definining the view
|
||||
// frustum and the light source point at infinity.
|
||||
|
||||
const float x = viewportWidth(viewport) / 2;
|
||||
const float y = viewportHeight(viewport) / 2;
|
||||
const float zn = m_nearPlaneZ;
|
||||
const float zf = m_farPlaneZ;
|
||||
float xx, zz, yy;
|
||||
|
||||
float halfFOV = m_fieldOfView * 0.5f;
|
||||
|
||||
// This computes the normal, which is based on the complement of the
|
||||
// halfFOV angle, so the equations are "backwards"
|
||||
if (m_direction == VERTICAL)
|
||||
{
|
||||
yy = -cosf(halfFOV);
|
||||
xx = yy * viewport.height() / viewport.width();
|
||||
zz = -sinf(halfFOV);
|
||||
}
|
||||
else
|
||||
{
|
||||
xx = -cosf(halfFOV);
|
||||
yy = xx * viewport.width() / viewport.height();
|
||||
zz = -sinf(halfFOV);
|
||||
}
|
||||
|
||||
// Near face (ccw from UR)
|
||||
fr.vertexPos.append(Vector4(x, y, zn, 1), Vector4(-x, y, zn, 1), Vector4(-x, -y, zn, 1), Vector4(x, -y, zn, 1));
|
||||
|
||||
// Far face (ccw from UR, from origin)
|
||||
if (m_farPlaneZ == -finf())
|
||||
{
|
||||
fr.vertexPos.append(Vector4(x, y, zn, 0), Vector4(-x, y, zn, 0), Vector4(-x, -y, zn, 0), Vector4(x, -y, zn, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Finite
|
||||
const float s = zf / zn;
|
||||
fr.vertexPos.append(
|
||||
Vector4(x * s, y * s, zf, 1), Vector4(-x * s, y * s, zf, 1), Vector4(-x * s, -y * s, zf, 1), Vector4(x * s, -y * s, zf, 1));
|
||||
}
|
||||
|
||||
Frustum::Face face;
|
||||
|
||||
// Near plane (wind backwards so normal faces into frustum)
|
||||
// Recall that nearPlane, farPlane are positive numbers, so
|
||||
// we need to negate them to produce actual z values.
|
||||
face.plane = Plane(Vector3(0, 0, -1), Vector3(0, 0, m_nearPlaneZ));
|
||||
face.vertexIndex[0] = 3;
|
||||
face.vertexIndex[1] = 2;
|
||||
face.vertexIndex[2] = 1;
|
||||
face.vertexIndex[3] = 0;
|
||||
fr.faceArray.append(face);
|
||||
|
||||
// Right plane
|
||||
face.plane = Plane(Vector3(xx, 0, zz), Vector3::zero());
|
||||
face.vertexIndex[0] = 0;
|
||||
face.vertexIndex[1] = 4;
|
||||
face.vertexIndex[2] = 7;
|
||||
face.vertexIndex[3] = 3;
|
||||
fr.faceArray.append(face);
|
||||
|
||||
// Left plane
|
||||
face.plane = Plane(Vector3(-fr.faceArray.last().plane.normal().x, 0, fr.faceArray.last().plane.normal().z), Vector3::zero());
|
||||
face.vertexIndex[0] = 5;
|
||||
face.vertexIndex[1] = 1;
|
||||
face.vertexIndex[2] = 2;
|
||||
face.vertexIndex[3] = 6;
|
||||
fr.faceArray.append(face);
|
||||
|
||||
// Top plane
|
||||
face.plane = Plane(Vector3(0, yy, zz), Vector3::zero());
|
||||
face.vertexIndex[0] = 1;
|
||||
face.vertexIndex[1] = 5;
|
||||
face.vertexIndex[2] = 4;
|
||||
face.vertexIndex[3] = 0;
|
||||
fr.faceArray.append(face);
|
||||
|
||||
// Bottom plane
|
||||
face.plane = Plane(Vector3(0, -fr.faceArray.last().plane.normal().y, fr.faceArray.last().plane.normal().z), Vector3::zero());
|
||||
face.vertexIndex[0] = 2;
|
||||
face.vertexIndex[1] = 3;
|
||||
face.vertexIndex[2] = 7;
|
||||
face.vertexIndex[3] = 6;
|
||||
fr.faceArray.append(face);
|
||||
|
||||
// Far plane
|
||||
if (-m_farPlaneZ < finf())
|
||||
{
|
||||
face.plane = Plane(Vector3(0, 0, 1), Vector3(0, 0, m_farPlaneZ));
|
||||
face.vertexIndex[0] = 4;
|
||||
face.vertexIndex[1] = 5;
|
||||
face.vertexIndex[2] = 6;
|
||||
face.vertexIndex[3] = 7;
|
||||
fr.faceArray.append(face);
|
||||
}
|
||||
|
||||
// Transform vertices to world space
|
||||
for (int v = 0; v < fr.vertexPos.size(); ++v)
|
||||
{
|
||||
fr.vertexPos[v] = m_cframe.toWorldSpace(fr.vertexPos[v]);
|
||||
}
|
||||
|
||||
// Transform planes to world space
|
||||
for (int p = 0; p < fr.faceArray.size(); ++p)
|
||||
{
|
||||
// Since there is no scale factor, we don't have to
|
||||
// worry about the inverse transpose of the normal.
|
||||
Vector3 normal;
|
||||
float d;
|
||||
|
||||
fr.faceArray[p].plane.getEquation(normal, d);
|
||||
|
||||
Vector3 newNormal = m_cframe.rotation * normal;
|
||||
|
||||
if (isFinite(d))
|
||||
{
|
||||
d = (newNormal * -d + m_cframe.translation).dot(newNormal);
|
||||
fr.faceArray[p].plane = Plane(newNormal, newNormal * d);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When d is infinite, we can't multiply 0's by it without
|
||||
// generating NaNs.
|
||||
fr.faceArray[p].plane = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCamera::getNearViewportCorners(const Rect2D& viewport, Vector3& outUR, Vector3& outUL, Vector3& outLL, Vector3& outLR) const
|
||||
{
|
||||
|
||||
// Must be kept in sync with getFrustum()
|
||||
const float w = viewportWidth(viewport) / 2.0f;
|
||||
const float h = viewportHeight(viewport) / 2.0f;
|
||||
const float z = nearPlaneZ();
|
||||
|
||||
// Compute the points
|
||||
outUR = Vector3(w, h, z);
|
||||
outUL = Vector3(-w, h, z);
|
||||
outLL = Vector3(-w, -h, z);
|
||||
outLR = Vector3(w, -h, z);
|
||||
|
||||
// Take to world space
|
||||
outUR = m_cframe.pointToWorldSpace(outUR);
|
||||
outUL = m_cframe.pointToWorldSpace(outUL);
|
||||
outLR = m_cframe.pointToWorldSpace(outLR);
|
||||
outLL = m_cframe.pointToWorldSpace(outLL);
|
||||
}
|
||||
|
||||
void GCamera::getFarViewportCorners(const Rect2D& viewport, Vector3& outUR, Vector3& outUL, Vector3& outLL, Vector3& outLR) const
|
||||
{
|
||||
|
||||
// Must be kept in sync with getFrustum()
|
||||
const float w = viewportWidth(viewport) * m_farPlaneZ / m_nearPlaneZ;
|
||||
const float h = viewportHeight(viewport) * m_farPlaneZ / m_nearPlaneZ;
|
||||
const float z = m_farPlaneZ;
|
||||
|
||||
// Compute the points
|
||||
outUR = Vector3(w / 2, h / 2, z);
|
||||
outUL = Vector3(-w / 2, h / 2, z);
|
||||
outLL = Vector3(-w / 2, -h / 2, z);
|
||||
outLR = Vector3(w / 2, -h / 2, z);
|
||||
|
||||
// Take to world space
|
||||
outUR = m_cframe.pointToWorldSpace(outUR);
|
||||
outUL = m_cframe.pointToWorldSpace(outUL);
|
||||
outLR = m_cframe.pointToWorldSpace(outLR);
|
||||
outLL = m_cframe.pointToWorldSpace(outLL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GCamera::setPosition(const Vector3& t)
|
||||
{
|
||||
m_cframe.translation = t;
|
||||
}
|
||||
|
||||
|
||||
void GCamera::lookAt(const Vector3& position, const Vector3& up)
|
||||
{
|
||||
m_cframe.lookAt(position, up);
|
||||
}
|
||||
|
||||
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 G3D
|
||||
334
engine/3d/src/GCamera.hpp
Normal file
334
engine/3d/src/GCamera.hpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/**
|
||||
Aya - We are not using the G3D::GCamera, intstead we are using the Aya:Rbx::Camera from old G3D.
|
||||
@file GCamera.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2005-07-20
|
||||
@edited 2009-04-20
|
||||
*/
|
||||
|
||||
#ifndef G3D_GCamera_H
|
||||
#define G3D_GCamera_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "debugAssert.hpp"
|
||||
|
||||
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().
|
||||
|
||||
See http://bittermanandy.wordpress.com/2009/04/10/a-view-to-a-thrill-part-one-camera-concepts/
|
||||
for a nice introduction to camera transformations.
|
||||
*/
|
||||
class GCamera
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
Stores the direction of the field of view
|
||||
*/
|
||||
enum FOVDirection
|
||||
{
|
||||
HORIZONTAL,
|
||||
VERTICAL
|
||||
};
|
||||
|
||||
private:
|
||||
/** Full 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;
|
||||
|
||||
Vector2 m_pixelOffset;
|
||||
|
||||
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();
|
||||
|
||||
GCamera(const Matrix4& proj, const CFrame& frame);
|
||||
|
||||
virtual ~GCamera();
|
||||
|
||||
/** Returns the current coordinate frame */
|
||||
const CoordinateFrame& coordinateFrame() const
|
||||
{
|
||||
return m_cframe;
|
||||
}
|
||||
|
||||
/** Displacement from the upper left added in pixels in screen
|
||||
space to the projection matrix. This is useful for shifting
|
||||
the sampled location from the pixel center (OpenGL convention)
|
||||
to other locations, such as the upper-left.*/
|
||||
void setPixelOffset(const Vector2& p)
|
||||
{
|
||||
m_pixelOffset = p;
|
||||
}
|
||||
|
||||
const Vector2& pixelOffset() const
|
||||
{
|
||||
return m_pixelOffset;
|
||||
}
|
||||
|
||||
/** Sets c to the camera's coordinate frame */
|
||||
void getCoordinateFrame(CoordinateFrame& c) const;
|
||||
|
||||
/** Sets a new coordinate frame for the camera */
|
||||
void setCoordinateFrame(const CoordinateFrame& c);
|
||||
|
||||
/** Sets \a P equal to the camera's projection matrix. This is the
|
||||
matrix that maps points to the homogeneous clip cube that
|
||||
varies from -1 to 1 on all axes. The projection matrix does
|
||||
not include the camera transform.
|
||||
|
||||
This is the matrix that a RenderDevice (or OpenGL) uses as the projection matrix.
|
||||
@sa RenderDevice::setProjectionAndCameraMatrix, RenderDevice::setProjectionMatrix, Matrix4::perspectiveProjection
|
||||
*/
|
||||
void getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const;
|
||||
|
||||
/** Sets \a P equal to the matrix that transforms points to pixel
|
||||
coordinates on the given viewport. A point correspoinding to
|
||||
the top-left corner of the viewport in camera space will
|
||||
transform to viewport.x0y0() and the bottom-right to viewport.x1y1(). */
|
||||
void getProjectPixelMatrix(const Rect2D& viewport, Matrix4& P) const;
|
||||
|
||||
/** Converts projected points from OpenGL standards
|
||||
(-1, 1) to normal 3D coordinate standards (0, 1)
|
||||
|
||||
\deprecated
|
||||
*/ // TODO: Remove
|
||||
Vector3 convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const;
|
||||
|
||||
/**
|
||||
Sets the field of view, in radians. The
|
||||
initial angle is toRadians(55). Must specify
|
||||
the direction of the angle.
|
||||
|
||||
This is the full angle, i.e., from the left side of the
|
||||
viewport to the right side.
|
||||
*/
|
||||
void setFieldOfView(float edgeToEdgeAngleRadians, FOVDirection direction);
|
||||
|
||||
/** Returns the current full field of view angle (from the left side of the
|
||||
viewport to the right side) and direction */
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
1414
engine/3d/src/GImage.cpp
Normal file
1414
engine/3d/src/GImage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
538
engine/3d/src/GImage.hpp
Normal file
538
engine/3d/src/GImage.hpp
Normal file
@@ -0,0 +1,538 @@
|
||||
/**
|
||||
\file GImage.h
|
||||
|
||||
See G3D::GImage for details.
|
||||
|
||||
@cite JPEG compress/decompressor is the <A HREF="http://www.ijg.org/files/">IJG library</A>, used in accordance with their license.
|
||||
@cite JPG code by John Chisholm, using the IJG Library
|
||||
@cite TGA code by Morgan McGuire
|
||||
@cite BMP code by John Chisholm, based on code by Edward "CGameProgrammer" Resnick <A HREF="mailto:cgp@gdnmail.net">mailto:cgp@gdnmail.net</A> at <A
|
||||
HREF="ftp://ftp.flipcode.com/cotd/LoadPicture.txt">ftp://ftp.flipcode.com/cotd/LoadPicture.txt</A>
|
||||
@cite PCX format described in the ZSOFT PCX manual http://www.nist.fss.ru/hr/doc/spec/pcx.htm#2
|
||||
@cite PNG compress/decompressor is the <A HREF="http://www.libpng.org/pub/png/libpng.html">libpng library</A>, used in accordance with their
|
||||
license.
|
||||
@cite PPM code by Morgan McGuire based on http://netpbm.sourceforge.net/doc/ppm.html
|
||||
|
||||
\maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
\created 2002-05-27
|
||||
\edited 2010-01-04
|
||||
|
||||
Copyright 2000-2010, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_GImage_h
|
||||
#define G3D_GImage_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include <string>
|
||||
#include "Array.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "stringutils.hpp"
|
||||
#include "Color1uint8.hpp"
|
||||
#include "Color3uint8.hpp"
|
||||
#include "Color4uint8.hpp"
|
||||
#include "MemoryManager.hpp"
|
||||
#include "BumpMapPreprocess.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class BinaryInput;
|
||||
class BinaryOutput;
|
||||
|
||||
|
||||
/**
|
||||
Interface to image compression & file formats.
|
||||
|
||||
Supported formats (decode and encode): Color JPEG, PNG,
|
||||
(Uncompressed) TGA 24, (Uncompressed) TGA 32, BMP 1, BMP 4, BMP 8, BMP
|
||||
24, PPM (P6), and PPM ASCII (P1, P2, P3), which includes PPM, PGM, PBM,
|
||||
and JXL. (Compressed) TGA 24, (Compressed) TGA 32, 8-bit paletted PCX, 24-bit PCX, and ICO are supported for
|
||||
decoding only.
|
||||
|
||||
Sample usage:
|
||||
|
||||
\verbatim
|
||||
// Loading from disk:
|
||||
G3D::GImage im1("test.jpg");
|
||||
|
||||
// Loading from memory:
|
||||
G3D::GImage im2(data, length);
|
||||
|
||||
// im.pixel is a pointer to RGB color data. If you want
|
||||
// an alpha channel, call RGBtoRGBA or RGBtoARGB for
|
||||
// conversion.
|
||||
|
||||
// Saving to memory:
|
||||
G3D::GImage im3(width, height);
|
||||
// (Set the pixels of im3...)
|
||||
uint8* data2;
|
||||
int len2;
|
||||
im3.encode(G3D::GImage::JPEG, data2, len2);
|
||||
|
||||
// Saving to disk
|
||||
im3.save("out.jpg");
|
||||
\endverbatim
|
||||
|
||||
The free Image Magick Magick Wand API
|
||||
(http://www.imagemagick.org/www/api/magick_wand.html) provides a more powerful
|
||||
API for image manipulation and wider set of image load/save formats. It is
|
||||
recommended over GImage (we don't include it directly in G3D because their license
|
||||
is more restrictive than the BSD one).
|
||||
|
||||
\cite http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga_specs.pdf
|
||||
|
||||
\sa Image3, Image3uint8, Image4, Image4uint8, Image1, Image1uint8, Texture, Map2D
|
||||
|
||||
*/
|
||||
class GImage
|
||||
{
|
||||
private:
|
||||
/** Used exclusively for allocating m_byte; this may be an
|
||||
implementation that allocates directly on a GPU.*/
|
||||
MemoryManager::Ref m_memMan;
|
||||
uint8* m_byte;
|
||||
|
||||
int m_channels;
|
||||
int m_width;
|
||||
int m_height;
|
||||
|
||||
public:
|
||||
class Error
|
||||
{
|
||||
public:
|
||||
Error(const std::string& reason, const std::string& filename = "")
|
||||
: reason(reason)
|
||||
, filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
std::string reason;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
enum Format
|
||||
{
|
||||
JPEG,
|
||||
JXL,
|
||||
BMP,
|
||||
TGA,
|
||||
PCX,
|
||||
ICO,
|
||||
PNG,
|
||||
AUTODETECT,
|
||||
UNKNOWN,
|
||||
DDS,
|
||||
PVR
|
||||
};
|
||||
|
||||
/**
|
||||
The number of channels; either 3 (RGB) or 4 (RGBA)
|
||||
*/
|
||||
inline int channels() const
|
||||
{
|
||||
return m_channels;
|
||||
}
|
||||
|
||||
inline int width() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
inline int height() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
inline const uint8* byte() const
|
||||
{
|
||||
return m_byte;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the underlying data, which is stored
|
||||
in row-major order without row padding.
|
||||
e.g., <code>uint8* ptr = image.rawData<uint8>();
|
||||
*/
|
||||
template<typename Type>
|
||||
inline const Type* rawData() const
|
||||
{
|
||||
return (Type*)m_byte;
|
||||
}
|
||||
|
||||
/** \copybrief GImage::rawData() const */
|
||||
template<typename Type>
|
||||
inline Type* rawData()
|
||||
{
|
||||
return (Type*)m_byte;
|
||||
}
|
||||
|
||||
inline const Color1uint8* pixel1() const
|
||||
{
|
||||
debugAssertM(m_channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", m_channels));
|
||||
return (Color1uint8*)m_byte;
|
||||
}
|
||||
|
||||
inline Color1uint8* pixel1()
|
||||
{
|
||||
debugAssertM(m_channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", m_channels));
|
||||
return (Color1uint8*)m_byte;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the upper left pixel
|
||||
as Color4uint8.
|
||||
*/
|
||||
inline const Color4uint8* pixel4() const
|
||||
{
|
||||
debugAssertM(m_channels == 4, format("Tried to call GImage::pixel4 on an image with %d channels", m_channels));
|
||||
return (Color4uint8*)m_byte;
|
||||
}
|
||||
|
||||
inline Color4uint8* pixel4()
|
||||
{
|
||||
debugAssert(m_channels == 4);
|
||||
return (Color4uint8*)m_byte;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the upper left pixel
|
||||
as Color3uint8.
|
||||
*/
|
||||
inline const Color3uint8* pixel3() const
|
||||
{
|
||||
debugAssertM(m_channels == 3, format("Tried to call GImage::pixel3 on an image with %d channels", m_channels));
|
||||
return (Color3uint8*)m_byte;
|
||||
}
|
||||
|
||||
inline Color3uint8* pixel3()
|
||||
{
|
||||
debugAssert(m_channels == 3);
|
||||
return (Color3uint8*)m_byte;
|
||||
}
|
||||
|
||||
/** Returns the pixel at (x, y), where (0,0) is the upper left. */
|
||||
inline const Color1uint8& pixel1(int x, int y) const
|
||||
{
|
||||
debugAssert(x >= 0 && x < m_width);
|
||||
debugAssert(y >= 0 && y < m_height);
|
||||
return pixel1()[x + y * m_width];
|
||||
}
|
||||
|
||||
/** Returns the pixel at (x, y), where (0,0) is the upper left. */
|
||||
inline Color1uint8& pixel1(int x, int y)
|
||||
{
|
||||
debugAssert(x >= 0 && x < m_width);
|
||||
debugAssert(y >= 0 && y < m_height);
|
||||
return pixel1()[x + y * m_width];
|
||||
}
|
||||
|
||||
/** Returns the pixel at (x, y), where (0,0) is the upper left. */
|
||||
inline const Color3uint8& pixel3(int x, int y) const
|
||||
{
|
||||
debugAssert(x >= 0 && x < m_width);
|
||||
debugAssert(y >= 0 && y < m_height);
|
||||
return pixel3()[x + y * m_width];
|
||||
}
|
||||
|
||||
inline Color3uint8& pixel3(int x, int y)
|
||||
{
|
||||
debugAssert(x >= 0 && x < m_width);
|
||||
debugAssert(y >= 0 && y < m_height);
|
||||
return pixel3()[x + y * m_width];
|
||||
}
|
||||
|
||||
/** Returns the pixel at (x, y), where (0,0) is the upper left. */
|
||||
inline const Color4uint8& pixel4(int x, int y) const
|
||||
{
|
||||
debugAssert(x >= 0 && x < m_width);
|
||||
debugAssert(y >= 0 && y < m_height);
|
||||
return pixel4()[x + y * m_width];
|
||||
}
|
||||
|
||||
inline Color4uint8& pixel4(int x, int y)
|
||||
{
|
||||
debugAssert(x >= 0 && x < m_width);
|
||||
debugAssert(y >= 0 && y < m_height);
|
||||
return pixel4()[x + y * m_width];
|
||||
}
|
||||
|
||||
inline uint8* byte()
|
||||
{
|
||||
return m_byte;
|
||||
}
|
||||
// Aya
|
||||
G3D::Color4uint8 getPixel(int x, int y) const;
|
||||
void setPixel(int x, int y, G3D::Color4uint8 val);
|
||||
/**
|
||||
Resizes the image using bilinear interpolation.
|
||||
All software and _slow_!
|
||||
*/
|
||||
GImage bilinearStretchBlt(int newwidth, int newheight) const;
|
||||
/**
|
||||
Resizes the image.
|
||||
All software and _slow_!
|
||||
*/
|
||||
GImage stretchBlt(int newwidth, int newheight) const;
|
||||
|
||||
// ======
|
||||
|
||||
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 encodeJXL(BinaryOutput& out) const;
|
||||
void decodeJXL(BinaryInput& input);
|
||||
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 _copy(const GImage& other);
|
||||
|
||||
public:
|
||||
// Aya: moved from private to public
|
||||
/**
|
||||
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 flipHorizontal();
|
||||
void flipVertical();
|
||||
void rotate90CW(int numTimes = 1);
|
||||
|
||||
/**
|
||||
Create an empty image of the given size.
|
||||
\sa load()
|
||||
*/
|
||||
GImage(int width = 0, int height = 0, int channels = 3, const MemoryManager::Ref& m = MemoryManager::create());
|
||||
|
||||
/**
|
||||
Decodes an image stored in a buffer.
|
||||
*/
|
||||
GImage(const unsigned char* data, int length, Format format = AUTODETECT, const MemoryManager::Ref& m = MemoryManager::create());
|
||||
|
||||
GImage(const GImage& other, const MemoryManager::Ref& m = MemoryManager::create());
|
||||
|
||||
GImage& operator=(const GImage& other);
|
||||
|
||||
/**
|
||||
Returns a new GImage that has 4 channels. RGB is
|
||||
taken from this GImage and the alpha from the red
|
||||
channel of the supplied image. The new GImage is passed
|
||||
as a reference parameter for speed.
|
||||
*/
|
||||
void insertRedAsAlpha(const GImage& alpha, GImage& output) const;
|
||||
|
||||
/**
|
||||
Returns a new GImage with 3 channels, removing
|
||||
the alpha channel if there is one. The new GImage
|
||||
is passed as a reference parameter for speed.
|
||||
*/
|
||||
void stripAlpha(GImage& output) const;
|
||||
// Aya
|
||||
GImage stripAlpha() const;
|
||||
void setColorAlphaTest(const Color4uint8& color);
|
||||
// ======
|
||||
|
||||
/**
|
||||
Frees memory and resets to a 0x0 image.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
Deallocates the pixels.
|
||||
*/
|
||||
virtual ~GImage();
|
||||
|
||||
/**
|
||||
Resizes the internal buffer to (\a width x \a height) with the
|
||||
number of \a channels specified.
|
||||
|
||||
\param zero If true, all data is set to 0 (black).
|
||||
*/
|
||||
void resize(int width, int height, int channels, bool zero = true);
|
||||
|
||||
/**
|
||||
Copies src sub-image data into dest at a certain offset.
|
||||
The dest variable must already contain an image that is large
|
||||
enough to contain the src sub-image at the specified offset.
|
||||
Returns true on success and false if the src sub-image cannot
|
||||
completely fit within dest at the specified offset. Both
|
||||
src and dest must have the same number of channels.
|
||||
*/
|
||||
static bool pasteSubImage(GImage& dest, const GImage& src, int destX, int destY, int srcX, int srcY, int srcWidth, int srcHeight);
|
||||
// Aya
|
||||
static bool rotateAndPasteSubImage(
|
||||
GImage& dest, GImage& src, int destX, int destY, int srcX, int srcY, int srcWidth, int srcHeight, int rotation);
|
||||
static G3D::Color4uint8 getPixel(const GImage& img, int x, int y);
|
||||
static void setPixel(GImage& img, int x, int y, G3D::Color4uint8 val);
|
||||
static G3D::Color4uint8 interpolateColor(G3D::Color4uint8 c1, G3D::Color4uint8 c2, float p);
|
||||
static void cornerGrad(GImage& src, int x, int y, int w, int h, int rotation);
|
||||
|
||||
// =====
|
||||
|
||||
|
||||
/**
|
||||
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);
|
||||
|
||||
/**
|
||||
The caller must delete the returned buffer.
|
||||
TODO: provide a memory manager
|
||||
*/
|
||||
void encode(Format format, uint8*& outData, int& outLength) const;
|
||||
|
||||
/**
|
||||
Does not commit the BinaryOutput when done.
|
||||
*/
|
||||
void encode(Format format, BinaryOutput& out) const;
|
||||
|
||||
/**
|
||||
Decodes the buffer into this image.
|
||||
@param format Must be the correct format.
|
||||
*/
|
||||
void decode(BinaryInput& input, Format format);
|
||||
|
||||
/** Returns the size of this object in bytes */
|
||||
int sizeInMemory() const;
|
||||
|
||||
/** Ok for in == out */
|
||||
static void R8G8B8_to_Y8U8V8(int width, int height, const uint8* in, uint8* out);
|
||||
|
||||
/** Ok for in == out */
|
||||
static void Y8U8V8_to_R8G8B8(int width, int height, const uint8* in, uint8* out);
|
||||
|
||||
/**
|
||||
@param in RGB buffer of numPixels * 3 bytes
|
||||
@param out Buffer of numPixels * 4 bytes
|
||||
@param numPixels Number of RGB pixels to convert
|
||||
*/
|
||||
static void RGBtoRGBA(const uint8* in, uint8* out, int numPixels);
|
||||
|
||||
static void RGBtoARGB(const uint8* in, uint8* out, int numPixels);
|
||||
|
||||
static void LtoRGB(const uint8* in, uint8* out, int numPixels);
|
||||
|
||||
static void LtoRGBA(const uint8* in, uint8* out, int numPixels);
|
||||
|
||||
/** Safe for in == out */
|
||||
static void RGBtoBGR(const uint8* in, uint8* out, int numPixels);
|
||||
|
||||
/**
|
||||
Win32 32-bit HDC format.
|
||||
*/
|
||||
static void RGBtoBGRA(const uint8* in, uint8* out, int numPixels);
|
||||
|
||||
static void RGBAtoRGB(const uint8* in, uint8* out, int numPixels);
|
||||
/**
|
||||
Uses the red channel of the second image as an alpha channel.
|
||||
*/
|
||||
static void RGBxRGBtoRGBA(const uint8* colorRGB, const uint8* alphaRGB, uint8* out, int numPixels);
|
||||
|
||||
/**
|
||||
Flips the image along the vertical axis.
|
||||
Safe for in == out.
|
||||
*/
|
||||
static void flipRGBVertical(const uint8* in, uint8* out, int width, int height);
|
||||
|
||||
static void flipRGBAVertical(const uint8* in, uint8* out, int width, int height);
|
||||
|
||||
/**
|
||||
Given a tangent space bump map, computes a new image where the
|
||||
RGB channels are a tangent space normal map and the alpha channel
|
||||
is the original bump map. Assumes the input image is tileable.
|
||||
|
||||
In the resulting image, x = red = tangent, y = green = binormal, and z = blue = normal.
|
||||
|
||||
Particularly useful as part of the idiom:
|
||||
<PRE>
|
||||
GImage normal;
|
||||
computeNormalMap(GImage(filename), normal);
|
||||
return Texture::fromGImage(filename, normal);
|
||||
</PRE>
|
||||
|
||||
*/
|
||||
static void computeNormalMap(const class GImage& bump, class GImage& normal, const BumpMapPreprocess& preprocess = BumpMapPreprocess());
|
||||
|
||||
static void computeNormalMap(
|
||||
int width, int height, int channels, const uint8* src, GImage& normal, const BumpMapPreprocess& preprocess = BumpMapPreprocess());
|
||||
|
||||
/**
|
||||
Bayer demosaicing using the filter proposed in
|
||||
|
||||
HIGH-QUALITY LINEAR INTERPOLATION FOR DEMOSAICING OF BAYER-PATTERNED COLOR IMAGES
|
||||
Henrique S. Malvar, Li-wei He, and Ross Cutler
|
||||
|
||||
The filter wraps at the image boundaries.
|
||||
|
||||
Assumes in != out.
|
||||
*/
|
||||
static void BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
|
||||
static void BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
|
||||
static void BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
|
||||
static void BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
|
||||
|
||||
/** Fast conversion; the output has 1/2 the size of the input in each direction. Assumes in != out.
|
||||
See G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC for a much better result. */
|
||||
static void BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int inWidth, int inHeight, const uint8* in, uint8* out);
|
||||
|
||||
/** Attempt to undo fast conversion of G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8;
|
||||
the green channel will lose data. Assumes in != out
|
||||
The input should have size 3 * inWidth * inHeight. The output should have size
|
||||
2 * inWidth * 2 * inHeight.
|
||||
*/
|
||||
static void Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out);
|
||||
|
||||
/** Overwrites every pixel with one of the two colors in a checkerboard pattern.
|
||||
The fields used from the two colors depend on the current number of channels in @a im.
|
||||
*/
|
||||
static void makeCheckerboard(GImage& im, int checkerSize = 1, const Color4uint8& color1 = Color4uint8(255, 255, 255, 255),
|
||||
const Color4uint8& color2 = Color4uint8(0, 0, 0, 255));
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
306
engine/3d/src/GImage_bayer.cpp
Normal file
306
engine/3d/src/GImage_bayer.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
@file GImage_bayer.cpp
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-05-27
|
||||
@edited 2006-05-10
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "GImage.hpp"
|
||||
|
||||
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
|
||||
|
||||
} // namespace G3D
|
||||
843
engine/3d/src/GImage_bmp.cpp
Normal file
843
engine/3d/src/GImage_bmp.cpp
Normal file
@@ -0,0 +1,843 @@
|
||||
/**
|
||||
@file GImage_bmp.cpp
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-05-27
|
||||
@edited 2006-05-10
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "GImage.hpp"
|
||||
#include "BinaryOutput.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
#ifndef G3D_WIN32
|
||||
/**
|
||||
This is used by the Windows bitmap I/O.
|
||||
*/
|
||||
static const int BI_RGB = 0;
|
||||
#endif
|
||||
|
||||
void GImage::encodeBMP(BinaryOutput& out) const
|
||||
{
|
||||
|
||||
debugAssert(m_channels == 1 || m_channels == 3);
|
||||
out.setEndian(G3D_LITTLE_ENDIAN);
|
||||
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
int pixelBufferSize = m_width * m_height * 3;
|
||||
int fileHeaderSize = 14;
|
||||
int infoHeaderSize = 40;
|
||||
int BMScanWidth;
|
||||
int BMPadding;
|
||||
|
||||
// First write the BITMAPFILEHEADER
|
||||
//
|
||||
// WORD bfType;
|
||||
// DWORD bfSize;
|
||||
// WORD bfReserved1;
|
||||
// WORD bfReserved2;
|
||||
// DWORD bfOffBits;
|
||||
|
||||
// Type
|
||||
out.writeUInt8('B');
|
||||
out.writeUInt8('M');
|
||||
|
||||
// File size
|
||||
out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize);
|
||||
|
||||
// Two reserved fields set to zero
|
||||
out.writeUInt16(0);
|
||||
out.writeUInt16(0);
|
||||
|
||||
// The offset, in bytes, from the BITMAPFILEHEADER structure
|
||||
// to the bitmap bits.
|
||||
out.writeUInt32(infoHeaderSize + fileHeaderSize);
|
||||
|
||||
// Now the BITMAPINFOHEADER
|
||||
//
|
||||
// DWORD biSize;
|
||||
// LONG biWidth;
|
||||
// LONG biHeight;
|
||||
// WORD biPlanes;
|
||||
// WORD biBitCount
|
||||
// DWORD biCompression;
|
||||
// DWORD biSizeImage;
|
||||
// LONG biXPelsPerMeter;
|
||||
// LONG biYPelsPerMeter;
|
||||
// DWORD biClrUsed;
|
||||
// DWORD biClrImportant;
|
||||
|
||||
// Size of the info header
|
||||
out.writeUInt32(infoHeaderSize);
|
||||
|
||||
// Width and height of the image
|
||||
out.writeUInt32(m_width);
|
||||
out.writeUInt32(m_height);
|
||||
|
||||
// Planes ("must be set to 1")
|
||||
out.writeUInt16(1);
|
||||
|
||||
// BitCount and CompressionType
|
||||
out.writeUInt16(24);
|
||||
out.writeUInt32(BI_RGB);
|
||||
|
||||
// Image size ("may be zero for BI_RGB bitmaps")
|
||||
out.writeUInt32(0);
|
||||
|
||||
// biXPelsPerMeter
|
||||
out.writeUInt32(0);
|
||||
// biYPelsPerMeter
|
||||
out.writeUInt32(0);
|
||||
|
||||
// biClrUsed
|
||||
out.writeUInt32(0);
|
||||
|
||||
// biClrImportant
|
||||
out.writeUInt32(0);
|
||||
|
||||
BMScanWidth = m_width * 3;
|
||||
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMPadding = 4 - (BMScanWidth & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
BMPadding = 0;
|
||||
}
|
||||
|
||||
int hStart = m_height - 1;
|
||||
int hEnd = -1;
|
||||
int hDir = -1;
|
||||
int dest;
|
||||
|
||||
// Write the pixel data
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
dest = m_channels * h * m_width;
|
||||
for (int w = 0; w < m_width; ++w)
|
||||
{
|
||||
|
||||
if (m_channels == 3)
|
||||
{
|
||||
red = m_byte[dest];
|
||||
green = m_byte[dest + 1];
|
||||
blue = m_byte[dest + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
red = m_byte[dest];
|
||||
green = m_byte[dest];
|
||||
blue = m_byte[dest];
|
||||
}
|
||||
|
||||
out.writeUInt8(blue);
|
||||
out.writeUInt8(green);
|
||||
out.writeUInt8(red);
|
||||
|
||||
dest += m_channels;
|
||||
}
|
||||
|
||||
if (BMPadding > 0)
|
||||
{
|
||||
out.skip(BMPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isDataSizeValid(int height, int paddedWidth, int headerSize, int palleteSize, int inputLength)
|
||||
{
|
||||
int64 dataSize = height * paddedWidth + headerSize + palleteSize;
|
||||
return inputLength >= dataSize;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
static const uint16 BMP_HEADERSIZE = 14;
|
||||
|
||||
(void)PICTURE_16BIT;
|
||||
(void)PICTURE_32BIT;
|
||||
|
||||
// This is a simple BMP loader that can handle uncompressed BMP files.
|
||||
|
||||
input.reset();
|
||||
|
||||
if (input.getLength() < BMP_HEADERSIZE)
|
||||
throw Error("BMB header corrupted - incomplete file");
|
||||
|
||||
// Verify this is a BMP file by looking for the BM tag.
|
||||
std::string tag = input.readString(2);
|
||||
if (tag != "BM")
|
||||
{
|
||||
throw Error("Not a supported BMP file");
|
||||
}
|
||||
|
||||
m_channels = 3;
|
||||
// Skip to the BITMAPINFOHEADER's width and height
|
||||
input.skip(12);
|
||||
|
||||
int32 BMPInfoHeaderSize = input.readUInt32();
|
||||
int32 wholeHeaderSize = BMP_HEADERSIZE + BMPInfoHeaderSize;
|
||||
|
||||
if (input.getLength() < wholeHeaderSize)
|
||||
throw Error("BMP data header corrupted - incomplete file");
|
||||
|
||||
m_width = input.readUInt32();
|
||||
m_height = input.readUInt32();
|
||||
|
||||
// Skip to the bit count and compression type
|
||||
input.skip(2);
|
||||
|
||||
uint16 bitCount = input.readUInt16();
|
||||
uint32 compressionType = input.readUInt32();
|
||||
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
uint8 blank;
|
||||
|
||||
// Only uncompressed bitmaps are supported by this code
|
||||
if ((int32)compressionType != BI_RGB)
|
||||
{
|
||||
throw Error("BMP images must be uncompressed");
|
||||
}
|
||||
|
||||
uint8* palette = NULL;
|
||||
|
||||
uint32 paletteDataSize = 0;
|
||||
// Create the palette if needed
|
||||
if (bitCount <= 8)
|
||||
{
|
||||
|
||||
// Skip to the palette color count in the header
|
||||
input.skip(12);
|
||||
|
||||
int numColors = input.readUInt32();
|
||||
|
||||
// if numColor == 0 its full size pallete
|
||||
if (numColors == 0)
|
||||
switch (bitCount)
|
||||
{
|
||||
case 1:
|
||||
numColors = 2;
|
||||
break;
|
||||
case 4:
|
||||
numColors = 16;
|
||||
break;
|
||||
case 8:
|
||||
numColors = 256;
|
||||
break;
|
||||
|
||||
default:
|
||||
numColors = 0;
|
||||
break; // well, we know nothing about this crap. Its gonna fail lower in a code
|
||||
}
|
||||
|
||||
paletteDataSize = numColors * 4;
|
||||
palette = (uint8*)System::malloc(numColors * 3);
|
||||
debugAssert(palette);
|
||||
|
||||
if (input.getLength() < wholeHeaderSize + paletteDataSize)
|
||||
throw Error("BMB palette corruption - incomplete file");
|
||||
|
||||
// Skip past the end of the header to the palette info
|
||||
input.skip(4);
|
||||
|
||||
int c;
|
||||
for (c = 0; c < numColors * 3; c += 3)
|
||||
{
|
||||
// Palette information in bitmaps is stored in BGR_ format.
|
||||
// That means it's blue-green-red-blank, for each entry.
|
||||
blue = input.readUInt8();
|
||||
green = input.readUInt8();
|
||||
red = input.readUInt8();
|
||||
blank = input.readUInt8();
|
||||
|
||||
palette[c] = red;
|
||||
palette[c + 1] = green;
|
||||
palette[c + 2] = blue;
|
||||
}
|
||||
}
|
||||
|
||||
int hStart = 0;
|
||||
int hEnd = 0;
|
||||
int hDir = 0;
|
||||
|
||||
if (m_height < 0)
|
||||
{
|
||||
m_height = -m_height;
|
||||
hStart = 0;
|
||||
hEnd = m_height;
|
||||
hDir = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// height = height;
|
||||
hStart = m_height - 1;
|
||||
hEnd = -1;
|
||||
hDir = -1;
|
||||
}
|
||||
|
||||
uint32 hSize = std::abs(hStart - hEnd);
|
||||
|
||||
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
|
||||
debugAssert(m_byte);
|
||||
|
||||
int BMScanWidth;
|
||||
int BMPadding;
|
||||
uint8 BMGroup;
|
||||
uint8 BMPixel8;
|
||||
int currPixel;
|
||||
int dest;
|
||||
int flags = PICTURE_NONE;
|
||||
|
||||
if (bitCount == 1)
|
||||
{
|
||||
// Note that this file is not necessarily grayscale, since it's possible
|
||||
// the palette is blue-and-white, or whatever. But of course most image
|
||||
// programs only write 1-bit images if they're black-and-white.
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = (m_width + 7) >> 3;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
// Powers of 2
|
||||
int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 3 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
|
||||
// Now we read the pixels. Usually there are eight pixels per byte,
|
||||
// since each pixel is represented by one bit, but if the width
|
||||
// is not a multiple of eight, the last byte will have some bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "width" number of pixels.
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int src = 3 * ((BMGroup & pow2[i]) >> i);
|
||||
|
||||
m_byte[dest] = palette[src];
|
||||
m_byte[dest + 1] = palette[src + 1];
|
||||
m_byte[dest + 2] = palette[src + 2];
|
||||
|
||||
++currPixel;
|
||||
dest += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 4)
|
||||
{
|
||||
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
int BMScanWidth = (m_width + 1) >> 1;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 3 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; w++)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
int src[2];
|
||||
src[0] = 3 * ((BMGroup & 0xF0) >> 4);
|
||||
src[1] = 3 * (BMGroup & 0x0F);
|
||||
|
||||
// Now we read the pixels. Usually there are two pixels per byte,
|
||||
// since each pixel is represented by four bits, but if the width
|
||||
// is not a multiple of two, the last byte will have only four bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "Width" number of pixels.
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int tsrc = src[i];
|
||||
|
||||
m_byte[dest] = palette[tsrc];
|
||||
m_byte[dest + 1] = palette[tsrc + 1];
|
||||
m_byte[dest + 2] = palette[tsrc + 2];
|
||||
|
||||
++currPixel;
|
||||
dest += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 8)
|
||||
{
|
||||
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = m_width;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMPixel8 = input.readUInt8();
|
||||
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
dest = 3 * ((h * m_width) + currPixel);
|
||||
int src = 3 * BMPixel8;
|
||||
|
||||
m_byte[dest] = palette[src];
|
||||
m_byte[dest + 1] = palette[src + 1];
|
||||
m_byte[dest + 2] = palette[src + 2];
|
||||
|
||||
++currPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 16)
|
||||
{
|
||||
|
||||
m_memMan->free(m_byte);
|
||||
m_byte = NULL;
|
||||
System::free(palette);
|
||||
palette = NULL;
|
||||
throw Error("16-bit bitmaps not supported");
|
||||
}
|
||||
else if (bitCount == 24)
|
||||
{
|
||||
input.skip(20);
|
||||
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = m_width * 3;
|
||||
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMPadding = 4 - (BMScanWidth & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
BMPadding = 0;
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth + BMPadding, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
dest = 3 * h * m_width;
|
||||
for (int w = 0; w < m_width; ++w)
|
||||
{
|
||||
|
||||
blue = input.readUInt8();
|
||||
green = input.readUInt8();
|
||||
red = input.readUInt8();
|
||||
|
||||
m_byte[dest] = red;
|
||||
m_byte[dest + 1] = green;
|
||||
m_byte[dest + 2] = blue;
|
||||
|
||||
dest += 3;
|
||||
}
|
||||
|
||||
if (BMPadding)
|
||||
{
|
||||
input.skip(BMPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 32)
|
||||
{
|
||||
|
||||
m_memMan->free(m_byte);
|
||||
m_byte = NULL;
|
||||
System::free(palette);
|
||||
palette = NULL;
|
||||
throw Error("32 bit bitmaps not supported");
|
||||
}
|
||||
else
|
||||
{
|
||||
// We support all possible bit depths, so if the
|
||||
// code gets here, it's not even a real bitmap.
|
||||
m_memMan->free(m_byte);
|
||||
m_byte = NULL;
|
||||
throw Error("Not a bitmap!");
|
||||
}
|
||||
|
||||
System::free(palette);
|
||||
palette = NULL;
|
||||
}
|
||||
|
||||
|
||||
void GImage::decodeICO(BinaryInput& input)
|
||||
{
|
||||
|
||||
// Header
|
||||
uint16 r = input.readUInt16();
|
||||
debugAssert(r == 0);
|
||||
r = input.readUInt16();
|
||||
debugAssert(r == 1);
|
||||
|
||||
// Read the number of icons, although we'll only load the
|
||||
// first one.
|
||||
int count = input.readUInt16();
|
||||
|
||||
m_channels = 4;
|
||||
|
||||
debugAssert(count > 0);
|
||||
|
||||
const uint8* headerBuffer = input.getCArray() + input.getPosition();
|
||||
int maxWidth = 0, maxHeight = 0;
|
||||
int maxHeaderNum = 0;
|
||||
for (int currentHeader = 0; currentHeader < count; ++currentHeader)
|
||||
{
|
||||
|
||||
const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16);
|
||||
int tmpWidth = curHeaderBuffer[0];
|
||||
int tmpHeight = curHeaderBuffer[1];
|
||||
// Just in case there is a non-square icon, checking area
|
||||
if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight))
|
||||
{
|
||||
maxWidth = tmpWidth;
|
||||
maxHeight = tmpHeight;
|
||||
maxHeaderNum = currentHeader;
|
||||
}
|
||||
}
|
||||
|
||||
input.skip(maxHeaderNum * 16);
|
||||
|
||||
m_width = input.readUInt8();
|
||||
m_height = input.readUInt8();
|
||||
int numColors = input.readUInt8();
|
||||
|
||||
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
|
||||
debugAssert(m_byte);
|
||||
|
||||
// Bit mask for packed bits
|
||||
int mask = 0;
|
||||
|
||||
int bitsPerPixel = 8;
|
||||
|
||||
switch (numColors)
|
||||
{
|
||||
case 2:
|
||||
mask = 0x01;
|
||||
bitsPerPixel = 1;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
mask = 0x0F;
|
||||
bitsPerPixel = 4;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
numColors = 256;
|
||||
mask = 0xFF;
|
||||
bitsPerPixel = 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unsupported ICO color count.");
|
||||
}
|
||||
|
||||
input.skip(5);
|
||||
// Skip 'size' unused
|
||||
input.skip(4);
|
||||
|
||||
int offset = input.readUInt32();
|
||||
|
||||
// Skip over any other icon descriptions
|
||||
input.setPosition(offset);
|
||||
|
||||
// Skip over bitmap header; it is redundant
|
||||
input.skip(40);
|
||||
|
||||
Array<Color4uint8> palette;
|
||||
palette.resize(numColors, true);
|
||||
for (int c = 0; c < numColors; ++c)
|
||||
{
|
||||
palette[c].b = input.readUInt8();
|
||||
palette[c].g = input.readUInt8();
|
||||
palette[c].r = input.readUInt8();
|
||||
palette[c].a = input.readUInt8();
|
||||
}
|
||||
|
||||
// The actual image and mask follow
|
||||
|
||||
// The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap
|
||||
// using the same encoding as BMP files. The AND Bitmap is stored in as
|
||||
// 1-bit uncompressed Bitmap.
|
||||
//
|
||||
// Pixels are stored bottom-up, left-to-right. Pixel lines are padded
|
||||
// with zeros to end on a 32bit (4byte) boundary. Every line will have the
|
||||
// same number of bytes. Color indices are zero based, meaning a pixel color
|
||||
// of 0 represents the first color table entry, a pixel color of 255 (if there
|
||||
// are that many) represents the 256th entry.
|
||||
/*
|
||||
int bitsPerRow = width * bitsPerPixel;
|
||||
int bytesPerRow = iCeil((double)bitsPerRow / 8);
|
||||
// Rows are padded to 32-bit boundaries
|
||||
bytesPerRow += bytesPerRow % 4;
|
||||
|
||||
// Read the XOR values into the color channel
|
||||
for (int y = height - 1; y >= 0; --y) {
|
||||
int x = 0;
|
||||
// Read the row
|
||||
for (int i = 0; i < bytesPerRow; ++i) {
|
||||
uint8 byte = input.readUInt8();
|
||||
for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) {
|
||||
int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask;
|
||||
pixel4(x, y) = colorTable[bit];
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
int hStart = 0;
|
||||
int hEnd = 0;
|
||||
int hDir = 0;
|
||||
|
||||
if (m_height < 0)
|
||||
{
|
||||
m_height = -m_height;
|
||||
hStart = 0;
|
||||
hEnd = m_height;
|
||||
hDir = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// height = height;
|
||||
hStart = m_height - 1;
|
||||
hEnd = -1;
|
||||
hDir = -1;
|
||||
}
|
||||
|
||||
int BMScanWidth;
|
||||
uint8 BMGroup;
|
||||
uint8 BMPixel8;
|
||||
int currPixel;
|
||||
int dest;
|
||||
|
||||
if (bitsPerPixel == 1)
|
||||
{
|
||||
// Note that this file is not necessarily grayscale, since it's possible
|
||||
// the palette is blue-and-white, or whatever. But of course most image
|
||||
// programs only write 1-bit images if they're black-and-white.
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = (m_width + 7) >> 3;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
// Powers of 2
|
||||
int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 3 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
|
||||
// Now we read the pixels. Usually there are eight pixels per byte,
|
||||
// since each pixel is represented by one bit, but if the width
|
||||
// is not a multiple of eight, the last byte will have some bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "width" number of pixels.
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int src = ((BMGroup & pow2[i]) >> i);
|
||||
|
||||
m_byte[dest] = palette[src].r;
|
||||
m_byte[dest + 1] = palette[src].g;
|
||||
m_byte[dest + 2] = palette[src].b;
|
||||
|
||||
++currPixel;
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitsPerPixel == 4)
|
||||
{
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
int BMScanWidth = (m_width + 1) >> 1;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 4 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; w++)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
int src[2];
|
||||
src[0] = ((BMGroup & 0xF0) >> 4);
|
||||
src[1] = (BMGroup & 0x0F);
|
||||
|
||||
// Now we read the pixels. Usually there are two pixels per byte,
|
||||
// since each pixel is represented by four bits, but if the width
|
||||
// is not a multiple of two, the last byte will have only four bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "Width" number of pixels.
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int tsrc = src[i];
|
||||
|
||||
m_byte[dest] = palette[tsrc].r;
|
||||
m_byte[dest + 1] = palette[tsrc].g;
|
||||
m_byte[dest + 2] = palette[tsrc].b;
|
||||
|
||||
++currPixel;
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitsPerPixel == 8)
|
||||
{
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = m_width;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMPixel8 = input.readUInt8();
|
||||
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
dest = 4 * ((h * m_width) + currPixel);
|
||||
int src = BMPixel8;
|
||||
|
||||
m_byte[dest] = palette[src].r;
|
||||
m_byte[dest + 1] = palette[src].g;
|
||||
m_byte[dest + 2] = palette[src].b;
|
||||
|
||||
++currPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the mask into the alpha channel
|
||||
int bitsPerRow = m_width;
|
||||
int bytesPerRow = iCeil((double)bitsPerRow / 8);
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
// BMScanWidth = (width + 1) >> 1;
|
||||
if (bytesPerRow & 3)
|
||||
{
|
||||
bytesPerRow += 4 - (bytesPerRow & 3);
|
||||
}
|
||||
|
||||
for (int y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
int x = 0;
|
||||
// Read the row
|
||||
for (int i = 0; i < bytesPerRow; ++i)
|
||||
{
|
||||
uint8 byte = input.readUInt8();
|
||||
for (int j = 0; (j < 8) && (x < m_width); ++x, ++j)
|
||||
{
|
||||
int bit = (byte >> (7 - j)) & 0x01;
|
||||
pixel4(x, y).a = (1 - bit) * 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
474
engine/3d/src/GImage_jpeg.cpp
Normal file
474
engine/3d/src/GImage_jpeg.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
@file GImage_jpeg.cpp
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-05-27
|
||||
@edited 2009-04-20
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "GImage.hpp"
|
||||
#include "BinaryOutput.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#ifdef G3D_LINUX
|
||||
#include <jconfig.h>
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#else
|
||||
#include "jconfig.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jerror.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
|
||||
const int jpegQuality = 96;
|
||||
|
||||
/**
|
||||
The IJG library needs special setup for compress/decompressing
|
||||
from memory. These classes provide them.
|
||||
|
||||
The format of this class is defined by the IJG library; do not
|
||||
change it.
|
||||
*/
|
||||
class memory_destination_mgr
|
||||
{
|
||||
public:
|
||||
struct jpeg_destination_mgr pub;
|
||||
JOCTET* buffer;
|
||||
int size;
|
||||
int count;
|
||||
};
|
||||
|
||||
typedef memory_destination_mgr* mem_dest_ptr;
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void init_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = dest->size;
|
||||
dest->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static boolean empty_output_buffer(j_compress_ptr cinfo)
|
||||
{
|
||||
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = dest->size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void term_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
|
||||
dest->count = dest->size - dest->pub.free_in_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void jpeg_memory_dest(j_compress_ptr cinfo, JOCTET* buffer, int size)
|
||||
{
|
||||
|
||||
mem_dest_ptr dest;
|
||||
|
||||
if (cinfo->dest == NULL)
|
||||
{
|
||||
// First time for this JPEG object; call the
|
||||
// IJG allocator to get space.
|
||||
cinfo->dest = (struct jpeg_destination_mgr*)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(memory_destination_mgr));
|
||||
}
|
||||
|
||||
dest = (mem_dest_ptr)cinfo->dest;
|
||||
dest->size = size;
|
||||
dest->buffer = buffer;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define INPUT_BUF_SIZE 4096
|
||||
|
||||
/**
|
||||
Structure dictated by IJG.
|
||||
*/
|
||||
class memory_source_mgr
|
||||
{
|
||||
public:
|
||||
struct jpeg_source_mgr pub;
|
||||
int source_size;
|
||||
unsigned char* source_data;
|
||||
boolean start_of_data;
|
||||
JOCTET* buffer;
|
||||
};
|
||||
|
||||
|
||||
typedef memory_source_mgr* mem_src_ptr;
|
||||
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void init_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
|
||||
mem_src_ptr src = (mem_src_ptr)cinfo->src;
|
||||
|
||||
src->start_of_data = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static boolean fill_input_buffer(j_decompress_ptr cinfo)
|
||||
{
|
||||
|
||||
mem_src_ptr src = (mem_src_ptr)cinfo->src;
|
||||
|
||||
size_t bytes_read = 0;
|
||||
|
||||
if (src->source_size > INPUT_BUF_SIZE)
|
||||
bytes_read = INPUT_BUF_SIZE;
|
||||
else
|
||||
bytes_read = src->source_size;
|
||||
|
||||
if (bytes_read <= 0)
|
||||
{
|
||||
if (src->start_of_data) // Treat empty input file as fatal error
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
// Insert a fake EOI marker
|
||||
src->buffer[0] = (JOCTET)0xFF;
|
||||
src->buffer[1] = (JOCTET)JPEG_EOI;
|
||||
bytes_read = 2;
|
||||
}
|
||||
else
|
||||
memcpy(src->buffer, src->source_data, bytes_read);
|
||||
|
||||
src->source_data += bytes_read;
|
||||
if (src->source_size >= bytes_read)
|
||||
src->source_size -= bytes_read;
|
||||
else
|
||||
src->source_size = 0;
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = bytes_read;
|
||||
src->start_of_data = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
|
||||
mem_src_ptr src = (mem_src_ptr)cinfo->src;
|
||||
|
||||
if (num_bytes > 0)
|
||||
{
|
||||
while (num_bytes > (long)src->pub.bytes_in_buffer)
|
||||
{
|
||||
num_bytes -= (long)src->pub.bytes_in_buffer;
|
||||
boolean s = fill_input_buffer(cinfo);
|
||||
debugAssert(s);
|
||||
(void)s;
|
||||
}
|
||||
|
||||
src->pub.next_input_byte += (size_t)num_bytes;
|
||||
src->pub.bytes_in_buffer -= (size_t)num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void term_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
(void)cinfo;
|
||||
// Intentionally empty
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Signature dictated by IJG.
|
||||
*/
|
||||
static void jpeg_memory_src(j_decompress_ptr cinfo, JOCTET* buffer, int size)
|
||||
{
|
||||
|
||||
mem_src_ptr src;
|
||||
|
||||
if (cinfo->src == NULL)
|
||||
{
|
||||
// First time for this JPEG object
|
||||
cinfo->src = (struct jpeg_source_mgr*)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(memory_source_mgr));
|
||||
|
||||
src = (mem_src_ptr)cinfo->src;
|
||||
|
||||
src->buffer = (JOCTET*)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET));
|
||||
}
|
||||
|
||||
src = (mem_src_ptr)cinfo->src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
|
||||
// use default method
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart;
|
||||
src->pub.term_source = term_source;
|
||||
src->source_data = buffer;
|
||||
src->source_size = size;
|
||||
|
||||
// forces fill_input_buffer on first read
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
|
||||
// until buffer loaded
|
||||
src->pub.next_input_byte = NULL;
|
||||
}
|
||||
|
||||
|
||||
void GImage::encodeJPEG(BinaryOutput& out) const
|
||||
{
|
||||
|
||||
if (m_channels != 3)
|
||||
{
|
||||
// Convert to three channel
|
||||
GImage tmp = *this;
|
||||
tmp.convertToRGB();
|
||||
tmp.encodeJPEG(out);
|
||||
return;
|
||||
}
|
||||
|
||||
debugAssert(m_channels == 3);
|
||||
out.setEndian(G3D_LITTLE_ENDIAN);
|
||||
|
||||
// Allocate and initialize a compression object
|
||||
jpeg_compress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
// Specify the destination for the compressed data.
|
||||
// (Overestimate the size)
|
||||
int buffer_size = m_width * m_height * 3 + 200;
|
||||
JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
|
||||
jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
|
||||
|
||||
|
||||
cinfo.image_width = m_width;
|
||||
cinfo.image_height = m_height;
|
||||
|
||||
// # of color components per pixel
|
||||
cinfo.input_components = 3;
|
||||
|
||||
// colorspace of input image
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
cinfo.input_gamma = 1.0;
|
||||
|
||||
// Set parameters for compression, including image size & colorspace
|
||||
jpeg_set_defaults(&cinfo);
|
||||
#if !defined(__linux) && !defined(__APPLE__)
|
||||
jpeg_set_quality(&cinfo, jpegQuality, false);
|
||||
#else
|
||||
jpeg_set_quality(&cinfo, jpegQuality, FALSE);
|
||||
#endif
|
||||
cinfo.smoothing_factor = 0;
|
||||
cinfo.optimize_coding = TRUE;
|
||||
// cinfo.dct_method = JDCT_FLOAT;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.jpeg_color_space = JCS_YCbCr;
|
||||
|
||||
// Initialize the compressor
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
|
||||
// Iterate over all scanlines from top to bottom
|
||||
// pointer to a single row
|
||||
JSAMPROW row_pointer[1];
|
||||
|
||||
// JSAMPLEs per row in image_buffer
|
||||
int row_stride = cinfo.image_width * 3;
|
||||
while (cinfo.next_scanline < cinfo.image_height)
|
||||
{
|
||||
row_pointer[0] = &(m_byte[cinfo.next_scanline * row_stride]);
|
||||
jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
||||
}
|
||||
|
||||
// Shut down the compressor
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
// Figure out how big the result was.
|
||||
int outLength = ((mem_dest_ptr)cinfo.dest)->count;
|
||||
|
||||
// Release the JPEG compression object
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
// Copy into an appropriately sized output buffer.
|
||||
out.writeBytes(compressed_data, outLength);
|
||||
|
||||
// Free the conservative buffer.
|
||||
System::free(compressed_data);
|
||||
compressed_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
void GImage::decodeJPEG(BinaryInput& input)
|
||||
{
|
||||
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
int loc = 0;
|
||||
|
||||
m_channels = 3;
|
||||
// We have to set up the error handler, in case initialization fails.
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
// Initialize the JPEG decompression object.
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
// Specify data source (eg, a file, for us, memory)
|
||||
jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size());
|
||||
|
||||
// Read the parameters with jpeg_read_header()
|
||||
int ret = jpeg_read_header(&cinfo, FALSE);
|
||||
if (ret != JPEG_HEADER_OK)
|
||||
throw Error("Problem reading file header.");
|
||||
|
||||
// Set parameters for decompression
|
||||
// (We do nothing here since the defaults are fine)
|
||||
|
||||
// Start decompressor
|
||||
if (!jpeg_start_decompress(&cinfo))
|
||||
throw Error("Problem decompressing file.");
|
||||
|
||||
// Get and set the values of interest to this object
|
||||
m_width = cinfo.output_width;
|
||||
m_height = cinfo.output_height;
|
||||
|
||||
// Prepare the pointer object for the pixel data
|
||||
size_t bytesToAlloc = m_width * m_height * 3;
|
||||
m_byte = (uint8*)m_memMan->alloc(bytesToAlloc);
|
||||
|
||||
if (!m_byte)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Out of memory while allocating " << bytesToAlloc << " bytes";
|
||||
throw GImage::Error(ss.str());
|
||||
}
|
||||
|
||||
// JSAMPLEs per row in output buffer
|
||||
int bpp = cinfo.output_components;
|
||||
int row_stride = cinfo.output_width * bpp;
|
||||
|
||||
// Make a one-row-high sample array that will go away when done with image
|
||||
JSAMPARRAY temp = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
|
||||
|
||||
// Read data on a scanline by scanline basis
|
||||
while (cinfo.output_scanline < cinfo.output_height)
|
||||
{
|
||||
|
||||
// We may need to adjust the output based on the
|
||||
// number of channels it has.
|
||||
switch (bpp)
|
||||
{
|
||||
case 1:
|
||||
// Grayscale; decompress to temp.
|
||||
jpeg_read_scanlines(&cinfo, temp, 1);
|
||||
|
||||
// Expand to three channels
|
||||
{
|
||||
uint8* scan = &(m_byte[loc * 3]);
|
||||
uint8* endScan = scan + (m_width * 3);
|
||||
uint8* t = *temp;
|
||||
|
||||
while (scan < endScan)
|
||||
{
|
||||
uint8 value = t[0];
|
||||
|
||||
// Spread the value 3x.
|
||||
scan[0] = value;
|
||||
scan[1] = value;
|
||||
scan[2] = value;
|
||||
|
||||
scan += 3;
|
||||
t += 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Read directly into the array
|
||||
{
|
||||
// Need one extra level of indirection.
|
||||
uint8* scan = m_byte + loc;
|
||||
JSAMPARRAY ptr = &scan;
|
||||
jpeg_read_scanlines(&cinfo, ptr, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// RGBA; decompress to temp.
|
||||
jpeg_read_scanlines(&cinfo, temp, 1);
|
||||
|
||||
// Drop the 3rd channel
|
||||
{
|
||||
uint8* scan = &(m_byte[loc * 3]);
|
||||
uint8* endScan = scan + m_width * 3;
|
||||
uint8* t = *temp;
|
||||
|
||||
while (scan < endScan)
|
||||
{
|
||||
scan[0] = t[0];
|
||||
scan[1] = t[1];
|
||||
scan[2] = t[2];
|
||||
|
||||
scan += 3;
|
||||
t += 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unexpected number of channels.");
|
||||
}
|
||||
|
||||
loc += row_stride;
|
||||
}
|
||||
|
||||
// Finish decompression
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
alwaysAssertM(this, "Corrupt GImage");
|
||||
// Release JPEG decompression object
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
190
engine/3d/src/GImage_jxl.cpp
Normal file
190
engine/3d/src/GImage_jxl.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
@file GImage_jxl.cpp
|
||||
@author PATRICK STAR
|
||||
@created 2025
|
||||
*/
|
||||
|
||||
#include "GImage.hpp"
|
||||
#include "BinaryOutput.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <jxl/encode.h>
|
||||
#include <jxl/decode.h>
|
||||
#include <jxl/thread_parallel_runner.h>
|
||||
#include <jxl/memory_manager.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
void GImage::encodeJXL(BinaryOutput& out) const
|
||||
{
|
||||
if (!(m_channels == 1 || m_channels == 3 || m_channels == 4))
|
||||
{
|
||||
throw GImage::Error("Unsupported number of channels for JXL.");
|
||||
}
|
||||
if (m_width <= 0 || m_height <= 0)
|
||||
{
|
||||
throw GImage::Error("Invalid dimensions for JXL.");
|
||||
}
|
||||
|
||||
JxlEncoder* encoder = JxlEncoderCreate(nullptr);
|
||||
if (!encoder)
|
||||
{
|
||||
throw GImage::Error("Failed to create JXL encoder.");
|
||||
}
|
||||
|
||||
void* runner = JxlThreadParallelRunnerCreate(nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
|
||||
if (!runner)
|
||||
{
|
||||
JxlEncoderDestroy(encoder);
|
||||
throw GImage::Error("Failed to create JXL thread runner.");
|
||||
}
|
||||
JxlEncoderSetParallelRunner(encoder, JxlThreadParallelRunner, runner);
|
||||
|
||||
// Create frame settings
|
||||
JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(encoder, nullptr);
|
||||
if (!frame_settings)
|
||||
{
|
||||
JxlEncoderDestroy(encoder);
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
throw GImage::Error("Failed to create JXL frame settings.");
|
||||
}
|
||||
|
||||
JxlPixelFormat pixel_format = {static_cast<uint32_t>(m_channels), JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
|
||||
|
||||
if (JxlEncoderAddImageFrame(frame_settings, &pixel_format, m_byte, m_width * m_height * m_channels) != JXL_ENC_SUCCESS)
|
||||
{
|
||||
JxlEncoderDestroy(encoder);
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
throw GImage::Error("Failed to add image frame to JXL encoder.");
|
||||
}
|
||||
|
||||
JxlEncoderCloseInput(encoder);
|
||||
|
||||
std::vector<uint8_t> output(4096);
|
||||
uint8_t* next_out = output.data();
|
||||
size_t avail_out = output.size();
|
||||
JxlEncoderStatus status;
|
||||
|
||||
while ((status = JxlEncoderProcessOutput(encoder, &next_out, &avail_out)) == JXL_ENC_NEED_MORE_OUTPUT)
|
||||
{
|
||||
size_t offset = output.size() - avail_out;
|
||||
output.resize(output.size() * 2);
|
||||
next_out = output.data() + offset;
|
||||
avail_out = output.size() - offset;
|
||||
}
|
||||
|
||||
if (status != JXL_ENC_SUCCESS)
|
||||
{
|
||||
JxlEncoderDestroy(encoder);
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
throw GImage::Error("Error during JXL encoding.");
|
||||
}
|
||||
|
||||
out.writeBytes(output.data(), output.size() - avail_out);
|
||||
|
||||
JxlEncoderDestroy(encoder);
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
|
||||
void GImage::decodeJXL(BinaryInput& input)
|
||||
{
|
||||
std::vector<uint8_t> input_data(input.getLength());
|
||||
input.readBytes(input_data.data(), input_data.size());
|
||||
|
||||
JxlDecoder* decoder = JxlDecoderCreate(nullptr);
|
||||
if (!decoder)
|
||||
{
|
||||
throw GImage::Error("Failed to create JXL decoder.");
|
||||
}
|
||||
|
||||
// Subscribe to the events we need
|
||||
if (JxlDecoderSubscribeEvents(decoder, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Failed to subscribe to JXL decoder events.");
|
||||
}
|
||||
|
||||
if (JxlDecoderSetInput(decoder, input_data.data(), input_data.size()) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Failed to set JXL input data.");
|
||||
}
|
||||
JxlDecoderCloseInput(decoder);
|
||||
|
||||
JxlBasicInfo info;
|
||||
JxlPixelFormat pixel_format;
|
||||
bool info_retrieved = false;
|
||||
bool buffer_set = false;
|
||||
|
||||
// Process decoder events
|
||||
JxlDecoderStatus status;
|
||||
while ((status = JxlDecoderProcessInput(decoder)) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
if (status == JXL_DEC_BASIC_INFO)
|
||||
{
|
||||
if (JxlDecoderGetBasicInfo(decoder, &info) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Failed to get JXL basic info.");
|
||||
}
|
||||
|
||||
m_width = info.xsize;
|
||||
m_height = info.ysize;
|
||||
m_channels = (info.alpha_bits > 0) ? 4 : 3;
|
||||
info_retrieved = true;
|
||||
}
|
||||
else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER)
|
||||
{
|
||||
if (!info_retrieved)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Need output buffer before basic info retrieved.");
|
||||
}
|
||||
|
||||
pixel_format = {static_cast<uint32_t>(m_channels), JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
|
||||
m_byte = (uint8_t*)m_memMan->alloc(m_width * m_height * m_channels);
|
||||
|
||||
if (!m_byte)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Failed to allocate memory for decoded JXL image.");
|
||||
}
|
||||
|
||||
if (JxlDecoderSetImageOutBuffer(decoder, &pixel_format, m_byte, m_width * m_height * m_channels) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Failed to set JXL output buffer.");
|
||||
}
|
||||
buffer_set = true;
|
||||
}
|
||||
else if (status == JXL_DEC_FULL_IMAGE)
|
||||
{
|
||||
// Image successfully decoded
|
||||
break;
|
||||
}
|
||||
else if (status == JXL_DEC_ERROR)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Error during JXL decoding.");
|
||||
}
|
||||
else
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("Unexpected JXL decoder status.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!info_retrieved || !buffer_set)
|
||||
{
|
||||
JxlDecoderDestroy(decoder);
|
||||
throw GImage::Error("JXL decoding completed without retrieving all necessary data.");
|
||||
}
|
||||
|
||||
JxlDecoderDestroy(decoder);
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
333
engine/3d/src/GImage_png.cpp
Normal file
333
engine/3d/src/GImage_png.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/**
|
||||
@file GImage_png.cpp
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-05-27
|
||||
@edited 2009-04-20
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "GImage.hpp"
|
||||
#include <png.h>
|
||||
#include <sstream>
|
||||
#include "BinaryOutput.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
// libpng required function signature
|
||||
static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||
{
|
||||
|
||||
// d9mz - would anybody even debug this? debugAssert(png_ptr->io_ptr != NULL);
|
||||
// d9mz - would anybody even debug this? debugAssert(data != NULL);
|
||||
|
||||
((BinaryOutput*)png_get_io_ptr(png_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;
|
||||
// d9mz - would anybody even debug this? debugAssert(error_msg != NULL);
|
||||
|
||||
// png_ptr->error_ptr = const_cast<png_charp>(error_msg);
|
||||
png_longjmp(png_ptr, 1);
|
||||
}
|
||||
|
||||
|
||||
// libpng required function signature
|
||||
void png_warning(png_structp png_ptr, png_const_charp warning_msg) {}
|
||||
|
||||
// libpng required function signature
|
||||
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||
{
|
||||
|
||||
|
||||
// d9mz - would anybody even debug this? debugAssert(png_ptr->io_ptr != NULL);
|
||||
// d9mz - would anybody even debug this? debugAssert(length >= 0);
|
||||
// d9mz - would anybody even debug this? debugAssert(data != NULL);
|
||||
|
||||
int64 pos = ((BinaryInput*)png_get_io_ptr(png_ptr))->getPosition();
|
||||
int64 binaryOutputLen = ((BinaryInput*)png_get_io_ptr(png_ptr))->getLength();
|
||||
|
||||
if (pos + length > binaryOutputLen)
|
||||
{
|
||||
length = binaryOutputLen - pos;
|
||||
G3D::png_error(png_ptr, "Trying to load incomplete image");
|
||||
}
|
||||
|
||||
((BinaryInput*)png_get_io_ptr(png_ptr))->readBytes(data, length);
|
||||
}
|
||||
|
||||
|
||||
void GImage::encodePNG(BinaryOutput& out) const
|
||||
{
|
||||
|
||||
if (!(m_channels == 1 || m_channels == 2 || m_channels == 3 || m_channels == 4))
|
||||
{
|
||||
throw GImage::Error(format("Illegal channels for PNG: %d", m_channels));
|
||||
}
|
||||
if (m_width <= 0)
|
||||
{
|
||||
throw GImage::Error(format("Illegal width for PNG: %d", m_width));
|
||||
}
|
||||
if (m_height <= 0)
|
||||
{
|
||||
throw GImage::Error(format("Illegal height for PNG: %d", m_height));
|
||||
}
|
||||
|
||||
// PNG library requires that the height * pointer size fit within an int
|
||||
if (png_uint_32(m_height) * sizeof(png_bytep) > PNG_UINT_32_MAX)
|
||||
{
|
||||
throw GImage::Error("Unsupported PNG height.");
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
const char* message = png_get_error_ptr(png_ptr) ? static_cast<const char*>(png_get_error_ptr(png_ptr)) : "Unknown PNG error";
|
||||
|
||||
throw GImage::Error(message);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
// setup libpng write handler so can use BinaryOutput
|
||||
png_set_write_fn(png_ptr, (void*)&out, png_write_data, png_flush_data);
|
||||
png_color_8_struct sig_bit;
|
||||
|
||||
switch (m_channels)
|
||||
{
|
||||
case 1:
|
||||
png_set_IHDR(
|
||||
png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
sig_bit.red = 0;
|
||||
sig_bit.green = 0;
|
||||
sig_bit.blue = 0;
|
||||
sig_bit.alpha = 0;
|
||||
sig_bit.gray = 8;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
png_set_IHDR(
|
||||
png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
sig_bit.red = 0;
|
||||
sig_bit.green = 0;
|
||||
sig_bit.blue = 0;
|
||||
sig_bit.alpha = 8;
|
||||
sig_bit.gray = 8;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
png_set_IHDR(
|
||||
png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
|
||||
sig_bit.red = 8;
|
||||
sig_bit.green = 8;
|
||||
sig_bit.blue = 8;
|
||||
sig_bit.alpha = 0;
|
||||
sig_bit.gray = 0;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
png_set_IHDR(
|
||||
png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
sig_bit.red = 8;
|
||||
sig_bit.green = 8;
|
||||
sig_bit.blue = 8;
|
||||
sig_bit.alpha = 8;
|
||||
sig_bit.gray = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
throw GImage::Error("Unsupported number of channels for PNG.");
|
||||
}
|
||||
|
||||
|
||||
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
|
||||
|
||||
// write the png header
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
png_bytepp row_pointers = new png_bytep[m_height];
|
||||
|
||||
for (int i = 0; i < m_height; ++i)
|
||||
{
|
||||
row_pointers[i] = (png_bytep)&m_byte[m_width * m_channels * i];
|
||||
}
|
||||
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
|
||||
delete[] row_pointers;
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
}
|
||||
|
||||
|
||||
void GImage::decodePNG(BinaryInput& input)
|
||||
{
|
||||
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
|
||||
if (png_ptr == NULL)
|
||||
{
|
||||
throw GImage::Error("Unable to initialize PNG decoder.");
|
||||
}
|
||||
|
||||
#if !defined(__linux) && !defined(__APPLE__)
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
const char* message = png_get_error_ptr(png_ptr) ? static_cast<const char*>(png_get_error_ptr(png_ptr)) : "Unknown PNG error";
|
||||
|
||||
throw GImage::Error(ERROR);
|
||||
}
|
||||
#else
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
const char* message = png_get_error_ptr(png_ptr) ? static_cast<const char*>(png_get_error_ptr(png_ptr)) : "Unknown PNG error";
|
||||
|
||||
// Correctly use the 'message' variable to throw an error
|
||||
throw GImage::Error(message);
|
||||
}
|
||||
#endif
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL)
|
||||
{
|
||||
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
|
||||
throw GImage::Error("Unable to initialize PNG decoder.");
|
||||
}
|
||||
|
||||
png_infop end_info = png_create_info_struct(png_ptr);
|
||||
if (end_info == NULL)
|
||||
{
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
throw GImage::Error("Unable to initialize PNG decoder.");
|
||||
}
|
||||
|
||||
// now that the libpng structures are setup, change the error handlers and read routines
|
||||
// to use G3D functions so that BinaryInput can be used.
|
||||
|
||||
png_set_read_fn(png_ptr, (png_voidp)&input, png_read_data);
|
||||
|
||||
// read in sequentially so that three copies of the file are not in memory at once
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
png_uint_32 png_width, png_height;
|
||||
int bit_depth, color_type, interlace_type;
|
||||
// this will validate the data it extracts from info_ptr
|
||||
png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
|
||||
|
||||
m_width = static_cast<uint32>(png_width);
|
||||
m_height = static_cast<uint32>(png_height);
|
||||
|
||||
// swap bytes of 16 bit files to least significant byte first
|
||||
png_set_swap(png_ptr);
|
||||
|
||||
png_set_strip_16(png_ptr);
|
||||
|
||||
// Expand paletted colors into true RGB triplets
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
{
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
// Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||
{
|
||||
png_set_expand(png_ptr);
|
||||
}
|
||||
|
||||
// Expand paletted or RGB images with transparency to full alpha channels
|
||||
// so the data will be available as RGBA quartets.
|
||||
bool has1BitAlpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
|
||||
if (has1BitAlpha)
|
||||
{
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
}
|
||||
|
||||
// Fix sub-8 bit_depth to 8bit
|
||||
if (bit_depth < 8)
|
||||
{
|
||||
png_set_packing(png_ptr);
|
||||
}
|
||||
|
||||
size_t bytesToAlloc = 0;
|
||||
png_bytep trans;
|
||||
int num_trans;
|
||||
|
||||
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
|
||||
|
||||
if ((color_type == PNG_COLOR_TYPE_RGBA) || ((color_type == PNG_COLOR_TYPE_PALETTE) && (num_trans > 0)) ||
|
||||
(color_type == PNG_COLOR_TYPE_RGB && has1BitAlpha))
|
||||
{
|
||||
m_channels = 4;
|
||||
}
|
||||
else if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_PALETTE))
|
||||
{
|
||||
m_channels = 3;
|
||||
}
|
||||
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA || (color_type == PNG_COLOR_TYPE_GRAY && has1BitAlpha))
|
||||
{
|
||||
m_channels = 2;
|
||||
}
|
||||
else if (color_type == PNG_COLOR_TYPE_GRAY)
|
||||
{
|
||||
m_channels = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw GImage::Error("Unsupported PNG bit-depth or type.");
|
||||
}
|
||||
|
||||
bytesToAlloc = m_width * m_height * m_channels;
|
||||
m_byte = (uint8*)m_memMan->alloc(bytesToAlloc);
|
||||
|
||||
if (!m_byte)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Out of memory while allocating " << bytesToAlloc << " bytes";
|
||||
throw GImage::Error(ss.str());
|
||||
}
|
||||
|
||||
// since we are reading row by row, required to handle interlacing
|
||||
uint32 number_passes = png_set_interlace_handling(png_ptr);
|
||||
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
|
||||
for (uint32 pass = 0; pass < number_passes; ++pass)
|
||||
{
|
||||
for (uint32 y = 0; y < (uint32)m_height; ++y)
|
||||
{
|
||||
png_bytep rowPointer = &m_byte[m_width * m_channels * y];
|
||||
png_read_rows(png_ptr, &rowPointer, NULL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
png_read_end(png_ptr, info_ptr);
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
284
engine/3d/src/GImage_tga.cpp
Normal file
284
engine/3d/src/GImage_tga.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
@file GImage_tga.cpp
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-05-27
|
||||
@edited 2009-05-10
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "GImage.hpp"
|
||||
#include "BinaryOutput.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
void GImage::encodeTGA(BinaryOutput& out) const
|
||||
{
|
||||
|
||||
out.setEndian(G3D_LITTLE_ENDIAN);
|
||||
|
||||
// ID length
|
||||
out.writeUInt8(0);
|
||||
|
||||
// Color map Type
|
||||
out.writeUInt8(0);
|
||||
|
||||
// Type
|
||||
out.writeUInt8(2);
|
||||
|
||||
// Color map
|
||||
out.skip(5);
|
||||
|
||||
// x, y offsets
|
||||
out.writeUInt16(0);
|
||||
out.writeUInt16(0);
|
||||
|
||||
// Width & height
|
||||
out.writeUInt16(m_width);
|
||||
out.writeUInt16(m_height);
|
||||
|
||||
// Color depth
|
||||
if (m_channels == 1)
|
||||
{
|
||||
// Force RGB mode
|
||||
out.writeUInt8(8 * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.writeUInt8(8 * m_channels);
|
||||
}
|
||||
|
||||
// Image descriptor
|
||||
if (m_channels < 4)
|
||||
{
|
||||
// 0 alpha bits
|
||||
out.writeUInt8(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 8 alpha bits
|
||||
out.writeUInt8(8);
|
||||
}
|
||||
|
||||
// Image ID (zero length)
|
||||
|
||||
if (m_channels == 1)
|
||||
{
|
||||
// Pixels are upside down in BGR format.
|
||||
for (int y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
for (int x = 0; x < m_width; ++x)
|
||||
{
|
||||
uint8 p = (m_byte[(y * m_width + x)]);
|
||||
out.writeUInt8(p);
|
||||
out.writeUInt8(p);
|
||||
out.writeUInt8(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_channels == 3)
|
||||
{
|
||||
// Pixels are upside down in BGR format.
|
||||
for (int y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
for (int x = 0; x < m_width; ++x)
|
||||
{
|
||||
uint8* p = &(m_byte[3 * (y * m_width + x)]);
|
||||
out.writeUInt8(p[2]);
|
||||
out.writeUInt8(p[1]);
|
||||
out.writeUInt8(p[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pixels are upside down in BGRA format.
|
||||
for (int y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
for (int x = 0; x < m_width; ++x)
|
||||
{
|
||||
uint8* p = &(m_byte[4 * (y * m_width + x)]);
|
||||
out.writeUInt8(p[2]);
|
||||
out.writeUInt8(p[1]);
|
||||
out.writeUInt8(p[0]);
|
||||
out.writeUInt8(p[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write "TRUEVISION-XFILE " 18 bytes from the end
|
||||
// (with null termination)
|
||||
out.writeString("TRUEVISION-XFILE ");
|
||||
}
|
||||
|
||||
inline static void readBGR(uint8* byte, BinaryInput& bi)
|
||||
{
|
||||
int b = bi.readUInt8();
|
||||
int g = bi.readUInt8();
|
||||
int r = bi.readUInt8();
|
||||
|
||||
byte[0] = r;
|
||||
byte[1] = g;
|
||||
byte[2] = b;
|
||||
}
|
||||
|
||||
inline static void readBGRA(uint8* byte, BinaryInput& bi)
|
||||
{
|
||||
readBGR(byte, bi);
|
||||
byte[3] = bi.readUInt8();
|
||||
}
|
||||
|
||||
void GImage::decodeTGA(BinaryInput& input)
|
||||
{
|
||||
|
||||
// This is a simple TGA loader that can handle uncompressed
|
||||
// truecolor TGA files (TGA type 2).
|
||||
// Verify this is a TGA file by looking for the TRUEVISION tag.
|
||||
int pos = input.getPosition();
|
||||
input.setPosition(input.size() - 18);
|
||||
std::string tag = input.readString(16);
|
||||
if (tag != "TRUEVISION-XFILE")
|
||||
{
|
||||
throw Error("Not a TGA file");
|
||||
}
|
||||
|
||||
input.setPosition(pos);
|
||||
|
||||
int IDLength = input.readUInt8();
|
||||
int colorMapType = input.readUInt8();
|
||||
int imageType = input.readUInt8();
|
||||
|
||||
(void)colorMapType;
|
||||
|
||||
// 2 is the type supported by this routine.
|
||||
if (imageType != 2 && imageType != 10)
|
||||
{
|
||||
throw Error("TGA images must be type 2 (Uncompressed truecolor) or 10 (Run-length truecolor)");
|
||||
}
|
||||
|
||||
// Color map specification
|
||||
input.skip(5);
|
||||
|
||||
// Image specification
|
||||
|
||||
// Skip x and y offsets
|
||||
input.skip(4);
|
||||
|
||||
m_width = input.readInt16();
|
||||
m_height = input.readInt16();
|
||||
|
||||
int colorDepth = input.readUInt8();
|
||||
|
||||
if ((colorDepth != 24) && (colorDepth != 32))
|
||||
{
|
||||
throw Error("TGA files must be 24 or 32 bit.");
|
||||
}
|
||||
|
||||
if (colorDepth == 32)
|
||||
{
|
||||
m_channels = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_channels = 3;
|
||||
}
|
||||
|
||||
// Image descriptor contains overlay data as well
|
||||
// as data indicating where the origin is
|
||||
int imageDescriptor = input.readUInt8();
|
||||
(void)imageDescriptor;
|
||||
|
||||
// Image ID
|
||||
input.skip(IDLength);
|
||||
|
||||
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
|
||||
debugAssert(m_byte);
|
||||
|
||||
// Pixel data
|
||||
int x;
|
||||
int y;
|
||||
|
||||
if (imageType == 2)
|
||||
{
|
||||
// Uncompressed
|
||||
if (m_channels == 3)
|
||||
{
|
||||
for (y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
for (x = 0; x < m_width; ++x)
|
||||
{
|
||||
int i = (x + y * m_width) * 3;
|
||||
readBGR(m_byte + i, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
for (x = 0; x < m_width; ++x)
|
||||
{
|
||||
int i = (x + y * m_width) * 4;
|
||||
readBGRA(m_byte + i, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (imageType == 10)
|
||||
{
|
||||
|
||||
// Run-length encoded
|
||||
for (y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
for (int x = 0; x < m_width; /* intentionally no x increment */)
|
||||
{
|
||||
// The specification guarantees that no packet will wrap past the end of a row
|
||||
const uint8 repetitionCount = input.readUInt8();
|
||||
const uint8 numValues = (repetitionCount & (~128)) + 1;
|
||||
int byteOffset = (x + y * m_width) * 3;
|
||||
|
||||
if (repetitionCount & 128)
|
||||
{
|
||||
// When the high bit is 1, this is a run-length packet
|
||||
if (m_channels == 3)
|
||||
{
|
||||
Color3uint8 value;
|
||||
readBGR((uint8*)(&value), input);
|
||||
for (int i = 0; i < numValues; ++i, ++x)
|
||||
{
|
||||
for (int b = 0; b < 3; ++b, ++byteOffset)
|
||||
{
|
||||
m_byte[byteOffset] = value[b];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Color4uint8 value;
|
||||
readBGRA((uint8*)(&value), input);
|
||||
for (int i = 0; i < numValues; ++i, ++x)
|
||||
{
|
||||
for (int b = 0; b < 3; ++b, ++byteOffset)
|
||||
{
|
||||
m_byte[byteOffset] = value[b];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// When the high bit is 0, this is a raw packet
|
||||
for (int i = 0; i < numValues; ++i, ++x, byteOffset += m_channels)
|
||||
{
|
||||
readBGR(m_byte + byteOffset, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alwaysAssertM(false, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
205
engine/3d/src/GLight.cpp
Normal file
205
engine/3d/src/GLight.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
@file GLight.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-11-12
|
||||
@edited 2009-11-16
|
||||
*/
|
||||
#include "GLight.hpp"
|
||||
#include "Sphere.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
GLight::GLight()
|
||||
: position(0, 0, 0, 0)
|
||||
, rightDirection(0, 0, 0)
|
||||
, spotDirection(0, 0, -1)
|
||||
, spotCutoff(180)
|
||||
, spotSquare(false)
|
||||
, color(Color3::white())
|
||||
, enabled(false)
|
||||
, specular(true)
|
||||
, diffuse(true)
|
||||
{
|
||||
|
||||
attenuation[0] = 1.0;
|
||||
attenuation[1] = 0.0;
|
||||
attenuation[2] = 0.0;
|
||||
}
|
||||
|
||||
|
||||
GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d)
|
||||
{
|
||||
GLight L;
|
||||
L.position = Vector4(toLight.direction(), 0);
|
||||
L.color = color;
|
||||
L.specular = s;
|
||||
L.diffuse = d;
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d)
|
||||
{
|
||||
GLight L;
|
||||
L.position = Vector4(pos, 1);
|
||||
L.color = color;
|
||||
L.attenuation[0] = constAtt;
|
||||
L.attenuation[1] = linAtt;
|
||||
L.attenuation[2] = quadAtt;
|
||||
L.specular = s;
|
||||
L.diffuse = d;
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt,
|
||||
float quadAtt, bool s, bool d)
|
||||
{
|
||||
GLight L;
|
||||
L.position = Vector4(pos, 1.0f);
|
||||
L.spotDirection = pointDirection.direction();
|
||||
debugAssert(cutOffAngleDegrees <= 90);
|
||||
L.spotCutoff = cutOffAngleDegrees;
|
||||
L.color = color;
|
||||
L.attenuation[0] = constAtt;
|
||||
L.attenuation[1] = linAtt;
|
||||
L.attenuation[2] = quadAtt;
|
||||
L.specular = s;
|
||||
L.diffuse = d;
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
bool GLight::operator==(const GLight& other) const
|
||||
{
|
||||
return (position == other.position) && (rightDirection == other.rightDirection) && (spotDirection == other.spotDirection) &&
|
||||
(spotCutoff == other.spotCutoff) && (spotSquare == other.spotSquare) && (attenuation[0] == other.attenuation[0]) &&
|
||||
(attenuation[1] == other.attenuation[1]) && (attenuation[2] == other.attenuation[2]) && (color == other.color) &&
|
||||
(enabled == other.enabled) && (specular == other.specular) && (diffuse == other.diffuse);
|
||||
}
|
||||
|
||||
|
||||
bool GLight::operator!=(const GLight& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
Sphere GLight::effectSphere(float cutoff) const
|
||||
{
|
||||
if (position.w == 0)
|
||||
{
|
||||
// Directional light
|
||||
return Sphere(Vector3::zero(), finf());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Avoid divide by zero
|
||||
cutoff = max(cutoff, 0.00001f);
|
||||
float maxIntensity = max(color.r, max(color.g, color.b));
|
||||
|
||||
float radius = finf();
|
||||
|
||||
if (attenuation[2] != 0)
|
||||
{
|
||||
|
||||
// Solve I / attenuation.dot(1, r, r^2) < cutoff for r
|
||||
//
|
||||
// a[0] + a[1] r + a[2] r^2 > I/cutoff
|
||||
//
|
||||
|
||||
float a = attenuation[2];
|
||||
float b = attenuation[1];
|
||||
float c = attenuation[0] - maxIntensity / cutoff;
|
||||
|
||||
float discrim = square(b) - 4 * a * c;
|
||||
|
||||
if (discrim >= 0)
|
||||
{
|
||||
discrim = sqrt(discrim);
|
||||
|
||||
float r1 = (-b + discrim) / (2 * a);
|
||||
float r2 = (-b - discrim) / (2 * a);
|
||||
|
||||
if (r1 < 0)
|
||||
{
|
||||
if (r2 > 0)
|
||||
{
|
||||
radius = r2;
|
||||
}
|
||||
}
|
||||
else if (r2 > 0)
|
||||
{
|
||||
radius = min(r1, r2);
|
||||
}
|
||||
else
|
||||
{
|
||||
radius = r1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attenuation[1] != 0)
|
||||
{
|
||||
|
||||
// Solve I / attenuation.dot(1, r) < cutoff for r
|
||||
//
|
||||
// r * a[1] + a[0] = I / cutoff
|
||||
// r = (I / cutoff - a[0]) / a[1]
|
||||
|
||||
float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1];
|
||||
radius = max(radius, 0.0f);
|
||||
}
|
||||
|
||||
return Sphere(position.xyz(), radius);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CoordinateFrame GLight::frame() const
|
||||
{
|
||||
CoordinateFrame f;
|
||||
if (rightDirection == Vector3::zero())
|
||||
{
|
||||
// No specified right direction; choose one automatically
|
||||
if (position.w == 0)
|
||||
{
|
||||
// Directional light
|
||||
f.lookAt(-position.xyz());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Spot light
|
||||
f.lookAt(spotDirection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Vector3& Z = -spotDirection.direction();
|
||||
Vector3 X = rightDirection.direction();
|
||||
|
||||
// Ensure the vectors are not too close together
|
||||
while (abs(X.dot(Z)) > 0.9f)
|
||||
{
|
||||
X = Vector3::random();
|
||||
}
|
||||
|
||||
// Ensure perpendicular
|
||||
X -= Z * Z.dot(X);
|
||||
const Vector3& Y = Z.cross(X);
|
||||
|
||||
f.rotation.setColumn(Vector3::X_AXIS, X);
|
||||
f.rotation.setColumn(Vector3::Y_AXIS, Y);
|
||||
f.rotation.setColumn(Vector3::Z_AXIS, Z);
|
||||
}
|
||||
f.translation = position.xyz();
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
102
engine/3d/src/GLight.hpp
Normal file
102
engine/3d/src/GLight.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
@file GLight.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-11-12
|
||||
@edited 2009-11-08
|
||||
*/
|
||||
|
||||
#ifndef G3D_GLight_h
|
||||
#define G3D_GLight_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Color4.hpp"
|
||||
|
||||
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;
|
||||
|
||||
/** For a spot or directional light, this is the "right vector" that will be used when constructing
|
||||
a reference frame(). */
|
||||
Vector3 rightDirection;
|
||||
|
||||
/** Direction in which the light faces, if a spot light. This is the "look vector" of the light source. */
|
||||
Vector3 spotDirection;
|
||||
|
||||
/** In <B>degrees</B>. 180 = no cutoff (point/dir). Values less than 90 = spot light */
|
||||
float spotCutoff;
|
||||
|
||||
/** If true, G3D::SuperShader will render a cone of light large
|
||||
enough to encompass the entire square that bounds the cutoff
|
||||
angle. This produces a square prism instead of a cone of light
|
||||
when used with a G3D::ShadowMap. for an unshadowed light this
|
||||
has no effect.*/
|
||||
bool spotSquare;
|
||||
|
||||
/** Constant, linear, quadratic */
|
||||
float attenuation[3];
|
||||
|
||||
/** May be outside the range [0, 1] */
|
||||
Color3 color;
|
||||
|
||||
/** If false, this light is ignored */
|
||||
bool enabled;
|
||||
|
||||
/** If false, this light does not create specular highlights
|
||||
(useful when using negative lights). */
|
||||
bool specular;
|
||||
|
||||
/** If false, this light does not create diffuse illumination
|
||||
(useful when rendering a specular-only pass). */
|
||||
bool diffuse;
|
||||
|
||||
GLight();
|
||||
|
||||
/** @param toLight will be normalized */
|
||||
static GLight directional(const Vector3& toLight, const Color3& color, bool specular = true, bool diffuse = true);
|
||||
|
||||
static GLight point(const Vector3& pos, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0.5f, bool specular = true,
|
||||
bool diffuse = true);
|
||||
|
||||
/** @param pointDirection Will be normalized. Points in the
|
||||
direction that light propagates.
|
||||
|
||||
@param cutOffAngleDegrees Must be on the range [0, 90]. This
|
||||
is the angle from the point direction to the edge of the light
|
||||
cone. I.e., a value of 45 produces a light with a 90-degree
|
||||
cone of view.
|
||||
*/
|
||||
static GLight spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt = 1,
|
||||
float linAtt = 0, float quadAtt = 0, bool specular = true, bool diffuse = true);
|
||||
|
||||
/** Creates a spot light that looks at a specific point (by calling spot() ) */
|
||||
static GLight spotTarget(const Vector3& pos, const Vector3& target, float cutOffAngleDegrees, const Color3& color, float constAtt = 1,
|
||||
float linAtt = 0, float quadAtt = 0, bool specular = true, bool diffuse = true)
|
||||
{
|
||||
return spot(pos, target - pos, cutOffAngleDegrees, color, constAtt, linAtt, quadAtt, specular, diffuse);
|
||||
}
|
||||
|
||||
/** Returns the sphere within which this light has some noticable effect. May be infinite.
|
||||
@param cutoff The value at which the light intensity is considered negligible. */
|
||||
class Sphere effectSphere(float cutoff = 30.0f / 255) const;
|
||||
|
||||
/** Computes a reference frame (e.g., for use with G3D::ShadowMap */
|
||||
class CoordinateFrame frame() const;
|
||||
|
||||
bool operator==(const GLight& other) const;
|
||||
bool operator!=(const GLight& other) const;
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
#endif
|
||||
15
engine/3d/src/HandleType.hpp
Normal file
15
engine/3d/src/HandleType.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
enum HandleType
|
||||
{
|
||||
HANDLE_RESIZE = 0,
|
||||
HANDLE_MOVE,
|
||||
HANDLE_ROTATE,
|
||||
HANDLE_VELOCITY
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
144
engine/3d/src/HashTrait.hpp
Normal file
144
engine/3d/src/HashTrait.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
@file HashTrait.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2008-10-01
|
||||
@edited 2009-11-01
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_HashTrait_h
|
||||
#define G3D_HashTrait_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Crypto.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "uint128.hpp"
|
||||
|
||||
/** Must be specialized for custom types.
|
||||
@see G3D::Table for specialization requirements.
|
||||
*/
|
||||
template<typename T>
|
||||
struct HashTrait
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct HashTrait<T*>
|
||||
{
|
||||
static size_t hashCode(const void* k)
|
||||
{
|
||||
return reinterpret_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
template <> struct HashTrait <int> {
|
||||
static size_t hashCode(int k) { return static_cast<size_t>(k); }
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::int16>
|
||||
{
|
||||
static size_t hashCode(G3D::int16 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::uint16>
|
||||
{
|
||||
static size_t hashCode(G3D::uint16 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
// template <> struct HashTrait <int> {
|
||||
// static size_t hashCode(int k) { return static_cast<size_t>(k); }
|
||||
// };
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::int32>
|
||||
{
|
||||
static size_t hashCode(G3D::int32 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::uint32>
|
||||
{
|
||||
static size_t hashCode(G3D::uint32 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(G3D_OSX) || defined(G3D_IOS) // Aya
|
||||
template<>
|
||||
struct HashTrait<long unsigned int>
|
||||
{
|
||||
static size_t hashCode(G3D::uint32 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::int64>
|
||||
{
|
||||
static size_t hashCode(G3D::int64 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::uint64>
|
||||
{
|
||||
static size_t hashCode(G3D::uint64 k)
|
||||
{
|
||||
return static_cast<size_t>(k);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct HashTrait<std::string>
|
||||
{
|
||||
static size_t hashCode(const std::string& k)
|
||||
{
|
||||
return static_cast<size_t>(G3D::Crypto::crc32(k.c_str(), k.size()));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct HashTrait<G3D::uint128>
|
||||
{
|
||||
// Use the FNV-1 hash (http://isthe.com/chongo/tech/comp/fnv/#FNV-1).
|
||||
static size_t hashCode(G3D::uint128 key)
|
||||
{
|
||||
static const G3D::uint128 FNV_PRIME_128(1 << 24, 0x159);
|
||||
static const G3D::uint128 FNV_OFFSET_128(0xCF470AAC6CB293D2ULL, 0xF52F88BF32307F8FULL);
|
||||
|
||||
G3D::uint128 hash = FNV_OFFSET_128;
|
||||
G3D::uint128 mask(0, 0xFF);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
hash *= FNV_PRIME_128;
|
||||
hash ^= (mask & key);
|
||||
key >>= 8;
|
||||
}
|
||||
|
||||
G3D::uint64 foldedHash = hash.hi ^ hash.lo;
|
||||
return static_cast<size_t>((foldedHash >> 32) ^ (foldedHash & 0xFFFFFFFF));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
59
engine/3d/src/HitTest.cpp
Normal file
59
engine/3d/src/HitTest.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
#include "HitTest.hpp"
|
||||
#include "Base/Part.hpp"
|
||||
#include "CollisionDetection.hpp"
|
||||
#include "Capsule.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
bool HitTest::hitTestBox(const Part& part, Aya::RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal)
|
||||
{
|
||||
Vector3 halfRealSize = part.gridSize * gridToReal * 0.5;
|
||||
|
||||
return G3D::CollisionDetection::collisionLocationForMovingPointFixedAABox(
|
||||
rayInPartCoords.origin(), rayInPartCoords.direction(), G3D::AABox(-halfRealSize, halfRealSize), hitPointInPartCoords);
|
||||
}
|
||||
|
||||
|
||||
bool HitTest::hitTestBall(const Part& part, Aya::RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal)
|
||||
{
|
||||
float radius = part.gridSize.x * gridToReal * 0.5f;
|
||||
|
||||
return (G3D::CollisionDetection::collisionTimeForMovingPointFixedSphere(
|
||||
rayInPartCoords.origin(), rayInPartCoords.direction(), G3D::Sphere(Vector3::zero(), radius), hitPointInPartCoords)
|
||||
|
||||
!= G3D::inf());
|
||||
}
|
||||
|
||||
// TODO: Big optimization possible here...
|
||||
// TODO: Clean up hit test here - offset stuff going on
|
||||
bool HitTest::hitTestCylinder(const Part& part, Aya::RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal)
|
||||
{
|
||||
float radius = part.gridSize.z * gridToReal * 0.5f;
|
||||
float axis = part.gridSize.x * gridToReal * 0.5f; // TODO - Document all of this - World and Grid space
|
||||
|
||||
return (G3D::CollisionDetection::collisionTimeForMovingPointFixedCapsule(rayInPartCoords.origin(), rayInPartCoords.direction(),
|
||||
G3D::Capsule(Vector3::zero(), Vector3(axis, 0, 0), radius), hitPointInPartCoords)
|
||||
|
||||
!= G3D::inf());
|
||||
}
|
||||
|
||||
bool HitTest::hitTest(const Part& part, Aya::RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal)
|
||||
{
|
||||
switch (part.type)
|
||||
{
|
||||
case Aya::BLOCK_PART:
|
||||
return hitTestBox(part, rayInPartCoords, hitPointInPartCoords, gridToReal);
|
||||
case Aya::BALL_PART:
|
||||
return hitTestBall(part, rayInPartCoords, hitPointInPartCoords, gridToReal);
|
||||
case Aya::CYLINDER_PART:
|
||||
return hitTestCylinder(part, rayInPartCoords, hitPointInPartCoords, gridToReal);
|
||||
default:
|
||||
debugAssert(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
24
engine/3d/src/HitTest.hpp
Normal file
24
engine/3d/src/HitTest.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/G3DCore.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Part;
|
||||
|
||||
class HitTest
|
||||
{
|
||||
private:
|
||||
// hitTests
|
||||
static bool hitTestBox(const Part& part, RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal);
|
||||
static bool hitTestBall(const Part& part, RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal);
|
||||
static bool hitTestCylinder(const Part& part, RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal);
|
||||
|
||||
public:
|
||||
static bool hitTest(const Part& part, RbxRay& rayInPartCoords, Vector3& hitPointInPartCoords, float gridToReal);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
80
engine/3d/src/Image1.hpp
Normal file
80
engine/3d/src/Image1.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
@file Image1.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2007-01-31
|
||||
@edited 2007-01-31
|
||||
*/
|
||||
|
||||
|
||||
#ifndef G3D_IMAGE1_H
|
||||
#define G3D_IMAGE1_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Map2D.hpp"
|
||||
#include "Color1.hpp"
|
||||
#include "GImage.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
typedef ReferenceCountedPointer<class Image1> Image1Ref;
|
||||
|
||||
/**
|
||||
Luminance image with 32-bit floating point storage.
|
||||
|
||||
See also G3D::Image1uint8, G3D::GImage.
|
||||
*/
|
||||
class Image1 : public Map2D<Color1, Color1>
|
||||
{
|
||||
public:
|
||||
typedef Image1 Type;
|
||||
typedef ReferenceCountedPointer<class Image1> Ref;
|
||||
typedef Color1 Storage;
|
||||
typedef Color1 Compute;
|
||||
|
||||
protected:
|
||||
Image1(int w, int h, WrapMode wrap);
|
||||
|
||||
void copyGImage(const class GImage& im);
|
||||
void copyArray(const Color1* src, int w, int h);
|
||||
void copyArray(const Color3* src, int w, int h);
|
||||
void copyArray(const Color4* src, int w, int h);
|
||||
void copyArray(const Color1uint8* src, int w, int h);
|
||||
void copyArray(const Color3uint8* src, int w, int h);
|
||||
void copyArray(const Color4uint8* src, int w, int h);
|
||||
|
||||
public:
|
||||
const class ImageFormat* format() const;
|
||||
|
||||
/** Creates an all-zero width x height image. */
|
||||
static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
|
||||
/** Creates a 0 x 0 image. */
|
||||
static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
|
||||
|
||||
static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
|
||||
|
||||
static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
|
||||
|
||||
static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im);
|
||||
|
||||
static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
|
||||
|
||||
/** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
|
||||
it is stripped. */
|
||||
void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
|
||||
|
||||
/** Saves in any of the formats supported by G3D::GImage. */
|
||||
void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
207
engine/3d/src/LightingParameters.cpp
Normal file
207
engine/3d/src/LightingParameters.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
@file LightingParameters.cpp
|
||||
|
||||
@maintainer Morgan McGuire, matrix@graphics3d.com
|
||||
@created 2002-10-05
|
||||
@edited 2006-06-28
|
||||
*/
|
||||
|
||||
#include "LightingParameters.hpp"
|
||||
#include "Matrix3.hpp"
|
||||
#include "spline.hpp"
|
||||
#include "GLight.hpp"
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define _timeb timeb
|
||||
#define _ftime ftime
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4305)
|
||||
#endif
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
static const double sunRiseAndSetTime = HOUR / 2;
|
||||
static const double solarYear = 365.2564 * DAY;
|
||||
static const double halfSolarYear = 182.6282;
|
||||
static const double moonPhaseInterval = DAY * 29.53;
|
||||
|
||||
// Tilt amount from the ecliptic
|
||||
static const double earthTilt = toRadians(23.5);
|
||||
static const double moonTilt = toRadians(5);
|
||||
|
||||
// (very rough) Initial star offset on Jan 1 1970 midnight
|
||||
static const double initialStarRot = 1;
|
||||
|
||||
// Initial moon phase on Jan 1 1970 midnight
|
||||
static const double initialMoonPhase = 0.75;
|
||||
|
||||
LightingParameters::LightingParameters()
|
||||
{
|
||||
physicallyCorrect = true;
|
||||
setLatitude(BROWN_UNIVERSITY_LATITUDE);
|
||||
setTime(0);
|
||||
}
|
||||
|
||||
|
||||
LightingParameters::LightingParameters(const GameTime _time, bool _physicallyCorrect, float _latitude)
|
||||
{
|
||||
physicallyCorrect = _physicallyCorrect;
|
||||
setLatitude(_latitude);
|
||||
setTime(_time);
|
||||
}
|
||||
|
||||
void LightingParameters::setLatitude(float _latitude)
|
||||
{
|
||||
geoLatitude = _latitude;
|
||||
}
|
||||
|
||||
void LightingParameters::setTime(const GameTime _time)
|
||||
{
|
||||
// wrap to a 1 day interval
|
||||
double time = _time - floor(_time / DAY) * DAY;
|
||||
|
||||
// Calculate starfield coordinate frame
|
||||
double starRot = initialStarRot - (2 * G3D::pi() * (_time - (_time * floor(_time / SIDEREAL_DAY))) / SIDEREAL_DAY);
|
||||
float aX, aY, aZ;
|
||||
starVec.x = cos(starRot);
|
||||
starVec.y = 0;
|
||||
starVec.z = sin(starRot);
|
||||
|
||||
starFrame.lookAt(starVec, Vector3::unitY());
|
||||
trueStarFrame.lookAt(starVec, Vector3::unitY());
|
||||
trueStarFrame.rotation.toEulerAnglesXYZ(aX, aY, aZ);
|
||||
aX -= geoLatitude;
|
||||
trueStarFrame.rotation = Matrix3::fromEulerAnglesXYZ(aX, aY, aZ);
|
||||
|
||||
// sunAngle = 0 at midnight
|
||||
float sourceAngle = 2 * (float)G3D::pi() * time / DAY;
|
||||
|
||||
// Calculate fake solar and lunar positions
|
||||
sunPosition.x = sin(sourceAngle);
|
||||
sunPosition.y = -cos(sourceAngle);
|
||||
sunPosition.z = 0;
|
||||
|
||||
moonPosition.x = sin(sourceAngle + (float)G3D::pi());
|
||||
moonPosition.y = -cos(sourceAngle + (float)G3D::pi());
|
||||
moonPosition.z = 0;
|
||||
|
||||
// Calculate "true" solar and lunar positions
|
||||
// These positions will always be somewhat wrong
|
||||
// unless _time is equal to real world GMT time,
|
||||
// and the current longitude is equal to zero. Also,
|
||||
// I'm assuming that the equinox-solstice interval
|
||||
// occurs exactly every 90 days, which isn't exactly
|
||||
// correct.
|
||||
// In addition, the precession of the moon's orbit is
|
||||
// not taken into account, but this should only account
|
||||
// for a 5 degree margin of error at most.
|
||||
|
||||
float dayOfYearOffset = (_time - (_time * floor(_time / solarYear))) / DAY;
|
||||
moonPhase = floor(_time / moonPhaseInterval) + initialMoonPhase;
|
||||
|
||||
float latRad = toRadians(geoLatitude);
|
||||
float sunOffset = -earthTilt * cos(G3D::pi() * (dayOfYearOffset - halfSolarYear) / halfSolarYear) - latRad;
|
||||
float moonOffset = ((-earthTilt + moonTilt) * sin(moonPhase * 4)) - latRad;
|
||||
float curMoonPhase = (moonPhase * G3D::pi() * 2);
|
||||
|
||||
Matrix3 rotMat = Matrix3::fromAxisAngle(Vector3::unitZ().cross(sunPosition), sunOffset);
|
||||
trueSunPosition = rotMat * sunPosition;
|
||||
|
||||
Vector3 trueMoon = Vector3(sin(curMoonPhase + sourceAngle), -cos(curMoonPhase + sourceAngle), 0);
|
||||
rotMat = Matrix3::fromAxisAngleFast(Vector3::unitZ().cross(trueMoon), moonOffset);
|
||||
trueMoonPosition = rotMat * trueMoon;
|
||||
|
||||
// Determine which light source we observe.
|
||||
if (!physicallyCorrect)
|
||||
{
|
||||
if ((sourceAngle < (G3D::pi() / 2)) || (sourceAngle > (3 * G3D::pi() / 2)))
|
||||
{
|
||||
source = MOON;
|
||||
sourceAngle += (float)G3D::pi();
|
||||
}
|
||||
else
|
||||
{
|
||||
source = SUN;
|
||||
}
|
||||
|
||||
lightDirection.x = sin(sourceAngle);
|
||||
lightDirection.y = -cos(sourceAngle);
|
||||
lightDirection.z = 0;
|
||||
}
|
||||
else if (trueSunPosition.y > -.3f)
|
||||
{
|
||||
// The sun is always the stronger light source. When using
|
||||
// physically correct parameters, the sun and moon will
|
||||
// occasionally be in the visible sky at the same time.
|
||||
source = SUN;
|
||||
lightDirection = trueSunPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = MOON;
|
||||
lightDirection = trueMoonPosition;
|
||||
}
|
||||
|
||||
const Color3 dayAmbient = Color3::white() * .40f;
|
||||
const Color3 dayDiffuse = Color3::white() * .75f;
|
||||
|
||||
{
|
||||
static const double times[] = {MIDNIGHT, SUNRISE - HOUR, SUNRISE, SUNRISE + sunRiseAndSetTime / 4, SUNRISE + sunRiseAndSetTime,
|
||||
SUNSET - sunRiseAndSetTime, SUNSET - sunRiseAndSetTime / 2, SUNSET, SUNSET + HOUR / 2, DAY};
|
||||
static const Color3 color[] = {Color3(.2f, .2f, .2f), Color3(.1f, .1, .1), Color3(0, 0, 0), Color3(.6, .6, 0), dayDiffuse, dayDiffuse,
|
||||
Color3(.1, .1, .075), Color3(.1, .05, .05), Color3(.1, .1, .1), Color3(.2, .2, .2)};
|
||||
lightColor = linearSpline(time, times, color, 10);
|
||||
}
|
||||
|
||||
{
|
||||
static const double times[] = {MIDNIGHT, SUNRISE - HOUR, SUNRISE, SUNRISE + sunRiseAndSetTime / 4, SUNRISE + sunRiseAndSetTime,
|
||||
SUNSET - sunRiseAndSetTime, SUNSET - sunRiseAndSetTime / 2, SUNSET, SUNSET + HOUR / 2, DAY};
|
||||
static const Color3 color[] = {Color3(0, .1, .3), Color3(0, .0, .1), Color3(0, 0, 0), Color3(0, 0, 0), dayAmbient, dayAmbient,
|
||||
Color3(.5, .2, .2), Color3(.05, .05, .1), Color3(0, .0, .1), Color3(0, .1, .3)};
|
||||
ambient = linearSpline(time, times, color, 10);
|
||||
}
|
||||
|
||||
{
|
||||
static const double times[] = {MIDNIGHT, SUNRISE - HOUR, SUNRISE, SUNRISE + sunRiseAndSetTime / 2, SUNRISE + sunRiseAndSetTime,
|
||||
SUNSET - sunRiseAndSetTime, SUNSET - sunRiseAndSetTime / 2, SUNSET, SUNSET + HOUR / 2, DAY};
|
||||
static const Color3 color[] = {Color3(.1, .1, .17), Color3(.05, .06, .07), Color3(.08, .08, .01), Color3(1, 1, 1) * .75,
|
||||
Color3(1, 1, 1) * .75, Color3(1, 1, 1) * .35, Color3(.5, .2, .2), Color3(.05, .05, .1), Color3(.06, .06, .07), Color3(.1, .1, .17)};
|
||||
diffuseAmbient = linearSpline(time, times, color, 10);
|
||||
}
|
||||
|
||||
{
|
||||
static const double times[] = {MIDNIGHT, SUNRISE - 2 * HOUR, SUNRISE - HOUR, SUNRISE - HOUR / 2, SUNRISE, SUNRISE + sunRiseAndSetTime,
|
||||
SUNSET - sunRiseAndSetTime, SUNSET, SUNSET + HOUR / 3, DAY};
|
||||
static const Color3 color[] = {Color3(0, 0, 0), Color3(0, 0, 0), Color3(.07, .07, .1), Color3(.2, .15, .01), Color3(.2, .15, .01),
|
||||
Color3(1, 1, 1), Color3(1, 1, 1), Color3(.4, .2, .05), Color3(0, 0, 0), Color3(0, 0, 0)};
|
||||
skyAmbient = linearSpline(time, times, color, sizeof(times) / sizeof(times[0]));
|
||||
}
|
||||
|
||||
{
|
||||
static const double times[] = {MIDNIGHT, SUNRISE - 3 * HOUR, SUNRISE - 2 * HOUR, SUNRISE - HOUR / 2, SUNRISE, SUNRISE + sunRiseAndSetTime,
|
||||
SUNSET - sunRiseAndSetTime, SUNSET, SUNSET + HOUR / 3, SUNSET + 2 * HOUR, SUNSET + 3 * HOUR, DAY};
|
||||
static const Color3 color[] = {Color3(0.0, 0.0, 0.0), 0.7f * Color3(0.0, 0.0, 0.0), 0.7f * Color3(0.3, 0.3, 0.4), Color3(0.4, 0.3, 0.3),
|
||||
Color3(0.3, 0.2, 0.3), Color3(1, 1, 1), Color3(1, 1, 1), Color3(.4, .3, .2), Color3(0.3, 0.2, 0.3), Color3(0.3, 0.2, 0.3),
|
||||
Color3(0, 0, 0), Color3(0, 0, 0)};
|
||||
skyAmbient2 = linearSpline(time, times, color, sizeof(times) / sizeof(times[0]));
|
||||
}
|
||||
|
||||
emissiveScale = Color3::white();
|
||||
}
|
||||
|
||||
|
||||
GLight LightingParameters::directionalLight() const
|
||||
{
|
||||
return GLight::directional(lightDirection, lightColor);
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
158
engine/3d/src/LightingParameters.hpp
Normal file
158
engine/3d/src/LightingParameters.hpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
@file LightingParameters.h
|
||||
|
||||
@maintainer Morgan McGuire, matrix@graphics3d.com
|
||||
@created 2002-10-05
|
||||
@edited 2005-06-01
|
||||
|
||||
Copyright 2000-2005, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_LIGHTINGPARAMETERS_H
|
||||
#define G3D_LIGHTINGPARAMETERS_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Color3.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "GLight.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
#define BROWN_UNIVERSITY_LATITUDE 41.7333f
|
||||
#define BROWN_UNIVERSITY_LONGITUDE 71.4333f
|
||||
|
||||
/* Initital star offset on Jan 1 1970 midnight */
|
||||
/* Definition of a sidereal day */
|
||||
#define SIDEREAL_DAY ((23 * HOUR) + (56 * MINUTE) + (4.071f * SECOND))
|
||||
|
||||
/**
|
||||
Provides a reasonable (but not 100% physically correct) set of lighting
|
||||
parameters based on the time of day. See also G3D::Lighting, which describes
|
||||
a rich lighting environment.
|
||||
*/
|
||||
class LightingParameters
|
||||
{
|
||||
public:
|
||||
/** Multiply this by all emissive values when rendering.
|
||||
Some algorithms (e.g. contrib/ArticulatedModel/ToneMap) scale
|
||||
down light intensity to preserve dynamic range.*/
|
||||
Color3 emissiveScale;
|
||||
|
||||
/** Modulate sky box color */
|
||||
Color3 skyAmbient;
|
||||
|
||||
/**
|
||||
Use this for objects that do not receive directional lighting
|
||||
(e.g. billboards).
|
||||
*/
|
||||
Color3 diffuseAmbient;
|
||||
|
||||
/**
|
||||
Directional light color.
|
||||
*/
|
||||
Color3 lightColor;
|
||||
Color3 ambient;
|
||||
|
||||
/** Only one light source, the sun or moon, is active at a given time. */
|
||||
Vector3 lightDirection;
|
||||
enum
|
||||
{
|
||||
SUN,
|
||||
MOON
|
||||
} source;
|
||||
|
||||
/** Using physically correct parameters. When false, the sun and moon
|
||||
travel in a perfectly east-west arc where +x = east and -x = west. */
|
||||
bool physicallyCorrect;
|
||||
|
||||
/** The vector <B>to</B> the sun */
|
||||
Vector3 trueSunPosition;
|
||||
Vector3 sunPosition;
|
||||
|
||||
/** The vector <B>to</B> the moon */
|
||||
Vector3 trueMoonPosition;
|
||||
Vector3 moonPosition;
|
||||
double moonPhase;
|
||||
|
||||
/** The coordinate frame and vector related to the starfield */
|
||||
CoordinateFrame starFrame;
|
||||
CoordinateFrame trueStarFrame;
|
||||
Vector3 starVec;
|
||||
|
||||
/* Geographic position */
|
||||
float geoLatitude;
|
||||
// float geoLongitude;
|
||||
|
||||
Color3 skyAmbient2; // used for some cheap sky gradient (secondary, horizon-level color) -- Max
|
||||
|
||||
LightingParameters();
|
||||
|
||||
/**
|
||||
Sets light parameters for the sun/moon based on the
|
||||
specified time since midnight, as well as geographic
|
||||
latitude for starfield orientation (positive for north
|
||||
of the equator and negative for south) and geographic
|
||||
longitude for sun positioning (postive for east of
|
||||
Greenwich, and negative for west). The latitude and
|
||||
longitude is set by default to that of Providence, RI,
|
||||
USA.
|
||||
*/
|
||||
LightingParameters(const GameTime _time, bool _physicallyCorrect = true, float _latitude = BROWN_UNIVERSITY_LATITUDE);
|
||||
|
||||
void setTime(const GameTime _time);
|
||||
void setLatitude(float _latitude);
|
||||
|
||||
/**
|
||||
Returns a directional light composed from the light direction
|
||||
and color.
|
||||
*/
|
||||
GLight directionalLight() const;
|
||||
};
|
||||
|
||||
|
||||
/** A rich environment lighting model that contains both global and local sources.
|
||||
See also LightingParameters, a class that describes a sun and moon lighting
|
||||
model. */
|
||||
class Lighting
|
||||
{
|
||||
private:
|
||||
Lighting()
|
||||
: emissiveScale(Color3::white())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/** Multiply this by all emissive values when rendering.
|
||||
Some algorithms (e.g. contrib/ArticulatedModel/ToneMap) scale
|
||||
down light intensity to preserve dynamic range.*/
|
||||
Color3 emissiveScale;
|
||||
|
||||
/** Light reflected from the sky (usually slightly blue) */
|
||||
Color3 ambientTop;
|
||||
|
||||
/** Light reflected from the ground. A simpler code path is taken
|
||||
if identical to ambientTop. */
|
||||
Color3 ambientBottom;
|
||||
|
||||
/** Color to modulate environment map by */
|
||||
Color3 environmentMapColor;
|
||||
|
||||
/** Local illumination sources that do not cast shadows. */
|
||||
Array<GLight> lightArray;
|
||||
|
||||
/** Local illumination sources that cast shadows. */
|
||||
Array<GLight> shadowedLightArray;
|
||||
|
||||
/** Creates a (dark) environment. */
|
||||
static Lighting* create()
|
||||
{
|
||||
return new Lighting();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
77
engine/3d/src/Line.cpp
Normal file
77
engine/3d/src/Line.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
@file Line.cpp
|
||||
|
||||
Line class
|
||||
|
||||
@maintainer Morgan McGuire, graphics3d.com
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2006-01-28
|
||||
*/
|
||||
|
||||
#include "Line.hpp"
|
||||
#include "Plane.hpp"
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
155
engine/3d/src/Line.hpp
Normal file
155
engine/3d/src/Line.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
@file Line.h
|
||||
|
||||
Line class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2006-02-28
|
||||
*/
|
||||
|
||||
#ifndef G3D_LINE_H
|
||||
#define G3D_LINE_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
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() {}
|
||||
|
||||
virtual ~Line() {}
|
||||
|
||||
/**
|
||||
Constructs a line from two (not equal) points.
|
||||
*/
|
||||
static Line fromTwoPoints(const Vector3& point1, const Vector3& point2)
|
||||
{
|
||||
// Aya
|
||||
// return Line(point1, point2 - point1);
|
||||
return Line(point1, (point2 - point1).direction());
|
||||
// =====
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a line from a point and a (nonzero) direction.
|
||||
*/
|
||||
static Line fromPointAndDirection(const Vector3& point, const Vector3& direction)
|
||||
{
|
||||
// Aya
|
||||
// return Line(point, direction);
|
||||
return Line(point, direction.direction());
|
||||
// =====
|
||||
}
|
||||
|
||||
// Aya
|
||||
static Line fromPointAndUnitDirection(const Vector3& point, const Vector3& unitDirection)
|
||||
{
|
||||
return Line(point, unitDirection);
|
||||
}
|
||||
//=====
|
||||
|
||||
/**
|
||||
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);
|
||||
}
|
||||
// Aya
|
||||
bool static parallel(const Line& line0, const Line& line1, float epsilon)
|
||||
{
|
||||
Vector3 crossAxis = line0.direction().cross(line1.direction());
|
||||
return (crossAxis.squaredMagnitude() < (epsilon * epsilon));
|
||||
}
|
||||
|
||||
// Returns false if unable to find closest points (i.e. lines are parallel)
|
||||
static bool closestPoints(const Line& line0, const Line& line1, Vector3& p0, Vector3& p1)
|
||||
{
|
||||
const Vector3& pa = line0.point();
|
||||
const Vector3& pb = line1.point();
|
||||
|
||||
const Vector3& ua = line0.direction();
|
||||
const Vector3& ub = line1.direction();
|
||||
|
||||
Vector3 p = pb - pa;
|
||||
float uaub = ua.dot(ub);
|
||||
float q1 = ua.dot(p);
|
||||
float q2 = -ub.dot(p);
|
||||
float d = 1 - uaub * uaub;
|
||||
if (d > 1e-6f)
|
||||
{
|
||||
d = 1.0f / d;
|
||||
float distance0 = (q1 + uaub * q2) * d;
|
||||
float distance1 = (uaub * q1 + q2) * d;
|
||||
p0 = pa + distance0 * ua;
|
||||
p1 = pb + distance1 * ub;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
p0 = pa;
|
||||
p1 = pb;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// =============================
|
||||
};
|
||||
|
||||
}; // namespace G3D
|
||||
|
||||
|
||||
#endif
|
||||
245
engine/3d/src/LineSegment.cpp
Normal file
245
engine/3d/src/LineSegment.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
@file LineSegment.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-08
|
||||
@edited 2008-02-02
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "LineSegment.hpp"
|
||||
#include "Sphere.hpp"
|
||||
#include "G3DDebug.hpp"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
118
engine/3d/src/LineSegment.hpp
Normal file
118
engine/3d/src/LineSegment.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
@file LineSegment.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-08
|
||||
@edited 2008-02-02
|
||||
*/
|
||||
|
||||
#ifndef G3D_LINESEGMENT_H
|
||||
#define G3D_LINESEGMENT_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
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())
|
||||
{
|
||||
}
|
||||
|
||||
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 G3D
|
||||
|
||||
|
||||
#endif
|
||||
743
engine/3d/src/Map2D.hpp
Normal file
743
engine/3d/src/Map2D.hpp
Normal file
@@ -0,0 +1,743 @@
|
||||
/**
|
||||
@file Map2D.h
|
||||
|
||||
More flexible support than provided by G3D::GImage.
|
||||
|
||||
@maintainer Morgan McGuire, morgan@cs.brown.edu
|
||||
@created 2004-10-10
|
||||
@edited 2009-03-24
|
||||
*/
|
||||
#ifndef G3D_Map2D_h
|
||||
#define G3D_Map2D_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "vectorMath.hpp"
|
||||
#include "Vector2int16.hpp"
|
||||
#include "ReferenceCount.hpp"
|
||||
#include "AtomicInt32.hpp"
|
||||
#include "GThread.hpp"
|
||||
#include "Rect2D.hpp"
|
||||
#include "WrapMode.hpp"
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
} // namespace _internal
|
||||
} // namespace G3D
|
||||
|
||||
// This weird syntax is needed to support VC6, which doesn't
|
||||
// properly implement template overloading.
|
||||
#define DECLARE_COMPUTE_TYPE(StorageType, ComputeType) \
|
||||
namespace G3D \
|
||||
{ \
|
||||
namespace _internal \
|
||||
{ \
|
||||
template<> \
|
||||
class _GetComputeType<StorageType> \
|
||||
{ \
|
||||
public: \
|
||||
typedef ComputeType Type; \
|
||||
}; \
|
||||
} \
|
||||
}
|
||||
|
||||
DECLARE_COMPUTE_TYPE(float32, float64)
|
||||
DECLARE_COMPUTE_TYPE(float64, float64)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(int8, float32)
|
||||
DECLARE_COMPUTE_TYPE(int16, float32)
|
||||
DECLARE_COMPUTE_TYPE(int32, float64)
|
||||
DECLARE_COMPUTE_TYPE(int64, float64)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(uint8, float32)
|
||||
DECLARE_COMPUTE_TYPE(uint16, float32)
|
||||
DECLARE_COMPUTE_TYPE(uint32, float64)
|
||||
DECLARE_COMPUTE_TYPE(uint64, float64)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(Vector2, Vector2)
|
||||
DECLARE_COMPUTE_TYPE(Vector2int16, Vector2)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(Vector3, Vector3)
|
||||
DECLARE_COMPUTE_TYPE(Vector3int16, Vector3)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(Vector4, Vector4)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(Color3, Color3)
|
||||
DECLARE_COMPUTE_TYPE(Color3uint8, Color3)
|
||||
|
||||
DECLARE_COMPUTE_TYPE(Color4, Color4)
|
||||
DECLARE_COMPUTE_TYPE(Color4uint8, Color4)
|
||||
#undef DECLARE_COMPUTE_TYPE
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Map of values across a discrete 2D plane. Can be thought of as a generic class for 2D images,
|
||||
allowing flexibility as to pixel format and convenient methods.
|
||||
In fact, the "pixels" can be any values
|
||||
on a grid that can be sensibly interpolated--RGB colors, scalars, 4D vectors, and so on.
|
||||
|
||||
Other "image" classes in G3D:
|
||||
|
||||
G3D::GImage - Supports file formats, fast, Color3uint8 and Color4uint8 formats. No interpolation.
|
||||
|
||||
G3D::Texture::Ref - Represents image on the graphics card (not directly readable on the CPU). Supports 2D, 3D, and a variety of interpolation
|
||||
methods, loads file formats.
|
||||
|
||||
G3D::Image3 - A subclass of Map2D<Color3> that supports image loading and saving and conversion to Texture.
|
||||
|
||||
G3D::Image4 - A subclass of Map2D<Color4> that supports image loading and saving and conversion to Texture.
|
||||
|
||||
G3D::Image3uint8 - A subclass of Map2D<Color3uint8> that supports image loading and saving and conversion to Texture.
|
||||
|
||||
G3D::Image4uint8 - A subclass of Map2D<Color4uint8> that supports image loading and saving and conversion to Texture.
|
||||
|
||||
There are two type parameters-- the first (@ Storage) is the type
|
||||
used to store the "pixel" values efficiently and
|
||||
the second (@a Compute) is
|
||||
the type operated on by computation. The Compute::Compute(Storage&) constructor
|
||||
is used to convert between storage and computation types.
|
||||
@a Storage is often an integer version of @a Compute, for example
|
||||
<code>Map2D<double, uint8></code>. By default, the computation type is:
|
||||
|
||||
<pre>
|
||||
Storage Computation
|
||||
|
||||
uint8 float32
|
||||
uint16 float32
|
||||
uint32 float64
|
||||
uint64 float64
|
||||
|
||||
int8 float32
|
||||
int16 float32
|
||||
int32 float64
|
||||
int64 float64
|
||||
|
||||
float32 float64
|
||||
float64 float64
|
||||
|
||||
Vector2 Vector2
|
||||
Vector2int16 Vector2
|
||||
|
||||
Vector3 Vector3
|
||||
Vector3int16 Vector3
|
||||
|
||||
Vector4 Vector4
|
||||
|
||||
Color3 Color3
|
||||
Color3uint8 Color3
|
||||
|
||||
Color4 Color4
|
||||
Color4uint8 Color4
|
||||
</pre>
|
||||
Any other storage type defaults to itself as the computation type.
|
||||
|
||||
The computation type can be any that
|
||||
supports lerp, +, -, *, /, and an empty constructor.
|
||||
|
||||
Assign value:
|
||||
|
||||
<code>im->set(x, y, 7);</code> or
|
||||
<code>im->get(x, y) = 7;</code>
|
||||
|
||||
Read value:
|
||||
|
||||
<code>int c = im(x, y);</code>
|
||||
|
||||
Can also sample with nearest neighbor, bilinear, and bicubic
|
||||
interpolation.
|
||||
|
||||
Sampling follows OpenGL conventions, where
|
||||
pixel values represent grid points and (0.5, 0.5) is half-way
|
||||
between two vertical and two horizontal grid points.
|
||||
To draw an image of dimensions w x h with nearest neighbor
|
||||
sampling, render pixels from [0, 0] to [w - 1, h - 1].
|
||||
|
||||
Under the WrapMode::CLAMP wrap mode, the value of bilinear interpolation
|
||||
becomes constant outside [1, w - 2] horizontally. Nearest neighbor
|
||||
interpolation is constant outside [0, w - 1] and bicubic outside
|
||||
[3, w - 4]. The class does not offer quadratic interpolation because
|
||||
the interpolation filter could not center over a pixel.
|
||||
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
*/
|
||||
template<typename Storage, typename Compute = typename G3D::_internal::_GetComputeType<Storage>::Type>
|
||||
class Map2D : public ReferenceCountedObject
|
||||
{
|
||||
|
||||
//
|
||||
// It doesn't make sense to automatically convert from Compute back to Storage
|
||||
// because the rounding rule (and scaling) is application dependent.
|
||||
// Thus the interpolation methods all return type Compute.
|
||||
//
|
||||
|
||||
public:
|
||||
typedef Storage StorageType;
|
||||
typedef Compute ComputeType;
|
||||
typedef Map2D<Storage, Compute> Type;
|
||||
typedef ReferenceCountedPointer<Map2D> Ref;
|
||||
|
||||
protected:
|
||||
Storage ZERO;
|
||||
|
||||
/** Width, in pixels. */
|
||||
uint32 w;
|
||||
|
||||
/** Height, in pixels. */
|
||||
uint32 h;
|
||||
|
||||
WrapMode _wrapMode;
|
||||
|
||||
/** 0 if no mutating method has been invoked
|
||||
since the last call to setChanged(); */
|
||||
AtomicInt32 m_changed;
|
||||
|
||||
Array<Storage> data;
|
||||
|
||||
/** Handles the exceptional cases from get */
|
||||
const Storage& slowGet(int x, int y, WrapMode wrap)
|
||||
{
|
||||
switch (wrap)
|
||||
{
|
||||
case WrapMode::CLAMP:
|
||||
return fastGet(iClamp(x, 0, w - 1), iClamp(y, 0, h - 1));
|
||||
|
||||
case WrapMode::TILE:
|
||||
return fastGet(iWrap(x, w), iWrap(y, h));
|
||||
|
||||
case WrapMode::ZERO:
|
||||
return ZERO;
|
||||
|
||||
case WrapMode::ERROR:
|
||||
alwaysAssertM(((uint32)x < w) && ((uint32)y < h), format("Index out of bounds: (%d, %d), w = %d, h = %d", x, y, w, h));
|
||||
|
||||
// intentionally fall through
|
||||
case WrapMode::IGNORE:
|
||||
// intentionally fall through
|
||||
default:
|
||||
{
|
||||
static Storage temp;
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */
|
||||
inline const Storage& fastGet(int x, int y) const
|
||||
{
|
||||
debugAssert(((uint32)x < w) && ((uint32)y < h));
|
||||
return data[x + y * w];
|
||||
}
|
||||
|
||||
/** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */
|
||||
inline void fastSet(int x, int y, const Storage& v)
|
||||
{
|
||||
debugAssert(((uint32)x < w) && ((uint32)y < h));
|
||||
data[x + y * w] = v;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Given four control points and a value on the range [0, 1)
|
||||
evaluates the Catmull-rom spline between the times of the
|
||||
middle two control points */
|
||||
Compute bicubic(const Compute* ctrl, double s) const
|
||||
{
|
||||
|
||||
// f = B * S * ctrl'
|
||||
|
||||
// B matrix: Catmull-Rom spline basis
|
||||
static const double B[4][4] = {{0.0, -0.5, 1.0, -0.5}, {1.0, 0.0, -2.5, 1.5}, {0.0, 0.5, 2.0, -1.5}, {0.0, 0.0, -0.5, 0.5}};
|
||||
|
||||
// S: Powers of the fraction
|
||||
double S[4];
|
||||
double s2 = s * s;
|
||||
S[0] = 1.0;
|
||||
S[1] = s;
|
||||
S[2] = s2;
|
||||
S[3] = s2 * s;
|
||||
|
||||
Compute sum(ZERO);
|
||||
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
double coeff = 0.0;
|
||||
for (int power = 0; power < 4; ++power)
|
||||
{
|
||||
coeff += B[c][power] * S[power];
|
||||
}
|
||||
sum += ctrl[c] * coeff;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
Map2D(int w, int h, WrapMode wrap)
|
||||
: w(0)
|
||||
, h(0)
|
||||
, _wrapMode(wrap)
|
||||
, m_changed(1)
|
||||
{
|
||||
ZERO = Storage(Compute(Storage()) * 0);
|
||||
resize(w, h);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
Although Map2D is not threadsafe (except for the setChanged() method),
|
||||
you can use this mutex to create your own threadsafe access to a Map2D.
|
||||
Not used by the default implementation.
|
||||
*/
|
||||
GMutex mutex;
|
||||
|
||||
static Ref create(int w = 0, int h = 0, WrapMode wrap = WrapMode::ERROR)
|
||||
{
|
||||
return new Map2D(w, h, wrap);
|
||||
}
|
||||
|
||||
/** Resizes without clearing, leaving garbage.
|
||||
*/
|
||||
void resize(uint32 newW, uint32 newH)
|
||||
{
|
||||
if ((newW != w) || (newH != h))
|
||||
{
|
||||
w = newW;
|
||||
h = newH;
|
||||
data.resize(w * h);
|
||||
setChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if this map has been written to since the last call to setChanged(false).
|
||||
This is useful if you are caching a texture map other value that must be recomputed
|
||||
whenever this changes.
|
||||
*/
|
||||
bool changed()
|
||||
{
|
||||
return m_changed.value() != 0;
|
||||
}
|
||||
|
||||
/** Set/unset the changed flag. */
|
||||
void setChanged(bool c)
|
||||
{
|
||||
m_changed = c ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the underlying row-major data. There is no padding at the end of the row.
|
||||
Be careful--this will be reallocated during a resize. You should call setChanged(true) if you mutate the array.*/
|
||||
Storage* getCArray()
|
||||
{
|
||||
return data.getCArray();
|
||||
}
|
||||
|
||||
|
||||
const Storage* getCArray() const
|
||||
{
|
||||
return data.getCArray();
|
||||
}
|
||||
|
||||
|
||||
/** Row-major array. You should call setChanged(true) if you mutate the array. */
|
||||
Array<Storage>& getArray()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
const Array<Storage>& getArray() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
/** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */
|
||||
inline bool inBounds(int x, int y) const
|
||||
{
|
||||
return (((uint32)x < w) && ((uint32)y < h));
|
||||
}
|
||||
|
||||
/** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */
|
||||
inline bool inBounds(const Vector2int16& v) const
|
||||
{
|
||||
return inBounds(v.x, v.y);
|
||||
}
|
||||
|
||||
/** Get the value at (x, y).
|
||||
|
||||
Note that the type of image->get(x, y) is
|
||||
the storage type, not the computation
|
||||
type. If the constructor promoting Storage to Compute rescales values
|
||||
(as, for example Color3(Color3uint8&) does), this will not match the value
|
||||
returned by Map2D::nearest.
|
||||
*/
|
||||
inline const Storage& get(int x, int y, WrapMode wrap) const
|
||||
{
|
||||
if (((uint32)x < w) && ((uint32)y < h))
|
||||
{
|
||||
return data[x + y * w];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove the const to allow a slowGet on this object
|
||||
// (we're returning a const reference so this is ok)
|
||||
return const_cast<Type*>(this)->slowGet(x, y, wrap);
|
||||
}
|
||||
#ifndef G3D_WIN32
|
||||
// gcc gives a useless warning that the above code might reach the end of the function;
|
||||
// we use this line to supress the warning.
|
||||
return ZERO;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const Storage& get(int x, int y) const
|
||||
{
|
||||
return get(x, y, _wrapMode);
|
||||
}
|
||||
|
||||
inline const Storage& get(const Vector2int16& p) const
|
||||
{
|
||||
return get(p.x, p.y, _wrapMode);
|
||||
}
|
||||
|
||||
inline const Storage& get(const Vector2int16& p, WrapMode wrap) const
|
||||
{
|
||||
return get(p.x, p.y, wrap);
|
||||
}
|
||||
|
||||
inline Storage& get(int x, int y, WrapMode wrap)
|
||||
{
|
||||
return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y, wrap));
|
||||
#ifndef G3D_WIN32
|
||||
// gcc gives a useless warning that the above code might reach the end of the function;
|
||||
// we use this line to supress the warning.
|
||||
return ZERO;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline Storage& get(int x, int y)
|
||||
{
|
||||
return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y));
|
||||
#ifndef G3D_WIN32
|
||||
// gcc gives a useless warning that the above code might reach the end of the function;
|
||||
// we use this line to supress the warning.
|
||||
return ZERO;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline Storage& get(const Vector2int16& p)
|
||||
{
|
||||
return get(p.x, p.y);
|
||||
}
|
||||
|
||||
/** Sets the changed flag to true */
|
||||
inline void set(const Vector2int16& p, const Storage& v)
|
||||
{
|
||||
set(p.x, p.y, v);
|
||||
}
|
||||
|
||||
/** Sets the changed flag to true */
|
||||
void set(int x, int y, const Storage& v, WrapMode wrap)
|
||||
{
|
||||
setChanged(true);
|
||||
if (((uint32)x < w) && ((uint32)y < h))
|
||||
{
|
||||
// In bounds, wrapping isn't an issue.
|
||||
data[x + y * w] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
const_cast<Storage&>(slowGet(x, y, wrap)) = v;
|
||||
}
|
||||
}
|
||||
|
||||
void set(int x, int y, const Storage& v)
|
||||
{
|
||||
set(x, y, v, _wrapMode);
|
||||
}
|
||||
|
||||
|
||||
void setAll(const Storage& v)
|
||||
{
|
||||
for (int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
data[i] = v;
|
||||
}
|
||||
setChanged(true);
|
||||
}
|
||||
|
||||
/** flips if @a flip is true*/
|
||||
void maybeFlipVertical(bool flip)
|
||||
{
|
||||
if (flip)
|
||||
{
|
||||
flipVertical();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void flipVertical()
|
||||
{
|
||||
int halfHeight = h / 2;
|
||||
Storage* d = data.getCArray();
|
||||
for (int y = 0; y < halfHeight; ++y)
|
||||
{
|
||||
int o1 = y * w;
|
||||
int o2 = (h - y - 1) * w;
|
||||
for (int x = 0; x < (int)w; ++x)
|
||||
{
|
||||
int i1 = o1 + x;
|
||||
int i2 = o2 + x;
|
||||
Storage temp = d[i1];
|
||||
d[i1] = d[i2];
|
||||
d[i2] = temp;
|
||||
}
|
||||
}
|
||||
setChanged(true);
|
||||
}
|
||||
|
||||
virtual void flipHorizontal()
|
||||
{
|
||||
int halfWidth = w / 2;
|
||||
Storage* d = data.getCArray();
|
||||
for (int x = 0; x < halfWidth; ++x)
|
||||
{
|
||||
for (int y = 0; y < (int)h; ++y)
|
||||
{
|
||||
int i1 = y * w + x;
|
||||
int i2 = y * w + (w - x - 1);
|
||||
Storage temp = d[i1];
|
||||
d[i1] = d[i2];
|
||||
d[i2] = temp;
|
||||
}
|
||||
}
|
||||
setChanged(true);
|
||||
}
|
||||
|
||||
/**
|
||||
Crops this map so that it only contains pixels between (x, y) and (x + w - 1, y + h - 1) inclusive.
|
||||
*/
|
||||
virtual void crop(int newX, int newY, int newW, int newH)
|
||||
{
|
||||
alwaysAssertM(newX + newW <= (int)w, "Cannot grow when cropping");
|
||||
alwaysAssertM(newY + newH <= (int)h, "Cannot grow when cropping");
|
||||
alwaysAssertM(newX >= 0 && newY >= 0, "Origin out of bounds.");
|
||||
|
||||
// Always safe to copy towards the upper left, provided
|
||||
// that we're iterating towards the lower right. This lets us avoid
|
||||
// reallocating the underlying array.
|
||||
for (int y = 0; y < newH; ++y)
|
||||
{
|
||||
for (int x = 0; x < newW; ++x)
|
||||
{
|
||||
data[x + y * newW] = data[(x + newX) + (y + newY) * w];
|
||||
}
|
||||
}
|
||||
|
||||
resize(newW, newH);
|
||||
}
|
||||
|
||||
/** iRounds to the nearest x0 and y0. */
|
||||
virtual void crop(const Rect2D& rect)
|
||||
{
|
||||
crop(iRound(rect.x0()), iRound(rect.y0()), iRound(rect.x1()) - iRound(rect.x0()), iRound(rect.y1()) - iRound(rect.y0()));
|
||||
}
|
||||
|
||||
/** Returns the nearest neighbor. Pixel values are considered
|
||||
to be at the upper left corner, so <code>image->nearest(x, y) == image(x, y)</code>
|
||||
*/
|
||||
inline Compute nearest(float x, float y, WrapMode wrap) const
|
||||
{
|
||||
int ix = iRound(x);
|
||||
int iy = iRound(y);
|
||||
return Compute(get(ix, iy, wrap));
|
||||
}
|
||||
|
||||
inline Compute nearest(float x, float y) const
|
||||
{
|
||||
return nearest(x, y, _wrapMode);
|
||||
}
|
||||
|
||||
inline Compute nearest(const Vector2& p) const
|
||||
{
|
||||
return nearest(p.x, p.y);
|
||||
}
|
||||
|
||||
/** Returns the average value of all elements of the map */
|
||||
Compute average() const
|
||||
{
|
||||
if ((w == 0) || (h == 0))
|
||||
{
|
||||
return ZERO;
|
||||
}
|
||||
|
||||
// To avoid overflows, compute the average of row averages
|
||||
|
||||
Compute rowSum = ZERO;
|
||||
for (unsigned int y = 0; y < h; ++y)
|
||||
{
|
||||
Compute sum = ZERO;
|
||||
int offset = y * w;
|
||||
for (unsigned int x = 0; x < w; ++x)
|
||||
{
|
||||
sum += Compute(data[offset + x]);
|
||||
}
|
||||
rowSum += sum * (1.0f / w);
|
||||
}
|
||||
|
||||
return rowSum * (1.0f / h);
|
||||
}
|
||||
|
||||
/**
|
||||
Needs to access elements from (floor(x), floor(y))
|
||||
to (floor(x) + 1, floor(y) + 1) and will use
|
||||
the wrap mode appropriately (possibly generating
|
||||
out of bounds errors).
|
||||
|
||||
Guaranteed to match nearest(x, y) at integers. */
|
||||
Compute bilinear(float x, float y, WrapMode wrap) const
|
||||
{
|
||||
const int i = iFloor(x);
|
||||
const int j = iFloor(y);
|
||||
|
||||
const float fX = x - i;
|
||||
const float fY = y - j;
|
||||
|
||||
// Horizontal interpolation, first row
|
||||
const Compute& t0 = get(i, j, wrap);
|
||||
const Compute& t1 = get(i + 1, j, wrap);
|
||||
|
||||
// Horizontal interpolation, second row
|
||||
const Compute& t2 = get(i, j + 1, wrap);
|
||||
const Compute& t3 = get(i + 1, j + 1, wrap);
|
||||
|
||||
const Compute& A = lerp(t0, t1, fX);
|
||||
const Compute& B = lerp(t2, t3, fX);
|
||||
|
||||
// Vertical interpolation
|
||||
return lerp(A, B, fY);
|
||||
}
|
||||
|
||||
Compute bilinear(float x, float y) const
|
||||
{
|
||||
return bilinear(x, y, _wrapMode);
|
||||
}
|
||||
|
||||
inline Compute bilinear(const Vector2& p) const
|
||||
{
|
||||
return bilinear(p.x, p.y, _wrapMode);
|
||||
}
|
||||
|
||||
inline Compute bilinear(const Vector2& p, WrapMode wrap) const
|
||||
{
|
||||
return bilinear(p.x, p.y, wrap);
|
||||
}
|
||||
|
||||
/**
|
||||
Uses Catmull-Rom splines to interpolate between grid
|
||||
values. Guaranteed to match nearest(x, y) at integers.
|
||||
*/
|
||||
Compute bicubic(float x, float y, WrapMode wrap) const
|
||||
{
|
||||
int i = iFloor(x);
|
||||
int j = iFloor(y);
|
||||
float fX = x - i;
|
||||
float fY = y - j;
|
||||
|
||||
Compute vsample[4];
|
||||
for (int v = 0; v < 4; ++v)
|
||||
{
|
||||
|
||||
// Horizontal interpolation
|
||||
Compute hsample[4];
|
||||
for (int u = 0; u < 4; ++u)
|
||||
{
|
||||
hsample[u] = Compute(get(i + u - 1, j + v - 1, wrap));
|
||||
}
|
||||
|
||||
vsample[v] = bicubic(hsample, fX);
|
||||
}
|
||||
|
||||
// Vertical interpolation
|
||||
return bicubic(vsample, fY);
|
||||
}
|
||||
|
||||
Compute bicubic(float x, float y) const
|
||||
{
|
||||
return bicubic(x, y, _wrapMode);
|
||||
}
|
||||
|
||||
inline Compute bicubic(const Vector2& p, WrapMode wrap) const
|
||||
{
|
||||
return bicubic(p.x, p.y, wrap);
|
||||
}
|
||||
|
||||
inline Compute bicubic(const Vector2& p) const
|
||||
{
|
||||
return bicubic(p.x, p.y, _wrapMode);
|
||||
}
|
||||
|
||||
/** Pixel width */
|
||||
inline int32 width() const
|
||||
{
|
||||
return (int32)w;
|
||||
}
|
||||
|
||||
|
||||
/** Pixel height */
|
||||
inline int32 height() const
|
||||
{
|
||||
return (int32)h;
|
||||
}
|
||||
|
||||
|
||||
/** Dimensions in pixels */
|
||||
Vector2int16 size() const
|
||||
{
|
||||
return Vector2int16(w, h);
|
||||
}
|
||||
|
||||
/** Rectangle from (0, 0) to (w, h) */
|
||||
Rect2D rect2DBounds() const
|
||||
{
|
||||
return Rect2D::xywh(0, 0, w, h);
|
||||
}
|
||||
|
||||
/** Number of bytes occupied by the image data and this structure */
|
||||
size_t sizeInMemory() const
|
||||
{
|
||||
return data.size() * sizeof(Storage) + sizeof(*this);
|
||||
}
|
||||
|
||||
|
||||
WrapMode wrapMode() const
|
||||
{
|
||||
return _wrapMode;
|
||||
}
|
||||
|
||||
|
||||
void setWrapMode(WrapMode m)
|
||||
{
|
||||
_wrapMode = m;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif // G3D_IMAGE_H
|
||||
83
engine/3d/src/Matrix2.hpp
Normal file
83
engine/3d/src/Matrix2.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef G3D_Matrix2_h
|
||||
#define G3D_Matrix2_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector2.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/** @beta */
|
||||
class Matrix2
|
||||
{
|
||||
private:
|
||||
float data[2][2];
|
||||
|
||||
public:
|
||||
Matrix2()
|
||||
{
|
||||
data[0][0] = 1.0f;
|
||||
data[0][1] = 0.0f;
|
||||
data[1][0] = 0.0f;
|
||||
data[1][1] = 1.0f;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static Matrix2 identity()
|
||||
{
|
||||
return Matrix2(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
Vector2 operator*(const Vector2& v) const
|
||||
{
|
||||
return Vector2(data[0][0] * v[0] + data[0][1] * v[1], data[1][0] * v[0] + data[1][1] * v[1]);
|
||||
}
|
||||
|
||||
Matrix2 inverse() const
|
||||
{
|
||||
return Matrix2(data[1][1], -data[0][1], -data[1][0], data[0][0]) * (1.0f / determinant());
|
||||
}
|
||||
|
||||
Matrix2 transpose() const
|
||||
{
|
||||
return Matrix2(data[0][0], data[1][0], data[0][1], data[1][1]);
|
||||
}
|
||||
|
||||
float determinant() const
|
||||
{
|
||||
return data[0][0] * data[1][1] - data[0][1] * data[1][0];
|
||||
}
|
||||
|
||||
Matrix2 operator*(float f) const
|
||||
{
|
||||
return Matrix2(data[0][0] * f, data[0][1] * f, data[1][0] * f, data[1][1] * f);
|
||||
}
|
||||
|
||||
Matrix2 operator/(float f) const
|
||||
{
|
||||
return Matrix2(data[0][0] / f, data[0][1] / f, data[1][0] / f, data[1][1] / f);
|
||||
}
|
||||
|
||||
float* operator[](int i)
|
||||
{
|
||||
debugAssert(i >= 0 && i <= 2);
|
||||
return data[i];
|
||||
}
|
||||
|
||||
const float* operator[](int i) const
|
||||
{
|
||||
debugAssert(i >= 0 && i <= 1);
|
||||
return data[i];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
1925
engine/3d/src/Matrix3.cpp
Normal file
1925
engine/3d/src/Matrix3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
398
engine/3d/src/Matrix3.hpp
Normal file
398
engine/3d/src/Matrix3.hpp
Normal file
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
@file Matrix3.h
|
||||
|
||||
3x3 matrix class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2006-04-05
|
||||
*/
|
||||
|
||||
#ifndef G3D_Matrix3_h
|
||||
#define G3D_Matrix3_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "debugAssert.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Turn off "conditional expression is constant" warning; MSVC generates this
|
||||
// for debug assertions in inlined methods.
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
/**
|
||||
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(const float aafEntry[3][3]);
|
||||
|
||||
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);
|
||||
|
||||
/** Returns true if column(0).cross(column(1)).dot(column(2)) > 0. */
|
||||
bool isRightHanded() const;
|
||||
|
||||
/**
|
||||
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];
|
||||
}
|
||||
|
||||
Vector3 column(int c) const;
|
||||
|
||||
const Vector3& row(int iRow) const
|
||||
{
|
||||
debugAssert((0 <= iRow) && (iRow < 3));
|
||||
return *reinterpret_cast<const Vector3*>(elt[iRow]);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
static inline Matrix3 fillRotation(const Vector3& axis)
|
||||
{
|
||||
float angle = axis.magnitude();
|
||||
if (angle <= 0.0f)
|
||||
{
|
||||
return Matrix3::identity();
|
||||
}
|
||||
Vector3 normalAxis = 1.0f / angle * axis;
|
||||
|
||||
return Matrix3::fromAxisAngle(normalAxis, angle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
Matrix3& operator*=(float k);
|
||||
Matrix3& operator/=(float k);
|
||||
|
||||
|
||||
private:
|
||||
/** Multiplication where out != A and out != B */
|
||||
static void _mul(const Matrix3& A, const Matrix3& B, Matrix3& out);
|
||||
|
||||
public:
|
||||
// Aya
|
||||
inline static void fastMul(const Matrix3& A, const Matrix3& B, Matrix3& out)
|
||||
{
|
||||
_mul(A, B, out);
|
||||
}
|
||||
// ======
|
||||
|
||||
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
Polar decomposition of a matrix. Based on pseudocode from Nicholas J
|
||||
Higham, "Computing the Polar Decomposition -- with Applications Siam
|
||||
Journal of Science and Statistical Computing, Vol 7, No. 4, October
|
||||
1986.
|
||||
|
||||
Decomposes A into R*S, where R is orthogonal and S is symmetric.
|
||||
|
||||
Ken Shoemake's "Matrix animation and polar decomposition"
|
||||
in Proceedings of the conference on Graphics interface '92
|
||||
seems to be better known in the world of graphics, but Higham's version
|
||||
uses a scaling constant that can lead to faster convergence than
|
||||
Shoemake's when the initial matrix is far from orthogonal.
|
||||
*/
|
||||
void polarDecomposition(Matrix3& R, Matrix3& S) const;
|
||||
|
||||
/**
|
||||
* Matrix norms.
|
||||
*/
|
||||
float spectralNorm() const;
|
||||
|
||||
float squaredFrobeniusNorm() const;
|
||||
|
||||
float frobeniusNorm() const;
|
||||
|
||||
float l1Norm() const;
|
||||
|
||||
float lInfNorm() const;
|
||||
|
||||
float diffOneNorm(const Matrix3& y) const;
|
||||
|
||||
/** matrix must be orthonormal */
|
||||
void toAxisAngle(Vector3& rkAxis, float& rfRadians) const;
|
||||
|
||||
static Matrix3 fromDiagonal(const Vector3& d)
|
||||
{
|
||||
return Matrix3(d.x, 0, 0, 0, d.y, 0, 0, 0, d.z);
|
||||
}
|
||||
|
||||
static Matrix3 fromAxisAngleFast(const Vector3& _normalizedAxis, float fRadians);
|
||||
|
||||
// Aya
|
||||
static Matrix3 fromAxisAngle(const Vector3& _axis, float fRadians)
|
||||
{
|
||||
return fromAxisAngleFast(_axis.direction(), 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;
|
||||
|
||||
// Aya NOTE:
|
||||
// The folowing G3D comment is wrong. Arseny prove this by compiling code
|
||||
// and checking the pointers are the same, also by reading assembly. I keep the comment here
|
||||
// for the future generations.
|
||||
|
||||
// *** Original G3D comment ***
|
||||
// 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 <20><><EFBFBD><EFBFBD> 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/
|
||||
// *** End of G3D comment ***
|
||||
|
||||
static const Matrix3& zero()
|
||||
{
|
||||
static Matrix3 m(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
static const Matrix3& identity()
|
||||
{
|
||||
static Matrix3 m(1, 0, 0, 0, 1, 0, 0, 0, 1);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
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 G3D
|
||||
|
||||
#endif
|
||||
480
engine/3d/src/Matrix4.cpp
Normal file
480
engine/3d/src/Matrix4.cpp
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
@file Matrix4.cpp
|
||||
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-10-02
|
||||
@edited 2010-01-29
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Matrix4.hpp"
|
||||
#include "Matrix3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Rect2D.hpp"
|
||||
|
||||
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, float upDirection)
|
||||
{
|
||||
return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval, upDirection);
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::orthogonalProjection(float left, float right, float bottom, float top, float nearval, float farval, float upDirection)
|
||||
{
|
||||
|
||||
// Adapted from Mesa. Note that Microsoft (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_8qnj.asp)
|
||||
// and Linux (http://www.xfree86.org/current/glOrtho.3.html) have different matrices shown in their documentation.
|
||||
|
||||
float x, y, z;
|
||||
float tx, ty, tz;
|
||||
|
||||
x = 2.0f / (right - left);
|
||||
y = 2.0f / (top - bottom);
|
||||
z = -2.0f / (farval - nearval);
|
||||
tx = -(right + left) / (right - left);
|
||||
ty = -(top + bottom) / (top - bottom);
|
||||
tz = -(farval + nearval) / (farval - nearval);
|
||||
|
||||
y *= upDirection;
|
||||
ty *= upDirection;
|
||||
|
||||
return Matrix4(x, 0.0f, 0.0f, tx, 0.0f, y, 0.0f, ty, 0.0f, 0.0f, z, tz, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::perspectiveProjection(float left, float right, float bottom, float top, float nearval, float farval, float upDirection)
|
||||
{
|
||||
|
||||
float x, y, a, b, c, d;
|
||||
|
||||
x = (2.0f * nearval) / (right - left);
|
||||
y = (2.0f * nearval) / (top - bottom);
|
||||
a = (right + left) / (right - left);
|
||||
b = (top + bottom) / (top - bottom);
|
||||
|
||||
if (farval >= finf())
|
||||
{
|
||||
// Infinite view frustum
|
||||
c = -1.0f;
|
||||
d = -2.0f * nearval;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = -(farval + nearval) / (farval - nearval);
|
||||
d = -(2.0f * farval * nearval) / (farval - nearval);
|
||||
}
|
||||
|
||||
debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1");
|
||||
y *= upDirection;
|
||||
b *= upDirection;
|
||||
|
||||
return Matrix4(x, 0, a, 0, 0, y, b, 0, 0, 0, c, d, 0, 0, -1, 0);
|
||||
}
|
||||
|
||||
|
||||
void Matrix4::getPerspectiveProjectionParameters(
|
||||
float& left, float& right, float& bottom, float& top, float& nearval, float& farval, float upDirection) const
|
||||
{
|
||||
|
||||
debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1");
|
||||
|
||||
float x = elt[0][0];
|
||||
float y = elt[1][1] * upDirection;
|
||||
float a = elt[0][2];
|
||||
float b = elt[1][2] * upDirection;
|
||||
float c = elt[2][2];
|
||||
float d = elt[2][3];
|
||||
|
||||
// Verify that this really is a projection matrix
|
||||
debugAssertM(elt[3][2] == -1, "Not a projection matrix");
|
||||
debugAssertM(elt[0][1] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[0][3] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[1][3] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[3][3] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[1][0] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[2][0] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[2][1] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[3][0] == 0, "Not a projection matrix");
|
||||
debugAssertM(elt[3][1] == 0, "Not a projection matrix");
|
||||
|
||||
if (c == -1)
|
||||
{
|
||||
farval = finf();
|
||||
nearval = -d / 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
nearval = d * ((c - 1.0f) / (c + 1.0f) - 1.0f) / (-2.0f * (c - 1.0f) / (c + 1.0f));
|
||||
farval = nearval * ((c - 1.0f) / (c + 1.0f));
|
||||
}
|
||||
|
||||
|
||||
left = (a - 1.0f) * nearval / x;
|
||||
right = 2.0f * nearval / x + left;
|
||||
|
||||
bottom = (b - 1.0f) * nearval / y;
|
||||
top = 2.0f * nearval / y + bottom;
|
||||
}
|
||||
|
||||
|
||||
Matrix4::Matrix4(float r1c1, float r1c2, float r1c3, float r1c4, float r2c1, float r2c2, float r2c3, float r2c4, float r3c1, float r3c2, float r3c3,
|
||||
float r3c4, float r4c1, float r4c2, float r4c3, float r4c4)
|
||||
{
|
||||
elt[0][0] = r1c1;
|
||||
elt[0][1] = r1c2;
|
||||
elt[0][2] = r1c3;
|
||||
elt[0][3] = r1c4;
|
||||
elt[1][0] = r2c1;
|
||||
elt[1][1] = r2c2;
|
||||
elt[1][2] = r2c3;
|
||||
elt[1][3] = r2c4;
|
||||
elt[2][0] = r3c1;
|
||||
elt[2][1] = r3c2;
|
||||
elt[2][2] = r3c3;
|
||||
elt[2][3] = r3c4;
|
||||
elt[3][0] = r4c1;
|
||||
elt[3][1] = r4c2;
|
||||
elt[3][2] = r4c3;
|
||||
elt[3][3] = r4c4;
|
||||
}
|
||||
|
||||
/**
|
||||
init should be <B>row major</B>.
|
||||
*/
|
||||
Matrix4::Matrix4(const float* init)
|
||||
{
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
elt[r][c] = init[r * 4 + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Matrix4::Matrix4(const double* init)
|
||||
{
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
elt[r][c] = (float)init[r * 4 + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Matrix4::Matrix4()
|
||||
{
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
elt[r][c] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Matrix4::setRow(int r, const Vector4& v)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
elt[r][c] = v[c];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Matrix4::setColumn(int c, const Vector4& v)
|
||||
{
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
elt[r][c] = v[r];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Vector4& Matrix4::row(int r) const
|
||||
{
|
||||
return reinterpret_cast<const Vector4*>(elt[r])[0];
|
||||
}
|
||||
|
||||
|
||||
Vector4 Matrix4::column(int c) const
|
||||
{
|
||||
Vector4 v;
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
v[r] = elt[r][c];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::operator*(const Matrix4& other) const
|
||||
{
|
||||
Matrix4 result;
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
result.elt[r][c] += elt[r][i] * other.elt[i][c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::operator*(const float s) const
|
||||
{
|
||||
Matrix4 result;
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
result.elt[r][c] = elt[r][c] * s;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Vector3 Matrix4::homoMul(const class Vector3& v, float w) const
|
||||
{
|
||||
Vector4 r = (*this) * Vector4(v, w);
|
||||
return r.xyz() * (1.0f / r.w);
|
||||
}
|
||||
|
||||
|
||||
Vector4 Matrix4::operator*(const Vector4& vector) const
|
||||
{
|
||||
Vector4 result(0, 0, 0, 0);
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
result[r] += elt[r][c] * vector[c];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::transpose() const
|
||||
{
|
||||
Matrix4 result;
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
result.elt[c][r] = elt[r][c];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Matrix4::operator!=(const Matrix4& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
bool Matrix4::operator==(const Matrix4& other) const
|
||||
{
|
||||
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
if (elt[r][c] != other.elt[r][c])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
float Matrix4::determinant() const
|
||||
{
|
||||
// Determinant is the dot product of the first row and the first row
|
||||
// of cofactors (i.e. the first col of the adjoint matrix)
|
||||
return cofactor().row(0).dot(row(0));
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::adjoint() const
|
||||
{
|
||||
return cofactor().transpose();
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::inverse() const
|
||||
{
|
||||
// Inverse = adjoint / determinant
|
||||
|
||||
Matrix4 A = adjoint();
|
||||
|
||||
// Determinant is the dot product of the first row and the first row
|
||||
// of cofactors (i.e. the first col of the adjoint matrix)
|
||||
float det = A.column(0).dot(row(0));
|
||||
|
||||
return A * (1.0f / det);
|
||||
}
|
||||
|
||||
|
||||
Matrix4 Matrix4::cofactor() const
|
||||
{
|
||||
Matrix4 out;
|
||||
|
||||
// We'll use i to incrementally compute -1 ^ (r+c)
|
||||
int i = 1;
|
||||
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
// Compute the determinant of the 3x3 submatrix
|
||||
float det = subDeterminant(r, c);
|
||||
out.elt[r][c] = i * det;
|
||||
i = -i;
|
||||
}
|
||||
i = -i;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
float Matrix4::subDeterminant(int excludeRow, int excludeCol) const
|
||||
{
|
||||
// Compute non-excluded row and column indices
|
||||
int row[3];
|
||||
int col[3];
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
row[i] = i;
|
||||
col[i] = i;
|
||||
|
||||
if (i >= excludeRow)
|
||||
{
|
||||
++row[i];
|
||||
}
|
||||
if (i >= excludeCol)
|
||||
{
|
||||
++col[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the first row of cofactors
|
||||
float cofactor00 = elt[row[1]][col[1]] * elt[row[2]][col[2]] - elt[row[1]][col[2]] * elt[row[2]][col[1]];
|
||||
|
||||
float cofactor10 = elt[row[1]][col[2]] * elt[row[2]][col[0]] - elt[row[1]][col[0]] * elt[row[2]][col[2]];
|
||||
|
||||
float cofactor20 = elt[row[1]][col[0]] * elt[row[2]][col[1]] - elt[row[1]][col[1]] * elt[row[2]][col[0]];
|
||||
|
||||
// Product of the first row and the cofactors along the first row
|
||||
return elt[row[0]][col[0]] * cofactor00 + elt[row[0]][col[1]] * cofactor10 + elt[row[0]][col[2]] * cofactor20;
|
||||
}
|
||||
|
||||
|
||||
CoordinateFrame Matrix4::approxCoordinateFrame() const
|
||||
{
|
||||
CoordinateFrame cframe;
|
||||
|
||||
for (int r = 0; r < 3; ++r)
|
||||
{
|
||||
for (int c = 0; c < 3; ++c)
|
||||
{
|
||||
cframe.rotation[r][c] = elt[r][c];
|
||||
}
|
||||
cframe.translation[r] = elt[r][3];
|
||||
}
|
||||
|
||||
// Ensure that the rotation matrix is orthonormal
|
||||
cframe.rotation.orthonormalize();
|
||||
|
||||
return cframe;
|
||||
}
|
||||
|
||||
|
||||
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 G3D
|
||||
233
engine/3d/src/Matrix4.hpp
Normal file
233
engine/3d/src/Matrix4.hpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
@file Matrix4.h
|
||||
|
||||
4x4 matrix class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-10-02
|
||||
@edited 2009-10-20
|
||||
*/
|
||||
|
||||
#ifndef G3D_Matrix4_h
|
||||
#define G3D_Matrix4_h
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable conditional expression is constant, which occurs incorrectly on inlined functions
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "debugAssert.hpp"
|
||||
#include "Matrix3.hpp"
|
||||
#include "Vector3.hpp"
|
||||
|
||||
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();
|
||||
|
||||
/** If this is a perspective projection matrix created by
|
||||
Matrix4::perspectiveProjection, extract its parameters. */
|
||||
void getPerspectiveProjectionParameters(
|
||||
float& left, float& right, float& bottom, float& top, float& nearval, float& farval, float updirection = -1.0f) const;
|
||||
|
||||
inline float* operator[](int r)
|
||||
{
|
||||
debugAssert(r >= 0);
|
||||
debugAssert(r < 4);
|
||||
return (float*)&elt[r];
|
||||
}
|
||||
|
||||
inline const float* operator[](int r) const
|
||||
{
|
||||
debugAssert(r >= 0);
|
||||
debugAssert(r < 4);
|
||||
return (const float*)&elt[r];
|
||||
}
|
||||
|
||||
inline operator float*()
|
||||
{
|
||||
return (float*)&elt[0][0];
|
||||
}
|
||||
|
||||
inline operator const float*() const
|
||||
{
|
||||
return (const float*)&elt[0][0];
|
||||
}
|
||||
|
||||
Matrix4 operator*(const Matrix4& other) const;
|
||||
Matrix4 operator+(const Matrix4& other) const
|
||||
{
|
||||
Matrix4 result;
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
result.elt[r][c] = elt[r][c] + other.elt[r][c];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class Matrix3 upper3x3() const;
|
||||
|
||||
/** Homogeneous multiplication. Let k = M * [v w]^T. result = k.xyz() / k.w */
|
||||
class Vector3 homoMul(const class Vector3& v, float w) const;
|
||||
|
||||
/**
|
||||
Constructs an orthogonal projection matrix from the given parameters.
|
||||
Near and far are the <b>NEGATIVE</b> of the near and far plane Z values
|
||||
(to follow OpenGL conventions).
|
||||
|
||||
\param upDirection Use -1.0 for 2D Y increasing downwards (the G3D 8.x default convention),
|
||||
1.0 for 2D Y increasing upwards (the G3D 7.x default and OpenGL convention)
|
||||
*/
|
||||
static Matrix4 orthogonalProjection(float left, float right, float bottom, float top, float nearval, float farval, float upDirection = -1.0f);
|
||||
|
||||
|
||||
/** \param upDirection Use -1.0 for 2D Y increasing downwards (the G3D 8.x default convention),
|
||||
1.0 for 2D Y increasing upwards (the G3D 7.x default and OpenGL convention)
|
||||
*/
|
||||
static Matrix4 orthogonalProjection(const class Rect2D& rect, float nearval, float farval, float upDirection = -1.0f);
|
||||
|
||||
/** \param upDirection Use -1.0 for 2D Y increasing downwards (the G3D 8.x default convention),
|
||||
1.0 for 2D Y increasing upwards (the G3D 7.x default and OpenGL convention)
|
||||
*/
|
||||
static Matrix4 perspectiveProjection(float left, float right, float bottom, float top, float nearval, float farval, float upDirection = -1.0f);
|
||||
|
||||
void setRow(int r, const class Vector4& v);
|
||||
void setColumn(int c, const Vector4& v);
|
||||
|
||||
const Vector4& row(int r) const;
|
||||
Vector4 column(int c) const;
|
||||
|
||||
Matrix4 operator*(const float s) const;
|
||||
Vector4 operator*(const Vector4& vector) const;
|
||||
|
||||
Matrix4 transpose() const;
|
||||
|
||||
bool operator!=(const Matrix4& other) const;
|
||||
bool operator==(const Matrix4& other) const;
|
||||
|
||||
float determinant() const;
|
||||
Matrix4 inverse() const;
|
||||
|
||||
/**
|
||||
Transpose of the cofactor matrix (used in computing the inverse).
|
||||
Note: This is in fact only one type of adjoint. More generally,
|
||||
an adjoint of a matrix is any mapping of a matrix which possesses
|
||||
certain properties. This returns the so-called adjugate
|
||||
or classical adjoint.
|
||||
*/
|
||||
Matrix4 adjoint() const;
|
||||
Matrix4 cofactor() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
/** 3D scale matrix */
|
||||
inline static Matrix4 scale(const Vector3& v)
|
||||
{
|
||||
return Matrix4(v.x, 0, 0, 0, 0, v.y, 0, 0, 0, 0, v.z, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/** 3D scale matrix */
|
||||
inline static Matrix4 scale(float x, float y, float z)
|
||||
{
|
||||
return scale(Vector3(x, y, z));
|
||||
}
|
||||
|
||||
/** 3D scale matrix */
|
||||
inline static Matrix4 scale(float s)
|
||||
{
|
||||
return scale(s, s, s);
|
||||
}
|
||||
|
||||
/** 3D translation matrix */
|
||||
inline static Matrix4 translation(const Vector3& v)
|
||||
{
|
||||
return Matrix4(Matrix3::identity(), v);
|
||||
}
|
||||
|
||||
inline static Matrix4 translation(float x, float y, float z)
|
||||
{
|
||||
return Matrix4(Matrix3::identity(), Vector3(x, y, z));
|
||||
}
|
||||
|
||||
/** Create a rotation matrix that rotates \a deg degrees around the Y axis */
|
||||
inline static Matrix4 yawDegrees(float deg)
|
||||
{
|
||||
return Matrix4(Matrix3::fromAxisAngle(Vector3::unitY(), toRadians(deg)));
|
||||
}
|
||||
|
||||
inline static Matrix4 pitchDegrees(float deg)
|
||||
{
|
||||
return Matrix4(Matrix3::fromAxisAngle(Vector3::unitX(), toRadians(deg)));
|
||||
}
|
||||
|
||||
inline static Matrix4 rollDegrees(float deg)
|
||||
{
|
||||
return Matrix4(Matrix3::fromAxisAngle(Vector3::unitZ(), toRadians(deg)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
48
engine/3d/src/MemoryManager.cpp
Normal file
48
engine/3d/src/MemoryManager.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
@file MemoryManager.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2009-04-20
|
||||
@edited 2009-05-29
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#include "MemoryManager.hpp"
|
||||
#include "System.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
MemoryManager::MemoryManager() {}
|
||||
|
||||
|
||||
void* MemoryManager::alloc(size_t s)
|
||||
{
|
||||
return System::malloc(s);
|
||||
}
|
||||
|
||||
|
||||
void MemoryManager::free(void* ptr)
|
||||
{
|
||||
System::free(ptr);
|
||||
}
|
||||
|
||||
|
||||
bool MemoryManager::isThreadsafe() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MemoryManager* MemoryManager::create()
|
||||
{
|
||||
static MemoryManager m;
|
||||
return &m;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
} // namespace G3D
|
||||
54
engine/3d/src/MemoryManager.hpp
Normal file
54
engine/3d/src/MemoryManager.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
@file MemoryManager.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2009-04-20
|
||||
@edited 2009-04-20
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
#ifndef G3D_MemoryManager_h
|
||||
#define G3D_MemoryManager_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "platform.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Abstraction of memory management.
|
||||
Default implementation uses G3D::System::malloc and is threadsafe.
|
||||
|
||||
\sa CRTMemoryManager, AlignedMemoryManager, AreaMemoryManager */
|
||||
class MemoryManager
|
||||
{
|
||||
protected:
|
||||
MemoryManager();
|
||||
|
||||
public:
|
||||
typedef MemoryManager* Ref;
|
||||
|
||||
/** Return a pointer to \a s bytes of memory that are unused by
|
||||
the rest of the program. The contents of the memory are
|
||||
undefined */
|
||||
virtual void* alloc(size_t s);
|
||||
|
||||
/** Invoke to declare that this memory will no longer be used by
|
||||
the program. The memory manager is not required to actually
|
||||
reuse or release this memory. */
|
||||
virtual void free(void* ptr);
|
||||
|
||||
/** Returns true if this memory manager is threadsafe (i.e., alloc
|
||||
and free can be called asychronously) */
|
||||
virtual bool isThreadsafe() const;
|
||||
|
||||
/** Return the instance. There's only one instance of the default
|
||||
MemoryManager; it is cached after the first creation. */
|
||||
static MemoryManager* create();
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
631
engine/3d/src/MeshAlg.hpp
Normal file
631
engine/3d/src/MeshAlg.hpp
Normal file
@@ -0,0 +1,631 @@
|
||||
/**
|
||||
@file MeshAlg.h
|
||||
|
||||
Indexed Mesh algorithms.
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-09-14
|
||||
@edited 2010-01-18
|
||||
*/
|
||||
|
||||
#ifndef G3D_MeshAlg_h
|
||||
#define G3D_MeshAlg_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "SmallArray.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "Image1.hpp"
|
||||
|
||||
#ifdef G3D_WIN32
|
||||
// Turn off "conditional expression is constant" warning; MSVC generates this
|
||||
// for debug assertions in inlined methods.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Indexed <B>mesh alg</B>orithms. You have to build your own mesh class.
|
||||
<P>
|
||||
No mesh class is provided with G3D because there isn't an "ideal"
|
||||
mesh format-- one application needs keyframed animation, another
|
||||
skeletal animation, a third texture coordinates, a fourth
|
||||
cannot precompute information, etc. Instead of compromising, this
|
||||
class implements the hard parts of mesh computation and you can write
|
||||
your own ideal mesh class on top of it.
|
||||
|
||||
\sa G3D::ArticulatedModel, G3D::IFSModel
|
||||
*/
|
||||
class MeshAlg
|
||||
{
|
||||
public:
|
||||
/** \deprecated */
|
||||
typedef PrimitiveType Primitive;
|
||||
|
||||
/** Adjacency information for a vertex.
|
||||
Does not contain the vertex position or normal,
|
||||
which are stored in the MeshAlg::Geometry object.
|
||||
<CODE>Vertex</CODE>s must be stored in an array
|
||||
parallel to (indexed in the same way as)
|
||||
MeshAlg::Geometry::vertexArray.
|
||||
*/
|
||||
class Vertex
|
||||
{
|
||||
public:
|
||||
Vertex() {}
|
||||
|
||||
/**
|
||||
Array of edges adjacent to this vertex.
|
||||
Let e = edgeIndex[i].
|
||||
edge[(e >= 0) ? e : ~e].vertexIndex[0] == this
|
||||
vertex index.
|
||||
|
||||
Edges may be listed multiple times if they are
|
||||
degenerate.
|
||||
*/
|
||||
SmallArray<int, 6> edgeIndex;
|
||||
|
||||
/**
|
||||
Returns true if e or ~e is in the edgeIndex list.
|
||||
*/
|
||||
inline bool inEdge(int e) const
|
||||
{
|
||||
return edgeIndex.contains(~e) || edgeIndex.contains(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Array of faces containing this vertex. Faces
|
||||
may be listed multiple times if they are degenerate.
|
||||
*/
|
||||
SmallArray<int, 6> faceIndex;
|
||||
|
||||
inline bool inFace(int f) const
|
||||
{
|
||||
debugAssert(f >= 0);
|
||||
return faceIndex.contains(f);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Oriented, indexed triangle.
|
||||
*/
|
||||
class Face
|
||||
{
|
||||
public:
|
||||
Face();
|
||||
|
||||
/**
|
||||
Used by Edge::faceIndex to indicate a missing face.
|
||||
This is a large negative value.
|
||||
*/
|
||||
static const int NONE;
|
||||
|
||||
|
||||
/**
|
||||
Vertices in the face in counter-clockwise order.
|
||||
Degenerate faces may include the same vertex multiple times.
|
||||
*/
|
||||
int vertexIndex[3];
|
||||
|
||||
inline bool containsVertex(int v) const
|
||||
{
|
||||
return contains(vertexIndex, 3, v);
|
||||
}
|
||||
|
||||
/**
|
||||
Edge indices in counter-clockwise order. Edges are
|
||||
undirected, so it is important to know which way
|
||||
each edge is pointing in a face. This is encoded
|
||||
using negative indices.
|
||||
|
||||
If <CODE>edgeIndex[i] >= 0</CODE> then this face
|
||||
contains the directed edge
|
||||
between vertex indices
|
||||
<CODE>edgeArray[face.edgeIndex[i]].vertexIndex[0]</CODE>
|
||||
and
|
||||
<CODE>edgeArray[face.edgeIndex[i]].vertexIndex[1]</CODE>.
|
||||
|
||||
If <CODE>edgeIndex[i] < 0</CODE> then
|
||||
<CODE>~edgeIndex[i]</CODE> (i.e. the two's
|
||||
complement of) is used and this face contains the directed
|
||||
edge between vertex indices
|
||||
<CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[0]</CODE>
|
||||
and
|
||||
<CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[1]</CODE>.
|
||||
|
||||
Degenerate faces may include the same edge multiple times.
|
||||
*/
|
||||
// Temporarily takes on the value Face::NONE during adjacency
|
||||
// computation to indicate an edge that has not yet been assigned.
|
||||
int edgeIndex[3];
|
||||
|
||||
inline bool containsEdge(int e) const
|
||||
{
|
||||
if (e < 0)
|
||||
{
|
||||
e = ~e;
|
||||
}
|
||||
return contains(edgeIndex, 3, e) || contains(edgeIndex, 3, ~e);
|
||||
}
|
||||
|
||||
/** Contains the forward edge e if e >= 0 and the backward edge
|
||||
~e otherwise. */
|
||||
inline bool containsDirectedEdge(int e) const
|
||||
{
|
||||
return contains(edgeIndex, 3, e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Oriented, indexed edge */
|
||||
class Edge
|
||||
{
|
||||
public:
|
||||
Edge();
|
||||
|
||||
/** Degenerate edges may include the same vertex times. */
|
||||
int vertexIndex[2];
|
||||
|
||||
inline bool containsVertex(int v) const
|
||||
{
|
||||
return contains(vertexIndex, 2, v);
|
||||
}
|
||||
|
||||
/**
|
||||
The edge is directed <B>forward</B> in face 0
|
||||
<B>backward</B> in face 1. Face index of MeshAlg::Face::NONE
|
||||
indicates a boundary (a.k.a. crack, broken) edge.
|
||||
*/
|
||||
int faceIndex[2];
|
||||
|
||||
/** Returns true if f is contained in the faceIndex array in either slot.
|
||||
To see if it is forward in that face, just check edge.faceIndex[0] == f.*/
|
||||
inline bool inFace(int f) const
|
||||
{
|
||||
return contains(faceIndex, 2, f);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if either faceIndex is NONE.
|
||||
*/
|
||||
inline bool boundary() const
|
||||
{
|
||||
return (faceIndex[0] == Face::NONE) || (faceIndex[1] == Face::NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the reversed edge.
|
||||
*/
|
||||
inline Edge reverse() const
|
||||
{
|
||||
Edge e;
|
||||
e.vertexIndex[0] = vertexIndex[1];
|
||||
e.vertexIndex[1] = vertexIndex[0];
|
||||
e.faceIndex[0] = faceIndex[1];
|
||||
e.faceIndex[1] = faceIndex[0];
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Convenient for passing around the per-vertex data that changes under
|
||||
animation. The faces and edges are needed to interpret
|
||||
these values.
|
||||
*/
|
||||
class Geometry
|
||||
{
|
||||
public:
|
||||
/** Vertex positions */
|
||||
Array<Vector3> vertexArray;
|
||||
|
||||
/** Vertex normals */
|
||||
Array<Vector3> normalArray;
|
||||
|
||||
/**
|
||||
Assignment is optimized using SSE.
|
||||
*/
|
||||
Geometry& operator=(const Geometry& src);
|
||||
|
||||
void clear()
|
||||
{
|
||||
vertexArray.clear();
|
||||
normalArray.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Given a set of vertices and a set of indices for traversing them
|
||||
to create triangles, computes other mesh properties.
|
||||
|
||||
<B>Colocated vertices are treated as separate.</B> To have
|
||||
colocated vertices collapsed (necessary for many algorithms,
|
||||
like shadowing), weld the mesh before computing adjacency.
|
||||
|
||||
<I>Recent change: In version 6.00, colocated vertices were automatically
|
||||
welded by this routine and degenerate faces and edges were removed. That
|
||||
is no longer the case.</I>
|
||||
|
||||
Where two faces meet, there are two opposite directed edges. These
|
||||
are collapsed into a single bidirectional edge in the edgeArray.
|
||||
If four faces meet exactly at the same edge, that edge will appear
|
||||
twice in the array, and so on. If an edge is a boundary of the mesh
|
||||
(i.e. if the edge has only one adjacent face) it will appear in the
|
||||
array with one face index set to MeshAlg::Face::NONE.
|
||||
|
||||
@param vertexGeometry %Vertex positions to use when deciding colocation.
|
||||
@param indexArray Order to traverse vertices to make triangles
|
||||
@param faceArray <I>Output</I>
|
||||
@param edgeArray <I>Output</I>. Sorted so that boundary edges are at the end of the array.
|
||||
@param vertexArray <I>Output</I>
|
||||
*/
|
||||
static void computeAdjacency(const Array<Vector3>& vertexGeometry, const Array<int>& indexArray, Array<Face>& faceArray, Array<Edge>& edgeArray,
|
||||
Array<Vertex>& vertexArray);
|
||||
|
||||
/**
|
||||
@deprecated Use the other version of computeAdjacency, which takes Array<Vertex>.
|
||||
@param facesAdjacentToVertex <I>Output</I> adjacentFaceArray[v] is an array of
|
||||
indices for faces touching vertex index v
|
||||
*/
|
||||
static void computeAdjacency(const Array<Vector3>& vertexArray, const Array<int>& indexArray, Array<Face>& faceArray, Array<Edge>& edgeArray,
|
||||
Array<Array<int>>& facesAdjacentToVertex);
|
||||
|
||||
/**
|
||||
Computes some basic mesh statistics including: min, max mean and median,
|
||||
edge lengths; and min, mean, median, and max face area.
|
||||
|
||||
@param vertexArray %Vertex positions to use when deciding colocation.
|
||||
@param indexArray Order to traverse vertices to make triangles
|
||||
@param minEdgeLength Minimum edge length
|
||||
@param meanEdgeLength Mean edge length
|
||||
@param medianEdgeLength Median edge length
|
||||
@param maxEdgeLength Max edge length
|
||||
@param minFaceArea Minimum face area
|
||||
@param meanFaceArea Mean face area
|
||||
@param medianFaceArea Median face area
|
||||
@param maxFaceArea Max face area
|
||||
*/
|
||||
static void computeAreaStatistics(const Array<Vector3>& vertexArray, const Array<int>& indexArray, double& minEdgeLength, double& meanEdgeLength,
|
||||
double& medianEdgeLength, double& maxEdgeLength, double& minFaceArea, double& meanFaceArea, double& medianFaceArea, double& maxFaceArea);
|
||||
|
||||
private:
|
||||
/** Helper for weldAdjacency */
|
||||
static void weldBoundaryEdges(Array<Face>& faceArray, Array<Edge>& edgeArray, Array<Vertex>& vertexArray);
|
||||
|
||||
public:
|
||||
/**
|
||||
Computes tangent and binormal vectors,
|
||||
which provide a (mostly) consistent
|
||||
parameterization over the surface for
|
||||
effects like bump mapping. In the resulting coordinate frame,
|
||||
T = x (varies with texture s coordinate), B = y (varies with negative texture t coordinate),
|
||||
and N = z for a right-handed coordinate frame. If a billboard is vertical on the screen
|
||||
in view of the camera, the tangent space matches the camera's coordinate frame.
|
||||
|
||||
The vertex, texCoord, tangent, and binormal
|
||||
arrays are parallel arrays.
|
||||
|
||||
The resulting tangent and binormal might not be exactly
|
||||
perpendicular to each other. They are guaranteed to
|
||||
be perpendicular to the normal.
|
||||
|
||||
@cite Max McGuire
|
||||
*/
|
||||
static void computeTangentSpaceBasis(const Array<Vector3>& vertexArray, const Array<Vector2>& texCoordArray,
|
||||
const Array<Vector3>& vertexNormalArray, const Array<Face>& faceArray, Array<Vector3>& tangent, Array<Vector3>& binormal);
|
||||
|
||||
/** @deprecated */
|
||||
static void computeNormals(const Array<Vector3>& vertexArray, const Array<Face>& faceArray, const Array<Array<int>>& adjacentFaceArray,
|
||||
Array<Vector3>& vertexNormalArray, Array<Vector3>& faceNormalArray);
|
||||
|
||||
/**
|
||||
Vertex normals are weighted by the area of adjacent faces.
|
||||
Nelson Max showed this is superior to uniform weighting for
|
||||
general meshes in jgt.
|
||||
|
||||
@param vertexNormalArray Output. Unit length
|
||||
@param faceNormalArray Output. Degenerate faces produce zero magnitude normals. Unit length
|
||||
@see weld
|
||||
*/
|
||||
static void computeNormals(const Array<Vector3>& vertexGeometry, const Array<Face>& faceArray, const Array<Vertex>& vertexArray,
|
||||
Array<Vector3>& vertexNormalArray, Array<Vector3>& faceNormalArray);
|
||||
|
||||
/** Computes unit length normals in place using the other computeNormals methods.
|
||||
If you already have a face array use another method; it will be faster.
|
||||
@see weld*/
|
||||
static void computeNormals(Geometry& geometry, const Array<int>& indexArray);
|
||||
|
||||
/**
|
||||
Computes face normals only. Significantly faster (especially if
|
||||
normalize is false) than computeNormals.
|
||||
@see weld
|
||||
*/
|
||||
static void computeFaceNormals(
|
||||
const Array<Vector3>& vertexArray, const Array<Face>& faceArray, Array<Vector3>& faceNormals, bool normalize = true);
|
||||
|
||||
/**
|
||||
Classifies each face as a backface or a front face relative
|
||||
to the observer point P (which is at infinity when P.w = 0).
|
||||
A face with normal exactly perpendicular to the observer vector
|
||||
may be classified as either a front or a back face arbitrarily.
|
||||
*/
|
||||
static void identifyBackfaces(const Array<Vector3>& vertexArray, const Array<Face>& faceArray, const Vector4& P, Array<bool>& backface);
|
||||
|
||||
/** A faster version of identifyBackfaces for the case where
|
||||
face normals have already been computed */
|
||||
static void identifyBackfaces(
|
||||
const Array<Vector3>& vertexArray, const Array<Face>& faceArray, const Vector4& P, Array<bool>& backface, const Array<Vector3>& faceNormals);
|
||||
|
||||
/**
|
||||
Welds nearby and colocated elements of the <I>oldVertexArray</I> together so that
|
||||
<I>newVertexArray</I> contains no vertices within <I>radius</I> of one another.
|
||||
Every vertex in newVertexPositions also appears in oldVertexPositions.
|
||||
This is useful for downsampling meshes and welding cracks created by artist errors
|
||||
or numerical imprecision.
|
||||
|
||||
The two integer arrays map indices back and forth between the arrays according to:
|
||||
<PRE>
|
||||
oldVertexArray[toOld[ni]] == newVertexArray[ni]
|
||||
oldVertexArray[oi] == newVertexArray[toNew[ni]]
|
||||
</PRE>
|
||||
|
||||
Note that newVertexPositions is never longer than oldVertexPositions
|
||||
and is shorter when vertices are welded.
|
||||
|
||||
Welding with a large radius will effectively compute a lower level of detail for
|
||||
the mesh.
|
||||
|
||||
The welding method runs in roughly linear time in the length of oldVertexArray--
|
||||
a uniform spatial grid is used to achieve nearly constant time vertex collapses
|
||||
for uniformly distributed vertices.
|
||||
|
||||
It is sometimes desirable to keep the original vertex ordering but
|
||||
identify the unique vertices. The following code computes
|
||||
array canonical s.t. canonical[v] = first occurance of
|
||||
a vertex near oldVertexPositions[v] in oldVertexPositions.
|
||||
|
||||
<PRE>
|
||||
Array<int> canonical(oldVertexPositions.size()), toNew, toOld;
|
||||
computeWeld(oldVertexPositions, Array<Vector3>(), toNew, toOld, radius);
|
||||
for (int v = 0; v < canonical.size(); ++v) {
|
||||
canonical[v] = toOld[toNew[v]];
|
||||
}
|
||||
</PRE>
|
||||
|
||||
See also G3D::MeshAlg::weldAdjacency.
|
||||
|
||||
@cite The method is that described as the 'Grouper' in Baum, Mann, Smith, and Winget,
|
||||
Making Radiosity Usable: Automatic Preprocessing and Meshing Techniques for
|
||||
the Generation of Accurate Radiosity Solutions, Computer Graphics vol 25, no 4, July 1991.
|
||||
|
||||
@deprecated Use weld.
|
||||
*/
|
||||
static void computeWeld(const Array<Vector3>& oldVertexPositions, Array<Vector3>& newVertexPositions, Array<int>& toNew, Array<int>& toOld,
|
||||
double radius = fuzzyEpsilon);
|
||||
|
||||
/**
|
||||
Modifies the face, edge, and vertex arrays in place so that
|
||||
colocated (within radius) vertices are treated as identical.
|
||||
Note that the vertexArray and corresponding geometry will
|
||||
contain elements that are no longer used. In the vertexArray,
|
||||
these elements are initialized to MeshAlg::Vertex() but not
|
||||
removed (because removal would change the indexing).
|
||||
|
||||
This is a good preprocessing step for algorithms that are only
|
||||
concerned with the shape of a mesh (e.g. cartoon rendering, fur, shadows)
|
||||
and not the indexing of the vertices.
|
||||
|
||||
Use this method when you have already computed adjacency information
|
||||
and want to collapse colocated vertices within that data without
|
||||
disturbing the actual mesh vertices or indexing scheme.
|
||||
|
||||
If you have not computed adjacency already, use MeshAlg::computeWeld
|
||||
instead and compute adjacency information after welding.
|
||||
|
||||
@deprecated Use weld.
|
||||
|
||||
@param faceArray Mutated in place. Size is maintained (degenerate
|
||||
faces are <b>not</B> removed).
|
||||
@param edgeArray Mutated in place. May shrink if boundary edges
|
||||
are welded together.
|
||||
@param vertexArray Mutated in place. Size is maintained (duplicate
|
||||
vertices contain no adjacency info).
|
||||
*/
|
||||
static void weldAdjacency(const Array<Vector3>& originalGeometry, Array<Face>& faceArray, Array<Edge>& edgeArray, Array<Vertex>& vertexArray,
|
||||
double radius = fuzzyEpsilon);
|
||||
|
||||
|
||||
/**
|
||||
Counts the number of edges (in an edge array returned from
|
||||
MeshAlg::computeAdjacency) that have only one adjacent face.
|
||||
*/
|
||||
static int countBoundaryEdges(const Array<Edge>& edgeArray);
|
||||
|
||||
|
||||
/**
|
||||
Generates an array of integers from start to start + n - 1 that have run numbers
|
||||
in series then omit the next skip before the next run. Useful for turning
|
||||
a triangle list into an indexed face set.
|
||||
|
||||
Example:
|
||||
<PRE>
|
||||
createIndexArray(10, x);
|
||||
// x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
createIndexArray(5, x, 2);
|
||||
// x = [2, 3, 4, 5, 6, 7]
|
||||
|
||||
createIndexArray(6, x, 0, 2, 1);
|
||||
// x = [0, 1, 3, 4, 6, 7]
|
||||
</PRE>
|
||||
*/
|
||||
static void createIndexArray(int n, Array<int>& array, int start = 0, int run = 1, int skip = 0);
|
||||
|
||||
/**
|
||||
Computes a conservative, near-optimal axis aligned bounding box and sphere.
|
||||
|
||||
@cite The bounding sphere uses the method from J. Ritter. An effcient bounding sphere. In Andrew S. Glassner, editor, Graphics Gems. Academic
|
||||
Press, Boston, MA, 1990.
|
||||
|
||||
*/
|
||||
static void computeBounds(const Array<Vector3>& vertex, class AABox& box, class Sphere& sphere);
|
||||
|
||||
/** Computes bounds for a subset of the vertices. It is ok if vertices appear more than once in the index array. */
|
||||
static void computeBounds(const Array<Vector3>& vertex, const Array<int>& index, class AABox& box, class Sphere& sphere);
|
||||
|
||||
/**
|
||||
In debug mode, asserts that the adjacency references between the
|
||||
face, edge, and vertex array are consistent.
|
||||
*/
|
||||
static void debugCheckConsistency(const Array<Face>& faceArray, const Array<Edge>& edgeArray, const Array<Vertex>& vertexArray);
|
||||
|
||||
/**
|
||||
Generates a unit square in the X-Z plane composed of a grid of wCells x hCells
|
||||
squares and then transforms it by xform.
|
||||
|
||||
@param vertex Output vertices
|
||||
@param texCoord Output texture coordinates
|
||||
@param index Output triangle list indices
|
||||
@param textureScale Lower-right texture coordinate
|
||||
@param spaceCentered If true, the coordinates generated are centered at the origin before the transformation.
|
||||
@param twoSided If true, matching top and bottom planes are generated.
|
||||
\param elevation If non-NULL, values from this image are used as elevations. Apply an \a xform to adjust the scale
|
||||
*/
|
||||
static void generateGrid(Array<Vector3>& vertex, Array<Vector2>& texCoord, Array<int>& index, int wCells = 10, int hCells = 10,
|
||||
const Vector2& textureScale = Vector2(1, 1), bool spaceCentered = true, bool twoSided = true,
|
||||
const CoordinateFrame& xform = CoordinateFrame(), const Image1::Ref& elevation = NULL);
|
||||
|
||||
/** Converts quadlist (QUADS),
|
||||
triangle fan (TRIANGLE_FAN),
|
||||
tristrip(TRIANGLE_STRIP), and quadstrip (QUAD_STRIP) indices into
|
||||
triangle list (TRIANGLES) indices and appends them to outIndices. */
|
||||
template<class IndexType>
|
||||
static void toIndexedTriList(const Array<IndexType>& inIndices, MeshAlg::Primitive inType, Array<IndexType>& outIndices)
|
||||
{
|
||||
|
||||
debugAssert(inType == PrimitiveType::TRIANGLE_STRIP || inType == PrimitiveType::TRIANGLE_FAN || inType == PrimitiveType::QUADS ||
|
||||
inType == PrimitiveType::QUAD_STRIP);
|
||||
|
||||
const int inSize = inIndices.size();
|
||||
|
||||
switch (inType)
|
||||
{
|
||||
case PrimitiveType::TRIANGLE_FAN:
|
||||
{
|
||||
debugAssert(inSize >= 3);
|
||||
|
||||
int N = outIndices.size();
|
||||
outIndices.resize(N + (inSize - 2) * 3);
|
||||
|
||||
for (IndexType i = 1, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3)
|
||||
{
|
||||
outIndices[outIndex] = inIndices[0];
|
||||
outIndices[outIndex + 1] = inIndices[i];
|
||||
outIndices[outIndex + 2] = inIndices[i + 1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PrimitiveType::TRIANGLE_STRIP:
|
||||
{
|
||||
debugAssert(inSize >= 3);
|
||||
|
||||
int N = outIndices.size();
|
||||
outIndices.resize(N + (inSize - 2) * 3);
|
||||
|
||||
bool atEven = false;
|
||||
for (IndexType i = 0, outIndex = N; i < (inSize - 2); ++i, outIndex += 3)
|
||||
{
|
||||
if (atEven)
|
||||
{
|
||||
outIndices[outIndex] = inIndices[i + 1];
|
||||
outIndices[outIndex + 1] = inIndices[i];
|
||||
outIndices[outIndex + 2] = inIndices[i + 2];
|
||||
atEven = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
outIndices[outIndex] = inIndices[i];
|
||||
outIndices[outIndex + 1] = inIndices[i + 1];
|
||||
outIndices[outIndex + 2] = inIndices[i + 2];
|
||||
atEven = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PrimitiveType::QUADS:
|
||||
{
|
||||
debugAssert(inIndices.size() >= 4);
|
||||
|
||||
int N = outIndices.size();
|
||||
outIndices.resize(N + (inSize / 4) * 3);
|
||||
|
||||
for (IndexType i = 0, outIndex = N; i <= (inSize - 4); i += 4, outIndex += 6)
|
||||
{
|
||||
outIndices[outIndex] = inIndices[i];
|
||||
outIndices[outIndex + 1] = inIndices[i + 1];
|
||||
outIndices[outIndex + 2] = inIndices[i + 3];
|
||||
outIndices[outIndex + 3] = inIndices[i + 1];
|
||||
outIndices[outIndex + 4] = inIndices[i + 2];
|
||||
outIndices[outIndex + 5] = inIndices[i + 3];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PrimitiveType::QUAD_STRIP:
|
||||
{
|
||||
debugAssert(inIndices.size() >= 4);
|
||||
|
||||
int N = outIndices.size();
|
||||
outIndices.resize(N + (inSize - 2) * 3);
|
||||
|
||||
for (IndexType i = 0, outIndex = N; i <= (inSize - 2); i += 2, outIndex += 6)
|
||||
{
|
||||
outIndices[outIndex] = inIndices[i];
|
||||
outIndices[outIndex + 1] = inIndices[i + 1];
|
||||
outIndices[outIndex + 2] = inIndices[i + 2];
|
||||
outIndices[outIndex + 3] = inIndices[i + 2];
|
||||
outIndices[outIndex + 4] = inIndices[i + 1];
|
||||
outIndices[outIndex + 5] = inIndices[i + 3];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
alwaysAssertM(false, "Illegal argument");
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
Helper for computeAdjacency. If a directed edge with index e already
|
||||
exists from i0 to i1 then e is returned. If a directed edge with index e
|
||||
already exists from i1 to i0, ~e is returned (the complement) and
|
||||
edgeArray[e] is set to f. Otherwise, a new edge is created from i0 to i1
|
||||
with first face index f and its index is returned.
|
||||
|
||||
@param vertexArray Vertex positions to use when deciding colocation.
|
||||
|
||||
@param area Area of face f. When multiple edges of the same direction
|
||||
are found between the same vertices (usually because of degenerate edges)
|
||||
the face with larger area is kept in the edge table.
|
||||
*/
|
||||
static int findEdgeIndex(const Array<Vector3>& vertexArray, Array<Edge>& geometricEdgeArray, int i0, int i1, int f, double area);
|
||||
};
|
||||
} // namespace G3D
|
||||
|
||||
#ifdef G3D_WIN32
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
90
engine/3d/src/MeshBuilder.hpp
Normal file
90
engine/3d/src/MeshBuilder.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
@file MeshBuilder.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2002-02-27
|
||||
@edited 2004-10-04
|
||||
*/
|
||||
#ifndef G3D_MESHBUILDER_H
|
||||
#define G3D_MESHBUILDER_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Triangle.hpp"
|
||||
|
||||
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 G3D
|
||||
|
||||
#endif
|
||||
81
engine/3d/src/ParseError.hpp
Normal file
81
engine/3d/src/ParseError.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
@file ParseError.h
|
||||
|
||||
@maintainer Morgan McGuire
|
||||
|
||||
@created 2009-11-15
|
||||
@edited 2009-11-15
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
#ifndef G3D_ParseError_h
|
||||
#define G3D_ParseError_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/** Thrown by TextInput, Any, and other parsers on unexpected input. */
|
||||
class ParseError
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
UNKNOWN = -1
|
||||
};
|
||||
|
||||
/** Empty means unknown */
|
||||
std::string filename;
|
||||
|
||||
/** For a binary file, the location of the parse error. -1 if unknown.*/
|
||||
int64 byte;
|
||||
|
||||
/** For a text file, the line number is the line number of start of token which caused the exception. 1 is
|
||||
the first line of the file. -1 means unknown. Note that you can use
|
||||
TextInput::Settings::startingLineNumberOffset to shift the effective line
|
||||
number that is reported by that class.
|
||||
*/
|
||||
int line;
|
||||
|
||||
/** Character number (in the line) of the start of the token which caused the
|
||||
exception. 1 is the character in the line. May be -1 if unknown.
|
||||
*/
|
||||
int character;
|
||||
|
||||
std::string message;
|
||||
|
||||
ParseError()
|
||||
: byte(UNKNOWN)
|
||||
, line(UNKNOWN)
|
||||
, character(UNKNOWN)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ParseError() {}
|
||||
|
||||
ParseError(const std::string& f, int l, int c, const std::string& m)
|
||||
: filename(f)
|
||||
, byte(UNKNOWN)
|
||||
, line(l)
|
||||
, character(c)
|
||||
, message(m)
|
||||
{
|
||||
}
|
||||
|
||||
ParseError(const std::string& f, int64 b, const std::string& m)
|
||||
: filename(f)
|
||||
, byte(b)
|
||||
, line(UNKNOWN)
|
||||
, character(UNKNOWN)
|
||||
, message(m)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
66
engine/3d/src/PhysicsFrame.cpp
Normal file
66
engine/3d/src/PhysicsFrame.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
@file PhysicsFrame.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2002-07-09
|
||||
@edited 2010-03-25
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "stringutils.hpp"
|
||||
#include "PhysicsFrame.hpp"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
PhysicsFrame::operator CoordinateFrame() const
|
||||
{
|
||||
CoordinateFrame f;
|
||||
|
||||
f.translation = translation;
|
||||
f.rotation = rotation.toRotationMatrix();
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
PhysicsFrame PhysicsFrame::lerp(const PhysicsFrame& other, float alpha) const
|
||||
{
|
||||
|
||||
PhysicsFrame result;
|
||||
|
||||
result.translation = translation.lerp(other.translation, alpha);
|
||||
result.rotation = rotation.slerp(other.rotation, alpha);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace G3D
|
||||
115
engine/3d/src/PhysicsFrame.hpp
Normal file
115
engine/3d/src/PhysicsFrame.hpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
@file PhysicsFrame.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2002-07-08
|
||||
@edited 2006-01-10
|
||||
*/
|
||||
|
||||
#ifndef G3D_PHYSICSFRAME_H
|
||||
#define G3D_PHYSICSFRAME_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Matrix3.hpp"
|
||||
#include "Quat.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#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.
|
||||
*/
|
||||
PhysicsFrame(const Vector3& translation)
|
||||
: translation(translation)
|
||||
{
|
||||
}
|
||||
PhysicsFrame(const Quat& rot, const Vector3& translation)
|
||||
: rotation(rot)
|
||||
, translation(translation)
|
||||
{
|
||||
}
|
||||
PhysicsFrame(const Matrix3& rot, const Vector3& translation)
|
||||
: rotation(rot)
|
||||
, translation(translation)
|
||||
{
|
||||
}
|
||||
PhysicsFrame(const Matrix3& rot)
|
||||
: rotation(rot)
|
||||
, translation(Vector3::zero())
|
||||
{
|
||||
}
|
||||
PhysicsFrame(const CoordinateFrame& coordinateFrame);
|
||||
|
||||
/** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/
|
||||
PhysicsFrame operator*(const PhysicsFrame& other) const;
|
||||
|
||||
virtual ~PhysicsFrame() {}
|
||||
|
||||
/**
|
||||
Linear interpolation (spherical linear for the rotations).
|
||||
*/
|
||||
PhysicsFrame lerp(const PhysicsFrame& other, float alpha) const;
|
||||
|
||||
operator CFrame() const;
|
||||
|
||||
/** Multiplies both pieces by \a f; note that this will result in a non-unit
|
||||
quaternion that needs to be normalized */
|
||||
PhysicsFrame& operator*=(float f)
|
||||
{
|
||||
rotation *= f;
|
||||
translation *= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Multiplies both pieces by \a f; note that this will result in a non-unit
|
||||
quaternion that needs to be normalized */
|
||||
PhysicsFrame operator*(float f) const
|
||||
{
|
||||
return PhysicsFrame(rotation * f, translation * f);
|
||||
}
|
||||
|
||||
PhysicsFrame operator+(const PhysicsFrame& f) const
|
||||
{
|
||||
return PhysicsFrame(rotation + f.rotation, translation + f.translation);
|
||||
}
|
||||
|
||||
PhysicsFrame& operator+=(const PhysicsFrame& f)
|
||||
{
|
||||
rotation += f.rotation;
|
||||
translation += f.translation;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
typedef PhysicsFrame PFrame;
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
134
engine/3d/src/Plane.cpp
Normal file
134
engine/3d/src/Plane.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
@file Plane.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-02-06
|
||||
@edited 2006-01-29
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
200
engine/3d/src/Plane.hpp
Normal file
200
engine/3d/src/Plane.hpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
@file Plane.h
|
||||
|
||||
Plane class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2004-07-18
|
||||
*/
|
||||
|
||||
#ifndef G3D_PLANE_H
|
||||
#define G3D_PLANE_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "debugAssert.hpp"
|
||||
|
||||
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.
|
||||
*/
|
||||
// Aya
|
||||
public:
|
||||
// =====
|
||||
Plane(const Vector3& n, float d)
|
||||
: _normal(n)
|
||||
, _distance(d)
|
||||
{
|
||||
}
|
||||
// Aya
|
||||
// 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);
|
||||
|
||||
// Aya
|
||||
bool operator==(const Plane& other) const
|
||||
{
|
||||
return ((_normal == other._normal) && (_distance == other._distance));
|
||||
}
|
||||
// =======
|
||||
|
||||
|
||||
virtual ~Plane() {}
|
||||
|
||||
// Aya
|
||||
#if 0
|
||||
/**
|
||||
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;
|
||||
}
|
||||
#else
|
||||
bool halfSpaceContains(Vector3 point) const
|
||||
{
|
||||
point = point.clamp(Vector3::minFinite(), Vector3::maxFinite());
|
||||
return _normal.dot(point) >= _distance;
|
||||
}
|
||||
|
||||
bool pointOnOrBehind(Vector3 point) const
|
||||
{
|
||||
point = point.clamp(Vector3::minFinite(), Vector3::maxFinite());
|
||||
return _normal.dot(point) <= _distance;
|
||||
}
|
||||
#endif
|
||||
// ========
|
||||
|
||||
/**
|
||||
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;
|
||||
}
|
||||
|
||||
// Aya
|
||||
inline float distance() const
|
||||
{
|
||||
return _distance;
|
||||
}
|
||||
//====
|
||||
|
||||
/**
|
||||
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 G3D
|
||||
|
||||
#endif
|
||||
9
engine/3d/src/PositionTrait.hpp
Normal file
9
engine/3d/src/PositionTrait.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef G3D_POSITIONTRAIT_H
|
||||
#define G3D_POSITIONTRAIT_H
|
||||
|
||||
template<typename Value>
|
||||
struct PositionTrait
|
||||
{
|
||||
};
|
||||
|
||||
#endif
|
||||
1649
engine/3d/src/Quat.cpp
Normal file
1649
engine/3d/src/Quat.cpp
Normal file
File diff suppressed because it is too large
Load Diff
801
engine/3d/src/Quat.hpp
Normal file
801
engine/3d/src/Quat.hpp
Normal file
@@ -0,0 +1,801 @@
|
||||
/**
|
||||
@file Quat.h
|
||||
|
||||
Quaternion
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2002-01-23
|
||||
@edited 2009-05-10
|
||||
*/
|
||||
|
||||
#ifndef G3D_Quat_h
|
||||
#define G3D_Quat_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Matrix3.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
Arbitrary quaternion (not necessarily unit)
|
||||
|
||||
Unit quaternions are used in computer graphics to represent
|
||||
rotation about an axis. Any 3x3 rotation matrix can
|
||||
be stored as a quaternion.
|
||||
|
||||
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, (0,0,0,1)
|
||||
*/
|
||||
Quat()
|
||||
: x(0)
|
||||
, y(0)
|
||||
, z(0)
|
||||
, w(1)
|
||||
{
|
||||
}
|
||||
|
||||
Quat(const Matrix3& rot);
|
||||
|
||||
Quat(float _x, float _y, float _z, float _w)
|
||||
: x(_x)
|
||||
, y(_y)
|
||||
, z(_z)
|
||||
, w(_w)
|
||||
{
|
||||
}
|
||||
|
||||
/** Defaults to a pure vector quaternion */
|
||||
Quat(const Vector3& v, float _w = 0)
|
||||
: x(v.x)
|
||||
, y(v.y)
|
||||
, z(v.z)
|
||||
, w(_w)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Quat(const float values[4])
|
||||
: x(values[0])
|
||||
, y(values[1])
|
||||
, z(values[2])
|
||||
, w(values[3])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
The real part of the quaternion.
|
||||
*/
|
||||
const float& real() const
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
float& real()
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
Quat operator-() const
|
||||
{
|
||||
return Quat(-x, -y, -z, -w);
|
||||
}
|
||||
|
||||
Quat operator-(const Quat& other) const
|
||||
{
|
||||
return Quat(x - other.x, y - other.y, z - other.z, w - other.w);
|
||||
}
|
||||
|
||||
Quat& operator-=(const Quat& q)
|
||||
{
|
||||
x -= q.x;
|
||||
y -= q.y;
|
||||
z -= q.z;
|
||||
w -= q.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Quat operator+(const Quat& q) const
|
||||
{
|
||||
return Quat(x + q.x, y + q.y, z + q.z, w + q.w);
|
||||
}
|
||||
|
||||
Quat& operator+=(const Quat& q)
|
||||
{
|
||||
x += q.x;
|
||||
y += q.y;
|
||||
z += q.z;
|
||||
w += q.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
Negates the imaginary part.
|
||||
*/
|
||||
Quat conj() const
|
||||
{
|
||||
return Quat(-x, -y, -z, w);
|
||||
}
|
||||
|
||||
float sum() const
|
||||
{
|
||||
return x + y + z + w;
|
||||
}
|
||||
|
||||
float average() const
|
||||
{
|
||||
return sum() / 4.0f;
|
||||
}
|
||||
|
||||
Quat operator*(float s) const
|
||||
{
|
||||
return Quat(x * s, y * s, z * s, w * s);
|
||||
}
|
||||
|
||||
Quat& operator*=(float s)
|
||||
{
|
||||
x *= s;
|
||||
y *= s;
|
||||
z *= s;
|
||||
w *= s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/** @cite Based on Watt & Watt, page 360 */
|
||||
friend Quat operator*(float s, const Quat& q);
|
||||
|
||||
inline Quat operator/(float s) const
|
||||
{
|
||||
return Quat(x / s, y / s, z / s, w / s);
|
||||
}
|
||||
|
||||
float dot(const Quat& other) const
|
||||
{
|
||||
return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.w);
|
||||
}
|
||||
|
||||
/** Note: two quats can represent the Quat::sameRotation and not be equal. */
|
||||
bool fuzzyEq(const Quat& q)
|
||||
{
|
||||
return G3D::fuzzyEq(x, q.x) && G3D::fuzzyEq(y, q.y) && G3D::fuzzyEq(z, q.z) && G3D::fuzzyEq(w, q.w);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the imaginary part (x, y, z)
|
||||
*/
|
||||
const Vector3& imag() const
|
||||
{
|
||||
return *(reinterpret_cast<const Vector3*>(this));
|
||||
}
|
||||
|
||||
Vector3& imag()
|
||||
{
|
||||
return *(reinterpret_cast<Vector3*>(this));
|
||||
}
|
||||
|
||||
/** q = [sin(angle/2)*axis, cos(angle/2)] */
|
||||
static Quat fromAxisAngleRotation(const Vector3& axis, float angle);
|
||||
|
||||
static Quat fromRotation(const Vector3& rotation);
|
||||
static Quat fromVectors(const Vector3& from, const Vector3& to);
|
||||
|
||||
/** 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;
|
||||
|
||||
|
||||
|
||||
/** Note that q<SUP>-1</SUP> = q.conj() for a unit quaternion.
|
||||
@cite Dam99 page 13 */
|
||||
inline Quat inverse() const
|
||||
{
|
||||
return conj() / dot(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
Quaternion multiplication (composition of rotations).
|
||||
Note that this does not commute.
|
||||
*/
|
||||
Quat operator*(const Quat& other) const;
|
||||
|
||||
/* Quaternion-vector transformation */
|
||||
Vector3 operator*(const Vector3& other) const;
|
||||
|
||||
/* (*this) * other.inverse() */
|
||||
Quat operator/(const Quat& other) const
|
||||
{
|
||||
return (*this) * other.inverse();
|
||||
}
|
||||
|
||||
/** Is the magnitude nearly 1.0? */
|
||||
bool isUnit(float tolerance = 1e-5) const
|
||||
{
|
||||
return abs(dot(*this) - 1.0f) < tolerance;
|
||||
}
|
||||
|
||||
float magnitude() const
|
||||
{
|
||||
return sqrtf(dot(*this));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/** Make unit length in place */
|
||||
void unitize()
|
||||
{
|
||||
*this *= rsq(dot(*this));
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a unit quaterion obtained by dividing through by
|
||||
the magnitude.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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();
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
|
||||
} // 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
|
||||
219
engine/3d/src/Random.cpp
Normal file
219
engine/3d/src/Random.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
@file Random.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2009-01-02
|
||||
@edited 2009-03-29
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
#include "Random.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
Random& Random::common()
|
||||
{
|
||||
static Random r;
|
||||
return r;
|
||||
}
|
||||
|
||||
Random::Random(void* x)
|
||||
: state(NULL)
|
||||
{
|
||||
(void)x;
|
||||
}
|
||||
|
||||
|
||||
Random::Random(uint32 seed)
|
||||
{
|
||||
const uint32 X = 1812433253UL;
|
||||
|
||||
state = new uint32[N];
|
||||
state[0] = seed;
|
||||
for (index = 1; index < (int)N; ++index)
|
||||
{
|
||||
state[index] = X * (state[index - 1] ^ (state[index - 1] >> 30)) + index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Random::~Random()
|
||||
{
|
||||
delete[] state;
|
||||
state = NULL;
|
||||
}
|
||||
|
||||
|
||||
uint32 Random::bits()
|
||||
{
|
||||
// See http://en.wikipedia.org/wiki/Mersenne_twister
|
||||
|
||||
// Make a local copy of the index variable to ensure that it
|
||||
// is not out of bounds
|
||||
int localIndex = index;
|
||||
|
||||
// Automatically checks for index < 0 if corrupted
|
||||
// by unsynchronized threads.
|
||||
if ((unsigned int)localIndex >= (unsigned int)N)
|
||||
{
|
||||
generate();
|
||||
localIndex = 0;
|
||||
}
|
||||
// Increment the global index. It may go out of bounds on
|
||||
// multiple threads, but the above check ensures that the
|
||||
// array index actually used never goes out of bounds.
|
||||
// It doesn't matter if we grab the same array index twice
|
||||
// on two threads, since the distribution of random numbers
|
||||
// will still be uniform.
|
||||
++index;
|
||||
// Return the next random in the sequence
|
||||
uint32 r = state[localIndex];
|
||||
|
||||
// Temper the result
|
||||
r ^= r >> U;
|
||||
r ^= (r << S) & B;
|
||||
r ^= (r << T) & C;
|
||||
r ^= r >> L;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/** Generate the next N ints, and store them for readback later */
|
||||
void Random::generate()
|
||||
{
|
||||
// Lower R bits
|
||||
static const uint32 LOWER_MASK = (1LU << R) - 1;
|
||||
|
||||
// Upper (32 - R) bits
|
||||
static const uint32 UPPER_MASK = 0xFFFFFFFF << R;
|
||||
static const uint32 mag01[2] = {0UL, (uint32)A};
|
||||
|
||||
// First N - M
|
||||
for (unsigned int i = 0; i < N - M; ++i)
|
||||
{
|
||||
uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
|
||||
state[i] = state[i + M] ^ (x >> 1) ^ mag01[x & 1];
|
||||
}
|
||||
|
||||
// Rest
|
||||
for (unsigned int i = N - M + 1; i < N - 1; ++i)
|
||||
{
|
||||
uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
|
||||
state[i] = state[i + (M - N)] ^ (x >> 1) ^ mag01[x & 1];
|
||||
}
|
||||
|
||||
uint32 y = (state[N - 1] & UPPER_MASK) | (state[0] & LOWER_MASK);
|
||||
state[N - 1] = state[M - 1] ^ (y >> 1) ^ mag01[y & 1];
|
||||
index = 0;
|
||||
}
|
||||
|
||||
|
||||
int Random::integer(int low, int high)
|
||||
{
|
||||
int r = iFloor(low + (high - low + 1) * (double)bits() / 0xFFFFFFFFUL);
|
||||
|
||||
// There is a *very small* chance of generating
|
||||
// a number larger than high.
|
||||
if (r > high)
|
||||
{
|
||||
return high;
|
||||
}
|
||||
else
|
||||
{
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float Random::gaussian(float mean, float stdev)
|
||||
{
|
||||
|
||||
// Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
|
||||
// Modified to specify standard deviation and mean of distribution
|
||||
float w, x1, x2;
|
||||
|
||||
// Loop until w is less than 1 so that log(w) is negative
|
||||
do
|
||||
{
|
||||
x1 = uniform(-1.0, 1.0);
|
||||
x2 = uniform(-1.0, 1.0);
|
||||
|
||||
w = float(square(x1) + square(x2));
|
||||
} while (w > 1.0f);
|
||||
|
||||
// Transform to gassian distribution
|
||||
// Multiply by sigma (stdev ^ 2) and add mean.
|
||||
return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w)) / w) + mean;
|
||||
}
|
||||
|
||||
|
||||
void Random::cosHemi(float& x, float& y, float& z)
|
||||
{
|
||||
const float e1 = uniform();
|
||||
const float e2 = uniform();
|
||||
|
||||
// Jensen's method
|
||||
const float sin_theta = sqrtf(1.0f - e1);
|
||||
const float cos_theta = sqrtf(e1);
|
||||
const float phi = 6.28318531f * e2;
|
||||
|
||||
x = cos(phi) * sin_theta;
|
||||
y = sin(phi) * sin_theta;
|
||||
z = cos_theta;
|
||||
|
||||
// We could also use Malley's method (pbrt p.657), since they are the same cost:
|
||||
//
|
||||
// r = sqrt(e1);
|
||||
// t = 2*pi*e2;
|
||||
// x = cos(t)*r;
|
||||
// y = sin(t)*r;
|
||||
// z = sqrt(1.0 - x*x + y*y);
|
||||
}
|
||||
|
||||
|
||||
void Random::cosPowHemi(const float k, float& x, float& y, float& z)
|
||||
{
|
||||
const float e1 = uniform();
|
||||
const float e2 = uniform();
|
||||
|
||||
const float cos_theta = pow(e1, 1.0f / (k + 1.0f));
|
||||
const float sin_theta = sqrtf(1.0f - square(cos_theta));
|
||||
const float phi = 6.28318531f * e2;
|
||||
|
||||
x = cos(phi) * sin_theta;
|
||||
y = sin(phi) * sin_theta;
|
||||
z = cos_theta;
|
||||
}
|
||||
|
||||
|
||||
void Random::hemi(float& x, float& y, float& z)
|
||||
{
|
||||
sphere(x, y, z);
|
||||
z = fabsf(z);
|
||||
}
|
||||
|
||||
|
||||
void Random::sphere(float& x, float& y, float& z)
|
||||
{
|
||||
// Squared magnitude
|
||||
float m2;
|
||||
|
||||
// Rejection sample
|
||||
do
|
||||
{
|
||||
x = uniform() * 2.0f - 1.0f, y = uniform() * 2.0f - 1.0f, z = uniform() * 2.0f - 1.0f;
|
||||
m2 = x * x + y * y + z * z;
|
||||
} while (m2 >= 1.0f);
|
||||
|
||||
// Divide by magnitude to produce a unit vector
|
||||
float s = rsqrt(m2);
|
||||
x *= s;
|
||||
y *= s;
|
||||
z *= s;
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
132
engine/3d/src/Random.hpp
Normal file
132
engine/3d/src/Random.hpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
@file Random.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2009-01-02
|
||||
@edited 2009-03-20
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
#ifndef G3D_Random_h
|
||||
#define G3D_Random_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/** Random number generator.
|
||||
|
||||
Threadsafe.
|
||||
|
||||
Useful for generating consistent random numbers across platforms
|
||||
and when multiple threads are involved.
|
||||
|
||||
Uses the Fast Mersenne Twister (FMT-19937) algorithm.
|
||||
|
||||
On average, uniform() runs about 2x-3x faster than rand().
|
||||
|
||||
@cite http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html
|
||||
|
||||
On OS X, Random is about 10x faster than drand48()and 4x faster than rand()
|
||||
|
||||
Not thread safe anymore, but who cares
|
||||
*/
|
||||
class Random
|
||||
{
|
||||
protected:
|
||||
/** Constants (important for the algorithm; do not modify) */
|
||||
enum
|
||||
{
|
||||
N = 624,
|
||||
M = 397,
|
||||
R = 31,
|
||||
U = 11,
|
||||
S = 7,
|
||||
T = 15,
|
||||
L = 18,
|
||||
A = 0x9908B0DF,
|
||||
B = 0x9D2C5680,
|
||||
C = 0xEFC60000
|
||||
};
|
||||
|
||||
/** State vector (these are the next N values that will be returned) */
|
||||
uint32* state;
|
||||
|
||||
/** Index into state */
|
||||
int index;
|
||||
|
||||
/** Generate the next N ints, and store them for readback later.
|
||||
Called from bits() */
|
||||
virtual void generate();
|
||||
|
||||
/** For subclasses. The void* parameter is just to distinguish this from the
|
||||
public constructor.*/
|
||||
Random(void*);
|
||||
|
||||
public:
|
||||
Random(uint32 seed = 0xF018A4D2);
|
||||
|
||||
virtual ~Random();
|
||||
|
||||
/** Each bit is random. Subclasses can choose to override just
|
||||
this method and the other methods will all work automatically. */
|
||||
virtual uint32 bits();
|
||||
|
||||
/** Uniform random integer on the range [min, max] */
|
||||
virtual int integer(int min, int max);
|
||||
|
||||
/** Uniform random float on the range [min, max] */
|
||||
virtual inline float uniform(float low, float high)
|
||||
{
|
||||
// We could compute the ratio in double precision here for
|
||||
// about 1.5x slower performance and slightly better
|
||||
// precision.
|
||||
return low + (high - low) * ((float)bits() / (float)0xFFFFFFFFUL);
|
||||
}
|
||||
|
||||
/** Uniform random float on the range [0, 1] */
|
||||
virtual inline float uniform()
|
||||
{
|
||||
// We could compute the ratio in double precision here for
|
||||
// about 1.5x slower performance and slightly better
|
||||
// precision.
|
||||
const float norm = 1.0f / (float)0xFFFFFFFFUL;
|
||||
return (float)bits() * norm;
|
||||
}
|
||||
|
||||
/** Normally distributed reals. */
|
||||
virtual float gaussian(float mean, float stdev);
|
||||
|
||||
/** Returns 3D unit vectors distributed according to
|
||||
a cosine distribution about the z-axis. */
|
||||
virtual void cosHemi(float& x, float& y, float& z);
|
||||
|
||||
/** Returns 3D unit vectors distributed according to a cosine
|
||||
power distribution (\f$ \cos^k \theta \f$) about
|
||||
the z-axis. */
|
||||
virtual void cosPowHemi(const float k, float& x, float& y, float& z);
|
||||
|
||||
/** Returns 3D unit vectors uniformly distributed on the
|
||||
hemisphere about the z-axis. */
|
||||
virtual void hemi(float& x, float& y, float& z);
|
||||
|
||||
/** Returns 3D unit vectors uniformly distributed on the sphere */
|
||||
virtual void sphere(float& x, float& y, float& z);
|
||||
|
||||
/**
|
||||
A shared instance for when the performance and features but not
|
||||
consistency of the class are desired. It is slightly (10%)
|
||||
faster to use a distinct instance than to use the common one.
|
||||
|
||||
Threadsafe.
|
||||
*/
|
||||
static Random& common();
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
274
engine/3d/src/Ray.cpp
Normal file
274
engine/3d/src/Ray.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
Aya - We are not using the G3D 8.0 G3D::Ray, intstead we are using the Aya:RbxRay from old G3D.
|
||||
@file Ray.cpp
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2002-07-12
|
||||
@edited 2004-03-19
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Ray.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "Sphere.hpp"
|
||||
#include "CollisionDetection.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
void Ray::set(const Vector3& origin, const Vector3& direction)
|
||||
{
|
||||
m_origin = origin;
|
||||
m_direction = direction;
|
||||
debugAssert(direction.isUnit());
|
||||
|
||||
m_invDirection = Vector3::one() / direction;
|
||||
|
||||
// ray slope
|
||||
ibyj = m_direction.x * m_invDirection.y;
|
||||
jbyi = m_direction.y * m_invDirection.x;
|
||||
jbyk = m_direction.y * m_invDirection.z;
|
||||
kbyj = m_direction.z * m_invDirection.y;
|
||||
ibyk = m_direction.x * m_invDirection.z;
|
||||
kbyi = m_direction.z * m_invDirection.x;
|
||||
|
||||
// precomputed terms
|
||||
c_xy = m_origin.y - jbyi * m_origin.x;
|
||||
c_xz = m_origin.z - kbyi * m_origin.x;
|
||||
c_yx = m_origin.x - ibyj * m_origin.y;
|
||||
c_yz = m_origin.z - kbyj * m_origin.y;
|
||||
c_zx = m_origin.x - ibyk * m_origin.z;
|
||||
c_zy = m_origin.y - jbyk * m_origin.z;
|
||||
|
||||
// ray slope classification
|
||||
if (m_direction.x < 0)
|
||||
{
|
||||
if (m_direction.y < 0)
|
||||
{
|
||||
if (m_direction.z < 0)
|
||||
{
|
||||
classification = MMM;
|
||||
}
|
||||
else if (m_direction.z > 0)
|
||||
{
|
||||
classification = MMP;
|
||||
}
|
||||
else
|
||||
{ //(m_direction.z >= 0)
|
||||
classification = MMO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //(m_direction.y >= 0)
|
||||
if (m_direction.z < 0)
|
||||
{
|
||||
if (m_direction.y == 0)
|
||||
{
|
||||
classification = MOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = MPM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //(m_direction.z >= 0)
|
||||
if ((m_direction.y == 0) && (m_direction.z == 0))
|
||||
{
|
||||
classification = MOO;
|
||||
}
|
||||
else if (m_direction.z == 0)
|
||||
{
|
||||
classification = MPO;
|
||||
}
|
||||
else if (m_direction.y == 0)
|
||||
{
|
||||
classification = MOP;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = MPP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //(m_direction.x >= 0)
|
||||
if (m_direction.y < 0)
|
||||
{
|
||||
if (m_direction.z < 0)
|
||||
{
|
||||
if (m_direction.x == 0)
|
||||
{
|
||||
classification = OMM;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = PMM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //(m_direction.z >= 0)
|
||||
if ((m_direction.x == 0) && (m_direction.z == 0))
|
||||
{
|
||||
classification = OMO;
|
||||
}
|
||||
else if (m_direction.z == 0)
|
||||
{
|
||||
classification = PMO;
|
||||
}
|
||||
else if (m_direction.x == 0)
|
||||
{
|
||||
classification = OMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = PMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //(m_direction.y >= 0)
|
||||
if (m_direction.z < 0)
|
||||
{
|
||||
if ((m_direction.x == 0) && (m_direction.y == 0))
|
||||
{
|
||||
classification = OOM;
|
||||
}
|
||||
else if (m_direction.x == 0)
|
||||
{
|
||||
classification = OPM;
|
||||
}
|
||||
else if (m_direction.y == 0)
|
||||
{
|
||||
classification = POM;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = PPM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //(m_direction.z > 0)
|
||||
if (m_direction.x == 0)
|
||||
{
|
||||
if (m_direction.y == 0)
|
||||
{
|
||||
classification = OOP;
|
||||
}
|
||||
else if (m_direction.z == 0)
|
||||
{
|
||||
classification = OPO;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = OPP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_direction.y == 0) && (m_direction.z == 0))
|
||||
{
|
||||
classification = POO;
|
||||
}
|
||||
else if (m_direction.y == 0)
|
||||
{
|
||||
classification = POP;
|
||||
}
|
||||
else if (m_direction.z == 0)
|
||||
{
|
||||
classification = PPO;
|
||||
}
|
||||
else
|
||||
{
|
||||
classification = PPP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ray Ray::refract(const Vector3& newOrigin, const Vector3& normal, float iInside, float iOutside) const
|
||||
{
|
||||
|
||||
Vector3 D = m_direction.refractionDirection(normal, iInside, iOutside);
|
||||
return Ray(newOrigin + (m_direction + normal * (float)sign(m_direction.dot(normal))) * 0.001f, D);
|
||||
}
|
||||
|
||||
|
||||
Ray Ray::reflect(const Vector3& newOrigin, const Vector3& normal) const
|
||||
{
|
||||
|
||||
Vector3 D = m_direction.reflectionDirection(normal);
|
||||
return Ray(newOrigin + (D + normal) * 0.001f, D);
|
||||
}
|
||||
|
||||
|
||||
Vector3 Ray::intersection(const Plane& plane) const
|
||||
{
|
||||
float d;
|
||||
Vector3 normal = plane.normal();
|
||||
plane.getEquation(normal, d);
|
||||
float rate = m_direction.dot(normal);
|
||||
|
||||
if (rate >= 0.0f)
|
||||
{
|
||||
return Vector3::inf();
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = -(d + m_origin.dot(normal)) / rate;
|
||||
return m_origin + m_direction * t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float Ray::intersectionTime(const class Sphere& sphere, bool solid) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
return CollisionDetection::collisionTimeForMovingPointFixedSphere(m_origin, m_direction, sphere, dummy, dummy, solid);
|
||||
}
|
||||
|
||||
|
||||
float Ray::intersectionTime(const class Plane& plane) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
return CollisionDetection::collisionTimeForMovingPointFixedPlane(m_origin, m_direction, plane, dummy);
|
||||
}
|
||||
|
||||
|
||||
float Ray::intersectionTime(const class Box& box) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
float time = CollisionDetection::collisionTimeForMovingPointFixedBox(m_origin, m_direction, box, dummy);
|
||||
|
||||
if ((time == finf()) && (box.contains(m_origin)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float Ray::intersectionTime(const class AABox& box) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
bool inside;
|
||||
float time = CollisionDetection::collisionTimeForMovingPointFixedAABox(m_origin, m_direction, box, dummy, inside);
|
||||
|
||||
if ((time == finf()) && inside)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace G3D
|
||||
390
engine/3d/src/Ray.hpp
Normal file
390
engine/3d/src/Ray.hpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/**
|
||||
Aya - We are not using the G3D 8.0 G3D::Ray, intstead we are using the Aya:RbxRay from old G3D.
|
||||
@file Ray.h
|
||||
|
||||
Ray class
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2002-07-12
|
||||
@edited 2009-06-29
|
||||
*/
|
||||
|
||||
#ifndef G3D_Ray_h
|
||||
#define G3D_Ray_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Triangle.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
A 3D Ray.
|
||||
*/
|
||||
class Ray
|
||||
{
|
||||
private:
|
||||
friend class Intersect;
|
||||
|
||||
Vector3 m_origin;
|
||||
|
||||
/** Unit length */
|
||||
Vector3 m_direction;
|
||||
|
||||
/** 1.0 / direction */
|
||||
Vector3 m_invDirection;
|
||||
|
||||
|
||||
// The following are for the "ray slope" optimization from
|
||||
// "Fast Ray / Axis-Aligned Bounding Box Overlap Tests using Ray Slopes"
|
||||
// by Martin Eisemann, Thorsten Grosch, Stefan M<>ller and Marcus Magnor
|
||||
// Computer Graphics Lab, TU Braunschweig, Germany and
|
||||
// University of Koblenz-Landau, Germany*/
|
||||
enum Classification
|
||||
{
|
||||
MMM,
|
||||
MMP,
|
||||
MPM,
|
||||
MPP,
|
||||
PMM,
|
||||
PMP,
|
||||
PPM,
|
||||
PPP,
|
||||
POO,
|
||||
MOO,
|
||||
OPO,
|
||||
OMO,
|
||||
OOP,
|
||||
OOM,
|
||||
OMM,
|
||||
OMP,
|
||||
OPM,
|
||||
OPP,
|
||||
MOM,
|
||||
MOP,
|
||||
POM,
|
||||
POP,
|
||||
MMO,
|
||||
MPO,
|
||||
PMO,
|
||||
PPO
|
||||
};
|
||||
Classification classification;
|
||||
// ray slope
|
||||
float ibyj, jbyi, kbyj, jbyk, ibyk, kbyi;
|
||||
// Precomputed components
|
||||
float c_xy, c_xz, c_yx, c_yz, c_zx, c_zy;
|
||||
|
||||
public:
|
||||
void set(const Vector3& origin, const Vector3& direction);
|
||||
|
||||
inline const Vector3& origin() const
|
||||
{
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
/** Unit direction vector. */
|
||||
inline const Vector3& direction() const
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
/** Component-wise inverse of direction vector. May have inf() components */
|
||||
inline const Vector3& invDirection() const
|
||||
{
|
||||
return m_invDirection;
|
||||
}
|
||||
|
||||
inline Ray()
|
||||
{
|
||||
set(Vector3::zero(), Vector3::unitX());
|
||||
}
|
||||
|
||||
inline Ray(const Vector3& origin, const Vector3& direction)
|
||||
{
|
||||
set(origin, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a Ray from a origin and a (nonzero) unit direction.
|
||||
*/
|
||||
static Ray fromOriginAndDirection(const Vector3& point, const Vector3& direction)
|
||||
{
|
||||
debugAssert(1); /// Aya - Do not use, intead use Aya::RbxRay
|
||||
return Ray(point, direction);
|
||||
}
|
||||
|
||||
/** Advances the origin along the direction by @a distance */
|
||||
inline Ray bump(float distance) const
|
||||
{
|
||||
return Ray(m_origin + m_direction * distance, m_direction);
|
||||
}
|
||||
|
||||
/** Advances the origin along the @a bumpDirection by @a distance and returns the new ray*/
|
||||
inline Ray bump(float distance, const Vector3& bumpDirection) const
|
||||
{
|
||||
return Ray(m_origin + bumpDirection * distance, m_direction);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the closest point on the Ray to point.
|
||||
*/
|
||||
Vector3 closestPoint(const Vector3& point) const
|
||||
{
|
||||
float t = m_direction.dot(point - m_origin);
|
||||
if (t < 0)
|
||||
{
|
||||
return m_origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_origin + m_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 sphere or the (solid) ball bounded by the sphere.
|
||||
Will be 0 if inside the sphere, inf if there is no intersection.
|
||||
|
||||
The ray direction is <B>not</B> normalized. If the ray direction
|
||||
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.
|
||||
|
||||
\param solid If true, rays inside the sphere immediately intersect (good for collision detection). If false, they hit the opposite side of the
|
||||
sphere (good for ray tracing).
|
||||
*/
|
||||
float intersectionTime(const class Sphere& sphere, bool solid = false) 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, m_direction, edge2);
|
||||
|
||||
// if determinant is near zero, ray lies in plane of triangle
|
||||
const float det = DOT(edge1, pvec);
|
||||
|
||||
if (det < EPSILON)
|
||||
{
|
||||
return finf();
|
||||
}
|
||||
|
||||
// calculate distance from vert0 to ray origin
|
||||
SUB(tvec, m_origin, vert0);
|
||||
|
||||
// calculate U parameter and test bounds
|
||||
u = DOT(tvec, pvec);
|
||||
if ((u < 0.0f) || (u > det))
|
||||
{
|
||||
// Hit the plane outside the triangle
|
||||
return finf();
|
||||
}
|
||||
|
||||
// prepare to test V parameter
|
||||
CROSS(qvec, tvec, edge1);
|
||||
|
||||
// calculate V parameter and test bounds
|
||||
v = DOT(m_direction, qvec);
|
||||
if ((v < 0.0f) || (u + v > det))
|
||||
{
|
||||
// Hit the plane outside the triangle
|
||||
return finf();
|
||||
}
|
||||
|
||||
|
||||
// Case where we don't need correct (u, v):
|
||||
const float t = DOT(edge2, qvec);
|
||||
|
||||
if (t >= 0.0f)
|
||||
{
|
||||
// Note that det must be positive
|
||||
return t / det;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We had to travel backwards in time to intersect
|
||||
return finf();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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, m_direction, edge2);
|
||||
|
||||
// if determinant is near zero, ray lies in plane of triangle
|
||||
const float det = DOT(edge1, pvec);
|
||||
|
||||
if (det < EPSILON)
|
||||
{
|
||||
return finf();
|
||||
}
|
||||
|
||||
// calculate distance from vert0 to ray origin
|
||||
SUB(tvec, m_origin, vert0);
|
||||
|
||||
// calculate U parameter and test bounds
|
||||
u = DOT(tvec, pvec);
|
||||
if ((u < 0.0f) || (u > det))
|
||||
{
|
||||
// Hit the plane outside the triangle
|
||||
return finf();
|
||||
}
|
||||
|
||||
// prepare to test V parameter
|
||||
CROSS(qvec, tvec, edge1);
|
||||
|
||||
// calculate V parameter and test bounds
|
||||
v = DOT(m_direction, qvec);
|
||||
if ((v < 0.0f) || (u + v > det))
|
||||
{
|
||||
// Hit the plane outside the triangle
|
||||
return finf();
|
||||
}
|
||||
|
||||
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 finf();
|
||||
}
|
||||
}
|
||||
|
||||
#undef EPSILON
|
||||
#undef CROSS
|
||||
#undef DOT
|
||||
#undef SUB
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
489
engine/3d/src/RbxCamera.cpp
Normal file
489
engine/3d/src/RbxCamera.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
/**
|
||||
Roblox
|
||||
This is not the G3D 8.0 GCamera, instead it is from the old G3D release modified by Roblox.
|
||||
@file RbxCamera.cpp
|
||||
|
||||
@author Morgan McGuire, matrix@graphics3d.com
|
||||
|
||||
@created 2001-04-15
|
||||
@edited 2006-01-11
|
||||
*/
|
||||
|
||||
#include "RbxCamera.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "Ray.hpp"
|
||||
#include "Matrix4.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
RbxCamera::RbxCamera()
|
||||
{
|
||||
nearPlane = 0.1f;
|
||||
farPlane = (float)inf();
|
||||
setFieldOfView((float)G3D::toRadians(55.0f));
|
||||
}
|
||||
|
||||
|
||||
RbxCamera::~RbxCamera() {}
|
||||
|
||||
|
||||
CoordinateFrame RbxCamera::coordinateFrame() const
|
||||
{
|
||||
return cframe;
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::getCoordinateFrame(CoordinateFrame& c) const
|
||||
{
|
||||
c = cframe;
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::setCoordinateFrame(const CoordinateFrame& c)
|
||||
{
|
||||
cframe = c;
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::setFieldOfView(float angle)
|
||||
{
|
||||
debugAssert((angle < G3D::pi()) && (angle > 0));
|
||||
|
||||
fieldOfView = angle;
|
||||
|
||||
// Solve for the corresponding image plane depth, as if the extent
|
||||
// of the film was 1x1.
|
||||
imagePlaneDepth = 1.0f / (2.0f * tanf(angle / 2.0f));
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::setImagePlaneDepth(float depth, const class G3D::Rect2D& viewport)
|
||||
{
|
||||
|
||||
debugAssert(depth > 0);
|
||||
setFieldOfView(2.0f * atanf(viewport.height() / (2.0f * depth)));
|
||||
}
|
||||
|
||||
|
||||
float RbxCamera::getImagePlaneDepth(const class G3D::Rect2D& viewport) const
|
||||
{
|
||||
|
||||
// The image plane depth has been pre-computed for
|
||||
// a 1x1 image. Now that the image is width x height,
|
||||
// we need to scale appropriately.
|
||||
|
||||
return imagePlaneDepth * viewport.height();
|
||||
}
|
||||
|
||||
|
||||
float RbxCamera::getViewportWidth(const G3D::Rect2D& viewport) const
|
||||
{
|
||||
return getViewportHeight(viewport) * viewport.width() / viewport.height();
|
||||
}
|
||||
|
||||
|
||||
float RbxCamera::getViewportHeight(const G3D::Rect2D& viewport) const
|
||||
{
|
||||
(void)viewport;
|
||||
return nearPlane / imagePlaneDepth;
|
||||
}
|
||||
|
||||
|
||||
// Aya
|
||||
Aya::RbxRay RbxCamera::worldRay(float x, float y, const G3D::Rect2D& viewport) const
|
||||
{
|
||||
|
||||
int screenWidth = G3D::iFloor(viewport.width());
|
||||
int screenHeight = G3D::iFloor(viewport.height());
|
||||
|
||||
Vector3 origin = cframe.translation;
|
||||
|
||||
float cx = screenWidth / 2.0f;
|
||||
float cy = screenHeight / 2.0f;
|
||||
|
||||
Vector3 direction = Vector3((x - cx), -(y - cy), -(getImagePlaneDepth(viewport)));
|
||||
|
||||
|
||||
direction = cframe.vectorToWorldSpace(direction);
|
||||
|
||||
// Normalize the direction (we didn't do it before)
|
||||
direction = direction.direction();
|
||||
|
||||
return Aya::RbxRay::fromOriginAndDirection(origin, direction);
|
||||
}
|
||||
// ==================
|
||||
|
||||
|
||||
Vector3 RbxCamera::project(const Vector3& point, const G3D::Rect2D& viewport) const
|
||||
{
|
||||
|
||||
int screenWidth = (int)viewport.width();
|
||||
int screenHeight = (int)viewport.height();
|
||||
|
||||
Vector3 out = cframe.pointToObjectSpace(point);
|
||||
float w = out.z * (-1.0f);
|
||||
|
||||
if (w <= 0)
|
||||
{
|
||||
// provide at least basic quadrant information.
|
||||
// (helps with clipping)
|
||||
return Vector3(((out.x < 0) ? -std::numeric_limits<float>::infinity() : std::numeric_limits<float>::infinity()),
|
||||
((out.y > 0) ? -std::numeric_limits<float>::infinity() : std::numeric_limits<float>::infinity()), std::numeric_limits<float>::infinity());
|
||||
}
|
||||
debugAssert(w > 0);
|
||||
|
||||
// Find where it hits an image plane of these dimensions
|
||||
float zImagePlane = getImagePlaneDepth(viewport);
|
||||
|
||||
// Recover the distance
|
||||
float rhw = zImagePlane / w;
|
||||
|
||||
// Add the image center, flip the y axis
|
||||
out.x = screenWidth / 2.0f - (rhw * out.x * (-1.0f));
|
||||
out.y = screenHeight / 2.0f - (rhw * out.y);
|
||||
out.z = rhw;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Vector3 RbxCamera::inverseProject(const Vector3& point, const G3D::Rect2D& viewport) const
|
||||
{
|
||||
|
||||
int screenWidth = (int)viewport.width();
|
||||
int screenHeight = (int)viewport.height();
|
||||
|
||||
float rhw = point.z;
|
||||
Vector3 p;
|
||||
// back to [-1..1][-1..1] coords ?
|
||||
p.x = (screenWidth / 2.0f - point.x) / (rhw * (-1.0f));
|
||||
p.y = (screenHeight / 2.0f - point.y) / (rhw);
|
||||
|
||||
float zImagePlane = getImagePlaneDepth(viewport);
|
||||
float w = zImagePlane / rhw;
|
||||
p.z = w * (-1.0f);
|
||||
|
||||
Vector3 out = cframe.pointToWorldSpace(p);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Matrix4 RbxCamera::projectionMatrix(const class G3D::Rect2D& viewport) const
|
||||
{
|
||||
double pixelAspect = viewport.width() / viewport.height();
|
||||
|
||||
// Half extents of viewport
|
||||
double y = -nearPlaneZ() * tan(getFieldOfView() / 2.0);
|
||||
double x = y * pixelAspect;
|
||||
|
||||
double r, l, t, b, n, f;
|
||||
n = -nearPlaneZ();
|
||||
f = -farPlaneZ();
|
||||
r = x;
|
||||
l = -x;
|
||||
t = y;
|
||||
b = -y;
|
||||
|
||||
return Matrix4::perspectiveProjection(l, r, b, t, n, f);
|
||||
}
|
||||
|
||||
|
||||
float RbxCamera::worldToScreenSpaceArea(float area, float z, const G3D::Rect2D& viewport) const
|
||||
{
|
||||
|
||||
if (z >= 0)
|
||||
{
|
||||
return (float)inf();
|
||||
}
|
||||
|
||||
float zImagePlane = getImagePlaneDepth(viewport);
|
||||
|
||||
return area * (float)G3D::square(zImagePlane / z);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
double RbxCamera::getZValue(
|
||||
double x,
|
||||
double y,
|
||||
const class G3D::Rect2D& viewport int width,
|
||||
int height,
|
||||
double lineOffset) const {
|
||||
|
||||
double depth = renderDevice->getDepthBufferValue((int)x, (int)(height - y));
|
||||
|
||||
double n = -nearPlane;
|
||||
double f = -farPlane;
|
||||
|
||||
// Undo the hyperbolic scaling.
|
||||
// Derivation:
|
||||
// a = ((1/out) - (1/n)) / ((1/f) - (1/n))
|
||||
// depth = (1-a) * lineOffset) + (a * 1)
|
||||
//
|
||||
// depth = lineOffset + a * (-lineOffset + 1)
|
||||
// depth = lineOffset + (((1/z) - (1/n)) / ((1/f) - (1/n))) * (1 - lineOffset)
|
||||
// depth - lineOffset = (((1/z) - (1/n)) / ((1/f) - (1/n))) * (1 - lineOffset)
|
||||
//
|
||||
//(depth - lineOffset) / (1 - lineOffset) = (((1/z) - (1/n)) / ((1/f) - (1/n)))
|
||||
//((1/f) - (1/n)) * (depth - lineOffset) / (1 - lineOffset) = ((1/z) - (1/n))
|
||||
//(((1/f) - (1/n)) * (depth - lineOffset) / (1 - lineOffset)) + 1/n = (1/z)
|
||||
//
|
||||
// z = 1/( (((1/f) - (1/n)) * (depth - lineOffset) / (1 - lineOffset)) + 1/n)
|
||||
|
||||
if (f >= inf) {
|
||||
// Infinite far plane
|
||||
return 1 / (((-1/n) * (depth - lineOffset) / (1 - lineOffset)) + 1/n);
|
||||
} else {
|
||||
return 1 / ((((1/f) - (1/n)) * (depth - lineOffset) / (1 - lineOffset)) + 1/n);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void RbxCamera::getClipPlanes(const G3D::Rect2D& viewport, Array<Plane>& clip) const
|
||||
{
|
||||
|
||||
Frustum fr;
|
||||
frustum(viewport, fr);
|
||||
clip.resize(fr.faceArray.size(), G3D::DONT_SHRINK_UNDERLYING_ARRAY);
|
||||
for (int f = 0; f < clip.size(); ++f)
|
||||
{
|
||||
clip[f] = fr.faceArray[f].plane;
|
||||
}
|
||||
|
||||
/*
|
||||
clip.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
|
||||
|
||||
double screenWidth = viewport.width();
|
||||
double screenHeight = viewport.height();
|
||||
|
||||
// First construct the planes. Do this in the order of near, left,
|
||||
// right, bottom, top, far so that early out clipping tests are likely
|
||||
// to end quickly.
|
||||
|
||||
double fovx = screenWidth * fieldOfView / screenHeight;
|
||||
|
||||
// Near (recall that nearPlane, farPlane are positive numbers, so
|
||||
// we need to negate them to produce actual z values.)
|
||||
clip.append(Plane(Vector3(0,0,-1), Vector3(0,0,-nearPlane)));
|
||||
|
||||
// Right
|
||||
clip.append(Plane(Vector3(-cos(fovx/2), 0, -sin(fovx/2)), Vector3::zero()));
|
||||
|
||||
// Left
|
||||
clip.append(Plane(Vector3(-clip.last().normal().x, 0, clip.last().normal().z), Vector3::zero()));
|
||||
|
||||
// Top
|
||||
clip.append(Plane(Vector3(0, -cos(fieldOfView/2), -sin(fieldOfView/2)), Vector3::zero()));
|
||||
|
||||
// Bottom
|
||||
clip.append(Plane(Vector3(0, -clip.last().normal().y, clip.last().normal().z), Vector3::zero()));
|
||||
|
||||
// Far
|
||||
if (farPlane < inf()) {
|
||||
clip.append(Plane(Vector3(0, 0, 1), Vector3(0, 0, -farPlane)));
|
||||
}
|
||||
|
||||
// Now transform the planes to world space
|
||||
for (int p = 0; p < clip.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;
|
||||
|
||||
clip[p].getEquation(normal, d);
|
||||
|
||||
Vector3 newNormal = cframe.rotation * normal;
|
||||
|
||||
if (isFinite(d)) {
|
||||
d = (newNormal * -d + cframe.translation).dot(newNormal);
|
||||
clip[p] = Plane(newNormal, newNormal * d);
|
||||
} else {
|
||||
// When d is infinite, we can't multiply 0's by it without
|
||||
// generating NaNs.
|
||||
clip[p] = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
RbxCamera::Frustum RbxCamera::frustum(const G3D::Rect2D& viewport) const
|
||||
{
|
||||
Frustum f;
|
||||
frustum(viewport, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::frustum(const G3D::Rect2D& viewport, Frustum& fr) const
|
||||
{
|
||||
|
||||
fr.vertexPos.fastClear();
|
||||
fr.faceArray.fastClear();
|
||||
// The volume is the convex hull of the vertices definining the view
|
||||
// frustum and the light source point at infinity.
|
||||
|
||||
const float x = getViewportWidth(viewport) / 2;
|
||||
const float y = getViewportHeight(viewport) / 2;
|
||||
const float z = nearPlaneZ();
|
||||
const float w = z / farPlaneZ();
|
||||
const float fovx = x * fieldOfView / 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, -nearPlane));
|
||||
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(fieldOfView / 2.0f), -sinf(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 (farPlane < inf())
|
||||
{
|
||||
face.plane = Plane(Vector3(0, 0, 1), Vector3(0, 0, -farPlane));
|
||||
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] = 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 = cframe.rotation * normal;
|
||||
|
||||
if (G3D::isFinite(d))
|
||||
{
|
||||
d = (newNormal * -d + 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RbxCamera::Frustum::containsPoint(const Vector3& point) const
|
||||
{
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{ // ignore front, back
|
||||
const G3D::Plane& plane = faceArray[i].plane;
|
||||
if (!plane.halfSpaceContains(point))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RbxCamera::Frustum::intersectsSphere(const Vector3& center, float radius) const
|
||||
{
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{ // ignore front, back
|
||||
const G3D::Plane& p = faceArray[i].plane;
|
||||
G3D::Plane offsetplane(p.normal(), p.distance() - radius);
|
||||
if (!offsetplane.halfSpaceContains(center))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RbxCamera::get3DViewportCorners(const G3D::Rect2D& viewport, Vector3& outUR, Vector3& outUL, Vector3& outLL, Vector3& outLR) const
|
||||
{
|
||||
|
||||
// Must be kept in sync with frustum()
|
||||
const float sign = (-1.0f);
|
||||
const float w = -sign * getViewportWidth(viewport) / 2.0f;
|
||||
const float h = getViewportHeight(viewport) / 2.0f;
|
||||
const float z = -sign * 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 = cframe.pointToWorldSpace(outUR);
|
||||
outUL = cframe.pointToWorldSpace(outUL);
|
||||
outLR = cframe.pointToWorldSpace(outLR);
|
||||
outLL = cframe.pointToWorldSpace(outLL);
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::setPosition(const Vector3& t)
|
||||
{
|
||||
cframe.translation = t;
|
||||
}
|
||||
|
||||
|
||||
void RbxCamera::lookAt(const Vector3& position, const Vector3& up)
|
||||
{
|
||||
cframe.lookAt(position, up);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
258
engine/3d/src/RbxCamera.hpp
Normal file
258
engine/3d/src/RbxCamera.hpp
Normal file
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
Roblox
|
||||
This is not the G3D 8.0 GCamera, instead it is from the old G3D release modified by Roblox.
|
||||
@file RbxCamera.h
|
||||
|
||||
@maintainer Morgan McGuire, matrix@graphics3d.com
|
||||
|
||||
@created 2001-06-02
|
||||
@edited 2006-02-11
|
||||
*/
|
||||
|
||||
#ifndef G3D_AYA_CAMERA_H
|
||||
#define G3D_AYA_CAMERA_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Plane.hpp"
|
||||
#include "Rect2D.hpp"
|
||||
#include "debugAssert.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
using G3D::Array;
|
||||
using G3D::CoordinateFrame;
|
||||
using G3D::Matrix4;
|
||||
using G3D::Vector3;
|
||||
using G3D::Vector4;
|
||||
|
||||
/**
|
||||
There is a viewport of width x height size in world space that corresponds to
|
||||
a screenWidth x screenHeight pixel grid on a
|
||||
renderDevice->getWidth() x renderDevice->getHeight()
|
||||
window.
|
||||
|
||||
All viewport arguments are the pixel bounds of the viewport-- e.g.,
|
||||
RenderDevice::getViewport().
|
||||
*/
|
||||
class RbxCamera
|
||||
{
|
||||
private:
|
||||
/**
|
||||
Vertical field of view (in radians)
|
||||
*/
|
||||
float fieldOfView;
|
||||
|
||||
/**
|
||||
The image plane depth corresponding to a vertical field of
|
||||
view, where the film size is 1x1.
|
||||
*/
|
||||
float imagePlaneDepth;
|
||||
|
||||
/**
|
||||
Clipping plane, *not* imaging plane. Positive numbers.
|
||||
*/
|
||||
float nearPlane;
|
||||
|
||||
/**
|
||||
Positive
|
||||
*/
|
||||
float farPlane;
|
||||
|
||||
CoordinateFrame cframe;
|
||||
|
||||
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<G3D::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;
|
||||
|
||||
bool containsPoint(const Vector3& point) const;
|
||||
bool intersectsSphere(const Vector3& center, float radius) const;
|
||||
};
|
||||
|
||||
RbxCamera();
|
||||
|
||||
virtual ~RbxCamera();
|
||||
|
||||
|
||||
CoordinateFrame coordinateFrame() const;
|
||||
void getCoordinateFrame(CoordinateFrame& c) const;
|
||||
void setCoordinateFrame(const CoordinateFrame& c);
|
||||
|
||||
/**
|
||||
Sets the horizontal field of view, in radians. The
|
||||
initial angle is toRadians(55).
|
||||
<UL>
|
||||
<LI> toRadians(50) - Telephoto
|
||||
<LI> toRadians(110) - Normal
|
||||
<LI> toRadians(140) - Wide angle
|
||||
</UL>
|
||||
*/
|
||||
void setFieldOfView(float angle);
|
||||
|
||||
/**
|
||||
Sets the field of view based on a desired image plane depth
|
||||
(<I>s'</I>) and film dimensions in world space. Depth must be positive. Width,
|
||||
depth, and height are measured in the same units (meters are
|
||||
recommended). The field of view will span the diagonal to the
|
||||
image.<P> <I>Note</I>: to simulate a 35mm RbxCamera, set width =
|
||||
0.36 mm and height = 0.24 mm. The width and height used are
|
||||
generally not the pixel dimensions of the image.
|
||||
*/
|
||||
void setImagePlaneDepth(float depth, const class G3D::Rect2D& viewport);
|
||||
|
||||
inline double getFieldOfView() const
|
||||
{
|
||||
return fieldOfView;
|
||||
}
|
||||
|
||||
/**
|
||||
Projects a world space point onto a width x height screen. The
|
||||
returned coordinate uses pixmap addressing: x = right and y =
|
||||
down. The resulting z value is <I>rhw</I>.
|
||||
|
||||
If the point is behind the camera, Vector3::inf() is returned.
|
||||
*/
|
||||
Vector3 project(const G3D::Vector3& point, const class G3D::Rect2D& viewport) const;
|
||||
|
||||
Vector3 inverseProject(const Vector3& point, const G3D::Rect2D& viewport) const;
|
||||
|
||||
Matrix4 projectionMatrix(const class G3D::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 G3D::Rect2D& viewport) const;
|
||||
|
||||
/**
|
||||
Returns the world space 3D viewport corners. These
|
||||
are at the near clipping plane. The corners are constructed
|
||||
from the nearPlaneZ, getViewportWidth, and getViewportHeight.
|
||||
"left" and "right" are from the RbxCamera's perspective.
|
||||
*/
|
||||
void get3DViewportCorners(const class G3D::Rect2D& viewport, Vector3& outUR, Vector3& outUL, Vector3& outLL, Vector3& outLR) const;
|
||||
|
||||
/**
|
||||
Returns the image plane depth, <I>s'</I>, given the current field
|
||||
of view for film of dimensions width x height. See
|
||||
setImagePlaneDepth for a discussion of worldspace values width and height.
|
||||
*/
|
||||
float getImagePlaneDepth(const class G3D::Rect2D& viewport) 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.
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
Aya::RbxRay worldRay(float x, float y, const class G3D::Rect2D& viewport) const;
|
||||
|
||||
|
||||
/**
|
||||
Returns a negative z-value.
|
||||
*/
|
||||
inline float nearPlaneZ() const
|
||||
{
|
||||
return -nearPlane;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a negative z-value.
|
||||
*/
|
||||
inline float farPlaneZ() const
|
||||
{
|
||||
return -farPlane;
|
||||
}
|
||||
|
||||
inline void setFarPlaneZ(float z)
|
||||
{
|
||||
debugAssert(z < 0);
|
||||
farPlane = -z;
|
||||
}
|
||||
|
||||
inline void setNearPlaneZ(float z)
|
||||
{
|
||||
debugAssert(z < 0);
|
||||
nearPlane = -z;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the RbxCamera space width of the viewport.
|
||||
*/
|
||||
float getViewportWidth(const class G3D::Rect2D& viewport) const;
|
||||
|
||||
/**
|
||||
Returns the RbxCamera space height of the viewport.
|
||||
*/
|
||||
float getViewportHeight(const class G3D::Rect2D& viewport) const;
|
||||
|
||||
/**
|
||||
Read back a RbxCamera space z-value at pixel (x, y) from the depth buffer.
|
||||
double getZValue(
|
||||
double x,
|
||||
double y,
|
||||
const class G3D::Rect2D& viewport,
|
||||
double polygonOffset = 0) const;
|
||||
*/
|
||||
|
||||
void setPosition(const Vector3& t);
|
||||
|
||||
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 G3D::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 G3D::Rect2D& viewport, RbxCamera::Frustum& f) const;
|
||||
|
||||
RbxCamera::Frustum frustum(const G3D::Rect2D& viewport) const;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
#endif
|
||||
122
engine/3d/src/RbxRay.cpp
Normal file
122
engine/3d/src/RbxRay.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
@file RbxRay.cpp
|
||||
|
||||
@maintainer Morgan McGuire, matrix@graphics3d.com
|
||||
|
||||
@created 2002-07-12
|
||||
@edited 2004-03-19
|
||||
// Aya: from the old g3d (previous to upgrade to g3d 8.0). This version make our code happier.
|
||||
// It doesn't requires to have unit() vectors.
|
||||
// We should fix this and remove de need of thise file in the future and use the G3D ones.
|
||||
*/
|
||||
|
||||
// #include "platform.hpp"
|
||||
#include "RbxRay.hpp"
|
||||
#include "CollisionDetection.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
RbxRay RbxRay::refract(const Vector3& newOrigin, const Vector3& normal, float iInside, float iOutside) const
|
||||
{
|
||||
|
||||
Vector3 D = m_direction.refractionDirection(normal, iInside, iOutside);
|
||||
return RbxRay::fromOriginAndDirection(newOrigin + (m_direction + normal * (float)sign(m_direction.dot(normal))) * 0.001f, D);
|
||||
}
|
||||
|
||||
|
||||
RbxRay RbxRay::reflect(const Vector3& newOrigin, const Vector3& normal) const
|
||||
{
|
||||
|
||||
Vector3 D = m_direction.reflectionDirection(normal);
|
||||
return RbxRay::fromOriginAndDirection(newOrigin + (D + normal) * 0.001f, D);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vector3 RbxRay::intersectionPlane(const Plane& plane) const
|
||||
{
|
||||
/*
|
||||
float d;
|
||||
Vector3 normal = plane.normal();
|
||||
plane.getEquation(normal, d);
|
||||
float rate = m_direction.dot(normal);
|
||||
|
||||
if (rate >= 0.0f) {
|
||||
return Vector3::inf();
|
||||
} else {
|
||||
float t = -(d + m_origin.dot(normal)) / rate;
|
||||
|
||||
return m_origin + m_direction * t;
|
||||
}
|
||||
*/
|
||||
Vector3 planeNormal = plane.normal();
|
||||
float rate = m_direction.dot(planeNormal);
|
||||
|
||||
if (fabs(rate) < 1e-6f)
|
||||
{ // parallel
|
||||
return Vector3::inf();
|
||||
}
|
||||
else
|
||||
{
|
||||
float distanceOnRay = (plane.distance() - m_origin.dot(planeNormal)) / rate;
|
||||
if (distanceOnRay >= 0.0f)
|
||||
{
|
||||
return m_origin + m_direction * distanceOnRay;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Vector3::inf(); // other side of ray
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float RbxRay::intersectionTime(const class Sphere& sphere) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
return G3D::CollisionDetection::collisionTimeForMovingPointFixedSphere(m_origin, m_direction, sphere, dummy);
|
||||
}
|
||||
|
||||
|
||||
float RbxRay::intersectionTime(const class Plane& plane) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
return G3D::CollisionDetection::collisionTimeForMovingPointFixedPlane(m_origin, m_direction, plane, dummy);
|
||||
}
|
||||
|
||||
|
||||
float RbxRay::intersectionTime(const class Box& box) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
float time = G3D::CollisionDetection::collisionTimeForMovingPointFixedBox(m_origin, m_direction, box, dummy);
|
||||
|
||||
if ((time == inf()) && (box.contains(m_origin)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float RbxRay::intersectionTime(const class AABox& box) const
|
||||
{
|
||||
Vector3 dummy;
|
||||
bool inside;
|
||||
float time = G3D::CollisionDetection::collisionTimeForMovingPointFixedAABox(m_origin, m_direction, box, dummy, inside);
|
||||
|
||||
if ((time == inf()) && inside)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
368
engine/3d/src/RbxRay.hpp
Normal file
368
engine/3d/src/RbxRay.hpp
Normal file
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
@file RbxRay.h
|
||||
|
||||
RbxRay class
|
||||
|
||||
@maintainer Morgan McGuire, matrix@graphics3d.com
|
||||
|
||||
@created 2002-07-12
|
||||
@edited 2006-02-21
|
||||
// Aya: from the old g3d (previous to upgrade to g3d 8.0). This version make our code happier.
|
||||
// It doesn't requires to have unit() vectors.
|
||||
// We should fix this and remove de need of thise file in the future and use the G3D ones.
|
||||
*/
|
||||
|
||||
#ifndef G3D_RbxRay_H
|
||||
#define G3D_RbxRay_H
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Triangle.hpp"
|
||||
#include "Sphere.hpp"
|
||||
#include "Box.hpp"
|
||||
#include "AABox.hpp"
|
||||
#include "Plane.hpp"
|
||||
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
using G3D::AABox;
|
||||
using G3D::Box;
|
||||
using G3D::inf;
|
||||
using G3D::Plane;
|
||||
using G3D::sign;
|
||||
using G3D::Sphere;
|
||||
using G3D::Triangle;
|
||||
using G3D::Vector3;
|
||||
/**
|
||||
A 3D RbxRay.
|
||||
*/
|
||||
class RbxRay
|
||||
{
|
||||
private:
|
||||
Vector3 m_origin;
|
||||
|
||||
/**
|
||||
Not unit length
|
||||
*/
|
||||
Vector3 m_direction;
|
||||
|
||||
public:
|
||||
RbxRay(const Vector3& origin, const Vector3& direction)
|
||||
{
|
||||
this->m_origin = origin;
|
||||
this->m_direction = direction;
|
||||
}
|
||||
|
||||
RbxRay()
|
||||
: m_origin(Vector3::zero())
|
||||
, m_direction(Vector3::zero())
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RbxRay() {}
|
||||
|
||||
bool operator==(const RbxRay& rhs) const
|
||||
{
|
||||
return m_origin == rhs.origin() && m_direction == rhs.direction();
|
||||
}
|
||||
bool operator!=(const RbxRay& other) const
|
||||
{
|
||||
return m_origin != other.origin() || m_direction != other.direction();
|
||||
}
|
||||
|
||||
inline const Vector3& origin() const
|
||||
{
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
/** Not-Unit direction vector. */
|
||||
inline const Vector3& direction() const
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
inline float length() const
|
||||
{
|
||||
return m_direction.length();
|
||||
}
|
||||
|
||||
/// Non Cost versions of above, in lieu of exposing private data.
|
||||
inline Vector3& origin()
|
||||
{
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
/** Not-Unit direction vector. */
|
||||
inline Vector3& direction()
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a Ray from a origin and a (nonzero) direction.
|
||||
*/
|
||||
static RbxRay fromOriginAndDirection(const Vector3& point, const Vector3& direction)
|
||||
{
|
||||
return RbxRay(point, direction);
|
||||
}
|
||||
|
||||
RbxRay unit() const
|
||||
{
|
||||
return RbxRay(m_origin, m_direction.unit());
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the closest point on the Ray to point.
|
||||
*/
|
||||
Vector3 closestPoint(const Vector3& point) const
|
||||
{
|
||||
float t = m_direction.dot(point - this->m_origin);
|
||||
if (t < 0)
|
||||
{
|
||||
return this->m_origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->m_origin + m_direction * t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the closest distance between point and the Ray
|
||||
*/
|
||||
float distance(const Vector3& point) const
|
||||
{
|
||||
return (closestPoint(point) - point).magnitude();
|
||||
}
|
||||
|
||||
/**
|
||||
// modified from G3D - returns intersection of Ray/plane regardless of which side ray is on
|
||||
*/
|
||||
Vector3 intersectionPlane(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. */
|
||||
RbxRay 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. */
|
||||
RbxRay 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 RbxRay::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, m_direction, edge2);
|
||||
|
||||
// if determinant is near zero, ray lies in plane of triangle
|
||||
const float det = DOT(edge1, pvec);
|
||||
|
||||
if (det < EPSILON)
|
||||
{
|
||||
return (float)inf();
|
||||
}
|
||||
|
||||
// calculate distance from vert0 to ray origin
|
||||
SUB(tvec, m_origin, vert0);
|
||||
|
||||
// calculate U parameter and test bounds
|
||||
u = DOT(tvec, pvec);
|
||||
if ((u < 0.0f) || (u > det))
|
||||
{
|
||||
// Hit the plane outside the triangle
|
||||
return (float)inf();
|
||||
}
|
||||
|
||||
// prepare to test V parameter
|
||||
CROSS(qvec, tvec, edge1);
|
||||
|
||||
// calculate V parameter and test bounds
|
||||
v = DOT(m_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 RbxRay::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, m_direction, edge2);
|
||||
|
||||
// if determinant is near zero, ray lies in plane of triangle
|
||||
const float det = DOT(edge1, pvec);
|
||||
|
||||
if (det < EPSILON)
|
||||
{
|
||||
return (float)inf();
|
||||
}
|
||||
|
||||
// calculate distance from vert0 to ray origin
|
||||
SUB(tvec, m_origin, vert0);
|
||||
|
||||
// calculate U parameter and test bounds
|
||||
u = DOT(tvec, pvec);
|
||||
if ((u < 0.0f) || (u > det))
|
||||
{
|
||||
// Hit the plane outside the triangle
|
||||
return (float)inf();
|
||||
}
|
||||
|
||||
// prepare to test V parameter
|
||||
CROSS(qvec, tvec, edge1);
|
||||
|
||||
// calculate V parameter and test bounds
|
||||
v = DOT(m_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 Aya
|
||||
|
||||
#endif
|
||||
17
engine/3d/src/RbxTime.hpp
Normal file
17
engine/3d/src/RbxTime.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef AYA_TIME_H
|
||||
#define AYA_TIME_H
|
||||
#include "G3DGameUnits.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class RbxTime
|
||||
{
|
||||
public:
|
||||
static G3D::RealTime getTick();
|
||||
|
||||
private:
|
||||
static G3D::RealTime m_startTime;
|
||||
};
|
||||
} // namespace Aya
|
||||
|
||||
#endif
|
||||
472
engine/3d/src/Rect2D.hpp
Normal file
472
engine/3d/src/Rect2D.hpp
Normal file
@@ -0,0 +1,472 @@
|
||||
/**
|
||||
@file Rect2D.h
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2003-11-13
|
||||
@created 2009-11-16
|
||||
|
||||
Copyright 2000-2009, Morgan McGuire.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef G3D_Rect2D_h
|
||||
#define G3D_Rect2D_h
|
||||
|
||||
// Linux defines this as a macro
|
||||
#ifdef border
|
||||
#undef border
|
||||
#endif
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "Vector2.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Turn off "conditional expression is constant" warning; MSVC generates this
|
||||
// for debug assertions in inlined methods.
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
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:
|
||||
Rect2D()
|
||||
: min(0, 0)
|
||||
, max(0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a rectangle at 0,0 with the given width and height*/
|
||||
Rect2D(const Vector2& wh)
|
||||
: min(0, 0)
|
||||
, max(wh.x, wh.y)
|
||||
{
|
||||
}
|
||||
Rect2D(const Vector2int16& wh)
|
||||
: min(0, 0)
|
||||
, max(wh.x, wh.y)
|
||||
{
|
||||
}
|
||||
|
||||
/** Computes a rectangle that contains both @a a and @a b.
|
||||
Note that even if @a or @b has zero area, its origin will be included.*/
|
||||
Rect2D(const Rect2D& a, const Rect2D& b)
|
||||
{
|
||||
min = a.min.min(b.min);
|
||||
max = a.max.max(b.max);
|
||||
}
|
||||
|
||||
/** @brief Uniformly random point on the interior */
|
||||
Vector2 randomPoint() const
|
||||
{
|
||||
return Vector2(uniformRandom(0, max.x - min.x) + min.x, uniformRandom(0, max.y - min.y) + min.y);
|
||||
}
|
||||
|
||||
float width() const
|
||||
{
|
||||
return max.x - min.x;
|
||||
}
|
||||
|
||||
float height() const
|
||||
{
|
||||
return max.y - min.y;
|
||||
}
|
||||
|
||||
float x0() const
|
||||
{
|
||||
return min.x;
|
||||
}
|
||||
|
||||
float x1() const
|
||||
{
|
||||
return max.x;
|
||||
}
|
||||
|
||||
float y0() const
|
||||
{
|
||||
return min.y;
|
||||
}
|
||||
|
||||
float y1() const
|
||||
{
|
||||
return max.y;
|
||||
}
|
||||
|
||||
/** Min, min corner */
|
||||
Vector2 x0y0() const
|
||||
{
|
||||
return min;
|
||||
}
|
||||
|
||||
Vector2 x1y0() const
|
||||
{
|
||||
return Vector2(max.x, min.y);
|
||||
}
|
||||
|
||||
Vector2 x0y1() const
|
||||
{
|
||||
return Vector2(min.x, max.y);
|
||||
}
|
||||
|
||||
/** Max,max corner */
|
||||
Vector2 x1y1() const
|
||||
{
|
||||
return max;
|
||||
}
|
||||
|
||||
/** Width and height */
|
||||
Vector2 wh() const
|
||||
{
|
||||
return max - min;
|
||||
}
|
||||
|
||||
Vector2 center() const
|
||||
{
|
||||
return (max + min) * 0.5;
|
||||
}
|
||||
|
||||
float area() const
|
||||
{
|
||||
return width() * height();
|
||||
}
|
||||
|
||||
bool isFinite() const
|
||||
{
|
||||
return (min.isFinite() && max.isFinite());
|
||||
}
|
||||
|
||||
Rect2D lerp(const Rect2D& other, float alpha) const
|
||||
{
|
||||
Rect2D out;
|
||||
|
||||
out.min = min.lerp(other.min, alpha);
|
||||
out.max = max.lerp(other.max, alpha);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static Rect2D xyxy(float x0, float y0, float x1, float y1)
|
||||
{
|
||||
Rect2D r;
|
||||
|
||||
r.min.x = G3D::min(x0, x1);
|
||||
r.min.y = G3D::min(y0, y1);
|
||||
r.max.x = G3D::max(x0, x1);
|
||||
r.max.y = G3D::max(y0, y1);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static Rect2D xyxy(const Vector2& v0, const Vector2& v1)
|
||||
{
|
||||
Rect2D r;
|
||||
|
||||
r.min = v0.min(v1);
|
||||
r.max = v0.max(v1);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static Rect2D xywh(float x, float y, float w, float h)
|
||||
{
|
||||
return xyxy(x, y, x + w, y + h);
|
||||
}
|
||||
|
||||
static Rect2D xywh(const Vector2& v, const Vector2& w)
|
||||
{
|
||||
return xyxy(v.x, v.y, v.x + w.x, v.y + w.y);
|
||||
}
|
||||
|
||||
/** Constructs a Rect2D with infinite boundaries.
|
||||
Use isFinite() to test either min or max.
|
||||
*/
|
||||
static Rect2D inf()
|
||||
{
|
||||
return xyxy(Vector2::inf(), Vector2::inf());
|
||||
}
|
||||
|
||||
bool contains(const Vector2& v) const
|
||||
{
|
||||
return (v.x >= min.x) && (v.y >= min.y) && (v.x <= max.x) && (v.y <= max.y);
|
||||
}
|
||||
|
||||
bool contains(const Rect2D& r) const
|
||||
{
|
||||
return (min.x <= r.min.x) && (min.y <= r.min.y) && (max.x >= r.max.x) && (max.y >= r.max.y);
|
||||
}
|
||||
|
||||
/** True if there is non-zero area to the intersection between @a this and @a r.
|
||||
Note that two rectangles that are adjacent do not intersect because there is
|
||||
zero area to the overlap, even though one of them "contains" the corners of the other.*/
|
||||
bool intersects(const Rect2D& r) const
|
||||
{
|
||||
return (min.x < r.max.x) && (min.y < r.max.y) && (max.x > r.min.x) && (max.y > r.min.y);
|
||||
}
|
||||
|
||||
/** Like intersection, but counts the adjacent case as touching. */
|
||||
bool intersectsOrTouches(const Rect2D& r) const
|
||||
{
|
||||
return (min.x <= r.max.x) && (min.y <= r.max.y) && (max.x >= r.min.x) && (max.y >= r.min.y);
|
||||
}
|
||||
|
||||
Rect2D operator*(float s) const
|
||||
{
|
||||
return xyxy(min.x * s, min.y * s, max.x * s, max.y * s);
|
||||
}
|
||||
|
||||
Rect2D operator/(float s) const
|
||||
{
|
||||
return xyxy(min / s, max / s);
|
||||
}
|
||||
|
||||
Rect2D operator/(const Vector2& s) const
|
||||
{
|
||||
return xyxy(min / s, max / s);
|
||||
}
|
||||
|
||||
Rect2D operator+(const Vector2& v) const
|
||||
{
|
||||
return xyxy(min + v, max + v);
|
||||
}
|
||||
|
||||
Rect2D operator-(const Vector2& v) const
|
||||
{
|
||||
return xyxy(min - v, max - v);
|
||||
}
|
||||
|
||||
bool operator==(const Rect2D& other) const
|
||||
{
|
||||
return (min == other.min) && (max == other.max);
|
||||
}
|
||||
|
||||
bool operator!=(const Rect2D& other) const
|
||||
{
|
||||
return (min != other.min) || (max != other.max);
|
||||
}
|
||||
|
||||
/** Returns the corners in the order: (min,min), (max,min), (max,max), (min,max). */
|
||||
Vector2 corner(int i) const
|
||||
{
|
||||
debugAssert(i >= 0 && i < 4);
|
||||
switch (i & 3)
|
||||
{
|
||||
case 0:
|
||||
return Vector2(min.x, min.y);
|
||||
case 1:
|
||||
return Vector2(max.x, min.y);
|
||||
case 2:
|
||||
return Vector2(max.x, max.y);
|
||||
case 3:
|
||||
return Vector2(min.x, max.y);
|
||||
default:
|
||||
// Should never get here
|
||||
return Vector2(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated
|
||||
@sa expand() */
|
||||
Rect2D border(float delta) const
|
||||
{
|
||||
return Rect2D::xywh(x0() + delta, y0() + delta, width() - 2.0f * delta, height() - 2.0f * delta);
|
||||
}
|
||||
|
||||
/** Returns a new Rect2D that is bigger/smaller by the specified amount
|
||||
(negative is shrink.) */
|
||||
Rect2D expand(float delta) const
|
||||
{
|
||||
float newX = x0() - delta;
|
||||
float newY = y0() - delta;
|
||||
float newW = width() + 2.0f * delta;
|
||||
float newH = height() + 2.0f * delta;
|
||||
|
||||
if (newW < 0.0f)
|
||||
{
|
||||
newX = (x0() + width()) / 2.0f;
|
||||
newW = 0.0f;
|
||||
}
|
||||
|
||||
if (newH < 0.0f)
|
||||
{
|
||||
newY = (y0() + height()) / 2.0f;
|
||||
newH = 0.0f;
|
||||
}
|
||||
return Rect2D::xywh(newX, newY, newW, newH);
|
||||
}
|
||||
|
||||
/**
|
||||
Clips so that the rightmost point of the outPoly is at rect.x1 (e.g. a 800x600 window produces
|
||||
rightmost point 799, not 800). The results are suitable for pixel rendering if iRounded.
|
||||
Templated so that it will work for Vector2,3,4 (the z and w components are interpolated linearly).
|
||||
The template parameter must define T.lerp and contain x and y components.
|
||||
|
||||
If the entire polygon is clipped by a single side, the result will be empty.
|
||||
The result might also have zero area but not be empty.
|
||||
*/
|
||||
template<class T>
|
||||
void clip(const Array<T>& inPoly, Array<T>& outPoly) const
|
||||
{
|
||||
|
||||
const bool greaterThan = true;
|
||||
const bool lessThan = false;
|
||||
const int X = 0;
|
||||
const int Y = 1;
|
||||
|
||||
Array<T> temp;
|
||||
|
||||
bool entirelyClipped = clipSide2D(x0(), lessThan, X, inPoly, temp) || clipSide2D(x1(), greaterThan, X, temp, outPoly) ||
|
||||
clipSide2D(y0(), lessThan, Y, outPoly, temp) || clipSide2D(y1(), greaterThan, Y, temp, outPoly);
|
||||
|
||||
if (entirelyClipped)
|
||||
{
|
||||
outPoly.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns the largest, centered Rect2D that can fit inside this
|
||||
while maintaining the aspect ratio of x:y. Convenient for
|
||||
displaying images in odd-shaped windows.
|
||||
*/
|
||||
Rect2D largestCenteredSubRect(float ww, float hh) const
|
||||
{
|
||||
float textureAspect = hh / ww;
|
||||
float viewAspect = height() / width();
|
||||
|
||||
if (viewAspect > textureAspect)
|
||||
{
|
||||
// The view is too tall
|
||||
float h = width() * textureAspect;
|
||||
float y = (height() - h) / 2;
|
||||
return Rect2D::xywh(0, y, width(), h) + corner(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The view is too wide
|
||||
float w = height() / textureAspect;
|
||||
float x = (width() - w) / 2;
|
||||
return Rect2D::xywh(x, 0, w, height()) + corner(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the overlap region between the two rectangles. This may have zero area
|
||||
if they do not intersect. See the two-Rect2D constructor for a way to compute
|
||||
a union-like rectangle.
|
||||
*/
|
||||
Rect2D intersect(const Rect2D& other) const
|
||||
{
|
||||
if (intersects(other))
|
||||
{
|
||||
return Rect2D::xyxy(min.max(other.min), max.min(other.max));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Rect2D::xywh(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef Rect2D AABox2D;
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
214
engine/3d/src/Set.hpp
Normal file
214
engine/3d/src/Set.hpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
@file Set.h
|
||||
|
||||
Hash set
|
||||
|
||||
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||||
|
||||
@created 2001-12-09
|
||||
@edited 2009-06-10
|
||||
*/
|
||||
|
||||
#ifndef G3D_Set_h
|
||||
#define G3D_Set_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Table.hpp"
|
||||
#include "MemoryManager.hpp"
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/**
|
||||
An unordered data structure that has at most one of each element.
|
||||
Provides O(1) time insert, remove, and member test (contains).
|
||||
|
||||
Set uses G3D::Table internally, which means that the template type T
|
||||
must define a hashCode and operator== function. See G3D::Table for
|
||||
a discussion of these functions.
|
||||
*/
|
||||
// There is not copy constructor or assignment operator defined because
|
||||
// the default ones are correct for Set.
|
||||
template<class T, class HashFunc = HashTrait<T>, class EqualsFunc = EqualsTrait<T>>
|
||||
class Set
|
||||
{
|
||||
|
||||
/**
|
||||
If an object is a member, it is contained in
|
||||
this table.
|
||||
*/
|
||||
Table<T, bool, HashFunc, EqualsFunc> memberTable;
|
||||
|
||||
public:
|
||||
void clearAndSetMemoryManager(const MemoryManager::Ref& m)
|
||||
{
|
||||
memberTable.clearAndSetMemoryManager(m);
|
||||
}
|
||||
|
||||
virtual ~Set() {}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return (int)memberTable.size();
|
||||
}
|
||||
|
||||
bool contains(const T& member) const
|
||||
{
|
||||
return memberTable.containsKey(member);
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts into the table if not already present.
|
||||
Returns true if this is the first time the element was added.
|
||||
*/
|
||||
bool insert(const T& member)
|
||||
{
|
||||
bool isNew = false;
|
||||
memberTable.getCreate(member, isNew) = true;
|
||||
return isNew;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if the element was present and removed. Returns false
|
||||
if the element was not present.
|
||||
*/
|
||||
bool remove(const T& member)
|
||||
{
|
||||
return memberTable.remove(member);
|
||||
}
|
||||
|
||||
/** If @a member is present, sets @a removed to the element
|
||||
being removed and returns true. Otherwise returns false
|
||||
and does not write to @a removed. This is useful when building
|
||||
efficient hashed data structures that wrap Set.
|
||||
*/
|
||||
bool getRemove(const T& member, T& removed)
|
||||
{
|
||||
bool ignore;
|
||||
return memberTable.getRemove(member, removed, ignore);
|
||||
}
|
||||
|
||||
/** If a value that is EqualsFunc to @a member is present, returns a pointer to the
|
||||
version stored in the data structure, otherwise returns NULL.
|
||||
*/
|
||||
const T* getPointer(const T& member) const
|
||||
{
|
||||
return memberTable.getKeyPointer(member);
|
||||
}
|
||||
|
||||
Array<T> getMembers() const
|
||||
{
|
||||
return memberTable.getKeys();
|
||||
}
|
||||
|
||||
void getMembers(Array<T>& keyArray) const
|
||||
{
|
||||
memberTable.getKeys(keyArray);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
memberTable.clear();
|
||||
}
|
||||
|
||||
void deleteAll()
|
||||
{
|
||||
getMembers().deleteAll();
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
C++ STL style iterator variable. See begin().
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
private:
|
||||
friend class Set<T>;
|
||||
|
||||
// Note: this is a Table iterator, we are currently defining
|
||||
// Set iterator
|
||||
typename Table<T, bool>::Iterator it;
|
||||
|
||||
Iterator(const typename Table<T, bool>::Iterator& it)
|
||||
: it(it)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
inline bool operator!=(const Iterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool hasMore() const
|
||||
{
|
||||
return it.hasMore();
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& other) const
|
||||
{
|
||||
return it == other.it;
|
||||
}
|
||||
|
||||
/**
|
||||
Pre increment.
|
||||
*/
|
||||
Iterator& operator++()
|
||||
{
|
||||
++it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
Post increment (slower than preincrement).
|
||||
*/
|
||||
Iterator operator++(int)
|
||||
{
|
||||
Iterator old = *this;
|
||||
++(*this);
|
||||
return old;
|
||||
}
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
return it->key;
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return &(it->key);
|
||||
}
|
||||
|
||||
operator T*() const
|
||||
{
|
||||
return &(it->key);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
C++ STL style iterator method. Returns the first member.
|
||||
Use preincrement (++entry) to get to the next element.
|
||||
Do not modify the set while iterating.
|
||||
*/
|
||||
Iterator begin() const
|
||||
{
|
||||
return Iterator(memberTable.begin());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
C++ STL style iterator method. Returns one after the last iterator
|
||||
element.
|
||||
*/
|
||||
const Iterator end() const
|
||||
{
|
||||
return Iterator(memberTable.end());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
|
||||
#endif
|
||||
206
engine/3d/src/SmallArray.hpp
Normal file
206
engine/3d/src/SmallArray.hpp
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
@file SmallArray.h
|
||||
|
||||
@created 2009-04-26
|
||||
@edited 2010-02-26
|
||||
|
||||
Copyright 2000-2010, Morgan McGuire, http://graphics.cs.williams.edu
|
||||
All rights reserved.
|
||||
*/
|
||||
#ifndef G3D_SmallArray_h
|
||||
#define G3D_SmallArray_h
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "Array.hpp"
|
||||
#include "MemoryManager.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
/** Embeds \a N elements to reduce allocation time and increase
|
||||
memory coherence when working with arrays of arrays.
|
||||
Offers a limited subset of the functionality of G3D::Array.*/
|
||||
template<class T, int N>
|
||||
class SmallArray
|
||||
{
|
||||
private:
|
||||
int m_size;
|
||||
|
||||
/** First N elements */
|
||||
T m_embedded[N];
|
||||
|
||||
/** Remaining elements */
|
||||
Array<T> m_rest;
|
||||
|
||||
public:
|
||||
SmallArray()
|
||||
: m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline int size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void resize(int n, bool shrinkIfNecessary = true)
|
||||
{
|
||||
m_rest.resize(std::max(0, n - N), shrinkIfNecessary);
|
||||
m_size = n;
|
||||
}
|
||||
|
||||
void clear(bool shrinkIfNecessary = true)
|
||||
{
|
||||
resize(0, shrinkIfNecessary);
|
||||
}
|
||||
|
||||
void clearAndSetMemoryManager(MemoryManager::Ref& m)
|
||||
{
|
||||
clear();
|
||||
m_rest.clearAndSetMemoryManager(m);
|
||||
}
|
||||
|
||||
inline T& operator[](int i)
|
||||
{
|
||||
debugAssert(i < m_size && i >= 0);
|
||||
if (i < N)
|
||||
{
|
||||
return m_embedded[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_rest[i - N];
|
||||
}
|
||||
}
|
||||
|
||||
inline const T& operator[](int i) const
|
||||
{
|
||||
debugAssert(i < m_size && i >= 0);
|
||||
if (i < N)
|
||||
{
|
||||
return m_embedded[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_rest[i - N];
|
||||
}
|
||||
}
|
||||
|
||||
inline void push(const T& v)
|
||||
{
|
||||
++m_size;
|
||||
if (m_size <= N)
|
||||
{
|
||||
m_embedded[m_size - 1] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rest.append(v);
|
||||
}
|
||||
}
|
||||
|
||||
inline void append(const T& v)
|
||||
{
|
||||
push(v);
|
||||
}
|
||||
|
||||
void fastRemove(int i, bool shrinkIfNecessary = false)
|
||||
{
|
||||
debugAssert(i < m_size && i >= 0);
|
||||
if (i < N)
|
||||
{
|
||||
if (m_size <= N)
|
||||
{
|
||||
// Exclusively embedded
|
||||
m_embedded[i] = m_embedded[m_size - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move one down from the rest array
|
||||
m_embedded[i] = m_rest.pop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Removing from the rest array
|
||||
m_rest.fastRemove(i - N, shrinkIfNecessary);
|
||||
}
|
||||
--m_size;
|
||||
}
|
||||
|
||||
T pop()
|
||||
{
|
||||
debugAssert(m_size > 0);
|
||||
if (m_size <= N)
|
||||
{
|
||||
// Popping from embedded, don't need a temporary
|
||||
--m_size;
|
||||
return m_embedded[m_size];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Popping from rest
|
||||
--m_size;
|
||||
return m_rest.pop();
|
||||
}
|
||||
}
|
||||
|
||||
inline void popDiscard()
|
||||
{
|
||||
debugAssert(m_size > 0);
|
||||
if (m_size > N)
|
||||
{
|
||||
m_rest.popDiscard();
|
||||
}
|
||||
--m_size;
|
||||
}
|
||||
|
||||
inline T& next()
|
||||
{
|
||||
++m_size;
|
||||
if (m_size <= N)
|
||||
{
|
||||
return m_embedded[m_size - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_rest.next();
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(const T& value) const
|
||||
{
|
||||
for (int i = std::min(m_size, N) - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_embedded[i] == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return m_rest.contains(value);
|
||||
}
|
||||
|
||||
template<int MIN_ELEMENTS, int MIN_BYTES>
|
||||
SmallArray<T, N>& operator=(const Array<T, MIN_ELEMENTS, MIN_BYTES>& src)
|
||||
{
|
||||
resize(src.size());
|
||||
for (int i = 0; i < src.size(); ++i)
|
||||
{
|
||||
(*this)[i] = src[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const T& last() const
|
||||
{
|
||||
return (*this)[size() - 1];
|
||||
}
|
||||
|
||||
inline T& last()
|
||||
{
|
||||
return (*this)[size() - 1];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace G3D
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user