Big re-organization of repository [W.I.P]

This commit is contained in:
Yehonal
2016-08-11 20:25:27 +02:00
parent c62a72c0a8
commit 0f85ce1c54
3016 changed files with 1271 additions and 1 deletions

View File

@@ -0,0 +1,370 @@
/**
@file AABox.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2004-01-10
@edited 2006-01-11
*/
#include "G3D/platform.h"
#include "G3D/AABox.h"
#include "G3D/Box.h"
#include "G3D/Plane.h"
#include "G3D/Sphere.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
const AABox& AABox::maxFinite() {
static const AABox b = AABox(Vector3::minFinite(),
Vector3::maxFinite());
return b;
}
const AABox& AABox::large() {
static const AABox b = AABox(Vector3::minFinite() * 0.5f,
Vector3::maxFinite() * 0.5f);
return b;
}
const AABox& AABox::inf() {
static const AABox b = AABox(-Vector3::inf(), Vector3::inf());
return b;
}
const AABox& AABox::zero() {
static const AABox b = AABox(Vector3::zero(), Vector3::zero());
return b;
}
void AABox::serialize(class BinaryOutput& b) const {
b.writeVector3(lo);
b.writeVector3(hi);
}
void AABox::deserialize(class BinaryInput& b) {
lo = b.readVector3();
hi = b.readVector3();
}
void AABox::split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const {
// Low, medium, and high along the chosen axis
float L = G3D::min(location, lo[axis]);
float M = G3D::min(G3D::max(location, lo[axis]), hi[axis]);
float H = G3D::max(location, hi[axis]);
// Copy over this box.
high = low = *this;
// Now move the split points along the special axis
low.lo[axis] = L;
low.hi[axis] = M;
high.lo[axis] = M;
high.hi[axis] = H;
}
Vector3 AABox::randomSurfacePoint() const {
Vector3 extent = hi - lo;
float aXY = extent.x * extent.y;
float aYZ = extent.y * extent.z;
float aZX = extent.z * extent.x;
float r = (float)uniformRandom(0.0f, aXY + aYZ + aZX);
// Choose evenly between positive and negative face planes
float d = ((float)uniformRandom(0, 1) < 0.5f) ? 0.0f : 1.0f;
// The probability of choosing a given face is proportional to
// its area.
if (r < aXY) {
return
lo +
Vector3(
(float)uniformRandom(0.0f, extent.x),
(float)uniformRandom(0.0f, extent.y),
d * extent.z);
} else if (r < aYZ) {
return
lo +
Vector3(
d * extent.x,
(float)uniformRandom(0, extent.y),
(float)uniformRandom(0, extent.z));
} else {
return
lo +
Vector3(
(float)uniformRandom(0, extent.x),
d * extent.y,
(float)uniformRandom(0, extent.z));
}
}
Vector3 AABox::randomInteriorPoint() const {
return Vector3(
(float)uniformRandom(lo.x, hi.x),
(float)uniformRandom(lo.y, hi.y),
(float)uniformRandom(lo.z, hi.z));
}
bool AABox::intersects(const AABox& other) const {
// Must be overlap along all three axes.
// Try to find a separating axis.
for (int a = 0; a < 3; ++a) {
// |--------|
// |------|
if ((lo[a] > other.hi[a]) ||
(hi[a] < other.lo[a])) {
return false;
}
}
return true;
}
int AABox::dummy = 0;
bool AABox::culledBy(
const Array<Plane>& plane,
int& cullingPlane,
const uint32 _inMask,
uint32& childMask) const {
uint32 inMask = _inMask;
assert(plane.size() < 31);
childMask = 0;
const bool finite =
(abs(lo.x) < G3D::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;
assert(plane.size() < 31);
const bool finite =
(abs(lo.x) < G3D::finf()) &&
(abs(hi.x) < G3D::finf()) &&
(abs(lo.y) < G3D::finf()) &&
(abs(hi.y) < G3D::finf()) &&
(abs(lo.z) < G3D::finf()) &&
(abs(hi.z) < G3D::finf());
// See if there is one plane for which all of the
// vertices are in the negative half space.
for (int p = 0; p < 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;
}
void AABox::getBounds(Sphere& s) const {
s.center = center();
s.radius = extent().length() / 2;
}
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;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/**
@file AreaMemoryManager.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-01-20
@edited 2009-01-20
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/AreaMemoryManager.h"
#include "G3D/System.h"
namespace G3D {
AreaMemoryManager::Buffer::Buffer(size_t size) : m_size(size), m_used(0) {
// Allocate space for a lot of buffers.
m_first = (uint8*)::malloc(m_size);
}
AreaMemoryManager::Buffer::~Buffer() {
::free(m_first);
}
void* AreaMemoryManager::Buffer::alloc(size_t s) {
if (s + m_used > m_size) {
return NULL;
} else {
void* old = m_first + m_used;
m_used += s;
return old;
}
}
bool AreaMemoryManager::isThreadsafe() const {
return false;
}
AreaMemoryManager::Ref AreaMemoryManager::create(size_t sizeHint) {
return new AreaMemoryManager(sizeHint);
}
AreaMemoryManager::AreaMemoryManager(size_t sizeHint) : m_sizeHint(sizeHint) {
debugAssert(sizeHint > 0);
}
AreaMemoryManager::~AreaMemoryManager() {
deallocateAll();
}
size_t AreaMemoryManager::bytesAllocated() const {
return m_sizeHint * m_bufferArray.size();
}
void* AreaMemoryManager::alloc(size_t s) {
void* n = (m_bufferArray.size() > 0) ? m_bufferArray.last()->alloc(s) : NULL;
if (n == NULL) {
// This buffer is full
m_bufferArray.append(new Buffer(max(s, m_sizeHint)));
return m_bufferArray.last()->alloc(s);
} else {
return n;
}
}
void AreaMemoryManager::free(void* x) {
// Intentionally empty; we block deallocate
}
void AreaMemoryManager::deallocateAll() {
m_bufferArray.deleteAll();
m_bufferArray.clear();
}
}

View File

@@ -0,0 +1,81 @@
/**
@file BinaryFormat.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2005-06-10
@edited 2005-06-10
*/
#include "G3D/BinaryFormat.h"
namespace G3D {
int32 byteSize(BinaryFormat f) {
switch (f) {
case BOOL8_BINFMT:
case UINT8_BINFMT:
case INT8_BINFMT:
return 1;
case UINT16_BINFMT:
case INT16_BINFMT:
return 2;
case FLOAT16_BINFMT:
return 2;
case UINT32_BINFMT:
case INT32_BINFMT:
case FLOAT32_BINFMT:
return 4;
case FLOAT64_BINFMT:
case UINT64_BINFMT:
case INT64_BINFMT:
return 8;
case INT128_BINFMT:
case UINT128_BINFMT:
return 16;
case VECTOR2_BINFMT:
return 2 * 4;
case VECTOR2INT16_BINFMT:
return 2 * 2;
case VECTOR3_BINFMT:
return 3 * 4;
case VECTOR3INT16_BINFMT:
return 3 * 2;
case VECTOR4_BINFMT:
return 4 * 4;
case VECTOR4INT16_BINFMT:
return 4 * 4;
case COLOR3_BINFMT:
return 3 * 4;
case COLOR3UINT8_BINFMT:
return 3 * 1;
case COLOR3INT16_BINFMT:
return 3 * 2;
case COLOR4_BINFMT:
return 4 * 4;
case COLOR4UINT8_BINFMT:
return 4 * 1;
case COLOR4INT16_BINFMT:
return 4 * 2;
default:
return -1;
}
}
}

View File

@@ -0,0 +1,621 @@
/**
@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 "G3D/platform.h"
#include "G3D/BinaryInput.h"
#include "G3D/Array.h"
#include "G3D/fileutils.h"
#include "G3D/Log.h"
#include "G3D/FileSystem.h"
#include <zlib.h>
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
#include "zip.h"
#endif
#include <cstring>
namespace G3D {
void BinaryInput::readBool8(std::vector<bool>& out, int64 n) {
out.resize((int)n);
// std::vector optimizes bool in a way that prevents fast reading
for (int64 i = 0; i < n ; ++i) {
out[i] = readBool8();
}
}
void BinaryInput::readBool8(Array<bool>& out, int64 n) {
out.resize(n);
readBool8(out.begin(), n);
}
#define IMPLEMENT_READER(ucase, lcase)\
void BinaryInput::read##ucase(std::vector<lcase>& out, int64 n) {\
out.resize(n);\
read##ucase(&out[0], n);\
}\
\
\
void BinaryInput::read##ucase(Array<lcase>& out, int64 n) {\
out.resize(n);\
read##ucase(out.begin(), n);\
}
IMPLEMENT_READER(UInt8, uint8)
IMPLEMENT_READER(Int8, int8)
IMPLEMENT_READER(UInt16, uint16)
IMPLEMENT_READER(Int16, int16)
IMPLEMENT_READER(UInt32, uint32)
IMPLEMENT_READER(Int32, int32)
IMPLEMENT_READER(UInt64, uint64)
IMPLEMENT_READER(Int64, int64)
IMPLEMENT_READER(Float32, float32)
IMPLEMENT_READER(Float64, float64)
#undef IMPLEMENT_READER
// Data structures that are one byte per element can be
// directly copied, regardles of endian-ness.
#define IMPLEMENT_READER(ucase, lcase)\
void BinaryInput::read##ucase(lcase* out, int64 n) {\
if (sizeof(lcase) == 1) {\
readBytes(out, n);\
} else {\
for (int64 i = 0; i < n ; ++i) {\
out[i] = read##ucase();\
}\
}\
}
IMPLEMENT_READER(Bool8, bool)
IMPLEMENT_READER(UInt8, uint8)
IMPLEMENT_READER(Int8, int8)
#undef IMPLEMENT_READER
#define IMPLEMENT_READER(ucase, lcase)\
void BinaryInput::read##ucase(lcase* out, int64 n) {\
if (m_swapBytes) {\
for (int64 i = 0; i < n; ++i) {\
out[i] = read##ucase();\
}\
} else {\
readBytes(out, sizeof(lcase) * n);\
}\
}
IMPLEMENT_READER(UInt16, uint16)
IMPLEMENT_READER(Int16, int16)
IMPLEMENT_READER(UInt32, uint32)
IMPLEMENT_READER(Int32, int32)
IMPLEMENT_READER(UInt64, uint64)
IMPLEMENT_READER(Int64, int64)
IMPLEMENT_READER(Float32, float32)
IMPLEMENT_READER(Float64, float64)
#undef IMPLEMENT_READER
void BinaryInput::loadIntoMemory(int64 startPosition, int64 minLength) {
// Load the next section of the file
debugAssertM(m_filename != "<memory>", "Read past end of file.");
int64 absPos = m_alreadyRead + m_pos;
if (m_bufferLength < minLength) {
// The current buffer isn't big enough to hold the chunk we want to read.
// This happens if there was little memory available during the initial constructor
// read but more memory has since been freed.
m_bufferLength = minLength;
debugAssert(m_freeBuffer);
m_buffer = (uint8*)System::realloc(m_buffer, m_bufferLength);
if (m_buffer == NULL) {
throw "Tried to read a larger memory chunk than could fit in memory. (2)";
}
}
m_alreadyRead = startPosition;
# ifdef G3D_WIN32
FILE* file = fopen(m_filename.c_str(), "rb");
debugAssert(file);
int ret = fseek(file, (off_t)m_alreadyRead, SEEK_SET);
debugAssert(ret == 0);
size_t toRead = (size_t)G3D::min(m_bufferLength, m_length - m_alreadyRead);
ret = fread(m_buffer, 1, toRead, file);
debugAssert(ret == toRead);
fclose(file);
file = NULL;
# else
FILE* file = fopen(m_filename.c_str(), "rb");
debugAssert(file);
int ret = fseeko(file, (off_t)m_alreadyRead, SEEK_SET);
debugAssert(ret == 0);
size_t toRead = (size_t)G3D::min<int64>(m_bufferLength, m_length - m_alreadyRead);
ret = fread(m_buffer, 1, toRead, file);
debugAssert((size_t)ret == (size_t)toRead);
fclose(file);
file = NULL;
# endif
m_pos = absPos - m_alreadyRead;
debugAssert(m_pos >= 0);
}
const bool BinaryInput::NO_COPY = false;
static bool needSwapBytes(G3DEndian fileEndian) {
return (fileEndian != System::machineEndian());
}
/** Helper used by the constructors for decompression */
static uint32 readUInt32(const uint8* data, bool swapBytes) {
if (swapBytes) {
uint8 out[4];
out[0] = data[3];
out[1] = data[2];
out[2] = data[1];
out[3] = data[0];
return *((uint32*)out);
} else {
return *((uint32*)data);
}
}
void BinaryInput::setEndian(G3DEndian e) {
m_fileEndian = e;
m_swapBytes = needSwapBytes(m_fileEndian);
}
BinaryInput::BinaryInput(
const uint8* data,
int64 dataLen,
G3DEndian dataEndian,
bool compressed,
bool copyMemory) :
m_filename("<memory>"),
m_bitPos(0),
m_bitString(0),
m_beginEndBits(0),
m_alreadyRead(0),
m_bufferLength(0),
m_pos(0) {
m_freeBuffer = copyMemory || compressed;
setEndian(dataEndian);
if (compressed) {
// Read the decompressed size from the first 4 bytes
m_length = G3D::readUInt32(data, m_swapBytes);
debugAssert(m_freeBuffer);
m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
unsigned long L = m_length;
// Decompress with zlib
int64 result = uncompress(m_buffer, (unsigned long*)&L, data + 4, dataLen - 4);
m_length = L;
m_bufferLength = L;
debugAssert(result == Z_OK); (void)result;
} else {
m_length = dataLen;
m_bufferLength = m_length;
if (! copyMemory) {
debugAssert(!m_freeBuffer);
m_buffer = const_cast<uint8*>(data);
} else {
debugAssert(m_freeBuffer);
m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
System::memcpy(m_buffer, data, dataLen);
}
}
}
BinaryInput::BinaryInput(
const std::string& filename,
G3DEndian fileEndian,
bool compressed) :
m_filename(filename),
m_bitPos(0),
m_bitString(0),
m_beginEndBits(0),
m_alreadyRead(0),
m_length(0),
m_bufferLength(0),
m_buffer(NULL),
m_pos(0),
m_freeBuffer(true) {
setEndian(fileEndian);
// Update global file tracker
_internal::currentFilesUsed.insert(m_filename);
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
std::string zipfile;
if (FileSystem::inZipfile(m_filename, zipfile)) {
// Load from zipfile
// zipRead(filename, v, s);
std::string internalFile = m_filename.substr(zipfile.length() + 1);
struct zip* z = zip_open(zipfile.c_str(), ZIP_CHECKCONS, NULL);
{
struct zip_stat info;
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
zip_stat(z, internalFile.c_str(), ZIP_FL_NOCASE, &info);
m_bufferLength = m_length = info.size;
// sets machines up to use MMX, if they want
m_buffer = reinterpret_cast<uint8*>(System::alignedMalloc(m_length, 16));
struct zip_file* zf = zip_fopen( z, internalFile.c_str(), ZIP_FL_NOCASE );
{
int64 test = zip_fread( zf, m_buffer, m_length );
debugAssertM(test == m_length,
internalFile + " was corrupt because it unzipped to the wrong size.");
(void)test;
}
zip_fclose( zf );
}
zip_close( z );
if (compressed) {
decompress();
}
m_freeBuffer = true;
return;
}
#endif
// Figure out how big the file is and verify that it exists.
m_length = FileSystem::size(m_filename);
// Read the file into memory
FILE* file = fopen(m_filename.c_str(), "rb");
if (! file || (m_length == -1)) {
throw format("File not found: \"%s\"", m_filename.c_str());
return;
}
if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) {
// Read only a subset of the file so we don't consume
// all available memory.
m_bufferLength = INITIAL_BUFFER_LENGTH;
} else {
// Either the length is fine or the file is compressed
// and requires us to read the whole thing for zlib.
m_bufferLength = m_length;
}
debugAssert(m_freeBuffer);
m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
if (m_buffer == NULL) {
if (compressed) {
throw "Not enough memory to load compressed file. (1)";
}
// Try to allocate a small array; not much memory is available.
// Give up if we can't allocate even 1k.
while ((m_buffer == NULL) && (m_bufferLength > 1024)) {
m_bufferLength /= 2;
m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
}
}
debugAssert(m_buffer);
fread(m_buffer, m_bufferLength, sizeof(int8), file);
fclose(file);
file = NULL;
if (compressed) {
if (m_bufferLength != m_length) {
throw "Not enough memory to load compressed file. (2)";
}
decompress();
}
}
void BinaryInput::decompress() {
// Decompress
// Use the existing buffer as the source, allocate
// a new buffer to use as the destination.
int64 tempLength = m_length;
m_length = G3D::readUInt32(m_buffer, m_swapBytes);
// The file couldn't have better than 500:1 compression
alwaysAssertM(m_length < m_bufferLength * 500, "Compressed file header is corrupted");
uint8* tempBuffer = m_buffer;
m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
debugAssert(m_buffer);
debugAssert(isValidHeapPointer(tempBuffer));
debugAssert(isValidHeapPointer(m_buffer));
unsigned long L = m_length;
int64 result = uncompress(m_buffer, &L, tempBuffer + 4, tempLength - 4);
m_length = L;
m_bufferLength = m_length;
debugAssertM(result == Z_OK, "BinaryInput/zlib detected corruption in " + m_filename);
(void)result;
System::alignedFree(tempBuffer);
}
void BinaryInput::readBytes(void* bytes, int64 n) {
prepareToRead(n);
debugAssert(isValidPointer(bytes));
memcpy(bytes, m_buffer + m_pos, n);
m_pos += n;
}
BinaryInput::~BinaryInput() {
if (m_freeBuffer) {
System::alignedFree(m_buffer);
}
m_buffer = NULL;
}
uint64 BinaryInput::readUInt64() {
prepareToRead(8);
uint8 out[8];
if (m_swapBytes) {
out[0] = m_buffer[m_pos + 7];
out[1] = m_buffer[m_pos + 6];
out[2] = m_buffer[m_pos + 5];
out[3] = m_buffer[m_pos + 4];
out[4] = m_buffer[m_pos + 3];
out[5] = m_buffer[m_pos + 2];
out[6] = m_buffer[m_pos + 1];
out[7] = m_buffer[m_pos + 0];
} else {
*(uint64*)out = *(uint64*)(m_buffer + m_pos);
}
m_pos += 8;
return *(uint64*)out;
}
std::string BinaryInput::readString(int64 n) {
prepareToRead(n);
debugAssertM((m_pos + n) <= m_length, "Read past end of file");
char *s = (char*)System::alignedMalloc(n + 1, 16);
assert(s != NULL);
memcpy(s, m_buffer + m_pos, n);
// There may not be a null, so make sure
// we add one.
s[n] = '\0';
std::string out = s;
System::alignedFree(s);
s = NULL;
m_pos += n;
return out;
}
std::string BinaryInput::readString() {
int64 n = 0;
if ((m_pos + m_alreadyRead + n) < (m_length - 1)) {
prepareToRead(1);
}
if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
(m_buffer[m_pos + n] != '\0')) {
++n;
while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
(m_buffer[m_pos + n] != '\0')) {
prepareToRead(1);
++n;
}
}
// Consume NULL
++n;
return readString(n);
}
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;
}
}

View File

@@ -0,0 +1,534 @@
/**
@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 "G3D/platform.h"
#include "G3D/BinaryOutput.h"
#include "G3D/fileutils.h"
#include "G3D/FileSystem.h"
#include "G3D/stringutils.h"
#include "G3D/Array.h"
#include <zlib.h>
#include "G3D/Log.h"
#include <cstring>
#ifdef G3D_LINUX
# include <errno.h>
#endif
#ifdef __CYGWIN__
# 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) {
if (m_filename == "<memory>") {
throw "Out of memory while writing to memory in BinaryOutput (no RAM left).";
} else if ((int)bytes > (int)m_maxBufferLen) {
throw "Out of memory while writing to disk in BinaryOutput (could not create a large enough buffer).";
} else {
// Dump the contents to disk. In order to enable seeking backwards,
// we keep the last 10 MB in memory.
int writeBytes = m_bufferLen - 10 * 1024 * 1024;
if (writeBytes < m_bufferLen / 3) {
// We're going to write less than 1/3 of the file;
// give up and just write the whole thing.
writeBytes = m_bufferLen;
}
debugAssert(writeBytes > 0);
//debugPrintf("Writing %d bytes to disk\n", writeBytes);
const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
FILE* file = FileSystem::fopen(m_filename.c_str(), mode);
debugAssert(file);
size_t count = fwrite(m_buffer, 1, writeBytes, file);
debugAssert((int)count == writeBytes); (void)count;
fclose(file);
file = NULL;
// Record that we saved this data.
m_alreadyWritten += writeBytes;
m_bufferLen -= writeBytes;
m_pos -= writeBytes;
debugAssert(m_bufferLen < m_maxBufferLen);
debugAssert(m_bufferLen >= 0);
debugAssert(m_pos >= 0);
debugAssert(m_pos <= m_bufferLen);
// Shift the unwritten data back appropriately in the buffer.
debugAssert(isValidHeapPointer(m_buffer));
System::memcpy(m_buffer, m_buffer + writeBytes, m_bufferLen);
debugAssert(isValidHeapPointer(m_buffer));
// *now* we allocate bytes (there should presumably be enough
// space in the buffer; if not, we'll come back through this
// code and dump the last 10MB to disk as well. Note that the
// bytes > maxBufferLen case above would already have triggered
// if this call couldn't succeed.
reserveBytes(bytes);
}
}
BinaryOutput::BinaryOutput() {
m_alreadyWritten = 0;
m_swapBytes = false;
m_pos = 0;
m_filename = "<memory>";
m_buffer = NULL;
m_bufferLen = 0;
m_maxBufferLen = 0;
m_beginEndBits = 0;
m_bitString = 0;
m_bitPos = 0;
m_ok = true;
m_committed = false;
}
BinaryOutput::BinaryOutput(
const std::string& filename,
G3DEndian fileEndian) {
m_pos = 0;
m_alreadyWritten = 0;
setEndian(fileEndian);
m_filename = filename;
m_buffer = NULL;
m_bufferLen = 0;
m_maxBufferLen = 0;
m_beginEndBits = 0;
m_bitString = 0;
m_bitPos = 0;
m_committed = false;
m_ok = true;
/** Verify ability to write to disk */
commit(false);
m_committed = false;
}
void BinaryOutput::reset() {
debugAssert(m_beginEndBits == 0);
alwaysAssertM(m_filename == "<memory>",
"Can only reset a BinaryOutput that writes to memory.");
// Do not reallocate, just clear the size of the buffer.
m_pos = 0;
m_alreadyWritten = 0;
m_bufferLen = 0;
m_beginEndBits = 0;
m_bitString = 0;
m_bitPos = 0;
m_committed = false;
}
BinaryOutput::~BinaryOutput() {
debugAssert((m_buffer == NULL) || isValidHeapPointer(m_buffer));
System::free(m_buffer);
m_buffer = NULL;
m_bufferLen = 0;
m_maxBufferLen = 0;
}
void BinaryOutput::setEndian(G3DEndian fileEndian) {
m_fileEndian = fileEndian;
m_swapBytes = (fileEndian != System::machineEndian());
}
bool BinaryOutput::ok() const {
return m_ok;
}
void BinaryOutput::compress() {
if (m_alreadyWritten > 0) {
throw "Cannot compress huge files (part of this file has already been written to disk).";
}
// Old buffer size
int L = m_bufferLen;
uint8* convert = (uint8*)&L;
// Zlib requires the output buffer to be this big
unsigned long newSize = iCeil(m_bufferLen * 1.01) + 12;
uint8* temp = (uint8*)System::malloc(newSize);
int result = compress2(temp, &newSize, m_buffer, m_bufferLen, 9);
debugAssert(result == Z_OK); (void)result;
// Write the header
if (m_swapBytes) {
m_buffer[0] = convert[3];
m_buffer[1] = convert[2];
m_buffer[2] = convert[1];
m_buffer[3] = convert[0];
} else {
m_buffer[0] = convert[0];
m_buffer[1] = convert[1];
m_buffer[2] = convert[2];
m_buffer[3] = convert[3];
}
// Write the data
if ((int64)newSize + 4 > (int64)m_maxBufferLen) {
m_maxBufferLen = newSize + 4;
m_buffer = (uint8*)System::realloc(m_buffer, m_maxBufferLen);
}
m_bufferLen = newSize + 4;
System::memcpy(m_buffer + 4, temp, newSize);
m_pos = m_bufferLen;
System::free(temp);
}
void BinaryOutput::commit(bool flush) {
debugAssertM(! m_committed, "Cannot commit twice");
m_committed = true;
debugAssertM(m_beginEndBits == 0, "Missing endBits before commit");
// Make sure the directory exists.
std::string root, base, ext, path;
Array<std::string> pathArray;
parseFilename(m_filename, root, pathArray, base, ext);
path = root + stringJoin(pathArray, '/');
if (! FileSystem::exists(path, false)) {
FileSystem::createDirectory(path);
}
const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
FILE* file = FileSystem::fopen(m_filename.c_str(), mode);
if (! file) {
logPrintf("Error %d while trying to open \"%s\"\n", errno, m_filename.c_str());
}
m_ok = (file != NULL) && m_ok;
if (m_ok) {
debugAssertM(file, std::string("Could not open '") + m_filename + "'");
if (m_buffer != NULL) {
m_alreadyWritten += m_bufferLen;
int success = fwrite(m_buffer, m_bufferLen, 1, file);
(void)success;
debugAssertM(success == 1, std::string("Could not write to '") + m_filename + "'");
}
if (flush) {
fflush(file);
}
FileSystem::fclose(file);
file = NULL;
}
}
void BinaryOutput::commit(
uint8* out) {
debugAssertM(! m_committed, "Cannot commit twice");
m_committed = true;
System::memcpy(out, m_buffer, m_bufferLen);
}
void BinaryOutput::writeUInt16(uint16 u) {
reserveBytes(2);
uint8* convert = (uint8*)&u;
if (m_swapBytes) {
m_buffer[m_pos] = convert[1];
m_buffer[m_pos + 1] = convert[0];
} else {
*(uint16*)(m_buffer + m_pos) = u;
}
m_pos += 2;
}
void BinaryOutput::writeUInt32(uint32 u) {
reserveBytes(4);
uint8* convert = (uint8*)&u;
debugAssert(m_beginEndBits == 0);
if (m_swapBytes) {
m_buffer[m_pos] = convert[3];
m_buffer[m_pos + 1] = convert[2];
m_buffer[m_pos + 2] = convert[1];
m_buffer[m_pos + 3] = convert[0];
} else {
*(uint32*)(m_buffer + m_pos) = u;
}
m_pos += 4;
}
void BinaryOutput::writeUInt64(uint64 u) {
reserveBytes(8);
uint8* convert = (uint8*)&u;
if (m_swapBytes) {
m_buffer[m_pos] = convert[7];
m_buffer[m_pos + 1] = convert[6];
m_buffer[m_pos + 2] = convert[5];
m_buffer[m_pos + 3] = convert[4];
m_buffer[m_pos + 4] = convert[3];
m_buffer[m_pos + 5] = convert[2];
m_buffer[m_pos + 6] = convert[1];
m_buffer[m_pos + 7] = convert[0];
} else {
*(uint64*)(m_buffer + m_pos) = u;
}
m_pos += 8;
}
void BinaryOutput::writeString(const char* s) {
// +1 is because strlen doesn't count the null
int len = strlen(s) + 1;
debugAssert(m_beginEndBits == 0);
reserveBytes(len);
System::memcpy(m_buffer + m_pos, s, len);
m_pos += len;
}
void BinaryOutput::writeStringEven(const char* s) {
// +1 is because strlen doesn't count the null
int len = strlen(s) + 1;
reserveBytes(len);
System::memcpy(m_buffer + m_pos, s, len);
m_pos += len;
// Pad with another NULL
if ((len % 2) == 1) {
writeUInt8(0);
}
}
void BinaryOutput::writeString32(const char* s) {
writeUInt32(strlen(s) + 1);
writeString(s);
}
void BinaryOutput::writeVector4(const Vector4& v) {
writeFloat32(v.x);
writeFloat32(v.y);
writeFloat32(v.z);
writeFloat32(v.w);
}
void BinaryOutput::writeVector3(const Vector3& v) {
writeFloat32(v.x);
writeFloat32(v.y);
writeFloat32(v.z);
}
void BinaryOutput::writeVector2(const Vector2& v) {
writeFloat32(v.x);
writeFloat32(v.y);
}
void BinaryOutput::writeColor4(const Color4& v) {
writeFloat32(v.r);
writeFloat32(v.g);
writeFloat32(v.b);
writeFloat32(v.a);
}
void BinaryOutput::writeColor3(const Color3& v) {
writeFloat32(v.r);
writeFloat32(v.g);
writeFloat32(v.b);
}
void BinaryOutput::beginBits() {
debugAssertM(m_beginEndBits == 0, "Already in beginBits...endBits");
m_bitString = 0x00;
m_bitPos = 0;
m_beginEndBits = 1;
}
void BinaryOutput::writeBits(uint32 value, int numBits) {
while (numBits > 0) {
// Extract the current bit of value and
// insert it into the current byte
m_bitString |= (value & 1) << m_bitPos;
++m_bitPos;
value = value >> 1;
--numBits;
if (m_bitPos > 7) {
// We've reached the end of this byte
writeUInt8(m_bitString);
m_bitString = 0x00;
m_bitPos = 0;
}
}
}
void BinaryOutput::endBits() {
debugAssertM(m_beginEndBits == 1, "Not in beginBits...endBits");
if (m_bitPos > 0) {
writeUInt8(m_bitString);
}
m_bitString = 0;
m_bitPos = 0;
m_beginEndBits = 0;
}
}

View File

@@ -0,0 +1,393 @@
/**
@file Box.cpp
Box class
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
@edited 2006-02-05
*/
#include "G3D/Box.h"
#include "G3D/debug.h"
#include "G3D/Plane.h"
#include "G3D/AABox.h"
#include "G3D/CoordinateFrame.h"
namespace G3D {
/**
Sets a field on four vertices. Used by the constructor.
*/
#define setMany(i0, i1, i2, i3, field, extreme) \
_corner[i0].field = _corner[i1].field = \
_corner[i2].field = _corner[i3].field = \
(extreme).field
Box::Box() {
}
Box::Box(const AABox& b) {
init(b.low(), b.high());
}
Box::Box(class BinaryInput& b) {
deserialize(b);
}
void Box::serialize(class BinaryOutput& b) const {
int i;
for (i = 0; i < 8; ++i) {
_corner[i].serialize(b);
}
// Other state can be reconstructed
}
void Box::deserialize(class BinaryInput& b) {
int i;
_center = Vector3::zero();
for (i = 0; i < 8; ++i) {
_corner[i].deserialize(b);
_center += _corner[i];
}
_center = _center / 8;
// Reconstruct other state from the corners
_axis[0] = _corner[5] - _corner[4];
_axis[1] = _corner[7] - _corner[4];
_axis[2] = _corner[0] - _corner[4];
for (i = 0; i < 3; ++i) {
_extent[i] = _axis[i].magnitude();
_axis[i] /= _extent[i];
}
_volume = _extent.x * _extent.y * _extent.z;
_area = 2 *
(_extent.x * _extent.y +
_extent.y * _extent.z +
_extent.z * _extent.x);
}
Box::Box(
const Vector3& min,
const Vector3& max) {
init(min.min(max), min.max(max));
}
void Box::init(
const Vector3& min,
const Vector3& max) {
debugAssert(
(min.x <= max.x) &&
(min.y <= max.y) &&
(min.z <= max.z));
setMany(0, 1, 2, 3, z, max);
setMany(4, 5, 6, 7, z, min);
setMany(1, 2, 5, 6, x, max);
setMany(0, 3, 4, 7, x, min);
setMany(3, 2, 6, 7, y, max);
setMany(0, 1, 5, 4, y, min);
_extent = max - min;
_axis[0] = Vector3::unitX();
_axis[1] = Vector3::unitY();
_axis[2] = Vector3::unitZ();
if (_extent.isFinite()) {
_volume = _extent.x * _extent.y * _extent.z;
} else {
_volume = G3D::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

View File

@@ -0,0 +1,113 @@
/**
@file Box.cpp
Box class
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
@edited 2008-12-27
*/
#include "G3D/Box2D.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Rect2D.h"
namespace G3D {
bool Box2D::overlaps1Way(const Box2D& other) const {
for (int a = 0; a < 2; ++a) {
float t = other.m_corner[0].dot(m_axisin[a]);
// Find the extent of box 2 on m_axisin a
float tMin = t;
float tMax = t;
for (int c = 1; c < 4; ++c) {
t = other.m_corner[c].dot(m_axisin[a]);
if (t < tMin) {
tMin = t;
} else if (t > tMax) {
tMax = t;
}
}
// We have to subtract off the origin
// See if [tMin, tMax] intersects [0, 1]
if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
// There was no intersection along this dimension;
// the boxes cannot possibly overlap.
return false;
}
}
// There was no dimension along which there is no intersection.
// Therefore the boxes overlap.
return true;
}
void Box2D::computeAxes() {
m_axis[0] = m_corner[1] - m_corner[0];
m_axis[1] = m_corner[3] - m_corner[0];
// Make the length of each m_axisin = 1/edge length so we know any
// dot product must be less than 1 to fall within the edge.
float len[2];
for (int a = 0; a < 2; ++a) {
float lenSq = m_axis[a].squaredLength();
m_axisin[a] = m_axis[a] / lenSq;
origin[a] = m_corner[0].dot(m_axisin[a]);
len[a] = sqrt(lenSq);
m_axis[a] /= len[a];
}
// w * h
m_area = len[0] * len[1];
m_center = (m_corner[0] + m_corner[2]) * 0.5f;
}
Box2D::Box2D(const Vector2& center, float w, float h, float angle) {
Vector2 X( cos(angle), sin(angle));
Vector2 Y(-sin(angle), cos(angle));
X *= w / 2;
Y *= h / 2;
m_corner[0] = center - X - Y;
m_corner[1] = center + X - Y;
m_corner[2] = center + X + Y;
m_corner[3] = center - X + Y;
computeAxes();
}
Box2D::Box2D(const AABox2D& b) {
for (int i = 0; i < 4; ++i) {
m_corner[i] = b.corner(i);
}
computeAxes();
}
Box2D::Box2D(const Vector2& min, const Vector2& max) {
*this = Box2D(Rect2D::xyxy(min, max));
}
Box2D::Box2D(const CFrame& frame, Box2D& b) {
for (int i = 0; i < 4; ++i) {
m_corner[i] = frame.pointToWorldSpace(Vector3(b.corner(i), 0)).xy();
}
computeAxes();
}
} // G3D

View File

@@ -0,0 +1,43 @@
/**
\file BumpMapPreprocess.cpp
\maintainer Morgan McGuire, http://graphics.cs.williams.edu
\created 2010-01-28
\edited 2010-01-28
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/BumpMapPreprocess.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
BumpMapPreprocess::BumpMapPreprocess(const Any& any) {
*this = BumpMapPreprocess();
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& key = toLower(it->key);
if (key == "lowpassfilter") {
lowPassFilter = it->value;
} else if (key == "zextentpixels") {
zExtentPixels = it->value;
} else if (key == "scalezbynz") {
scaleZByNz = it->value;
} else {
any.verify(false, "Illegal key: " + it->key);
}
}
}
BumpMapPreprocess::operator Any() const {
Any any(Any::TABLE, "BumpMapPreprocess");
any["lowPassFilter"] = lowPassFilter;
any["zExtentPixels"] = zExtentPixels;
any["scaleZByNz"] = scaleZByNz;
return any;
}
}

View File

@@ -0,0 +1,179 @@
/**
@file Capsule.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-02-07
@edited 2005-08-18
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Capsule.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/LineSegment.h"
#include "G3D/Sphere.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Line.h"
#include "G3D/AABox.h"
namespace G3D {
Capsule::Capsule(class BinaryInput& b) {
deserialize(b);
}
Capsule::Capsule() {
}
Capsule::Capsule(const Vector3& _p1, const Vector3& _p2, float _r)
: p1(_p1), p2(_p2), _radius(_r) {
}
void Capsule::serialize(class BinaryOutput& b) const {
p1.serialize(b);
p2.serialize(b);
b.writeFloat64(_radius);
}
void Capsule::deserialize(class BinaryInput& b) {
p1.deserialize(b);
p2.deserialize(b);
_radius = b.readFloat64();
}
Line Capsule::axis() const {
return Line::fromTwoPoints(p1, p2);
}
float Capsule::volume() const {
return
// Sphere volume
pow(_radius, 3) * pi() * 4 / 3 +
// Cylinder volume
pow(_radius, 2) * (p1 - p2).magnitude();
}
float Capsule::area() const {
return
// Sphere area
pow(_radius, 2) * 4 * pi() +
// Cylinder area
twoPi() * _radius * (p1 - p2).magnitude();
}
void Capsule::getBounds(AABox& out) const {
Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * _radius);
Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * _radius);
out = AABox(min, max);
}
bool Capsule::contains(const Vector3& p) const {
return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(radius());
}
void Capsule::getRandomSurfacePoint(Vector3& p, Vector3& N) const {
float h = height();
float r = radius();
// Create a random point on a standard capsule and then rotate to the global frame.
// Relative areas
float capRelArea = sqrt(r) / 2.0f;
float sideRelArea = r * h;
float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea);
if (r1 < capRelArea * 2) {
// Select a point uniformly at random on a sphere
N = Sphere(Vector3::zero(), 1).randomSurfacePoint();
p = N * r;
p.y += sign(p.y) * h / 2.0f;
} else {
// Side
float a = uniformRandom(0, (float)twoPi());
N.x = cos(a);
N.y = 0;
N.z = sin(a);
p.x = N.x * r;
p.z = N.y * r;
p.y = uniformRandom(-h / 2.0f, h / 2.0f);
}
// Transform to world space
CoordinateFrame cframe;
getReferenceFrame(cframe);
p = cframe.pointToWorldSpace(p);
N = cframe.normalToWorldSpace(N);
}
void Capsule::getReferenceFrame(CoordinateFrame& cframe) const {
cframe.translation = center();
Vector3 Y = (p1 - p2).direction();
Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX();
Vector3 Z = X.cross(Y).direction();
X = Y.cross(Z);
cframe.rotation.setColumn(0, X);
cframe.rotation.setColumn(1, Y);
cframe.rotation.setColumn(2, Z);
}
Vector3 Capsule::randomInteriorPoint() const {
float h = height();
float r = radius();
// Create a random point in a standard capsule and then rotate to the global frame.
Vector3 p;
float hemiVolume = pi() * (r*r*r) * 4 / 6.0;
float cylVolume = pi() * square(r) * h;
float r1 = uniformRandom(0, 2.0 * hemiVolume + cylVolume);
if (r1 < 2.0 * hemiVolume) {
p = Sphere(Vector3::zero(), r).randomInteriorPoint();
p.y += sign(p.y) * h / 2.0f;
} else {
// Select a point uniformly at random on a disk
float a = uniformRandom(0, (float)twoPi());
float r2 = sqrt(uniformRandom(0, 1)) * r;
p = Vector3(cos(a) * r2,
uniformRandom(-h / 2.0f, h / 2.0f),
sin(a) * r2);
}
// Transform to world space
CoordinateFrame cframe;
getReferenceFrame(cframe);
return cframe.pointToWorldSpace(p);
}
} // namespace

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
/**
@file Color1.cpp
Color class.
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-30
@edited 2009-03-27
*/
#include "G3D/platform.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Color3.h"
namespace G3D {
const Color1& Color1::one() {
static const Color1 x(1.0f);
return x;
}
const Color1& Color1::zero() {
const static Color1 x(0.0f);
return x;
}
Color1::Color1(BinaryInput& bi) {
deserialize(bi);
}
Color3 Color1::rgb() const {
return Color3(value, value, value);
}
void Color1::deserialize(BinaryInput& bi) {
value = bi.readFloat32();
}
void Color1::serialize(BinaryOutput& bo) const {
bo.writeFloat32(value);
}
Color1::Color1(const class Color1uint8& other) {
value = other.value / 255.0f;
}
} // namespace G3D

View File

@@ -0,0 +1,38 @@
/**
@file Color1uint8.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-30
@edited 2007-01-30
*/
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/Color1uint8.h"
#include "G3D/Color1.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
Color1uint8::Color1uint8(const class Color1& c) : value(iClamp(iFloor(c.value * 256), 0, 255)) {
}
Color1uint8::Color1uint8(class BinaryInput& bi) {
deserialize(bi);
}
void Color1uint8::serialize(class BinaryOutput& bo) const {
bo.writeUInt8(value);
}
void Color1uint8::deserialize(class BinaryInput& bi) {
value = bi.readUInt8();
}
}

View File

@@ -0,0 +1,384 @@
/**
@file Color3.cpp
Color class.
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
@edited 2010-01-28
*/
#include "G3D/platform.h"
#include <stdlib.h>
#include "G3D/Color3.h"
#include "G3D/Vector3.h"
#include "G3D/format.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Color3uint8.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
Color3::Color3(const Any& any) {
*this = Color3::zero();
any.verifyName("Color3");
std::string name = toLower(any.name());
switch (any.type()) {
case Any::TABLE:
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& key = toLower(it->key);
if (key == "r") {
r = it->value;
} else if (key == "g") {
g = it->value;
} else if (key == "b") {
b = it->value;
} else {
any.verify(false, "Illegal key: " + it->key);
}
}
break;
case Any::ARRAY:
if (name == "color3") {
any.verifySize(3);
r = any[0];
g = any[1];
b = any[2];
} else if (name == "color3::one") {
any.verifySize(0);
*this = one();
} else if (name == "color3::zero") {
any.verifySize(0);
*this = zero();
} else if (name == "color3::fromargb") {
*this = Color3::fromARGB((int)any[0].number());
} else {
any.verify(false, "Expected Color3 constructor");
}
break;
default:
any.verify(false, "Bad Color3 constructor");
}
}
Color3::operator Any() const {
Any a(Any::ARRAY, "Color3");
a.append(r, g, b);
return a;
}
Color3 Color3::ansiMap(uint32 i) {
static const Color3 map[] =
{Color3::black(), Color3::red() * 0.75f, Color3::green() * 0.75f, Color3::yellow() * 0.75f,
Color3::blue() * 0.75f, Color3::purple() * 0.75f, Color3::cyan() * 0.75f, Color3::white() * 0.75f,
Color3::white() * 0.90f, Color3::red(), Color3::green(), Color3::yellow(), Color3::blue(),
Color3::purple(), Color3::cyan(), Color3::white()};
return map[i & 15];
}
Color3 Color3::pastelMap(uint32 i) {
uint32 x = Crypto::crc32(&i, sizeof(uint32));
// Create fairly bright, saturated colors
Vector3 v(((x >> 22) & 1023) / 1023.0f,
(((x >> 11) & 2047) / 2047.0f) * 0.5f + 0.25f,
((x & 2047) / 2047.0f) * 0.75f + 0.25f);
return Color3::fromHSV(v);
}
const Color3& Color3::red() {
static Color3 c(1.0f, 0.0f, 0.0f);
return c;
}
const Color3& Color3::green() {
static Color3 c(0.0f, 1.0f, 0.0f);
return c;
}
const Color3& Color3::blue() {
static Color3 c(0.0f, 0.0f, 1.0f);
return c;
}
const Color3& Color3::purple() {
static Color3 c(0.7f, 0.0f, 1.0f);
return c;
}
const Color3& Color3::cyan() {
static Color3 c(0.0f, 0.7f, 1.0f);
return c;
}
const Color3& Color3::yellow() {
static Color3 c(1.0f, 1.0f, 0.0f);
return c;
}
const Color3& Color3::brown() {
static Color3 c(0.5f, 0.5f, 0.0f);
return c;
}
const Color3& Color3::orange() {
static Color3 c(1.0f, 0.5f, 0.0f);
return c;
}
const Color3& Color3::black() {
static Color3 c(0.0f, 0.0f, 0.0f);
return c;
}
const Color3& Color3::zero() {
static Color3 c(0.0f, 0.0f, 0.0f);
return c;
}
const Color3& Color3::one() {
static Color3 c(1.0f, 1.0f, 1.0f);
return c;
}
const Color3& Color3::gray() {
static Color3 c(0.7f, 0.7f, 0.7f);
return c;
}
const Color3& Color3::white() {
static Color3 c(1, 1, 1);
return c;
}
bool Color3::isFinite() const {
return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b);
}
Color3::Color3(BinaryInput& bi) {
deserialize(bi);
}
void Color3::deserialize(BinaryInput& bi) {
r = bi.readFloat32();
g = bi.readFloat32();
b = bi.readFloat32();
}
void Color3::serialize(BinaryOutput& bo) const {
bo.writeFloat32(r);
bo.writeFloat32(g);
bo.writeFloat32(b);
}
const Color3& Color3::wheelRandom() {
static const Color3 colorArray[8] =
{Color3::blue(), Color3::red(), Color3::green(),
Color3::orange(), Color3::yellow(),
Color3::cyan(), Color3::purple(), Color3::brown()};
return colorArray[iRandom(0, 7)];
}
size_t Color3::hashCode() const {
unsigned int rhash = (*(int*)(void*)(&r));
unsigned int ghash = (*(int*)(void*)(&g));
unsigned int bhash = (*(int*)(void*)(&b));
return rhash + (ghash * 37) + (bhash * 101);
}
Color3::Color3(const Vector3& v) {
r = v.x;
g = v.y;
b = v.z;
}
Color3::Color3(const class Color3uint8& other) {
r = other.r / 255.0f;
g = other.g / 255.0f;
b = other.b / 255.0f;
}
Color3 Color3::fromARGB(uint32 x) {
return Color3((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF)) / 255.0f;
}
//----------------------------------------------------------------------------
Color3 Color3::random() {
return Color3(uniformRandom(),
uniformRandom(),
uniformRandom()).direction();
}
//----------------------------------------------------------------------------
Color3& Color3::operator/= (float fScalar) {
if (fScalar != 0.0f) {
float fInvScalar = 1.0f / fScalar;
r *= fInvScalar;
g *= fInvScalar;
b *= fInvScalar;
} else {
r = (float)G3D::finf();
g = (float)G3D::finf();
b = (float)G3D::finf();
}
return *this;
}
//----------------------------------------------------------------------------
float Color3::unitize (float fTolerance) {
float fLength = length();
if ( fLength > fTolerance ) {
float fInvLength = 1.0f / fLength;
r *= fInvLength;
g *= fInvLength;
b *= fInvLength;
} else {
fLength = 0.0f;
}
return fLength;
}
//----------------------------------------------------------------------------
Color3 Color3::fromHSV(const Vector3& _hsv) {
debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f)
&& (_hsv.y <= 1.0f && _hsv.y >= 0.0f)
&& ( _hsv.z <= 1.0f && _hsv.z >= 0.0f), "H,S,V must be between [0,1]");
const int i = iMin(5, G3D::iFloor(6.0 * _hsv.x));
const float f = 6.0f * _hsv.x - i;
const float m = _hsv.z * (1.0f - (_hsv.y));
const float n = _hsv.z * (1.0f - (_hsv.y * f));
const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f)));
switch(i) {
case 0:
return Color3(_hsv.z, k, m);
case 1:
return Color3(n, _hsv.z, m);
case 2:
return Color3(m, _hsv.z, k);
case 3:
return Color3(m, n, _hsv.z);
case 4:
return Color3(k, m, _hsv.z);
case 5:
return Color3(_hsv.z, m, n);
default:
debugAssertM(false, "fell through switch..");
}
return Color3::black();
}
Vector3 Color3::toHSV(const Color3& _rgb) {
debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f)
&& (_rgb.g <= 1.0f && _rgb.g >= 0.0f)
&& (_rgb.b <= 1.0f && _rgb.b >= 0.0f), "R,G,B must be between [0,1]");
Vector3 hsv = Vector3::zero();
hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b);
if (G3D::fuzzyEq(hsv.z, 0.0f)) {
return hsv;
}
const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b);
hsv.y = (hsv.z - x) / hsv.z;
if (G3D::fuzzyEq(hsv.y, 0.0f)) {
return hsv;
}
Vector3 rgbN;
rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x);
rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x);
rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x);
if (_rgb.r == hsv.z) { // note from the max we know that it exactly equals one of the three.
hsv.x = (_rgb.g == x)? 5.0f + rgbN.z : 1.0f - rgbN.y;
} else if (_rgb.g == hsv.z) {
hsv.x = (_rgb.b == x)? 1.0f + rgbN.x : 3.0f - rgbN.z;
} else {
hsv.x = (_rgb.r == x)? 3.0f + rgbN.y : 5.0f - rgbN.x;
}
hsv.x /= 6.0f;
return hsv;
}
Color3 Color3::jetColorMap(const float& val) {
debugAssertM(val <= 1.0f && val >= 0.0f , "value should be in [0,1]");
//truncated triangles where sides have slope 4
Color3 jet;
jet.r = G3D::min(4.0f * val - 1.5f,-4.0f * val + 4.5f) ;
jet.g = G3D::min(4.0f * val - 0.5f,-4.0f * val + 3.5f) ;
jet.b = G3D::min(4.0f * val + 0.5f,-4.0f * val + 2.5f) ;
jet.r = G3D::clamp(jet.r, 0.0f, 1.0f);
jet.g = G3D::clamp(jet.g, 0.0f, 1.0f);
jet.b = G3D::clamp(jet.b, 0.0f, 1.0f);
return jet;
}
std::string Color3::toString() const {
return G3D::format("(%g, %g, %g)", r, g, b);
}
//----------------------------------------------------------------------------
Color3 Color3::rainbowColorMap(float hue) {
return fromHSV(Vector3(hue, 1.0f, 1.0f));
}
}; // namespace

View File

@@ -0,0 +1,45 @@
/**
@file Color3uint8.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-04-07
@edited 2006-01-07
*/
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/Color3uint8.h"
#include "G3D/Color3.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
Color3uint8::Color3uint8(const class Color3& c) {
r = iMin(255, iFloor(c.r * 256));
g = iMin(255, iFloor(c.g * 256));
b = iMin(255, iFloor(c.b * 256));
}
Color3uint8::Color3uint8(class BinaryInput& bi) {
deserialize(bi);
}
void Color3uint8::serialize(class BinaryOutput& bo) const {
bo.writeUInt8(r);
bo.writeUInt8(g);
bo.writeUInt8(b);
}
void Color3uint8::deserialize(class BinaryInput& bi) {
r = bi.readUInt8();
g = bi.readUInt8();
b = bi.readUInt8();
}
}

View File

@@ -0,0 +1,192 @@
/**
@file Color4.cpp
Color class.
@author Morgan McGuire, http://graphics.cs.williams.edu
@cite Portions by Laura Wollstadt, graphics3d.com
@cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
@created 2002-06-25
@edited 2009-11-10
*/
#include <stdlib.h>
#include "G3D/Color4.h"
#include "G3D/Color4uint8.h"
#include "G3D/Vector4.h"
#include "G3D/format.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
Color4::Color4(const Any& any) {
*this = Color4::zero();
any.verifyName("Color4");
if (any.type() == Any::TABLE) {
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& key = toLower(it->key);
if (key == "r") {
r = it->value;
} else if (key == "g") {
g = it->value;
} else if (key == "b") {
b = it->value;
} else if (key == "a") {
a = it->value;
} else {
any.verify(false, "Illegal key: " + it->key);
}
}
} else if (toLower(any.name()) == "color4") {
r = any[0];
g = any[1];
b = any[2];
a = any[3];
} else {
any.verifyName("Color4::fromARGB");
*this = Color4::fromARGB((int)any[0].number());
}
}
Color4::operator Any() const {
Any any(Any::ARRAY, "Color4");
any.append(r, g, b, a);
return any;
}
const Color4& Color4::one() {
const static Color4 x(1.0f, 1.0f, 1.0f, 1.0f);
return x;
}
const Color4& Color4::zero() {
static Color4 c(0.0f, 0.0f, 0.0f, 0.0f);
return c;
}
const Color4& Color4::inf() {
static Color4 c((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf());
return c;
}
const Color4& Color4::nan() {
static Color4 c((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan());
return c;
}
const Color4& Color4::clear() {
return Color4::zero();
}
Color4::Color4(const Vector4& v) {
r = v.x;
g = v.y;
b = v.z;
a = v.w;
}
Color4::Color4(const Color4uint8& c) : r(c.r), g(c.g), b(c.b), a(c.a) {
*this /= 255.0f;
}
size_t Color4::hashCode() const {
unsigned int rhash = (*(int*)(void*)(&r));
unsigned int ghash = (*(int*)(void*)(&g));
unsigned int bhash = (*(int*)(void*)(&b));
unsigned int ahash = (*(int*)(void*)(&a));
return rhash + (ghash * 37) + (bhash * 101) + (ahash * 241);
}
Color4 Color4::fromARGB(uint32 x) {
return Color4(
(float)((x >> 16) & 0xFF),
(float)((x >> 8) & 0xFF),
(float)(x & 0xFF),
(float)((x >> 24) & 0xFF)) / 255.0;
}
Color4::Color4(BinaryInput& bi) {
deserialize(bi);
}
void Color4::deserialize(BinaryInput& bi) {
r = bi.readFloat32();
g = bi.readFloat32();
b = bi.readFloat32();
a = bi.readFloat32();
}
void Color4::serialize(BinaryOutput& bo) const {
bo.writeFloat32(r);
bo.writeFloat32(g);
bo.writeFloat32(b);
bo.writeFloat32(a);
}
//----------------------------------------------------------------------------
Color4 Color4::operator/ (float fScalar) const {
Color4 kQuot;
if (fScalar != 0.0f) {
float fInvScalar = 1.0f / fScalar;
kQuot.r = fInvScalar * r;
kQuot.g = fInvScalar * g;
kQuot.b = fInvScalar * b;
kQuot.a = fInvScalar * a;
return kQuot;
} else {
return Color4::inf();
}
}
//----------------------------------------------------------------------------
Color4& Color4::operator/= (float fScalar) {
if (fScalar != 0.0f) {
float fInvScalar = 1.0f / fScalar;
r *= fInvScalar;
g *= fInvScalar;
b *= fInvScalar;
a *= fInvScalar;
} else {
r = (float)G3D::finf();
g = (float)G3D::finf();
b = (float)G3D::finf();
a = (float)G3D::finf();
}
return *this;
}
//----------------------------------------------------------------------------
std::string Color4::toString() const {
return G3D::format("(%g, %g, %g, %g)", r, g, b, a);
}
//----------------------------------------------------------------------------
}; // namespace

View File

@@ -0,0 +1,47 @@
/**
@file Color4uint8.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-04-07
@edited 2006-01-07
*/
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/Color4uint8.h"
#include "G3D/Color4.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
Color4uint8::Color4uint8(const class Color4& c) {
r = iMin(255, iFloor(c.r * 256));
g = iMin(255, iFloor(c.g * 256));
b = iMin(255, iFloor(c.b * 256));
a = iMin(255, iFloor(c.a * 256));
}
Color4uint8::Color4uint8(class BinaryInput& bi) {
deserialize(bi);
}
void Color4uint8::serialize(class BinaryOutput& bo) const {
bo.writeUInt8(r);
bo.writeUInt8(g);
bo.writeUInt8(b);
bo.writeUInt8(a);
}
void Color4uint8::deserialize(class BinaryInput& bi) {
r = bi.readUInt8();
g = bi.readUInt8();
b = bi.readUInt8();
a = bi.readUInt8();
}
}

View File

@@ -0,0 +1,79 @@
/**
@file Cone.cpp
Cone class
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-07-09
@edited 2006-01-29
*/
#include "G3D/platform.h"
#include "G3D/Cone.h"
#include "G3D/Line.h"
#include "G3D/Sphere.h"
#include "G3D/Box.h"
namespace G3D {
Cone::Cone(const Vector3 &tip, const Vector3 &direction, float angle) {
this->tip = tip;
this->direction = direction.direction();
this->angle = angle;
debugAssert(angle >= 0);
debugAssert(angle <= pi());
}
/**
Forms the smallest cone that contains the box. Undefined if
the tip is inside or on the box.
*/
Cone::Cone(const Vector3& tip, const Box& box) {
this->tip = tip;
this->direction = (box.center() - tip).direction();
// Find the biggest angle
float smallestDotProduct = direction.dot((box.corner(0) - tip).direction());
for (int i = 1; i < 8; ++i) {
float dp = direction.dot((box.corner(i) - tip).direction());
debugAssert(dp > 0);
if (dp < smallestDotProduct) {
smallestDotProduct = dp;
}
}
angle = acosf(smallestDotProduct);
}
bool Cone::intersects(const Sphere& b) const {
// If the bounding sphere contains the tip, then
// they definitely touch.
if (b.contains(this->tip)) {
return true;
}
// Move the tip backwards, effectively making the cone bigger
// to account for the radius of the sphere.
Vector3 tip = this->tip - direction * b.radius / sinf(angle);
return Cone(tip, direction, angle).contains(b.center);
}
bool Cone::contains(const Vector3& v) const {
Vector3 d = (v - tip).direction();
float x = d.dot(direction);
return (x > 0) && (x >= cosf(angle));
}
}; // namespace

View File

@@ -0,0 +1,457 @@
/**
@file ConvexPolyhedron.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-11-11
@edited 2009-08-10
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
#include "G3D/ConvexPolyhedron.h"
#include "G3D/debug.h"
namespace G3D {
ConvexPolygon::ConvexPolygon(const Array<Vector3>& __vertex) : _vertex(__vertex) {
// Intentionally empty
}
ConvexPolygon::ConvexPolygon(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
_vertex.append(v0, v1, v2);
}
bool ConvexPolygon::isEmpty() const {
return (_vertex.length() == 0) || (getArea() <= fuzzyEpsilon);
}
float ConvexPolygon::getArea() const {
if (_vertex.length() < 3) {
return 0;
}
float sum = 0;
int length = _vertex.length();
// Split into triangle fan, compute individual area
for (int v = 2; v < length; v++) {
int i0 = 0;
int i1 = v - 1;
int i2 = v;
sum += (_vertex[i1] - _vertex[i0]).cross(_vertex[i2] - _vertex[i0]).magnitude() / 2;
}
return sum;
}
void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below) {
DirectedEdge edge;
cut(plane, above, below, edge);
}
void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge &newEdge) {
above._vertex.resize(0);
below._vertex.resize(0);
if (isEmpty()) {
//debugPrintf("Empty\n");
return;
}
int v = 0;
int length = _vertex.length();
Vector3 polyNormal = normal();
Vector3 planeNormal= plane.normal();
// See if the polygon is *in* the plane.
if (planeNormal.fuzzyEq(polyNormal) || planeNormal.fuzzyEq(-polyNormal)) {
// Polygon is parallel to the plane. It must be either above,
// below, or in the plane.
double a, b, c, d;
Vector3 pt = _vertex[0];
plane.getEquation(a,b,c,d);
float r = (float)(a * pt.x + b * pt.y + c * pt.z + d);
if (fuzzyGe(r, 0)) {
// The polygon is entirely in the plane.
//debugPrintf("Entirely above\n");
above = *this;
return;
} else {
//debugPrintf("Entirely below (1)\n");
below = *this;
return;
}
}
// Number of edges crossing the plane. Used for
// debug assertions.
int count = 0;
// True when the last _vertex we looked at was above the plane
bool lastAbove = plane.halfSpaceContains(_vertex[v]);
if (lastAbove) {
above._vertex.append(_vertex[v]);
} else {
below._vertex.append(_vertex[v]);
}
for (v = 1; v < length; v++) {
bool isAbove = plane.halfSpaceContains(_vertex[v]);
if (lastAbove ^ isAbove) {
// Switched sides.
// Create an interpolated point that lies
// in the plane, between the two points.
Line line = Line::fromTwoPoints(_vertex[v - 1], _vertex[v]);
Vector3 interp = line.intersection(plane);
if (! interp.isFinite()) {
// Since the polygon is not in the plane (we checked above),
// it must be the case that this edge (and only this edge)
// is in the plane. This only happens when the polygon is
// entirely below the plane except for one edge. This edge
// forms a degenerate polygon, so just treat the whole polygon
// as below the plane.
below = *this;
above._vertex.resize(0);
//debugPrintf("Entirely below\n");
return;
}
above._vertex.append(interp);
below._vertex.append(interp);
if (lastAbove) {
newEdge.stop = interp;
} else {
newEdge.start = interp;
}
count++;
}
lastAbove = isAbove;
if (lastAbove) {
above._vertex.append(_vertex[v]);
} else {
below._vertex.append(_vertex[v]);
}
}
// Loop back to the first point, seeing if an interpolated point is
// needed.
bool isAbove = plane.halfSpaceContains(_vertex[0]);
if (lastAbove ^ isAbove) {
Line line = Line::fromTwoPoints(_vertex[length - 1], _vertex[0]);
Vector3 interp = line.intersection(plane);
if (! interp.isFinite()) {
// Since the polygon is not in the plane (we checked above),
// it must be the case that this edge (and only this edge)
// is in the plane. This only happens when the polygon is
// entirely below the plane except for one edge. This edge
// forms a degenerate polygon, so just treat the whole polygon
// as below the plane.
below = *this;
above._vertex.resize(0);
//debugPrintf("Entirely below\n");
return;
}
above._vertex.append(interp);
below._vertex.append(interp);
debugAssertM(count < 2, "Convex polygons may only intersect planes at two edges.");
if (lastAbove) {
newEdge.stop = interp;
} else {
newEdge.start = interp;
}
++count;
}
debugAssertM((count == 2) || (count == 0), "Convex polygons may only intersect planes at two edges.");
}
ConvexPolygon ConvexPolygon::inverse() const {
ConvexPolygon result;
int length = _vertex.length();
result._vertex.resize(length);
for (int v = 0; v < length; v++) {
result._vertex[v] = _vertex[length - v - 1];
}
return result;
}
void ConvexPolygon::removeDuplicateVertices(){
// Any valid polygon should have 3 or more vertices, but why take chances?
if (_vertex.size() >= 2){
// Remove duplicate vertices.
for (int i=0;i<_vertex.size()-1;++i){
if (_vertex[i].fuzzyEq(_vertex[i+1])){
_vertex.remove(i+1);
--i; // Don't move forward.
}
}
// Check the last vertex against the first.
if (_vertex[_vertex.size()-1].fuzzyEq(_vertex[0])){
_vertex.pop();
}
}
}
//////////////////////////////////////////////////////////////////////////////
ConvexPolyhedron::ConvexPolyhedron(const Array<ConvexPolygon>& _face) : face(_face) {
// Intentionally empty
}
float ConvexPolyhedron::getVolume() const {
if (face.length() < 4) {
return 0;
}
// The volume of any pyramid is 1/3 * h * base area.
// Discussion at: http://nrich.maths.org/mathsf/journalf/oct01/art1/
float sum = 0;
// Choose the first _vertex of the first face as the origin.
// This lets us skip one face, too, and avoids negative heights.
Vector3 v0 = face[0]._vertex[0];
for (int f = 1; f < face.length(); f++) {
const ConvexPolygon& poly = face[f];
float height = (poly._vertex[0] - v0).dot(poly.normal());
float base = poly.getArea();
sum += height * base;
}
return sum / 3;
}
bool ConvexPolyhedron::isEmpty() const {
return (face.length() == 0) || (getVolume() <= fuzzyEpsilon);
}
void ConvexPolyhedron::cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below) {
above.face.resize(0);
below.face.resize(0);
Array<DirectedEdge> edge;
int f;
// See if the plane cuts this polyhedron at all. Detect when
// the polyhedron is entirely to one side or the other.
//{
int numAbove = 0, numIn = 0, numBelow = 0;
bool ruledOut = false;
double d;
Vector3 abc;
plane.getEquation(abc, d);
// This number has to be fairly large to prevent precision problems down
// the road.
const float eps = 0.005f;
for (f = face.length() - 1; (f >= 0) && (!ruledOut); f--) {
const ConvexPolygon& poly = face[f];
for (int v = poly._vertex.length() - 1; (v >= 0) && (!ruledOut); v--) {
double r = abc.dot(poly._vertex[v]) + d;
if (r > eps) {
numAbove++;
} else if (r < -eps) {
numBelow++;
} else {
numIn++;
}
ruledOut = (numAbove != 0) && (numBelow !=0);
}
}
if (numBelow == 0) {
above = *this;
return;
} else if (numAbove == 0) {
below = *this;
return;
}
//}
// Clip each polygon, collecting split edges.
for (f = face.length() - 1; f >= 0; f--) {
ConvexPolygon a, b;
DirectedEdge e;
face[f].cut(plane, a, b, e);
bool aEmpty = a.isEmpty();
bool bEmpty = b.isEmpty();
//debugPrintf("\n");
if (! aEmpty) {
//debugPrintf(" Above %f\n", a.getArea());
above.face.append(a);
}
if (! bEmpty) {
//debugPrintf(" Below %f\n", b.getArea());
below.face.append(b);
}
if (! aEmpty && ! bEmpty) {
//debugPrintf(" == Split\n");
edge.append(e);
} else {
// Might be the case that the polygon is entirely on
// one side of the plane yet there is an edge we need
// because it touches the plane.
//
// Extract the non-empty _vertex list and examine it.
// If we find exactly one edge in the plane, add that edge.
const Array<Vector3>& _vertex = (aEmpty ? b._vertex : a._vertex);
int L = _vertex.length();
int count = 0;
for (int v = 0; v < L; v++) {
if (plane.fuzzyContains(_vertex[v]) && plane.fuzzyContains(_vertex[(v + 1) % L])) {
e.start = _vertex[v];
e.stop = _vertex[(v + 1) % L];
count++;
}
}
if (count == 1) {
edge.append(e);
}
}
}
if (above.face.length() == 1) {
// Only one face above means that this entire
// polyhedron is below the plane. Move that face over.
below.face.append(above.face[0]);
above.face.resize(0);
} else if (below.face.length() == 1) {
// This shouldn't happen, but it arises in practice
// from numerical imprecision.
above.face.append(below.face[0]);
below.face.resize(0);
}
if ((above.face.length() > 0) && (below.face.length() > 0)) {
// The polyhedron was actually cut; create a cap polygon
ConvexPolygon cap;
// Collect the final polgyon by sorting the edges
int numVertices = edge.length();
/*debugPrintf("\n");
for (int xx=0; xx < numVertices; xx++) {
std::string s1 = edge[xx].start.toString();
std::string s2 = edge[xx].stop.toString();
debugPrintf("%s -> %s\n", s1.c_str(), s2.c_str());
}
*/
// Need at least three points to make a polygon
debugAssert(numVertices >= 3);
Vector3 last_vertex = edge.last().stop;
cap._vertex.append(last_vertex);
// Search for the next _vertex. Because of accumulating
// numerical error, we have to find the closest match, not
// just the one we expect.
for (int v = numVertices - 1; v >= 0; v--) {
// matching edge index
int index = 0;
int num = edge.length();
double distance = (edge[index].start - last_vertex).squaredMagnitude();
for (int e = 1; e < num; e++) {
double d = (edge[e].start - last_vertex).squaredMagnitude();
if (d < distance) {
// This is the new closest one
index = e;
distance = d;
}
}
// Don't tolerate ridiculous error.
debugAssertM(distance < 0.02, "Edge missing while closing polygon.");
last_vertex = edge[index].stop;
cap._vertex.append(last_vertex);
}
//debugPrintf("\n");
//debugPrintf("Cap (both) %f\n", cap.getArea());
above.face.append(cap);
below.face.append(cap.inverse());
}
// Make sure we put enough faces on each polyhedra
debugAssert((above.face.length() == 0) || (above.face.length() >= 4));
debugAssert((below.face.length() == 0) || (below.face.length() >= 4));
}
///////////////////////////////////////////////
ConvexPolygon2D::ConvexPolygon2D(const Array<Vector2>& pts, bool reverse) : m_vertex(pts) {
if (reverse) {
m_vertex.reverse();
}
}
bool ConvexPolygon2D::contains(const Vector2& p, bool reverse) const {
// Compute the signed area of each polygon from p to an edge.
// If the area is non-negative for all polygons then p is inside
// the polygon. (To adapt this algorithm for a concave polygon,
// the *sum* of the areas must be non-negative).
float r = reverse ? -1 : 1;
for (int i0 = 0; i0 < m_vertex.size(); ++i0) {
int i1 = (i0 + 1) % m_vertex.size();
const Vector2& v0 = m_vertex[i0];
const Vector2& v1 = m_vertex[i1];
Vector2 e0 = v0 - p;
Vector2 e1 = v1 - p;
// Area = (1/2) cross product, negated to be ccw in
// a 2D space; we neglect the 1/2
float area = -(e0.x * e1.y - e0.y * e1.x);
if (area * r < 0) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,465 @@
/**
@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 "G3D/platform.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Quat.h"
#include "G3D/Matrix4.h"
#include "G3D/Box.h"
#include "G3D/AABox.h"
#include "G3D/Sphere.h"
#include "G3D/Triangle.h"
#include "G3D/Ray.h"
#include "G3D/Capsule.h"
#include "G3D/Cylinder.h"
#include "G3D/UprightFrame.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
#include "G3D/PhysicsFrame.h"
#include "G3D/UprightFrame.h"
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(const Any& any) {
*this = CFrame();
const std::string& n = toUpper(any.name());
if (beginsWith(n, "VECTOR3")) {
translation = any;
} else if (beginsWith(n, "MATRIX3")) {
rotation = any;
} else if ((n == "CFRAME") || (n == "COORDINATEFRAME")) {
any.verifyType(Any::TABLE, Any::ARRAY);
if (any.type() == Any::ARRAY) {
any.verifySize(2);
rotation = any[0];
translation = any[1];
} else {
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& n = toLower(it->key);
if (n == "translation") {
translation = Vector3(it->value);
} else if (n == "rotation") {
rotation = Matrix3(it->value);
} else {
any.verify(false, "Illegal table key: " + it->key);
}
}
}
} else if (beginsWith(n, "PHYSICSFRAME") || beginsWith(n, "PFRAME")) {
*this = PhysicsFrame(any);
} else {
any.verifyName("CFrame::fromXYZYPRDegrees", "CoordinateFrame::fromXYZYPRDegrees");
any.verifyType(Any::ARRAY);
any.verifySize(3, 6);
int s = any.size();
*this = fromXYZYPRDegrees(any[0], any[1], any[2],
(s > 3) ? any[3].number() : 0.0f,
(s > 4) ? any[4].number() : 0.0f,
(s > 5) ? any[5].number() : 0.0f);
}
}
CoordinateFrame::operator Any() const {
float x, y, z, yaw, pitch, roll;
getXYZYPRDegrees(x, y, z, yaw, pitch, roll);
Any a(Any::ARRAY, "CFrame::fromXYZYPRDegrees");
a.append(x, y, z, yaw);
if ( ! G3D::fuzzyEq(yaw, 0.0f) || ! G3D::fuzzyEq(pitch, 0.0f) || ! G3D::fuzzyEq(roll, 0.0f)) {
a.append(yaw);
if (! G3D::fuzzyEq(pitch, 0.0f) || ! G3D::fuzzyEq(roll, 0.0f)) {
a.append(pitch);
if (! G3D::fuzzyEq(roll, 0.0f)) {
a.append(roll);
}
}
}
return a;
}
CoordinateFrame::CoordinateFrame(const class UprightFrame& f) {
*this = f.toCoordinateFrame();
}
CoordinateFrame::CoordinateFrame() :
rotation(Matrix3::identity()), translation(Vector3::zero()) {
}
CoordinateFrame CoordinateFrame::fromXYZYPRRadians(float x, float y, float z, float yaw,
float pitch, float roll) {
Matrix3 rotation = Matrix3::fromAxisAngle(Vector3::unitY(), yaw);
rotation = Matrix3::fromAxisAngle(rotation.column(0), pitch) * rotation;
rotation = Matrix3::fromAxisAngle(rotation.column(2), roll) * rotation;
const Vector3 translation(x, y, z);
return CoordinateFrame(rotation, translation);
}
void CoordinateFrame::getXYZYPRRadians(float& x, float& y, float& z,
float& yaw, float& pitch, float& roll) const {
x = translation.x;
y = translation.y;
z = translation.z;
const Vector3& look = lookVector();
if (abs(look.y) > 0.99f) {
// Looking nearly straight up or down
yaw = G3D::pi() + atan2(look.x, look.z);
pitch = asin(look.y);
roll = 0.0f;
} else {
// Yaw cannot be affected by others, so pull it first
yaw = G3D::pi() + atan2(look.x, look.z);
// Pitch is the elevation of the yaw vector
pitch = asin(look.y);
Vector3 actualRight = rightVector();
Vector3 expectedRight = look.cross(Vector3::unitY());
roll = 0;//acos(actualRight.dot(expectedRight)); TODO
}
}
void CoordinateFrame::getXYZYPRDegrees(float& x, float& y, float& z,
float& yaw, float& pitch, float& roll) const {
getXYZYPRRadians(x, y, z, yaw, pitch, roll);
yaw = toDegrees(yaw);
pitch = toDegrees(pitch);
roll = toDegrees(roll);
}
CoordinateFrame CoordinateFrame::fromXYZYPRDegrees(float x, float y, float z,
float yaw, float pitch, float roll) {
return fromXYZYPRRadians(x, y, z, toRadians(yaw), toRadians(pitch), toRadians(roll));
}
Ray CoordinateFrame::lookRay() const {
return Ray::fromOriginAndDirection(translation, lookVector());
}
bool CoordinateFrame::fuzzyEq(const CoordinateFrame& other) const {
for (int c = 0; c < 3; ++c) {
for (int r = 0; r < 3; ++r) {
if (! G3D::fuzzyEq(other.rotation[r][c], rotation[r][c])) {
return false;
}
}
if (! G3D::fuzzyEq(translation[c], other.translation[c])) {
return false;
}
}
return true;
}
bool CoordinateFrame::fuzzyIsIdentity() const {
const Matrix3& I = Matrix3::identity();
for (int c = 0; c < 3; ++c) {
for (int r = 0; r < 3; ++r) {
if (fuzzyNe(I[r][c], rotation[r][c])) {
return false;
}
}
if (fuzzyNe(translation[c], 0)) {
return false;
}
}
return true;
}
bool CoordinateFrame::isIdentity() const {
return
(translation == Vector3::zero()) &&
(rotation == Matrix3::identity());
}
Matrix4 CoordinateFrame::toMatrix4() const {
return Matrix4(*this);
}
std::string CoordinateFrame::toXML() const {
return G3D::format(
"<COORDINATEFRAME>\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf\n</COORDINATEFRAME>\n",
rotation[0][0], rotation[0][1], rotation[0][2], translation.x,
rotation[1][0], rotation[1][1], rotation[1][2], translation.y,
rotation[2][0], rotation[2][1], rotation[2][2], translation.z,
0.0, 0.0, 0.0, 1.0);
}
Plane CoordinateFrame::toObjectSpace(const Plane& p) const {
Vector3 N, P;
double d;
p.getEquation(N, d);
P = N * (float)d;
P = pointToObjectSpace(P);
N = normalToObjectSpace(N);
return Plane(N, P);
}
Plane CoordinateFrame::toWorldSpace(const Plane& p) const {
Vector3 N, P;
double d;
p.getEquation(N, d);
P = N * (float)d;
P = pointToWorldSpace(P);
N = normalToWorldSpace(N);
return Plane(N, P);
}
Triangle CoordinateFrame::toObjectSpace(const Triangle& t) const {
return Triangle(pointToObjectSpace(t.vertex(0)),
pointToObjectSpace(t.vertex(1)),
pointToObjectSpace(t.vertex(2)));
}
Triangle CoordinateFrame::toWorldSpace(const Triangle& t) const {
return Triangle(pointToWorldSpace(t.vertex(0)),
pointToWorldSpace(t.vertex(1)),
pointToWorldSpace(t.vertex(2)));
}
Cylinder CoordinateFrame::toWorldSpace(const Cylinder& c) const {
return Cylinder(
pointToWorldSpace(c.point(0)),
pointToWorldSpace(c.point(1)),
c.radius());
}
Capsule CoordinateFrame::toWorldSpace(const Capsule& c) const {
return Capsule(
pointToWorldSpace(c.point(0)),
pointToWorldSpace(c.point(1)),
c.radius());
}
Box CoordinateFrame::toWorldSpace(const AABox& b) const {
Box b2(b);
return toWorldSpace(b2);
}
Box CoordinateFrame::toWorldSpace(const Box& b) const {
Box out(b);
for (int i = 0; i < 8; ++i) {
out._corner[i] = pointToWorldSpace(b._corner[i]);
debugAssert(! isNaN(out._corner[i].x));
}
for (int i = 0; i < 3; ++i) {
out._axis[i] = vectorToWorldSpace(b._axis[i]);
}
out._center = pointToWorldSpace(b._center);
return out;
}
Box CoordinateFrame::toObjectSpace(const Box &b) const {
return inverse().toWorldSpace(b);
}
Box CoordinateFrame::toObjectSpace(const AABox& b) const {
return toObjectSpace(Box(b));
}
CoordinateFrame::CoordinateFrame(class BinaryInput& b) : rotation(Matrix3::zero()) {
deserialize(b);
}
void CoordinateFrame::deserialize(class BinaryInput& b) {
rotation.deserialize(b);
translation.deserialize(b);
}
void CoordinateFrame::serialize(class BinaryOutput& b) const {
rotation.serialize(b);
translation.serialize(b);
}
Sphere CoordinateFrame::toWorldSpace(const Sphere &b) const {
return Sphere(pointToWorldSpace(b.center), b.radius);
}
Sphere CoordinateFrame::toObjectSpace(const Sphere &b) const {
return Sphere(pointToObjectSpace(b.center), b.radius);
}
Ray CoordinateFrame::toWorldSpace(const Ray& r) const {
return Ray::fromOriginAndDirection(pointToWorldSpace(r.origin()), vectorToWorldSpace(r.direction()));
}
Ray CoordinateFrame::toObjectSpace(const Ray& r) const {
return Ray::fromOriginAndDirection(pointToObjectSpace(r.origin()), vectorToObjectSpace(r.direction()));
}
void CoordinateFrame::lookAt(const Vector3 &target) {
lookAt(target, Vector3::unitY());
}
void CoordinateFrame::lookAt(
const Vector3& target,
Vector3 up) {
up = up.direction();
Vector3 look = (target - translation).direction();
if (fabs(look.dot(up)) > .99f) {
up = Vector3::unitX();
if (fabs(look.dot(up)) > .99f) {
up = Vector3::unitY();
}
}
up -= look * look.dot(up);
up.unitize();
Vector3 z = -look;
Vector3 x = -z.cross(up);
x.unitize();
Vector3 y = z.cross(x);
rotation.setColumn(0, x);
rotation.setColumn(1, y);
rotation.setColumn(2, z);
}
CoordinateFrame CoordinateFrame::lerp(
const CoordinateFrame& other,
float alpha) const {
if (alpha == 1.0f) {
return other;
} else if (alpha == 0.0f) {
return *this;
} else {
const Quat q1(this->rotation);
const Quat q2(other.rotation);
return CoordinateFrame(
q1.slerp(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]);
}
}
} // namespace

View File

@@ -0,0 +1,70 @@
/**
@file Crypto.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2006-03-28
@edited 2006-04-06
*/
#include "G3D/platform.h"
#include "G3D/Crypto.h"
#include "G3D/g3dmath.h"
#include <zlib.h>
namespace G3D {
int Crypto::smallPrime(int n) {
debugAssert(n < numSmallPrimes() && n >= 0);
// From:
// http://primes.utm.edu/lists/small/1000.txt
static const int table[] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889,
1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
1993, 1997, 1999};
return table[n];
}
int Crypto::numSmallPrimes() {
return 303;
}
uint32 Crypto::crc32(const void* byte, size_t numBytes) {
return ::crc32(::crc32(0, Z_NULL, 0), static_cast<const Bytef *>(byte), numBytes);
}
} // G3D

View File

@@ -0,0 +1,471 @@
/**
@file Crypto_md5.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
Copyright 2006-2007, Morgan McGuire. All rights reserved.
@created 2006-03-28
@edited 2006-04-06
*/
#include "G3D/platform.h"
#include "G3D/Crypto.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include <cstring>
namespace G3D {
MD5Hash::MD5Hash(class BinaryInput& b) {
deserialize(b);
}
void MD5Hash::deserialize(class BinaryInput& b) {
b.readBytes(value, 16);
}
void MD5Hash::serialize(class BinaryOutput& b) const {
b.writeBytes(value, 16);
}
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
#ifdef __cplusplus
extern "C"
{
#endif
/* Initialize the algorithm. */
static void md5_init(md5_state_t *pms);
/* Append a string to the message. */
static void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
/* Finish the message and return the digest. */
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#ifdef __cplusplus
}
#endif
MD5Hash Crypto::md5(const void* data, size_t n) {
md5_state_t state;
md5_init(&state);
md5_append(&state, (const uint8*)data, (int)n);
MD5Hash h;
md5_finish(&state, &(h[0]));
return h;
}
/*
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
either statically or dynamically; added missing #include <string.h>
in library.
2002-03-11 lpd Corrected argument list for main(), and added int return
type, in test program and T value program.
2002-02-21 lpd Added missing #include <stdio.h> in test program.
2000-07-03 lpd Patched to eliminate warnings about "constant is
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
* defined as non-zero, the code will be compiled to run only on big-endian
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/
#if defined(G3D_LINUX) || defined(G3D_OSX)
# if defined(G3D_OSX_PPC)
# include <ppc/endian.h>
# elif defined(G3D_OSX_INTEL)
# include <i386/endian.h>
# elif defined(__linux__)
# include <endian.h>
# elif defined(__FreeBSD__)
# include <sys/endian.h>
# endif
#else
# define BYTE_ORDER 0
#endif
#define T_MASK ((md5_word_t)~0)
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
#define T3 0x242070db
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
#define T6 0x4787c62a
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
#define T9 0x698098d8
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
#define T13 0x6b901122
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
#define T16 0x49b40821
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
#define T19 0x265e5a51
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
#define T22 0x02441453
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
#define T25 0x21e1cde6
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
#define T28 0x455a14ed
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
#define T31 0x676f02d9
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
#define T35 0x6d9d6122
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
#define T38 0x4bdecfa9
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
#define T41 0x289b7ec6
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
#define T44 0x04881d05
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
#define T47 0x1fa27cf8
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
#define T50 0x432aff97
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
#define T53 0x655b59c3
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
#define T57 0x6fa87e4f
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
#define T60 0x4e0811a1
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
#define T63 0x2ad7d2bb
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
static void
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
{
md5_word_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
md5_word_t t;
#if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
md5_word_t X[16];
#else
/* Define storage for little-endian or both types of CPUs. */
md5_word_t xbuf[16];
const md5_word_t *X;
#endif
{
#if BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (const md5_byte_t *)0) & 3)) {
/* data are properly aligned */
X = (const md5_word_t *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
X = xbuf;
}
}
#endif
#if BYTE_ORDER == 0
else /* dynamic big-endian */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
# if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
# else
# define xbuf X /* (static only) */
# endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
#endif
}
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
void
md5_init(md5_state_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
}
void
md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
{
const md5_byte_t *p = data;
int left = nbytes;
int offset = (pms->count[0] >> 3) & 63;
md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
/* Update the message length. */
pms->count[1] += nbytes >> 29;
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
}
void
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
{
static const md5_byte_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}
}

View File

@@ -0,0 +1,176 @@
/**
@file Cylinder.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-02-07
@edited 2006-02-18
Copyright 2000-2006, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
#include "G3D/Cylinder.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/LineSegment.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Line.h"
#include "G3D/AABox.h"
namespace G3D {
Cylinder::Cylinder(class BinaryInput& b) {
deserialize(b);
}
Cylinder::Cylinder() {
}
Cylinder::Cylinder(const Vector3& _p1, const Vector3& _p2, float _r)
: p1(_p1), p2(_p2), mRadius(_r) {
}
void Cylinder::serialize(class BinaryOutput& b) const {
p1.serialize(b);
p2.serialize(b);
b.writeFloat64(mRadius);
}
void Cylinder::deserialize(class BinaryInput& b) {
p1.deserialize(b);
p2.deserialize(b);
mRadius = b.readFloat64();
}
Line Cylinder::axis() const {
return Line::fromTwoPoints(p1, p2);
}
float Cylinder::radius() const {
return mRadius;
}
float Cylinder::volume() const {
return
(float)pi() * square(mRadius) * (p1 - p2).magnitude();
}
float Cylinder::area() const {
return
// Sides
(twoPi() * mRadius) * height() +
// Caps
twoPi() * square(mRadius);
}
void Cylinder::getBounds(AABox& out) const {
Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * mRadius);
Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * mRadius);
out = AABox(min, max);
}
bool Cylinder::contains(const Vector3& p) const {
return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(mRadius);
}
void Cylinder::getReferenceFrame(CoordinateFrame& cframe) const {
cframe.translation = center();
Vector3 Y = (p1 - p2).direction();
Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX();
Vector3 Z = X.cross(Y).direction();
X = Y.cross(Z);
cframe.rotation.setColumn(0, X);
cframe.rotation.setColumn(1, Y);
cframe.rotation.setColumn(2, Z);
}
void Cylinder::getRandomSurfacePoint(Vector3& p, Vector3& N) const {
float h = height();
float r = radius();
// Create a random point on a standard cylinder and then rotate to the global frame.
// Relative areas (factor of 2PI already taken out)
float capRelArea = square(r) / 2.0f;
float sideRelArea = r * h;
float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea);
if (r1 < capRelArea * 2) {
// Select a point uniformly at random on a disk
// @cite http://mathworld.wolfram.com/DiskPointPicking.html
float a = uniformRandom(0, (float)twoPi());
float r2 = sqrt(uniformRandom(0, 1)) * r;
p.x = cos(a) * r2;
p.z = sin(a) * r2;
N.x = 0;
N.z = 0;
if (r1 < capRelArea) {
// Top
p.y = h / 2.0f;
N.y = 1;
} else {
// Bottom
p.y = -h / 2.0f;
N.y = -1;
}
} else {
// Side
float a = uniformRandom(0, (float)twoPi());
N.x = cos(a);
N.y = 0;
N.z = sin(a);
p.x = N.x * r;
p.z = N.y * r;
p.y = uniformRandom(-h / 2.0f, h / 2.0f);
}
// Transform to world space
CoordinateFrame cframe;
getReferenceFrame(cframe);
p = cframe.pointToWorldSpace(p);
N = cframe.normalToWorldSpace(N);
}
Vector3 Cylinder::randomInteriorPoint() const {
float h = height();
float r = radius();
// Create a random point in a standard cylinder and then rotate to the global frame.
// Select a point uniformly at random on a disk
// @cite http://mathworld.wolfram.com/DiskPointPicking.html
float a = uniformRandom(0, (float)twoPi());
float r2 = sqrt(uniformRandom(0, 1)) * r;
Vector3 p( cos(a) * r2,
uniformRandom(-h / 2.0f, h / 2.0f),
sin(a) * r2);
// Transform to world space
CoordinateFrame cframe;
getReferenceFrame(cframe);
return cframe.pointToWorldSpace(p);
}
} // namespace

View File

@@ -0,0 +1,874 @@
/**
@file FileSystem.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@author 2002-06-06
@edited 2010-04-10
*/
#include "G3D/FileSystem.h"
#include "G3D/System.h"
#include "G3D/stringutils.h"
#include "G3D/fileutils.h"
#include <sys/stat.h>
#include <sys/types.h>
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
#include "zip.h"
#endif
#include "G3D/g3dfnmatch.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#ifdef G3D_WIN32
// Needed for _getcwd
# include <direct.h>
// Needed for _findfirst
# include <io.h>
# ifdef __MINGW32__
# define stat64 stat
# else
# define stat64 _stat64
# endif
#else
# include <dirent.h>
# include <fnmatch.h>
# include <unistd.h>
# define _getcwd getcwd
# define _stat stat
#endif
#ifdef __CYGWIN__
#define stat64 stat
#endif
namespace G3D {
static FileSystem* common = NULL;
FileSystem& FileSystem::instance() {
init();
return *common;
}
void FileSystem::init() {
if (common == NULL) {
common = new FileSystem();
}
}
void FileSystem::cleanup() {
if (common != NULL) {
delete common;
common = NULL;
}
}
FileSystem::FileSystem() : m_cacheLifetime(10) {}
/////////////////////////////////////////////////////////////
bool FileSystem::Dir::contains(const std::string& f) const {
for (int i = 0; i < nodeArray.size(); ++i) {
# ifdef G3D_WIN32
if (stricmp(f.c_str(), nodeArray[i].name.c_str()) == 0) {
return true;
}
# else
if (f == nodeArray[i].name) {
return true;
}
# endif
}
return false;
}
void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::string& pathInsideZipfile) {
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
struct zip* z = zip_open( FilePath::removeTrailingSlash(zipfile).c_str(), ZIP_CHECKCONS, NULL );
debugAssert(z);
int count = zip_get_num_files( z );
Set<std::string> alreadyAdded;
for (int i = 0; i < count; ++i) {
struct zip_stat info;
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
zip_stat_index( z, i, ZIP_FL_NOCASE, &info );
// Fully-qualified name of a file inside zipfile
std::string name = info.name;
if (beginsWith(name, pathInsideZipfile)) {
// We found something inside the directory we were looking for,
// so the directory itself must exist
exists = true;
// For building the cached directory listing, extract only elements that do not contain
// additional subdirectories.
int start = pathInsideZipfile.size();
if ((int(name.length()) > start) && isSlash(name[start])) {
++start;
}
int end = findSlash(name, start);
if (end == -1) {
// There are no more slashes; add this name
name = name.substr(start);
if (alreadyAdded.insert(name)) {
Entry& e = nodeArray.next();
e.name = name;
e.type = FILE_TYPE;
}
} else {
// There are more slashes, indicating that this is a directory
name = name.substr(start, end);
if (alreadyAdded.insert(name)) {
Entry& e = nodeArray.next();
e.name = name;
e.type = DIR_TYPE;
}
}
}
}
zip_close(z);
z = NULL;
#endif
}
FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpdate) {
const std::string& key =
# if defined(G3D_WIN32)
FilePath::canonicalize(FilePath::removeTrailingSlash(toLower(FilePath::canonicalize(resolve(path)))));
# else
FilePath::canonicalize(FilePath::removeTrailingSlash(FilePath::canonicalize(resolve(path))));
# endif
RealTime now = System::time();
Dir& dir = m_cache.getCreate(key);
if ((now > dir.lastChecked + cacheLifetime()) || forceUpdate) {
dir = Dir();
// Out of date: update
dir.lastChecked = now;
struct _stat st;
const bool exists = _stat(key.c_str(), &st) != -1;
const bool isDirectory = (st.st_mode & S_IFDIR) != 0;
// Does this path exist on the real filesystem?
if (exists && isDirectory) {
// Is this path actually a directory?
if (isDirectory) {
dir.exists = true;
// Update contents
# ifdef G3D_WIN32
const std::string& filespec = FilePath::concat(key, "*");
struct _finddata_t fileinfo;
intptr_t handle = _findfirst(filespec.c_str(), &fileinfo);
debugAssert(handle != -1);
int result = 0;
do {
if ((strcmp(fileinfo.name, ".") != 0) && (strcmp(fileinfo.name, "..") != 0)) {
Entry& e = dir.nodeArray.next();
e.name = fileinfo.name;
if ((fileinfo.attrib & _A_SUBDIR) != 0) {
e.type = DIR_TYPE;
} else {
e.type = FILE_TYPE;
}
}
result = _findnext(handle, &fileinfo);
} while (result == 0);
_findclose(handle);
# else
DIR* listing = opendir(key.c_str());
debugAssertM(listing, "opendir failed on '" + key + "'");
struct dirent* entry = readdir(listing);
while (entry != NULL) {
if ((strcmp(entry->d_name, "..") != 0) && (strcmp(entry->d_name, ".") != 0)) {
Entry& e = dir.nodeArray.next();
e.name = entry->d_name;
# ifdef _DIRENT_HAVE_D_TYPE
// Not all POSIX systems support this field
// http://www.delorie.com/gnu/docs/glibc/libc_270.html
switch (entry->d_type) {
case DT_DIR:
e.type = DIR_TYPE;
break;
case DT_REG:
e.type = FILE_TYPE;
break;
case DT_UNKNOWN:
default:
e.type = UNKNOWN;
break;
}
# endif
}
entry = readdir(listing);
}
closedir(listing);
listing = NULL;
entry = NULL;
# endif
}
} else {
std::string zip;
if (exists && isZipfile(path)) {
// This is a zipfile; get its root
dir.isZipfile = true;
dir.computeZipListing(path, "");
} else if (inZipfile(path, zip)) {
// There is a zipfile somewhere in the path. Does
// the rest of the path exist inside the zipfile?
dir.inZipfile = true;
dir.computeZipListing(zip, path.substr(zip.length() + 1));
}
}
}
return dir;
}
bool FileSystem::_inZipfile(const std::string& path, std::string& z) {
// Reject trivial cases before parsing
if (path.find('.') == std::string::npos) {
// There is no zipfile possible, since G3D requires
// an extension on zipfiles.
return false;
}
// Look at all sub-paths containing periods.
// For each, ask if it is a zipfile.
int current = 0;
current = path.find('.', current);
while (current != -1) {
// xxxxx/foo.zip/yyyyy
current = path.find('.', current);
// Look forward for the next slash
int s = findSlash(path, current);
if (s == -1) {
// No more slashes
return false;
}
z = path.substr(0, s);
if (_isZipfile(z)) {
return true;
}
current = s + 1;
}
z = "";
return false;
}
bool FileSystem::_isZipfile(const std::string& filename) {
if (FilePath::ext(filename).empty()) {
return false;
}
FILE* f = fopen(FilePath::removeTrailingSlash(filename).c_str(), "r");
if (f == NULL) {
return false;
}
uint8 header[4];
fread(header, 4, 1, f);
const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
for (int i = 0; i < 4; ++i) {
if (header[i] != zipHeader[i]) {
fclose(f);
return false;
}
}
fclose(f);
return true;
}
FILE* FileSystem::_fopen(const char* filename, const char* mode) {
for (const char* m = mode; *m != '\0'; ++m) {
if (*m == 'w') {
// Purge the cache entry for the parent of this directory
_clearCache(FilePath::parent(_resolve(filename)));
break;
}
}
return ::fopen(filename, mode);
}
void FileSystem::_clearCache(const std::string& path) {
if ((path == "") || FilePath::isRoot(path)) {
m_cache.clear();
} else {
Array<std::string> keys;
m_cache.getKeys(keys);
const std::string& prefix =
# ifdef G3D_WIN32
toLower(FilePath::canonicalize(FilePath::removeTrailingSlash(_resolve(path))));
# else
FilePath::canonicalize(FilePath::removeTrailingSlash(_resolve(path)));
# endif
const std::string& prefixSlash = prefix + "/";
for (int k = 0; k < keys.size(); ++k) {
const std::string& key = keys[k];
if ((key == prefix) || beginsWith(key, prefixSlash)) {
m_cache.remove(keys[k]);
}
}
}
}
void FileSystem::_setCacheLifetime(float t) {
m_cacheLifetime = t;
}
void FileSystem::_createDirectory(const std::string& dir) {
if (dir == "") {
return;
}
std::string d = _resolve(dir);
// Add a trailing / if there isn't one.
switch (d[d.size() - 1]) {
case '/':
case '\\':
break;
default:
d += "/";
}
// If it already exists, do nothing
if (_exists(FilePath::removeTrailingSlash(d))) {
return;
}
// Parse the name apart
std::string root, base, ext;
Array<std::string> path;
std::string lead;
FilePath::parse(d, root, path, base, ext);
debugAssert(base == "");
debugAssert(ext == "");
// Begin with an extra period so "c:\" becomes "c:\.\" after
// appending a path and "c:" becomes "c:.\", not root: "c:\"
std::string p = root + ".";
// Create any intermediate that doesn't exist
for (int i = 0; i < path.size(); ++i) {
p += "/" + path[i];
if (! _exists(p)) {
// Windows only requires one argument to mkdir,
// where as unix also requires the permissions.
# ifndef G3D_WIN32
mkdir(p.c_str(), 0777);
# else
_mkdir(p.c_str());
# endif
}
}
_clearCache(FilePath::parent(FilePath::removeTrailingSlash(d)));
}
void FileSystem::_copyFile(const std::string& source, const std::string& dest) {
# ifdef G3D_WIN32
// TODO: handle case where srcPath is in a zipfile
CopyFileA(source.c_str(), dest.c_str(), FALSE);
_clearCache(FilePath::parent(_resolve(dest)));
# else
// Read it all in, then dump it out
BinaryInput in(source, G3D_LITTLE_ENDIAN);
BinaryOutput out(dest, G3D_LITTLE_ENDIAN);
out.writeBytes(in.getCArray(), in.size());
out.commit(false);
# endif
}
bool FileSystem::_exists(const std::string& f, bool trustCache) {
if (FilePath::isRoot(f)) {
# ifdef G3D_WIN32
const std::string& winname = toLower(f.substr(0, 1)) + ":\\";
return _drives().contains(winname);
# else
return true;
# endif
}
std::string path = FilePath::removeTrailingSlash(f);
std::string parentPath = FilePath::parent(path);
const Dir& entry = getContents(parentPath, ! trustCache);
if (FilePath::containsWildcards(f)) {
if (! entry.exists) {
// The directory didn't exist, so neither do its contents
return false;
}
const std::string& pattern = FilePath::baseExt(path);
# ifdef G3D_WIN32
static const int flags = FNM_CASEFOLD;
# else
static const int flags = 0;
# endif
// See if any element of entry matches the wild card
for (int i = 0; i < entry.nodeArray.size(); ++i) {
if (FilePath::matches(entry.nodeArray[i].name, pattern, flags)) {
return true;
}
}
// Could not find a match
return false;
} else {
return entry.exists && entry.contains(FilePath::baseExt(path));
}
}
bool FileSystem::_isDirectory(const std::string& filename) {
// TODO: work with zipfiles and cache
struct _stat st;
const bool exists = _stat(FilePath::removeTrailingSlash(filename).c_str(), &st) != -1;
return exists && ((st.st_mode & S_IFDIR) != 0);
}
std::string FileSystem::_resolve(const std::string& filename, const std::string& cwd) {
if (filename.size() >= 1) {
if (isSlash(filename[0])) {
// Already resolved
return filename;
} else {
#ifdef G3D_WIN32
if ((filename.size() >= 2) && (filename[1] == ':')) {
// There is a drive spec on the front.
if ((filename.size() >= 3) && isSlash(filename[2])) {
// Already fully qualified
return filename;
} else {
// The drive spec is relative to the
// working directory on that drive.
debugAssertM(false, "Files of the form d:path are"
" not supported (use a fully qualified"
" name).");
return filename;
}
}
#endif
}
}
// Prepend the working directory.
return FilePath::concat(cwd, filename);
}
std::string FileSystem::_currentDirectory() {
static const int N = 2048;
char buffer[N];
_getcwd(buffer, N);
return std::string(buffer);
}
bool FileSystem::_isNewer(const std::string& src, const std::string& dst) {
// TODO: work with cache and zipfiles
struct _stat sts;
bool sexists = _stat(src.c_str(), &sts) != -1;
struct _stat dts;
bool dexists = _stat(dst.c_str(), &dts) != -1;
return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime));
}
int64 FileSystem::_size(const std::string& filename) {
struct stat64 st;
int result = stat64(filename.c_str(), &st);
if (result == -1) {
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
std::string zip, contents;
if (zipfileExists(filename, zip, contents)) {
int64 requiredMem;
struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
debugAssertM(z != NULL, zip + ": zip open failed.");
{
struct zip_stat info;
zip_stat_init( &info ); // Docs unclear if zip_stat_init is required.
int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
requiredMem = info.size;
}
zip_close(z);
return requiredMem;
} else {
#endif
return -1;
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
}
#endif
}
return st.st_size;
}
void FileSystem::listHelper(const std::string& shortSpec, const std::string& parentPath, Array<std::string>& result, const ListSettings& settings) {
Dir& dir = getContents(parentPath, false);
if (! dir.exists) {
return;
}
for (int i = 0; i < dir.nodeArray.size(); ++i) {
Entry& entry = dir.nodeArray[i];
// See if it matches the spec
if (FilePath::matches(entry.name, shortSpec, settings.caseSensitive)) {
if ((entry.type == UNKNOWN) && ! (settings.files && settings.directories)) {
// Update the type
entry.type = isDirectory(FilePath::concat(parentPath, entry.name)) ? DIR_TYPE : FILE_TYPE;
}
if ((settings.files && settings.directories) ||
(settings.files && (entry.type == FILE_TYPE)) ||
(settings.directories && (entry.type == DIR_TYPE))) {
if (settings.includeParentPath) {
result.append(FilePath::concat(parentPath, entry.name));
} else {
result.append(entry.name);
}
}
} // match
if (settings.recursive && (entry.type == DIR_TYPE)) {
listHelper(shortSpec, FilePath::concat(parentPath, entry.name), result, settings);
}
} // for
}
void FileSystem::_list(const std::string& spec, Array<std::string>& result, const ListSettings& settings) {
const std::string& shortSpec = FilePath::baseExt(spec);
const std::string& parentPath = FilePath::parent(spec);
listHelper(shortSpec, parentPath, result, settings);
}
#ifdef G3D_WIN32
const Array<std::string>& FileSystem::_drives() {
if (m_winDrive.length() == 0) {
// See http://msdn.microsoft.com/en-us/library/aa364975(VS.85).aspx
static const size_t bufSize = 5000;
char bufData[bufSize];
GetLogicalDriveStringsA(bufSize, bufData);
// Drive list is a series of NULL-terminated strings, itself terminated with a NULL.
for (int i = 0; bufData[i] != '\0'; ++i) {
const char* thisString = bufData + i;
m_winDrive.append(toLower(thisString));
i += strlen(thisString) + 1;
}
}
return m_winDrive;
}
#endif
/////////////////////////////////////////////////////////////////////
bool FilePath::isRoot(const std::string& f) {
# ifdef G3D_WIN32
if (f.length() < 2) {
return false;
}
if (f[1] == ':') {
if (f.length() == 2) {
// e.g., "x:"
return true;
} else if ((f.length() == 3) && isSlash(f[2])) {
// e.g., "x:\"
return true;
}
}
if (isSlash(f[0]) && isSlash(f[1])) {
// e.g., "\\foo\"
return true;
}
# else
if (f == "/") {
return true;
}
# endif
return false;
}
std::string FilePath::removeTrailingSlash(const std::string& f) {
bool removeSlash = ((endsWith(f, "/") || endsWith(f, "\\"))) && ! isRoot(f);
return removeSlash ? f.substr(0, f.length() - 1) : f;
}
std::string FilePath::concat(const std::string& dirname, const std::string& file) {
// Ensure that the directory ends in a slash
if (! dirname.empty() &&
! isSlash(dirname[dirname.size() - 1]) &&
(dirname[dirname.size() - 1] != ':')) {
return dirname + '/' + file;
} else {
return dirname + file;
}
}
std::string FilePath::ext(const std::string& filename) {
int i = filename.rfind(".");
if (i >= 0) {
return filename.substr(i + 1, filename.length() - i);
} else {
return "";
}
}
std::string FilePath::baseExt(const std::string& filename) {
int i = findLastSlash(filename);
# ifdef G3D_WIN32
int j = filename.rfind(":");
if ((i == -1) && (j >= 0)) {
i = j;
}
# endif
if (i == -1) {
return filename;
} else {
return filename.substr(i + 1, filename.length() - i);
}
}
std::string FilePath::base(const std::string& path) {
std::string filename = baseExt(path);
int i = filename.rfind(".");
if (i == -1) {
// No extension
return filename;
} else {
return filename.substr(0, i);
}
}
std::string FilePath::parent(const std::string& path) {
int i = findLastSlash(removeTrailingSlash(path));
# ifdef G3D_WIN32
int j = path.rfind(":");
if ((i == -1) && (j >= 0)) {
i = j;
}
# endif
if (i == -1) {
return "";
} else {
return path.substr(0, i + 1);
}
}
bool FilePath::containsWildcards(const std::string& filename) {
return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos);
}
bool FilePath::matches(const std::string& path, const std::string& pattern, bool caseSensitive) {
int flags = FNM_PERIOD | FNM_NOESCAPE | FNM_PATHNAME;
if (! caseSensitive) {
flags |= FNM_CASEFOLD;
}
return g3dfnmatch(pattern.c_str(), path.c_str(), flags) == 0;
}
static int fixslash(int c) {
return (c == '\\') ? '/' : c;
}
std::string FilePath::canonicalize(std::string x) {
std::transform(x.begin(), x.end(), x.begin(), fixslash);
return x;
}
void FilePath::parse
(const std::string& filename,
std::string& root,
Array<std::string>& path,
std::string& base,
std::string& ext) {
std::string f = filename;
root = "";
path.clear();
base = "";
ext = "";
if (f == "") {
// Empty filename
return;
}
// See if there is a root/drive spec.
if ((f.size() >= 2) && (f[1] == ':')) {
if ((f.size() > 2) && isSlash(f[2])) {
// e.g. c:\foo
root = f.substr(0, 3);
f = f.substr(3, f.size() - 3);
} else {
// e.g. c:foo
root = f.substr(2);
f = f.substr(2, f.size() - 2);
}
} else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) {
// e.g. //foo
root = f.substr(0, 2);
f = f.substr(2, f.size() - 2);
} else if (isSlash(f[0])) {
root = f.substr(0, 1);
f = f.substr(1, f.size() - 1);
}
// Pull the extension off
{
// Find the period
size_t i = f.rfind('.');
if (i != std::string::npos) {
// Make sure it is after the last slash!
size_t j = iMax(f.rfind('/'), f.rfind('\\'));
if ((j == std::string::npos) || (i > j)) {
ext = f.substr(i + 1, f.size() - i - 1);
f = f.substr(0, i);
}
}
}
// Pull the basename off
{
// Find the last slash
size_t i = iMax(f.rfind('/'), f.rfind('\\'));
if (i == std::string::npos) {
// There is no slash; the basename is the whole thing
base = f;
f = "";
} else if ((i != std::string::npos) && (i < f.size() - 1)) {
base = f.substr(i + 1, f.size() - i - 1);
f = f.substr(0, i);
}
}
// Parse what remains into path.
size_t prev, cur = 0;
while (cur < f.size()) {
prev = cur;
// Allow either slash
size_t i = f.find('/', prev + 1);
size_t j = f.find('\\', prev + 1);
if (i == std::string::npos) {
i = f.size();
}
if (j == std::string::npos) {
j = f.size();
}
cur = iMin(i, j);
if (cur == std::string::npos) {
cur = f.size();
}
path.append(f.substr(prev, cur - prev));
++cur;
}
}
}

View File

@@ -0,0 +1,511 @@
/**
@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 "G3D/GCamera.h"
#include "G3D/platform.h"
#include "G3D/Rect2D.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Ray.h"
#include "G3D/Matrix4.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
GCamera::GCamera(const Any& any) {
any.verifyName("GCamera");
any.verifyType(Any::TABLE);
*this = GCamera();
const Any::AnyTable& table = any.table();
Any::AnyTable::Iterator it = table.begin();
while (it.hasMore()) {
const std::string& k = toUpper(it->key);
if (k == "FOVDIRECTION") {
const std::string& v = toUpper(it->value);
if (v == "HORIZONTAL") {
m_direction = HORIZONTAL;
} else if (v == "VERTICAL") {
m_direction = VERTICAL;
} else {
any.verify(false, "fovDirection must be \"HORIZONTAL\" or \"VERTICAL\"");
}
} else if (k == "COORDINATEFRAME") {
m_cframe = it->value;
} else if (k == "FOVDEGREES") {
m_fieldOfView = toRadians(it->value.number());
} else if (k == "NEARPLANEZ") {
m_nearPlaneZ = it->value;
} else if (k == "FARPLANEZ") {
m_farPlaneZ = it->value;
} else if (k == "PIXELOFFSET") {
m_pixelOffset = it->value;
} else {
any.verify(false, std::string("Illegal key in table: ") + it->key);
}
++it;
}
}
GCamera::operator Any() const {
Any any(Any::TABLE, "GCamera");
any.set("fovDirection", std::string((m_direction == HORIZONTAL) ? "HORIZONTAL" : "VERTICAL"));
any.set("fovDegrees", toDegrees(m_fieldOfView));
any.set("nearPlaneZ", nearPlaneZ());
any.set("farPlaneZ", farPlaneZ());
any.set("coordinateFrame", coordinateFrame());
any.set("pixelOffset", pixelOffset());
return any;
}
GCamera::GCamera() {
setNearPlaneZ(-0.2f);
setFarPlaneZ(-150.0f);
setFieldOfView((float)toRadians(90.0f), HORIZONTAL);
}
GCamera::GCamera(const Matrix4& proj, const CFrame& frame) {
float left, right, bottom, top, nearval, farval;
proj.getPerspectiveProjectionParameters(left, right, bottom, top, nearval, farval);
setNearPlaneZ(-nearval);
setFarPlaneZ(-farval);
float x = right;
// Assume horizontal field of view
setFieldOfView(atan2(x, -m_nearPlaneZ) * 2.0f, HORIZONTAL);
setCoordinateFrame(frame);
}
GCamera::~GCamera() {
}
void GCamera::getCoordinateFrame(CoordinateFrame& c) const {
c = m_cframe;
}
void GCamera::setCoordinateFrame(const CoordinateFrame& c) {
m_cframe = c;
}
void GCamera::setFieldOfView(float angle, FOVDirection dir) {
debugAssert((angle < pi()) && (angle > 0));
m_fieldOfView = angle;
m_direction = dir;
}
float GCamera::imagePlaneDepth() const{
return -m_nearPlaneZ;
}
float GCamera::viewportWidth(const Rect2D& viewport) const {
// Compute the side of a square at the near plane based on our field of view
float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
if (m_direction == VERTICAL) {
s *= viewport.width() / viewport.height();
}
return s;
}
float GCamera::viewportHeight(const Rect2D& viewport) const {
// Compute the side of a square at the near plane based on our field of view
float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
debugAssert(m_fieldOfView < toRadians(180));
if (m_direction == HORIZONTAL) {
s *= viewport.height() / viewport.width();
}
return s;
}
Ray GCamera::worldRay(float x, float y, const Rect2D& viewport) const {
int screenWidth = iFloor(viewport.width());
int screenHeight = iFloor(viewport.height());
Vector3 origin = m_cframe.translation;
float cx = screenWidth / 2.0f;
float cy = screenHeight / 2.0f;
float vw = viewportWidth(viewport);
float vh = viewportHeight(viewport);
Vector3 direction = Vector3( (x - cx) * vw / screenWidth,
-(y - cy) * vh / screenHeight,
m_nearPlaneZ);
direction = m_cframe.vectorToWorldSpace(direction);
// Normalize the direction (we didn't do it before)
direction = direction.direction();
return Ray::fromOriginAndDirection(origin, direction);
}
void GCamera::getProjectPixelMatrix(const Rect2D& viewport, Matrix4& P) const {
getProjectUnitMatrix(viewport, P);
float screenWidth = viewport.width();
float screenHeight = viewport.height();
float sx = screenWidth / 2.0;
float sy = screenHeight / 2.0;
P = Matrix4(sx, 0, 0, sx + viewport.x0() - 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);
}
void GCamera::serialize(BinaryOutput& bo) const {
bo.writeFloat32(m_fieldOfView);
bo.writeFloat32(imagePlaneDepth());
debugAssert(nearPlaneZ() < 0.0f);
bo.writeFloat32(nearPlaneZ());
debugAssert(farPlaneZ() < 0.0f);
bo.writeFloat32(farPlaneZ());
m_cframe.serialize(bo);
bo.writeInt8(m_direction);
m_pixelOffset.serialize(bo);
}
void GCamera::deserialize(BinaryInput& bi) {
m_fieldOfView = bi.readFloat32();
m_nearPlaneZ = bi.readFloat32();
debugAssert(m_nearPlaneZ < 0.0f);
m_farPlaneZ = bi.readFloat32();
debugAssert(m_farPlaneZ < 0.0f);
m_cframe.deserialize(bi);
m_direction = (FOVDirection)bi.readInt8();
m_pixelOffset.deserialize(bi);
}
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,298 @@
/**
@file GImage_bayer.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2006-05-10
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
namespace G3D {
void GImage::BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int width, int height, const uint8* in, uint8* out) {
debugAssert(in != out);
int halfHeight = height / 2;
int halfWidth = width / 2;
int dst_off = 0;
for (int y = 0; y < halfHeight; ++y) {
for (int x = 0; x < halfWidth; ++x) {
// GBRG
int src_off = x*2 + y*2*width;
out[dst_off] = in[src_off+width]; // red
out[dst_off+1] = ((int)in[src_off] + (int)in[src_off+width+1])/2; // green
out[dst_off+2] = in[src_off+1]; // blue
dst_off = dst_off + 3;
}
}
}
void GImage::Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out) {
// Undo quarter-size Bayer as best we can. This code isn't very efficient, but it
// also isn't used very frequently.
debugAssert(out != in);
int outWidth = 2 * inWidth;
int outHeight = 2 * inHeight;
for (int y = 0; y < outHeight; ++y) {
for (int x = 0; x < outWidth; ++x) {
const Color3uint8* inp = ((const Color3uint8*)in) + ((x/2) + (y/2)* inWidth);
uint8* outp = out + x + y * outWidth;
if (isEven(y)) {
// GB row
if (isEven(x)) {
// Green
*outp = inp->g;
} else {
// Blue
*outp = inp->b;
}
} else {
// RG row
if (isEven(x)) {
// Red
*outp = inp->r;
} else {
// Green
*outp = inp->g;
}
}
}
}
}
/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
static uint8 applyFilter(
const uint8* I,
int x,
int y,
int w,
int h,
const float filter[5][5]) {
debugAssert(isEven(w));
debugAssert(isEven(h));
float sum = 0.0f;
float denom = 0.0f;
for (int dy = 0; dy < 5; ++dy) {
int offset = ((y + dy + h - 2) % h) * w;
for (int dx = 0; dx < 5; ++dx) {
float f = filter[dy][dx];
sum += f * I[((x + dx + w - 2) % w) + offset];
denom += f;
}
}
return (uint8)iClamp(iRound(sum / denom), 0, 255);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//
// Bayer conversions
//
// There are two kinds of rows (GR and BG).
// In each row, there are two kinds of pixels (G/R, B/G).
// We express the four kinds of INPUT pixels as:
// GRG, GRG, BGB, BGG
//
// There are three kinds of OUTPUT pixels: R, G, B.
// Thus there are nominally 12 different I/O combinations,
// but several are impulses because needed output at that
// location *is* the input (e.g., G_GRG and G_BGG).
//
// The following 5x5 row-major filters are named as output_input.
// Green
static const float G_GRR[5][5] =
{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
static const float G_BGB[5][5] =
{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
// Red
//(the caption in the paper is wrong for this case:
// "R row B column really means R row G column"
static const float R_GRG[5][5] =
{{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
{ -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
static const float R_BGG[5][5] =
{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
{ 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
static const float R_BGB[5][5] =
{{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
{-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
// Blue
//(the caption in the paper is wrong for this case:
// "B row R column really means B row G column")
#define B_BGG R_GRG
#define B_GRG R_BGG
#define B_GRR R_BGB
void GImage::BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
debugAssert(in != _out);
Color3uint8* out = (Color3uint8*)_out;
for (int y = 0; y < h; ++y) {
// Row beginning in the input array.
int offset = y * w;
// RG row
for (int x = 0; x < w; ++x, ++out) {
// R pixel
{
out->r = in[x + offset];
out->g = applyFilter(in, x, y, w, h, G_GRR);
out->b = applyFilter(in, x, y, w, h, B_GRR);
}
++x; ++out;
// G pixel
{
out->r = applyFilter(in, x, y, w, h, R_GRG);
out->g = in[x + offset];
out->b = applyFilter(in, x, y, w, h, B_GRG);
}
}
++y;
offset += w;
// GB row
for (int x = 0; x < w; ++x, ++out) {
// G pixel
{
out->r = applyFilter(in, x, y, w, h, R_BGG);
out->g = in[x + offset];
out->b = applyFilter(in, x, y, w, h, B_BGG);
}
++x; ++out;
// B pixel
{
out->r = applyFilter(in, x, y, w, h, R_BGB);
out->g = applyFilter(in, x, y, w, h, G_BGB);
out->b = in[x + offset];
}
}
}
}
static void swapRedAndBlue(int N, Color3uint8* out) {
for (int i = N - 1; i >= 0; --i) {
uint8 tmp = out[i].r;
out[i].r = out[i].b;
out[i].b = tmp;
}
}
void GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
// Run the equivalent function for red
BAYER_G8B8_R8G8_to_R8G8B8_MHC(w, h, in, _out);
// Now swap red and blue
swapRedAndBlue(w * h, (Color3uint8*)_out);
}
void GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
// Run the equivalent function for red
BAYER_R8G8_G8B8_to_R8G8B8_MHC(w, h, in, _out);
// Now swap red and blue
swapRedAndBlue(w * h, (Color3uint8*)_out);
}
void GImage::BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
debugAssert(in != _out);
Color3uint8* out = (Color3uint8*)_out;
for (int y = 0; y < h; ++y) {
// Row beginning in the input array.
int offset = y * w;
// GB row
for (int x = 0; x < w; ++x, ++out) {
// G pixel
{
out->r = applyFilter(in, x, y, w, h, R_BGG);
out->g = in[x + offset];
out->b = applyFilter(in, x, y, w, h, B_BGG);
}
++x; ++out;
// B pixel
{
out->r = applyFilter(in, x, y, w, h, R_BGB);
out->g = applyFilter(in, x, y, w, h, G_BGB);
out->b = in[x + offset];
}
}
++y;
offset += w;
// RG row
for (int x = 0; x < w; ++x, ++out) {
// R pixel
{
out->r = in[x + offset];
out->g = applyFilter(in, x, y, w, h, G_GRR);
out->b = applyFilter(in, x, y, w, h, B_GRR);
}
++x; ++out;
// G pixel
{
out->r = applyFilter(in, x, y, w, h, R_GRG);
out->g = in[x + offset];
out->b = applyFilter(in, x, y, w, h, B_GRG);
}
}
}
}
#undef B_BGG
#undef B_GRG
#undef B_GRR
}

View File

@@ -0,0 +1,717 @@
/**
@file GImage_bmp.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2006-05-10
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Log.h"
namespace G3D {
#ifndef G3D_WIN32
/**
This is used by the Windows bitmap I/O.
*/
static const int BI_RGB = 0;
#endif
void GImage::encodeBMP(
BinaryOutput& out) const {
debugAssert(m_channels == 1 || m_channels == 3);
out.setEndian(G3D_LITTLE_ENDIAN);
uint8 red;
uint8 green;
uint8 blue;
int pixelBufferSize = m_width * m_height * 3;
int fileHeaderSize = 14;
int infoHeaderSize = 40;
int BMScanWidth;
int BMPadding;
// First write the BITMAPFILEHEADER
//
// WORD bfType;
// DWORD bfSize;
// WORD bfReserved1;
// WORD bfReserved2;
// DWORD bfOffBits;
// Type
out.writeUInt8('B');
out.writeUInt8('M');
// File size
out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize);
// Two reserved fields set to zero
out.writeUInt16(0);
out.writeUInt16(0);
// The offset, in bytes, from the BITMAPFILEHEADER structure
// to the bitmap bits.
out.writeUInt32(infoHeaderSize + fileHeaderSize);
// Now the BITMAPINFOHEADER
//
// DWORD biSize;
// LONG biWidth;
// LONG biHeight;
// WORD biPlanes;
// WORD biBitCount
// DWORD biCompression;
// DWORD biSizeImage;
// LONG biXPelsPerMeter;
// LONG biYPelsPerMeter;
// DWORD biClrUsed;
// DWORD biClrImportant;
// Size of the info header
out.writeUInt32(infoHeaderSize);
// Width and height of the image
out.writeUInt32(m_width);
out.writeUInt32(m_height);
// Planes ("must be set to 1")
out.writeUInt16(1);
// BitCount and CompressionType
out.writeUInt16(24);
out.writeUInt32(BI_RGB);
// Image size ("may be zero for BI_RGB bitmaps")
out.writeUInt32(0);
// biXPelsPerMeter
out.writeUInt32(0);
// biYPelsPerMeter
out.writeUInt32(0);
// biClrUsed
out.writeUInt32(0);
// biClrImportant
out.writeUInt32(0);
BMScanWidth = m_width * 3;
if (BMScanWidth & 3) {
BMPadding = 4 - (BMScanWidth & 3);
} else {
BMPadding = 0;
}
int hStart = m_height - 1;
int hEnd = -1;
int hDir = -1;
int dest;
// Write the pixel data
for (int h = hStart; h != hEnd; h += hDir) {
dest = m_channels * h * m_width;
for (int w = 0; w < m_width; ++w) {
if (m_channels == 3) {
red = m_byte[dest];
green = m_byte[dest + 1];
blue = m_byte[dest + 2];
} else {
red = m_byte[dest];
green = m_byte[dest];
blue = m_byte[dest];
}
out.writeUInt8(blue);
out.writeUInt8(green);
out.writeUInt8(red);
dest += m_channels;
}
if (BMPadding > 0) {
out.skip(BMPadding);
}
}
}
void GImage::decodeBMP(
BinaryInput& input) {
// The BMP decoding uses these flags.
static const uint16 PICTURE_NONE = 0x0000;
static const uint16 PICTURE_BITMAP = 0x1000;
// Compression Flags
static const uint16 PICTURE_UNCOMPRESSED = 0x0100;
static const uint16 PICTURE_MONOCHROME = 0x0001;
static const uint16 PICTURE_4BIT = 0x0002;
static const uint16 PICTURE_8BIT = 0x0004;
static const uint16 PICTURE_16BIT = 0x0008;
static const uint16 PICTURE_24BIT = 0x0010;
static const uint16 PICTURE_32BIT = 0x0020;
(void)PICTURE_16BIT;
(void)PICTURE_32BIT;
// This is a simple BMP loader that can handle uncompressed BMP files.
// Verify this is a BMP file by looking for the BM tag.
input.reset();
std::string tag = input.readString(2);
if (tag != "BM") {
throw Error("Not a BMP file", input.getFilename());
}
m_channels = 3;
// Skip to the BITMAPINFOHEADER's width and height
input.skip(16);
m_width = input.readUInt32();
m_height = input.readUInt32();
// Skip to the bit count and compression type
input.skip(2);
uint16 bitCount = input.readUInt16();
uint32 compressionType = input.readUInt32();
uint8 red;
uint8 green;
uint8 blue;
uint8 blank;
// Only uncompressed bitmaps are supported by this code
if ((int32)compressionType != BI_RGB) {
throw Error("BMP images must be uncompressed", input.getFilename());
}
uint8* palette = NULL;
// Create the palette if needed
if (bitCount <= 8) {
// Skip to the palette color count in the header
input.skip(12);
int numColors = input.readUInt32();
palette = (uint8*)System::malloc(numColors * 3);
debugAssert(palette);
// Skip past the end of the header to the palette info
input.skip(4);
int c;
for(c = 0; c < numColors * 3; c += 3) {
// Palette information in bitmaps is stored in BGR_ format.
// That means it's blue-green-red-blank, for each entry.
blue = input.readUInt8();
green = input.readUInt8();
red = input.readUInt8();
blank = input.readUInt8();
palette[c] = red;
palette[c + 1] = green;
palette[c + 2] = blue;
}
}
int hStart = 0;
int hEnd = 0;
int hDir = 0;
if (m_height < 0) {
m_height = -m_height;
hStart = 0;
hEnd = m_height;
hDir = 1;
} else {
//height = height;
hStart = m_height - 1;
hEnd = -1;
hDir = -1;
}
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
debugAssert(m_byte);
int BMScanWidth;
int BMPadding;
uint8 BMGroup;
uint8 BMPixel8;
int currPixel;
int dest;
int flags = PICTURE_NONE;
if (bitCount == 1) {
// Note that this file is not necessarily grayscale, since it's possible
// the palette is blue-and-white, or whatever. But of course most image
// programs only write 1-bit images if they're black-and-white.
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;
// For bitmaps, each scanline is dword-aligned.
BMScanWidth = (m_width + 7) >> 3;
if (BMScanWidth & 3) {
BMScanWidth += 4 - (BMScanWidth & 3);
}
// Powers of 2
int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
for (int h = hStart; h != hEnd; h += hDir) {
currPixel = 0;
dest = 3 * h * m_width;
for (int w = 0; w < BMScanWidth; ++w) {
BMGroup = input.readUInt8();
// Now we read the pixels. Usually there are eight pixels per byte,
// since each pixel is represented by one bit, but if the width
// is not a multiple of eight, the last byte will have some bits
// set, with the others just being extra. Plus there's the
// dword-alignment padding. So we keep checking to see if we've
// already read "width" number of pixels.
for (int i = 7; i >= 0; --i) {
if (currPixel < m_width) {
int src = 3 * ((BMGroup & pow2[i]) >> i);
m_byte[dest] = palette[src];
m_byte[dest + 1] = palette[src + 1];
m_byte[dest + 2] = palette[src + 2];
++currPixel;
dest += 3;
}
}
}
}
} else if (bitCount == 4) {
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;
// For bitmaps, each scanline is dword-aligned.
int BMScanWidth = (m_width + 1) >> 1;
if (BMScanWidth & 3) {
BMScanWidth += 4 - (BMScanWidth & 3);
}
for (int h = hStart; h != hEnd; h += hDir) {
currPixel = 0;
dest = 3 * h * m_width;
for (int w = 0; w < BMScanWidth; w++) {
BMGroup = input.readUInt8();
int src[2];
src[0] = 3 * ((BMGroup & 0xF0) >> 4);
src[1] = 3 * (BMGroup & 0x0F);
// Now we read the pixels. Usually there are two pixels per byte,
// since each pixel is represented by four bits, but if the width
// is not a multiple of two, the last byte will have only four bits
// set, with the others just being extra. Plus there's the
// dword-alignment padding. So we keep checking to see if we've
// already read "Width" number of pixels.
for (int i = 0; i < 2; ++i) {
if (currPixel < m_width) {
int tsrc = src[i];
m_byte[dest] = palette[tsrc];
m_byte[dest + 1] = palette[tsrc + 1];
m_byte[dest + 2] = palette[tsrc + 2];
++currPixel;
dest += 3;
}
}
}
}
} else if (bitCount == 8) {
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;
// For bitmaps, each scanline is dword-aligned.
BMScanWidth = m_width;
if (BMScanWidth & 3) {
BMScanWidth += 4 - (BMScanWidth & 3);
}
for (int h = hStart; h != hEnd; h += hDir) {
currPixel = 0;
for (int w = 0; w < BMScanWidth; ++w) {
BMPixel8 = input.readUInt8();
if (currPixel < m_width) {
dest = 3 * ((h * m_width) + currPixel);
int src = 3 * BMPixel8;
m_byte[dest] = palette[src];
m_byte[dest + 1] = palette[src + 1];
m_byte[dest + 2] = palette[src + 2];
++currPixel;
}
}
}
} else if (bitCount == 16) {
m_memMan->free(m_byte);
m_byte = NULL;
System::free(palette);
palette = NULL;
throw Error("16-bit bitmaps not supported", input.getFilename());
} else if (bitCount == 24) {
input.skip(20);
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;
// For bitmaps, each scanline is dword-aligned.
BMScanWidth = m_width * 3;
if (BMScanWidth & 3) {
BMPadding = 4 - (BMScanWidth & 3);
} else {
BMPadding = 0;
}
for (int h = hStart; h != hEnd; h += hDir) {
dest = 3 * h * m_width;
for (int w = 0; w < m_width; ++w) {
blue = input.readUInt8();
green = input.readUInt8();
red = input.readUInt8();
m_byte[dest] = red;
m_byte[dest + 1] = green;
m_byte[dest + 2] = blue;
dest += 3;
}
if (BMPadding) {
input.skip(2);
}
}
} else if (bitCount == 32) {
m_memMan->free(m_byte);
m_byte = NULL;
System::free(palette);
palette = NULL;
throw Error("32 bit bitmaps not supported", input.getFilename());
} else {
// We support all possible bit depths, so if the
// code gets here, it's not even a real bitmap.
m_memMan->free(m_byte);
m_byte = NULL;
throw Error("Not a bitmap!", input.getFilename());
}
System::free(palette);
palette = NULL;
}
void GImage::decodeICO(
BinaryInput& input) {
// Header
uint16 r = input.readUInt16();
debugAssert(r == 0);
r = input.readUInt16();
debugAssert(r == 1);
// Read the number of icons, although we'll only load the
// first one.
int count = input.readUInt16();
m_channels = 4;
debugAssert(count > 0);
const uint8* headerBuffer = input.getCArray() + input.getPosition();
int maxWidth = 0, maxHeight = 0;
int maxHeaderNum = 0;
for (int currentHeader = 0; currentHeader < count; ++currentHeader) {
const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16);
int tmpWidth = curHeaderBuffer[0];
int tmpHeight = curHeaderBuffer[1];
// Just in case there is a non-square icon, checking area
if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight)) {
maxWidth = tmpWidth;
maxHeight = tmpHeight;
maxHeaderNum = currentHeader;
}
}
input.skip(maxHeaderNum * 16);
m_width = input.readUInt8();
m_height = input.readUInt8();
int numColors = input.readUInt8();
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
debugAssert(m_byte);
// Bit mask for packed bits
int mask = 0;
int bitsPerPixel = 8;
switch (numColors) {
case 2:
mask = 0x01;
bitsPerPixel = 1;
break;
case 16:
mask = 0x0F;
bitsPerPixel = 4;
break;
case 0:
numColors = 256;
mask = 0xFF;
bitsPerPixel = 8;
break;
default:
throw Error("Unsupported ICO color count.", input.getFilename());
}
input.skip(5);
// Skip 'size' unused
input.skip(4);
int offset = input.readUInt32();
// Skip over any other icon descriptions
input.setPosition(offset);
// Skip over bitmap header; it is redundant
input.skip(40);
Array<Color4uint8> palette;
palette.resize(numColors, true);
for (int c = 0; c < numColors; ++c) {
palette[c].b = input.readUInt8();
palette[c].g = input.readUInt8();
palette[c].r = input.readUInt8();
palette[c].a = input.readUInt8();
}
// The actual image and mask follow
// The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap
// using the same encoding as BMP files. The AND Bitmap is stored in as
// 1-bit uncompressed Bitmap.
//
// Pixels are stored bottom-up, left-to-right. Pixel lines are padded
// with zeros to end on a 32bit (4byte) boundary. Every line will have the
// same number of bytes. Color indices are zero based, meaning a pixel color
// of 0 represents the first color table entry, a pixel color of 255 (if there
// are that many) represents the 256th entry.
/*
int bitsPerRow = width * bitsPerPixel;
int bytesPerRow = iCeil((double)bitsPerRow / 8);
// Rows are padded to 32-bit boundaries
bytesPerRow += bytesPerRow % 4;
// Read the XOR values into the color channel
for (int y = height - 1; y >= 0; --y) {
int x = 0;
// Read the row
for (int i = 0; i < bytesPerRow; ++i) {
uint8 byte = input.readUInt8();
for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) {
int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask;
pixel4(x, y) = colorTable[bit];
}
}
}
*/
int hStart = 0;
int hEnd = 0;
int hDir = 0;
if (m_height < 0) {
m_height = -m_height;
hStart = 0;
hEnd = m_height;
hDir = 1;
} else {
//height = height;
hStart = m_height - 1;
hEnd = -1;
hDir = -1;
}
int BMScanWidth;
uint8 BMGroup;
uint8 BMPixel8;
int currPixel;
int dest;
if (bitsPerPixel == 1) {
// Note that this file is not necessarily grayscale, since it's possible
// the palette is blue-and-white, or whatever. But of course most image
// programs only write 1-bit images if they're black-and-white.
// For bitmaps, each scanline is dword-aligned.
BMScanWidth = (m_width + 7) >> 3;
if (BMScanWidth & 3) {
BMScanWidth += 4 - (BMScanWidth & 3);
}
// Powers of 2
int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
for (int h = hStart; h != hEnd; h += hDir) {
currPixel = 0;
dest = 3 * h * m_width;
for (int w = 0; w < BMScanWidth; ++w) {
BMGroup = input.readUInt8();
// Now we read the pixels. Usually there are eight pixels per byte,
// since each pixel is represented by one bit, but if the width
// is not a multiple of eight, the last byte will have some bits
// set, with the others just being extra. Plus there's the
// dword-alignment padding. So we keep checking to see if we've
// already read "width" number of pixels.
for (int i = 7; i >= 0; --i) {
if (currPixel < m_width) {
int src = ((BMGroup & pow2[i]) >> i);
m_byte[dest] = palette[src].r;
m_byte[dest + 1] = palette[src].g;
m_byte[dest + 2] = palette[src].b;
++currPixel;
dest += 4;
}
}
}
}
} else if (bitsPerPixel == 4) {
// For bitmaps, each scanline is dword-aligned.
int BMScanWidth = (m_width + 1) >> 1;
if (BMScanWidth & 3) {
BMScanWidth += 4 - (BMScanWidth & 3);
}
for (int h = hStart; h != hEnd; h += hDir) {
currPixel = 0;
dest = 4 * h * m_width;
for (int w = 0; w < BMScanWidth; w++) {
BMGroup = input.readUInt8();
int src[2];
src[0] = ((BMGroup & 0xF0) >> 4);
src[1] = (BMGroup & 0x0F);
// Now we read the pixels. Usually there are two pixels per byte,
// since each pixel is represented by four bits, but if the width
// is not a multiple of two, the last byte will have only four bits
// set, with the others just being extra. Plus there's the
// dword-alignment padding. So we keep checking to see if we've
// already read "Width" number of pixels.
for (int i = 0; i < 2; ++i) {
if (currPixel < m_width) {
int tsrc = src[i];
m_byte[dest] = palette[tsrc].r;
m_byte[dest + 1] = palette[tsrc].g;
m_byte[dest + 2] = palette[tsrc].b;
++currPixel;
dest += 4;
}
}
}
}
} else if (bitsPerPixel == 8) {
// For bitmaps, each scanline is dword-aligned.
BMScanWidth = m_width;
if (BMScanWidth & 3) {
BMScanWidth += 4 - (BMScanWidth & 3);
}
for (int h = hStart; h != hEnd; h += hDir) {
currPixel = 0;
for (int w = 0; w < BMScanWidth; ++w) {
BMPixel8 = input.readUInt8();
if (currPixel < m_width) {
dest = 4 * ((h * m_width) + currPixel);
int src = BMPixel8;
m_byte[dest] = palette[src].r;
m_byte[dest + 1] = palette[src].g;
m_byte[dest + 2] = palette[src].b;
++currPixel;
}
}
}
}
// Read the mask into the alpha channel
int bitsPerRow = m_width;
int bytesPerRow = iCeil((double)bitsPerRow / 8);
// For bitmaps, each scanline is dword-aligned.
//BMScanWidth = (width + 1) >> 1;
if (bytesPerRow & 3) {
bytesPerRow += 4 - (bytesPerRow & 3);
}
for (int y = m_height - 1; y >= 0; --y) {
int x = 0;
// Read the row
for (int i = 0; i < bytesPerRow; ++i) {
uint8 byte = input.readUInt8();
for (int j = 0; (j < 8) && (x < m_width); ++x, ++j) {
int bit = (byte >> (7 - j)) & 0x01;
pixel4(x, y).a = (1 - bit) * 0xFF;
}
}
}
}
}

View File

@@ -0,0 +1,446 @@
/**
@file GImage_jpeg.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2009-04-20
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include <cstring>
extern "C" {
#ifdef G3D_LINUX
# include <jconfig.h>
# include <jpeglib.h>
#else
# include "jconfig.h"
# include "jpeglib.h"
#endif
}
namespace G3D {
const int jpegQuality = 96;
/**
The IJG library needs special setup for compress/decompressing
from memory. These classes provide them.
The format of this class is defined by the IJG library; do not
change it.
*/
class memory_destination_mgr {
public:
struct jpeg_destination_mgr pub;
JOCTET* buffer;
int size;
int count;
};
typedef memory_destination_mgr* mem_dest_ptr;
/**
Signature dictated by IJG.
*/
static void init_destination (
j_compress_ptr cinfo) {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = dest->size;
dest->count=0;
}
/**
Signature dictated by IJG.
*/
static boolean empty_output_buffer (
j_compress_ptr cinfo) {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = dest->size;
return TRUE;
}
/**
Signature dictated by IJG.
*/
static void term_destination (
j_compress_ptr cinfo) {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->count = dest->size - dest->pub.free_in_buffer;
}
/**
Signature dictated by IJG.
*/
static void jpeg_memory_dest (
j_compress_ptr cinfo,
JOCTET* buffer,
int size) {
mem_dest_ptr dest;
if (cinfo->dest == NULL) {
// First time for this JPEG object; call the
// IJG allocator to get space.
cinfo->dest = (struct jpeg_destination_mgr*)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
JPOOL_PERMANENT,
sizeof(memory_destination_mgr));
}
dest = (mem_dest_ptr) cinfo->dest;
dest->size = size;
dest->buffer = buffer;
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
}
////////////////////////////////////////////////////////////////////////////////////////
#define INPUT_BUF_SIZE 4096
/**
Structure dictated by IJG.
*/
class memory_source_mgr {
public:
struct jpeg_source_mgr pub;
int source_size;
unsigned char* source_data;
boolean start_of_data;
JOCTET* buffer;
};
typedef memory_source_mgr* mem_src_ptr;
/**
Signature dictated by IJG.
*/
static void init_source(
j_decompress_ptr cinfo) {
mem_src_ptr src = (mem_src_ptr) cinfo->src;
src->start_of_data = TRUE;
}
/**
Signature dictated by IJG.
*/
static boolean fill_input_buffer(
j_decompress_ptr cinfo) {
mem_src_ptr src = (mem_src_ptr) cinfo->src;
size_t bytes_read = 0;
if (src->source_size > INPUT_BUF_SIZE)
bytes_read = INPUT_BUF_SIZE;
else
bytes_read = src->source_size;
memcpy (src->buffer, src->source_data, bytes_read);
src->source_data += bytes_read;
src->source_size -= bytes_read;
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = bytes_read;
src->start_of_data = FALSE;
return TRUE;
}
/**
Signature dictated by IJG.
*/
static void skip_input_data(
j_decompress_ptr cinfo,
long num_bytes) {
mem_src_ptr src = (mem_src_ptr)cinfo->src;
if (num_bytes > 0) {
while (num_bytes > (long) src->pub.bytes_in_buffer) {
num_bytes -= (long) src->pub.bytes_in_buffer;
boolean s = fill_input_buffer(cinfo);
debugAssert(s); (void)s;
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
/**
Signature dictated by IJG.
*/
static void term_source (
j_decompress_ptr cinfo) {
(void)cinfo;
// Intentionally empty
}
/**
Signature dictated by IJG.
*/
static void jpeg_memory_src (
j_decompress_ptr cinfo,
JOCTET* buffer,
int size) {
mem_src_ptr src;
if (cinfo->src == NULL) {
// First time for this JPEG object
cinfo->src = (struct jpeg_source_mgr*)
(*cinfo->mem->alloc_small)(
(j_common_ptr) cinfo,
JPOOL_PERMANENT,
sizeof(memory_source_mgr));
src = (mem_src_ptr)cinfo->src;
src->buffer = (JOCTET*)
(*cinfo->mem->alloc_small)(
(j_common_ptr) cinfo,
JPOOL_PERMANENT,
INPUT_BUF_SIZE * sizeof(JOCTET));
}
src = (mem_src_ptr)cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
// use default method
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = term_source;
src->source_data = buffer;
src->source_size = size;
// forces fill_input_buffer on first read
src->pub.bytes_in_buffer = 0;
// until buffer loaded
src->pub.next_input_byte = NULL;
}
void GImage::encodeJPEG(
BinaryOutput& out) const {
if (m_channels != 3) {
// Convert to three channel
GImage tmp = *this;
tmp.convertToRGB();
tmp.encodeJPEG(out);
return;
}
debugAssert(m_channels == 3);
out.setEndian(G3D_LITTLE_ENDIAN);
// Allocate and initialize a compression object
jpeg_compress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
// Specify the destination for the compressed data.
// (Overestimate the size)
int buffer_size = m_width * m_height * 3 + 200;
JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
cinfo.image_width = m_width;
cinfo.image_height = m_height;
// # of color components per pixel
cinfo.input_components = 3;
// colorspace of input image
cinfo.in_color_space = JCS_RGB;
cinfo.input_gamma = 1.0;
// Set parameters for compression, including image size & colorspace
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, jpegQuality, false);
cinfo.smoothing_factor = 0;
cinfo.optimize_coding = TRUE;
// cinfo.dct_method = JDCT_FLOAT;
cinfo.dct_method = JDCT_ISLOW;
cinfo.jpeg_color_space = JCS_YCbCr;
// Initialize the compressor
jpeg_start_compress(&cinfo, TRUE);
// Iterate over all scanlines from top to bottom
// pointer to a single row
JSAMPROW row_pointer[1];
// JSAMPLEs per row in image_buffer
int row_stride = cinfo.image_width * 3;
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &(m_byte[cinfo.next_scanline * row_stride]);
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
// Shut down the compressor
jpeg_finish_compress(&cinfo);
// Figure out how big the result was.
int outLength = ((mem_dest_ptr)cinfo.dest)->count;
// Release the JPEG compression object
jpeg_destroy_compress(&cinfo);
// Copy into an appropriately sized output buffer.
out.writeBytes(compressed_data, outLength);
// Free the conservative buffer.
System::free(compressed_data);
compressed_data = NULL;
}
void GImage::decodeJPEG(
BinaryInput& input) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
int loc = 0;
m_channels = 3;
// We have to set up the error handler, in case initialization fails.
cinfo.err = jpeg_std_error(&jerr);
// Initialize the JPEG decompression object.
jpeg_create_decompress(&cinfo);
// Specify data source (eg, a file, for us, memory)
jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size());
// Read the parameters with jpeg_read_header()
jpeg_read_header(&cinfo, TRUE);
// Set parameters for decompression
// (We do nothing here since the defaults are fine)
// Start decompressor
jpeg_start_decompress(&cinfo);
// Get and set the values of interest to this object
m_width = cinfo.output_width;
m_height = cinfo.output_height;
// Prepare the pointer object for the pixel data
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
// JSAMPLEs per row in output buffer
int bpp = cinfo.output_components;
int row_stride = cinfo.output_width * bpp;
// Make a one-row-high sample array that will go away when done with image
JSAMPARRAY temp = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
// Read data on a scanline by scanline basis
while (cinfo.output_scanline < cinfo.output_height) {
// We may need to adjust the output based on the
// number of channels it has.
switch (bpp) {
case 1:
// Grayscale; decompress to temp.
jpeg_read_scanlines(&cinfo, temp, 1);
// Expand to three channels
{
uint8* scan = &(m_byte[loc * 3]);
uint8* endScan = scan + (m_width * 3);
uint8* t = *temp;
while (scan < endScan) {
uint8 value = t[0];
// Spread the value 3x.
scan[0] = value;
scan[1] = value;
scan[2] = value;
scan += 3;
t += 1;
}
}
break;
case 3:
// Read directly into the array
{
// Need one extra level of indirection.
uint8* scan = m_byte + loc;
JSAMPARRAY ptr = &scan;
jpeg_read_scanlines(&cinfo, ptr, 1);
}
break;
case 4:
// RGBA; decompress to temp.
jpeg_read_scanlines(&cinfo, temp, 1);
// Drop the 3rd channel
{
uint8* scan = &(m_byte[loc * 3]);
uint8* endScan = scan + m_width * 3;
uint8* t = *temp;
while (scan < endScan) {
scan[0] = t[0];
scan[1] = t[1];
scan[2] = t[2];
scan += 3;
t += 4;
}
}
break;
default:
throw Error("Unexpected number of channels.", input.getFilename());
}
loc += row_stride;
}
// Finish decompression
jpeg_finish_decompress(&cinfo);
alwaysAssertM(this, "Corrupt GImage");
// Release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
}
}

View File

@@ -0,0 +1,276 @@
/**
@file GImage_png.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2009-04-20
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Log.h"
#include <png.h>
namespace G3D {
//libpng required function signature
static void png_read_data(
png_structp png_ptr,
png_bytep data,
png_size_t length) {
debugAssert( png_ptr->io_ptr != NULL );
debugAssert( length >= 0 );
debugAssert( data != NULL );
((BinaryInput*)png_ptr->io_ptr)->readBytes(data, length);
}
//libpng required function signature
static void png_write_data(png_structp png_ptr,
png_bytep data,
png_size_t length) {
debugAssert( png_ptr->io_ptr != NULL );
debugAssert( data != NULL );
((BinaryOutput*)png_ptr->io_ptr)->writeBytes(data, length);
}
//libpng required function signature
static void png_flush_data(
png_structp png_ptr) {
(void)png_ptr;
//Do nothing.
}
//libpng required function signature
static void png_error(
png_structp png_ptr,
png_const_charp error_msg) {
(void)png_ptr;
debugAssert( error_msg != NULL );
throw GImage::Error(error_msg, "PNG");
}
//libpng required function signature
void png_warning(
png_structp png_ptr,
png_const_charp warning_msg) {
(void)png_ptr;
debugAssert( warning_msg != NULL );
Log::common()->println(warning_msg);
}
void GImage::encodePNG(
BinaryOutput& out) const {
if (! (m_channels == 1 || m_channels == 3 || m_channels == 4)) {
throw GImage::Error(format("Illegal channels for PNG: %d", m_channels), out.getFilename());
}
if (m_width <= 0) {
throw GImage::Error(format("Illegal width for PNG: %d", m_width), out.getFilename());
}
if (m_height <= 0) {
throw GImage::Error(format("Illegal height for PNG: %d", m_height), out.getFilename());
}
// PNG library requires that the height * pointer size fit within an int
if (png_uint_32(m_height) * png_sizeof(png_bytep) > PNG_UINT_32_MAX) {
throw GImage::Error("Unsupported PNG height.", out.getFilename());
}
out.setEndian(G3D_LITTLE_ENDIAN);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
if (! png_ptr) {
throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename());
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (! info_ptr) {
png_destroy_write_struct(&png_ptr, &info_ptr);
throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename());
}
//setup libpng write handler so can use BinaryOutput
png_set_write_fn(png_ptr, (void*)&out, png_write_data, png_flush_data);
png_color_8_struct sig_bit;
switch (m_channels) {
case 1:
png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_GRAY,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
sig_bit.red = 0;
sig_bit.green = 0;
sig_bit.blue = 0;
sig_bit.alpha = 0;
sig_bit.gray = 8;
break;
case 3:
png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
sig_bit.alpha = 0;
sig_bit.gray = 0;
break;
case 4:
png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
sig_bit.alpha = 8;
sig_bit.gray = 0;
break;
default:
png_destroy_write_struct(&png_ptr, &info_ptr);
throw GImage::Error("Unsupported number of channels for PNG.", out.getFilename());
}
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
//write the png header
png_write_info(png_ptr, info_ptr);
png_bytepp row_pointers = new png_bytep[m_height];
for (int i=0; i < m_height; ++i) {
row_pointers[i] = (png_bytep)&m_byte[m_width * m_channels * i];
}
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
delete[] row_pointers;
png_destroy_write_struct(&png_ptr, &info_ptr);
}
void GImage::decodePNG(
BinaryInput& input) {
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
if (png_ptr == NULL) {
throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
}
png_infop end_info = png_create_info_struct(png_ptr);
if (end_info == NULL) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
}
// now that the libpng structures are setup, change the error handlers and read routines
// to use G3D functions so that BinaryInput can be used.
png_set_read_fn(png_ptr, (png_voidp)&input, png_read_data);
// read in sequentially so that three copies of the file are not in memory at once
png_read_info(png_ptr, info_ptr);
png_uint_32 png_width, png_height;
int bit_depth, color_type, interlace_type;
// this will validate the data it extracts from info_ptr
png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
throw GImage::Error("Unsupported PNG color type - PNG_COLOR_TYPE_GRAY_ALPHA.", input.getFilename());
}
m_width = static_cast<uint32>(png_width);
m_height = static_cast<uint32>(png_height);
//swap bytes of 16 bit files to least significant byte first
png_set_swap(png_ptr);
png_set_strip_16(png_ptr);
//Expand paletted colors into true RGB triplets
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
}
//Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
png_set_expand(png_ptr);
}
//Expand paletted or RGB images with transparency to full alpha channels
//so the data will be available as RGBA quartets.
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png_ptr);
}
// Fix sub-8 bit_depth to 8bit
if (bit_depth < 8) {
png_set_packing(png_ptr);
}
if ((color_type == PNG_COLOR_TYPE_RGBA) ||
((color_type == PNG_COLOR_TYPE_PALETTE) && (png_ptr->num_trans > 0)) ) {
m_channels = 4;
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 4);
} else if ((color_type == PNG_COLOR_TYPE_RGB) ||
(color_type == PNG_COLOR_TYPE_PALETTE)) {
m_channels = 3;
m_byte = (uint8*)System::malloc(m_width * m_height * 3);
} else if (color_type == PNG_COLOR_TYPE_GRAY) {
m_channels = 1;
// Round up to the nearest 8 rows to avoid a bug in the PNG decoder
int h = iCeil(m_height / 8) * 8;
int sz = m_width * h;
m_byte = (uint8*)m_memMan->alloc(sz);
} else {
throw GImage::Error("Unsupported PNG bit-depth or type.", input.getFilename());
}
//since we are reading row by row, required to handle interlacing
uint32 number_passes = png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
for (uint32 pass = 0; pass < number_passes; ++pass) {
for (uint32 y = 0; y < (uint32)m_height; ++y) {
png_bytep rowPointer = &m_byte[m_width * m_channels * y];
png_read_rows(png_ptr, &rowPointer, NULL, 1);
}
}
// png_read_image(png_ptr, &_byte);
png_read_end(png_ptr, info_ptr);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
}
}

View File

@@ -0,0 +1,217 @@
/**
@file GImage_ppm.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2006-05-10
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#include "G3D/Log.h"
namespace G3D {
void GImage::encodePPMASCII(
BinaryOutput& out) const {
TextOutput::Settings ppmOptions;
ppmOptions.convertNewlines = false;
ppmOptions.numColumns = 70;
ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
TextOutput ppm(ppmOptions);
switch (m_channels) {
case 1:
{
ppm.printf("P2\n%d %d\n255\n", m_width, m_height);
const Color1uint8* c = this->pixel1();
// Insert newlines every 70 characters max
for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
ppm.printf("%d%c", c[i].value, (i % (70/4) == 0) ? '\n' : ' ');
}
}
break;
case 3:
{
ppm.printf("P3\n%d %d\n255\n", m_width, m_height);
const Color3uint8* c = this->pixel3();
// Insert newlines every 70 characters max
for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b,
(i % (70/12) == 0) ?
'\n' : ' ');
}
}
break;
default:
alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
}
const std::string& s = ppm.commitString();
out.writeBytes(s.c_str(), s.length());
}
void GImage::encodePPM(
BinaryOutput& out) const {
// http://netpbm.sourceforge.net/doc/ppm.html
if (m_channels == 3) {
std::string header = format("P6 %d %d 255 ", m_width, m_height);
out.writeBytes(header.c_str(), header.size());
out.writeBytes(this->pixel3(), m_width * m_height * 3);
} else if (m_channels == 1) {
std::string header = format("P5 %d %d 255 ", m_width, m_height);
out.writeBytes(header.c_str(), header.size());
out.writeBytes(this->pixel1(), m_width * m_height);
} else {
alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
}
}
void GImage::decodePPMASCII(
BinaryInput& input) {
int ppmWidth;
int ppmHeight;
double maxColor;
// Create a TextInput object to parse ascii format
// Mixed binary/ascii formats will require more
const std::string inputStr = input.readString();
TextInput::Settings ppmOptions;
ppmOptions.cppLineComments = false;
ppmOptions.otherCommentCharacter = '#';
ppmOptions.signedNumbers = true;
ppmOptions.singleQuotedStrings = false;
TextInput ppmInput(TextInput::FROM_STRING, inputStr, ppmOptions);
//Skip first line in header P#
std::string ppmType = ppmInput.readSymbol();
ppmWidth = (int)ppmInput.readNumber();
ppmHeight = (int)ppmInput.readNumber();
// Everything but a PBM will have a max color value
if (ppmType != "P2") {
maxColor = ppmInput.readNumber();
} else {
maxColor = 255;
}
if ((ppmWidth < 0) ||
(ppmHeight < 0) ||
(maxColor <= 0)) {
throw GImage::Error("Invalid PPM Header.", input.getFilename());
}
// I don't think it's proper to scale values less than 255
if (maxColor <= 255.0) {
maxColor = 255.0;
}
m_width = ppmWidth;
m_height = ppmHeight;
m_channels = 3;
// always scale down to 1 byte per channel
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
// Read in the image data. I am not validating if the values match the maxColor
// requirements. I only scale if needed to fit within the byte available.
for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
// read in color and scale to max pixel defined in header
// A max color less than 255 might need to be left alone and not scaled.
Color3uint8& curPixel = *(pixel3() + i);
if (ppmType == "P3") {
curPixel.r = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
curPixel.g = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
curPixel.b = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
} else if (ppmType == "P2") {
uint8 pixel = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
curPixel.r = pixel;
curPixel.g = pixel;
curPixel.b = pixel;
} else if (ppmType == "P1") {
int pixel = (uint8)(ppmInput.readNumber() * maxColor);
curPixel.r = pixel;
curPixel.g = pixel;
curPixel.b = pixel;
}
}
}
/** Consumes whitespace up to and including a number, but not the following character */
static int scanUInt(BinaryInput& input) {
char c = input.readUInt8();
while (isWhiteSpace(c)) {
c = input.readUInt8();
}
std::string s;
s += c;
c = input.readUInt8();
while (!isWhiteSpace(c)) {
s += c;
c = input.readUInt8();
}
// Back up one to avoid consuming the last character
input.setPosition(input.getPosition() - 1);
int x;
sscanf(s.c_str(), "%d", &x);
return x;
}
void GImage::decodePPM(
BinaryInput& input) {
char head[2];
int w, h;
input.readBytes(head, 2);
if (head[0] != 'P' || ((head[1] != '6') && (head[1] != '5'))) {
throw GImage::Error("Invalid PPM Header.", input.getFilename());
}
w = scanUInt(input);
h = scanUInt(input);
// Skip the max color specifier
scanUInt(input);
if ((w < 0) ||
(h < 0) ||
(w > 100000) ||
(h > 100000)) {
throw GImage::Error("Invalid PPM size in header.", input.getFilename());
}
// Trailing whitespace
input.readUInt8();
if (head[1] == '6') {
// 3 channel
resize(w, h, 3);
input.readBytes(m_byte, m_width * m_height * 3);
} else if (head[1] == '5') {
// 1 channel
resize(w, h, 1);
input.readBytes(m_byte, m_width * m_height);
}
}
}

View File

@@ -0,0 +1,236 @@
/**
@file GImage_tga.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2009-05-10
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Log.h"
namespace G3D {
void GImage::encodeTGA(
BinaryOutput& out) const {
out.setEndian(G3D_LITTLE_ENDIAN);
// ID length
out.writeUInt8(0);
// Color map Type
out.writeUInt8(0);
// Type
out.writeUInt8(2);
// Color map
out.skip(5);
// x, y offsets
out.writeUInt16(0);
out.writeUInt16(0);
// Width & height
out.writeUInt16(m_width);
out.writeUInt16(m_height);
// Color depth
if (m_channels == 1) {
// Force RGB mode
out.writeUInt8(8 * 3);
} else {
out.writeUInt8(8 * m_channels);
}
// Image descriptor
if (m_channels < 4) {
// 0 alpha bits
out.writeUInt8(0);
} else {
// 8 alpha bits
out.writeUInt8(8);
}
// Image ID (zero length)
if (m_channels == 1) {
// Pixels are upside down in BGR format.
for (int y = m_height - 1; y >= 0; --y) {
for (int x = 0; x < m_width; ++x) {
uint8 p = (m_byte[(y * m_width + x)]);
out.writeUInt8(p);
out.writeUInt8(p);
out.writeUInt8(p);
}
}
} else if (m_channels == 3) {
// Pixels are upside down in BGR format.
for (int y = m_height - 1; y >= 0; --y) {
for (int x = 0; x < m_width; ++x) {
uint8* p = &(m_byte[3 * (y * m_width + x)]);
out.writeUInt8(p[2]);
out.writeUInt8(p[1]);
out.writeUInt8(p[0]);
}
}
} else {
// Pixels are upside down in BGRA format.
for (int y = m_height - 1; y >= 0; --y) {
for (int x = 0; x < m_width; ++x) {
uint8* p = &(m_byte[4 * (y * m_width + x)]);
out.writeUInt8(p[2]);
out.writeUInt8(p[1]);
out.writeUInt8(p[0]);
out.writeUInt8(p[3]);
}
}
}
// Write "TRUEVISION-XFILE " 18 bytes from the end
// (with null termination)
out.writeString("TRUEVISION-XFILE ");
}
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.getFilename());
}
input.setPosition(pos);
int IDLength = input.readUInt8();
int colorMapType = input.readUInt8();
int imageType = input.readUInt8();
(void)colorMapType;
// 2 is the type supported by this routine.
if (imageType != 2 && imageType != 10) {
throw Error("TGA images must be type 2 (Uncompressed truecolor) or 10 (Run-length truecolor)", input.getFilename());
}
// Color map specification
input.skip(5);
// Image specification
// Skip x and y offsets
input.skip(4);
m_width = input.readInt16();
m_height = input.readInt16();
int colorDepth = input.readUInt8();
if ((colorDepth != 24) && (colorDepth != 32)) {
throw Error("TGA files must be 24 or 32 bit.", input.getFilename());
}
if (colorDepth == 32) {
m_channels = 4;
} else {
m_channels = 3;
}
// Image descriptor contains overlay data as well
// as data indicating where the origin is
int imageDescriptor = input.readUInt8();
(void)imageDescriptor;
// Image ID
input.skip(IDLength);
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
debugAssert(m_byte);
// Pixel data
int x;
int y;
if (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");
}
}
}

View File

@@ -0,0 +1,275 @@
/**
@file GLight.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-11-12
@edited 2009-11-16
*/
#include "G3D/GLight.h"
#include "G3D/Sphere.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
GLight::GLight(const Any& any) {
any.verifyName("GLight");
if (any.type() == Any::TABLE) {
*this = GLight();
Vector3 spotTarget;
bool hasSpotTarget = false;
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& key = toLower(it->key);
if (key == "position") {
position = it->value;
} else if (key == "rightdirection") {
rightDirection = it->value;
} else if (key == "spotdirection") {
spotDirection = Vector3(it->value).directionOrZero();
} else if (key == "spottarget") {
spotTarget = it->value;
hasSpotTarget = true;
} else if (key == "spotcutoff") {
spotCutoff = it->value.number();
} else if (key == "spotsquare") {
spotSquare = it->value.boolean();
} else if (key == "attenuation") {
attenuation[0] = it->value[0].number();
attenuation[1] = it->value[1].number();
attenuation[2] = it->value[2].number();
} else if (key == "color") {
color = it->value;
} else if (key == "enabled") {
enabled = it->value.boolean();
} else if (key == "specular") {
specular = it->value.boolean();
} else if (key == "diffuse") {
diffuse = it->value.boolean();
} else {
any.verify(false, "Illegal key: " + it->key);
}
}
if (hasSpotTarget) {
spotDirection = (spotTarget - position.xyz()).direction();
}
} else if (toLower(any.name()) == "glight::directional") {
*this = directional(any[0], any[1],
(any.size() > 2) ? any[2] : Any(true),
(any.size() > 3) ? any[3] : Any(true));
} else if (toLower(any.name()) == "glight::point") {
*this = point(any[0], any[1],
(any.size() > 2) ? any[2] : Any(1),
(any.size() > 3) ? any[3] : Any(0),
(any.size() > 4) ? any[4] : Any(0.5f),
(any.size() > 5) ? any[5] : Any(true),
(any.size() > 6) ? any[6] : Any(true));
} else if (toLower(any.name()) == "glight::spot") {
*this = spot(any[0], any[1], any[2], any[3],
(any.size() > 4) ? any[4] : Any(1),
(any.size() > 5) ? any[5] : Any(0),
(any.size() > 6) ? any[6] : Any(0),
(any.size() > 7) ? any[7] : Any(true),
(any.size() > 8) ? any[8] : Any(true));
} else {
any.verify(false, "Unrecognized name");
}
}
GLight::operator Any() const {
Any a(Any::TABLE, "GLight");
a.set("position", position.operator Any());
a.set("rightDirection", rightDirection.operator Any());
a.set("spotDirection", spotDirection.operator Any());
a.set("spotCutoff", spotCutoff);
a.set("spotSquare", spotSquare);
Any att(Any::ARRAY);
att.append(attenuation[0], attenuation[1], attenuation[2]);
a.set("attenuation", att);
a.set("color", color.operator Any());
a.set("enabled", enabled);
a.set("specular", specular);
a.set("diffuse", diffuse);
return a;
}
GLight::GLight() :
position(0, 0, 0, 0),
rightDirection(0,0,0),
spotDirection(0, 0, -1),
spotCutoff(180),
spotSquare(false),
color(Color3::white()),
enabled(false),
specular(true),
diffuse(true) {
attenuation[0] = 1.0;
attenuation[1] = 0.0;
attenuation[2] = 0.0;
}
GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d) {
GLight L;
L.position = Vector4(toLight.direction(), 0);
L.color = color;
L.specular = s;
L.diffuse = d;
return L;
}
GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
GLight L;
L.position = Vector4(pos, 1);
L.color = color;
L.attenuation[0] = constAtt;
L.attenuation[1] = linAtt;
L.attenuation[2] = quadAtt;
L.specular = s;
L.diffuse = d;
return L;
}
GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
GLight L;
L.position = Vector4(pos, 1.0f);
L.spotDirection = pointDirection.direction();
debugAssert(cutOffAngleDegrees <= 90);
L.spotCutoff = cutOffAngleDegrees;
L.color = color;
L.attenuation[0] = constAtt;
L.attenuation[1] = linAtt;
L.attenuation[2] = quadAtt;
L.specular = s;
L.diffuse = d;
return L;
}
bool GLight::operator==(const GLight& other) const {
return (position == other.position) &&
(rightDirection == other.rightDirection) &&
(spotDirection == other.spotDirection) &&
(spotCutoff == other.spotCutoff) &&
(spotSquare == other.spotSquare) &&
(attenuation[0] == other.attenuation[0]) &&
(attenuation[1] == other.attenuation[1]) &&
(attenuation[2] == other.attenuation[2]) &&
(color == other.color) &&
(enabled == other.enabled) &&
(specular == other.specular) &&
(diffuse == other.diffuse);
}
bool GLight::operator!=(const GLight& other) const {
return !(*this == other);
}
Sphere GLight::effectSphere(float cutoff) const {
if (position.w == 0) {
// Directional light
return Sphere(Vector3::zero(), finf());
} else {
// Avoid divide by zero
cutoff = max(cutoff, 0.00001f);
float maxIntensity = max(color.r, max(color.g, color.b));
float radius = finf();
if (attenuation[2] != 0) {
// Solve I / attenuation.dot(1, r, r^2) < cutoff for r
//
// a[0] + a[1] r + a[2] r^2 > I/cutoff
//
float a = attenuation[2];
float b = attenuation[1];
float c = attenuation[0] - maxIntensity / cutoff;
float discrim = square(b) - 4 * a * c;
if (discrim >= 0) {
discrim = sqrt(discrim);
float r1 = (-b + discrim) / (2 * a);
float r2 = (-b - discrim) / (2 * a);
if (r1 < 0) {
if (r2 > 0) {
radius = r2;
}
} else if (r2 > 0) {
radius = min(r1, r2);
} else {
radius = r1;
}
}
} else if (attenuation[1] != 0) {
// Solve I / attenuation.dot(1, r) < cutoff for r
//
// r * a[1] + a[0] = I / cutoff
// r = (I / cutoff - a[0]) / a[1]
float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1];
radius = max(radius, 0.0f);
}
return Sphere(position.xyz(), radius);
}
}
CoordinateFrame GLight::frame() const {
CoordinateFrame f;
if (rightDirection == Vector3::zero()) {
// No specified right direction; choose one automatically
if (position.w == 0) {
// Directional light
f.lookAt(-position.xyz());
} else {
// Spot light
f.lookAt(spotDirection);
}
} else {
const Vector3& Z = -spotDirection.direction();
Vector3 X = rightDirection.direction();
// Ensure the vectors are not too close together
while (abs(X.dot(Z)) > 0.9f) {
X = Vector3::random();
}
// Ensure perpendicular
X -= Z * Z.dot(X);
const Vector3& Y = Z.cross(X);
f.rotation.setColumn(Vector3::X_AXIS, X);
f.rotation.setColumn(Vector3::Y_AXIS, Y);
f.rotation.setColumn(Vector3::Z_AXIS, Z);
}
f.translation = position.xyz();
return f;
}
} // G3D

View File

@@ -0,0 +1,229 @@
/**
@file GThread.cpp
GThread class.
@created 2005-09-24
@edited 2005-10-22
*/
#include "G3D/GThread.h"
#include "G3D/System.h"
#include "G3D/debugAssert.h"
#include "G3D/GMutex.h"
namespace G3D {
namespace _internal {
class BasicThread: public GThread {
public:
BasicThread(const std::string& name, void (*proc)(void*), void* param):
GThread(name), m_wrapperProc(proc), m_param(param) { }
protected:
virtual void threadMain() {
m_wrapperProc(m_param);
}
private:
void (*m_wrapperProc)(void*);
void* m_param;
};
} // namespace _internal
GThread::GThread(const std::string& name):
m_status(STATUS_CREATED),
m_name(name) {
#ifdef G3D_WIN32
m_event = NULL;
#endif
// system-independent clear of handle
System::memset(&m_handle, 0, sizeof(m_handle));
}
GThread::~GThread() {
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning( disable : 4127 )
#endif
alwaysAssertM(m_status != STATUS_RUNNING, "Deleting thread while running.");
#ifdef _MSC_VER
# pragma warning( pop )
#endif
#ifdef G3D_WIN32
if (m_event) {
::CloseHandle(m_event);
}
#endif
}
GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) {
return new _internal::BasicThread(name, proc, param);
}
bool GThread::started() const {
return m_status != STATUS_CREATED;
}
bool GThread::start(SpawnBehavior behavior) {
debugAssertM(! started(), "Thread has already executed.");
if (started()) {
return false;
}
m_status = STATUS_STARTED;
if (behavior == USE_CURRENT_THREAD) {
// Run on this thread
m_status = STATUS_RUNNING;
threadMain();
m_status = STATUS_COMPLETED;
return true;
}
# ifdef G3D_WIN32
DWORD threadId;
m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
debugAssert(m_event);
m_handle = ::CreateThread(NULL, 0, &internalThreadProc, this, 0, &threadId);
if (m_handle == NULL) {
::CloseHandle(m_event);
m_event = NULL;
}
return (m_handle != NULL);
# else
if (!pthread_create(&m_handle, NULL, &internalThreadProc, this)) {
return true;
} else {
// system-independent clear of handle
System::memset(&m_handle, 0, sizeof(m_handle));
return false;
}
# endif
}
void GThread::terminate() {
if (m_handle) {
# ifdef G3D_WIN32
::TerminateThread(m_handle, 0);
# else
pthread_kill(m_handle, SIGSTOP);
# endif
// system-independent clear of handle
System::memset(&m_handle, 0, sizeof(m_handle));
}
}
bool GThread::running() const{
return (m_status == STATUS_RUNNING);
}
bool GThread::completed() const {
return (m_status == STATUS_COMPLETED);
}
void GThread::waitForCompletion() {
if (m_status == STATUS_COMPLETED) {
// Must be done
return;
}
# ifdef G3D_WIN32
debugAssert(m_event);
::WaitForSingleObject(m_event, INFINITE);
# else
debugAssert(m_handle);
pthread_join(m_handle, NULL);
# endif
}
#ifdef G3D_WIN32
DWORD WINAPI GThread::internalThreadProc(LPVOID param) {
GThread* current = reinterpret_cast<GThread*>(param);
debugAssert(current->m_event);
current->m_status = STATUS_RUNNING;
current->threadMain();
current->m_status = STATUS_COMPLETED;
::SetEvent(current->m_event);
return 0;
}
#else
void* GThread::internalThreadProc(void* param) {
GThread* current = reinterpret_cast<GThread*>(param);
current->m_status = STATUS_RUNNING;
current->threadMain();
current->m_status = STATUS_COMPLETED;
return (void*)NULL;
}
#endif
//GMutex implementation
GMutex::GMutex() {
#ifdef G3D_WIN32
::InitializeCriticalSection(&m_handle);
#else
int ret = pthread_mutexattr_init(&m_attr);
debugAssert(ret == 0);
ret = pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);
debugAssert(ret == 0);
ret = pthread_mutex_init(&m_handle, &m_attr);
debugAssert(ret == 0);
#endif
}
GMutex::~GMutex() {
//TODO: Debug check for locked
#ifdef G3D_WIN32
::DeleteCriticalSection(&m_handle);
#else
int ret = pthread_mutex_destroy(&m_handle);
debugAssert(ret == 0);
ret = pthread_mutexattr_destroy(&m_attr);
debugAssert(ret == 0);
#endif
}
bool GMutex::tryLock() {
#ifdef G3D_WIN32
return (::TryEnterCriticalSection(&m_handle) != 0);
#else
return (pthread_mutex_trylock(&m_handle) == 0);
#endif
}
void GMutex::lock() {
#ifdef G3D_WIN32
::EnterCriticalSection(&m_handle);
#else
pthread_mutex_lock(&m_handle);
#endif
}
void GMutex::unlock() {
#ifdef G3D_WIN32
::LeaveCriticalSection(&m_handle);
#else
pthread_mutex_unlock(&m_handle);
#endif
}
} // namespace G3D

View File

@@ -0,0 +1,78 @@
/**
@file GUniqueID.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
*/
#include "G3D/GUniqueID.h"
#include "G3D/BinaryInput.h"
#include "G3D/TextInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/TextOutput.h"
#include "G3D/NetworkDevice.h"
namespace G3D {
void GUniqueID::serialize(BinaryOutput& b) const {
b.writeUInt64(id);
}
void GUniqueID::deserialize(BinaryInput& b) {
id = b.readUInt64();
}
void GUniqueID::serialize(TextOutput& t) const {
t.writeSymbol("(");
t.writeNumber((double)(id >> 32));
t.writeNumber((double)(id & 0xFFFFFFFF));
t.writeSymbol(")");
}
void GUniqueID::deserialize(TextInput& t) {
t.readSymbol("(");
id = (((uint64)t.readNumber()) << 32) + (uint64)t.readNumber();
t.readSymbol(")");
}
GUniqueID GUniqueID::create(uint16 tag) {
static uint64 counter = 0;
static uint64 systemID = 0;
if (systemID == 0) {
// Create a unique ID for this machine/program instance
// TODO: see ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr)
Array<NetAddress> addr;
NetworkDevice::instance()->localHostAddresses(addr);
if (addr.size() > 0) {
systemID |= addr[0].ip();
}
union {
float64 ft;
uint64 ut;
};
ft = System::time();
systemID = ut << 22;
systemID ^= ((uint64)iRandom(0, 32768)) << 8;
systemID &= ~((uint64)1023 << 54);
// Ensure that the systemID is non-zero (vanishingly small probability)
if (systemID == 0) {
systemID = 1;
}
}
// No need for modulo; we'll all be dead before this counter
// overflows 54 bits
++counter;
GUniqueID i;
i.id = (((uint64)(tag & 1023)) << 54) | (counter ^ systemID);
return i;
}
} // G3D

View File

@@ -0,0 +1,224 @@
/**
@file Image1.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-31
@edited 2007-01-31
*/
#include "G3D/Image1.h"
#include "G3D/Image1uint8.h"
#include "G3D/GImage.h"
#include "G3D/Color4.h"
#include "G3D/Color4uint8.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/ImageFormat.h"
namespace G3D {
Image1::Image1(int w, int h, WrapMode wrap) : Map2D<Color1, Color1>(w, h, wrap) {
setAll(Color1(0.0f));
}
Image1::Ref Image1::fromGImage(const GImage& im, WrapMode wrap) {
switch (im.channels()) {
case 1:
return fromArray(im.pixel1(), im.width(), im.height(), wrap);
case 3:
return fromArray(im.pixel3(), im.width(), im.height(), wrap);
case 4:
return fromArray(im.pixel4(), im.width(), im.height(), wrap);
default:
debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
return NULL;
}
}
Image1::Ref Image1::fromImage1uint8(const ReferenceCountedPointer<Image1uint8>& im) {
Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
out->resize(im->width(), im->height());
int N = im->width() * im->height();
const Color1uint8* src = reinterpret_cast<Color1uint8*>(im->getCArray());
for (int i = 0; i < N; ++i) {
out->data[i] = Color1(src[i]);
}
return out;
}
Image1::Ref Image1::createEmpty(int width, int height, WrapMode wrap) {
return new Type(width, height, wrap);
}
Image1::Ref Image1::createEmpty(WrapMode wrap) {
return createEmpty(0, 0, wrap);
}
Image1::Ref Image1::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
Ref out = createEmpty(wrap);
out->load(filename, fmt);
return out;
}
void Image1::load(const std::string& filename, GImage::Format fmt) {
copyGImage(GImage(filename, fmt));
setChanged(true);
}
Image1::Ref Image1::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1::Ref Image1::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1::Ref Image1::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1::Ref Image1::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1::Ref Image1::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1::Ref Image1::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
void Image1::copyGImage(const GImage& im) {
switch (im.channels()) {
case 1:
copyArray(im.pixel1(), im.width(), im.height());
break;
case 3:
copyArray(im.pixel3(), im.width(), im.height());
break;
case 4:
copyArray(im.pixel4(), im.width(), im.height());
break;
}
}
void Image1::copyArray(const Color3uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1* dst = data.getCArray();
// Convert int8 -> float
for (int i = 0; i < N; ++i) {
dst[i] = Color1(Color3(src[i]).average());
}
}
void Image1::copyArray(const Color4uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1* dst = data.getCArray();
// Strip alpha and convert
for (int i = 0; i < N; ++i) {
dst[i] = Color1(Color3(src[i].rgb()).average());
}
}
void Image1::copyArray(const Color1* src, int w, int h) {
resize(w, h);
System::memcpy(getCArray(), src, w * h * sizeof(Color1));
}
void Image1::copyArray(const Color4* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1* dst = data.getCArray();
// Strip alpha
for (int i = 0; i < N; ++i) {
dst[i] = Color1(src[i].rgb().average());
}
}
void Image1::copyArray(const Color1uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i]= Color1(src[i]);
}
}
void Image1::copyArray(const Color3* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color1(src[i].average());
}
}
/** Saves in any of the formats supported by G3D::GImage. */
void Image1::save(const std::string& filename, GImage::Format fmt) {
GImage im(width(), height(), 1);
int N = im.width() * im.height();
Color1uint8* dst = im.pixel1();
for (int i = 0; i < N; ++i) {
dst[i] = Color1uint8(data[i]);
}
im.save(filename, fmt);
}
const ImageFormat* Image1::format() const {
return ImageFormat::L32F();
}
} // G3D

View File

@@ -0,0 +1,212 @@
/**
@file Image1uint8.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-31
@edited 2008-01-13
*/
#include "G3D/Image1uint8.h"
#include "G3D/Image3uint8.h"
#include "G3D/Image1.h"
#include "G3D/GImage.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/Color4.h"
#include "G3D/Color4uint8.h"
#include "G3D/ImageFormat.h"
namespace G3D {
Image1uint8::Image1uint8(int w, int h, WrapMode wrap) : Map2D<Color1uint8, Color1>(w, h, wrap) {
setAll(Color1uint8(0));
}
Image1uint8::Ref Image1uint8::fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im) {
return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
}
Image1uint8::Ref Image1uint8::fromGImage(const GImage& im, WrapMode wrap) {
switch (im.channels()) {
case 1:
return fromArray(im.pixel1(), im.width(), im.height(), wrap);
case 3:
return fromArray(im.pixel3(), im.width(), im.height(), wrap);
case 4:
return fromArray(im.pixel4(), im.width(), im.height(), wrap);
default:
debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
return NULL;
}
}
Image1uint8::Ref Image1uint8::fromImage1(const ReferenceCountedPointer<Image1>& im) {
Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
out->copyArray(im->getCArray(), im->width(), im->height());
return out;
}
Image1uint8::Ref Image1uint8::createEmpty(int width, int height, WrapMode wrap) {
return new Type(width, height, wrap);
}
Image1uint8::Ref Image1uint8::createEmpty(WrapMode wrap) {
return createEmpty(0, 0, wrap);
}
Image1uint8::Ref Image1uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
Ref out = createEmpty(wrap);
out->load(filename, fmt);
return out;
}
Image1uint8::Ref Image1uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1uint8::Ref Image1uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1uint8::Ref Image1uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1uint8::Ref Image1uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1uint8::Ref Image1uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image1uint8::Ref Image1uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
void Image1uint8::load(const std::string& filename, GImage::Format fmt) {
copyGImage(GImage(filename, fmt));
setChanged(true);
}
void Image1uint8::copyGImage(const GImage& im) {
switch (im.channels()) {
case 1:
copyArray(im.pixel1(), im.width(), im.height());
break;
case 3:
copyArray(im.pixel3(), im.width(), im.height());
break;
case 4:
copyArray(im.pixel4(), im.width(), im.height());
break;
}
}
void Image1uint8::copyArray(const Color3uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].value = (src[i].r + src[i].g + src[i].b) / 3;
}
}
void Image1uint8::copyArray(const Color3* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color1uint8(Color1(src[i].average()));
}
}
void Image1uint8::copyArray(const Color1uint8* ptr, int w, int h) {
resize(w, h);
System::memcpy(getCArray(), ptr, w * h);
}
void Image1uint8::copyArray(const Color1* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color1uint8(src[i]);
}
}
void Image1uint8::copyArray(const Color4uint8* ptr, int w, int h) {
resize(w, h);
int N = w * h;
Color1uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].value = (ptr[i].r + ptr[i].g + ptr[i].b) / 3;
}
}
void Image1uint8::copyArray(const Color4* src, int w, int h) {
resize(w, h);
int N = w * h;
Color1uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color1uint8(Color1(src[i].rgb().average()));
}
}
/** Saves in any of the formats supported by G3D::GImage. */
void Image1uint8::save(const std::string& filename, GImage::Format fmt) {
GImage im(width(), height(), 1);
System::memcpy(im.byte(), getCArray(), width() * height());
im.save(filename, fmt);
}
const ImageFormat* Image1uint8::format() const {
return ImageFormat::L8();
}
} // G3D

View File

@@ -0,0 +1,225 @@
/**
@file Image3.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-31
@edited 2007-01-31
*/
#include "G3D/Image3.h"
#include "G3D/Image3uint8.h"
#include "G3D/GImage.h"
#include "G3D/Color4.h"
#include "G3D/Color4uint8.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/ImageFormat.h"
namespace G3D {
Image3::Image3(int w, int h, WrapMode wrap) : Map2D<Color3, Color3>(w, h, wrap) {
setAll(Color3::black());
}
Image3::Ref Image3::fromGImage(const GImage& im, WrapMode wrap) {
switch (im.channels()) {
case 1:
return fromArray(im.pixel1(), im.width(), im.height(), wrap);
case 3:
return fromArray(im.pixel3(), im.width(), im.height(), wrap);
case 4:
return fromArray(im.pixel4(), im.width(), im.height(), wrap);
default:
debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
return NULL;
}
}
Image3::Ref Image3::fromImage3uint8(const ReferenceCountedPointer<Image3uint8>& im) {
Ref out = createEmpty(im->wrapMode());
out->resize(im->width(), im->height());
int N = im->width() * im->height();
const Color3uint8* src = reinterpret_cast<Color3uint8*>(im->getCArray());
for (int i = 0; i < N; ++i) {
out->data[i] = Color3(src[i]);
}
return out;
}
Image3::Ref Image3::createEmpty(int width, int height, WrapMode wrap) {
return new Image3(width, height, wrap);
}
Image3::Ref Image3::createEmpty(WrapMode wrap) {
return createEmpty(0, 0, wrap);
}
Image3::Ref Image3::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
Ref out = createEmpty(wrap);
out->load(filename, fmt);
return out;
}
void Image3::load(const std::string& filename, GImage::Format fmt) {
copyGImage(GImage(filename, fmt));
setChanged(true);
}
Image3::Ref Image3::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3::Ref Image3::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3::Ref Image3::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3::Ref Image3::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3::Ref Image3::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3::Ref Image3::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
void Image3::copyGImage(const GImage& im) {
switch (im.channels()) {
case 1:
copyArray(im.pixel1(), im.width(), im.height());
break;
case 3:
copyArray(im.pixel3(), im.width(), im.height());
break;
case 4:
copyArray(im.pixel4(), im.width(), im.height());
break;
}
}
void Image3::copyArray(const Color3uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3* dst = data.getCArray();
// Convert int8 -> float
for (int i = 0; i < N; ++i) {
dst[i] = Color3(src[i]);
}
}
void Image3::copyArray(const Color4uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3* dst = data.getCArray();
// Strip alpha and convert
for (int i = 0; i < N; ++i) {
dst[i] = Color3(src[i].rgb());
}
}
void Image3::copyArray(const Color3* src, int w, int h) {
resize(w, h);
System::memcpy(getCArray(), src, w * h * sizeof(Color3));
}
void Image3::copyArray(const Color4* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3* dst = data.getCArray();
// Strip alpha
for (int i = 0; i < N; ++i) {
dst[i] = src[i].rgb();
}
}
void Image3::copyArray(const Color1uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
}
}
void Image3::copyArray(const Color1* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = src[i].value;
}
}
/** Saves in any of the formats supported by G3D::GImage. */
void Image3::save(const std::string& filename, GImage::Format fmt) {
GImage im(width(), height(), 3);
int N = im.width() * im.height();
Color3uint8* dst = im.pixel3();
for (int i = 0; i < N; ++i) {
dst[i] = Color3uint8(data[i]);
}
im.save(filename, fmt);
}
const ImageFormat* Image3::format() const {
return ImageFormat::RGB32F();
}
} // G3D

View File

@@ -0,0 +1,225 @@
/**
@file Image3uint8.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-31
@edited 2008-01-08
*/
#include "G3D/Image1uint8.h"
#include "G3D/Image3uint8.h"
#include "G3D/Image3.h"
#include "G3D/GImage.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/Color4.h"
#include "G3D/Color4uint8.h"
#include "G3D/ImageFormat.h"
namespace G3D {
Image3uint8::Ref Image3uint8::fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im) {
return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
}
Image3uint8::Image3uint8(int w, int h, WrapMode wrap) : Map2D<Color3uint8>(w, h, wrap) {
setAll(Color3::black());
}
Image3uint8::Ref Image3uint8::fromGImage(const GImage& im, WrapMode wrap) {
switch (im.channels()) {
case 1:
return fromArray(im.pixel1(), im.width(), im.height(), wrap);
case 3:
return fromArray(im.pixel3(), im.width(), im.height(), wrap);
case 4:
return fromArray(im.pixel4(), im.width(), im.height(), wrap);
default:
debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
return NULL;
}
}
Image3uint8::Ref Image3uint8::fromImage3(const ReferenceCountedPointer<Image3>& im) {
Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
out->copyArray(im->getCArray(), im->width(), im->height());
return out;
}
Image3uint8::Ref Image3uint8::createEmpty(int width, int height, WrapMode wrap) {
return new Type(width, height, wrap);
}
Image3uint8::Ref Image3uint8::createEmpty(WrapMode wrap) {
return createEmpty(0, 0, wrap);
}
Image3uint8::Ref Image3uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
Ref out = createEmpty(wrap);
out->load(filename, fmt);
return out;
}
Image3uint8::Ref Image3uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3uint8::Ref Image3uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3uint8::Ref Image3uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3uint8::Ref Image3uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3uint8::Ref Image3uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image3uint8::Ref Image3uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
void Image3uint8::load(const std::string& filename, GImage::Format fmt) {
copyGImage(GImage(filename, fmt));
setChanged(true);
}
void Image3uint8::copyGImage(const GImage& im) {
switch (im.channels()) {
case 1:
copyArray(im.pixel1(), im.width(), im.height());
break;
case 3:
copyArray(im.pixel3(), im.width(), im.height());
break;
case 4:
copyArray(im.pixel4(), im.width(), im.height());
break;
}
}
void Image3uint8::copyArray(const Color1uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = src[i].value;
}
}
void Image3uint8::copyArray(const Color1* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
}
}
void Image3uint8::copyArray(const Color3uint8* ptr, int w, int h) {
resize(w, h);
System::memcpy(getCArray(), ptr, w * h * 3);
}
void Image3uint8::copyArray(const Color3* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color3uint8(src[i]);
}
}
void Image3uint8::copyArray(const Color4uint8* ptr, int w, int h) {
resize(w, h);
// Copy 3/4 bytes
GImage::RGBAtoRGB((const uint8*)ptr, (uint8*)getCArray(), w * h);
}
void Image3uint8::copyArray(const Color4* src, int w, int h) {
resize(w, h);
int N = w * h;
Color3uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color3uint8(src[i].rgb());
}
}
/** Saves in any of the formats supported by G3D::GImage. */
void Image3uint8::save(const std::string& filename, GImage::Format fmt) {
GImage im(width(), height(), 3);
System::memcpy(im.byte(), getCArray(), width() * height() * 3);
im.save(filename, fmt);
}
ReferenceCountedPointer<class Image1uint8> Image3uint8::getChannel(int c) const {
debugAssert(c >= 0 && c <= 2);
Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
const Color3uint8* srcArray = getCArray();
Color1uint8* dstArray = dst->getCArray();
const int N = width() * height();
for (int i = 0; i < N; ++i) {
dstArray[i] = Color1uint8(srcArray[i][c]);
}
return dst;
}
const ImageFormat* Image3uint8::format() const {
return ImageFormat::RGB8();
}
} // G3D

View File

@@ -0,0 +1,226 @@
/**
@file Image4.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-31
@edited 2008-07-27
*/
#include "G3D/Image4.h"
#include "G3D/Image4uint8.h"
#include "G3D/GImage.h"
#include "G3D/Color3.h"
#include "G3D/Color3uint8.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/ImageFormat.h"
namespace G3D {
Image4::Image4(int w, int h, WrapMode wrap) : Map2D<Color4, Color4>(w, h, wrap) {
setAll(Color4::zero());
}
Image4::Ref Image4::fromGImage(const GImage& im, WrapMode wrap) {
switch (im.channels()) {
case 1:
return fromArray(im.pixel1(), im.width(), im.height(), wrap);
case 3:
return fromArray(im.pixel3(), im.width(), im.height(), wrap);
case 4:
return fromArray(im.pixel4(), im.width(), im.height(), wrap);
default:
debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
return NULL;
}
}
Image4::Ref Image4::fromImage4uint8(const ReferenceCountedPointer<Image4uint8>& im) {
Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
out->resize(im->width(), im->height());
int N = im->width() * im->height();
const Color4uint8* src = reinterpret_cast<Color4uint8*>(im->getCArray());
for (int i = 0; i < N; ++i) {
out->data[i] = Color4(src[i]);
}
return out;
}
Image4::Ref Image4::createEmpty(int width, int height, WrapMode wrap) {
return new Type(width, height, wrap);
}
Image4::Ref Image4::createEmpty(WrapMode wrap) {
return createEmpty(0, 0, wrap);
}
Image4::Ref Image4::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
Ref out = createEmpty(wrap);
out->load(filename);
return out;
}
void Image4::load(const std::string& filename, GImage::Format fmt) {
copyGImage(GImage(filename, fmt));
setChanged(true);
}
Image4::Ref Image4::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4::Ref Image4::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4::Ref Image4::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4::Ref Image4::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4::Ref Image4::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4::Ref Image4::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
void Image4::copyGImage(const GImage& im) {
switch (im.channels()) {
case 1:
copyArray(im.pixel1(), im.width(), im.height());
break;
case 3:
copyArray(im.pixel3(), im.width(), im.height());
break;
case 4:
copyArray(im.pixel4(), im.width(), im.height());
break;
}
}
void Image4::copyArray(const Color4uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4* dst = data.getCArray();
// Convert int8 -> float
for (int i = 0; i < N; ++i) {
dst[i] = Color4(src[i]);
}
}
void Image4::copyArray(const Color3uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4* dst = data.getCArray();
// Add alpha and convert
for (int i = 0; i < N; ++i) {
dst[i] = Color4(Color3(src[i]), 1.0f);
}
}
void Image4::copyArray(const Color4* src, int w, int h) {
resize(w, h);
System::memcpy(getCArray(), src, w * h * sizeof(Color4));
}
void Image4::copyArray(const Color3* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4* dst = data.getCArray();
// Add alpha
for (int i = 0; i < N; ++i) {
dst[i] = Color4(src[i], 1.0f);
}
}
void Image4::copyArray(const Color1uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
dst[i].a = 1.0f;
}
}
void Image4::copyArray(const Color1* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = src[i].value;
dst[i].a = 1.0f;
}
}
/** Saves in any of the formats supported by G3D::GImage. */
void Image4::save(const std::string& filename, GImage::Format fmt) {
GImage im(width(), height(), 4);
int N = im.width() * im.height();
Color4uint8* dst = im.pixel4();
for (int i = 0; i < N; ++i) {
dst[i] = Color4uint8(data[i]);
}
im.save(filename, fmt);
}
const ImageFormat* Image4::format() const {
return ImageFormat::RGBA32F();
}
} // G3D

View File

@@ -0,0 +1,222 @@
/**
@file Image4uint8.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-01-31
@edited 2008-07-31
*/
#include "G3D/Image4uint8.h"
#include "G3D/Image4.h"
#include "G3D/Image3uint8.h"
#include "G3D/Image3.h"
#include "G3D/GImage.h"
#include "G3D/Color1.h"
#include "G3D/Color1uint8.h"
#include "G3D/Color4.h"
#include "G3D/Color4uint8.h"
#include "G3D/ImageFormat.h"
namespace G3D {
Image4uint8::Image4uint8(int w, int h, WrapMode wrap) : Map2D<Color4uint8, Color4>(w, h, wrap) {
setAll(Color4::zero());
}
Image4uint8::Ref Image4uint8::fromGImage(const GImage& im, WrapMode wrap) {
switch (im.channels()) {
case 1:
return fromArray(im.pixel1(), im.width(), im.height(), wrap);
case 3:
return fromArray(im.pixel3(), im.width(), im.height(), wrap);
case 4:
return fromArray(im.pixel4(), im.width(), im.height(), wrap);
default:
debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
return NULL;
}
}
Image4uint8::Ref Image4uint8::fromImage4(const ReferenceCountedPointer<Image4>& im) {
Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
out->copyArray(im->getCArray(), im->width(), im->height());
return out;
}
Image4uint8::Ref Image4uint8::createEmpty(int width, int height, WrapMode wrap) {
return new Type(width, height, wrap);
}
Image4uint8::Ref Image4uint8::createEmpty(WrapMode wrap) {
return createEmpty(0, 0, wrap);
}
Image4uint8::Ref Image4uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
Ref out = createEmpty(wrap);
out->load(filename, fmt);
return out;
}
Image4uint8::Ref Image4uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4uint8::Ref Image4uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4uint8::Ref Image4uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4uint8::Ref Image4uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4uint8::Ref Image4uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
Image4uint8::Ref Image4uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
Ref out = createEmpty(wrap);
out->copyArray(ptr, w, h);
return out;
}
void Image4uint8::load(const std::string& filename, GImage::Format fmt) {
copyGImage(GImage(filename, fmt));
setChanged(true);
}
void Image4uint8::copyGImage(const GImage& im) {
switch (im.channels()) {
case 1:
copyArray(im.pixel1(), im.width(), im.height());
break;
case 3:
copyArray(im.pixel3(), im.width(), im.height());
break;
case 4:
copyArray(im.pixel4(), im.width(), im.height());
break;
}
}
void Image4uint8::copyArray(const Color1uint8* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = src[i].value;
dst[i].a = 255;
}
}
void Image4uint8::copyArray(const Color1* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
dst[i].a = 255;
}
}
void Image4uint8::copyArray(const Color4uint8* ptr, int w, int h) {
resize(w, h);
System::memcpy(getCArray(), ptr, w * h * 4);
}
void Image4uint8::copyArray(const Color4* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color4uint8(src[i]);
}
}
void Image4uint8::copyArray(const Color3uint8* ptr, int w, int h) {
resize(w, h);
GImage::RGBtoRGBA((const uint8*)ptr, (uint8*)getCArray(), w * h);
}
void Image4uint8::copyArray(const Color3* src, int w, int h) {
resize(w, h);
int N = w * h;
Color4uint8* dst = getCArray();
for (int i = 0; i < N; ++i) {
dst[i] = Color4uint8(Color4(src[i], 1.0f));
}
}
/** Saves in any of the formats supported by G3D::GImage. */
void Image4uint8::save(const std::string& filename, GImage::Format fmt) {
GImage im(width(), height(), 4);
System::memcpy(im.byte(), getCArray(), width() * height() * 4);
im.save(filename, fmt);
}
ReferenceCountedPointer<class Image1uint8> Image4uint8::getChannel(int c) const {
debugAssert(c >= 0 && c <= 3);
Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
const Color4uint8* srcArray = getCArray();
Color1uint8* dstArray = dst->getCArray();
const int N = width() * height();
for (int i = 0; i < N; ++i) {
dstArray[i] = Color1uint8(srcArray[i][c]);
}
return dst;
}
const ImageFormat* Image4uint8::format() const {
return ImageFormat::RGBA8();
}
} // G3D

View File

@@ -0,0 +1,588 @@
/**
@file ImageFormat.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-05-23
@edited 2010-03-30
*/
#include "GLG3D/glheaders.h"
#include "G3D/ImageFormat.h"
namespace G3D {
ImageFormat::ImageFormat(
int _numComponents,
bool _compressed,
int _glFormat,
int _glBaseFormat,
int _luminanceBits,
int _alphaBits,
int _redBits,
int _greenBits,
int _blueBits,
int _depthBits,
int _stencilBits,
int _hardwareBitsPerTexel,
int _packedBitsPerTexel,
int glDataFormat,
bool _opaque,
bool _floatingPoint,
Code _code,
ColorSpace _colorSpace,
BayerPattern _bayerPattern) :
numComponents(_numComponents),
compressed(_compressed),
code(_code),
colorSpace(_colorSpace),
bayerPattern(_bayerPattern),
openGLFormat(_glFormat),
openGLBaseFormat(_glBaseFormat),
luminanceBits(_luminanceBits),
alphaBits(_alphaBits),
redBits(_redBits),
greenBits(_greenBits),
blueBits(_blueBits),
stencilBits(_stencilBits),
depthBits(_depthBits),
cpuBitsPerPixel(_packedBitsPerTexel),
packedBitsPerTexel(_packedBitsPerTexel),
openGLBitsPerPixel(_hardwareBitsPerTexel),
hardwareBitsPerTexel(_hardwareBitsPerTexel),
openGLDataFormat(glDataFormat),
opaque(_opaque),
floatingPoint(_floatingPoint) {
debugAssert(_packedBitsPerTexel <= _hardwareBitsPerTexel);
}
const ImageFormat* ImageFormat::depth(int depthBits) {
switch (depthBits) {
case 16:
return DEPTH16();
case 24:
return DEPTH24();
case 32:
return DEPTH32();
default:
debugAssertM(false, "Depth must be 16, 24, or 32.");
return DEPTH32();
}
}
const ImageFormat* ImageFormat::stencil(int bits) {
switch (bits) {
case 1:
return STENCIL1();
case 4:
return STENCIL4();
case 8:
return STENCIL8();
case 16:
return STENCIL16();
default:
debugAssertM(false, "Stencil must be 1, 4, 8 or 16.");
return STENCIL16();
}
}
static const std::string nameArray[] =
{
"L8",
"L16",
"L16F",
"L32F",
"A8",
"A16",
"A16F",
"A32F",
"LA4",
"LA8",
"LA16",
"LA16F",
"LA32F",
"RGB5",
"RGB5A1",
"RGB8",
"RGB10",
"RGB10A2",
"RGB16",
"RGB16F",
"RGB32F",
"R11G11B10F",
"RGB9E10F",
"RGB8I",
"RGB8UI",
"RGBA8UI",
"ARGB8",
"BGR8",
"R8",
"RG8",
"RG8I",
"RG8UI",
"RG16F",
"RGBA8",
"RGBA16",
"RGBA16F",
"RGBA32F",
"RGBA32UI",
"BAYER_RGGB8",
"BAYER_GRBG8",
"BAYER_GBRG8",
"BAYER_BGGR8",
"BAYER_RGGB32F",
"BAYER_GRBG32F",
"BAYER_GBRG32F",
"BAYER_BGGR32F",
"HSV8",
"HSV32F",
"YUV420_PLANAR",
"YUV422",
"YUV444",
"RGB_DXT1",
"RGBA_DXT1",
"RGBA_DXT3",
"RGBA_DXT5",
"SRGB8",
"SRGBA8",
"SL8",
"SLA8",
"SRGB_DXT1",
"SRGBA_DXT1",
"SRGBA_DXT3",
"SRGBA_DXT5",
"DEPTH16",
"DEPTH24",
"DEPTH32",
"DEPTH32F",
"STENCIL1",
"STENCIL4",
"STENCIL8",
"STENCIL16",
"DEPTH24_STENCIL8",
""
};
const std::string& ImageFormat::name() const {
debugAssert(code < CODE_NUM);
return nameArray[code];
}
const ImageFormat* ImageFormat::fromString(const std::string& s) {
for (int i = 0; ! nameArray[i].empty(); ++i) {
if (s == nameArray[i]) {
return fromCode(ImageFormat::Code(i));
}
}
return NULL;
}
const ImageFormat* ImageFormat::fromCode(ImageFormat::Code code) {
switch (code) {
case ImageFormat::CODE_L8:
return ImageFormat::L8();
case ImageFormat::CODE_L16:
return ImageFormat::L16();
case ImageFormat::CODE_L16F:
return ImageFormat::L16F();
case ImageFormat::CODE_L32F:
return ImageFormat::L32F();
case ImageFormat::CODE_A8:
return ImageFormat::A8();
case ImageFormat::CODE_A16:
return ImageFormat::A16();
case ImageFormat::CODE_A16F:
return ImageFormat::A16F();
case ImageFormat::CODE_A32F:
return ImageFormat::A32F();
case ImageFormat::CODE_LA4:
return ImageFormat::LA4();
case ImageFormat::CODE_LA8:
return ImageFormat::LA8();
case ImageFormat::CODE_LA16:
return ImageFormat::LA16();
case ImageFormat::CODE_LA16F:
return ImageFormat::LA16F();
break;
case ImageFormat::CODE_LA32F:
return ImageFormat::LA32F();
case ImageFormat::CODE_RGB5:
return ImageFormat::RGB5();
case ImageFormat::CODE_RGB5A1:
return ImageFormat::RGB5A1();
case ImageFormat::CODE_RGB8:
return ImageFormat::RGB8();
case ImageFormat::CODE_RGB10:
return ImageFormat::RGB10();
case ImageFormat::CODE_RGB10A2:
return ImageFormat::RGB10A2();
case ImageFormat::CODE_RGB16:
return ImageFormat::RGB16();
case ImageFormat::CODE_RGB32F:
return ImageFormat::RGB32F();
case ImageFormat::CODE_R11G11B10F:
return ImageFormat::R11G11B10F();
case ImageFormat::CODE_RGB9E5F:
return ImageFormat::RGB9E5F();
case ImageFormat::CODE_RGB8I:
return ImageFormat::RGB8I();
case ImageFormat::CODE_RGB8UI:
return ImageFormat::RGB8UI();
case ImageFormat::CODE_ARGB8:
return NULL;
case ImageFormat::CODE_BGR8:
return ImageFormat::BGR8();
case ImageFormat::CODE_R8:
return ImageFormat::R8();
case ImageFormat::CODE_RG8:
return ImageFormat::RG8();
case ImageFormat::CODE_RG8I:
return ImageFormat::RG8I();
case ImageFormat::CODE_RG8UI:
return ImageFormat::RG8UI();
case ImageFormat::CODE_RG16F:
return ImageFormat::RG16F();
case ImageFormat::CODE_RGBA8:
return ImageFormat::RGBA8();
case ImageFormat::CODE_RGBA16:
return ImageFormat::RGBA16();
case ImageFormat::CODE_RGBA16F:
return ImageFormat::RGBA16F();
case ImageFormat::CODE_RGBA32F:
return ImageFormat::RGBA32F();
case ImageFormat::CODE_RGBA32UI:
return ImageFormat::RGBA32UI();
case ImageFormat::CODE_BAYER_RGGB8:
// TODO
case ImageFormat::CODE_BAYER_GRBG8:
// TODO
case ImageFormat::CODE_BAYER_GBRG8:
// TODO
case ImageFormat::CODE_BAYER_BGGR8:
// TODO
case ImageFormat::CODE_BAYER_RGGB32F:
// TODO
case ImageFormat::CODE_BAYER_GRBG32F:
// TODO
case ImageFormat::CODE_BAYER_GBRG32F:
// TODO
case ImageFormat::CODE_BAYER_BGGR32F:
// TODO
case ImageFormat::CODE_HSV8:
// TODO
case ImageFormat::CODE_HSV32F:
// TODO
return NULL;
break;
case ImageFormat::CODE_RGB_DXT1:
return ImageFormat::RGB_DXT1();
break;
case ImageFormat::CODE_RGBA_DXT1:
return ImageFormat::RGBA_DXT1();
break;
case ImageFormat::CODE_RGBA_DXT3:
return ImageFormat::RGBA_DXT3();
break;
case ImageFormat::CODE_RGBA_DXT5:
return ImageFormat::RGBA_DXT5();
break;
case ImageFormat::CODE_SRGB8:
return ImageFormat::SRGB8();
break;
case ImageFormat::CODE_SRGBA8:
return ImageFormat::SRGBA8();
break;
case ImageFormat::CODE_SL8:
return ImageFormat::SL8();
break;
case ImageFormat::CODE_SLA8:
return ImageFormat::SLA8();
break;
case ImageFormat::CODE_SRGB_DXT1:
return ImageFormat::SRGB_DXT1();
break;
case ImageFormat::CODE_SRGBA_DXT1:
return ImageFormat::SRGBA_DXT1();
break;
case ImageFormat::CODE_SRGBA_DXT3:
return ImageFormat::SRGBA_DXT3();
break;
case ImageFormat::CODE_SRGBA_DXT5:
return ImageFormat::SRGBA_DXT5();
break;
case ImageFormat::CODE_DEPTH16:
return ImageFormat::DEPTH16();
break;
case ImageFormat::CODE_DEPTH24:
return ImageFormat::DEPTH24();
break;
case ImageFormat::CODE_DEPTH32:
return ImageFormat::DEPTH32();
break;
case ImageFormat::CODE_DEPTH32F:
return ImageFormat::DEPTH32F();
break;
case ImageFormat::CODE_STENCIL1:
return ImageFormat::STENCIL1();
break;
case ImageFormat::CODE_STENCIL4:
return ImageFormat::STENCIL4();
break;
case ImageFormat::CODE_STENCIL8:
return ImageFormat::STENCIL8();
break;
case ImageFormat::CODE_STENCIL16:
return ImageFormat::STENCIL16();
break;
case ImageFormat::CODE_DEPTH24_STENCIL8:
return ImageFormat::DEPTH24_STENCIL8();
break;
case ImageFormat::CODE_YUV420_PLANAR:
return ImageFormat::YUV420_PLANAR();
break;
case ImageFormat::CODE_YUV422:
return ImageFormat::YUV422();
break;
case ImageFormat::CODE_YUV444:
return ImageFormat::YUV444();
break;
default:
return NULL;
}
}
// Helper variables for defining texture formats
// Is floating point format
static const bool FLOAT_FORMAT = true;
static const bool INT_FORMAT = false;
// Is opaque format (no alpha)
static const bool OPAQUE_FORMAT = true;
static const bool CLEAR_FORMAT = false;
// Is compressed format (not raw component data)
static const bool COMP_FORMAT = true;
static const bool UNCOMP_FORMAT = false;
#define DEFINE_TEXTUREFORMAT_METHOD(enumname, cmpnts, cmprssd, glf, glbf, lb, ab, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs) \
const ImageFormat* ImageFormat::enumname() { \
static const ImageFormat format(cmpnts, cmprssd, glf, glbf, lb, ab, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs); \
return &format; }
DEFINE_TEXTUREFORMAT_METHOD(L8, 1, UNCOMP_FORMAT, GL_LUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, CODE_L8, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(L16, 1, UNCOMP_FORMAT, GL_LUMINANCE16, GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16,GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, CODE_L16, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(L16F, 1, UNCOMP_FORMAT, GL_LUMINANCE16F_ARB,GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L16F, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(L32F, 1, UNCOMP_FORMAT, GL_LUMINANCE32F_ARB,GL_LUMINANCE, 32, 0, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L32F, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(A8, 1, UNCOMP_FORMAT, GL_ALPHA8, GL_ALPHA, 0, 8, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_A8, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(A16, 1, UNCOMP_FORMAT, GL_ALPHA16, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_A16, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(A16F, 1, UNCOMP_FORMAT, GL_ALPHA16F_ARB, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A16F, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(A32F, 1, UNCOMP_FORMAT, GL_ALPHA32F_ARB, GL_ALPHA, 0, 32, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A32F, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(LA4, 2, UNCOMP_FORMAT, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, 4, 4, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA4, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(LA8, 2, UNCOMP_FORMAT, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA8, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(LA16, 2, UNCOMP_FORMAT, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_LA16, COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(LA16F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA16F_ARB, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA16F, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(LA32F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA32F_ARB, GL_LUMINANCE_ALPHA, 32, 32, 0, 0, 0, 0, 0, 32*2, 32*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA32F, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(BGR8, 3, UNCOMP_FORMAT, GL_RGB8, GL_BGR, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_BGR8, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(R8, 1, UNCOMP_FORMAT, GL_R8, GL_RED, 0, 0, 8, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_R8, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RG8, 2, UNCOMP_FORMAT, GL_RG8, GL_RG, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8, ImageFormat::COLOR_SPACE_RGB);
// The base format for integer formats must be *_INTEGER even though the spec doesn't state this
DEFINE_TEXTUREFORMAT_METHOD(RG8I, 2, UNCOMP_FORMAT, GL_RG8I, GL_RG_INTEGER, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8I, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RG8UI, 2, UNCOMP_FORMAT, GL_RG8UI, GL_RG_INTEGER, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8UI, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RG16F, 2, UNCOMP_FORMAT, GL_RG16F, GL_RG, 0, 0, 16, 16, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RG16F, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB5, 3, UNCOMP_FORMAT, GL_RGB5, GL_RGBA, 0, 0, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB5A1, 4, UNCOMP_FORMAT, GL_RGB5_A1, GL_RGBA, 0, 1, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5A1, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB8, 3, UNCOMP_FORMAT, GL_RGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB10, 3, UNCOMP_FORMAT, GL_RGB10, GL_RGB, 0, 0, 10, 10, 10, 0, 0, 32, 10*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB10A2, 4, UNCOMP_FORMAT, GL_RGB10_A2, GL_RGBA, 0, 2, 10, 10, 10, 0, 0, 32, 32, GL_UNSIGNED_INT_10_10_10_2, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10A2, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB16, 3, UNCOMP_FORMAT, GL_RGB16, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB16, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB16F, 3, UNCOMP_FORMAT, GL_RGB16F_ARB, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB32F, 3, UNCOMP_FORMAT, GL_RGB32F_ARB, GL_RGB, 0, 0, 32, 32, 32, 0, 0, 32*3, 32*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB32F, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA8, 4, UNCOMP_FORMAT, GL_RGBA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 32, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA16, 4, UNCOMP_FORMAT, GL_RGBA16, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA16, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA16F, 4, UNCOMP_FORMAT, GL_RGBA16F_ARB, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA16F, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA32F, 4, UNCOMP_FORMAT, GL_RGBA32F_ARB, GL_RGBA, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA32F, ImageFormat::COLOR_SPACE_RGB);
// The base format for integer formats must be *_INTEGER even though the spec doesn't state this
DEFINE_TEXTUREFORMAT_METHOD(RGBA32UI, 4, UNCOMP_FORMAT, GL_RGBA32UI, GL_RGBA_INTEGER, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA32UI, ImageFormat::COLOR_SPACE_RGB);
// Unsigned
DEFINE_TEXTUREFORMAT_METHOD(R11G11B10F, 3, UNCOMP_FORMAT, GL_R11F_G11F_B10F_EXT, GL_RGB, 0, 0, 11, 11, 10, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_R11G11B10F, ImageFormat::COLOR_SPACE_RGB);
// Unsigned
DEFINE_TEXTUREFORMAT_METHOD(RGB9E5F, 3, UNCOMP_FORMAT, GL_RGB9_E5_EXT, GL_RGB, 0, 0, 14, 14, 14, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB9E5F, ImageFormat::COLOR_SPACE_RGB);
// The base format for integer formats must be *_INTEGER even though the spec doesn't state this
DEFINE_TEXTUREFORMAT_METHOD(RGB8I, 3, UNCOMP_FORMAT, GL_RGB8I_EXT, GL_RGB_INTEGER, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8I, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB8UI, 3, UNCOMP_FORMAT, GL_RGB8UI_EXT, GL_RGB_INTEGER, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8UI, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA8UI, 4, UNCOMP_FORMAT, GL_RGBA8UI_EXT, GL_RGBA_INTEGER, 0, 0, 8, 8, 8, 8, 0, 32, 32, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8UI, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB_DXT1, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT1, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT3, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT5, ImageFormat::COLOR_SPACE_RGB);
DEFINE_TEXTUREFORMAT_METHOD(SRGB8, 3, UNCOMP_FORMAT, GL_SRGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGB8, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SRGBA8, 4, UNCOMP_FORMAT, GL_SRGB8_ALPHA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA8, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SL8, 1, UNCOMP_FORMAT, GL_SLUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SL8, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SLA8, 2, UNCOMP_FORMAT, GL_SLUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SLA8, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SRGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGB_DXT1, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT1, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT3, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT5, ImageFormat::COLOR_SPACE_SRGB);
DEFINE_TEXTUREFORMAT_METHOD(DEPTH16, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 16, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH16, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(DEPTH24, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 24, 0, 32, 24, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(DEPTH32, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 32, 0, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH32, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(DEPTH32F, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 32, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_DEPTH32F, ImageFormat::COLOR_SPACE_NONE);
// These formats are for use with Renderbuffers only!
DEFINE_TEXTUREFORMAT_METHOD(STENCIL1, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 1, 1, 1, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL1, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(STENCIL4, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 4, 4, 4, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL4, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(STENCIL8, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 8, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(STENCIL16, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX16_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL16, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(DEPTH24_STENCIL8, 2, UNCOMP_FORMAT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_STENCIL_EXT,0, 0, 0, 0, 0, 24, 8, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
DEFINE_TEXTUREFORMAT_METHOD(YUV420_PLANAR, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 12, 12, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV420_PLANAR, ImageFormat::COLOR_SPACE_YUV);
DEFINE_TEXTUREFORMAT_METHOD(YUV422, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV422, ImageFormat::COLOR_SPACE_YUV);
DEFINE_TEXTUREFORMAT_METHOD(YUV444, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 24, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV444, ImageFormat::COLOR_SPACE_YUV);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,844 @@
/**
@file Intersect.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-06-29
@edited 2009-06-29
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
From the G3D Innovation Engine
http://g3d.sf.net
*/
#include "G3D/Intersect.h"
namespace G3D {
#ifdef _MSC_VER
// Turn on fast floating-point optimizations
#pragma float_control( push )
#pragma fp_contract( on )
#pragma fenv_access( off )
#pragma float_control( except, off )
#pragma float_control( precise, off )
#endif
bool __fastcall Intersect::rayAABox(const Ray& ray, const AABox& box) {
switch (ray.classification) {
case Ray::MMM:
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
)
return false;
return true;
case Ray::MMP:
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
)
return false;
return true;
case Ray::MPM:
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
)
return false;
return true;
case Ray::MPP:
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
)
return false;
return true;
case Ray::PMM:
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
)
return false;
return true;
case Ray::PMP:
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)
)
return false;
return true;
case Ray::PPM:
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
)
return false;
return true;
case Ray::PPP:
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
return true;
case Ray::OMM:
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
)
return false;
return true;
case Ray::OMP:
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
)
return false;
return true;
case Ray::OPM:
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
)
return false;
return true;
case Ray::OPP:
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
)
return false;
return true;
case Ray::MOM:
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.z < box.lo.z)
|| (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
)
return false;
return true;
case Ray::MOP:
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.z > box.hi.z)
|| (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
)
return false;
return true;
case Ray::POM:
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.z < box.lo.z)
|| (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
)
return false;
return true;
case Ray::POP:
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.z > box.hi.z)
|| (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)
)
return false;
return true;
case Ray::MMO:
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y)
|| (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
)
return false;
return true;
case Ray::MPO:
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y)
|| (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
)
return false;
return true;
case Ray::PMO:
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y)
|| (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
)
return false;
return true;
case Ray::PPO:
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y)
|| (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
)
return false;
return true;
case Ray::MOO:
if((ray.m_origin.x < box.lo.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
)
return false;
return true;
case Ray::POO:
if((ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
)
return false;
return true;
case Ray::OMO:
if((ray.m_origin.y < box.lo.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
)
return false;
case Ray::OPO:
if((ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
)
return false;
case Ray::OOM:
if((ray.m_origin.z < box.lo.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
)
return false;
case Ray::OOP:
if((ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
)
return false;
return true;
}
return false;
}
bool __fastcall Intersect::rayAABox(const Ray& ray, const AABox& box, float& time) {
switch (ray.classification) {
case Ray::MMM:
{
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
return false;
}
// compute the intersection distance
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::MMP:
{
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::MPM:
{
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::MPP:
{
if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
|| (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::PMM:
{
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::PMP:
{
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::PPM:
{
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
|| (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::PPP:
{
if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
|| (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
|| (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::OMM:
{
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)) {
return false;
}
time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::OMP:
{
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
|| (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)) {
return false;
}
time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::OPM:
{
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
|| (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)) {
return false;
}
time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::OPP:
{
if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
|| (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
|| (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)) {
return false;
}
time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::MOM:
{
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.z < box.lo.z)
|| (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::MOP:
{
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.z > box.hi.z)
|| (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::POM:
{
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.z < box.lo.z)
|| (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
|| (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::POP:
{
if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.z > box.hi.z)
|| (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
|| (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
if (t2 > time) {
time = t2;
}
return true;
}
case Ray::MMO:
{
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y)
|| (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
return true;
}
case Ray::MPO:
{
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y)
|| (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
return true;
}
case Ray::PMO:
{
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y)
|| (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
|| (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
return true;
}
case Ray::PPO:
{
if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y)
|| (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
|| (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
if (t1 > time) {
time = t1;
}
return true;
}
case Ray::MOO:
{
if((ray.m_origin.x < box.lo.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
return false;
}
time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
return true;
}
case Ray::POO:
{
if ((ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
return false;
}
time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
return true;
}
case Ray::OMO:
{
if ((ray.m_origin.y < box.lo.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
return false;
}
time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
return true;
}
case Ray::OPO:
{
if ((ray.m_origin.y > box.hi.y)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
return false;
}
time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
return true;
}
case Ray::OOM:
{
if ((ray.m_origin.z < box.lo.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)) {
return false;
}
time = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
return true;
}
case Ray::OOP:
{
if ((ray.m_origin.z > box.hi.z)
|| (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
|| (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)) {
return false;
}
time = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
return true;
}
}
return false;
}
#ifdef _MSC_VER
// Turn off fast floating-point optimizations
#pragma float_control( pop )
#endif
}

View File

@@ -0,0 +1,89 @@
/**
@file Line.cpp
Line class
@maintainer Morgan McGuire, graphics3d.com
@created 2001-06-02
@edited 2006-01-28
*/
#include "G3D/Line.h"
#include "G3D/Plane.h"
namespace G3D {
Vector3 Line::intersection(const Plane& plane) const {
float d;
Vector3 normal = plane.normal();
plane.getEquation(normal, d);
float rate = _direction.dot(normal);
if (rate == 0) {
return Vector3::inf();
} else {
float t = -(d + _point.dot(normal)) / rate;
return _point + _direction * t;
}
}
Line::Line(class BinaryInput& b) {
deserialize(b);
}
void Line::serialize(class BinaryOutput& b) const {
_point.serialize(b);
_direction.serialize(b);
}
void Line::deserialize(class BinaryInput& b) {
_point.deserialize(b);
_direction.deserialize(b);
}
Vector3 Line::closestPoint(const Vector3& pt) const {
float t = _direction.dot(pt - _point);
return _point + _direction * t;
}
Vector3 Line::point() const {
return _point;
}
Vector3 Line::direction() const {
return _direction;
}
Vector3 Line::closestPoint(const Line& B, float& minDist) const {
const Vector3& P1 = _point;
const Vector3& U1 = _direction;
Vector3 P2 = B.point();
Vector3 U2 = B.direction();
const Vector3& P21 = P2 - P1;
const Vector3& M = U2.cross(U1);
float m2 = M.length();
Vector3 R = P21.cross(M) / m2;
float t1 = R.dot(U2);
minDist = abs(P21.dot(M)) / sqrt(m2);
return P1 + t1 * U1;
}
}

View File

@@ -0,0 +1,236 @@
/**
@file LineSegment.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-02-08
@edited 2008-02-02
*/
#include "G3D/platform.h"
#include "G3D/LineSegment.h"
#include "G3D/Sphere.h"
#include "G3D/debug.h"
namespace G3D {
Vector3 LineSegment::closestPoint(const Vector3& p) const {
// The vector from the end of the capsule to the point in question.
Vector3 v(p - _point);
// Projection of v onto the line segment scaled by
// the length of direction.
float t = direction.dot(v);
// Avoid some square roots. Derivation:
// t/direction.length() <= direction.length()
// t <= direction.squaredLength()
if ((t >= 0) && (t <= direction.squaredMagnitude())) {
// The point falls within the segment. Normalize direction,
// divide t by the length of direction.
return _point + direction * t / direction.squaredMagnitude();
} else {
// The point does not fall within the segment; see which end is closer.
// Distance from 0, squared
float d0Squared = v.squaredMagnitude();
// Distance from 1, squared
float d1Squared = (v - direction).squaredMagnitude();
if (d0Squared < d1Squared) {
// Point 0 is closer
return _point;
} else {
// Point 1 is closer
return _point + direction;
}
}
}
Vector3 LineSegment::point(int i) const {
switch (i) {
case 0:
return _point;
case 1:
return _point + direction;
default:
debugAssertM(i == 0 || i == 1, "Argument to point must be 0 or 1");
return _point;
}
}
bool LineSegment::intersectsSolidSphere(const class Sphere& s) const {
return distanceSquared(s.center) <= square(s.radius);
}
LineSegment::LineSegment(class BinaryInput& b) {
deserialize(b);
}
void LineSegment::serialize(class BinaryOutput& b) const {
_point.serialize(b);
direction.serialize(b);
}
void LineSegment::deserialize(class BinaryInput& b) {
_point.deserialize(b);
direction.deserialize(b);
}
Vector3 LineSegment::randomPoint() const {
return _point + uniformRandom(0, 1) * direction;
}
/////////////////////////////////////////////////////////////////////////////////////
LineSegment2D LineSegment2D::fromTwoPoints(const Vector2& p0, const Vector2& p1) {
LineSegment2D s;
s.m_origin = p0;
s.m_direction = p1 - p0;
s.m_length = s.m_direction.length();
return s;
}
Vector2 LineSegment2D::point(int i) const {
debugAssert(i == 0 || i == 1);
if (i == 0) {
return m_origin;
} else {
return m_direction + m_origin;
}
}
Vector2 LineSegment2D::closestPoint(const Vector2& Q) const {
// Two constants that appear in the result
const Vector2 k1(m_origin - Q);
const Vector2& k2 = m_direction;
if (fuzzyEq(m_length, 0)) {
// This line segment has no length
return m_origin;
}
// Time [0, 1] at which we hit the closest point travelling from p0 to p1.
// Derivation can be obtained by minimizing the expression
// ||P0 + (P1 - P0)t - Q||.
const float t = -k1.dot(k2) / (m_length * m_length);
if (t < 0) {
// Clipped to low end point
return m_origin;
} else if (t > 1) {
// Clipped to high end point
return m_origin + m_direction;
} else {
// Subsitute into the line equation to find
// the point on the segment.
return m_origin + k2 * t;
}
}
float LineSegment2D::distance(const Vector2& p) const {
Vector2 closest = closestPoint(p);
return (closest - p).length();
}
float LineSegment2D::length() const {
return m_length;
}
Vector2 LineSegment2D::intersection(const LineSegment2D& other) const {
if ((m_origin == other.m_origin) ||
(m_origin == other.m_origin + other.m_direction)) {
return m_origin;
}
if (m_origin + m_direction == other.m_origin) {
return other.m_origin;
}
// Note: Now that we've checked the endpoints, all other parallel lines can now be assumed
// to not intersect (within numerical precision)
Vector2 dir1 = m_direction;
Vector2 dir2 = other.m_direction;
Vector2 origin1 = m_origin;
Vector2 origin2 = other.m_origin;
if (dir1.x == 0) {
// Avoid an upcoming divide by zero
dir1 = dir1.yx();
dir2 = dir2.yx();
origin1 = origin1.yx();
origin2 = origin2.yx();
}
// t1 = ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) / m_direction.x
//
// ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) * m_direction.y / m_direction.x =
// (other.m_origin.y - m_origin.y) + other.m_direction.y * t2
//
// m = m_direction.y / m_direction.x
// d = other.m_origin - m_origin
//
// (d.x + other.m_direction.x * t2) * m = d.y + other.m_direction.y * t2
//
// d.x * m + other.m_direction.x * m * t2 = d.y + other.m_direction.y * t2
//
// d.x * m - d.y = (other.m_direction.y - other.m_direction.x * m) * t2
//
// (d.x * m - d.y) / (other.m_direction.y - other.m_direction.x * m) = t2
//
Vector2 d = origin2 - origin1;
float m = dir1.y / dir1.x;
float t2 = (d.x * m - d.y) / (dir2.y - dir2.x * m);
if (! isFinite(t2)) {
// Parallel lines: no intersection
return Vector2::inf();
}
if ((t2 < 0.0f) || (t2 > 1.0f)) {
// Intersection occurs past the end of the line segments
return Vector2::inf();
}
float t1 = (d.x + dir2.x * t2) / dir1.x;
if ((t1 < 0.0f) || (t1 > 1.0f)) {
// Intersection occurs past the end of the line segments
return Vector2::inf();
}
// Return the intersection point (computed from non-transposed
// variables even if we flipped above)
return m_origin + m_direction * t1;
}
}

View File

@@ -0,0 +1,147 @@
/**
@file Log.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-08-04
@edited 2010-01-15
*/
#include "G3D/platform.h"
#include "G3D/Log.h"
#include "G3D/format.h"
#include "G3D/Array.h"
#include "G3D/fileutils.h"
#include "G3D/FileSystem.h"
#include <time.h>
#ifdef G3D_WIN32
#include <imagehlp.h>
#else
#include <stdarg.h>
#endif
namespace G3D {
void logPrintf(const char* fmt, ...) {
va_list arg_list;
va_start(arg_list, fmt);
Log::common()->vprintf(fmt, arg_list);
va_end(arg_list);
}
void logLazyPrintf(const char* fmt, ...) {
va_list arg_list;
va_start(arg_list, fmt);
Log::common()->lazyvprintf(fmt, arg_list);
va_end(arg_list);
}
Log* Log::commonLog = NULL;
Log::Log(const std::string& filename, int stripFromStackBottom) :
stripFromStackBottom(stripFromStackBottom) {
this->filename = filename;
logFile = FileSystem::fopen(filename.c_str(), "w");
if (logFile == NULL) {
std::string drive, base, ext;
Array<std::string> path;
parseFilename(filename, drive, path, base, ext);
std::string logName = base + ((ext != "") ? ("." + ext) : "");
// Write time is greater than 1ms. This may be a network drive.... try another file.
#ifdef G3D_WIN32
logName = std::string(std::getenv("TEMP")) + logName;
#else
logName = std::string("/tmp/") + logName;
#endif
logFile = FileSystem::fopen(logName.c_str(), "w");
}
// Use a large buffer (although we flush in logPrintf)
setvbuf(logFile, NULL, _IOFBF, 2048);
fprintf(logFile, "Application Log\n");
time_t t;
time(&t);
fprintf(logFile, "Start: %s\n", ctime(&t));
fflush(logFile);
if (commonLog == NULL) {
commonLog = this;
}
}
Log::~Log() {
section("Shutdown");
println("Closing log file");
// Make sure we don't leave a dangling pointer
if (Log::commonLog == this) {
Log::commonLog = NULL;
}
fclose(logFile);
}
FILE* Log::getFile() const {
return logFile;
}
Log* Log::common() {
if (commonLog == NULL) {
commonLog = new Log();
}
return commonLog;
}
std::string Log::getCommonLogFilename() {
return common()->filename;
}
void Log::section(const std::string& s) {
fprintf(logFile, "_____________________________________________________\n");
fprintf(logFile, "\n ### %s ###\n\n", s.c_str());
}
void __cdecl Log::printf(const char* fmt, ...) {
va_list arg_list;
va_start(arg_list, fmt);
print(vformat(fmt, arg_list));
va_end(arg_list);
}
void __cdecl Log::vprintf(const char* fmt, va_list argPtr) {
vfprintf(logFile, fmt, argPtr);
fflush(logFile);
}
void __cdecl Log::lazyvprintf(const char* fmt, va_list argPtr) {
vfprintf(logFile, fmt, argPtr);
}
void Log::print(const std::string& s) {
fprintf(logFile, "%s", s.c_str());
fflush(logFile);
}
void Log::println(const std::string& s) {
fprintf(logFile, "%s\n", s.c_str());
fflush(logFile);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,528 @@
/**
@file Matrix4.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-10-02
@edited 2010-01-29
*/
#include "G3D/platform.h"
#include "G3D/Matrix4.h"
#include "G3D/Matrix3.h"
#include "G3D/Vector4.h"
#include "G3D/Vector3.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Rect2D.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
Matrix4::Matrix4(const Any& any) {
any.verifyName("Matrix4");
any.verifyType(Any::ARRAY);
const std::string& name = toLower(any.name());
if (name == "matrix4") {
any.verifySize(16);
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
elt[r][c] = any[r * 4 + c];
}
}
} else if (name == "matrix4::scale") {
if (any.size() == 1) {
*this = scale(any[0].number());
} else if (any.size() == 3) {
*this = scale(any[0], any[1], any[2]);
} else {
any.verify(false, "Matrix4::scale() takes either 1 or 3 arguments");
}
} else if (name == "matrix4::translation") {
if (any.size() == 3) {
*this = translation(any[0], any[1], any[2]);
} else {
any.verify(false, "Matrix4::translation() takes either 1 or 3 arguments");
} } else {
any.verify(false, "Expected Matrix4 constructor");
}
}
Matrix4::operator Any() const {
Any any(Any::ARRAY, "Matrix4");
any.resize(16);
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
any[r * 4 + c] = elt[r][c];
}
}
return any;
}
const Matrix4& Matrix4::identity() {
static Matrix4 m(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
return m;
}
const Matrix4& Matrix4::zero() {
static Matrix4 m(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0);
return m;
}
Matrix4::Matrix4(const class CoordinateFrame& cframe) {
for (int r = 0; r < 3; ++r) {
for (int c = 0; c < 3; ++c) {
elt[r][c] = cframe.rotation[r][c];
}
elt[r][3] = cframe.translation[r];
}
elt[3][0] = 0.0f;
elt[3][1] = 0.0f;
elt[3][2] = 0.0f;
elt[3][3] = 1.0f;
}
Matrix4::Matrix4(const Matrix3& upper3x3, const Vector3& lastCol) {
for (int r = 0; r < 3; ++r) {
for (int c = 0; c < 3; ++c) {
elt[r][c] = upper3x3[r][c];
}
elt[r][3] = lastCol[r];
}
elt[3][0] = 0.0f;
elt[3][1] = 0.0f;
elt[3][2] = 0.0f;
elt[3][3] = 1.0f;
}
Matrix3 Matrix4::upper3x3() const {
return Matrix3(elt[0][0], elt[0][1], elt[0][2],
elt[1][0], elt[1][1], elt[1][2],
elt[2][0], elt[2][1], elt[2][2]);
}
Matrix4 Matrix4::orthogonalProjection(
const class Rect2D& rect,
float nearval,
float farval,
float upDirection) {
return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval, upDirection);
}
Matrix4 Matrix4::orthogonalProjection(
float left,
float right,
float bottom,
float top,
float nearval,
float farval,
float upDirection) {
// Adapted from Mesa. Note that Microsoft (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_8qnj.asp)
// and Linux (http://www.xfree86.org/current/glOrtho.3.html) have different matrices shown in their documentation.
float x, y, z;
float tx, ty, tz;
x = 2.0f / (right-left);
y = 2.0f / (top-bottom);
z = -2.0f / (farval-nearval);
tx = -(right+left) / (right-left);
ty = -(top+bottom) / (top-bottom);
tz = -(farval+nearval) / (farval-nearval);
y *= upDirection;
ty *= upDirection;
return
Matrix4( x , 0.0f, 0.0f, tx,
0.0f, y , 0.0f, ty,
0.0f, 0.0f, z , tz,
0.0f, 0.0f, 0.0f, 1.0f);
}
Matrix4 Matrix4::perspectiveProjection(
float left,
float right,
float bottom,
float top,
float nearval,
float farval,
float upDirection) {
float x, y, a, b, c, d;
x = (2.0f*nearval) / (right-left);
y = (2.0f*nearval) / (top-bottom);
a = (right+left) / (right-left);
b = (top+bottom) / (top-bottom);
if (farval >= finf()) {
// Infinite view frustum
c = -1.0f;
d = -2.0f * nearval;
} else {
c = -(farval+nearval) / (farval-nearval);
d = -(2.0f*farval*nearval) / (farval-nearval);
}
debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1");
y *= upDirection;
b *= upDirection;
return Matrix4(
x, 0, a, 0,
0, y, b, 0,
0, 0, c, d,
0, 0, -1, 0);
}
void Matrix4::getPerspectiveProjectionParameters(
float& left,
float& right,
float& bottom,
float& top,
float& nearval,
float& farval,
float upDirection) const {
debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1");
float x = elt[0][0];
float y = elt[1][1] * upDirection;
float a = elt[0][2];
float b = elt[1][2] * upDirection;
float c = elt[2][2];
float d = elt[2][3];
// Verify that this really is a projection matrix
debugAssertM(elt[3][2] == -1, "Not a projection matrix");
debugAssertM(elt[0][1] == 0, "Not a projection matrix");
debugAssertM(elt[0][3] == 0, "Not a projection matrix");
debugAssertM(elt[1][3] == 0, "Not a projection matrix");
debugAssertM(elt[3][3] == 0, "Not a projection matrix");
debugAssertM(elt[1][0] == 0, "Not a projection matrix");
debugAssertM(elt[2][0] == 0, "Not a projection matrix");
debugAssertM(elt[2][1] == 0, "Not a projection matrix");
debugAssertM(elt[3][0] == 0, "Not a projection matrix");
debugAssertM(elt[3][1] == 0, "Not a projection matrix");
if (c == -1) {
farval = finf();
nearval = -d / 2.0f;
} else {
nearval = d * ((c - 1.0f) / (c + 1.0f) - 1.0f) / (-2.0f * (c - 1.0f) / (c + 1.0f));
farval = nearval * ((c - 1.0f) / (c + 1.0f));
}
left = (a - 1.0f) * nearval / x;
right = 2.0f * nearval / x + left;
bottom = (b - 1.0f) * nearval / y;
top = 2.0f * nearval / y + bottom;
}
Matrix4::Matrix4(
float r1c1, float r1c2, float r1c3, float r1c4,
float r2c1, float r2c2, float r2c3, float r2c4,
float r3c1, float r3c2, float r3c3, float r3c4,
float r4c1, float r4c2, float r4c3, float r4c4) {
elt[0][0] = r1c1; elt[0][1] = r1c2; elt[0][2] = r1c3; elt[0][3] = r1c4;
elt[1][0] = r2c1; elt[1][1] = r2c2; elt[1][2] = r2c3; elt[1][3] = r2c4;
elt[2][0] = r3c1; elt[2][1] = r3c2; elt[2][2] = r3c3; elt[2][3] = r3c4;
elt[3][0] = r4c1; elt[3][1] = r4c2; elt[3][2] = r4c3; elt[3][3] = r4c4;
}
/**
init should be <B>row major</B>.
*/
Matrix4::Matrix4(const float* init) {
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
elt[r][c] = init[r * 4 + c];
}
}
}
Matrix4::Matrix4(const double* init) {
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
elt[r][c] = (float)init[r * 4 + c];
}
}
}
Matrix4::Matrix4() {
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
elt[r][c] = 0;
}
}
}
void Matrix4::setRow(int r, const Vector4& v) {
for (int c = 0; c < 4; ++c) {
elt[r][c] = v[c];
}
}
void Matrix4::setColumn(int c, const Vector4& v) {
for (int r = 0; r < 4; ++r) {
elt[r][c] = v[r];
}
}
const Vector4& Matrix4::row(int r) const {
return reinterpret_cast<const Vector4*>(elt[r])[0];
}
Vector4 Matrix4::column(int c) const {
Vector4 v;
for (int r = 0; r < 4; ++r) {
v[r] = elt[r][c];
}
return v;
}
Matrix4 Matrix4::operator*(const Matrix4& other) const {
Matrix4 result;
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
for (int i = 0; i < 4; ++i) {
result.elt[r][c] += elt[r][i] * other.elt[i][c];
}
}
}
return result;
}
Matrix4 Matrix4::operator*(const float s) const {
Matrix4 result;
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
result.elt[r][c] = elt[r][c] * s;
}
}
return result;
}
Vector3 Matrix4::homoMul(const class Vector3& v, float w) const {
Vector4 r = (*this) * Vector4(v, w);
return r.xyz() * (1.0f / r.w);
}
Vector4 Matrix4::operator*(const Vector4& vector) const {
Vector4 result(0,0,0,0);
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
result[r] += elt[r][c] * vector[c];
}
}
return result;
}
Matrix4 Matrix4::transpose() const {
Matrix4 result;
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
result.elt[c][r] = elt[r][c];
}
}
return result;
}
bool Matrix4::operator!=(const Matrix4& other) const {
return ! (*this == other);
}
bool Matrix4::operator==(const Matrix4& other) const {
// If the bit patterns are identical, they must be
// the same matrix. If not, they *might* still have
// equal elements due to floating point weirdness.
if (memcmp(this, &other, sizeof(Matrix4)) == 0) {
return true;
}
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
if (elt[r][c] != other.elt[r][c]) {
return false;
}
}
}
return true;
}
float Matrix4::determinant() const {
// Determinant is the dot product of the first row and the first row
// of cofactors (i.e. the first col of the adjoint matrix)
return cofactor().row(0).dot(row(0));
}
Matrix4 Matrix4::adjoint() const {
return cofactor().transpose();
}
Matrix4 Matrix4::inverse() const {
// Inverse = adjoint / determinant
Matrix4 A = adjoint();
// Determinant is the dot product of the first row and the first row
// of cofactors (i.e. the first col of the adjoint matrix)
float det = A.column(0).dot(row(0));
return A * (1.0f / det);
}
Matrix4 Matrix4::cofactor() const {
Matrix4 out;
// We'll use i to incrementally compute -1 ^ (r+c)
int i = 1;
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
// Compute the determinant of the 3x3 submatrix
float det = subDeterminant(r, c);
out.elt[r][c] = i * det;
i = -i;
}
i = -i;
}
return out;
}
float Matrix4::subDeterminant(int excludeRow, int excludeCol) const {
// Compute non-excluded row and column indices
int row[3];
int col[3];
for (int i = 0; i < 3; ++i) {
row[i] = i;
col[i] = i;
if (i >= excludeRow) {
++row[i];
}
if (i >= excludeCol) {
++col[i];
}
}
// Compute the first row of cofactors
float cofactor00 =
elt[row[1]][col[1]] * elt[row[2]][col[2]] -
elt[row[1]][col[2]] * elt[row[2]][col[1]];
float cofactor10 =
elt[row[1]][col[2]] * elt[row[2]][col[0]] -
elt[row[1]][col[0]] * elt[row[2]][col[2]];
float cofactor20 =
elt[row[1]][col[0]] * elt[row[2]][col[1]] -
elt[row[1]][col[1]] * elt[row[2]][col[0]];
// Product of the first row and the cofactors along the first row
return
elt[row[0]][col[0]] * cofactor00 +
elt[row[0]][col[1]] * cofactor10 +
elt[row[0]][col[2]] * cofactor20;
}
CoordinateFrame Matrix4::approxCoordinateFrame() const {
CoordinateFrame cframe;
for (int r = 0; r < 3; ++r) {
for (int c = 0; c < 3; ++c) {
cframe.rotation[r][c] = elt[r][c];
}
cframe.translation[r] = elt[r][3];
}
// Ensure that the rotation matrix is orthonormal
cframe.rotation.orthonormalize();
return cframe;
}
void Matrix4::serialize(class BinaryOutput& b) const {
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
b.writeFloat32(elt[r][c]);
}
}
}
void Matrix4::deserialize(class BinaryInput& b) {
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
elt[r][c] = b.readFloat32();
}
}
}
std::string Matrix4::toString() const {
return G3D::format("[%g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g]",
elt[0][0], elt[0][1], elt[0][2], elt[0][3],
elt[1][0], elt[1][1], elt[1][2], elt[1][3],
elt[2][0], elt[2][1], elt[2][2], elt[2][3],
elt[3][0], elt[3][1], elt[3][2], elt[3][3]);
}
} // namespace

View File

@@ -0,0 +1,91 @@
/**
@file MemoryManager.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-04-20
@edited 2009-05-29
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/MemoryManager.h"
#include "G3D/System.h"
namespace G3D {
MemoryManager::MemoryManager() {}
void* MemoryManager::alloc(size_t s) {
return System::malloc(s);
}
void MemoryManager::free(void* ptr) {
System::free(ptr);
}
bool MemoryManager::isThreadsafe() const {
return true;
}
MemoryManager::Ref MemoryManager::create() {
static MemoryManager::Ref m = new MemoryManager();
return m;
}
///////////////////////////////////////////////////
AlignedMemoryManager::AlignedMemoryManager() {}
void* AlignedMemoryManager::alloc(size_t s) {
return System::alignedMalloc(s, 16);
}
void AlignedMemoryManager::free(void* ptr) {
System::alignedFree(ptr);
}
bool AlignedMemoryManager::isThreadsafe() const {
return true;
}
AlignedMemoryManager::Ref AlignedMemoryManager::create() {
static AlignedMemoryManager::Ref m = new AlignedMemoryManager();
return m;
}
///////////////////////////////////////////////////
CRTMemoryManager::CRTMemoryManager() {}
void* CRTMemoryManager::alloc(size_t s) {
return ::malloc(s);
}
void CRTMemoryManager::free(void* ptr) {
return ::free(ptr);
}
bool CRTMemoryManager::isThreadsafe() const {
return true;
}
CRTMemoryManager::Ref CRTMemoryManager::create() {
static CRTMemoryManager::Ref m = new CRTMemoryManager();
return m;
}
}

View File

@@ -0,0 +1,637 @@
/**
@file MeshAlg.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-09-14
@edited 2008-09-03
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/MeshAlg.h"
#include "G3D/Table.h"
#include "G3D/Set.h"
#include "G3D/Box.h"
#include "G3D/Sphere.h"
#include "G3D/vectorMath.h"
#include "G3D/AABox.h"
#include "G3D/Image1.h"
#include <climits>
namespace G3D {
const int MeshAlg::Face::NONE = INT_MIN;
void MeshAlg::generateGrid(
Array<Vector3>& vertex,
Array<Vector2>& texCoord,
Array<int>& index,
int wCells,
int hCells,
const Vector2& textureScale,
bool spaceCentered,
bool twoSided,
const CoordinateFrame& xform,
const Image1::Ref& height) {
vertex.fastClear();
texCoord.fastClear();
index.fastClear();
// Generate vertices
for (int z = 0; z <= hCells; ++z) {
for (int x = 0; x <= wCells; ++x) {
Vector3 v(x / (float)wCells, 0, z / (float)hCells);
Vector2 t = v.xz() * textureScale;
texCoord.append(t);
if (height.notNull()) {
v.y = height->nearest(v.x * (height->width() - 1), v.z * (height->height() - 1)).value;
}
if (spaceCentered) {
v -= Vector3(0.5f, 0, 0.5f);
}
v = xform.pointToWorldSpace(v);
vertex.append(v);
}
}
// Generate indices
for (int z = 0; z < hCells; ++z) {
for (int x = 0; x < wCells; ++x) {
int A = x + z * (wCells + 1);
int B = A + 1;
int C = A + (wCells + 1);
int D = C + 1;
// A B
// *-----*
// | \ |
// | \ |
// *-----*
// C D
index.append(A, D, B);
index.append(A, C, D);
}
}
if (twoSided) {
// The index array needs to have reversed winding for the bottom
// and offset by the original number of vertices
Array<int> ti = index;
ti.reverse();
for (int i = 0; i < ti.size(); ++i) {
ti[i] += vertex.size();
}
index.append(ti);
// Duplicate the arrays
vertex.append(Array<Vector3>(vertex));
texCoord.append(Array<Vector2>(texCoord));
}
}
MeshAlg::Face::Face() {
for (int i = 0; i < 3; ++i) {
edgeIndex[i] = 0;
vertexIndex[i] = 0;
}
}
MeshAlg::Edge::Edge() {
for (int i = 0; i < 2; ++i) {
vertexIndex[i] = 0;
// Negative face indices are faces that don't exist
faceIndex[i] = -1;
}
}
MeshAlg::Geometry& MeshAlg::Geometry::operator=(const MeshAlg::Geometry& src) {
vertexArray.resize(src.vertexArray.size());
normalArray.resize(src.vertexArray.size());
System::memcpy(vertexArray.getCArray(), src.vertexArray.getCArray(), sizeof(Vector3)*vertexArray.size());
System::memcpy(normalArray.getCArray(), src.normalArray.getCArray(), sizeof(Vector3)*normalArray.size());
return *this;
}
void MeshAlg::computeNormals(
Geometry& geometry,
const Array<int>& indexArray) {
Array<Face> faceArray;
Array<Vertex> vertexArray;
Array<Edge> edgeArray;
Array<Vector3> faceNormalArray;
computeAdjacency(geometry.vertexArray, indexArray, faceArray, edgeArray, vertexArray);
computeNormals(geometry.vertexArray, faceArray, vertexArray,
geometry.normalArray, faceNormalArray);
}
void MeshAlg::computeNormals(
const Array<Vector3>& vertexGeometry,
const Array<Face>& faceArray,
const Array< Array<int> >& adjacentFaceArray,
Array<Vector3>& vertexNormalArray,
Array<Vector3>& faceNormalArray) {
// Construct a fake vertex array for backwards compatibility
Array<Vertex> fakeVertexArray;
fakeVertexArray.resize(adjacentFaceArray.size());
for (int v = 0; v < adjacentFaceArray.size(); ++v) {
fakeVertexArray[v].faceIndex.resize(adjacentFaceArray[v].size());
for (int i = 0; i < fakeVertexArray[v].faceIndex.size(); ++i) {
fakeVertexArray[v].faceIndex[i] = adjacentFaceArray[v][i];
}
// We leave out the edges because they aren't used to compute normals
}
computeNormals(vertexGeometry, faceArray, fakeVertexArray,
vertexNormalArray, faceNormalArray);
}
void MeshAlg::computeNormals(
const Array<Vector3>& vertexGeometry,
const Array<Face>& faceArray,
const Array<Vertex>& vertexArray,
Array<Vector3>& vertexNormalArray,
Array<Vector3>& faceNormalArray) {
// Face normals (not unit length)
faceNormalArray.resize(faceArray.size());
for (int f = 0; f < faceArray.size(); ++f) {
const Face& face = faceArray[f];
Vector3 vertex[3];
for (int j = 0; j < 3; ++j) {
vertex[j] = vertexGeometry[face.vertexIndex[j]];
debugAssert(vertex[j].isFinite());
}
faceNormalArray[f] = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
# ifdef G3D_DEBUG
const Vector3& N = faceNormalArray[f];
debugAssert(N.isFinite());
# endif
}
// Per-vertex normals, computed by averaging
vertexNormalArray.resize(vertexGeometry.size());
for (int v = 0; v < vertexNormalArray.size(); ++v) {
Vector3 sum = Vector3::zero();
for (int k = 0; k < vertexArray[v].faceIndex.size(); ++k) {
const int f = vertexArray[v].faceIndex[k];
sum += faceNormalArray[f];
}
vertexNormalArray[v] = sum.directionOrZero();
# ifdef G3D_DEBUG
const Vector3& N = vertexNormalArray[v];
debugAssert(N.isUnit() || N.isZero());
# endif
}
for (int f = 0; f < faceArray.size(); ++f) {
faceNormalArray[f] = faceNormalArray[f].directionOrZero();
# ifdef G3D_DEBUG
const Vector3& N = faceNormalArray[f];
debugAssert(N.isUnit() || N.isZero());
# endif
}
}
void MeshAlg::computeFaceNormals(
const Array<Vector3>& vertexArray,
const Array<MeshAlg::Face>& faceArray,
Array<Vector3>& faceNormals,
bool normalize) {
faceNormals.resize(faceArray.size());
for (int f = 0; f < faceArray.size(); ++f) {
const MeshAlg::Face& face = faceArray[f];
const Vector3& v0 = vertexArray[face.vertexIndex[0]];
const Vector3& v1 = vertexArray[face.vertexIndex[1]];
const Vector3& v2 = vertexArray[face.vertexIndex[2]];
faceNormals[f] = (v1 - v0).cross(v2 - v0);
}
if (normalize) {
for (int f = 0; f < faceArray.size(); ++f) {
faceNormals[f] = faceNormals[f].direction();
}
}
}
void MeshAlg::identifyBackfaces(
const Array<Vector3>& vertexArray,
const Array<MeshAlg::Face>& faceArray,
const Vector4& HP,
Array<bool>& backface) {
Vector3 P = HP.xyz();
backface.resize(faceArray.size());
if (fuzzyEq(HP.w, 0.0)) {
// Infinite case
for (int f = faceArray.size() - 1; f >= 0; --f) {
const MeshAlg::Face& face = faceArray[f];
const Vector3& v0 = vertexArray[face.vertexIndex[0]];
const Vector3& v1 = vertexArray[face.vertexIndex[1]];
const Vector3& v2 = vertexArray[face.vertexIndex[2]];
const Vector3 N = (v1 - v0).cross(v2 - v0);
backface[f] = N.dot(P) < 0;
}
} else {
// Finite case
for (int f = faceArray.size() - 1; f >= 0; --f) {
const MeshAlg::Face& face = faceArray[f];
const Vector3& v0 = vertexArray[face.vertexIndex[0]];
const Vector3& v1 = vertexArray[face.vertexIndex[1]];
const Vector3& v2 = vertexArray[face.vertexIndex[2]];
const Vector3 N = (v1 - v0).cross(v2 - v0);
backface[f] = N.dot(P - v0) < 0;
}
}
}
void MeshAlg::identifyBackfaces(
const Array<Vector3>& vertexArray,
const Array<MeshAlg::Face>& faceArray,
const Vector4& HP,
Array<bool>& backface,
const Array<Vector3>& faceNormals) {
Vector3 P = HP.xyz();
backface.resize(faceArray.size());
if (fuzzyEq(HP.w, 0.0)) {
// Infinite case
for (int f = faceArray.size() - 1; f >= 0; --f) {
const Vector3& N = faceNormals[f];
backface[f] = N.dot(P) < 0;
}
} else {
// Finite case
for (int f = faceArray.size() - 1; f >= 0; --f) {
const MeshAlg::Face& face = faceArray[f];
const Vector3& v0 = vertexArray[face.vertexIndex[0]];
const Vector3& N = faceNormals[f];
backface[f] = N.dot(P - v0) < 0;
}
}
}
void MeshAlg::createIndexArray(int n, Array<int>& array, int start, int run, int skip) {
debugAssert(skip >= 0);
debugAssert(run >= 0);
array.resize(n);
if (skip == 0) {
for (int i = 0; i < n; ++i) {
array[i] = start + i;
}
} else {
int rcount = 0;
int j = start;
for (int i = 0; i < n; ++i) {
array[i] = j;
++j;
++rcount;
if (rcount == run) {
rcount = 0;
j += skip;
}
}
}
}
void MeshAlg::computeAreaStatistics(
const Array<Vector3>& vertexArray,
const Array<int>& indexArray,
double& minEdgeLength,
double& meanEdgeLength,
double& medianEdgeLength,
double& maxEdgeLength,
double& minFaceArea,
double& meanFaceArea,
double& medianFaceArea,
double& maxFaceArea) {
debugAssert(indexArray.size() % 3 == 0);
Array<double> area;
area.resize(indexArray.size() / 3);
Array<double> magnitude;
magnitude.resize(indexArray.size());
for (int i = 0; i < indexArray.size(); i += 3) {
const Vector3& v0 = vertexArray[indexArray[i]];
const Vector3& v1 = vertexArray[indexArray[i + 1]];
const Vector3& v2 = vertexArray[indexArray[i + 2]];
area[i / 3] = (v1 - v0).cross(v2 - v0).magnitude() / 2.0;
magnitude[i] = (v1 - v0).magnitude();
magnitude[i + 1] = (v2 - v1).magnitude();
magnitude[i + 2] = (v0 - v2).magnitude();
}
area.sort();
magnitude.sort();
minEdgeLength = max(0.0, magnitude[0]);
maxEdgeLength = max(0.0, magnitude.last());
medianEdgeLength = max(0.0, magnitude[magnitude.size() / 2]);
meanEdgeLength = 0;
for (int i = 0; i < magnitude.size(); ++i) {
meanEdgeLength += magnitude[i];
}
meanEdgeLength /= magnitude.size();
minFaceArea = max(0.0, area[0]);
maxFaceArea = max(0.0, area.last());
medianFaceArea = max(0.0, area[area.size() / 2]);
meanFaceArea = 0;
for (int i = 0; i < area.size(); ++i) {
meanFaceArea += area[i];
}
meanFaceArea /= area.size();
// Make sure round-off hasn't pushed values less than zero
meanFaceArea = max(0.0, meanFaceArea);
meanEdgeLength = max(0.0, meanEdgeLength);
}
int MeshAlg::countBoundaryEdges(const Array<MeshAlg::Edge>& edgeArray) {
int b = 0;
for (int i = 0; i < edgeArray.size(); ++i) {
if ((edgeArray[i].faceIndex[0] == MeshAlg::Face::NONE) !=
(edgeArray[i].faceIndex[1] == MeshAlg::Face::NONE)) {
++b;
}
}
return b;
}
void MeshAlg::computeBounds(
const Array<Vector3>& vertexArray,
const Array<int>& indexArray,
AABox& box,
Sphere& sphere) {
Array<Vector3> newArray;
newArray.resize(indexArray.size());
for (int i = 0; i < indexArray.size(); ++i) {
newArray[i] = vertexArray[indexArray[i]];
}
computeBounds(newArray, box, sphere);
}
void MeshAlg::computeBounds(
const Array<Vector3>& vertexArray,
AABox& box,
Sphere& sphere) {
Vector3 xmin, xmax, ymin, ymax, zmin, zmax;
// FIRST PASS: find 6 minima/maxima points
xmin.x = ymin.y = zmin.z = finf();
xmax.x = ymax.y = zmax.z = -finf();
for (int v = 0; v < vertexArray.size(); ++v) {
const Vector3& vertex = vertexArray[v];
if (vertex.x < xmin.x) {
xmin = vertex;
}
if (vertex.x > xmax.x) {
xmax = vertex;
}
if (vertex.y < ymin.y) {
ymin = vertex;
}
if (vertex.y > ymax.y) {
ymax = vertex;
}
if (vertex.z < zmin.z) {
zmin = vertex;
}
if (vertex.z > zmax.z) {
zmax = vertex;
}
}
// Set points dia1 & dia2 to the maximally separated pair
Vector3 dia1 = xmin;
Vector3 dia2 = xmax;
{
// Set xspan = distance between the 2 points xmin & xmax (squared)
double xspan = (xmax - xmin).squaredMagnitude();
// Same for y & z spans
double yspan = (ymax - ymin).squaredMagnitude();
double zspan = (zmax - zmin).squaredMagnitude();
double maxspan = xspan;
if (yspan > maxspan) {
maxspan = yspan;
dia1 = ymin;
dia2 = ymax;
}
if (zspan > maxspan) {
maxspan = zspan;
dia1 = zmin;
dia2 = zmax;
}
}
// dia1, dia2 is a diameter of initial sphere
// calc initial center
Vector3 center = (dia1 + dia2) / 2.0;
// calculate initial radius^2 and radius
Vector3 d = dia2 - sphere.center;
double radSq = d.squaredMagnitude();
double rad = sqrt(radSq);
// SECOND PASS: increment current sphere
double old_to_p, old_to_new;
for (int v = 0; v < vertexArray.size(); ++v) {
const Vector3& vertex = vertexArray[v];
d = vertex - center;
double old_to_p_sq = d.squaredMagnitude();
// do r^2 test first
if (old_to_p_sq > radSq) {
// this point is outside of current sphere
old_to_p = sqrt(old_to_p_sq);
// calc radius of new sphere
rad = (rad + old_to_p) / 2.0;
// for next r^2 compare
radSq = rad * rad;
old_to_new = old_to_p - rad;
// calc center of new sphere
center = (rad * center + old_to_new * vertex) / old_to_p;
}
}
const Vector3 min(xmin.x, ymin.y, zmin.z);
const Vector3 max(xmax.x, ymax.y, zmax.z);
box = AABox(min, max);
const float boxRadSq = (max - min).squaredMagnitude() * 0.25f;
if (boxRadSq >= radSq){
if (isNaN(center.x) || ! isFinite(rad)) {
sphere = Sphere(Vector3::zero(), finf());
} else {
sphere = Sphere(center, rad);
}
} else {
sphere = Sphere((max + min) * 0.5f, sqrt(boxRadSq));
}
}
void MeshAlg::computeTangentSpaceBasis(
const Array<Vector3>& vertexArray,
const Array<Vector2>& texCoordArray,
const Array<Vector3>& vertexNormalArray,
const Array<Face>& faceArray,
Array<Vector3>& tangent,
Array<Vector3>& binormal) {
debugAssertM(faceArray.size() != 0, "Unable to calculate valid tangent space without faces.");
tangent.resize(vertexArray.size());
binormal.resize(vertexArray.size());
// Zero the output arrays.
System::memset(tangent.getCArray(), 0, sizeof(Vector3) * tangent.size());
System::memset(binormal.getCArray(), 0, sizeof(Vector3) * binormal.size());
// Iterate over faces, computing the tangent vectors for each
// vertex. Accumulate those into the tangent and binormal arrays
// and then orthonormalize at the end.
for (int f = 0; f < faceArray.size(); ++f) {
const Face& face = faceArray[f];
const int i0 = face.vertexIndex[0];
const int i1 = face.vertexIndex[1];
const int i2 = face.vertexIndex[2];
const Vector3& v0 = vertexArray[i0];
const Vector3& v1 = vertexArray[i1];
const Vector3& v2 = vertexArray[i2];
const Vector2& t0 = texCoordArray[i0];
const Vector2& t1 = texCoordArray[i1];
const Vector2& t2 = texCoordArray[i2];
// See http://www.terathon.com/code/tangent.html for a derivation of the following code
// vertex edges
Vector3 ve1 = v1 - v0;
Vector3 ve2 = v2 - v0;
// texture edges
Vector2 te1 = t1 - t0;
Vector2 te2 = t2 - t0;
Vector3 n(ve1.cross(ve2).direction());
Vector3 t, b;
float r = te1.x * te2.y - te1.y * te2.x;
if (r == 0.0) {
// degenerate case
Vector3::generateOrthonormalBasis(t, b, n, true);
} else {
r = 1.0f / r;
t = (te2.y * ve1 - te1.y * ve2) * r;
b = (te2.x * ve1 - te1.x * ve2) * r;
}
for (int v = 0; v < 3; ++v) {
int i = face.vertexIndex[v];
tangent[i] += t;
binormal[i] += b;
}
}
// Normalize the basis vectors
for (int v = 0; v < vertexArray.size(); ++v) {
// Remove the component parallel to the normal
const Vector3& N = vertexNormalArray[v];
Vector3& T = tangent[v];
Vector3& B = binormal[v];
debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length");
T -= T.dot(N) * N;
B -= B.dot(N) * N;
// Normalize
T = T.directionOrZero();
B = B.directionOrZero();
}
}
} // G3D namespace

View File

@@ -0,0 +1,745 @@
/**
@file MeshAlgAdjacency.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-09-14
@edited 2010-04-26
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Table.h"
#include "G3D/MeshAlg.h"
#include "G3D/Set.h"
#include "G3D/Stopwatch.h"
#include "G3D/SmallArray.h"
#include "G3D/AreaMemoryManager.h"
namespace G3D {
/** Two-level table mapping index 0 -> index 1 -> list of face indices */
class MeshEdgeTable {
public:
/** We expect 2 faces per edge. */
typedef SmallArray<int, 2> FaceIndexArray;
class Edge {
public:
int i1;
FaceIndexArray faceIndexArray;
};
/** We expect at most 6 edges per vertex; that matches a typical regular grid mesh */
typedef SmallArray<Edge, 6> EdgeArray;
typedef Array< EdgeArray > ET;
private:
ET table;
public:
MeshEdgeTable() {
AreaMemoryManager::Ref mm = AreaMemoryManager::create();
table.clearAndSetMemoryManager(mm);
}
void clear() {
table.clear();
}
void resize(int maxV) {
table.resize(maxV);
}
/**
Inserts the faceIndex into the edge's face list.
The index may be a negative number indicating a backface.
\param v0 Vertex index 0
\param v1 Vertex index 1
*/
void insert(int v0, int v1, int faceIndex) {
debugAssert(v0 <= v1);
EdgeArray& edgeArray = table[v0];
for (int i = 0; i < edgeArray.size(); ++i) {
if (edgeArray[i].i1 == v1) {
edgeArray[i].faceIndexArray.push(faceIndex);
return;
}
}
Edge& p = edgeArray.next();
p.i1 = v1;
p.faceIndexArray.push(faceIndex);
}
class Iterator {
friend class MeshEdgeTable;
private:
int m_i0;
/** Pair index */
int m_p;
ET& m_array;
EdgeArray* m_edgeArray;
bool m_end;
public:
int i0() const {
return m_i0;
}
int i1() const {
return (*m_edgeArray)[m_p].i1;
}
FaceIndexArray& faceIndex() {
return (*m_edgeArray)[m_p].faceIndexArray;
}
Iterator& operator++() {
if ((m_i0 >= 0) && (m_p < m_edgeArray->size() - 1)) {
++m_p;
} else {
// Skip over elements with no face array
do {
++m_i0;
if (m_i0 == m_array.size()) {
m_end = true;
return *this;
} else {
m_edgeArray = &m_array[m_i0];
m_p = 0;
}
} while (m_edgeArray->size() == 0);
}
return *this;
}
bool hasMore() const {
return ! m_end;
}
private:
Iterator(ET& a) : m_i0(-1), m_p(-1), m_array(a), m_edgeArray(NULL), m_end(false) {
++(*this);
}
};
Iterator begin() {
return Iterator(table);
}
};
/**
Assigns the edge index into the next unassigned edge
index. The edge index may be negative, indicating
a reverse edge.
*/
static void assignEdgeIndex(MeshAlg::Face& face, int e) {
for (int i = 0; i < 3; ++i) {
if (face.edgeIndex[i] == MeshAlg::Face::NONE) {
face.edgeIndex[i] = e;
return;
}
}
debugAssertM(false, "Face has already been assigned 3 edges");
}
void MeshAlg::computeAdjacency(
const Array<Vector3>& vertexGeometry,
const Array<int>& indexArray,
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array< Array<int> >& adjacentFaceArray) {
Array<Vertex> vertexArray;
computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
// Convert the vertexArray into adjacentFaceArray
adjacentFaceArray.clear();
adjacentFaceArray.resize(vertexArray.size());
for (int v = 0; v < adjacentFaceArray.size(); ++v) {
const SmallArray<int, 6>& src = vertexArray[v].faceIndex;
Array<int>& dst = adjacentFaceArray[v];
dst.resize(src.size());
for (int f = 0; f < dst.size(); ++f) {
dst[f] = src[f];
}
}
}
void MeshAlg::computeAdjacency(
const Array<Vector3>& vertexGeometry,
const Array<int>& indexArray,
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array<Vertex>& vertexArray) {
MeshEdgeTable edgeTable;
edgeArray.clear();
vertexArray.clear();
faceArray.clear();
// Face normals
Array<Vector3> faceNormal;
faceNormal.resize(indexArray.size() / 3);
faceArray.resize(faceNormal.size());
// This array has the same size as the vertex array
vertexArray.resize(vertexGeometry.size());
edgeTable.resize(vertexArray.size());
// Iterate through the triangle list
for (int q = 0, f = 0; q < indexArray.size(); ++f, q += 3) {
Vector3 vertex[3];
MeshAlg::Face& face = faceArray[f];
// Construct the face
for (int j = 0; j < 3; ++j) {
int v = indexArray[q + j];
face.vertexIndex[j] = v;
face.edgeIndex[j] = Face::NONE;
// Store back pointers in the vertices
vertexArray[v].faceIndex.append(f);
// We'll need these vertices to find the face normal
vertex[j] = vertexGeometry[v];
}
// Compute the face normal
const Vector3& N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
faceNormal[f] = N.directionOrZero();
static const int nextIndex[] = {1, 2, 0};
// Add each edge to the edge table.
for (int j = 0; j < 3; ++j) {
const int i0 = indexArray[q + j];
const int i1 = indexArray[q + nextIndex[j]];
if (i0 < i1) {
// The edge was directed in the same manner as in the face
edgeTable.insert(i0, i1, f);
} else {
// The edge was directed in the opposite manner as in the face
edgeTable.insert(i1, i0, ~f);
}
}
}
// For each edge in the edge table, create an edge in the edge array.
// Collapse every 2 edges from adjacent faces.
MeshEdgeTable::Iterator cur = edgeTable.begin();
Array<Edge> tempEdgeArray;
while (cur.hasMore()) {
MeshEdgeTable::FaceIndexArray& faceIndexArray = cur.faceIndex();
// Process this edge
while (faceIndexArray.size() > 0) {
// Remove the last index
int f0 = faceIndexArray.pop();
// Find the normal to that face
const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
bool found = false;
// We try to find the matching face with the closest
// normal. This ensures that we don't introduce a lot
// of artificial ridges into flat parts of a mesh.
float ndotn = -2;
int f1 = -1, i1 = -1;
// Try to find the face with the matching edge
for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
int f = faceIndexArray[i];
if ((f >= 0) != (f0 >= 0)) {
// This face contains the oppositely oriented edge
// and has not been assigned too many edges
const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
float d = n1.dot(n0);
if (found) {
// We previously found a good face; see if this
// one is better.
if (d > ndotn) {
// This face is better.
ndotn = d;
f1 = f;
i1 = i;
}
} else {
// This is the first face we've found
found = true;
ndotn = d;
f1 = f;
i1 = i;
}
}
}
// Create the new edge
int e = tempEdgeArray.size();
Edge& edge = tempEdgeArray.next();
edge.vertexIndex[0] = cur.i0();
edge.vertexIndex[1] = cur.i1();
if (f0 >= 0) {
edge.faceIndex[0] = f0;
edge.faceIndex[1] = Face::NONE;
assignEdgeIndex(faceArray[f0], e);
} else {
// The face indices above are two's complemented.
// this code restores them to regular indices.
debugAssert((~f0) >= 0);
edge.faceIndex[1] = ~f0;
edge.faceIndex[0] = Face::NONE;
// The edge index *does* need to be inverted, however.
assignEdgeIndex(faceArray[~f0], ~e);
}
if (found) {
// We found a matching face; remove both
// faces from the active list.
faceIndexArray.fastRemove(i1);
if (f1 >= 0) {
edge.faceIndex[0] = f1;
assignEdgeIndex(faceArray[f1], e);
} else {
edge.faceIndex[1] = ~f1;
assignEdgeIndex(faceArray[~f1], ~e);
}
}
}
++cur;
}
edgeTable.clear();
// Move boundary edges to the end of the list and then
// clean up the face references into them
{
// Map old edge indices to new edge indices
Array<int> newIndex;
newIndex.resize(tempEdgeArray.size());
// Index of the start and end of the edge array
int i = 0;
int j = tempEdgeArray.size() - 1;
edgeArray.resize(tempEdgeArray.size());
for (int e = 0; e < tempEdgeArray.size(); ++e) {
if (tempEdgeArray[e].boundary()) {
newIndex[e] = j;
--j;
} else {
newIndex[e] = i;
++i;
}
edgeArray[newIndex[e]] = tempEdgeArray[e];
}
debugAssertM(i == j + 1, "Counting from front and back of array did not match");
// Fix the faces
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
for (int q = 0; q < 3; ++q) {
int e = face.edgeIndex[q];
if (e < 0) {
// Backwards edge; twiddle before and after conversion
face.edgeIndex[q] = ~newIndex[~e];
} else {
// Regular edge; remap the index
face.edgeIndex[q] = newIndex[e];
}
}
}
}
// Now order the edge indices inside the faces correctly.
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
int e0 = face.edgeIndex[0];
int e1 = face.edgeIndex[1];
int e2 = face.edgeIndex[2];
// e0 will always remain first. The only
// question is whether e1 and e2 should be swapped.
// See if e1 begins at the vertex where e1 ends.
const int e0End = (e0 < 0) ?
edgeArray[~e0].vertexIndex[0] :
edgeArray[e0].vertexIndex[1];
const int e1Begin = (e1 < 0) ?
edgeArray[~e1].vertexIndex[1] :
edgeArray[e1].vertexIndex[0];
if (e0End != e1Begin) {
// We must swap e1 and e2
face.edgeIndex[1] = e2;
face.edgeIndex[2] = e1;
}
}
// Fill out the edge adjacency information in the vertex array
for (int e = 0; e < edgeArray.size(); ++e) {
const Edge& edge = edgeArray[e];
vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
}
}
void MeshAlg::weldBoundaryEdges(
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array<Vertex>& vertexArray) {
// Copy over the original edge array
Array<Edge> oldEdgeArray = edgeArray;
// newEdgeIndex[e] is the new index of the old edge with index e
// Note that newEdgeIndex[e] might be negative, indicating that
// the edge switched direction between the arrays.
Array<int> newEdgeIndex;
newEdgeIndex.resize(edgeArray.size());
edgeArray.resize(0);
// boundaryEdgeIndices[v_low] is an array of the indices of
// all boundary edges whose lower vertex is v_low.
Table<int, Array<int> > boundaryEdgeIndices;
// Copy over non-boundary edges to the new array
for (int e = 0; e < oldEdgeArray.size(); ++e) {
if (oldEdgeArray[e].boundary()) {
// Add to the boundary table
const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
if (! boundaryEdgeIndices.containsKey(v_low)) {
boundaryEdgeIndices.set(v_low, Array<int>());
}
boundaryEdgeIndices[v_low].append(e);
// We'll fill out newEdgeIndex[e] later, when we find pairs
} else {
// Copy the edge to the new array
newEdgeIndex[e] = edgeArray.size();
edgeArray.append(oldEdgeArray[e]);
}
}
// Remove all edges from the table that have pairs.
Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
while (cur != end) {
Array<int>& boundaryEdge = cur->value;
for (int i = 0; i < boundaryEdge.size(); ++i) {
int ei = boundaryEdge[i];
const Edge& edgei = oldEdgeArray[ei];
for (int j = i + 1; j < boundaryEdge.size(); ++j) {
int ej = boundaryEdge[j];
const Edge& edgej = oldEdgeArray[ej];
// See if edge ei is the reverse (match) of edge ej.
// True if the edges match
bool match = false;
// True if edgej's vertex indices are reversed from
// edgei's (usually true).
bool reversej = false;
int u = edgei.vertexIndex[0];
int v = edgei.vertexIndex[1];
if (edgei.faceIndex[0] != Face::NONE) {
// verts|faces
// edgei = [u v A /]
if (edgej.faceIndex[0] != Face::NONE) {
if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
// This is the most common of the four cases
// edgej = [v u B /]
match = true;
reversej = true;
}
} else {
if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
// edgej = [u v / B]
match = true;
}
}
} else {
// edgei = [u v / A]
if (edgej.faceIndex[0] != Face::NONE) {
if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
// edgej = [u v B /]
match = true;
}
} else {
if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
// edgej = [v u / B]
match = true;
reversej = true;
}
}
}
if (match) {
// ei and ej can be paired as a single edge
int e = edgeArray.size();
Edge& edge = edgeArray.next();
// Follow the direction of edgei.
edge = edgei;
newEdgeIndex[ei] = e;
// Insert the face index for edgej.
int fj = edgej.faceIndex[0];
if (fj == Face::NONE) {
fj = edgej.faceIndex[1];
}
if (edge.faceIndex[0] == Face::NONE) {
edge.faceIndex[0] = fj;
} else {
edge.faceIndex[1] = fj;
}
if (reversej) {
// The new edge is backwards of the old edge for ej
newEdgeIndex[ej] = ~e;
} else {
newEdgeIndex[ej] = e;
}
// Remove both ei and ej from being candidates for future pairing.
// Remove ej first since it comes later in the list (removing
// ei would decrease the index of ej to j - 1).
boundaryEdge.fastRemove(j);
boundaryEdge.fastRemove(i);
// Re-process element i, which is now a new edge index
--i;
// Jump out of the j for-loop
break;
}
}
}
++cur;
}
// Anything remaining in the table is a real boundary edge; just copy it to
// the end of the array.
cur = boundaryEdgeIndices.begin();
end = boundaryEdgeIndices.end();
while (cur != end) {
Array<int>& boundaryEdge = cur->value;
for (int b = 0; b < boundaryEdge.size(); ++b) {
const int e = boundaryEdge[b];
newEdgeIndex[e] = edgeArray.size();
edgeArray.append(oldEdgeArray[e]);
}
++cur;
}
// Finally, fix up edge indices in the face and vertex arrays
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
for (int i = 0; i < 3; ++i) {
int e = face.edgeIndex[i];
if (e < 0) {
face.edgeIndex[i] = ~newEdgeIndex[~e];
} else {
face.edgeIndex[i] = newEdgeIndex[e];
}
}
}
for (int v = 0; v < vertexArray.size(); ++v) {
Vertex& vertex = vertexArray[v];
for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
int e = vertex.edgeIndex[i];
if (e < 0) {
vertex.edgeIndex[i] = ~newEdgeIndex[~e];
} else {
vertex.edgeIndex[i] = newEdgeIndex[e];
}
}
}
}
void MeshAlg::weldAdjacency(
const Array<Vector3>& originalGeometry,
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array<Vertex>& vertexArray,
double radius) {
// Num vertices
const int n = originalGeometry.size();
// canonical[v] = first occurance of any vertex near oldVertexArray[v]
Array<int> canonical;
canonical.resize(n);
Array<int> toNew, toOld;
// Throw away the new vertex array
Array<Vector3> dummy;
computeWeld(originalGeometry, dummy, toNew, toOld, radius);
for (int v = 0; v < canonical.size(); ++v) {
// Round-trip through the toNew/toOld process. This will give
// us the original vertex.
canonical[v] = toOld[toNew[v]];
}
// Destroy vertexArray (we reconstruct it below)
vertexArray.clear();
vertexArray.resize(n);
bool hasBoundaryEdges = false;
// Fix edge vertex indices
for (int e = 0; e < edgeArray.size(); ++e) {
Edge& edge = edgeArray[e];
const int v0 = canonical[edge.vertexIndex[0]];
const int v1 = canonical[edge.vertexIndex[1]];
edge.vertexIndex[0] = v0;
edge.vertexIndex[1] = v1;
vertexArray[v0].edgeIndex.append(e);
vertexArray[v1].edgeIndex.append(~e);
hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
}
// Fix face vertex indices
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
for (int i = 0; i < 3; ++i) {
const int v = canonical[face.vertexIndex[i]];
face.vertexIndex[i] = v;
// Add the back pointer
vertexArray[v].faceIndex.append(f);
}
}
if (hasBoundaryEdges) {
// As a result of the welding, some of the boundary edges at
// the end of the array may now have mates and no longer be
// boundaries. Try to pair these up.
weldBoundaryEdges(faceArray, edgeArray, vertexArray);
}
}
void MeshAlg::debugCheckConsistency(
const Array<Face>& faceArray,
const Array<Edge>& edgeArray,
const Array<Vertex>& vertexArray) {
#ifdef _DEBUG
for (int v = 0; v < vertexArray.size(); ++v) {
const MeshAlg::Vertex& vertex = vertexArray[v];
for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
const int e = vertex.edgeIndex[i];
debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
}
for (int i = 0; i < vertex.faceIndex.size(); ++i) {
const int f = vertex.faceIndex[i];
debugAssert(faceArray[f].containsVertex(v));
}
}
for (int e = 0; e < edgeArray.size(); ++e) {
const MeshAlg::Edge& edge = edgeArray[e];
for (int i = 0; i < 2; ++i) {
debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
faceArray[edge.faceIndex[i]].containsEdge(e));
debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
}
}
// Every face's edge must be on that face
for (int f = 0; f < faceArray.size(); ++f) {
const MeshAlg::Face& face = faceArray[f];
for (int i = 0; i < 3; ++i) {
int e = face.edgeIndex[i];
int ei = (e >= 0) ? e : ~e;
debugAssert(edgeArray[ei].inFace(f));
// Make sure the edge is oriented appropriately
if (e >= 0) {
debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
} else {
debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
}
debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
}
}
#else
(void)faceArray;
(void)edgeArray;
(void)vertexArray;
#endif // _DEBUG
}
} // G3D namespace

View File

@@ -0,0 +1,213 @@
/**
@file MeshAlgWeld.cpp
The MeshAlg::computeWeld method.
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-10-22
@edited 2005-02-24
Copyright 2000-2003, Morgan McGuire.
All rights reserved.
*/
#include "G3D/MeshAlg.h"
#include "G3D/Table.h"
#include "G3D/Set.h"
namespace G3D {
namespace _internal {
class Welder {
private:
// Intentionally illegal
Welder& operator=(const Welder& w);
public:
/** Indices of newVertexArray elements in <B>or near</B> a grid cell. */
typedef Array<int> List;
enum {GRID_RES = 32};
List grid[GRID_RES][GRID_RES][GRID_RES];
const Array<Vector3>& oldVertexArray;
Array<Vector3>& newVertexArray;
Array<int>& toNew;
Array<int>& toOld;
/** Must be less than one grid cell, not checked */
const double radius;
/** (oldVertexArray[i] - offset) * scale is on the range [0, 1] */
Vector3 offset;
Vector3 scale;
Welder(
const Array<Vector3>& _oldVertexArray,
Array<Vector3>& _newVertexArray,
Array<int>& _toNew,
Array<int>& _toOld,
double _radius);
/**
Computes the grid index from an ordinate.
*/
void toGridCoords(Vector3 v, int& x, int& y, int& z) const;
/** Gets the index of a vertex, adding it to
newVertexArray if necessary. */
int getIndex(const Vector3& vertex);
void weld();
};
} // namespace _internal
} // namespace G3D
template<> struct HashTrait<G3D::_internal::Welder::List*> {
static size_t hashCode(const G3D::_internal::Welder::List* key) { return reinterpret_cast<size_t>(key); }
};
namespace G3D {
namespace _internal {
Welder::Welder(
const Array<Vector3>& _oldVertexArray,
Array<Vector3>& _newVertexArray,
Array<int>& _toNew,
Array<int>& _toOld,
double _radius) :
oldVertexArray(_oldVertexArray),
newVertexArray(_newVertexArray),
toNew(_toNew),
toOld(_toOld),
radius(_radius) {
// Compute a scale factor that moves the range
// of all ordinates to [0, 1]
Vector3 minBound = Vector3::inf();
Vector3 maxBound = -minBound;
for (int i = 0; i < oldVertexArray.size(); ++i) {
minBound = minBound.min(oldVertexArray[i]);
maxBound = maxBound.max(oldVertexArray[i]);
}
offset = minBound;
scale = maxBound - minBound;
for (int i = 0; i < 3; ++i) {
// The model might have zero extent along some axis
if (fuzzyEq(scale[i], 0.0)) {
scale[i] = 1.0;
} else {
scale[i] = 1.0 / scale[i];
}
}
}
void Welder::toGridCoords(Vector3 v, int& x, int& y, int& z) const {
v = (v - offset) * scale;
x = iClamp(iFloor(v.x * GRID_RES), 0, GRID_RES - 1);
y = iClamp(iFloor(v.y * GRID_RES), 0, GRID_RES - 1);
z = iClamp(iFloor(v.z * GRID_RES), 0, GRID_RES - 1);
}
int Welder::getIndex(const Vector3& vertex) {
int closestIndex = -1;
double distanceSquared = inf();
int ix, iy, iz;
toGridCoords(vertex, ix, iy, iz);
// Check against all vertices within radius of this grid cube
const List& list = grid[ix][iy][iz];
for (int i = 0; i < list.size(); ++i) {
double d = (newVertexArray[list[i]] - vertex).squaredMagnitude();
if (d < distanceSquared) {
distanceSquared = d;
closestIndex = list[i];
}
}
if (distanceSquared <= radius * radius) {
return closestIndex;
} else {
// This is a new vertex
int newIndex = newVertexArray.size();
newVertexArray.append(vertex);
// Create a new vertex and store its index in the
// neighboring grid cells (usually, only 1 neighbor)
Set<List*> neighbors;
for (float dx = -1; dx <= +1; ++dx) {
for (float dy = -1; dy <= +1; ++dy) {
for (float dz = -1; dz <= +1; ++dz) {
int ix, iy, iz;
toGridCoords(vertex + Vector3(dx, dy, dz) * radius, ix, iy, iz);
neighbors.insert(&(grid[ix][iy][iz]));
}
}
}
Set<List*>::Iterator neighbor(neighbors.begin());
Set<List*>::Iterator none(neighbors.end());
while (neighbor != none) {
(*neighbor)->append(newIndex);
++neighbor;
}
return newIndex;
}
}
void Welder::weld() {
newVertexArray.resize(0);
// Prime the vertex positions
for (int i = 0; i < oldVertexArray.size(); ++i) {
getIndex(oldVertexArray[i]);
}
// Now create the official remapping by snapping to
// nearby vertices.
toNew.resize(oldVertexArray.size());
toOld.resize(newVertexArray.size());
for (int oi = 0; oi < oldVertexArray.size(); ++oi) {
toNew[oi] = getIndex(oldVertexArray[oi]);
toOld[toNew[oi]] = oi;
}
}
} // internal namespace
void MeshAlg::computeWeld(
const Array<Vector3>& oldVertexArray,
Array<Vector3>& newVertexArray,
Array<int>& toNew,
Array<int>& toOld,
double radius) {
_internal::Welder welder(oldVertexArray, newVertexArray, toNew, toOld, radius);
welder.weld();
}
} // G3D namespace

View File

@@ -0,0 +1,113 @@
/**
@file MeshBuilder.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-02-27
@edited 2005-02-24
*/
#include "G3D/MeshBuilder.h"
#include "G3D/MeshAlg.h"
namespace G3D {
void MeshBuilder::setName(const std::string& n) {
name = n;
}
void MeshBuilder::commit(std::string& n, Array<int>& indexArray, Array<Vector3>& outvertexArray) {
n = name;
// Make the data fit in a unit cube
centerTriList();
Array<int> toNew, toOld;
if (close == MeshBuilder::AUTO_WELD) {
Array<int> index;
MeshAlg::createIndexArray(triList.size(), index);
double minEdgeLen, maxEdgeLen, meanEdgeLen, medianEdgeLen;
double minFaceArea, maxFaceArea, meanFaceArea, medianFaceArea;
MeshAlg::computeAreaStatistics(triList, index,
minEdgeLen, meanEdgeLen, medianEdgeLen, maxEdgeLen,
minFaceArea, meanFaceArea, medianFaceArea, maxFaceArea);
close = minEdgeLen * 0.1;
}
MeshAlg::computeWeld(triList, outvertexArray, toNew, toOld, close);
// Construct triangles
for (int t = 0; t < triList.size(); t += 3) {
int index[3];
for (int i = 0; i < 3; ++i) {
index[i] = toNew[t + i];
}
// Throw out zero size triangles
if ((index[0] != index[1]) &&
(index[1] != index[2]) &&
(index[2] != index[0])) {
indexArray.append(index[0], index[1], index[2]);
}
}
}
void MeshBuilder::centerTriList() {
// Compute the range of the vertices
Vector3 vmin, vmax;
computeBounds(vmin, vmax);
Vector3 diagonal = vmax - vmin;
double scale = max(max(diagonal.x, diagonal.y), diagonal.z) / 2;
debugAssert(scale > 0);
Vector3 translation = vmin + diagonal / 2;
// Center and scale all vertices in the input list
int v;
//Matrix3 rot90 = Matrix3::fromAxisAngle(Vector3::UNIT_Y, toRadians(180)) * Matrix3::fromAxisAngle(Vector3::UNIT_X, toRadians(90));
for (v = 0; v < triList.size(); ++v) {
triList[v] = (triList[v] - translation) / scale;
//triList[v] = rot90 * triList[v];
}
}
void MeshBuilder::computeBounds(Vector3& min, Vector3& max) {
min = Vector3::inf();
max = -min;
int v;
for (v = 0; v < triList.size(); ++v) {
min = min.min(triList[v]);
max = max.max(triList[v]);
}
}
void MeshBuilder::addTriangle(const Vector3& a, const Vector3& b, const Vector3& c) {
triList.append(a, b, c);
if (_twoSided) {
triList.append(c, b, a);
}
}
void MeshBuilder::addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) {
addTriangle(a, b, c);
addTriangle(a, c, d);
}
void MeshBuilder::addTriangle(const Triangle& t) {
addTriangle(t.vertex(0), t.vertex(1), t.vertex(2));
}
} // namespace

View File

@@ -0,0 +1,164 @@
/**
@file NetMessage.cpp
@maintainer Morgan McGuire, morgan@cs.brown.edu
@created 2005-02-06
@edited 2005-02-06
*/
#include "G3D/platform.h"
#include "G3D/NetAddress.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Array.h"
#include "G3D/stringutils.h"
#include "G3D/System.h"
#include "G3D/NetworkDevice.h"
#if defined(G3D_LINUX) || defined(G3D_OSX)
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#define _alloca alloca
# ifndef SOCKADDR_IN
# define SOCKADDR_IN struct sockaddr_in
# endif
# ifndef SOCKET
# define SOCKET int
# endif
// SOCKADDR_IN is supposed to be defined in NetAddress.h
#ifndef SOCKADDR_IN
# error Network headers included in wrong order
#endif
#endif
namespace G3D {
NetAddress::NetAddress() {
System::memset(&addr, 0, sizeof(addr));
}
void NetAddress::init(uint32 host, uint16 port) {
if ((host != 0) || (port != 0)) {
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (host == 0) {
host = INADDR_ANY;
}
addr.sin_addr.s_addr = htonl(host);
} else {
System::memset(&addr, 0, sizeof(addr));
}
}
NetAddress::NetAddress(
const std::string& hostname,
uint16 port) {
init(hostname, port);
}
void NetAddress::init(
const std::string& hostname,
uint16 port) {
uint32 addr;
if (hostname == "") {
addr = INADDR_NONE;
} else {
addr = inet_addr(hostname.c_str());
}
// The address wasn't in numeric form, resolve it
if (addr == INADDR_NONE) {
// Get the IP address of the server and store it in host
struct hostent* host = gethostbyname(hostname.c_str());
if (host == NULL) {
init(0, 0);
return;
}
System::memcpy(&addr, host->h_addr_list[0], host->h_length);
}
if (addr != INADDR_NONE) {
addr = ntohl(addr);
}
init(addr, port);
}
NetAddress::NetAddress(uint32 hostip, uint16 port) {
init(hostip, port);
}
NetAddress NetAddress::broadcastAddress(uint16 port) {
return NetAddress(NetworkDevice::instance()->broadcastAddressArray()[0], port);
}
NetAddress::NetAddress(const std::string& hostnameAndPort) {
Array<std::string> part = stringSplit(hostnameAndPort, ':');
debugAssert(part.length() == 2);
init(part[0], atoi(part[1].c_str()));
}
NetAddress::NetAddress(const SOCKADDR_IN& a) {
addr = a;
}
NetAddress::NetAddress(const struct in_addr& addr, uint16 port) {
#ifdef G3D_WIN32
init(ntohl(addr.S_un.S_addr), port);
#else
init(htonl(addr.s_addr), port);
#endif
}
void NetAddress::serialize(class BinaryOutput& b) const {
b.writeUInt32(ip());
b.writeUInt16(port());
}
void NetAddress::deserialize(class BinaryInput& b) {
uint32 i;
uint16 p;
i = b.readUInt32();
p = b.readUInt16();
init(i, p);
}
bool NetAddress::ok() const {
return addr.sin_family != 0;
}
std::string NetAddress::ipString() const {
return format("%s", inet_ntoa(*(in_addr*)&(addr.sin_addr)));
}
std::string NetAddress::toString() const {
return ipString() + format(":%d", ntohs(addr.sin_port));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/**
@file PhysicsFrame.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-07-09
@edited 2010-03-25
*/
#include "G3D/platform.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
#include "G3D/PhysicsFrame.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
PhysicsFrame::PhysicsFrame() {
translation = Vector3::zero();
rotation = Quat();
}
PhysicsFrame::PhysicsFrame(
const CoordinateFrame& coordinateFrame) {
translation = coordinateFrame.translation;
rotation = Quat(coordinateFrame.rotation);
}
PhysicsFrame::PhysicsFrame(const Any& a) {
const std::string& n = toLower(a.name());
*this = PhysicsFrame();
if (beginsWith(n, "vector3")) {
*this = PhysicsFrame(Vector3(a));
} else if (beginsWith(n, "matrix3")) {
*this = PhysicsFrame(Matrix3(a));
} else if (beginsWith(n, "cframe") || beginsWith(n, "coordinateframe")) {
*this = PhysicsFrame(CoordinateFrame(a));
} else if (beginsWith(n, "pframe") || beginsWith(n, "physicsframe")) {
if (a.type() == Any::ARRAY) {
a.verifySize(2);
rotation = a[0];
translation = a[1];
} else {
for (Any::AnyTable::Iterator it = a.table().begin(); it.hasMore(); ++it) {
const std::string& n = toLower(it->key);
if (n == "translation") {
translation = it->value;
} else if (n == "rotation") {
rotation = it->value;
} else {
a.verify(false, "Illegal table key: " + it->key);
}
}
}
}
}
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;
}
void PhysicsFrame::deserialize(class BinaryInput& b) {
translation.deserialize(b);
rotation.deserialize(b);
}
void PhysicsFrame::serialize(class BinaryOutput& b) const {
translation.serialize(b);
rotation.serialize(b);
}
}; // namespace

View File

@@ -0,0 +1,80 @@
/**
\file PhysicsFrameSpline.cpp
\author Morgan McGuire, http://graphics.cs.williams.edu
*/
#include "G3D/PhysicsFrameSpline.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
PhysicsFrameSpline::PhysicsFrameSpline() {}
PhysicsFrameSpline::PhysicsFrameSpline(const Any& any) {
*this = any;
}
PhysicsFrameSpline& PhysicsFrameSpline::operator=(const Any& any) {
const std::string& n = toLower(any.name());
*this = PhysicsFrameSpline();
if (n == "physicsframespline" || n == "pframespline") {
any.verifyName("PhysicsFrameSpline", "PFrameSpline");
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& k = toLower(it->key);
if (k == "cyclic") {
cyclic = it->value;
} else if (k == "control") {
const Any& v = it->value;
v.verifyType(Any::ARRAY);
control.resize(v.size());
for (int i = 0; i < control.size(); ++i) {
control[i] = v[i];
}
if (! any.containsKey("time")) {
time.resize(control.size());
for (int i = 0; i < time.size(); ++i) {
time[i] = i;
}
}
} else if (k == "finalinterval") {
finalInterval = it->value;
} else if (k == "time") {
const Any& v = it->value;
v.verifyType(Any::ARRAY);
time.resize(v.size());
for (int i = 0; i < time.size(); ++i) {
time[i] = v[i];
}
}
}
} else {
// Must be a PhysicsFrame constructor of some kind
append(any);
}
return *this;
}
void PhysicsFrameSpline::correct(PhysicsFrame& frame) const {
frame.rotation.unitize();
}
void PhysicsFrameSpline::ensureShortestPath(PhysicsFrame* A, int N) const {
for (int i = 1; i < N; ++i) {
const Quat& p = A[i - 1].rotation;
Quat& q = A[i].rotation;
float cosphi = p.dot(q);
if (cosphi < 0) {
// Going the long way, so change the order
q = -q;
}
}
}
}

View File

@@ -0,0 +1,149 @@
/**
@file Plane.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-02-06
@edited 2006-01-29
*/
#include "G3D/platform.h"
#include "G3D/Plane.h"
#include "G3D/BinaryOutput.h"
#include "G3D/BinaryInput.h"
#include "G3D/stringutils.h"
namespace G3D {
Plane::Plane(class BinaryInput& b) {
deserialize(b);
}
void Plane::serialize(class BinaryOutput& b) const {
_normal.serialize(b);
b.writeFloat64(_distance);
}
void Plane::deserialize(class BinaryInput& b) {
_normal.deserialize(b);
_distance = (float)b.readFloat64();
}
Plane::Plane(
Vector4 point0,
Vector4 point1,
Vector4 point2) {
debugAssertM(
point0.w != 0 ||
point1.w != 0 ||
point2.w != 0,
"At least one point must be finite.");
// Rotate the points around so that the finite points come first.
while ((point0.w == 0) &&
((point1.w == 0) || (point2.w != 0))) {
Vector4 temp = point0;
point0 = point1;
point1 = point2;
point2 = temp;
}
Vector3 dir1;
Vector3 dir2;
if (point1.w == 0) {
// 1 finite, 2 infinite points; the plane must contain
// the direction of the two direcitons
dir1 = point1.xyz();
dir2 = point2.xyz();
} else if (point2.w != 0) {
// 3 finite points, the plane must contain the directions
// betwseen the points.
dir1 = point1.xyz() - point0.xyz();
dir2 = point2.xyz() - point0.xyz();
} else {
// 2 finite, 1 infinite point; the plane must contain
// the direction between the first two points and the
// direction of the third point.
dir1 = point1.xyz() - point0.xyz();
dir2 = point2.xyz();
}
_normal = dir1.cross(dir2).direction();
_distance = _normal.dot(point0.xyz());
}
Plane::Plane(
const Vector3& point0,
const Vector3& point1,
const Vector3& point2) {
_normal = (point1 - point0).cross(point2 - point0).direction();
_distance = _normal.dot(point0);
}
Plane::Plane(
const Vector3& __normal,
const Vector3& point) {
_normal = __normal.direction();
_distance = _normal.dot(point);
}
Plane Plane::fromEquation(float a, float b, float c, float d) {
Vector3 n(a, b, c);
float magnitude = n.magnitude();
d /= magnitude;
n /= magnitude;
return Plane(n, -d);
}
void Plane::flip() {
_normal = -_normal;
_distance = -_distance;
}
void Plane::getEquation(Vector3& n, float& d) const {
double _d;
getEquation(n, _d);
d = (float)_d;
}
void Plane::getEquation(Vector3& n, double& d) const {
n = _normal;
d = -_distance;
}
void Plane::getEquation(float& a, float& b, float& c, float& d) const {
double _a, _b, _c, _d;
getEquation(_a, _b, _c, _d);
a = (float)_a;
b = (float)_b;
c = (float)_c;
d = (float)_d;
}
void Plane::getEquation(double& a, double& b, double& c, double& d) const {
a = _normal.x;
b = _normal.y;
c = _normal.z;
d = -_distance;
}
std::string Plane::toString() const {
return format("Plane(%g, %g, %g, %g)", _normal.x, _normal.y, _normal.z, _distance);
}
}

View File

@@ -0,0 +1,125 @@
/**
@file PrecomputedRandom.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-03-31
@edited 2009-07-01
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/PrecomputedRandom.h"
#include "G3D/System.h"
namespace G3D {
PrecomputedRandom::PrecomputedRandom(int dataSize, uint32 seed) :
Random((void*)NULL),
m_hemiUniform(NULL),
m_sphereBits(NULL),
m_modMask(dataSize - 1),
m_freeData(true) {
alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
m_index = seed & m_modMask;
HemiUniformData* h;
SphereBitsData* s;
m_hemiUniform = h = (HemiUniformData*) System::malloc(sizeof(HemiUniformData) * dataSize);
m_sphereBits = s = (SphereBitsData*) System::malloc(sizeof(SphereBitsData) * dataSize);
Random r;
for (int i = 0; i < dataSize; ++i) {
h[i].uniform = r.uniform();
r.cosHemi(h[i].cosHemiX, h[i].cosHemiY, h[i].cosHemiZ);
s[i].bits = r.bits();
r.sphere(s[i].sphereX, s[i].sphereY, s[i].sphereZ);
}
}
PrecomputedRandom::PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed) :
Random((void*)NULL),
m_hemiUniform(data1),
m_sphereBits(data2),
m_modMask(dataSize - 1),
m_freeData(false) {
m_index = seed & m_modMask;
alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
}
PrecomputedRandom::~PrecomputedRandom() {
if (m_freeData) {
System::free(const_cast<HemiUniformData*>(m_hemiUniform));
System::free(const_cast<SphereBitsData*>(m_sphereBits));
}
}
float PrecomputedRandom::uniform(float low, float high) {
m_index = (m_index + 1) & m_modMask;
return low + m_hemiUniform[m_index].uniform * (high - low);
}
float PrecomputedRandom::uniform() {
m_index = (m_index + 1) & m_modMask;
return m_hemiUniform[m_index].uniform;
}
void PrecomputedRandom::cosHemi(float& x, float& y, float& z) {
m_index = (m_index + 1) & m_modMask;
x = m_hemiUniform[m_index].cosHemiX;
y = m_hemiUniform[m_index].cosHemiY;
z = m_hemiUniform[m_index].cosHemiZ;
}
void PrecomputedRandom::cosPowHemi(const float k, float& x, float& y, float& z) {
// Computing a cosPowHemi costs 4 slow functions (pow, sqrt, sin,
// cos). We can do it with two, given a cosHemi sample, basically
// saving the cost of sin and cos and making a single 128-byte
// memory read (for a vector) instead of two (for adjacent uniform
// floats).
// cos^1 distribution sample
float cos1;
cosHemi(x, y, cos1);
// Fix the distribution by adjusting the cosine:
// rnd(cos^k t) = (rnd(cos(t))^2)^(1/k)
// produces cos^k distribution sample
z = pow(cos1, 2.0f / (1.0f + k));
// Rescale x and y by sqrt(1.0f - square(z)) / sqrt(x*x + y*y).
// Add a very tiny offset to handle the (almost impossibly unlikely) case where
// z = 1 and x^2+y^2 = 0.
static const float eps = 0.000001f;
const float s = sqrt((1.0f + eps - square(z)) / (square(x) + square(y) + eps));
x *= s;
y *= s;
}
uint32 PrecomputedRandom::bits() {
m_index = (m_index + 1) & m_modMask;
return m_sphereBits[m_index].bits;
}
void PrecomputedRandom::sphere(float& x, float& y, float& z) {
m_index = (m_index + 1) & m_modMask;
x = m_sphereBits[m_index].sphereX;
y = m_sphereBits[m_index].sphereY;
z = m_sphereBits[m_index].sphereZ;
}
}

View File

@@ -0,0 +1,599 @@
/**
@file Quat.cpp
Quaternion implementation based on Watt & Watt page 363
@author Morgan McGuire, graphics3d.com
@created 2002-01-23
@edited 2010-03-31
*/
#include "G3D/Quat.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
Quat Quat::fromAxisAngleRotation(
const Vector3& axis,
float angle) {
Quat q;
q.w = cos(angle / 2.0f);
q.imag() = axis.direction() * sin(angle / 2.0f);
return q;
}
Quat::Quat(const class Any& a) {
*this = Quat();
if (beginsWith(toLower(a.name()), "matrix3")) {
*this = a;
} else {
a.verifyName("Quat");
a.verifyType(Any::ARRAY);
x = a[0];
y = a[1];
z = a[2];
w = a[3];
}
}
Quat::Quat(const Matrix3& rot) {
static const int plus1mod3[] = {1, 2, 0};
// Find the index of the largest diagonal component
// These ? operations hopefully compile to conditional
// move instructions instead of branches.
int i = (rot[1][1] > rot[0][0]) ? 1 : 0;
i = (rot[2][2] > rot[i][i]) ? 2 : i;
// Find the indices of the other elements
int j = plus1mod3[i];
int k = plus1mod3[j];
// Index the elements of the vector part of the quaternion as a float*
float* v = (float*)(this);
// If we attempted to pre-normalize and trusted the matrix to be
// perfectly orthonormal, the result would be:
//
// double c = sqrt((rot[i][i] - (rot[j][j] + rot[k][k])) + 1.0)
// v[i] = -c * 0.5
// v[j] = -(rot[i][j] + rot[j][i]) * 0.5 / c
// v[k] = -(rot[i][k] + rot[k][i]) * 0.5 / c
// w = (rot[j][k] - rot[k][j]) * 0.5 / c
//
// Since we're going to pay the sqrt anyway, we perform a post normalization, which also
// fixes any poorly normalized input. Multiply all elements by 2*c in the above, giving:
// nc2 = -c^2
double nc2 = ((rot[j][j] + rot[k][k]) - rot[i][i]) - 1.0;
v[i] = nc2;
w = (rot[j][k] - rot[k][j]);
v[j] = -(rot[i][j] + rot[j][i]);
v[k] = -(rot[i][k] + rot[k][i]);
// We now have the correct result with the wrong magnitude, so normalize it:
float s = sqrt(x*x + y*y + z*z + w*w);
if (s > 0.00001f) {
s = 1.0f / s;
x *= s;
y *= s;
z *= s;
w *= s;
} else {
// The quaternion is nearly zero. Make it 0 0 0 1
x = 0.0f;
y = 0.0f;
z = 0.0f;
w = 1.0f;
}
}
void Quat::toAxisAngleRotation(
Vector3& axis,
double& angle) const {
// Decompose the quaternion into an angle and an axis.
axis = Vector3(x, y, z);
angle = 2 * acos(w);
float len = sqrt(1.0f - w * w);
if (fuzzyGt(abs(len), 0.0f)) {
axis /= len;
}
// Reduce the range of the angle.
if (angle < 0) {
angle = -angle;
axis = -axis;
}
while (angle > twoPi()) {
angle -= twoPi();
}
if (abs(angle) > pi()) {
angle -= twoPi();
}
// Make the angle positive.
if (angle < 0.0f) {
angle = -angle;
axis = -axis;
}
}
Matrix3 Quat::toRotationMatrix() const {
Matrix3 out = Matrix3::zero();
toRotationMatrix(out);
return out;
}
void Quat::toRotationMatrix(
Matrix3& rot) const {
rot = Matrix3(*this);
}
Quat Quat::slerp(
const Quat& _quat1,
float alpha,
float threshold) const {
// From: Game Physics -- David Eberly pg 538-540
// Modified to include lerp for small angles, which
// is a common practice.
// See also:
// http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html
const Quat& quat0 = *this;
Quat quat1 = _quat1;
// angle between quaternion rotations
float phi;
float cosphi = quat0.dot(quat1);
if (cosphi < 0) {
// Change the sign and fix the dot product; we need to
// loop the other way to get the shortest path
quat1 = -quat1;
cosphi = -cosphi;
}
// Using G3D::aCos will clamp the angle to 0 and pi
phi = static_cast<float>(G3D::aCos(cosphi));
if (phi >= threshold) {
// For large angles, slerp
float scale0, scale1;
scale0 = sin((1.0f - alpha) * phi);
scale1 = sin(alpha * phi);
return ( (quat0 * scale0) + (quat1 * scale1) ) / sin(phi);
} else {
// For small angles, linear interpolate
return quat0.nlerp(quat1, alpha);
}
}
Quat Quat::nlerp(
const Quat& quat1,
float alpha) const {
Quat result = (*this) * (1.0f - alpha) + quat1 * alpha;
return result / result.magnitude();
}
Quat Quat::operator*(const Quat& other) const {
// Following Watt & Watt, page 360
const Vector3& v1 = imag();
const Vector3& v2 = other.imag();
float s1 = w;
float s2 = other.w;
return Quat(s1*v2 + s2*v1 + v1.cross(v2), s1*s2 - v1.dot(v2));
}
// From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III.
Quat Quat::unitRandom() {
float x0 = uniformRandom();
float r1 = sqrtf(1 - x0),
r2 = sqrtf(x0);
float t1 = (float)G3D::twoPi() * uniformRandom();
float t2 = (float)G3D::twoPi() * uniformRandom();
float c1 = cosf(t1),
s1 = sinf(t1);
float c2 = cosf(t2),
s2 = sinf(t2);
return Quat(s1 * r1, c1 * r1, s2 * r2, c2 * r2);
}
void Quat::deserialize(class BinaryInput& b) {
x = b.readFloat32();
y = b.readFloat32();
z = b.readFloat32();
w = b.readFloat32();
}
void Quat::serialize(class BinaryOutput& b) const {
b.writeFloat32(x);
b.writeFloat32(y);
b.writeFloat32(z);
b.writeFloat32(w);
}
// 2-char swizzles
Vector2 Quat::xx() const { return Vector2 (x, x); }
Vector2 Quat::yx() const { return Vector2 (y, x); }
Vector2 Quat::zx() const { return Vector2 (z, x); }
Vector2 Quat::wx() const { return Vector2 (w, x); }
Vector2 Quat::xy() const { return Vector2 (x, y); }
Vector2 Quat::yy() const { return Vector2 (y, y); }
Vector2 Quat::zy() const { return Vector2 (z, y); }
Vector2 Quat::wy() const { return Vector2 (w, y); }
Vector2 Quat::xz() const { return Vector2 (x, z); }
Vector2 Quat::yz() const { return Vector2 (y, z); }
Vector2 Quat::zz() const { return Vector2 (z, z); }
Vector2 Quat::wz() const { return Vector2 (w, z); }
Vector2 Quat::xw() const { return Vector2 (x, w); }
Vector2 Quat::yw() const { return Vector2 (y, w); }
Vector2 Quat::zw() const { return Vector2 (z, w); }
Vector2 Quat::ww() const { return Vector2 (w, w); }
// 3-char swizzles
Vector3 Quat::xxx() const { return Vector3 (x, x, x); }
Vector3 Quat::yxx() const { return Vector3 (y, x, x); }
Vector3 Quat::zxx() const { return Vector3 (z, x, x); }
Vector3 Quat::wxx() const { return Vector3 (w, x, x); }
Vector3 Quat::xyx() const { return Vector3 (x, y, x); }
Vector3 Quat::yyx() const { return Vector3 (y, y, x); }
Vector3 Quat::zyx() const { return Vector3 (z, y, x); }
Vector3 Quat::wyx() const { return Vector3 (w, y, x); }
Vector3 Quat::xzx() const { return Vector3 (x, z, x); }
Vector3 Quat::yzx() const { return Vector3 (y, z, x); }
Vector3 Quat::zzx() const { return Vector3 (z, z, x); }
Vector3 Quat::wzx() const { return Vector3 (w, z, x); }
Vector3 Quat::xwx() const { return Vector3 (x, w, x); }
Vector3 Quat::ywx() const { return Vector3 (y, w, x); }
Vector3 Quat::zwx() const { return Vector3 (z, w, x); }
Vector3 Quat::wwx() const { return Vector3 (w, w, x); }
Vector3 Quat::xxy() const { return Vector3 (x, x, y); }
Vector3 Quat::yxy() const { return Vector3 (y, x, y); }
Vector3 Quat::zxy() const { return Vector3 (z, x, y); }
Vector3 Quat::wxy() const { return Vector3 (w, x, y); }
Vector3 Quat::xyy() const { return Vector3 (x, y, y); }
Vector3 Quat::yyy() const { return Vector3 (y, y, y); }
Vector3 Quat::zyy() const { return Vector3 (z, y, y); }
Vector3 Quat::wyy() const { return Vector3 (w, y, y); }
Vector3 Quat::xzy() const { return Vector3 (x, z, y); }
Vector3 Quat::yzy() const { return Vector3 (y, z, y); }
Vector3 Quat::zzy() const { return Vector3 (z, z, y); }
Vector3 Quat::wzy() const { return Vector3 (w, z, y); }
Vector3 Quat::xwy() const { return Vector3 (x, w, y); }
Vector3 Quat::ywy() const { return Vector3 (y, w, y); }
Vector3 Quat::zwy() const { return Vector3 (z, w, y); }
Vector3 Quat::wwy() const { return Vector3 (w, w, y); }
Vector3 Quat::xxz() const { return Vector3 (x, x, z); }
Vector3 Quat::yxz() const { return Vector3 (y, x, z); }
Vector3 Quat::zxz() const { return Vector3 (z, x, z); }
Vector3 Quat::wxz() const { return Vector3 (w, x, z); }
Vector3 Quat::xyz() const { return Vector3 (x, y, z); }
Vector3 Quat::yyz() const { return Vector3 (y, y, z); }
Vector3 Quat::zyz() const { return Vector3 (z, y, z); }
Vector3 Quat::wyz() const { return Vector3 (w, y, z); }
Vector3 Quat::xzz() const { return Vector3 (x, z, z); }
Vector3 Quat::yzz() const { return Vector3 (y, z, z); }
Vector3 Quat::zzz() const { return Vector3 (z, z, z); }
Vector3 Quat::wzz() const { return Vector3 (w, z, z); }
Vector3 Quat::xwz() const { return Vector3 (x, w, z); }
Vector3 Quat::ywz() const { return Vector3 (y, w, z); }
Vector3 Quat::zwz() const { return Vector3 (z, w, z); }
Vector3 Quat::wwz() const { return Vector3 (w, w, z); }
Vector3 Quat::xxw() const { return Vector3 (x, x, w); }
Vector3 Quat::yxw() const { return Vector3 (y, x, w); }
Vector3 Quat::zxw() const { return Vector3 (z, x, w); }
Vector3 Quat::wxw() const { return Vector3 (w, x, w); }
Vector3 Quat::xyw() const { return Vector3 (x, y, w); }
Vector3 Quat::yyw() const { return Vector3 (y, y, w); }
Vector3 Quat::zyw() const { return Vector3 (z, y, w); }
Vector3 Quat::wyw() const { return Vector3 (w, y, w); }
Vector3 Quat::xzw() const { return Vector3 (x, z, w); }
Vector3 Quat::yzw() const { return Vector3 (y, z, w); }
Vector3 Quat::zzw() const { return Vector3 (z, z, w); }
Vector3 Quat::wzw() const { return Vector3 (w, z, w); }
Vector3 Quat::xww() const { return Vector3 (x, w, w); }
Vector3 Quat::yww() const { return Vector3 (y, w, w); }
Vector3 Quat::zww() const { return Vector3 (z, w, w); }
Vector3 Quat::www() const { return Vector3 (w, w, w); }
// 4-char swizzles
Vector4 Quat::xxxx() const { return Vector4 (x, x, x, x); }
Vector4 Quat::yxxx() const { return Vector4 (y, x, x, x); }
Vector4 Quat::zxxx() const { return Vector4 (z, x, x, x); }
Vector4 Quat::wxxx() const { return Vector4 (w, x, x, x); }
Vector4 Quat::xyxx() const { return Vector4 (x, y, x, x); }
Vector4 Quat::yyxx() const { return Vector4 (y, y, x, x); }
Vector4 Quat::zyxx() const { return Vector4 (z, y, x, x); }
Vector4 Quat::wyxx() const { return Vector4 (w, y, x, x); }
Vector4 Quat::xzxx() const { return Vector4 (x, z, x, x); }
Vector4 Quat::yzxx() const { return Vector4 (y, z, x, x); }
Vector4 Quat::zzxx() const { return Vector4 (z, z, x, x); }
Vector4 Quat::wzxx() const { return Vector4 (w, z, x, x); }
Vector4 Quat::xwxx() const { return Vector4 (x, w, x, x); }
Vector4 Quat::ywxx() const { return Vector4 (y, w, x, x); }
Vector4 Quat::zwxx() const { return Vector4 (z, w, x, x); }
Vector4 Quat::wwxx() const { return Vector4 (w, w, x, x); }
Vector4 Quat::xxyx() const { return Vector4 (x, x, y, x); }
Vector4 Quat::yxyx() const { return Vector4 (y, x, y, x); }
Vector4 Quat::zxyx() const { return Vector4 (z, x, y, x); }
Vector4 Quat::wxyx() const { return Vector4 (w, x, y, x); }
Vector4 Quat::xyyx() const { return Vector4 (x, y, y, x); }
Vector4 Quat::yyyx() const { return Vector4 (y, y, y, x); }
Vector4 Quat::zyyx() const { return Vector4 (z, y, y, x); }
Vector4 Quat::wyyx() const { return Vector4 (w, y, y, x); }
Vector4 Quat::xzyx() const { return Vector4 (x, z, y, x); }
Vector4 Quat::yzyx() const { return Vector4 (y, z, y, x); }
Vector4 Quat::zzyx() const { return Vector4 (z, z, y, x); }
Vector4 Quat::wzyx() const { return Vector4 (w, z, y, x); }
Vector4 Quat::xwyx() const { return Vector4 (x, w, y, x); }
Vector4 Quat::ywyx() const { return Vector4 (y, w, y, x); }
Vector4 Quat::zwyx() const { return Vector4 (z, w, y, x); }
Vector4 Quat::wwyx() const { return Vector4 (w, w, y, x); }
Vector4 Quat::xxzx() const { return Vector4 (x, x, z, x); }
Vector4 Quat::yxzx() const { return Vector4 (y, x, z, x); }
Vector4 Quat::zxzx() const { return Vector4 (z, x, z, x); }
Vector4 Quat::wxzx() const { return Vector4 (w, x, z, x); }
Vector4 Quat::xyzx() const { return Vector4 (x, y, z, x); }
Vector4 Quat::yyzx() const { return Vector4 (y, y, z, x); }
Vector4 Quat::zyzx() const { return Vector4 (z, y, z, x); }
Vector4 Quat::wyzx() const { return Vector4 (w, y, z, x); }
Vector4 Quat::xzzx() const { return Vector4 (x, z, z, x); }
Vector4 Quat::yzzx() const { return Vector4 (y, z, z, x); }
Vector4 Quat::zzzx() const { return Vector4 (z, z, z, x); }
Vector4 Quat::wzzx() const { return Vector4 (w, z, z, x); }
Vector4 Quat::xwzx() const { return Vector4 (x, w, z, x); }
Vector4 Quat::ywzx() const { return Vector4 (y, w, z, x); }
Vector4 Quat::zwzx() const { return Vector4 (z, w, z, x); }
Vector4 Quat::wwzx() const { return Vector4 (w, w, z, x); }
Vector4 Quat::xxwx() const { return Vector4 (x, x, w, x); }
Vector4 Quat::yxwx() const { return Vector4 (y, x, w, x); }
Vector4 Quat::zxwx() const { return Vector4 (z, x, w, x); }
Vector4 Quat::wxwx() const { return Vector4 (w, x, w, x); }
Vector4 Quat::xywx() const { return Vector4 (x, y, w, x); }
Vector4 Quat::yywx() const { return Vector4 (y, y, w, x); }
Vector4 Quat::zywx() const { return Vector4 (z, y, w, x); }
Vector4 Quat::wywx() const { return Vector4 (w, y, w, x); }
Vector4 Quat::xzwx() const { return Vector4 (x, z, w, x); }
Vector4 Quat::yzwx() const { return Vector4 (y, z, w, x); }
Vector4 Quat::zzwx() const { return Vector4 (z, z, w, x); }
Vector4 Quat::wzwx() const { return Vector4 (w, z, w, x); }
Vector4 Quat::xwwx() const { return Vector4 (x, w, w, x); }
Vector4 Quat::ywwx() const { return Vector4 (y, w, w, x); }
Vector4 Quat::zwwx() const { return Vector4 (z, w, w, x); }
Vector4 Quat::wwwx() const { return Vector4 (w, w, w, x); }
Vector4 Quat::xxxy() const { return Vector4 (x, x, x, y); }
Vector4 Quat::yxxy() const { return Vector4 (y, x, x, y); }
Vector4 Quat::zxxy() const { return Vector4 (z, x, x, y); }
Vector4 Quat::wxxy() const { return Vector4 (w, x, x, y); }
Vector4 Quat::xyxy() const { return Vector4 (x, y, x, y); }
Vector4 Quat::yyxy() const { return Vector4 (y, y, x, y); }
Vector4 Quat::zyxy() const { return Vector4 (z, y, x, y); }
Vector4 Quat::wyxy() const { return Vector4 (w, y, x, y); }
Vector4 Quat::xzxy() const { return Vector4 (x, z, x, y); }
Vector4 Quat::yzxy() const { return Vector4 (y, z, x, y); }
Vector4 Quat::zzxy() const { return Vector4 (z, z, x, y); }
Vector4 Quat::wzxy() const { return Vector4 (w, z, x, y); }
Vector4 Quat::xwxy() const { return Vector4 (x, w, x, y); }
Vector4 Quat::ywxy() const { return Vector4 (y, w, x, y); }
Vector4 Quat::zwxy() const { return Vector4 (z, w, x, y); }
Vector4 Quat::wwxy() const { return Vector4 (w, w, x, y); }
Vector4 Quat::xxyy() const { return Vector4 (x, x, y, y); }
Vector4 Quat::yxyy() const { return Vector4 (y, x, y, y); }
Vector4 Quat::zxyy() const { return Vector4 (z, x, y, y); }
Vector4 Quat::wxyy() const { return Vector4 (w, x, y, y); }
Vector4 Quat::xyyy() const { return Vector4 (x, y, y, y); }
Vector4 Quat::yyyy() const { return Vector4 (y, y, y, y); }
Vector4 Quat::zyyy() const { return Vector4 (z, y, y, y); }
Vector4 Quat::wyyy() const { return Vector4 (w, y, y, y); }
Vector4 Quat::xzyy() const { return Vector4 (x, z, y, y); }
Vector4 Quat::yzyy() const { return Vector4 (y, z, y, y); }
Vector4 Quat::zzyy() const { return Vector4 (z, z, y, y); }
Vector4 Quat::wzyy() const { return Vector4 (w, z, y, y); }
Vector4 Quat::xwyy() const { return Vector4 (x, w, y, y); }
Vector4 Quat::ywyy() const { return Vector4 (y, w, y, y); }
Vector4 Quat::zwyy() const { return Vector4 (z, w, y, y); }
Vector4 Quat::wwyy() const { return Vector4 (w, w, y, y); }
Vector4 Quat::xxzy() const { return Vector4 (x, x, z, y); }
Vector4 Quat::yxzy() const { return Vector4 (y, x, z, y); }
Vector4 Quat::zxzy() const { return Vector4 (z, x, z, y); }
Vector4 Quat::wxzy() const { return Vector4 (w, x, z, y); }
Vector4 Quat::xyzy() const { return Vector4 (x, y, z, y); }
Vector4 Quat::yyzy() const { return Vector4 (y, y, z, y); }
Vector4 Quat::zyzy() const { return Vector4 (z, y, z, y); }
Vector4 Quat::wyzy() const { return Vector4 (w, y, z, y); }
Vector4 Quat::xzzy() const { return Vector4 (x, z, z, y); }
Vector4 Quat::yzzy() const { return Vector4 (y, z, z, y); }
Vector4 Quat::zzzy() const { return Vector4 (z, z, z, y); }
Vector4 Quat::wzzy() const { return Vector4 (w, z, z, y); }
Vector4 Quat::xwzy() const { return Vector4 (x, w, z, y); }
Vector4 Quat::ywzy() const { return Vector4 (y, w, z, y); }
Vector4 Quat::zwzy() const { return Vector4 (z, w, z, y); }
Vector4 Quat::wwzy() const { return Vector4 (w, w, z, y); }
Vector4 Quat::xxwy() const { return Vector4 (x, x, w, y); }
Vector4 Quat::yxwy() const { return Vector4 (y, x, w, y); }
Vector4 Quat::zxwy() const { return Vector4 (z, x, w, y); }
Vector4 Quat::wxwy() const { return Vector4 (w, x, w, y); }
Vector4 Quat::xywy() const { return Vector4 (x, y, w, y); }
Vector4 Quat::yywy() const { return Vector4 (y, y, w, y); }
Vector4 Quat::zywy() const { return Vector4 (z, y, w, y); }
Vector4 Quat::wywy() const { return Vector4 (w, y, w, y); }
Vector4 Quat::xzwy() const { return Vector4 (x, z, w, y); }
Vector4 Quat::yzwy() const { return Vector4 (y, z, w, y); }
Vector4 Quat::zzwy() const { return Vector4 (z, z, w, y); }
Vector4 Quat::wzwy() const { return Vector4 (w, z, w, y); }
Vector4 Quat::xwwy() const { return Vector4 (x, w, w, y); }
Vector4 Quat::ywwy() const { return Vector4 (y, w, w, y); }
Vector4 Quat::zwwy() const { return Vector4 (z, w, w, y); }
Vector4 Quat::wwwy() const { return Vector4 (w, w, w, y); }
Vector4 Quat::xxxz() const { return Vector4 (x, x, x, z); }
Vector4 Quat::yxxz() const { return Vector4 (y, x, x, z); }
Vector4 Quat::zxxz() const { return Vector4 (z, x, x, z); }
Vector4 Quat::wxxz() const { return Vector4 (w, x, x, z); }
Vector4 Quat::xyxz() const { return Vector4 (x, y, x, z); }
Vector4 Quat::yyxz() const { return Vector4 (y, y, x, z); }
Vector4 Quat::zyxz() const { return Vector4 (z, y, x, z); }
Vector4 Quat::wyxz() const { return Vector4 (w, y, x, z); }
Vector4 Quat::xzxz() const { return Vector4 (x, z, x, z); }
Vector4 Quat::yzxz() const { return Vector4 (y, z, x, z); }
Vector4 Quat::zzxz() const { return Vector4 (z, z, x, z); }
Vector4 Quat::wzxz() const { return Vector4 (w, z, x, z); }
Vector4 Quat::xwxz() const { return Vector4 (x, w, x, z); }
Vector4 Quat::ywxz() const { return Vector4 (y, w, x, z); }
Vector4 Quat::zwxz() const { return Vector4 (z, w, x, z); }
Vector4 Quat::wwxz() const { return Vector4 (w, w, x, z); }
Vector4 Quat::xxyz() const { return Vector4 (x, x, y, z); }
Vector4 Quat::yxyz() const { return Vector4 (y, x, y, z); }
Vector4 Quat::zxyz() const { return Vector4 (z, x, y, z); }
Vector4 Quat::wxyz() const { return Vector4 (w, x, y, z); }
Vector4 Quat::xyyz() const { return Vector4 (x, y, y, z); }
Vector4 Quat::yyyz() const { return Vector4 (y, y, y, z); }
Vector4 Quat::zyyz() const { return Vector4 (z, y, y, z); }
Vector4 Quat::wyyz() const { return Vector4 (w, y, y, z); }
Vector4 Quat::xzyz() const { return Vector4 (x, z, y, z); }
Vector4 Quat::yzyz() const { return Vector4 (y, z, y, z); }
Vector4 Quat::zzyz() const { return Vector4 (z, z, y, z); }
Vector4 Quat::wzyz() const { return Vector4 (w, z, y, z); }
Vector4 Quat::xwyz() const { return Vector4 (x, w, y, z); }
Vector4 Quat::ywyz() const { return Vector4 (y, w, y, z); }
Vector4 Quat::zwyz() const { return Vector4 (z, w, y, z); }
Vector4 Quat::wwyz() const { return Vector4 (w, w, y, z); }
Vector4 Quat::xxzz() const { return Vector4 (x, x, z, z); }
Vector4 Quat::yxzz() const { return Vector4 (y, x, z, z); }
Vector4 Quat::zxzz() const { return Vector4 (z, x, z, z); }
Vector4 Quat::wxzz() const { return Vector4 (w, x, z, z); }
Vector4 Quat::xyzz() const { return Vector4 (x, y, z, z); }
Vector4 Quat::yyzz() const { return Vector4 (y, y, z, z); }
Vector4 Quat::zyzz() const { return Vector4 (z, y, z, z); }
Vector4 Quat::wyzz() const { return Vector4 (w, y, z, z); }
Vector4 Quat::xzzz() const { return Vector4 (x, z, z, z); }
Vector4 Quat::yzzz() const { return Vector4 (y, z, z, z); }
Vector4 Quat::zzzz() const { return Vector4 (z, z, z, z); }
Vector4 Quat::wzzz() const { return Vector4 (w, z, z, z); }
Vector4 Quat::xwzz() const { return Vector4 (x, w, z, z); }
Vector4 Quat::ywzz() const { return Vector4 (y, w, z, z); }
Vector4 Quat::zwzz() const { return Vector4 (z, w, z, z); }
Vector4 Quat::wwzz() const { return Vector4 (w, w, z, z); }
Vector4 Quat::xxwz() const { return Vector4 (x, x, w, z); }
Vector4 Quat::yxwz() const { return Vector4 (y, x, w, z); }
Vector4 Quat::zxwz() const { return Vector4 (z, x, w, z); }
Vector4 Quat::wxwz() const { return Vector4 (w, x, w, z); }
Vector4 Quat::xywz() const { return Vector4 (x, y, w, z); }
Vector4 Quat::yywz() const { return Vector4 (y, y, w, z); }
Vector4 Quat::zywz() const { return Vector4 (z, y, w, z); }
Vector4 Quat::wywz() const { return Vector4 (w, y, w, z); }
Vector4 Quat::xzwz() const { return Vector4 (x, z, w, z); }
Vector4 Quat::yzwz() const { return Vector4 (y, z, w, z); }
Vector4 Quat::zzwz() const { return Vector4 (z, z, w, z); }
Vector4 Quat::wzwz() const { return Vector4 (w, z, w, z); }
Vector4 Quat::xwwz() const { return Vector4 (x, w, w, z); }
Vector4 Quat::ywwz() const { return Vector4 (y, w, w, z); }
Vector4 Quat::zwwz() const { return Vector4 (z, w, w, z); }
Vector4 Quat::wwwz() const { return Vector4 (w, w, w, z); }
Vector4 Quat::xxxw() const { return Vector4 (x, x, x, w); }
Vector4 Quat::yxxw() const { return Vector4 (y, x, x, w); }
Vector4 Quat::zxxw() const { return Vector4 (z, x, x, w); }
Vector4 Quat::wxxw() const { return Vector4 (w, x, x, w); }
Vector4 Quat::xyxw() const { return Vector4 (x, y, x, w); }
Vector4 Quat::yyxw() const { return Vector4 (y, y, x, w); }
Vector4 Quat::zyxw() const { return Vector4 (z, y, x, w); }
Vector4 Quat::wyxw() const { return Vector4 (w, y, x, w); }
Vector4 Quat::xzxw() const { return Vector4 (x, z, x, w); }
Vector4 Quat::yzxw() const { return Vector4 (y, z, x, w); }
Vector4 Quat::zzxw() const { return Vector4 (z, z, x, w); }
Vector4 Quat::wzxw() const { return Vector4 (w, z, x, w); }
Vector4 Quat::xwxw() const { return Vector4 (x, w, x, w); }
Vector4 Quat::ywxw() const { return Vector4 (y, w, x, w); }
Vector4 Quat::zwxw() const { return Vector4 (z, w, x, w); }
Vector4 Quat::wwxw() const { return Vector4 (w, w, x, w); }
Vector4 Quat::xxyw() const { return Vector4 (x, x, y, w); }
Vector4 Quat::yxyw() const { return Vector4 (y, x, y, w); }
Vector4 Quat::zxyw() const { return Vector4 (z, x, y, w); }
Vector4 Quat::wxyw() const { return Vector4 (w, x, y, w); }
Vector4 Quat::xyyw() const { return Vector4 (x, y, y, w); }
Vector4 Quat::yyyw() const { return Vector4 (y, y, y, w); }
Vector4 Quat::zyyw() const { return Vector4 (z, y, y, w); }
Vector4 Quat::wyyw() const { return Vector4 (w, y, y, w); }
Vector4 Quat::xzyw() const { return Vector4 (x, z, y, w); }
Vector4 Quat::yzyw() const { return Vector4 (y, z, y, w); }
Vector4 Quat::zzyw() const { return Vector4 (z, z, y, w); }
Vector4 Quat::wzyw() const { return Vector4 (w, z, y, w); }
Vector4 Quat::xwyw() const { return Vector4 (x, w, y, w); }
Vector4 Quat::ywyw() const { return Vector4 (y, w, y, w); }
Vector4 Quat::zwyw() const { return Vector4 (z, w, y, w); }
Vector4 Quat::wwyw() const { return Vector4 (w, w, y, w); }
Vector4 Quat::xxzw() const { return Vector4 (x, x, z, w); }
Vector4 Quat::yxzw() const { return Vector4 (y, x, z, w); }
Vector4 Quat::zxzw() const { return Vector4 (z, x, z, w); }
Vector4 Quat::wxzw() const { return Vector4 (w, x, z, w); }
Vector4 Quat::xyzw() const { return Vector4 (x, y, z, w); }
Vector4 Quat::yyzw() const { return Vector4 (y, y, z, w); }
Vector4 Quat::zyzw() const { return Vector4 (z, y, z, w); }
Vector4 Quat::wyzw() const { return Vector4 (w, y, z, w); }
Vector4 Quat::xzzw() const { return Vector4 (x, z, z, w); }
Vector4 Quat::yzzw() const { return Vector4 (y, z, z, w); }
Vector4 Quat::zzzw() const { return Vector4 (z, z, z, w); }
Vector4 Quat::wzzw() const { return Vector4 (w, z, z, w); }
Vector4 Quat::xwzw() const { return Vector4 (x, w, z, w); }
Vector4 Quat::ywzw() const { return Vector4 (y, w, z, w); }
Vector4 Quat::zwzw() const { return Vector4 (z, w, z, w); }
Vector4 Quat::wwzw() const { return Vector4 (w, w, z, w); }
Vector4 Quat::xxww() const { return Vector4 (x, x, w, w); }
Vector4 Quat::yxww() const { return Vector4 (y, x, w, w); }
Vector4 Quat::zxww() const { return Vector4 (z, x, w, w); }
Vector4 Quat::wxww() const { return Vector4 (w, x, w, w); }
Vector4 Quat::xyww() const { return Vector4 (x, y, w, w); }
Vector4 Quat::yyww() const { return Vector4 (y, y, w, w); }
Vector4 Quat::zyww() const { return Vector4 (z, y, w, w); }
Vector4 Quat::wyww() const { return Vector4 (w, y, w, w); }
Vector4 Quat::xzww() const { return Vector4 (x, z, w, w); }
Vector4 Quat::yzww() const { return Vector4 (y, z, w, w); }
Vector4 Quat::zzww() const { return Vector4 (z, z, w, w); }
Vector4 Quat::wzww() const { return Vector4 (w, z, w, w); }
Vector4 Quat::xwww() const { return Vector4 (x, w, w, w); }
Vector4 Quat::ywww() const { return Vector4 (y, w, w, w); }
Vector4 Quat::zwww() const { return Vector4 (z, w, w, w); }
Vector4 Quat::wwww() const { return Vector4 (w, w, w, w); }
}

View File

@@ -0,0 +1,212 @@
/**
@file Random.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-01-02
@edited 2009-03-29
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Random.h"
namespace G3D {
Random& Random::common() {
static Random r;
return r;
}
Random::Random(void* x) : state(NULL), m_threadsafe(false) {
(void)x;
}
Random::Random(uint32 seed, bool threadsafe) : m_threadsafe(threadsafe) {
const uint32 X = 1812433253UL;
state = new uint32[N];
state[0] = seed;
for (index = 1; index < (int)N; ++index) {
state[index] = X * (state[index - 1] ^ (state[index - 1] >> 30)) + index;
}
}
Random::~Random() {
delete[] state;
state = NULL;
}
uint32 Random::bits() {
// See http://en.wikipedia.org/wiki/Mersenne_twister
// Make a local copy of the index variable to ensure that it
// is not out of bounds
int localIndex = index;
// Automatically checks for index < 0 if corrupted
// by unsynchronized threads.
if ((unsigned int)localIndex >= (unsigned int)N) {
generate();
localIndex = 0;
}
// Increment the global index. It may go out of bounds on
// multiple threads, but the above check ensures that the
// array index actually used never goes out of bounds.
// It doesn't matter if we grab the same array index twice
// on two threads, since the distribution of random numbers
// will still be uniform.
++index;
// Return the next random in the sequence
uint32 r = state[localIndex];
// Temper the result
r ^= r >> U;
r ^= (r << S) & B;
r ^= (r << T) & C;
r ^= r >> L;
return r;
}
/** Generate the next N ints, and store them for readback later */
void Random::generate() {
// Lower R bits
static const uint32 LOWER_MASK = (1LU << R) - 1;
// Upper (32 - R) bits
static const uint32 UPPER_MASK = 0xFFFFFFFF << R;
static const uint32 mag01[2] = {0UL, (uint32)A};
if (m_threadsafe) {
bool contention = ! lock.lock();
if (contention) {
// Another thread just generated a set of numbers; no need for
// this thread to do it too
lock.unlock();
return;
}
}
// First N - M
for (unsigned int i = 0; i < N - M; ++i) {
uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
state[i] = state[i + M] ^ (x >> 1) ^ mag01[x & 1];
}
// Rest
for (unsigned int i = N - M + 1; i < N - 1; ++i) {
uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
state[i] = state[i + (M - N)] ^ (x >> 1) ^ mag01[x & 1];
}
uint32 y = (state[N - 1] & UPPER_MASK) | (state[0] & LOWER_MASK);
state[N - 1] = state[M - 1] ^ (y >> 1) ^ mag01[y & 1];
index = 0;
if (m_threadsafe) {
lock.unlock();
}
}
int Random::integer(int low, int high) {
int r = iFloor(low + (high - low + 1) * (double)bits() / 0xFFFFFFFFUL);
// There is a *very small* chance of generating
// a number larger than high.
if (r > high) {
return high;
} else {
return r;
}
}
float Random::gaussian(float mean, float stdev) {
// Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
// Modified to specify standard deviation and mean of distribution
float w, x1, x2;
// Loop until w is less than 1 so that log(w) is negative
do {
x1 = uniform(-1.0, 1.0);
x2 = uniform(-1.0, 1.0);
w = float(square(x1) + square(x2));
} while (w > 1.0f);
// Transform to gassian distribution
// Multiply by sigma (stdev ^ 2) and add mean.
return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean;
}
void Random::cosHemi(float& x, float& y, float& z) {
const float e1 = uniform();
const float e2 = uniform();
// Jensen's method
const float sin_theta = sqrtf(1.0f - e1);
const float cos_theta = sqrtf(e1);
const float phi = 6.28318531f * e2;
x = cos(phi) * sin_theta;
y = sin(phi) * sin_theta;
z = cos_theta;
// We could also use Malley's method (pbrt p.657), since they are the same cost:
//
// r = sqrt(e1);
// t = 2*pi*e2;
// x = cos(t)*r;
// y = sin(t)*r;
// z = sqrt(1.0 - x*x + y*y);
}
void Random::cosPowHemi(const float k, float& x, float& y, float& z) {
const float e1 = uniform();
const float e2 = uniform();
const float cos_theta = pow(e1, 1.0f / (k + 1.0f));
const float sin_theta = sqrtf(1.0f - square(cos_theta));
const float phi = 6.28318531f * e2;
x = cos(phi) * sin_theta;
y = sin(phi) * sin_theta;
z = cos_theta;
}
void Random::hemi(float& x, float& y, float& z) {
sphere(x, y, z);
z = fabsf(z);
}
void Random::sphere(float& x, float& y, float& z) {
// Squared magnitude
float m2;
// Rejection sample
do {
x = uniform() * 2.0f - 1.0f,
y = uniform() * 2.0f - 1.0f,
z = uniform() * 2.0f - 1.0f;
m2 = x*x + y*y + z*z;
} while (m2 >= 1.0f);
// Divide by magnitude to produce a unit vector
float s = rsqrt(m2);
x *= s;
y *= s;
z *= s;
}
} // G3D

View File

@@ -0,0 +1,218 @@
/**
@file Ray.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-07-12
@edited 2004-03-19
*/
#include "G3D/platform.h"
#include "G3D/Ray.h"
#include "G3D/Plane.h"
#include "G3D/Sphere.h"
#include "G3D/CollisionDetection.h"
namespace G3D {
void Ray::set(const Vector3& origin, const Vector3& direction) {
m_origin = origin;
m_direction = direction;
debugAssert(direction.isUnit());
m_invDirection = Vector3::one() / direction;
// ray slope
ibyj = m_direction.x * m_invDirection.y;
jbyi = m_direction.y * m_invDirection.x;
jbyk = m_direction.y * m_invDirection.z;
kbyj = m_direction.z * m_invDirection.y;
ibyk = m_direction.x * m_invDirection.z;
kbyi = m_direction.z * m_invDirection.x;
// precomputed terms
c_xy = m_origin.y - jbyi * m_origin.x;
c_xz = m_origin.z - kbyi * m_origin.x;
c_yx = m_origin.x - ibyj * m_origin.y;
c_yz = m_origin.z - kbyj * m_origin.y;
c_zx = m_origin.x - ibyk * m_origin.z;
c_zy = m_origin.y - jbyk * m_origin.z;
//ray slope classification
if (m_direction.x < 0) {
if (m_direction.y < 0) {
if (m_direction.z < 0) {
classification = MMM;
} else if (m_direction.z > 0) {
classification = MMP;
} else { //(m_direction.z >= 0)
classification = MMO;
}
} else { //(m_direction.y >= 0)
if (m_direction.z < 0) {
if (m_direction.y == 0) {
classification = MOM;
} else {
classification = MPM;
}
} else { //(m_direction.z >= 0)
if ((m_direction.y == 0) && (m_direction.z == 0)) {
classification = MOO;
} else if (m_direction.z == 0) {
classification = MPO;
} else if (m_direction.y == 0) {
classification = MOP;
} else {
classification = MPP;
}
}
}
} else { //(m_direction.x >= 0)
if (m_direction.y < 0) {
if (m_direction.z < 0) {
if (m_direction.x == 0) {
classification = OMM;
} else {
classification = PMM;
}
} else { //(m_direction.z >= 0)
if ((m_direction.x == 0) && (m_direction.z == 0)) {
classification = OMO;
} else if (m_direction.z == 0) {
classification = PMO;
} else if (m_direction.x == 0) {
classification = OMP;
} else {
classification = PMP;
}
}
} else { //(m_direction.y >= 0)
if (m_direction.z < 0) {
if ((m_direction.x == 0) && (m_direction.y == 0)) {
classification = OOM;
} else if (m_direction.x == 0) {
classification = OPM;
} else if (m_direction.y == 0) {
classification = POM;
} else {
classification = PPM;
}
} else { //(m_direction.z > 0)
if (m_direction.x == 0) {
if (m_direction.y == 0) {
classification = OOP;
} else if (m_direction.z == 0) {
classification = OPO;
} else {
classification = OPP;
}
} else {
if ((m_direction.y == 0) && (m_direction.z == 0)) {
classification = POO;
} else if (m_direction.y == 0) {
classification = POP;
} else if (m_direction.z == 0) {
classification = PPO;
} else {
classification = PPP;
}
}
}
}
}
}
Ray::Ray(class BinaryInput& b) {
deserialize(b);
}
void Ray::serialize(class BinaryOutput& b) const {
m_origin.serialize(b);
m_direction.serialize(b);
}
void Ray::deserialize(class BinaryInput& b) {
m_origin.deserialize(b);
m_direction.deserialize(b);
set(m_origin, m_direction);
}
Ray Ray::refract(
const Vector3& newOrigin,
const Vector3& normal,
float iInside,
float iOutside) const {
Vector3 D = m_direction.refractionDirection(normal, iInside, iOutside);
return Ray(newOrigin + (m_direction + normal * (float)sign(m_direction.dot(normal))) * 0.001f, D);
}
Ray Ray::reflect(
const Vector3& newOrigin,
const Vector3& normal) const {
Vector3 D = m_direction.reflectionDirection(normal);
return Ray(newOrigin + (D + normal) * 0.001f, D);
}
Vector3 Ray::intersection(const Plane& plane) const {
float d;
Vector3 normal = plane.normal();
plane.getEquation(normal, d);
float rate = m_direction.dot(normal);
if (rate >= 0.0f) {
return Vector3::inf();
} else {
float t = -(d + m_origin.dot(normal)) / rate;
return m_origin + m_direction * t;
}
}
float Ray::intersectionTime(const class Sphere& sphere, bool solid) const {
Vector3 dummy;
return CollisionDetection::collisionTimeForMovingPointFixedSphere(
m_origin, m_direction, sphere, dummy, dummy, solid);
}
float Ray::intersectionTime(const class Plane& plane) const {
Vector3 dummy;
return CollisionDetection::collisionTimeForMovingPointFixedPlane(
m_origin, m_direction, plane, dummy);
}
float Ray::intersectionTime(const class Box& box) const {
Vector3 dummy;
float time = CollisionDetection::collisionTimeForMovingPointFixedBox(
m_origin, m_direction, box, dummy);
if ((time == finf()) && (box.contains(m_origin))) {
return 0.0f;
} else {
return time;
}
}
float Ray::intersectionTime(const class AABox& box) const {
Vector3 dummy;
bool inside;
float time = CollisionDetection::collisionTimeForMovingPointFixedAABox(
m_origin, m_direction, box, dummy, inside);
if ((time == finf()) && inside) {
return 0.0f;
} else {
return time;
}
}
}

View File

@@ -0,0 +1,41 @@
/**
@file Rect2D.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-11-13
@created 2009-11-16
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
#include "G3D/Rect2D.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
/** \param any Must either Rect2D::xywh(#, #, #, #) or Rect2D::xyxy(#, #, #, #)*/
Rect2D::Rect2D(const Any& any) {
any.verifyName("Rect2D");
any.verifyType(Any::ARRAY);
any.verifySize(4);
if (toUpper(any.name()) == "RECT2D::XYWH") {
*this = Rect2D::xywh(any[0], any[1], any[2], any[3]);
} else {
any.verifyName("Rect2D::xyxy");
*this = Rect2D::xyxy(any[0], any[1], any[2], any[3]);
}
}
/** Converts the Rect2D to an Any. */
Rect2D::operator Any() const {
Any any(Any::ARRAY, "Rect2D::xywh");
any.append(x0(), y0(), width(), height());
return any;
}
}

View File

@@ -0,0 +1,61 @@
/**
@file ReferenceCount.cpp
Reference Counting Garbage Collector for C++
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@cite Adapted and extended from Justin Miller's "RGC" class that appeared in BYTE magazine.
@cite See also http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm
@created 2001-10-23
@edited 2009-04-25
*/
#include "G3D/platform.h"
#include "G3D/ReferenceCount.h"
namespace G3D {
ReferenceCountedObject::ReferenceCountedObject() :
ReferenceCountedObject_refCount(0),
ReferenceCountedObject_weakPointer(0) {
debugAssertM(isValidHeapPointer(this),
"Reference counted objects must be allocated on the heap.");
}
void ReferenceCountedObject::ReferenceCountedObject_zeroWeakPointers() {
// Tell all of my weak pointers that I'm gone.
_WeakPtrLinkedList* node = ReferenceCountedObject_weakPointer;
while (node != NULL) {
// Notify the weak pointer that it is going away
node->weakPtr->objectCollected();
// Free the node and advance
_WeakPtrLinkedList* tmp = node;
node = node->next;
delete tmp;
}
}
ReferenceCountedObject::~ReferenceCountedObject() {}
ReferenceCountedObject::ReferenceCountedObject(const ReferenceCountedObject& notUsed) :
ReferenceCountedObject_refCount(0),
ReferenceCountedObject_weakPointer(0) {
(void)notUsed;
debugAssertM(G3D::isValidHeapPointer(this),
"Reference counted objects must be allocated on the heap.");
}
ReferenceCountedObject& ReferenceCountedObject::operator=(const ReferenceCountedObject& other) {
(void)other;
// Nothing changes when I am assigned; the reference count on
// both objects is the same (although my super-class probably
// changes).
return *this;
}
} // G3D

View File

@@ -0,0 +1,299 @@
/**
@file RegistryUtil.cpp
@created 2006-04-06
@edited 2006-04-24
Copyright 2000-2006, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
// This file is only used on Windows
#ifdef G3D_WIN32
#include "G3D/RegistryUtil.h"
#include "G3D/System.h"
#ifdef __MINGW32__
# ifndef HKEY_PERFORMANCE_TEXT
# define HKEY_PERFORMANCE_TEXT ((HKEY)((LONG)0x80000050))
# endif
# ifndef HKEY_PERFORMANCE_NLSTEXT
# define HKEY_PERFORMANCE_NLSTEXT ((HKEY)((LONG)0x80000060))
# endif
#endif
namespace G3D {
// static helpers
static HKEY getRootKeyFromString(const char* str, size_t length);
bool RegistryUtil::keyExists(const std::string& key) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if (hkey == NULL) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
RegCloseKey(openKey);
return true;
} else {
return false;
}
}
bool RegistryUtil::valueExists(const std::string& key, const std::string& value) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if ( hkey == NULL ) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
uint32 dataSize = 0;
result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
RegCloseKey(openKey);
}
return (result == ERROR_SUCCESS);
}
bool RegistryUtil::readInt32(const std::string& key, const std::string& value, int32& data) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if ( hkey == NULL ) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
uint32 dataSize = sizeof(int32);
result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize));
debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
RegCloseKey(openKey);
}
return (result == ERROR_SUCCESS);
}
bool RegistryUtil::readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if (hkey == NULL) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
if (data == NULL) {
result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
} else {
result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize));
}
debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
RegCloseKey(openKey);
}
return (result == ERROR_SUCCESS);
}
bool RegistryUtil::readString(const std::string& key, const std::string& value, std::string& data) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if (hkey == NULL) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
uint32 dataSize = 0;
result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
// increment datasize to allow for non null-terminated strings in registry
dataSize += 1;
if (result == ERROR_SUCCESS) {
char* tmpStr = static_cast<char*>(System::malloc(dataSize));
System::memset(tmpStr, 0, dataSize);
result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(tmpStr), reinterpret_cast<LPDWORD>(&dataSize));
debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
if (result == ERROR_SUCCESS) {
data = tmpStr;
}
RegCloseKey(openKey);
System::free(tmpStr);
}
}
return (result == ERROR_SUCCESS);
}
bool RegistryUtil::writeInt32(const std::string& key, const std::string& value, int32 data) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if (hkey == NULL) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
result = RegSetValueExA(openKey, value.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&data), sizeof(int32));
debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
RegCloseKey(openKey);
}
return (result == ERROR_SUCCESS);
}
bool RegistryUtil::writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize) {
debugAssert(data);
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if (hkey == NULL) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
if (data) {
result = RegSetValueExA(openKey, value.c_str(), 0, REG_BINARY, reinterpret_cast<const BYTE*>(data), dataSize);
}
debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
RegCloseKey(openKey);
}
return (result == ERROR_SUCCESS);
}
bool RegistryUtil::writeString(const std::string& key, const std::string& value, const std::string& data) {
size_t pos = key.find('\\', 0);
if (pos == std::string::npos) {
return false;
}
HKEY hkey = getRootKeyFromString(key.c_str(), pos);
if (hkey == NULL) {
return false;
}
HKEY openKey;
int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
if (result == ERROR_SUCCESS) {
result = RegSetValueExA(openKey, value.c_str(), 0, REG_SZ, reinterpret_cast<const BYTE*>(data.c_str()), (data.size() + 1));
debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
RegCloseKey(openKey);
}
return (result == ERROR_SUCCESS);
}
// static helpers
static HKEY getRootKeyFromString(const char* str, size_t length) {
debugAssert(str);
if (str) {
if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) {
return HKEY_CLASSES_ROOT;
} else if ( strncmp(str, "HKEY_CURRENT_CONFIG", length) == 0 ) {
return HKEY_CURRENT_CONFIG;
} else if ( strncmp(str, "HKEY_CURRENT_USER", length) == 0 ) {
return HKEY_CURRENT_USER;
} else if ( strncmp(str, "HKEY_LOCAL_MACHINE", length) == 0 ) {
return HKEY_LOCAL_MACHINE;
} else if ( strncmp(str, "HKEY_PERFORMANCE_DATA", length) == 0 ) {
return HKEY_PERFORMANCE_DATA;
} else if ( strncmp(str, "HKEY_PERFORMANCE_NLSTEXT", length) == 0 ) {
return HKEY_PERFORMANCE_NLSTEXT;
} else if ( strncmp(str, "HKEY_PERFORMANCE_TEXT", length) == 0 ) {
return HKEY_PERFORMANCE_TEXT;
} else if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) {
return HKEY_CLASSES_ROOT;
} else {
return NULL;
}
} else {
return NULL;
}
}
} // namespace G3D
#endif // G3D_WIN32

View File

@@ -0,0 +1,223 @@
/**
@file Sphere.cpp
Sphere class
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-04-17
@edited 2009-01-20
*/
#include "G3D/platform.h"
#include "G3D/Sphere.h"
#include "G3D/stringutils.h"
#include "G3D/BinaryOutput.h"
#include "G3D/BinaryInput.h"
#include "G3D/AABox.h"
#include "G3D/Plane.h"
namespace G3D {
int32 Sphere::dummy;
Sphere::Sphere(class BinaryInput& b) {
deserialize(b);
}
void Sphere::serialize(class BinaryOutput& b) const {
center.serialize(b);
b.writeFloat64(radius);
}
void Sphere::deserialize(class BinaryInput& b) {
center.deserialize(b);
radius = (float)b.readFloat64();
}
std::string Sphere::toString() const {
return format("Sphere(<%g, %g, %g>, %g)",
center.x, center.y, center.z, radius);
}
bool Sphere::contains(const Vector3& point) const {
float distance = (center - point).squaredMagnitude();
return distance <= square(radius);
}
bool Sphere::contains(const Sphere& other) const {
float distance = (center - other.center).squaredMagnitude();
return (radius >= other.radius) && (distance <= square(radius - other.radius));
}
bool Sphere::intersects(const Sphere& other) const {
return (other.center - center).length() <= (radius + other.radius);
}
void Sphere::merge(const Sphere& other) {
if (other.contains(*this)) {
*this = other;
} else if (! contains(other)) {
// The farthest distance is along the axis between the centers, which
// must not be colocated since neither contains the other.
Vector3 toMe = center - other.center;
// Get a point on the axis from each
toMe = toMe.direction();
const Vector3& A = center + toMe * radius;
const Vector3& B = other.center - toMe * other.radius;
// Now just bound the A->B segment
center = (A + B) * 0.5f;
radius = (A - B).length();
}
// (if this contains other, we're done)
}
bool Sphere::culledBy(
const Array<Plane>& plane,
int& cullingPlaneIndex,
const uint32 inMask,
uint32& outMask) const {
return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask);
}
bool Sphere::culledBy(
const Array<Plane>& plane,
int& cullingPlaneIndex,
const uint32 inMask) const {
return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask);
}
bool Sphere::culledBy(
const class Plane* plane,
int numPlanes,
int& cullingPlane,
const uint32 _inMask,
uint32& childMask) const {
if (radius == finf()) {
// No plane can cull the infinite box
return false;
}
uint32 inMask = _inMask;
assert(numPlanes < 31);
childMask = 0;
// See if there is one plane for which all of the
// vertices are in the negative half space.
for (int p = 0; p < numPlanes; p++) {
// Only test planes that are not masked
if ((inMask & 1) != 0) {
bool culledLow = ! plane[p].halfSpaceContainsFinite(center + plane[p].normal() * radius);
bool culledHigh = ! plane[p].halfSpaceContainsFinite(center - plane[p].normal() * radius);
if (culledLow) {
// Plane p culled the sphere
cullingPlane = p;
// The caller should not recurse into the children,
// since the parent is culled. If they do recurse,
// make them only test against this one plane, which
// will immediately cull the volume.
childMask = 1 << p;
return true;
} else if (culledHigh) {
// The bounding volume straddled the plane; we have
// to keep testing against this plane
childMask |= (1 << p);
}
}
// Move on to the next bit.
inMask = inMask >> 1;
}
// None of the planes could cull this box
cullingPlane = -1;
return false;
}
bool Sphere::culledBy(
const class Plane* plane,
int numPlanes,
int& cullingPlane,
const uint32 _inMask) const {
uint32 inMask = _inMask;
assert(numPlanes < 31);
// See if there is one plane for which all of the
// vertices are in the negative half space.
for (int p = 0; p < numPlanes; p++) {
// Only test planes that are not masked
if ((inMask & 1) != 0) {
bool culled = ! plane[p].halfSpaceContains(center + plane[p].normal() * radius);
if (culled) {
// Plane p culled the sphere
cullingPlane = p;
return true;
}
}
// Move on to the next bit.
inMask = inMask >> 1;
}
// None of the planes could cull this box
cullingPlane = -1;
return false;
}
Vector3 Sphere::randomSurfacePoint() const {
return Vector3::random() * radius + center;
}
Vector3 Sphere::randomInteriorPoint() const {
Vector3 result;
do {
result = Vector3(uniformRandom(-1, 1),
uniformRandom(-1, 1),
uniformRandom(-1, 1));
} while (result.squaredMagnitude() >= 1.0f);
return result * radius + center;
}
float Sphere::volume() const {
return (float)pi() * (4.0f / 3.0f) * powf((float)radius, 3.0f);
}
float Sphere::area() const {
return (float)pi() * 4.0f * powf((float)radius, 2.0f);
}
void Sphere::getBounds(AABox& out) const {
Vector3 extent(radius, radius, radius);
out = AABox(center - extent, center + extent);
}
} // namespace

View File

@@ -0,0 +1,162 @@
#include "G3D/platform.h"
#include "G3D/Spline.h"
namespace G3D {
float SplineBase::getFinalInterval() const {
if (! cyclic) {
return 0;
} else if (finalInterval <= 0) {
int N = time.size();
if (N >= 2) {
return (time[1] - time[0] + time[N - 1] - time[N - 2]) * 0.5f;
} else {
return 1.0f;
}
} else {
return finalInterval;
}
}
Matrix4 SplineBase::computeBasis() {
// The standard Catmull-Rom spline basis (e.g., Watt & Watt p108)
// is for [u^3 u^2 u^1 u^0] * B * [p[0] p[1] p[2] p[3]]^T.
// We need a basis formed for:
//
// U * C * [2*p'[1] p[1] p[2] 2*p'[2]]^T
//
// U * C * [p2-p0 p1 p2 p3-p1]^T
//
// To make this transformation, compute the differences of columns in C:
// For [p0 p1 p2 p3]
Matrix4 basis =
Matrix4( -1, 3, -3, 1,
2, -5, 4, -1,
-1, 0, 1, 0,
0, 2, 0, 0) * 0.5f;
// For [-p0 p1 p2 p3]^T
basis.setColumn(0, -basis.column(0));
// For [-p0 p1 p2 p3-p1]^T
basis.setColumn(1, basis.column(1) + basis.column(3));
// For [p2-p0 p1 p2 p3-p1]^T
basis.setColumn(2, basis.column(2) - basis.column(0));
return basis;
}
float SplineBase::duration() const {
if (time.size() == 0) {
return 0;
} else {
return time.last() - time[0] + getFinalInterval();
}
}
void SplineBase::computeIndexInBounds(float s, int& i, float& u) const {
int N = time.size();
float t0 = time[0];
float tn = time[N - 1];
i = iFloor((N - 1) * (s - t0) / (tn - t0));
// Inclusive bounds for binary search
int hi = N - 1;
int lo = 0;
while ((time[i] > s) || (time[i + 1] <= s)) {
if (time[i] > s) {
// too big
hi = i - 1;
} else if (time[i + 1] <= s) {
// too small
lo = i + 1;
}
i = (hi + lo) / 2;
}
// Having exited the above loop, i must be correct, so compute u.
u = (s - time[i]) / (time[i + 1] - time[i]);
}
void SplineBase::computeIndex(float s, int& i, float& u) const {
int N = time.size();
debugAssertM(N > 0, "No control points");
float t0 = time[0];
float tn = time[N - 1];
if (N < 2) {
// No control points to work with
i = 0;
u = 0.0;
} else if (cyclic) {
float fi = getFinalInterval();
// Cyclic spline
if ((s < t0) || (s >= tn + fi)) {
// Cyclic, off the bottom or top
// Compute offset and reduce to the in-bounds case
float d = duration();
// Number of times we wrapped around the cyclic array
int wraps = iFloor((s - t0) / d);
debugAssert(s - d * wraps >= t0);
debugAssert(s - d * wraps < tn + getFinalInterval());
computeIndex(s - d * wraps, i, u);
i += wraps * N;
} else if (s >= tn) {
debugAssert(s < tn + fi);
// Cyclic, off the top but before the end of the last interval
i = N - 1;
u = (s - tn) / fi;
} else {
// Cyclic, in bounds
computeIndexInBounds(s, i, u);
}
} else {
// Non-cyclic
if (s < t0) {
// Non-cyclic, off the bottom. Assume points are spaced
// following the first time interval.
float dt = time[1] - t0;
float x = (s - t0) / dt;
i = iFloor(x);
u = x - i;
} else if (s >= tn) {
// Non-cyclic, off the top. Assume points are spaced following
// the last time interval.
float dt = tn - time[N - 2];
float x = N - 1 + (s - tn) / dt;
i = iFloor(x);
u = x - i;
} else {
// In bounds, non-cyclic. Assume a regular
// distribution (which gives O(1) for uniform spacing)
// and then binary search to handle the general case
// efficiently.
computeIndexInBounds(s, i, u);
} // if in bounds
} // if cyclic
}
}

View File

@@ -0,0 +1,119 @@
/**
@file Stopwatch.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2005-10-05
@edited 2009-03-14
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Stopwatch.h"
#include "G3D/System.h"
namespace G3D {
Stopwatch::Stopwatch(const std::string& myName) :
myName(myName),
inBetween(false), lastTockTime(-1),
lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0),
m_smoothFPS(0), emwaDuration(0) {
computeOverhead();
reset();
}
void Stopwatch::computeOverhead() {
cycleOverhead = 0;
tick();
tock();
cycleOverhead = elapsedCycles();
}
void Stopwatch::tick() {
// This is 'alwaysAssert' instead of 'debugAssert'
// since people rarely profile in debug mode.
alwaysAssertM(! inBetween, "Stopwatch::tick() called twice in a row.");
inBetween = true;
// We read RDTSC twice here, but it is more abstract to implement this
// way and at least we're reading the cycle count last.
timeStart = System::time();
System::beginCycleCount(cycleStart);
}
void Stopwatch::tock() {
System::endCycleCount(cycleStart);
RealTime now = System::time();
lastDuration = now - timeStart;
if (abs(emwaDuration - lastDuration) > max(emwaDuration, lastDuration) * 0.50) {
// Off by more than 50%
emwaDuration = lastDuration;
} else {
emwaDuration = lastDuration * 0.05 + emwaDuration * 0.95;
}
lastCycleCount = cycleStart - cycleOverhead;
if (lastCycleCount < 0) {
lastCycleCount = 0;
}
if (lastTockTime != -1.0) {
m_fps = 1.0 / (now - lastTockTime);
const double blend = 0.01;
emwaFPS = m_fps * blend + emwaFPS * (1.0 - blend);
double maxDiscrepancyPercentage = 0.25;
if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * maxDiscrepancyPercentage) {
// The difference between emwa and m_fps is way off, so
// update emwa directly.
emwaFPS = m_fps * 0.20 + emwaFPS * 0.80;
}
// Update m_smoothFPS only when the value varies significantly.
// We round so as to not mislead the user as to the accuracy of
// the number.
if (m_smoothFPS == 0) {
m_smoothFPS = m_fps;
} else if (emwaFPS <= 20) {
if (::fabs(m_smoothFPS - emwaFPS) > 0.75) {
// Small number and display is off by more than 0.75; round to the nearest 0.1
m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0;
}
} else if (::fabs(m_smoothFPS - emwaFPS) > 1.25) {
// Large number and display is off by more than 1.25; round to the nearest 1.0
m_smoothFPS = floor(emwaFPS + 0.5);
}
}
lastTockTime = now;
alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick.");
inBetween = false;
}
void Stopwatch::reset() {
prevTime = startTime = System::time();
prevMark = "start";
}
void Stopwatch::after(const std::string& s) {
RealTime now = System::time();
debugPrintf("%s: %10s - %8fs since %s (%fs since start)\n",
myName.c_str(),
s.c_str(),
now - prevTime,
prevMark.c_str(),
now - startTime);
prevTime = now;
prevMark = s;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,453 @@
/**
@file TextOutput.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2004-06-21
@edited 2010-03-14
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/TextOutput.h"
#include "G3D/Log.h"
#include "G3D/fileutils.h"
#include "G3D/FileSystem.h"
namespace G3D {
TextOutput::TextOutput(const TextOutput::Settings& opt) :
startingNewLine(true),
currentColumn(0),
inDQuote(false),
filename(""),
indentLevel(0)
{
setOptions(opt);
}
TextOutput::TextOutput(const std::string& fil, const TextOutput::Settings& opt) :
startingNewLine(true),
currentColumn(0),
inDQuote(false),
filename(fil),
indentLevel(0)
{
setOptions(opt);
}
void TextOutput::setIndentLevel(int i) {
indentLevel = i;
// If there were more pops than pushes, don't let that take us below 0 indent.
// Don't ever indent more than the number of columns.
indentSpaces =
iClamp(option.spacesPerIndent * indentLevel,
0,
option.numColumns - 1);
}
void TextOutput::setOptions(const Settings& _opt) {
option = _opt;
debugAssert(option.numColumns > 1);
setIndentLevel(indentLevel);
newline = (option.newlineStyle == Settings::NEWLINE_WINDOWS) ? "\r\n" : "\n";
}
void TextOutput::pushIndent() {
setIndentLevel(indentLevel + 1);
}
void TextOutput::popIndent() {
setIndentLevel(indentLevel - 1);
}
static std::string escape(const std::string& string) {
std::string result = "";
for (std::string::size_type i = 0; i < string.length(); ++i) {
char c = string.at(i);
switch (c) {
case '\0':
result += "\\0";
break;
case '\r':
result += "\\r";
break;
case '\n':
result += "\\n";
break;
case '\t':
result += "\\t";
break;
case '\\':
result += "\\\\";
break;
default:
result += c;
}
}
return result;
}
void TextOutput::writeString(const std::string& string) {
// Convert special characters to escape sequences
this->printf("\"%s\"", escape(string).c_str());
}
void TextOutput::writeBoolean(bool b) {
this->printf("%s ", b ? option.trueSymbol.c_str() : option.falseSymbol.c_str());
}
void TextOutput::writeNumber(double n) {
this->printf("%f ", n);
}
void TextOutput::writeNumber(int n) {
this->printf("%d ", n);
}
void TextOutput::writeSymbol(const std::string& string) {
if (string.size() > 0) {
// TODO: check for legal symbols?
this->printf("%s ", string.c_str());
}
}
void TextOutput::writeSymbols(
const std::string& a,
const std::string& b,
const std::string& c,
const std::string& d,
const std::string& e,
const std::string& f) {
writeSymbol(a);
writeSymbol(b);
writeSymbol(c);
writeSymbol(d);
writeSymbol(e);
writeSymbol(f);
}
void TextOutput::printf(const std::string formatString, ...) {
va_list argList;
va_start(argList, formatString);
this->vprintf(formatString.c_str(), argList);
va_end(argList);
}
void TextOutput::printf(const char* formatString, ...) {
va_list argList;
va_start(argList, formatString);
this->vprintf(formatString, argList);
va_end(argList);
}
void TextOutput::convertNewlines(const std::string& in, std::string& out) {
// TODO: can be significantly optimized in cases where
// single characters are copied in order by walking through
// the array and copying substrings as needed.
if (option.convertNewlines) {
out = "";
for (uint32 i = 0; i < in.size(); ++i) {
if (in[i] == '\n') {
// Unix newline
out += newline;
} else if ((in[i] == '\r') && (i + 1 < in.size()) && (in[i + 1] == '\n')) {
// Windows newline
out += newline;
++i;
} else {
out += in[i];
}
}
} else {
out = in;
}
}
void TextOutput::writeNewline() {
for (uint32 i = 0; i < newline.size(); ++i) {
indentAppend(newline[i]);
}
}
void TextOutput::writeNewlines(int numLines) {
for (int i = 0; i < numLines; ++i) {
writeNewline();
}
}
void TextOutput::wordWrapIndentAppend(const std::string& str) {
// TODO: keep track of the last space character we saw so we don't
// have to always search.
if ((option.wordWrap == Settings::WRAP_NONE) ||
(currentColumn + (int)str.size() <= option.numColumns)) {
// No word-wrapping is needed
// Add one character at a time.
// TODO: optimize for strings without newlines to add multiple
// characters.
for (uint32 i = 0; i < str.size(); ++i) {
indentAppend(str[i]);
}
return;
}
// Number of columns to wrap against
int cols = option.numColumns - indentSpaces;
// Copy forward until we exceed the column size,
// and then back up and try to insert newlines as needed.
for (uint32 i = 0; i < str.size(); ++i) {
indentAppend(str[i]);
if ((str[i] == '\r') && (i + 1 < str.size()) && (str[i + 1] == '\n')) {
// \r\n, we need to hit the \n to enter word wrapping.
++i;
indentAppend(str[i]);
}
if (currentColumn >= cols) {
debugAssertM(str[i] != '\n' && str[i] != '\r',
"Should never enter word-wrapping on a newline character");
// True when we're allowed to treat a space as a space.
bool unquotedSpace = option.allowWordWrapInsideDoubleQuotes || ! inDQuote;
// Cases:
//
// 1. Currently in a series of spaces that ends with a newline
// strip all spaces and let the newline
// flow through.
//
// 2. Currently in a series of spaces that does not end with a newline
// strip all spaces and replace them with single newline
//
// 3. Not in a series of spaces
// search backwards for a space, then execute case 2.
// Index of most recent space
uint32 lastSpace = data.size() - 1;
// How far back we had to look for a space
uint32 k = 0;
uint32 maxLookBackward = currentColumn - indentSpaces;
// Search backwards (from current character), looking for a space.
while ((k < maxLookBackward) &&
(lastSpace > 0) &&
(! ((data[lastSpace] == ' ') && unquotedSpace))) {
--lastSpace;
++k;
if ((data[lastSpace] == '\"') && !option.allowWordWrapInsideDoubleQuotes) {
unquotedSpace = ! unquotedSpace;
}
}
if (k == maxLookBackward) {
// We couldn't find a series of spaces
if (option.wordWrap == Settings::WRAP_ALWAYS) {
// Strip the last character we wrote, force a newline,
// and replace the last character;
data.pop();
writeNewline();
indentAppend(str[i]);
} else {
// Must be Settings::WRAP_WITHOUT_BREAKING
//
// Don't write the newline; we'll come back to
// the word wrap code after writing another character
}
} else {
// We found a series of spaces. If they continue
// to the new string, strip spaces off both. Otherwise
// strip spaces from data only and insert a newline.
// Find the start of the spaces. firstSpace is the index of the
// first non-space, looking backwards from lastSpace.
uint32 firstSpace = lastSpace;
while ((k < maxLookBackward) &&
(firstSpace > 0) &&
(data[firstSpace] == ' ')) {
--firstSpace;
++k;
}
if (k == maxLookBackward) {
++firstSpace;
}
if (lastSpace == (uint32)data.size() - 1) {
// Spaces continued up to the new string
data.resize(firstSpace + 1);
writeNewline();
// Delete the spaces from the new string
while ((i < str.size() - 1) && (str[i + 1] == ' ')) {
++i;
}
} else {
// Spaces were somewhere in the middle of the old string.
// replace them with a newline.
// Copy over the characters that should be saved
Array<char> temp;
for (uint32 j = lastSpace + 1; j < (uint32)data.size(); ++j) {
char c = data[j];
if (c == '\"') {
// Undo changes to quoting (they will be re-done
// when we paste these characters back on).
inDQuote = !inDQuote;
}
temp.append(c);
}
// Remove those characters and replace with a newline.
data.resize(firstSpace + 1);
writeNewline();
// Write them back
for (uint32 j = 0; j < (uint32)temp.size(); ++j) {
indentAppend(temp[j]);
}
// We are now free to continue adding from the
// new string, which may or may not begin with spaces.
} // if spaces included new string
} // if hit indent
} // if line exceeded
} // iterate over str
}
void TextOutput::indentAppend(char c) {
if (startingNewLine) {
for (int j = 0; j < indentSpaces; ++j) {
data.push(' ');
}
startingNewLine = false;
currentColumn = indentSpaces;
}
data.push(c);
// Don't increment the column count on return character
// newline is taken care of below.
if (c != '\r') {
++currentColumn;
}
if (c == '\"') {
inDQuote = ! inDQuote;
}
startingNewLine = (c == '\n');
if (startingNewLine) {
currentColumn = 0;
}
}
void TextOutput::vprintf(const char* formatString, va_list argPtr) {
std::string str = vformat(formatString, argPtr);
std::string clean;
convertNewlines(str, clean);
wordWrapIndentAppend(clean);
}
void TextOutput::commit(bool flush) {
std::string p = filenamePath(filename);
if (! FileSystem::exists(p, false)) {
FileSystem::createDirectory(p);
}
FILE* f = FileSystem::fopen(filename.c_str(), "wb");
debugAssertM(f, "Could not open \"" + filename + "\"");
fwrite(data.getCArray(), 1, data.size(), f);
if (flush) {
fflush(f);
}
FileSystem::fclose(f);
}
void TextOutput::commitString(std::string& out) {
// Null terminate
data.push('\0');
out = data.getCArray();
data.pop();
}
std::string TextOutput::commitString() {
std::string str;
commitString(str);
return str;
}
/////////////////////////////////////////////////////////////////////
void serialize(const float& b, TextOutput& to) {
to.writeNumber(b);
}
void serialize(const bool& b, TextOutput& to) {
to.writeSymbol(b ? "true" : "false");
}
void serialize(const int& b, TextOutput& to) {
to.writeNumber(b);
}
void serialize(const uint8& b, TextOutput& to) {
to.writeNumber(b);
}
void serialize(const double& b, TextOutput& to) {
to.writeNumber(b);
}
}

View File

@@ -0,0 +1,166 @@
#include "G3D/ThreadSet.h"
namespace G3D {
int ThreadSet::size() const {
ThreadSet* me = const_cast<ThreadSet*>(this);
me->m_lock.lock();
int s = m_thread.size();
me->m_lock.unlock();
return s;
}
int ThreadSet::numStarted() const {
ThreadSet* me = const_cast<ThreadSet*>(this);
me->m_lock.lock();
int count = 0;
for (int i = 0; i < m_thread.size(); ++i) {
if (m_thread[i]->started()) {
++count;
}
}
me->m_lock.unlock();
return count;
}
void ThreadSet::start(GThread::SpawnBehavior lastBehavior) const {
ThreadSet* me = const_cast<ThreadSet*>(this);
Array<GThreadRef> unstarted;
me->m_lock.lock();
// Find the unstarted threads
for (int i = 0; i < m_thread.size(); ++i) {
if (! m_thread[i]->started()) {
unstarted.append(m_thread[i]);
}
}
int last = unstarted.size();
if (lastBehavior == GThread::USE_CURRENT_THREAD) {
// Save the last unstarted for the current thread
--last;
}
for (int i = 0; i < last; ++i) {
unstarted[i]->start(GThread::USE_NEW_THREAD);
}
me->m_lock.unlock();
// Start the last one on my thread
if ((unstarted.size() > 0) && (lastBehavior == GThread::USE_CURRENT_THREAD)) {
unstarted.last()->start(GThread::USE_CURRENT_THREAD);
}
}
void ThreadSet::terminate() const {
ThreadSet* me = const_cast<ThreadSet*>(this);
me->m_lock.lock();
for (int i = 0; i < m_thread.size(); ++i) {
if (m_thread[i]->started()) {
m_thread[i]->terminate();
}
}
me->m_lock.unlock();
}
void ThreadSet::waitForCompletion() const {
ThreadSet* me = const_cast<ThreadSet*>(this);
me->m_lock.lock();
for (int i = 0; i < m_thread.size(); ++i) {
if (m_thread[i]->started()) {
m_thread[i]->waitForCompletion();
}
}
me->m_lock.unlock();
}
int ThreadSet::removeCompleted() {
m_lock.lock();
for (int i = 0; i < m_thread.size(); ++i) {
if (m_thread[i]->completed()) {
m_thread.fastRemove(i);
--i;
}
}
int s = m_thread.size();
m_lock.unlock();
return s;
}
void ThreadSet::clear() {
m_lock.lock();
m_thread.clear();
m_lock.unlock();
}
int ThreadSet::insert(const ThreadRef& t) {
m_lock.lock();
bool found = false;
for (int i = 0; i < m_thread.size() && ! found; ++i) {
found = (m_thread[i] == t);
}
if (! found) {
m_thread.append(t);
}
int s = m_thread.size();
m_lock.unlock();
return s;
}
bool ThreadSet::remove(const ThreadRef& t) {
m_lock.lock();
bool found = false;
for (int i = 0; i < m_thread.size() && ! found; ++i) {
found = (m_thread[i] == t);
if (found) {
m_thread.fastRemove(i);
}
}
m_lock.unlock();
return found;
}
bool ThreadSet::contains(const ThreadRef& t) const {
ThreadSet* me = const_cast<ThreadSet*>(this);
me->m_lock.lock();
bool found = false;
for (int i = 0; i < m_thread.size() && ! found; ++i) {
found = (m_thread[i] == t);
}
me->m_lock.unlock();
return found;
}
ThreadSet::Iterator ThreadSet::begin() {
return m_thread.begin();
}
ThreadSet::Iterator ThreadSet::end() {
return m_thread.end();
}
ThreadSet::ConstIterator ThreadSet::begin() const {
return m_thread.begin();
}
ThreadSet::ConstIterator ThreadSet::end() const {
return m_thread.end();
}
} // namespace G3D

View File

@@ -0,0 +1,186 @@
/**
@file Triangle.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-04-06
@edited 2008-12-28
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
#include "G3D/Triangle.h"
#include "G3D/Plane.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/debugAssert.h"
#include "G3D/AABox.h"
#include "G3D/Ray.h"
namespace G3D {
void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
_plane = Plane(v0, v1, v2);
_vertex[0] = v0;
_vertex[1] = v1;
_vertex[2] = v2;
static int next[] = {1,2,0};
for (int i = 0; i < 3; ++i) {
const Vector3& e = _vertex[next[i]] - _vertex[i];
edgeMagnitude[i] = e.magnitude();
if (edgeMagnitude[i] == 0) {
edgeDirection[i] = Vector3::zero();
} else {
edgeDirection[i] = e / (float)edgeMagnitude[i];
}
}
_edge01 = _vertex[1] - _vertex[0];
_edge02 = _vertex[2] - _vertex[0];
_primaryAxis = _plane.normal().primaryAxis();
_area = 0.5f * edgeDirection[0].cross(edgeDirection[2]).magnitude() * (edgeMagnitude[0] * edgeMagnitude[2]);
//0.5f * (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).dot(_plane.normal());
}
Triangle::Triangle() {
init(Vector3::zero(), Vector3::zero(), Vector3::zero());
}
Triangle::Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
init(v0, v1, v2);
}
Triangle::~Triangle() {
}
Triangle::Triangle(class BinaryInput& b) {
deserialize(b);
}
void Triangle::serialize(class BinaryOutput& b) {
_vertex[0].serialize(b);
_vertex[1].serialize(b);
_vertex[2].serialize(b);
}
void Triangle::deserialize(class BinaryInput& b) {
_vertex[0].deserialize(b);
_vertex[1].deserialize(b);
_vertex[2].deserialize(b);
init(_vertex[0], _vertex[1], _vertex[2]);
}
float Triangle::area() const {
return _area;
}
const Vector3& Triangle::normal() const {
return _plane.normal();
}
const Plane& Triangle::plane() const {
return _plane;
}
Vector3 Triangle::center() const {
return (_vertex[0] + _vertex[1] + _vertex[2]) / 3.0;
}
Vector3 Triangle::randomPoint() const {
// Choose a random point in the parallelogram
float s = uniformRandom();
float t = uniformRandom();
if (t > 1.0f - s) {
// Outside the triangle; reflect about the
// diagonal of the parallelogram
t = 1.0f - t;
s = 1.0f - s;
}
return _edge01 * s + _edge02 * t + _vertex[0];
}
void Triangle::getBounds(AABox& out) const {
Vector3 lo = _vertex[0];
Vector3 hi = lo;
for (int i = 1; i < 3; ++i) {
lo = lo.min(_vertex[i]);
hi = hi.max(_vertex[i]);
}
out = AABox(lo, hi);
}
bool Triangle::intersect(const Ray& ray, float& distance, float baryCoord[3]) const {
static const float EPS = 1e-5f;
// See RTR2 ch. 13.7 for the algorithm.
const Vector3& e1 = edge01();
const Vector3& e2 = edge02();
const Vector3 p(ray.direction().cross(e2));
const float a = e1.dot(p);
if (abs(a) < EPS) {
// Determinant is ill-conditioned; abort early
return false;
}
const float f = 1.0f / a;
const Vector3 s(ray.origin() - vertex(0));
const float u = f * s.dot(p);
if ((u < 0.0f) || (u > 1.0f)) {
// We hit the plane of the m_geometry, but outside the m_geometry
return false;
}
const Vector3 q(s.cross(e1));
const float v = f * ray.direction().dot(q);
if ((v < 0.0f) || ((u + v) > 1.0f)) {
// We hit the plane of the triangle, but outside the triangle
return false;
}
const float t = f * e2.dot(q);
if ((t > 0.0f) && (t < distance)) {
// This is a new hit, closer than the previous one
distance = t;
baryCoord[0] = 1.0 - u - v;
baryCoord[1] = u;
baryCoord[2] = v;
return true;
} else {
// This hit is after the previous hit, so ignore it
return false;
}
}
} // G3D

View File

@@ -0,0 +1,132 @@
/**
@file UprightFrame.cpp
Box class
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-05-02
@edited 2007-05-05
*/
#include "G3D/UprightFrame.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
UprightFrame::UprightFrame(const CoordinateFrame& cframe) {
Vector3 look = cframe.lookVector();
yaw = G3D::pi() + atan2(look.x, look.z);
pitch = asin(look.y);
translation = cframe.translation;
}
CoordinateFrame UprightFrame::toCoordinateFrame() const {
CoordinateFrame cframe;
Matrix3 P(Matrix3::fromAxisAngle(Vector3::unitX(), pitch));
Matrix3 Y(Matrix3::fromAxisAngle(Vector3::unitY(), yaw));
cframe.rotation = Y * P;
cframe.translation = translation;
return cframe;
}
UprightFrame UprightFrame::operator+(const UprightFrame& other) const {
return UprightFrame(translation + other.translation, pitch + other.pitch, yaw + other.yaw);
}
UprightFrame UprightFrame::operator*(const float k) const {
return UprightFrame(translation * k, pitch * k, yaw * k);
}
void UprightFrame::unwrapYaw(UprightFrame* a, int N) {
// Use the first point to establish the wrapping convention
for (int i = 1; i < N; ++i) {
const float prev = a[i - 1].yaw;
float& cur = a[i].yaw;
// No two angles should be more than pi (i.e., 180-degrees) apart.
if (abs(cur - prev) > G3D::pi()) {
// These angles must have wrapped at zero, causing them
// to be interpolated the long way.
// Find canonical [0, 2pi] versions of these numbers
float p = wrap(prev, twoPi());
float c = wrap(cur, twoPi());
// Find the difference -pi < diff < pi between the current and previous values
float diff = c - p;
if (diff < -G3D::pi()) {
diff += twoPi();
} else if (diff > G3D::pi()) {
diff -= twoPi();
}
// Offset the current from the previous by the difference
// between them.
cur = prev + diff;
}
}
}
void UprightFrame::serialize(class BinaryOutput& b) const {
translation.serialize(b);
b.writeFloat32(pitch);
b.writeFloat32(yaw);
}
void UprightFrame::deserialize(class BinaryInput& b) {
translation.deserialize(b);
pitch = b.readFloat32();
yaw = b.readFloat32();
}
void UprightSpline::serialize(class BinaryOutput& b) const {
b.writeBool8(cyclic);
b.writeInt32(control.size());
for (int i = 0; i < control.size(); ++i) {
control[i].serialize(b);
}
b.writeInt32(time.size());
for (int i = 0; i < time.size(); ++i) {
b.writeFloat32(time[i]);
}
}
void UprightSpline::deserialize(class BinaryInput& b) {
cyclic = b.readBool8();
control.resize(b.readInt32());
for (int i = 0; i < control.size(); ++i) {
control[i].deserialize(b);
}
if (b.hasMore()) {
time.resize(b.readInt32());
for (int i = 0; i < time.size(); ++i) {
time[i] = b.readFloat32();
}
debugAssert(time.size() == control.size());
} else {
// Import legacy path
time.resize(control.size());
for (int i = 0; i < time.size(); ++i) {
time[i] = i;
}
}
}
}

View File

@@ -0,0 +1,224 @@
/**
@file Vector2.cpp
2D vector class, used for texture coordinates primarily.
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@cite Portions based on Dave Eberly'x Magic Software Library
at http://www.magic-software.com
@created 2001-06-02
@edited 2009-11-16
*/
#include "G3D/platform.h"
#include <stdlib.h>
#include "G3D/Vector2.h"
#include "G3D/g3dmath.h"
#include "G3D/format.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#include "G3D/Any.h"
namespace G3D {
Vector2::Vector2(const Any& any) {
any.verifyName("Vector2");
any.verifyType(Any::TABLE, Any::ARRAY);
any.verifySize(2);
if (any.type() == Any::ARRAY) {
x = any[0];
y = any[1];
} else {
// Table
x = any["x"];
y = any["y"];
}
}
Vector2::operator Any() const {
Any any(Any::ARRAY, "Vector2");
any.append(x, y);
return any;
}
const Vector2& Vector2::one() {
static const Vector2 v(1, 1); return v;
}
const Vector2& Vector2::zero() {
static Vector2 v(0, 0);
return v;
}
const Vector2& Vector2::unitX() {
static Vector2 v(1, 0);
return v;
}
const Vector2& Vector2::unitY() {
static Vector2 v(0, 1);
return v;
}
const Vector2& Vector2::inf() {
static Vector2 v((float)G3D::finf(), (float)G3D::finf());
return v;
}
const Vector2& Vector2::nan() {
static Vector2 v((float)G3D::fnan(), (float)G3D::fnan());
return v;
}
const Vector2& Vector2::minFinite() {
static Vector2 v(-FLT_MAX, -FLT_MAX);
return v;
}
const Vector2& Vector2::maxFinite() {
static Vector2 v(FLT_MAX, FLT_MAX);
return v;
}
size_t Vector2::hashCode() const {
unsigned int xhash = (*(int*)(void*)(&x));
unsigned int yhash = (*(int*)(void*)(&y));
return xhash + (yhash * 37);
}
Vector2::Vector2(BinaryInput& b) {
deserialize(b);
}
void Vector2::deserialize(BinaryInput& b) {
x = b.readFloat32();
y = b.readFloat32();
}
void Vector2::serialize(BinaryOutput& b) const {
b.writeFloat32(x);
b.writeFloat32(y);
}
void Vector2::deserialize(TextInput& t) {
t.readSymbol("(");
x = (float)t.readNumber();
t.readSymbol(",");
y = (float)t.readNumber();
t.readSymbol(")");
}
void Vector2::serialize(TextOutput& t) const {
t.writeSymbol("(");
t.writeNumber(x);
t.writeSymbol(",");
t.writeNumber(y);
t.writeSymbol(")");
}
//----------------------------------------------------------------------------
Vector2 Vector2::random(G3D::Random& r) {
Vector2 result;
do {
result = Vector2(r.uniform(-1, 1), r.uniform(-1, 1));
} while (result.squaredLength() >= 1.0f);
result.unitize();
return result;
}
Vector2 Vector2::operator/ (float k) const {
return *this * (1.0f / k);
}
Vector2& Vector2::operator/= (float k) {
this->x /= k;
this->y /= k;
return *this;
}
//----------------------------------------------------------------------------
float Vector2::unitize (float fTolerance) {
float fLength = length();
if (fLength > fTolerance) {
float fInvLength = 1.0f / fLength;
x *= fInvLength;
y *= fInvLength;
} else {
fLength = 0.0;
}
return fLength;
}
//----------------------------------------------------------------------------
std::string Vector2::toString() const {
return G3D::format("(%g, %g)", x, y);
}
// 2-char swizzles
Vector2 Vector2::xx() const { return Vector2 (x, x); }
Vector2 Vector2::yx() const { return Vector2 (y, x); }
Vector2 Vector2::xy() const { return Vector2 (x, y); }
Vector2 Vector2::yy() const { return Vector2 (y, y); }
// 3-char swizzles
Vector3 Vector2::xxx() const { return Vector3 (x, x, x); }
Vector3 Vector2::yxx() const { return Vector3 (y, x, x); }
Vector3 Vector2::xyx() const { return Vector3 (x, y, x); }
Vector3 Vector2::yyx() const { return Vector3 (y, y, x); }
Vector3 Vector2::xxy() const { return Vector3 (x, x, y); }
Vector3 Vector2::yxy() const { return Vector3 (y, x, y); }
Vector3 Vector2::xyy() const { return Vector3 (x, y, y); }
Vector3 Vector2::yyy() const { return Vector3 (y, y, y); }
// 4-char swizzles
Vector4 Vector2::xxxx() const { return Vector4 (x, x, x, x); }
Vector4 Vector2::yxxx() const { return Vector4 (y, x, x, x); }
Vector4 Vector2::xyxx() const { return Vector4 (x, y, x, x); }
Vector4 Vector2::yyxx() const { return Vector4 (y, y, x, x); }
Vector4 Vector2::xxyx() const { return Vector4 (x, x, y, x); }
Vector4 Vector2::yxyx() const { return Vector4 (y, x, y, x); }
Vector4 Vector2::xyyx() const { return Vector4 (x, y, y, x); }
Vector4 Vector2::yyyx() const { return Vector4 (y, y, y, x); }
Vector4 Vector2::xxxy() const { return Vector4 (x, x, x, y); }
Vector4 Vector2::yxxy() const { return Vector4 (y, x, x, y); }
Vector4 Vector2::xyxy() const { return Vector4 (x, y, x, y); }
Vector4 Vector2::yyxy() const { return Vector4 (y, y, x, y); }
Vector4 Vector2::xxyy() const { return Vector4 (x, x, y, y); }
Vector4 Vector2::yxyy() const { return Vector4 (y, x, y, y); }
Vector4 Vector2::xyyy() const { return Vector4 (x, y, y, y); }
Vector4 Vector2::yyyy() const { return Vector4 (y, y, y, y); }
} // namespace

View File

@@ -0,0 +1,47 @@
/**
@file Vector2int16.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-08-09
@edited 2006-01-29
*/
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/Vector2int16.h"
#include "G3D/Vector2.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
namespace G3D {
Vector2int16::Vector2int16(const class Vector2& v) {
x = (int16)iFloor(v.x + 0.5);
y = (int16)iFloor(v.y + 0.5);
}
Vector2int16::Vector2int16(class BinaryInput& bi) {
deserialize(bi);
}
void Vector2int16::serialize(class BinaryOutput& bo) const {
bo.writeInt16(x);
bo.writeInt16(y);
}
void Vector2int16::deserialize(class BinaryInput& bi) {
x = bi.readInt16();
y = bi.readInt16();
}
Vector2int16 Vector2int16::clamp(const Vector2int16& lo, const Vector2int16& hi) {
return Vector2int16(iClamp(x, lo.x, hi.x), iClamp(y, lo.y, hi.y));
}
}

View File

@@ -0,0 +1,504 @@
/**
@file Vector3.cpp
3D vector class
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
@created 2001-06-02
@edited 2009-11-27
*/
#include <limits>
#include <stdlib.h>
#include "G3D/Vector3.h"
#include "G3D/g3dmath.h"
#include "G3D/stringutils.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#include "G3D/Vector3int16.h"
#include "G3D/Matrix3.h"
#include "G3D/Vector2.h"
#include "G3D/Color3.h"
#include "G3D/Vector4int8.h"
#include "G3D/Vector4.h"
#include "G3D/Vector3int32.h"
#include "G3D/Any.h"
namespace G3D {
Vector3::Vector3(const Any& any) {
any.verifyName("Vector3");
any.verifyType(Any::TABLE, Any::ARRAY);
any.verifySize(3);
if (any.type() == Any::ARRAY) {
x = any[0];
y = any[1];
z = any[2];
} else {
// Table
x = any["x"];
y = any["y"];
z = any["z"];
}
}
Vector3::operator Any() const {
Any any(Any::ARRAY, "Vector3");
any.append(x, y, z);
return any;
}
Vector3::Vector3(const class Color3& v) : x(v.r), y(v.g), z(v.b) {}
Vector3::Vector3(const class Vector3int32& v) : x((float)v.x), y((float)v.y), z((float)v.z) {}
Vector3::Vector3(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f) {}
Vector3::Vector3(const class Vector2& v, float _z) : x(v.x), y(v.y), z(_z) {
}
Vector3& Vector3::ignore() {
static Vector3 v;
return v;
}
const Vector3& Vector3::zero() { static const Vector3 v(0, 0, 0); return v; }
const Vector3& Vector3::one() { static const Vector3 v(1, 1, 1); return v; }
const Vector3& Vector3::unitX() { static const Vector3 v(1, 0, 0); return v; }
const Vector3& Vector3::unitY() { static const Vector3 v(0, 1, 0); return v; }
const Vector3& Vector3::unitZ() { static const Vector3 v(0, 0, 1); return v; }
const Vector3& Vector3::inf() { static const Vector3 v((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf()); return v; }
const Vector3& Vector3::nan() { static const Vector3 v((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan()); return v; }
const Vector3& Vector3::minFinite(){ static const Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; }
const Vector3& Vector3::maxFinite(){ static const Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; }
Vector3::Axis Vector3::primaryAxis() const {
Axis a = X_AXIS;
double nx = abs(x);
double ny = abs(y);
double nz = abs(z);
if (nx > ny) {
if (nx > nz) {
a = X_AXIS;
} else {
a = Z_AXIS;
}
} else {
if (ny > nz) {
a = Y_AXIS;
} else {
a = Z_AXIS;
}
}
return a;
}
size_t Vector3::hashCode() const {
return Vector4(*this, 0.0f).hashCode();
}
std::ostream& operator<<(std::ostream& os, const Vector3& v) {
return os << v.toString();
}
//----------------------------------------------------------------------------
double frand() {
return rand() / (double) RAND_MAX;
}
Vector3::Vector3(TextInput& t) {
deserialize(t);
}
Vector3::Vector3(BinaryInput& b) {
deserialize(b);
}
Vector3::Vector3(const class Vector3int16& v) {
x = v.x;
y = v.y;
z = v.z;
}
void Vector3::deserialize(BinaryInput& b) {
x = b.readFloat32();
y = b.readFloat32();
z = b.readFloat32();
}
void Vector3::deserialize(TextInput& t) {
t.readSymbol("(");
x = (float)t.readNumber();
t.readSymbol(",");
y = (float)t.readNumber();
t.readSymbol(",");
z = (float)t.readNumber();
t.readSymbol(")");
}
void Vector3::serialize(TextOutput& t) const {
t.writeSymbol("(");
t.writeNumber(x);
t.writeSymbol(",");
t.writeNumber(y);
t.writeSymbol(",");
t.writeNumber(z);
t.writeSymbol(")");
}
void Vector3::serialize(BinaryOutput& b) const {
b.writeFloat32(x);
b.writeFloat32(y);
b.writeFloat32(z);
}
Vector3 Vector3::random(Random& r) {
Vector3 result;
r.sphere(result.x, result.y, result.z);
return result;
}
float Vector3::unitize(float fTolerance) {
float fMagnitude = magnitude();
if (fMagnitude > fTolerance) {
float fInvMagnitude = 1.0f / fMagnitude;
x *= fInvMagnitude;
y *= fInvMagnitude;
z *= fInvMagnitude;
} else {
fMagnitude = 0.0f;
}
return fMagnitude;
}
Vector3 Vector3::reflectAbout(const Vector3& normal) const {
Vector3 out;
Vector3 N = normal.direction();
// 2 * normal.dot(this) * normal - this
return N * 2 * this->dot(N) - *this;
}
Vector3 Vector3::cosHemiRandom(const Vector3& normal, Random& r) {
debugAssertM(G3D::fuzzyEq(normal.length(), 1.0f),
"cosHemiRandom requires its argument to have unit length");
float x, y, z;
r.cosHemi(x, y, z);
// Make a coordinate system
const Vector3& Z = normal;
Vector3 X, Y;
normal.getTangents(X, Y);
return
x * X +
y * Y +
z * Z;
}
Vector3 Vector3::cosPowHemiRandom(const Vector3& normal, const float k, Random& r) {
debugAssertM(G3D::fuzzyEq(normal.length(), 1.0f),
"cosPowHemiRandom requires its argument to have unit length");
float x, y, z;
r.cosPowHemi(k, x, y, z);
// Make a coordinate system
const Vector3& Z = normal;
Vector3 X, Y;
normal.getTangents(X, Y);
return
x * X +
y * Y +
z * Z;
}
Vector3 Vector3::hemiRandom(const Vector3& normal, Random& r) {
const Vector3& V = Vector3::random(r);
if (V.dot(normal) < 0) {
return -V;
} else {
return V;
}
}
//----------------------------------------------------------------------------
Vector3 Vector3::reflectionDirection(const Vector3& normal) const {
return -reflectAbout(normal).direction();
}
//----------------------------------------------------------------------------
Vector3 Vector3::refractionDirection(
const Vector3& normal,
float iInside,
float iOutside) const {
// From pg. 24 of Henrik Wann Jensen. Realistic Image Synthesis
// Using Photon Mapping. AK Peters. ISBN: 1568811470. July 2001.
// Invert the directions from Wann Jensen's formulation
// and normalize the vectors.
const Vector3 W = -direction();
Vector3 N = normal.direction();
float h1 = iOutside;
float h2 = iInside;
if (normal.dot(*this) > 0.0f) {
h1 = iInside;
h2 = iOutside;
N = -N;
}
const float hRatio = h1 / h2;
const float WdotN = W.dot(N);
float det = 1.0f - (float)square(hRatio) * (1.0f - (float)square(WdotN));
if (det < 0) {
// Total internal reflection
return Vector3::zero();
} else {
return -hRatio * (W - WdotN * N) - N * sqrt(det);
}
}
//----------------------------------------------------------------------------
void Vector3::orthonormalize (Vector3 akVector[3]) {
// If the input vectors are v0, v1, and v2, then the Gram-Schmidt
// orthonormalization produces vectors u0, u1, and u2 as follows,
//
// u0 = v0/|v0|
// u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0|
// u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1|
//
// where |A| indicates length of vector A and A*B indicates dot
// product of vectors A and B.
// compute u0
akVector[0].unitize();
// compute u1
float fDot0 = akVector[0].dot(akVector[1]);
akVector[1] -= akVector[0] * fDot0;
akVector[1].unitize();
// compute u2
float fDot1 = akVector[1].dot(akVector[2]);
fDot0 = akVector[0].dot(akVector[2]);
akVector[2] -= akVector[0] * fDot0 + akVector[1] * fDot1;
akVector[2].unitize();
}
//----------------------------------------------------------------------------
void Vector3::generateOrthonormalBasis (Vector3& rkU, Vector3& rkV,
Vector3& rkW, bool bUnitLengthW) {
if ( !bUnitLengthW )
rkW.unitize();
if ( G3D::abs(rkW.x) >= G3D::abs(rkW.y)
&& G3D::abs(rkW.x) >= G3D::abs(rkW.z) ) {
rkU.x = -rkW.y;
rkU.y = + rkW.x;
rkU.z = 0.0;
} else {
rkU.x = 0.0;
rkU.y = + rkW.z;
rkU.z = -rkW.y;
}
rkU.unitize();
rkV = rkW.cross(rkU);
}
//----------------------------------------------------------------------------
std::string Vector3::toString() const {
return G3D::format("(%g, %g, %g)", x, y, z);
}
//----------------------------------------------------------------------------
Matrix3 Vector3::cross() const {
return Matrix3( 0, -z, y,
z, 0, -x,
-y, x, 0);
}
void serialize(const Vector3::Axis& a, class BinaryOutput& bo) {
bo.writeUInt8((uint8)a);
}
void deserialize(Vector3::Axis& a, class BinaryInput& bi) {
a = (Vector3::Axis)bi.readUInt8();
}
//----------------------------------------------------------------------------
// 2-char swizzles
Vector2 Vector3::xx() const { return Vector2 (x, x); }
Vector2 Vector3::yx() const { return Vector2 (y, x); }
Vector2 Vector3::zx() const { return Vector2 (z, x); }
Vector2 Vector3::xy() const { return Vector2 (x, y); }
Vector2 Vector3::yy() const { return Vector2 (y, y); }
Vector2 Vector3::zy() const { return Vector2 (z, y); }
Vector2 Vector3::xz() const { return Vector2 (x, z); }
Vector2 Vector3::yz() const { return Vector2 (y, z); }
Vector2 Vector3::zz() const { return Vector2 (z, z); }
// 3-char swizzles
Vector3 Vector3::xxx() const { return Vector3 (x, x, x); }
Vector3 Vector3::yxx() const { return Vector3 (y, x, x); }
Vector3 Vector3::zxx() const { return Vector3 (z, x, x); }
Vector3 Vector3::xyx() const { return Vector3 (x, y, x); }
Vector3 Vector3::yyx() const { return Vector3 (y, y, x); }
Vector3 Vector3::zyx() const { return Vector3 (z, y, x); }
Vector3 Vector3::xzx() const { return Vector3 (x, z, x); }
Vector3 Vector3::yzx() const { return Vector3 (y, z, x); }
Vector3 Vector3::zzx() const { return Vector3 (z, z, x); }
Vector3 Vector3::xxy() const { return Vector3 (x, x, y); }
Vector3 Vector3::yxy() const { return Vector3 (y, x, y); }
Vector3 Vector3::zxy() const { return Vector3 (z, x, y); }
Vector3 Vector3::xyy() const { return Vector3 (x, y, y); }
Vector3 Vector3::yyy() const { return Vector3 (y, y, y); }
Vector3 Vector3::zyy() const { return Vector3 (z, y, y); }
Vector3 Vector3::xzy() const { return Vector3 (x, z, y); }
Vector3 Vector3::yzy() const { return Vector3 (y, z, y); }
Vector3 Vector3::zzy() const { return Vector3 (z, z, y); }
Vector3 Vector3::xxz() const { return Vector3 (x, x, z); }
Vector3 Vector3::yxz() const { return Vector3 (y, x, z); }
Vector3 Vector3::zxz() const { return Vector3 (z, x, z); }
Vector3 Vector3::xyz() const { return Vector3 (x, y, z); }
Vector3 Vector3::yyz() const { return Vector3 (y, y, z); }
Vector3 Vector3::zyz() const { return Vector3 (z, y, z); }
Vector3 Vector3::xzz() const { return Vector3 (x, z, z); }
Vector3 Vector3::yzz() const { return Vector3 (y, z, z); }
Vector3 Vector3::zzz() const { return Vector3 (z, z, z); }
// 4-char swizzles
Vector4 Vector3::xxxx() const { return Vector4 (x, x, x, x); }
Vector4 Vector3::yxxx() const { return Vector4 (y, x, x, x); }
Vector4 Vector3::zxxx() const { return Vector4 (z, x, x, x); }
Vector4 Vector3::xyxx() const { return Vector4 (x, y, x, x); }
Vector4 Vector3::yyxx() const { return Vector4 (y, y, x, x); }
Vector4 Vector3::zyxx() const { return Vector4 (z, y, x, x); }
Vector4 Vector3::xzxx() const { return Vector4 (x, z, x, x); }
Vector4 Vector3::yzxx() const { return Vector4 (y, z, x, x); }
Vector4 Vector3::zzxx() const { return Vector4 (z, z, x, x); }
Vector4 Vector3::xxyx() const { return Vector4 (x, x, y, x); }
Vector4 Vector3::yxyx() const { return Vector4 (y, x, y, x); }
Vector4 Vector3::zxyx() const { return Vector4 (z, x, y, x); }
Vector4 Vector3::xyyx() const { return Vector4 (x, y, y, x); }
Vector4 Vector3::yyyx() const { return Vector4 (y, y, y, x); }
Vector4 Vector3::zyyx() const { return Vector4 (z, y, y, x); }
Vector4 Vector3::xzyx() const { return Vector4 (x, z, y, x); }
Vector4 Vector3::yzyx() const { return Vector4 (y, z, y, x); }
Vector4 Vector3::zzyx() const { return Vector4 (z, z, y, x); }
Vector4 Vector3::xxzx() const { return Vector4 (x, x, z, x); }
Vector4 Vector3::yxzx() const { return Vector4 (y, x, z, x); }
Vector4 Vector3::zxzx() const { return Vector4 (z, x, z, x); }
Vector4 Vector3::xyzx() const { return Vector4 (x, y, z, x); }
Vector4 Vector3::yyzx() const { return Vector4 (y, y, z, x); }
Vector4 Vector3::zyzx() const { return Vector4 (z, y, z, x); }
Vector4 Vector3::xzzx() const { return Vector4 (x, z, z, x); }
Vector4 Vector3::yzzx() const { return Vector4 (y, z, z, x); }
Vector4 Vector3::zzzx() const { return Vector4 (z, z, z, x); }
Vector4 Vector3::xxxy() const { return Vector4 (x, x, x, y); }
Vector4 Vector3::yxxy() const { return Vector4 (y, x, x, y); }
Vector4 Vector3::zxxy() const { return Vector4 (z, x, x, y); }
Vector4 Vector3::xyxy() const { return Vector4 (x, y, x, y); }
Vector4 Vector3::yyxy() const { return Vector4 (y, y, x, y); }
Vector4 Vector3::zyxy() const { return Vector4 (z, y, x, y); }
Vector4 Vector3::xzxy() const { return Vector4 (x, z, x, y); }
Vector4 Vector3::yzxy() const { return Vector4 (y, z, x, y); }
Vector4 Vector3::zzxy() const { return Vector4 (z, z, x, y); }
Vector4 Vector3::xxyy() const { return Vector4 (x, x, y, y); }
Vector4 Vector3::yxyy() const { return Vector4 (y, x, y, y); }
Vector4 Vector3::zxyy() const { return Vector4 (z, x, y, y); }
Vector4 Vector3::xyyy() const { return Vector4 (x, y, y, y); }
Vector4 Vector3::yyyy() const { return Vector4 (y, y, y, y); }
Vector4 Vector3::zyyy() const { return Vector4 (z, y, y, y); }
Vector4 Vector3::xzyy() const { return Vector4 (x, z, y, y); }
Vector4 Vector3::yzyy() const { return Vector4 (y, z, y, y); }
Vector4 Vector3::zzyy() const { return Vector4 (z, z, y, y); }
Vector4 Vector3::xxzy() const { return Vector4 (x, x, z, y); }
Vector4 Vector3::yxzy() const { return Vector4 (y, x, z, y); }
Vector4 Vector3::zxzy() const { return Vector4 (z, x, z, y); }
Vector4 Vector3::xyzy() const { return Vector4 (x, y, z, y); }
Vector4 Vector3::yyzy() const { return Vector4 (y, y, z, y); }
Vector4 Vector3::zyzy() const { return Vector4 (z, y, z, y); }
Vector4 Vector3::xzzy() const { return Vector4 (x, z, z, y); }
Vector4 Vector3::yzzy() const { return Vector4 (y, z, z, y); }
Vector4 Vector3::zzzy() const { return Vector4 (z, z, z, y); }
Vector4 Vector3::xxxz() const { return Vector4 (x, x, x, z); }
Vector4 Vector3::yxxz() const { return Vector4 (y, x, x, z); }
Vector4 Vector3::zxxz() const { return Vector4 (z, x, x, z); }
Vector4 Vector3::xyxz() const { return Vector4 (x, y, x, z); }
Vector4 Vector3::yyxz() const { return Vector4 (y, y, x, z); }
Vector4 Vector3::zyxz() const { return Vector4 (z, y, x, z); }
Vector4 Vector3::xzxz() const { return Vector4 (x, z, x, z); }
Vector4 Vector3::yzxz() const { return Vector4 (y, z, x, z); }
Vector4 Vector3::zzxz() const { return Vector4 (z, z, x, z); }
Vector4 Vector3::xxyz() const { return Vector4 (x, x, y, z); }
Vector4 Vector3::yxyz() const { return Vector4 (y, x, y, z); }
Vector4 Vector3::zxyz() const { return Vector4 (z, x, y, z); }
Vector4 Vector3::xyyz() const { return Vector4 (x, y, y, z); }
Vector4 Vector3::yyyz() const { return Vector4 (y, y, y, z); }
Vector4 Vector3::zyyz() const { return Vector4 (z, y, y, z); }
Vector4 Vector3::xzyz() const { return Vector4 (x, z, y, z); }
Vector4 Vector3::yzyz() const { return Vector4 (y, z, y, z); }
Vector4 Vector3::zzyz() const { return Vector4 (z, z, y, z); }
Vector4 Vector3::xxzz() const { return Vector4 (x, x, z, z); }
Vector4 Vector3::yxzz() const { return Vector4 (y, x, z, z); }
Vector4 Vector3::zxzz() const { return Vector4 (z, x, z, z); }
Vector4 Vector3::xyzz() const { return Vector4 (x, y, z, z); }
Vector4 Vector3::yyzz() const { return Vector4 (y, y, z, z); }
Vector4 Vector3::zyzz() const { return Vector4 (z, y, z, z); }
Vector4 Vector3::xzzz() const { return Vector4 (x, z, z, z); }
Vector4 Vector3::yzzz() const { return Vector4 (y, z, z, z); }
Vector4 Vector3::zzzz() const { return Vector4 (z, z, z, z); }
} // namespace

View File

@@ -0,0 +1,49 @@
/**
@file Vector3int16.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-04-07
@edited 2006-01-17
*/
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/Vector3int16.h"
#include "G3D/Vector3.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/format.h"
namespace G3D {
Vector3int16::Vector3int16(const class Vector3& v) {
x = (int16)iFloor(v.x + 0.5);
y = (int16)iFloor(v.y + 0.5);
z = (int16)iFloor(v.z + 0.5);
}
Vector3int16::Vector3int16(class BinaryInput& bi) {
deserialize(bi);
}
void Vector3int16::serialize(class BinaryOutput& bo) const {
bo.writeInt16(x);
bo.writeInt16(y);
bo.writeInt16(z);
}
void Vector3int16::deserialize(class BinaryInput& bi) {
x = bi.readInt16();
y = bi.readInt16();
z = bi.readInt16();
}
std::string Vector3int16::toString() const {
return G3D::format("(%d, %d, %d)", x, y, z);
}
}

View File

@@ -0,0 +1,57 @@
/**
@file Vector3int32.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2008-07-01
@edited 2008-07-01
*/
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/Vector3int32.h"
#include "G3D/Vector3int16.h"
#include "G3D/Vector3.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/format.h"
namespace G3D {
Vector3int32::Vector3int32(const class Vector3& v) {
x = (int32)iFloor(v.x + 0.5);
y = (int32)iFloor(v.y + 0.5);
z = (int32)iFloor(v.z + 0.5);
}
Vector3int32::Vector3int32(const class Vector3int16& v) {
x = v.x;
y = v.y;
z = v.z;
}
Vector3int32::Vector3int32(class BinaryInput& bi) {
deserialize(bi);
}
void Vector3int32::serialize(class BinaryOutput& bo) const {
bo.writeInt32(x);
bo.writeInt32(y);
bo.writeInt32(z);
}
void Vector3int32::deserialize(class BinaryInput& bi) {
x = bi.readInt32();
y = bi.readInt32();
z = bi.readInt32();
}
std::string Vector3int32::toString() const {
return G3D::format("(%d, %d, %d)", x, y, z);
}
}

View File

@@ -0,0 +1,515 @@
/**
@file Vector4.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-07-09
@edited 2010-07-05
*/
#include <stdlib.h>
#include <limits>
#include "G3D/Vector4.h"
#include "G3D/Color4.h"
#include "G3D/g3dmath.h"
#include "G3D/stringutils.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Vector4int8.h"
#include "G3D/Matrix4.h"
#include "G3D/Any.h"
namespace G3D {
Vector4::Vector4(const Any& any) {
any.verifyName("Vector4");
any.verifyType(Any::TABLE, Any::ARRAY);
any.verifySize(4);
if (any.type() == Any::ARRAY) {
x = any[0];
y = any[1];
z = any[2];
w = any[3];
} else {
// Table
x = any["x"];
y = any["y"];
z = any["z"];
w = any["w"];
}
}
Vector4::operator Any() const {
Any any(Any::ARRAY, "Vector4");
any.append(x, y, z, w);
return any;
}
Vector4::Vector4(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f), w(v.w / 127.0f) {
}
const Vector4& Vector4::inf() {
static const Vector4 v((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf());
return v;
}
const Vector4& Vector4::zero() {
static const Vector4 v(0,0,0,0);
return v;
}
const Vector4& Vector4::nan() {
static Vector4 v((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan());
return v;
}
size_t Vector4::hashCode() const {
return HashTrait<uint128>::hashCode(*((uint128*)this));
}
Vector4::Vector4(const class Color4& c) {
x = c.r;
y = c.g;
z = c.b;
w = c.a;
}
Vector4::Vector4(const Vector2& v1, const Vector2& v2) {
x = v1.x;
y = v1.y;
z = v2.x;
w = v2.y;
}
Vector4::Vector4(const Vector2& v1, float fz, float fw) {
x = v1.x;
y = v1.y;
z = fz;
w = fw;
}
Vector4::Vector4(BinaryInput& b) {
deserialize(b);
}
void Vector4::deserialize(BinaryInput& b) {
x = b.readFloat32();
y = b.readFloat32();
z = b.readFloat32();
w = b.readFloat32();
}
void Vector4::serialize(BinaryOutput& b) const {
b.writeFloat32(x);
b.writeFloat32(y);
b.writeFloat32(z);
b.writeFloat32(w);
}
//----------------------------------------------------------------------------
Vector4 Vector4::operator*(const Matrix4& M) const {
Vector4 result;
for (int i = 0; i < 4; ++i) {
result[i] = 0.0f;
for (int j = 0; j < 4; ++j) {
result[i] += (*this)[j] * M[j][i];
}
}
return result;
}
Vector4 Vector4::operator/ (float fScalar) const {
Vector4 kQuot;
if ( fScalar != 0.0 ) {
float fInvScalar = 1.0f / fScalar;
kQuot.x = fInvScalar * x;
kQuot.y = fInvScalar * y;
kQuot.z = fInvScalar * z;
kQuot.w = fInvScalar * w;
return kQuot;
} else {
return Vector4::inf();
}
}
//----------------------------------------------------------------------------
Vector4& Vector4::operator/= (float fScalar) {
if (fScalar != 0.0f) {
float fInvScalar = 1.0f / fScalar;
x *= fInvScalar;
y *= fInvScalar;
z *= fInvScalar;
w *= fInvScalar;
} else {
*this = Vector4::inf();
}
return *this;
}
//----------------------------------------------------------------------------
std::string Vector4::toString() const {
return G3D::format("(%g, %g, %g, %g)", x, y, z, w);
}
// 2-char swizzles
Vector2 Vector4::xx() const { return Vector2 (x, x); }
Vector2 Vector4::yx() const { return Vector2 (y, x); }
Vector2 Vector4::zx() const { return Vector2 (z, x); }
Vector2 Vector4::wx() const { return Vector2 (w, x); }
Vector2 Vector4::xy() const { return Vector2 (x, y); }
Vector2 Vector4::yy() const { return Vector2 (y, y); }
Vector2 Vector4::zy() const { return Vector2 (z, y); }
Vector2 Vector4::wy() const { return Vector2 (w, y); }
Vector2 Vector4::xz() const { return Vector2 (x, z); }
Vector2 Vector4::yz() const { return Vector2 (y, z); }
Vector2 Vector4::zz() const { return Vector2 (z, z); }
Vector2 Vector4::wz() const { return Vector2 (w, z); }
Vector2 Vector4::xw() const { return Vector2 (x, w); }
Vector2 Vector4::yw() const { return Vector2 (y, w); }
Vector2 Vector4::zw() const { return Vector2 (z, w); }
Vector2 Vector4::ww() const { return Vector2 (w, w); }
// 3-char swizzles
Vector3 Vector4::xxx() const { return Vector3 (x, x, x); }
Vector3 Vector4::yxx() const { return Vector3 (y, x, x); }
Vector3 Vector4::zxx() const { return Vector3 (z, x, x); }
Vector3 Vector4::wxx() const { return Vector3 (w, x, x); }
Vector3 Vector4::xyx() const { return Vector3 (x, y, x); }
Vector3 Vector4::yyx() const { return Vector3 (y, y, x); }
Vector3 Vector4::zyx() const { return Vector3 (z, y, x); }
Vector3 Vector4::wyx() const { return Vector3 (w, y, x); }
Vector3 Vector4::xzx() const { return Vector3 (x, z, x); }
Vector3 Vector4::yzx() const { return Vector3 (y, z, x); }
Vector3 Vector4::zzx() const { return Vector3 (z, z, x); }
Vector3 Vector4::wzx() const { return Vector3 (w, z, x); }
Vector3 Vector4::xwx() const { return Vector3 (x, w, x); }
Vector3 Vector4::ywx() const { return Vector3 (y, w, x); }
Vector3 Vector4::zwx() const { return Vector3 (z, w, x); }
Vector3 Vector4::wwx() const { return Vector3 (w, w, x); }
Vector3 Vector4::xxy() const { return Vector3 (x, x, y); }
Vector3 Vector4::yxy() const { return Vector3 (y, x, y); }
Vector3 Vector4::zxy() const { return Vector3 (z, x, y); }
Vector3 Vector4::wxy() const { return Vector3 (w, x, y); }
Vector3 Vector4::xyy() const { return Vector3 (x, y, y); }
Vector3 Vector4::yyy() const { return Vector3 (y, y, y); }
Vector3 Vector4::zyy() const { return Vector3 (z, y, y); }
Vector3 Vector4::wyy() const { return Vector3 (w, y, y); }
Vector3 Vector4::xzy() const { return Vector3 (x, z, y); }
Vector3 Vector4::yzy() const { return Vector3 (y, z, y); }
Vector3 Vector4::zzy() const { return Vector3 (z, z, y); }
Vector3 Vector4::wzy() const { return Vector3 (w, z, y); }
Vector3 Vector4::xwy() const { return Vector3 (x, w, y); }
Vector3 Vector4::ywy() const { return Vector3 (y, w, y); }
Vector3 Vector4::zwy() const { return Vector3 (z, w, y); }
Vector3 Vector4::wwy() const { return Vector3 (w, w, y); }
Vector3 Vector4::xxz() const { return Vector3 (x, x, z); }
Vector3 Vector4::yxz() const { return Vector3 (y, x, z); }
Vector3 Vector4::zxz() const { return Vector3 (z, x, z); }
Vector3 Vector4::wxz() const { return Vector3 (w, x, z); }
Vector3 Vector4::xyz() const { return Vector3 (x, y, z); }
Vector3 Vector4::yyz() const { return Vector3 (y, y, z); }
Vector3 Vector4::zyz() const { return Vector3 (z, y, z); }
Vector3 Vector4::wyz() const { return Vector3 (w, y, z); }
Vector3 Vector4::xzz() const { return Vector3 (x, z, z); }
Vector3 Vector4::yzz() const { return Vector3 (y, z, z); }
Vector3 Vector4::zzz() const { return Vector3 (z, z, z); }
Vector3 Vector4::wzz() const { return Vector3 (w, z, z); }
Vector3 Vector4::xwz() const { return Vector3 (x, w, z); }
Vector3 Vector4::ywz() const { return Vector3 (y, w, z); }
Vector3 Vector4::zwz() const { return Vector3 (z, w, z); }
Vector3 Vector4::wwz() const { return Vector3 (w, w, z); }
Vector3 Vector4::xxw() const { return Vector3 (x, x, w); }
Vector3 Vector4::yxw() const { return Vector3 (y, x, w); }
Vector3 Vector4::zxw() const { return Vector3 (z, x, w); }
Vector3 Vector4::wxw() const { return Vector3 (w, x, w); }
Vector3 Vector4::xyw() const { return Vector3 (x, y, w); }
Vector3 Vector4::yyw() const { return Vector3 (y, y, w); }
Vector3 Vector4::zyw() const { return Vector3 (z, y, w); }
Vector3 Vector4::wyw() const { return Vector3 (w, y, w); }
Vector3 Vector4::xzw() const { return Vector3 (x, z, w); }
Vector3 Vector4::yzw() const { return Vector3 (y, z, w); }
Vector3 Vector4::zzw() const { return Vector3 (z, z, w); }
Vector3 Vector4::wzw() const { return Vector3 (w, z, w); }
Vector3 Vector4::xww() const { return Vector3 (x, w, w); }
Vector3 Vector4::yww() const { return Vector3 (y, w, w); }
Vector3 Vector4::zww() const { return Vector3 (z, w, w); }
Vector3 Vector4::www() const { return Vector3 (w, w, w); }
// 4-char swizzles
Vector4 Vector4::xxxx() const { return Vector4 (x, x, x, x); }
Vector4 Vector4::yxxx() const { return Vector4 (y, x, x, x); }
Vector4 Vector4::zxxx() const { return Vector4 (z, x, x, x); }
Vector4 Vector4::wxxx() const { return Vector4 (w, x, x, x); }
Vector4 Vector4::xyxx() const { return Vector4 (x, y, x, x); }
Vector4 Vector4::yyxx() const { return Vector4 (y, y, x, x); }
Vector4 Vector4::zyxx() const { return Vector4 (z, y, x, x); }
Vector4 Vector4::wyxx() const { return Vector4 (w, y, x, x); }
Vector4 Vector4::xzxx() const { return Vector4 (x, z, x, x); }
Vector4 Vector4::yzxx() const { return Vector4 (y, z, x, x); }
Vector4 Vector4::zzxx() const { return Vector4 (z, z, x, x); }
Vector4 Vector4::wzxx() const { return Vector4 (w, z, x, x); }
Vector4 Vector4::xwxx() const { return Vector4 (x, w, x, x); }
Vector4 Vector4::ywxx() const { return Vector4 (y, w, x, x); }
Vector4 Vector4::zwxx() const { return Vector4 (z, w, x, x); }
Vector4 Vector4::wwxx() const { return Vector4 (w, w, x, x); }
Vector4 Vector4::xxyx() const { return Vector4 (x, x, y, x); }
Vector4 Vector4::yxyx() const { return Vector4 (y, x, y, x); }
Vector4 Vector4::zxyx() const { return Vector4 (z, x, y, x); }
Vector4 Vector4::wxyx() const { return Vector4 (w, x, y, x); }
Vector4 Vector4::xyyx() const { return Vector4 (x, y, y, x); }
Vector4 Vector4::yyyx() const { return Vector4 (y, y, y, x); }
Vector4 Vector4::zyyx() const { return Vector4 (z, y, y, x); }
Vector4 Vector4::wyyx() const { return Vector4 (w, y, y, x); }
Vector4 Vector4::xzyx() const { return Vector4 (x, z, y, x); }
Vector4 Vector4::yzyx() const { return Vector4 (y, z, y, x); }
Vector4 Vector4::zzyx() const { return Vector4 (z, z, y, x); }
Vector4 Vector4::wzyx() const { return Vector4 (w, z, y, x); }
Vector4 Vector4::xwyx() const { return Vector4 (x, w, y, x); }
Vector4 Vector4::ywyx() const { return Vector4 (y, w, y, x); }
Vector4 Vector4::zwyx() const { return Vector4 (z, w, y, x); }
Vector4 Vector4::wwyx() const { return Vector4 (w, w, y, x); }
Vector4 Vector4::xxzx() const { return Vector4 (x, x, z, x); }
Vector4 Vector4::yxzx() const { return Vector4 (y, x, z, x); }
Vector4 Vector4::zxzx() const { return Vector4 (z, x, z, x); }
Vector4 Vector4::wxzx() const { return Vector4 (w, x, z, x); }
Vector4 Vector4::xyzx() const { return Vector4 (x, y, z, x); }
Vector4 Vector4::yyzx() const { return Vector4 (y, y, z, x); }
Vector4 Vector4::zyzx() const { return Vector4 (z, y, z, x); }
Vector4 Vector4::wyzx() const { return Vector4 (w, y, z, x); }
Vector4 Vector4::xzzx() const { return Vector4 (x, z, z, x); }
Vector4 Vector4::yzzx() const { return Vector4 (y, z, z, x); }
Vector4 Vector4::zzzx() const { return Vector4 (z, z, z, x); }
Vector4 Vector4::wzzx() const { return Vector4 (w, z, z, x); }
Vector4 Vector4::xwzx() const { return Vector4 (x, w, z, x); }
Vector4 Vector4::ywzx() const { return Vector4 (y, w, z, x); }
Vector4 Vector4::zwzx() const { return Vector4 (z, w, z, x); }
Vector4 Vector4::wwzx() const { return Vector4 (w, w, z, x); }
Vector4 Vector4::xxwx() const { return Vector4 (x, x, w, x); }
Vector4 Vector4::yxwx() const { return Vector4 (y, x, w, x); }
Vector4 Vector4::zxwx() const { return Vector4 (z, x, w, x); }
Vector4 Vector4::wxwx() const { return Vector4 (w, x, w, x); }
Vector4 Vector4::xywx() const { return Vector4 (x, y, w, x); }
Vector4 Vector4::yywx() const { return Vector4 (y, y, w, x); }
Vector4 Vector4::zywx() const { return Vector4 (z, y, w, x); }
Vector4 Vector4::wywx() const { return Vector4 (w, y, w, x); }
Vector4 Vector4::xzwx() const { return Vector4 (x, z, w, x); }
Vector4 Vector4::yzwx() const { return Vector4 (y, z, w, x); }
Vector4 Vector4::zzwx() const { return Vector4 (z, z, w, x); }
Vector4 Vector4::wzwx() const { return Vector4 (w, z, w, x); }
Vector4 Vector4::xwwx() const { return Vector4 (x, w, w, x); }
Vector4 Vector4::ywwx() const { return Vector4 (y, w, w, x); }
Vector4 Vector4::zwwx() const { return Vector4 (z, w, w, x); }
Vector4 Vector4::wwwx() const { return Vector4 (w, w, w, x); }
Vector4 Vector4::xxxy() const { return Vector4 (x, x, x, y); }
Vector4 Vector4::yxxy() const { return Vector4 (y, x, x, y); }
Vector4 Vector4::zxxy() const { return Vector4 (z, x, x, y); }
Vector4 Vector4::wxxy() const { return Vector4 (w, x, x, y); }
Vector4 Vector4::xyxy() const { return Vector4 (x, y, x, y); }
Vector4 Vector4::yyxy() const { return Vector4 (y, y, x, y); }
Vector4 Vector4::zyxy() const { return Vector4 (z, y, x, y); }
Vector4 Vector4::wyxy() const { return Vector4 (w, y, x, y); }
Vector4 Vector4::xzxy() const { return Vector4 (x, z, x, y); }
Vector4 Vector4::yzxy() const { return Vector4 (y, z, x, y); }
Vector4 Vector4::zzxy() const { return Vector4 (z, z, x, y); }
Vector4 Vector4::wzxy() const { return Vector4 (w, z, x, y); }
Vector4 Vector4::xwxy() const { return Vector4 (x, w, x, y); }
Vector4 Vector4::ywxy() const { return Vector4 (y, w, x, y); }
Vector4 Vector4::zwxy() const { return Vector4 (z, w, x, y); }
Vector4 Vector4::wwxy() const { return Vector4 (w, w, x, y); }
Vector4 Vector4::xxyy() const { return Vector4 (x, x, y, y); }
Vector4 Vector4::yxyy() const { return Vector4 (y, x, y, y); }
Vector4 Vector4::zxyy() const { return Vector4 (z, x, y, y); }
Vector4 Vector4::wxyy() const { return Vector4 (w, x, y, y); }
Vector4 Vector4::xyyy() const { return Vector4 (x, y, y, y); }
Vector4 Vector4::yyyy() const { return Vector4 (y, y, y, y); }
Vector4 Vector4::zyyy() const { return Vector4 (z, y, y, y); }
Vector4 Vector4::wyyy() const { return Vector4 (w, y, y, y); }
Vector4 Vector4::xzyy() const { return Vector4 (x, z, y, y); }
Vector4 Vector4::yzyy() const { return Vector4 (y, z, y, y); }
Vector4 Vector4::zzyy() const { return Vector4 (z, z, y, y); }
Vector4 Vector4::wzyy() const { return Vector4 (w, z, y, y); }
Vector4 Vector4::xwyy() const { return Vector4 (x, w, y, y); }
Vector4 Vector4::ywyy() const { return Vector4 (y, w, y, y); }
Vector4 Vector4::zwyy() const { return Vector4 (z, w, y, y); }
Vector4 Vector4::wwyy() const { return Vector4 (w, w, y, y); }
Vector4 Vector4::xxzy() const { return Vector4 (x, x, z, y); }
Vector4 Vector4::yxzy() const { return Vector4 (y, x, z, y); }
Vector4 Vector4::zxzy() const { return Vector4 (z, x, z, y); }
Vector4 Vector4::wxzy() const { return Vector4 (w, x, z, y); }
Vector4 Vector4::xyzy() const { return Vector4 (x, y, z, y); }
Vector4 Vector4::yyzy() const { return Vector4 (y, y, z, y); }
Vector4 Vector4::zyzy() const { return Vector4 (z, y, z, y); }
Vector4 Vector4::wyzy() const { return Vector4 (w, y, z, y); }
Vector4 Vector4::xzzy() const { return Vector4 (x, z, z, y); }
Vector4 Vector4::yzzy() const { return Vector4 (y, z, z, y); }
Vector4 Vector4::zzzy() const { return Vector4 (z, z, z, y); }
Vector4 Vector4::wzzy() const { return Vector4 (w, z, z, y); }
Vector4 Vector4::xwzy() const { return Vector4 (x, w, z, y); }
Vector4 Vector4::ywzy() const { return Vector4 (y, w, z, y); }
Vector4 Vector4::zwzy() const { return Vector4 (z, w, z, y); }
Vector4 Vector4::wwzy() const { return Vector4 (w, w, z, y); }
Vector4 Vector4::xxwy() const { return Vector4 (x, x, w, y); }
Vector4 Vector4::yxwy() const { return Vector4 (y, x, w, y); }
Vector4 Vector4::zxwy() const { return Vector4 (z, x, w, y); }
Vector4 Vector4::wxwy() const { return Vector4 (w, x, w, y); }
Vector4 Vector4::xywy() const { return Vector4 (x, y, w, y); }
Vector4 Vector4::yywy() const { return Vector4 (y, y, w, y); }
Vector4 Vector4::zywy() const { return Vector4 (z, y, w, y); }
Vector4 Vector4::wywy() const { return Vector4 (w, y, w, y); }
Vector4 Vector4::xzwy() const { return Vector4 (x, z, w, y); }
Vector4 Vector4::yzwy() const { return Vector4 (y, z, w, y); }
Vector4 Vector4::zzwy() const { return Vector4 (z, z, w, y); }
Vector4 Vector4::wzwy() const { return Vector4 (w, z, w, y); }
Vector4 Vector4::xwwy() const { return Vector4 (x, w, w, y); }
Vector4 Vector4::ywwy() const { return Vector4 (y, w, w, y); }
Vector4 Vector4::zwwy() const { return Vector4 (z, w, w, y); }
Vector4 Vector4::wwwy() const { return Vector4 (w, w, w, y); }
Vector4 Vector4::xxxz() const { return Vector4 (x, x, x, z); }
Vector4 Vector4::yxxz() const { return Vector4 (y, x, x, z); }
Vector4 Vector4::zxxz() const { return Vector4 (z, x, x, z); }
Vector4 Vector4::wxxz() const { return Vector4 (w, x, x, z); }
Vector4 Vector4::xyxz() const { return Vector4 (x, y, x, z); }
Vector4 Vector4::yyxz() const { return Vector4 (y, y, x, z); }
Vector4 Vector4::zyxz() const { return Vector4 (z, y, x, z); }
Vector4 Vector4::wyxz() const { return Vector4 (w, y, x, z); }
Vector4 Vector4::xzxz() const { return Vector4 (x, z, x, z); }
Vector4 Vector4::yzxz() const { return Vector4 (y, z, x, z); }
Vector4 Vector4::zzxz() const { return Vector4 (z, z, x, z); }
Vector4 Vector4::wzxz() const { return Vector4 (w, z, x, z); }
Vector4 Vector4::xwxz() const { return Vector4 (x, w, x, z); }
Vector4 Vector4::ywxz() const { return Vector4 (y, w, x, z); }
Vector4 Vector4::zwxz() const { return Vector4 (z, w, x, z); }
Vector4 Vector4::wwxz() const { return Vector4 (w, w, x, z); }
Vector4 Vector4::xxyz() const { return Vector4 (x, x, y, z); }
Vector4 Vector4::yxyz() const { return Vector4 (y, x, y, z); }
Vector4 Vector4::zxyz() const { return Vector4 (z, x, y, z); }
Vector4 Vector4::wxyz() const { return Vector4 (w, x, y, z); }
Vector4 Vector4::xyyz() const { return Vector4 (x, y, y, z); }
Vector4 Vector4::yyyz() const { return Vector4 (y, y, y, z); }
Vector4 Vector4::zyyz() const { return Vector4 (z, y, y, z); }
Vector4 Vector4::wyyz() const { return Vector4 (w, y, y, z); }
Vector4 Vector4::xzyz() const { return Vector4 (x, z, y, z); }
Vector4 Vector4::yzyz() const { return Vector4 (y, z, y, z); }
Vector4 Vector4::zzyz() const { return Vector4 (z, z, y, z); }
Vector4 Vector4::wzyz() const { return Vector4 (w, z, y, z); }
Vector4 Vector4::xwyz() const { return Vector4 (x, w, y, z); }
Vector4 Vector4::ywyz() const { return Vector4 (y, w, y, z); }
Vector4 Vector4::zwyz() const { return Vector4 (z, w, y, z); }
Vector4 Vector4::wwyz() const { return Vector4 (w, w, y, z); }
Vector4 Vector4::xxzz() const { return Vector4 (x, x, z, z); }
Vector4 Vector4::yxzz() const { return Vector4 (y, x, z, z); }
Vector4 Vector4::zxzz() const { return Vector4 (z, x, z, z); }
Vector4 Vector4::wxzz() const { return Vector4 (w, x, z, z); }
Vector4 Vector4::xyzz() const { return Vector4 (x, y, z, z); }
Vector4 Vector4::yyzz() const { return Vector4 (y, y, z, z); }
Vector4 Vector4::zyzz() const { return Vector4 (z, y, z, z); }
Vector4 Vector4::wyzz() const { return Vector4 (w, y, z, z); }
Vector4 Vector4::xzzz() const { return Vector4 (x, z, z, z); }
Vector4 Vector4::yzzz() const { return Vector4 (y, z, z, z); }
Vector4 Vector4::zzzz() const { return Vector4 (z, z, z, z); }
Vector4 Vector4::wzzz() const { return Vector4 (w, z, z, z); }
Vector4 Vector4::xwzz() const { return Vector4 (x, w, z, z); }
Vector4 Vector4::ywzz() const { return Vector4 (y, w, z, z); }
Vector4 Vector4::zwzz() const { return Vector4 (z, w, z, z); }
Vector4 Vector4::wwzz() const { return Vector4 (w, w, z, z); }
Vector4 Vector4::xxwz() const { return Vector4 (x, x, w, z); }
Vector4 Vector4::yxwz() const { return Vector4 (y, x, w, z); }
Vector4 Vector4::zxwz() const { return Vector4 (z, x, w, z); }
Vector4 Vector4::wxwz() const { return Vector4 (w, x, w, z); }
Vector4 Vector4::xywz() const { return Vector4 (x, y, w, z); }
Vector4 Vector4::yywz() const { return Vector4 (y, y, w, z); }
Vector4 Vector4::zywz() const { return Vector4 (z, y, w, z); }
Vector4 Vector4::wywz() const { return Vector4 (w, y, w, z); }
Vector4 Vector4::xzwz() const { return Vector4 (x, z, w, z); }
Vector4 Vector4::yzwz() const { return Vector4 (y, z, w, z); }
Vector4 Vector4::zzwz() const { return Vector4 (z, z, w, z); }
Vector4 Vector4::wzwz() const { return Vector4 (w, z, w, z); }
Vector4 Vector4::xwwz() const { return Vector4 (x, w, w, z); }
Vector4 Vector4::ywwz() const { return Vector4 (y, w, w, z); }
Vector4 Vector4::zwwz() const { return Vector4 (z, w, w, z); }
Vector4 Vector4::wwwz() const { return Vector4 (w, w, w, z); }
Vector4 Vector4::xxxw() const { return Vector4 (x, x, x, w); }
Vector4 Vector4::yxxw() const { return Vector4 (y, x, x, w); }
Vector4 Vector4::zxxw() const { return Vector4 (z, x, x, w); }
Vector4 Vector4::wxxw() const { return Vector4 (w, x, x, w); }
Vector4 Vector4::xyxw() const { return Vector4 (x, y, x, w); }
Vector4 Vector4::yyxw() const { return Vector4 (y, y, x, w); }
Vector4 Vector4::zyxw() const { return Vector4 (z, y, x, w); }
Vector4 Vector4::wyxw() const { return Vector4 (w, y, x, w); }
Vector4 Vector4::xzxw() const { return Vector4 (x, z, x, w); }
Vector4 Vector4::yzxw() const { return Vector4 (y, z, x, w); }
Vector4 Vector4::zzxw() const { return Vector4 (z, z, x, w); }
Vector4 Vector4::wzxw() const { return Vector4 (w, z, x, w); }
Vector4 Vector4::xwxw() const { return Vector4 (x, w, x, w); }
Vector4 Vector4::ywxw() const { return Vector4 (y, w, x, w); }
Vector4 Vector4::zwxw() const { return Vector4 (z, w, x, w); }
Vector4 Vector4::wwxw() const { return Vector4 (w, w, x, w); }
Vector4 Vector4::xxyw() const { return Vector4 (x, x, y, w); }
Vector4 Vector4::yxyw() const { return Vector4 (y, x, y, w); }
Vector4 Vector4::zxyw() const { return Vector4 (z, x, y, w); }
Vector4 Vector4::wxyw() const { return Vector4 (w, x, y, w); }
Vector4 Vector4::xyyw() const { return Vector4 (x, y, y, w); }
Vector4 Vector4::yyyw() const { return Vector4 (y, y, y, w); }
Vector4 Vector4::zyyw() const { return Vector4 (z, y, y, w); }
Vector4 Vector4::wyyw() const { return Vector4 (w, y, y, w); }
Vector4 Vector4::xzyw() const { return Vector4 (x, z, y, w); }
Vector4 Vector4::yzyw() const { return Vector4 (y, z, y, w); }
Vector4 Vector4::zzyw() const { return Vector4 (z, z, y, w); }
Vector4 Vector4::wzyw() const { return Vector4 (w, z, y, w); }
Vector4 Vector4::xwyw() const { return Vector4 (x, w, y, w); }
Vector4 Vector4::ywyw() const { return Vector4 (y, w, y, w); }
Vector4 Vector4::zwyw() const { return Vector4 (z, w, y, w); }
Vector4 Vector4::wwyw() const { return Vector4 (w, w, y, w); }
Vector4 Vector4::xxzw() const { return Vector4 (x, x, z, w); }
Vector4 Vector4::yxzw() const { return Vector4 (y, x, z, w); }
Vector4 Vector4::zxzw() const { return Vector4 (z, x, z, w); }
Vector4 Vector4::wxzw() const { return Vector4 (w, x, z, w); }
Vector4 Vector4::xyzw() const { return Vector4 (x, y, z, w); }
Vector4 Vector4::yyzw() const { return Vector4 (y, y, z, w); }
Vector4 Vector4::zyzw() const { return Vector4 (z, y, z, w); }
Vector4 Vector4::wyzw() const { return Vector4 (w, y, z, w); }
Vector4 Vector4::xzzw() const { return Vector4 (x, z, z, w); }
Vector4 Vector4::yzzw() const { return Vector4 (y, z, z, w); }
Vector4 Vector4::zzzw() const { return Vector4 (z, z, z, w); }
Vector4 Vector4::wzzw() const { return Vector4 (w, z, z, w); }
Vector4 Vector4::xwzw() const { return Vector4 (x, w, z, w); }
Vector4 Vector4::ywzw() const { return Vector4 (y, w, z, w); }
Vector4 Vector4::zwzw() const { return Vector4 (z, w, z, w); }
Vector4 Vector4::wwzw() const { return Vector4 (w, w, z, w); }
Vector4 Vector4::xxww() const { return Vector4 (x, x, w, w); }
Vector4 Vector4::yxww() const { return Vector4 (y, x, w, w); }
Vector4 Vector4::zxww() const { return Vector4 (z, x, w, w); }
Vector4 Vector4::wxww() const { return Vector4 (w, x, w, w); }
Vector4 Vector4::xyww() const { return Vector4 (x, y, w, w); }
Vector4 Vector4::yyww() const { return Vector4 (y, y, w, w); }
Vector4 Vector4::zyww() const { return Vector4 (z, y, w, w); }
Vector4 Vector4::wyww() const { return Vector4 (w, y, w, w); }
Vector4 Vector4::xzww() const { return Vector4 (x, z, w, w); }
Vector4 Vector4::yzww() const { return Vector4 (y, z, w, w); }
Vector4 Vector4::zzww() const { return Vector4 (z, z, w, w); }
Vector4 Vector4::wzww() const { return Vector4 (w, z, w, w); }
Vector4 Vector4::xwww() const { return Vector4 (x, w, w, w); }
Vector4 Vector4::ywww() const { return Vector4 (y, w, w, w); }
Vector4 Vector4::zwww() const { return Vector4 (z, w, w, w); }
Vector4 Vector4::wwww() const { return Vector4 (w, w, w, w); }
}; // namespace

View File

@@ -0,0 +1,58 @@
/**
@file Vector4int8.cpp
Homogeneous vector class.
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-02-09
@edited 2007-02-09
Copyright 2000-2007, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
#include "G3D/Vector4int8.h"
#include "G3D/Vector3.h"
#include "G3D/Vector4.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include <string>
namespace G3D {
Vector4int8::Vector4int8(const Vector4& source) {
x = iClamp(iRound(source.x), -128, 127);
y = iClamp(iRound(source.y), -128, 127);
z = iClamp(iRound(source.z), -128, 127);
w = iClamp(iRound(source.w), -128, 127);
}
Vector4int8::Vector4int8(const Vector3& source, int8 w) : w(w) {
x = iClamp(iRound(source.x), -128, 127);
y = iClamp(iRound(source.y), -128, 127);
z = iClamp(iRound(source.z), -128, 127);
}
Vector4int8::Vector4int8(class BinaryInput& b) {
deserialize(b);
}
void Vector4int8::serialize(class BinaryOutput& b) const {
// Intentionally write individual bytes to avoid endian issues
b.writeInt8(x);
b.writeInt8(y);
b.writeInt8(z);
b.writeInt8(w);
}
void Vector4int8::deserialize(class BinaryInput& b) {
x = b.readInt8();
y = b.readInt8();
z = b.readInt8();
w = b.readInt8();
}
} // namespace G3D

View File

@@ -0,0 +1,425 @@
/**
@file Welder.cpp
@author Morgan McGuire, Kyle Whitson, Corey Taylor
@created 2008-07-30
@edited 2009-11-29
*/
#include "G3D/platform.h"
#include "G3D/Vector2.h"
#include "G3D/Vector3.h"
#include "G3D/Sphere.h"
#include "G3D/PointHashGrid.h"
#include "G3D/Welder.h"
#include "G3D/Stopwatch.h" // for profiling
#include "G3D/AreaMemoryManager.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D { namespace _internal{
/** Used by WeldHelper2::smoothNormals. */
class VN {
public:
Vector3 vertex;
Vector3 normal;
VN() {}
VN(const Vector3& v, const Vector3& n) : vertex(v), normal(n) {}
};
/** Used by WeldHelper::getIndex to maintain a list of vertices by location. */
class VNTi {
public:
Vector3 vertex;
Vector3 normal;
Vector2 texCoord;
int index;
VNTi() : index(0) {}
VNTi(const Vector3& v, const Vector3& n, const Vector2& t, int i) :
vertex(v), normal(n), texCoord(t), index(i) {}
};
}} // G3D
template <> struct HashTrait <G3D::_internal::VN> {
static size_t hashCode(const G3D::_internal::VN& k) { return static_cast<size_t>(k.vertex.hashCode()); }
};
template <> struct HashTrait <G3D::_internal::VNTi> {
static size_t hashCode(const G3D::_internal::VNTi& k) { return static_cast<size_t>(k.vertex.hashCode()); }
};
template<> struct EqualsTrait <G3D::_internal::VN> {
static bool equals(const G3D::_internal::VN& a, const G3D::_internal::VN& b) { return a.vertex == b.vertex; }
};
template<> struct EqualsTrait <G3D::_internal::VNTi> {
static bool equals(const G3D::_internal::VNTi& a, const G3D::_internal::VNTi& b) { return a.vertex == b.vertex; }
};
template<> struct PositionTrait<G3D::_internal::VN> {
static void getPosition(const G3D::_internal::VN& v, G3D::Vector3& p) { p = v.vertex; }
};
template<> struct PositionTrait<G3D::_internal::VNTi> {
static void getPosition(const G3D::_internal::VNTi& v, G3D::Vector3& p) { p = v.vertex; }
};
namespace G3D { namespace _internal {
class WeldHelper {
private:
/** Used by getIndex and updateTriLists. Deallocating this is slow. */
PointHashGrid<VNTi> weldGrid;
Array<Vector3>* outputVertexArray;
Array<Vector3>* outputNormalArray;
Array<Vector2>* outputTexCoordArray;
float vertexWeldRadius;
/** Squared radius allowed for welding similar normals. */
float normalWeldRadius2;
float texCoordWeldRadius2;
float normalSmoothingAngle;
/**
Returns the index of the vertex in
outputVertexArray/outputNormalArray/outputTexCoordArray
that is within the global tolerances of v,n,t. If there
is no such vertex, adds it to the arrays and returns that index.
Called from updateTriLists().
*/
int getIndex(const Vector3& v, const Vector3& n, const Vector2& t) {
PointHashGrid<VNTi>::SphereIterator it =
weldGrid.beginSphereIntersection(Sphere(v, vertexWeldRadius));
if (n.isZero()) {
// Don't bother trying to match the surface normal, since this vertex has no surface normal.
while (it.hasMore()) {
if ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2) {
// This is the vertex
return it->index;
}
++it;
}
} else {
while (it.hasMore()) {
if (((n - it->normal).squaredLength() <= normalWeldRadius2) &&
((t - it->texCoord).squaredLength() <= texCoordWeldRadius2)) {
// This is the vertex
return it->index;
}
++it;
}
}
// Note that a sliver triangle processed before its neighbors may reach here
// with a zero length normal.
// The vertex does not exist. Create it.
const int i = outputVertexArray->size();
outputVertexArray->append(v);
outputNormalArray->append(n);
outputTexCoordArray->append(t);
// Store in the grid so that it will be remembered.
weldGrid.insert(VNTi(v, n, t, i));
return i;
}
/**
Updates each indexArray to refer to vertices in the
outputVertexArray.
Called from process()
*/
void updateTriLists(
Array<Array<int>*>& indexArrayArray,
const Array<Vector3>& vertexArray,
const Array<Vector3>& normalArray,
const Array<Vector2>& texCoordArray) {
// Compute a hash grid so that we can find neighbors quickly.
// It begins empty and is extended as the tri lists are iterated
// through.
weldGrid.clear();
// Process all triLists
int numTriLists = indexArrayArray.size();
int u = 0;
for (int t = 0; t < numTriLists; ++t) {
if (indexArrayArray[t] != NULL) {
Array<int>& triList = *(indexArrayArray[t]);
// For all vertices in this list
for (int v = 0; v < triList.size(); ++v) {
// This vertex mapped to u in the flatVertexArray
triList[v] = getIndex(vertexArray[u], normalArray[u], texCoordArray[u]);
/*
# ifdef G3D_DEBUG
{
int i = triList[v];
Vector3 N = normalArray[i];
debugAssertM(N.length() > 0.9f, "Produced non-unit normal");
}
# endif
*/
++u;
}
}
}
}
/** Expands the indexed triangle lists into a triangle list.
Called from process() */
void unroll(
const Array<Array<int>*>& indexArrayArray,
const Array<Vector3>& vertexArray,
const Array<Vector2>& texCoordArray,
Array<Vector3>& unrolledVertexArray,
Array<Vector2>& unrolledTexCoordArray) {
int numTriLists = indexArrayArray.size();
for (int t = 0; t < numTriLists; ++t) {
if (indexArrayArray[t] != NULL) {
const Array<int>& triList = *(indexArrayArray[t]);
for (int v = 0; v < triList.size(); ++v) {
int i = triList[v];
unrolledVertexArray.append(vertexArray[i]);
unrolledTexCoordArray.append(texCoordArray[i]);
}
}
}
}
/** For every three vertices, compute the face normal and store it three times.
Sliver triangles have a zero surface normal, which we will later take to
match *any* surface normal. */
void computeFaceNormals(
const Array<Vector3>& vertexArray,
Array<Vector3>& faceNormalArray) {
debugAssertM(vertexArray.size() % 3 == 0, "Input is not a triangle soup");
debugAssertM(faceNormalArray.size() == 0, "Output must start empty.");
for (int v = 0; v < vertexArray.size(); v += 3) {
const Vector3& e0 = vertexArray[v + 1] - vertexArray[v];
const Vector3& e1 = vertexArray[v + 2] - vertexArray[v];
// Note that the length may be zero in the case of sliver polygons, e.g.,
// those correcting a T-junction. Scale up by 256 to avoid underflow when
// multiplying very small edges
const Vector3& n = (e0.cross(e1 * 256.0f)).directionOrZero();
// Append the normal once per vertex.
faceNormalArray.append(n, n, n);
}
}
/**
Computes @a smoothNormalArray, whose elements are those of normalArray averaged
with neighbors within the angular cutoff.
*/
void smoothNormals(
const Array<Vector3>& vertexArray,
const Array<Vector3>& normalArray,
Array<Vector3>& smoothNormalArray) {
if (normalSmoothingAngle <= 0) {
smoothNormalArray = normalArray;
return;
}
// Create an area memory manager for fast deallocation
MemoryManager::Ref mm = AreaMemoryManager::create(iRound(sizeof(VN) * normalArray.size() * 1.5));
const float cosThresholdAngle = (float)cos(normalSmoothingAngle);
debugAssert(vertexArray.size() == normalArray.size());
smoothNormalArray.resize(normalArray.size());
// Compute a hash grid so that we can find neighbors quickly.
PointHashGrid<VN> grid(vertexWeldRadius, mm);
for (int v = 0; v < normalArray.size(); ++v) {
grid.insert(VN(vertexArray[v], normalArray[v]));
}
// TODO: this step could be done on multiple threads
for (int v = 0; v < normalArray.size(); ++v) {
// Compute the sum of all nearby normals within the cutoff angle.
// Search within the vertexWeldRadius, since those are the vertices
// that will collapse to the same point.
PointHashGrid<VN>::SphereIterator it =
grid.beginSphereIntersection(Sphere(vertexArray[v], vertexWeldRadius));
Vector3 sum;
const Vector3& original = normalArray[v];
while (it.hasMore()) {
const Vector3& N = it->normal;
const float cosAngle = N.dot(original);
if (cosAngle > cosThresholdAngle) {
// This normal is close enough to consider. Avoid underflow by scaling up
sum += (N * 256.0f);
}
++it;
}
const Vector3& average = sum.directionOrZero();
const bool indeterminate = average.isZero();
// Never "smooth" a normal so far that it points backwards
const bool backFacing = original.dot(average) < 0;
if (indeterminate || backFacing) {
// Revert to the face normal
smoothNormalArray[v] = original;
} else {
// Average available normals
smoothNormalArray[v] = average;
}
}
}
public:
/**
Algorithm:
1. Unroll the indexed triangle list into a triangle list, where
there are duplicated vertices.
2. Compute face normals for all triangles, and expand those into
the triangle vertices.
3. At each vertex, average all normals that are within normalSmoothingAngle.
4. Generate output indexArrayArray. While doing so, merge all vertices where
the distance between position, texCoord, and normal is within the thresholds.
*/
void process(
Array<Vector3>& vertexArray,
Array<Vector2>& texCoordArray,
Array<Vector3>& normalArray,
Array<Array<int>*>& indexArrayArray,
float normAngle,
float texRadius,
float normRadius) {
normalSmoothingAngle = normAngle;
normalWeldRadius2 = square(normRadius);
texCoordWeldRadius2 = square(texRadius);
const bool hasTexCoords = (texCoordArray.size() > 0);
if (hasTexCoords) {
debugAssertM(vertexArray.size() == texCoordArray.size(),
"Input arrays are not parallel.");
}
Array<Vector3> unrolledVertexArray;
Array<Vector3> unrolledFaceNormalArray;
Array<Vector3> unrolledSmoothNormalArray;
Array<Vector2> unrolledTexCoordArray;
if (! hasTexCoords) {
// Generate all zero texture coordinates
texCoordArray.resize(vertexArray.size());
}
// Generate a flat (unrolled) triangle list with texture coordinates.
unroll(indexArrayArray, vertexArray, texCoordArray,
unrolledVertexArray, unrolledTexCoordArray);
// Put the output back into the input slots.
outputVertexArray = &vertexArray;
outputNormalArray = &normalArray;
outputTexCoordArray = &texCoordArray;
outputVertexArray->fastClear();
outputNormalArray->fastClear();
outputTexCoordArray->fastClear();
// For every three vertices, generate their face normal and store it at
// each vertex. The output array has the same length as the input.
computeFaceNormals(unrolledVertexArray, unrolledFaceNormalArray);
// Compute smooth normals at vertices.
if (unrolledFaceNormalArray.size() > 0) {
smoothNormals(unrolledVertexArray, unrolledFaceNormalArray, unrolledSmoothNormalArray);
unrolledFaceNormalArray.clear();
}
// Regenerate the triangle lists
updateTriLists(indexArrayArray, unrolledVertexArray, unrolledSmoothNormalArray, unrolledTexCoordArray);
if (! hasTexCoords) {
// Throw away the generated texCoords
texCoordArray.resize(0);
}
}
WeldHelper(float vertRadius) :
weldGrid(vertRadius, AreaMemoryManager::create()),
vertexWeldRadius(vertRadius) {
}
};
} // Internal
void Welder::weld(
Array<Vector3>& vertexArray,
Array<Vector2>& texCoordArray,
Array<Vector3>& normalArray,
Array<Array<int>*>& indexArrayArray,
const Welder::Settings& settings) {
_internal::WeldHelper(settings.vertexWeldRadius).process(
vertexArray, texCoordArray, normalArray, indexArrayArray,
settings.normalSmoothingAngle, settings.textureWeldRadius, settings.normalWeldRadius);
}
Welder::Settings::Settings(const Any& any) {
*this = Settings();
any.verifyName("Welder::Settings");
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& key = toLower(it->key);
if (key == "normalsmoothingangle") {
normalSmoothingAngle = it->value;
} else if (key == "vertexweldradius") {
vertexWeldRadius = it->value;
} else if (key == "textureweldradius") {
textureWeldRadius = it->value;
} else if (key == "normalweldradius") {
normalWeldRadius = it->value;
} else {
any.verify(false, "Illegal key: " + it->key);
}
}
}
Welder::Settings::operator Any() const {
Any a(Any::TABLE, "Welder::Settings");
a.set("normalSmoothingAngle", normalSmoothingAngle);
a.set("vertexWeldRadius", vertexWeldRadius);
a.set("textureWeldRadius", textureWeldRadius);
a.set("normalWeldRadius", normalWeldRadius);
return a;
}
} // G3D

View File

@@ -0,0 +1,159 @@
/*
Dervied from SDL_main.c, which was placed in the public domain by Sam Lantinga 4/13/98
The WinMain function -- calls your program's main() function
*/
#include "G3D/platform.h"
#ifdef G3D_WIN32
#include <stdio.h>
#include <stdlib.h>
#include <cctype>
#ifdef main
# ifndef _WIN32_WCE_EMULATION
# undef main
# endif /* _WIN32_WCE_EMULATION */
#endif /* main */
#if defined(_WIN32_WCE) && _WIN32_WCE < 300
/* seems to be undefined in Win CE although in online help */
#define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t'))
#endif /* _WIN32_WCE < 300 */
// Turn off the G3D for loop scoping for C++
#ifdef for
# undef for
#endif
extern int main(int argc, const char** argv);
/* Parse a command line buffer into arguments */
static int ParseCommandLine(char *cmdline, char **argv) {
char *bufp;
int argc;
argc = 0;
for (bufp = cmdline; *bufp;) {
/* Skip leading whitespace */
while (isspace(*bufp)) {
++bufp;
}
/* Skip over argument */
if (*bufp == '"') {
++bufp;
if (*bufp) {
if (argv) {
argv[argc] = bufp;
}
++argc;
}
/* Skip over word */
while (*bufp && (*bufp != '"')) {
++bufp;
}
} else {
if (*bufp) {
if (argv) {
argv[argc] = bufp;
}
++argc;
}
/* Skip over word */
while (*bufp && !isspace(*bufp)) {
++bufp;
}
}
if (*bufp) {
if (argv) {
*bufp = '\0';
}
++bufp;
}
}
if (argv) {
argv[argc] = NULL;
}
return (argc);
}
/* Show an error message */
static void ShowError(const char *title, const char *message) {
/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
#ifdef USE_MESSAGEBOX
MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
#else
fprintf(stderr, "%s: %s\n", title, message);
#endif
}
/* Pop up an out of memory message, returns to Windows */
static BOOL OutOfMemory(void) {
ShowError("Fatal Error", "Out of memory - aborting");
return FALSE;
}
int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {
char **argv;
int argc;
int status;
char *cmdline;
# ifdef _WIN32_WCE
wchar_t *bufp;
int nLen;
# else
char *bufp;
size_t nLen;
# endif
(void)sw;
(void)szCmdLine;
(void)hInst;
(void)hPrev;
#ifdef _WIN32_WCE
#error WinCE not supported
/*
nLen = wcslen(szCmdLine) + 128 + 1;
bufp = SDL_stack_alloc(wchar_t, nLen * 2);
wcscpy(bufp, TEXT("\""));
GetModuleFileName(NULL, bufp + 1, 128 - 3);
wcscpy(bufp + wcslen(bufp), TEXT("\" "));
wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp));
nLen = wcslen(bufp) + 1;
cmdline = SDL_stack_alloc(char, nLen);
if (cmdline == NULL) {
return OutOfMemory();
}
WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
*/
#else
/* Grab the command line */
bufp = GetCommandLineA();
nLen = strlen(bufp) + 1;
cmdline = (char*)malloc(sizeof(char) * nLen);
if (cmdline == NULL) {
return OutOfMemory();
}
strncpy(cmdline, bufp, nLen);
#endif
/* Parse it into argv and argc */
argc = ParseCommandLine(cmdline, NULL);
argv = (char**)malloc(sizeof(char*) * (argc + 1));
if (argv == NULL) {
return OutOfMemory();
}
ParseCommandLine(cmdline, argv);
/* Run the main program */
status = main(argc, (const char**)argv);
free(argv);
free(cmdline);
return status;
}
#endif // if Win32

View File

@@ -0,0 +1,216 @@
/**
\file XML.h
\author Morgan McGuire
\maintainer Morgan McGuire
\created 2010-02-11
\edited 2010-02-24
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/XML.h"
#include "G3D/fileutils.h"
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#include "G3D/stringutils.h"
namespace G3D {
XML::XML(TextInput& t) : m_type(VALUE) {
deserialize(t);
}
double XML::number() const {
return TextInput::parseNumber(m_value);
}
bool XML::boolean() const {
return TextInput::parseBoolean(m_value);
}
void XML::load(const std::string& filename) {
TextInput::Settings s;
s.cppBlockComments = false;
s.cppLineComments = false;
s.proofSymbols = false;
TextInput t(filename, s);
deserialize(t);
}
void XML::save(const std::string& filename) const {
std::string s;
unparse(s);
writeWholeFile(filename, s);
}
void XML::unparse(std::string &s) const {
TextOutput::Settings set;
set.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
TextOutput t(set);
serialize(t);
t.commitString(s);
}
void XML::serialize(TextOutput& t) const {
if (m_type == VALUE) {
// Raw string; no quotes
t.writeSymbol(m_value);
} else {
t.printf("<%s", m_name.c_str());
for (AttributeTable::Iterator it = m_attribute.begin(); it.hasMore(); ++it) {
t.printf(" %s=\"%s\"", it->key.c_str(), it->value.m_value.c_str());
}
t.printf(">");
t.writeNewline();
t.pushIndent();
for (int i = 0; i < m_child.size(); ++i) {
m_child[i].serialize(t);
if (m_child[i].m_type == VALUE) {
// Only tags know to append a newline
t.writeNewline();
}
}
t.popIndent();
t.printf("</%s>", m_name.c_str());
t.writeNewline();
}
}
void XML::parse(const std::string& s) {
TextInput t(TextInput::FROM_STRING, s);
deserialize(t);
}
/** True if the next token begins the close tag */
static bool atClose(TextInput& t, const std::string name) {
if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "<")) {
// Need to keep looking ahead
Token p0 = t.read();
if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "/")) {
// Check the name on the close tag. It *must* match if
// this is a well-formed document, but there might be a
// tag error.
Token p1 = t.read();
Token p2 = t.peek();
std::string s = p2.string();
debugAssertM(beginsWith(name, s), "Mismatched close tag");
// Put the tokens back
t.push(p1);
t.push(p0);
return true;
} else {
// Put the read token back
t.push(p0);
return false;
}
} else {
return false;
}
}
void XML::deserialize(TextInput& t) {
begin:
Token n = t.read();
m_attribute.clear();
m_child.clear();
m_name = "";
m_value = "";
if ((n.type() == Token::SYMBOL) && (n.string() == "<")) {
// Beginning a tag
// Read name
n = t.read();
debugAssert(n.type() == Token::SYMBOL);
bool isComment =
(n.string() == "!") &&
(t.peek().type() == Token::SYMBOL) &&
(t.peek().string() == "--");
// ignored tag: <?xml> or <!xml>
// comment tag: <!-- ... -->
if ((n.string() == "?") || ((n.string() == "!") && ! isComment)) {
// Ignore this tag
while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">"))) {
n = t.read();
}
goto begin;
} else if (isComment) {
// Ignore until "-->"
bool prevWasDash = false;
while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">") && prevWasDash)) {
prevWasDash = (n.type() == Token::SYMBOL) && (n.string() == "--");
n = t.read();
}
goto begin;
}
// Keep reading until no colon
m_name += n.string();
n = t.read();
while ((n.type() == Token::SYMBOL) && (n.string() == ":")) {
// tag with namespace: <x:y>
m_name += ":" + t.readSymbol();
n = t.read();
}
// Read end of tag/close
bool done = false;
while (! done) {
debugAssert(n.type() == Token::SYMBOL);
if (n.string() == "/") {
// empty-element tag: <foo/>
// Consume the close tag
t.readSymbol(">");
done = true;
} else if (n.string() == ">") {
// End of open tag: read children until close tag
while (! atClose(t, m_name)) {
m_child.next().deserialize(t);
}
// Read close tag (we wouldn't be here unless it parses correctly)
while (t.hasMore() && ! (t.readSymbol() == ">")) {}
done = true;
} else {
// Attribute pair
std::string k = n.string();
t.readSymbol("=");
std::string v = t.read().string();
m_attribute.set(k, v);
// Advance to next
n = t.read();
}
}
} else {
// Beginning embedded content. Read until the end of file or the next tag.
m_type = VALUE;
m_value += n.string();
n = t.peek();
while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == "<"))) {
m_value += " " + t.read().string();
n = t.peek();
}
}
}
}

View File

@@ -0,0 +1,16 @@
/**
@file constants.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-05-20
@edited 2010-01-29
*/
#include "G3D/constants.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
namespace G3D {
} // G3D

View File

@@ -0,0 +1,389 @@
/**
@file debugAssert.cpp
Windows implementation of assertion routines.
@maintainer Morgan McGuire, graphics3d.com
@created 2001-08-26
@edited 2009-06-02
*/
#include "G3D/debugAssert.h"
#include "G3D/platform.h"
#ifdef G3D_WIN32
#include <tchar.h>
#endif
#include "G3D/format.h"
#include "G3D/prompt.h"
#include <string>
#include "G3D/debugPrintf.h"
#include "G3D/Log.h"
#include <cstdlib>
#ifdef _MSC_VER
// disable: "C++ exception handler used"
# pragma warning (push)
# pragma warning (disable : 4530)
#endif
using namespace std;
namespace G3D { namespace _internal {
ConsolePrintHook _consolePrintHook;
AssertionHook _debugHook = _handleDebugAssert_;
AssertionHook _failureHook = _handleErrorCheck_;
#ifdef G3D_LINUX
#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
Display* x11Display = NULL;
Window x11Window = 0;
#endif
#endif
#ifdef G3D_WIN32
static void postToClipboard(const char *text) {
if (OpenClipboard(NULL)) {
HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, strlen(text) + 1);
if (hMem) {
char *pMem = (char*)GlobalLock(hMem);
strcpy(pMem, text);
GlobalUnlock(hMem);
EmptyClipboard();
SetClipboardData(CF_TEXT, hMem);
}
CloseClipboard();
GlobalFree(hMem);
}
}
#endif
/**
outTitle should be set before the call
*/
static void createErrorMessage(
const char* expression,
const std::string& message,
const char* filename,
int lineNumber,
std::string& outTitle,
std::string& outMessage) {
std::string le = "";
const char* newline = "\n";
#ifdef G3D_WIN32
newline = "\r\n";
// The last error value. (Which is preserved across the call).
DWORD lastErr = GetLastError();
// The decoded message from FormatMessage
LPTSTR formatMsg = NULL;
if (NULL == formatMsg) {
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lastErr,
0,
(LPTSTR)&formatMsg,
0,
NULL);
}
// Make sure the message got translated into something.
LPTSTR realLastErr;
if (NULL != formatMsg) {
realLastErr = formatMsg;
} else {
realLastErr = _T("Last error code does not exist.");
}
if (lastErr != 0) {
le = G3D::format("Last Error (0x%08X): %s\r\n\r\n", lastErr, (LPCSTR)realLastErr);
}
// Get rid of the allocated memory from FormatMessage.
if (NULL != formatMsg) {
LocalFree((LPVOID)formatMsg);
}
char modulePath[MAX_PATH];
GetModuleFileNameA(NULL, modulePath, MAX_PATH);
const char* moduleName = strrchr(modulePath, '\\');
outTitle = outTitle + string(" - ") + string(moduleName ? (moduleName + 1) : modulePath);
#endif
// Build the message.
outMessage =
G3D::format("%s%s%sExpression: %s%s%s:%d%s%s%s",
message.c_str(), newline, newline, expression, newline,
filename, lineNumber, newline, newline, le.c_str());
}
bool _handleDebugAssert_(
const char* expression,
const std::string& message,
const char* filename,
int lineNumber,
bool useGuiPrompt) {
std::string dialogTitle = "Assertion Failure";
std::string dialogText = "";
createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
#ifdef G3D_WIN32
DWORD lastErr = GetLastError();
postToClipboard(dialogText.c_str());
debugPrintf("\n%s\n", dialogText.c_str());
#endif
const int cBreak = 0;
const int cIgnore = 1;
const int cAbort = 2;
static const char* choices[] = {"Debug", "Ignore", "Exit"};
// Log the error
Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
int result = G3D::prompt(dialogTitle.c_str(), dialogText.c_str(), (const char**)choices, 3, useGuiPrompt);
# ifdef G3D_WIN32
// Put the incoming last error back.
SetLastError(lastErr);
# endif
switch (result) {
// -1 shouldn't actually occur because it means
// that we're in release mode.
case -1:
case cBreak:
return true;
break;
case cIgnore:
return false;
break;
case cAbort:
exit(-1);
break;
}
// Should never get here
return false;
}
bool _handleErrorCheck_(
const char* expression,
const std::string& message,
const char* filename,
int lineNumber,
bool useGuiPrompt) {
std::string dialogTitle = "Critical Error";
std::string dialogText = "";
createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
// Log the error
Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
#ifdef G3D_WIN32
DWORD lastErr = GetLastError();
(void)lastErr;
postToClipboard(dialogText.c_str());
debugPrintf("\n%s\n", dialogText.c_str());
#endif
static const char* choices[] = {"Ok"};
const std::string& m =
std::string("An internal error has occured in this program and it will now close. "
"The specific error is below. More information has been saved in \"") +
Log::getCommonLogFilename() + "\".\n" + dialogText;
int result = G3D::prompt("Error", m.c_str(), (const char**)choices, 1, useGuiPrompt);
(void)result;
return true;
}
#ifdef G3D_WIN32
static HCURSOR oldCursor;
static RECT oldCursorRect;
static POINT oldCursorPos;
static int oldShowCursorCount;
#endif
void _releaseInputGrab_() {
#ifdef G3D_WIN32
GetCursorPos(&oldCursorPos);
// Stop hiding the cursor if the application hid it.
oldShowCursorCount = ShowCursor(true) - 1;
if (oldShowCursorCount < -1) {
for (int c = oldShowCursorCount; c < -1; ++c) {
ShowCursor(true);
}
}
// Set the default cursor in case the application
// set the cursor to NULL.
oldCursor = GetCursor();
SetCursor(LoadCursor(NULL, IDC_ARROW));
// Allow the cursor full access to the screen
GetClipCursor(&oldCursorRect);
ClipCursor(NULL);
#elif defined(G3D_LINUX)
#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
if (x11Display != NULL) {
XUngrabPointer(x11Display, CurrentTime);
XUngrabKeyboard(x11Display, CurrentTime);
if (x11Window != 0) {
//XUndefineCursor(x11Display, x11Window);
// TODO: Note that we leak this cursor; it should be
// freed in the restore code.
Cursor c = XCreateFontCursor(x11Display, 68);
XDefineCursor(x11Display, x11Window, c);
}
XSync(x11Display, false);
XAllowEvents(x11Display, AsyncPointer, CurrentTime);
XFlush(x11Display);
}
#endif
#elif defined(G3D_OSX)
// TODO: OS X
#endif
}
void _restoreInputGrab_() {
#ifdef G3D_WIN32
// Restore the old clipping region
ClipCursor(&oldCursorRect);
SetCursorPos(oldCursorPos.x, oldCursorPos.y);
// Restore the old cursor
SetCursor(oldCursor);
// Restore old visibility count
if (oldShowCursorCount < 0) {
for (int c = 0; c > oldShowCursorCount; --c) {
ShowCursor(false);
}
}
#elif defined(G3D_LINUX)
// TODO: Linux
#elif defined(G3D_OSX)
// TODO: OS X
#endif
}
}; // internal namespace
void setAssertionHook(AssertionHook hook) {
G3D::_internal::_debugHook = hook;
}
AssertionHook assertionHook() {
return G3D::_internal::_debugHook;
}
void setFailureHook(AssertionHook hook) {
G3D::_internal::_failureHook = hook;
}
AssertionHook failureHook() {
return G3D::_internal::_failureHook;
}
void setConsolePrintHook(ConsolePrintHook h) {
G3D::_internal::_consolePrintHook = h;
}
ConsolePrintHook consolePrintHook() {
return G3D::_internal::_consolePrintHook;
}
std::string __cdecl debugPrint(const std::string& s) {
# ifdef G3D_WIN32
const int MAX_STRING_LEN = 1024;
// Windows can't handle really long strings sent to
// the console, so we break the string.
if (s.size() < MAX_STRING_LEN) {
OutputDebugStringA(s.c_str());
} else {
for (unsigned int i = 0; i < s.size(); i += MAX_STRING_LEN) {
std::string sub = s.substr(i, MAX_STRING_LEN);
OutputDebugStringA(sub.c_str());
}
}
# else
fprintf(stderr, "%s", s.c_str());
fflush(stderr);
# endif
return s;
}
std::string __cdecl debugPrintf(const char* fmt ...) {
va_list argList;
va_start(argList, fmt);
std::string s = G3D::vformat(fmt, argList);
va_end(argList);
return debugPrint(s);
// return debugPrint(consolePrint(s));
}
std::string consolePrint(const std::string& s) {
FILE* L = Log::common()->getFile();
fprintf(L, "%s", s.c_str());
if (consolePrintHook()) {
consolePrintHook()(s);
}
fflush(L);
return s;
}
std::string __cdecl consolePrintf(const char* fmt ...) {
va_list argList;
va_start(argList, fmt);
std::string s = G3D::vformat(fmt, argList);
va_end(argList);
return consolePrint(s);
}
} // namespace
#ifdef _MSC_VER
# pragma warning (pop)
#endif

View File

@@ -0,0 +1,942 @@
/**
@file fileutils.cpp
@author Morgan McGuire, graphics3d.com
@author 2002-06-06
@edited 2010-02-05
*/
#include <cstring>
#include <cstdio>
#include "G3D/platform.h"
#include "G3D/fileutils.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/g3dmath.h"
#include "G3D/stringutils.h"
#include "G3D/Set.h"
#include "G3D/g3dfnmatch.h"
#include "G3D/FileSystem.h"
#include <sys/stat.h>
#include <sys/types.h>
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
#include "zip.h"
#endif
#ifdef G3D_WIN32
// Needed for _getcwd
#include <direct.h>
#include <io.h>
#else
#include <dirent.h>
#include <fnmatch.h>
#include <unistd.h>
#define _getcwd getcwd
#define _stat stat
#endif
namespace G3D {
namespace _internal {
Set<std::string> currentFilesUsed;
}
std::string pathConcat(const std::string& dirname, const std::string& file) {
// Ensure that the directory ends in a slash
if ((dirname.size() != 0) &&
(dirname[dirname.size() - 1] != '/') &&
(dirname[dirname.size() - 1] != '\\') &&
(dirname[dirname.size() - 1] != ':')) {
return dirname + '/' + file;
} else {
return dirname + file;
}
}
std::string resolveFilename(const std::string& filename) {
if (filename.size() >= 1) {
if ((filename[0] == '/') || (filename[0] == '\\')) {
// Already resolved
return filename;
} else {
#ifdef G3D_WIN32
if ((filename.size() >= 2) && (filename[1] == ':')) {
// There is a drive spec on the front.
if ((filename.size() >= 3) && ((filename[2] == '\\') ||
(filename[2] == '/'))) {
// Already fully qualified
return filename;
} else {
// The drive spec is relative to the
// working directory on that drive.
debugAssertM(false, "Files of the form d:path are"
" not supported (use a fully qualified"
" name).");
return filename;
}
}
#endif
}
}
char buffer[1024];
// Prepend the working directory.
_getcwd(buffer, 1024);
return format("%s/%s", buffer, filename.c_str());
}
bool zipfileExists(const std::string& filename) {
std::string outZipfile;
std::string outInternalFile;
return zipfileExists(filename, outZipfile, outInternalFile);
}
std::string readWholeFile(
const std::string& filename) {
_internal::currentFilesUsed.insert(filename);
std::string s;
debugAssert(filename != "");
debugAssertM(FileSystem::exists(filename), filename + " not found");
if (! FileSystem::inZipfile(filename)) {
int64 length = FileSystem::size(filename);
char* buffer = (char*)System::alignedMalloc(length + 1, 16);
debugAssert(buffer);
FILE* f = FileSystem::fopen(filename.c_str(), "rb");
debugAssert(f);
int ret = fread(buffer, 1, length, f);
debugAssert(ret == length);(void)ret;
FileSystem::fclose(f);
buffer[length] = '\0';
s = std::string(buffer);
System::alignedFree(buffer);
} else if (zipfileExists(filename)) {
void* zipBuffer;
size_t length;
zipRead(filename, zipBuffer, length);
char* buffer = (char*)System::alignedMalloc(length + 1, 16);
System::memcpy(buffer,zipBuffer, length + 1);
zipClose(zipBuffer);
buffer[length] = '\0';
s = std::string(buffer);
System::alignedFree(buffer);
}
return s;
}
void zipRead(const std::string& file,
void*& data,
size_t& length) {
std::string zip, desiredFile;
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
if (zipfileExists(file, zip, desiredFile)) {
struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
{
struct zip_stat info;
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
zip_stat( z, desiredFile.c_str(), ZIP_FL_NOCASE, &info );
length = info.size;
// sets machines up to use MMX, if they want
data = System::alignedMalloc(length, 16);
struct zip_file *zf = zip_fopen( z, desiredFile.c_str(), ZIP_FL_NOCASE );
{
int test = zip_fread( zf, data, length );
debugAssertM((size_t)test == length,
desiredFile + " was corrupt because it unzipped to the wrong size.");
(void)test;
}
zip_fclose( zf );
}
zip_close( z );
} else {
data = NULL;
}
#else
data = NULL;
#endif
}
void zipClose(void* data) {
System::alignedFree(data);
}
int64 fileLength(const std::string& filename) {
struct _stat st;
int result = _stat(filename.c_str(), &st);
if (result == -1) {
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
std::string zip, contents;
if(zipfileExists(filename, zip, contents)){
int64 requiredMem;
struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
debugAssertM(z != NULL, zip + ": zip open failed.");
{
struct zip_stat info;
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
(void)success;
debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
requiredMem = info.size;
}
zip_close( z );
return requiredMem;
} else {
return -1;
}
#else
return -1;
#endif
}
return st.st_size;
}
///////////////////////////////////////////////////////////////////////////////
void writeWholeFile(
const std::string& filename,
const std::string& str,
bool flush) {
// Make sure the directory exists.
std::string root, base, ext, path;
Array<std::string> pathArray;
parseFilename(filename, root, pathArray, base, ext);
path = root + stringJoin(pathArray, '/');
if (! FileSystem::exists(path, false)) {
FileSystem::createDirectory(path);
}
FILE* file = FileSystem::fopen(filename.c_str(), "wb");
debugAssert(file);
fwrite(str.c_str(), str.size(), 1, file);
if (flush) {
fflush(file);
}
FileSystem::fclose(file);
}
///////////////////////////////////////////////////////////////////////////////
/**
Creates the directory (which may optionally end in a /)
and any parents needed to reach it.
*/
void createDirectory(
const std::string& dir) {
if (dir == "") {
return;
}
std::string d;
// Add a trailing / if there isn't one.
switch (dir[dir.size() - 1]) {
case '/':
case '\\':
d = dir;
break;
default:
d = dir + "/";
}
// If it already exists, do nothing
if (FileSystem::exists(d.substr(0, d.size() - 1))) {
return;
}
// Parse the name apart
std::string root, base, ext;
Array<std::string> path;
std::string lead;
parseFilename(d, root, path, base, ext);
debugAssert(base == "");
debugAssert(ext == "");
// Begin with an extra period so "c:\" becomes "c:\.\" after
// appending a path and "c:" becomes "c:.\", not root: "c:\"
std::string p = root + ".";
// Create any intermediate that doesn't exist
for (int i = 0; i < path.size(); ++i) {
p += "/" + path[i];
if (! FileSystem::exists(p)) {
// Windows only requires one argument to mkdir,
// where as unix also requires the permissions.
# ifndef G3D_WIN32
mkdir(p.c_str(), 0777);
# else
_mkdir(p.c_str());
# endif
}
}
}
///////////////////////////////////////////////////////////////////////////////
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
/* Helper methods for zipfileExists()*/
// Given a string (the drive) and an array (the path), computes the directory
static void _zip_resolveDirectory(std::string& completeDir, const std::string& drive, const Array<std::string>& path, const int length){
completeDir = drive;
int tempLength;
// if the given length is longer than the array, we correct it
if(length > path.length()){
tempLength = path.length();
} else{
tempLength = length;
}
for(int t = 0; t < tempLength; ++t){
if(t > 0){
completeDir += "/";
}
completeDir += path[t];
}
}
// assumes that zipDir references a .zip file
static bool _zip_zipContains(const std::string& zipDir, const std::string& desiredFile){
struct zip *z = zip_open( zipDir.c_str(), ZIP_CHECKCONS, NULL );
//the last parameter, an int, determines case sensitivity:
//1 is sensitive, 2 is not, 0 is default
int test = zip_name_locate( z, desiredFile.c_str(), ZIP_FL_NOCASE );
zip_close( z );
if(test == -1){
return false;
}
return true;
}
#endif
// If no zipfile exists, outZipfile and outInternalFile are unchanged
bool zipfileExists(const std::string& filename, std::string& outZipfile,
std::string& outInternalFile){
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
Array<std::string> path;
std::string drive, base, ext, zipfile, infile;
parseFilename(filename, drive, path, base, ext);
// Put the filename back together
if ((base != "") && (ext != "")) {
infile = base + "." + ext;
} else {
infile = base + ext;
}
// Remove "." from path
for (int i = 0; i < path.length(); ++i) {
if (path[i] == ".") {
path.remove(i);
--i;
}
}
// Remove ".." from path
for (int i = 1; i < path.length(); ++i) {
if ((path[i] == "..") && (i > 0) && (path[i - 1] != "..")) {
// Remove both i and i - 1
path.remove(i - 1, 2);
i -= 2;
}
}
// Walk the path backwards, accumulating pieces onto the infile until
// we find a zipfile that contains it
for (int t = 0; t < path.length(); ++t){
_zip_resolveDirectory(zipfile, drive, path, path.length() - t);
if (t > 0) {
infile = path[path.length() - t] + "/" + infile;
}
if (endsWith(zipfile, "..")) {
return false;
}
if (FileSystem::exists(zipfile)) {
// test if it actually is a zipfile
// if not, return false, a bad
// directory structure has been given,
// not a .zip
if (FileSystem::isZipfile(zipfile)){
if (_zip_zipContains(zipfile, infile)){
outZipfile = zipfile;
outInternalFile = infile;
return true;
} else {
return false;
}
} else {
// the directory structure was valid but did not point to a .zip
return false;
}
}
}
#endif
// not a valid directory structure ever,
// obviously no .zip was found within the path
return false;
}
///////////////////////////////////////////////////////////////////////////////
std::string generateFilenameBase(const std::string& prefix, const std::string& suffix) {
Array<std::string> exist;
// Note "template" is a reserved word in C++
std::string templat = prefix + System::currentDateString() + "_";
FileSystem::getFiles(templat + "*", exist);
// Remove extensions
for (int i = 0; i < exist.size(); ++i) {
exist[i] = filenameBase(exist[i]);
}
int num = 0;
std::string result;
templat += "%03d" + suffix;
do {
result = format(templat.c_str(), num);
++num;
} while (exist.contains(result));
return result;
}
///////////////////////////////////////////////////////////////////////////////
void copyFile(
const std::string& source,
const std::string& dest) {
#ifdef G3D_WIN32
CopyFileA(source.c_str(), dest.c_str(), FALSE);
#else
// TODO: don't use BinaryInput and BinaryOutput
// Read it all in, then dump it out
BinaryInput in(source, G3D_LITTLE_ENDIAN);
BinaryOutput out(dest, G3D_LITTLE_ENDIAN);
out.writeBytes(in.getCArray(), in.size());
out.commit(false);
#endif
}
//////////////////////////////////////////////////////////////////////////////
void parseFilename(
const std::string& filename,
std::string& root,
Array<std::string>& path,
std::string& base,
std::string& ext) {
std::string f = filename;
root = "";
path.clear();
base = "";
ext = "";
if (f == "") {
// Empty filename
return;
}
// See if there is a root/drive spec.
if ((f.size() >= 2) && (f[1] == ':')) {
if ((f.size() > 2) && isSlash(f[2])) {
// e.g. c:\foo
root = f.substr(0, 3);
f = f.substr(3, f.size() - 3);
} else {
// e.g. c:foo
root = f.substr(2);
f = f.substr(2, f.size() - 2);
}
} else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) {
// e.g. //foo
root = f.substr(0, 2);
f = f.substr(2, f.size() - 2);
} else if (isSlash(f[0])) {
root = f.substr(0, 1);
f = f.substr(1, f.size() - 1);
}
// Pull the extension off
{
// Find the period
size_t i = f.rfind('.');
if (i != std::string::npos) {
// Make sure it is after the last slash!
size_t j = iMax(f.rfind('/'), f.rfind('\\'));
if ((j == std::string::npos) || (i > j)) {
ext = f.substr(i + 1, f.size() - i - 1);
f = f.substr(0, i);
}
}
}
// Pull the basename off
{
// Find the last slash
size_t i = iMax(f.rfind('/'), f.rfind('\\'));
if (i == std::string::npos) {
// There is no slash; the basename is the whole thing
base = f;
f = "";
} else if ((i != std::string::npos) && (i < f.size() - 1)) {
base = f.substr(i + 1, f.size() - i - 1);
f = f.substr(0, i);
}
}
// Parse what remains into path.
size_t prev, cur = 0;
while (cur < f.size()) {
prev = cur;
// Allow either slash
size_t i = f.find('/', prev + 1);
size_t j = f.find('\\', prev + 1);
if (i == std::string::npos) {
i = f.size();
}
if (j == std::string::npos) {
j = f.size();
}
cur = iMin(i, j);
if (cur == std::string::npos) {
cur = f.size();
}
path.append(f.substr(prev, cur - prev));
++cur;
}
}
/**
Helper for getFileList and getDirectoryList.
@param wantFiles If false, returns the directories, otherwise
returns the files.
@param includePath If true, the names include paths
*/
static void getFileOrDirListNormal
(const std::string& filespec,
Array<std::string>& files,
bool wantFiles,
bool includePath) {
bool test = wantFiles ? true : false;
std::string path = "";
// Find the place where the path ends and the file-spec begins
size_t i = filespec.rfind('/');
size_t j = filespec.rfind('\\');
// Drive letters on Windows can separate a path
size_t k = filespec.rfind(':');
if (((j != std::string::npos) && (j > i)) ||
(i == std::string::npos)) {
i = j;
}
if (((k != std::string::npos) && (k > i)) ||
(i == std::string::npos)) {
i = k;
}
// If there is a path, pull it off
if (i != std::string::npos) {
path = filespec.substr(0, i + 1);
}
std::string prefix = path;
if (path.size() > 0) {
// Strip the trailing character
path = path.substr(0, path.size() - 1);
}
# ifdef G3D_WIN32
{
struct _finddata_t fileinfo;
long handle = _findfirst(filespec.c_str(), &fileinfo);
int result = handle;
while (result != -1) {
if ((((fileinfo.attrib & _A_SUBDIR) == 0) == test) &&
strcmp(fileinfo.name, ".") &&
strcmp(fileinfo.name, "..")) {
if (includePath) {
files.append(prefix + fileinfo.name);
} else {
files.append(fileinfo.name);
}
}
result = _findnext(handle, &fileinfo);
}
}
# else
{
if (path == "") {
// Empty paths don't work on Unix
path = ".";
}
// Unix implementation
DIR* dir = opendir(path.c_str());
if (dir != NULL) {
struct dirent* entry = readdir(dir);
while (entry != NULL) {
// Exclude '.' and '..'
if ((strcmp(entry->d_name, ".") != 0) &&
(strcmp(entry->d_name, "..") != 0)) {
// Form a name with a path
std::string filename = prefix + entry->d_name;
// See if this is a file or a directory
struct _stat st;
bool exists = _stat(filename.c_str(), &st) != -1;
if (exists &&
// Make sure it has the correct type
(((st.st_mode & S_IFDIR) == 0) == test) &&
// Make sure it matches the wildcard
(fnmatch(filespec.c_str(),
filename.c_str(),
FNM_PATHNAME) == 0)) {
if (includePath) {
files.append(filename);
} else {
files.append(entry->d_name);
}
}
}
entry = readdir(dir);
}
closedir(dir);
}
}
# endif
}
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
/**
@param path The zipfile name (no trailing slash)
@param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty.
@param file Name inside the zipfile that we are testing to see if it matches prefix + "*"
*/
static void _zip_addEntry(const std::string& path,
const std::string& prefix,
const std::string& file,
Set<std::string>& files,
bool wantFiles,
bool includePath) {
// Make certain we are within the desired parent folder (prefix)
if (beginsWith(file, prefix)) {
// validityTest was prefix/file
// Extract everything to the right of the prefix
std::string s = file.substr(prefix.length());
if (s == "") {
// This was the name of the prefix
return;
}
// See if there are any slashes
size_t slashPos = s.find('/');
bool add = false;
if (slashPos == std::string::npos) {
// No slashes, so s must be a file
add = wantFiles;
} else if (! wantFiles) {
// Not all zipfiles list directories as explicit entries.
// Because of this, if we're looking for directories and see
// any path longer than prefix, we must add the subdirectory.
// The Set will fix duplicates for us.
s = s.substr(0, slashPos);
add = true;
}
if (add) {
if (includePath) {
files.insert(path + "/" + prefix + s);
} else {
files.insert(s);
}
}
}
}
#endif
static void getFileOrDirListZip(const std::string& path,
const std::string& prefix,
Array<std::string>& files,
bool wantFiles,
bool includePath){
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
struct zip *z = zip_open( path.c_str(), ZIP_CHECKCONS, NULL );
Set<std::string> fileSet;
int count = zip_get_num_files( z );
for( int i = 0; i < count; ++i ) {
struct zip_stat info;
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
zip_stat_index( z, i, ZIP_FL_NOCASE, &info );
_zip_addEntry(path, prefix, info.name, fileSet, wantFiles, includePath);
}
zip_close( z );
fileSet.getMembers(files);
#endif
}
static void determineFileOrDirList(
const std::string& filespec,
Array<std::string>& files,
bool wantFiles,
bool includePath) {
// if it is a .zip, prefix will specify the folder within
// whose contents we want to see
std::string prefix = "";
std::string path = filenamePath(filespec);
if ((path.size() > 0) && isSlash(path[path.size() - 1])) {
// Strip the trailing slash
path = path.substr(0, path.length() -1);
}
if ((path == "") || FileSystem::exists(path)) {
if ((path != "") && FileSystem::isZipfile(path)) {
// .zip should only work if * is specified as the Base + Ext
// Here, we have been asked for the root's contents
debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard");
getFileOrDirListZip(path, prefix, files, wantFiles, includePath);
} else {
// It is a normal directory
getFileOrDirListNormal(filespec, files, wantFiles, includePath);
}
} else if (zipfileExists(filenamePath(filespec), path, prefix)) {
// .zip should only work if * is specified as the Base + Ext
// Here, we have been asked for the contents of a folder within the .zip
debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard");
getFileOrDirListZip(path, prefix, files, wantFiles, includePath);
}
}
void getFiles(const std::string& filespec,
Array<std::string>& files,
bool includePath) {
determineFileOrDirList(filespec, files, true, includePath);
}
void getDirs(
const std::string& filespec,
Array<std::string>& files,
bool includePath) {
determineFileOrDirList(filespec, files, false, includePath);
}
std::string filenameBaseExt(const std::string& filename) {
int i = filename.rfind("/");
int j = filename.rfind("\\");
if ((j > i) && (j >= 0)) {
i = j;
}
# ifdef G3D_WIN32
j = filename.rfind(":");
if ((i == -1) && (j >= 0)) {
i = j;
}
# endif
if (i == -1) {
return filename;
} else {
return filename.substr(i + 1, filename.length() - i);
}
}
std::string filenameBase(const std::string& s) {
std::string drive;
std::string base;
std::string ext;
Array<std::string> path;
parseFilename(s, drive, path, base, ext);
return base;
}
std::string filenameExt(const std::string& filename) {
int i = filename.rfind(".");
if (i >= 0) {
return filename.substr(i + 1, filename.length() - i);
} else {
return "";
}
}
std::string filenamePath(const std::string& filename) {
int i = filename.rfind("/");
int j = filename.rfind("\\");
if ((j > i) && (j >= 0)) {
i = j;
}
# ifdef G3D_WIN32
j = filename.rfind(":");
if ((i == -1) && (j >= 0)) {
i = j;
}
# endif
if (i == -1) {
return "";
} else {
return filename.substr(0, i+1);
}
}
bool isZipfile(const std::string& filename) {
FILE* f = fopen(filename.c_str(), "r");
if (f == NULL) {
return false;
}
uint8 header[4];
fread(header, 4, 1, f);
const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
for (int i = 0; i < 4; ++i) {
if (header[i] != zipHeader[i]) {
fclose(f);
return false;
}
}
fclose(f);
return true;
}
bool isDirectory(const std::string& filename) {
struct _stat st;
bool exists = _stat(filename.c_str(), &st) != -1;
return exists && ((st.st_mode & S_IFDIR) != 0);
}
bool filenameContainsWildcards(const std::string& filename) {
return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos);
}
bool fileIsNewer(const std::string& src, const std::string& dst) {
struct _stat sts;
bool sexists = _stat(src.c_str(), &sts) != -1;
struct _stat dts;
bool dexists = _stat(dst.c_str(), &dts) != -1;
return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime));
}
Array<std::string> filesUsed() {
Array<std::string> f;
_internal::currentFilesUsed.getMembers(f);
return f;
}
}
#ifndef G3D_WIN32
#undef _stat
#endif

View File

@@ -0,0 +1,32 @@
/**
@file filter.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-03-01
@edited 2007-03-01
Copyright 2000-2007, Morgan McGuire.
All rights reserved.
*/
#include "G3D/filter.h"
namespace G3D {
void gaussian1D(Array<float>& coeff, int N, float std) {
coeff.resize(N);
float sum = 0.0f;
for (int i = 0; i < N; ++i) {
float x = i - (N - 1) / 2.0f;
float p = -square(x / std) / 2.0f;
float y = exp(p);
coeff[i] = y;
sum += y;
}
for (int i = 0; i < N; ++i) {
coeff[i] /= sum;
}
}
} // namespace

View File

@@ -0,0 +1,164 @@
/**
@file format.cpp
@author Morgan McGuire, graphics3d.com
@created 2000-09-09
@edited 2006-08-14
*/
#include "G3D/format.h"
#include "G3D/platform.h"
#include "G3D/System.h"
#ifdef _MSC_VER
// disable: "C++ exception handler used"
# pragma warning (push)
# pragma warning (disable : 4530)
#endif // _MSC_VER
// If your platform does not have vsnprintf, you can find a
// implementation at http://www.ijs.si/software/snprintf/
namespace G3D {
std::string __cdecl format(const char* fmt,...) {
va_list argList;
va_start(argList,fmt);
std::string result = vformat(fmt, argList);
va_end(argList);
return result;
}
#if defined(_MSC_VER) && (_MSC_VER >= 1300)
// Both MSVC seems to use the non-standard vsnprintf
// so we are using vscprintf to determine buffer size, however
// only MSVC7 and up headers include vscprintf for some reason.
std::string vformat(const char *fmt, va_list argPtr) {
// We draw the line at a 1MB string.
const int maxSize = 1000000;
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
char stackBuffer[bufSize];
// MSVC does not support va_copy
int actualSize = _vscprintf(fmt, argPtr) + 1;
if (actualSize > bufSize) {
// Now use the heap.
char* heapBuffer = NULL;
if (actualSize < maxSize) {
heapBuffer = (char*)System::malloc(maxSize + 1);
_vsnprintf(heapBuffer, maxSize, fmt, argPtr);
heapBuffer[maxSize] = '\0';
} else {
heapBuffer = (char*)System::malloc(actualSize);
vsprintf(heapBuffer, fmt, argPtr);
}
std::string formattedString(heapBuffer);
System::free(heapBuffer);
return formattedString;
} else {
vsprintf(stackBuffer, fmt, argPtr);
return std::string(stackBuffer);
}
}
#elif defined(_MSC_VER) && (_MSC_VER < 1300)
std::string vformat(const char *fmt, va_list argPtr) {
// We draw the line at a 1MB string.
const int maxSize = 1000000;
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
char stackBuffer[bufSize];
// MSVC6 doesn't support va_copy, however it also seems to compile
// correctly if we just pass our argument list along. Note that
// this whole code block is only compiled if we're on MSVC6 anyway
int actualWritten = _vsnprintf(stackBuffer, bufSize, fmt, argPtr);
// Not a big enough buffer, bufSize characters written
if (actualWritten == -1) {
int heapSize = 512;
double powSize = 1.0;
char* heapBuffer = (char*)System::malloc(heapSize);
while ((_vsnprintf(heapBuffer, heapSize, fmt, argPtr) == -1) &&
(heapSize < maxSize)) {
heapSize = iCeil(heapSize * ::pow((double)2.0, powSize++));
heapBuffer = (char*)System::realloc(heapBuffer, heapSize);
}
heapBuffer[heapSize-1] = '\0';
std::string heapString(heapBuffer);
System::free(heapBuffer);
return heapString;
} else {
return std::string(stackBuffer);
}
}
#else
// glibc 2.1 has been updated to the C99 standard
std::string vformat(const char* fmt, va_list argPtr) {
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time. The number 161 is chosen
// to support two lines of text on an 80 character
// console (plus the null terminator).
const int bufSize = 161;
char stackBuffer[bufSize];
va_list argPtrCopy;
va_copy(argPtrCopy, argPtr);
int numChars = vsnprintf(stackBuffer, bufSize, fmt, argPtrCopy);
va_end(argPtrCopy);
if (numChars >= bufSize) {
// We didn't allocate a big enough string.
char* heapBuffer = (char*)System::malloc((numChars + 1) * sizeof(char));
debugAssert(heapBuffer);
int numChars2 = vsnprintf(heapBuffer, numChars + 1, fmt, argPtr);
debugAssert(numChars2 == numChars);
(void)numChars2;
std::string result(heapBuffer);
System::free(heapBuffer);
return result;
} else {
return std::string(stackBuffer);
}
}
#endif
} // namespace
#ifdef _MSC_VER
# pragma warning (pop)
#endif

View File

@@ -0,0 +1,223 @@
/* $Id: g3dfnmatch.cpp,v 1.3 2010/03/15 05:01:23 morgan3d Exp $ */
/* $OpenBSD: fnmatch.c,v 1.7 2000/03/23 19:13:51 millert Exp $ */
/*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "G3D/g3dfnmatch.h"
#ifdef G3D_WIN32
#include <ctype.h>
#include <string.h>
#include <stdio.h>
namespace G3D {
#define EOS '\0'
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int rangematch(const char *, char, int, char **);
int
g3dfnmatch(const char *pattern, const char *string, int flags)
{
const char *stringstart;
char *newp;
char c, test;
for (stringstart = string;;)
switch (c = *pattern++) {
case EOS:
if ((flags & FNM_LEADING_DIR) && *string == '/')
return (0);
return (*string == EOS ? 0 : FNM_NOMATCH);
case '?':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
++string;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
/* Optimize for pattern with * at end or before /. */
if (c == EOS) {
if (flags & FNM_PATHNAME)
return ((flags & FNM_LEADING_DIR) ||
strchr(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
} else if (c == '/' && (flags & FNM_PATHNAME)) {
if ((string = strchr(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
/* General case, use recursion. */
while ((test = *string) != EOS) {
if (!g3dfnmatch(pattern, string, flags & ~FNM_PERIOD))
return (0);
if (test == '/' && (flags & FNM_PATHNAME))
break;
++string;
}
return (FNM_NOMATCH);
case '[':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
switch (rangematch(pattern, *string, flags, &newp)) {
case RANGE_ERROR:
/* not a good range, treat as normal text */
goto normal;
case RANGE_MATCH:
pattern = newp;
break;
case RANGE_NOMATCH:
return (FNM_NOMATCH);
}
++string;
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
if ((c = *pattern++) == EOS) {
c = '\\';
--pattern;
}
}
/* FALLTHROUGH */
default:
normal:
if (c != *string && !((flags & FNM_CASEFOLD) &&
(tolower((unsigned char)c) ==
tolower((unsigned char)*string))))
return (FNM_NOMATCH);
++string;
break;
}
/* NOTREACHED */
}
static int
rangematch(const char *pattern, char test, int flags, char **newp)
{
int negate, ok;
char c, c2;
/*
* A bracket expression starting with an unquoted circumflex
* character produces unspecified results (IEEE 1003.2-1992,
* 3.13.2). This implementation treats it like '!', for
* consistency with the regular expression syntax.
* J.T. Conklin (conklin@ngai.kaleida.com)
*/
if ((negate = (*pattern == '!' || *pattern == '^')))
++pattern;
if (flags & FNM_CASEFOLD)
test = tolower((unsigned char)test);
/*
* A right bracket shall lose its special meaning and represent
* itself in a bracket expression if it occurs first in the list.
* -- POSIX.2 2.8.3.2
*/
ok = 0;
c = *pattern++;
do {
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *pattern++;
if (c == EOS)
return (RANGE_ERROR);
if (c == '/' && (flags & FNM_PATHNAME))
return (RANGE_NOMATCH);
if ((flags & FNM_CASEFOLD))
c = tolower((unsigned char)c);
if (*pattern == '-'
&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
pattern += 2;
if (c2 == '\\' && !(flags & FNM_NOESCAPE))
c2 = *pattern++;
if (c2 == EOS)
return (RANGE_ERROR);
if (flags & FNM_CASEFOLD)
c2 = tolower((unsigned char)c2);
if (c <= test && test <= c2)
ok = 1;
} else if (c == test)
ok = 1;
} while ((c = *pattern++) != ']');
*newp = (char *)pattern;
return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
}
}
#else
namespace G3D {
int g3dfnmatch(const char * a, const char *b, int c) {
return fnmatch(a, b, c);
}
}
#endif

View File

@@ -0,0 +1,109 @@
/**
@file g3dmath.cpp
@author Morgan McGuire, graphics3d.com
@created 2001-06-02
@edited 2004-02-24
*/
#include "G3D/g3dmath.h"
#include <cstdlib>
#include <cstring>
namespace G3D {
float gaussRandom(float mean, float stdev) {
// Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
// Modified to specify standard deviation and mean of distribution
float w, x1, x2;
// Loop until w is less than 1 so that log(w) is negative
do {
x1 = uniformRandom(-1.0, 1.0);
x2 = uniformRandom(-1.0, 1.0);
w = float(square(x1) + square(x2));
} while (w > 1.0f);
// Transform to gassian distribution
// Multiply by sigma (stdev ^ 2) and add mean.
return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean;
}
/**
This value should not be tested against directly, instead
G3D::isNan() and G3D::isFinite() will return reliable results. */
double inf() {
return std::numeric_limits<double>::infinity();
}
bool isNaN(float x) {
static const float n = fnan();
return memcmp(&x, &n, sizeof(float)) == 0;
}
bool isNaN(double x) {
static const double n = nan();
return memcmp(&x, &n, sizeof(double)) == 0;
}
/**
This value should not be tested against directly, instead
G3D::isNan() and G3D::isFinite() will return reliable results. */
float finf() {
return std::numeric_limits<float>::infinity();
}
/** This value should not be tested against directly, instead
G3D::isNan() and G3D::isFinite() will return reliable results. */
double nan() {
// double is a standard type and should have quiet NaN
return std::numeric_limits<double>::quiet_NaN();
}
float fnan() {
// double is a standard type and should have quiet NaN
return std::numeric_limits<float>::quiet_NaN();
}
int highestBit(uint32 x) {
// Binary search.
int base = 0;
if (x & 0xffff0000) {
base = 16;
x >>= 16;
}
if (x & 0x0000ff00) {
base += 8;
x >>= 8;
}
if (x & 0x000000f0) {
base += 4;
x >>= 4;
}
static const int lut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3};
return base + lut[x];
}
int iRandom(int low, int high) {
int r = iFloor(low + (high - low + 1) * (double)rand() / RAND_MAX);
// There is a *very small* chance of generating
// a number larger than high.
if (r > high) {
return high;
} else {
return r;
}
}
}

View File

@@ -0,0 +1,73 @@
/**
@file license.cpp
@author Morgan McGuire, graphics3d.com
@created 2004-04-15
@edited 2004-04-15
*/
#include "G3D/format.h"
#include <string>
namespace G3D {
std::string license() {
return format(
"This software is based in part on the PNG Reference Library which is\n"
"Copyright (c) 2004 Glenn Randers-Pehrson\n\n"
"This software is based in part on the work of the Independent JPEG Group.\n\n"
"This software is based on part on the FFmpeg libavformat and libavcodec libraries\n"
"(\"FFmpeg\", http://ffmpeg.mplayerhq.hu), which are included under the terms of the\n"
"GNU Lesser General Public License (LGPL), (http://www.gnu.org/copyleft/lesser.html).\n\n"
"%s"
"This program uses the G3D Library (http://g3d.sf.net), which\n"
"is licensed under the \"Modified BSD\" Open Source license. The G3D library\n"
"source code is Copyright © 2000-2010, Morgan McGuire, All rights reserved.\n"
"This program uses The OpenGL Extension Wrangler Library, which \n"
"is licensed under the \"Modified BSD\" Open Source license. \n"
"The OpenGL Extension Wrangler Library source code is\n"
"Copyright (C) 2002-2008, Milan Ikits <milan ikits[]ieee org>\n"
"Copyright (C) 2002-2008, Marcelo E. Magallon <mmagallo[]debian org>\n"
"Copyright (C) 2002, Lev Povalahev\n"
"All rights reserved.\n\n"
"The Modified BSD license is below, and requires the following statement:\n"
"\n"
"Redistribution and use in source and binary forms, with or without \n"
"modification, are permitted provided that the following conditions are met:\n"
"\n"
"* Redistributions of source code must retain the above copyright notice, \n"
" this list of conditions and the following disclaimer.\n"
"* Redistributions in binary form must reproduce the above copyright notice, \n"
" this list of conditions and the following disclaimer in the documentation \n"
" and/or other materials provided with the distribution.\n"
"* The name of the author may be used to endorse or promote products \n"
" derived from this software without specific prior written permission.\n"
"\n"
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" \n"
"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \n"
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
"ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE \n"
"LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR \n"
"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF \n"
"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n"
"THE POSSIBILITY OF SUCH DAMAGE.\n"
"\n\n"
"G3D VERSION %d\n",
#ifdef G3D_WIN32
"" // Win32 doesn't use SDL
#else
"This software uses the Simple DirectMedia Layer library (\"SDL\",\n"
"http://www.libsdl.org), which is included under the terms of the\n"
"GNU Lesser General Public License, (http://www.gnu.org/copyleft/lesser.html).\n\n"
#endif
,
G3D_VER);
}
}

View File

@@ -0,0 +1,738 @@
/**
@file prompt.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com
@cite Font setting code by Kurt Miller, kurt@flipcode.com
@created 2000-08-26
@edited 2005-01-14
*/
#include "G3D/prompt.h"
#include "G3D/platform.h"
#include <stdio.h>
#ifdef G3D_WIN32
# include <sstream>
# include <conio.h>
#else
# define _getch getchar
#endif
#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_OSX
/*#ifdef __LP64__
# undef __LP64__
#endif
*/
# include <Carbon/Carbon.h>
/*
#ifdef G3D_64BIT
# define __LP64__
#endif
*/
#endif
#endif /* G3DFIX: exclude GUI prompt code */
namespace G3D {
#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_WIN32
namespace _internal {
/**
Generic Win32 dialog facility.
@author Max McGuire
*/
class DialogTemplate {
public:
DialogTemplate(LPCSTR caption, DWORD style,
int x, int y, int w, int h,
LPCSTR font = NULL, WORD fontSize = 8) {
usedBufferLength = sizeof(DLGTEMPLATE);
totalBufferLength = usedBufferLength;
dialogTemplate = (DLGTEMPLATE*)malloc(totalBufferLength);
dialogTemplate->style = style;
if (font != NULL) {
dialogTemplate->style |= DS_SETFONT;
}
dialogTemplate->x = (short)x;
dialogTemplate->y = (short)y;
dialogTemplate->cx = (short)w;
dialogTemplate->cy = (short)h;
dialogTemplate->cdit = 0;
dialogTemplate->dwExtendedStyle = 0;
// The dialog box doesn't have a menu or a special class
AppendData("\0", 2);
AppendData("\0", 2);
// Add the dialog's caption to the template
AppendString(caption);
if (font != NULL) {
AppendData(&fontSize, sizeof(WORD));
AppendString(font);
}
}
void AddComponent(LPCSTR type, LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
DLGITEMTEMPLATE item;
item.style = style;
item.x = (short)x;
item.y = (short)y;
item.cx = (short)w;
item.cy = (short)h;
item.id = id;
item.dwExtendedStyle = exStyle;
AppendData(&item, sizeof(DLGITEMTEMPLATE));
AppendString(type);
AppendString(caption);
WORD creationDataLength = 0;
AppendData(&creationDataLength, sizeof(WORD));
// Increment the component count
dialogTemplate->cdit++;
}
void AddButton(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
AddStandardComponent(0x0080, caption, style, exStyle, x, y, w, h, id);
WORD creationDataLength = 0;
AppendData(&creationDataLength, sizeof(WORD));
}
void AddEditBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
AddStandardComponent(0x0081, caption, style, exStyle, x, y, w, h, id);
WORD creationDataLength = 0;
AppendData(&creationDataLength, sizeof(WORD));
}
void AddStatic(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
AddStandardComponent(0x0082, caption, style, exStyle, x, y, w, h, id);
WORD creationDataLength = 0;
AppendData(&creationDataLength, sizeof(WORD));
}
void AddListBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
AddStandardComponent(0x0083, caption, style, exStyle, x, y, w, h, id);
WORD creationDataLength = sizeof(WORD) + 5 * sizeof(WCHAR);
AppendData(&creationDataLength, sizeof(WORD));
AppendString("TEST");
}
void AddScrollBar(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
AddStandardComponent(0x0084, caption, style, exStyle, x, y, w, h, id);
WORD creationDataLength = 0;
AppendData(&creationDataLength, sizeof(WORD));
}
void AddComboBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
AddStandardComponent(0x0085, caption, style, exStyle, x, y, w, h, id);
WORD creationDataLength = 0;
AppendData(&creationDataLength, sizeof(WORD));
}
/**
*
* Returns a pointer to the Win32 dialog template which the object
* represents. This pointer may become invalid if additional components
* are added to the template.
*
*/
operator const DLGTEMPLATE*() const {
return dialogTemplate;
}
virtual ~DialogTemplate() {
free(dialogTemplate);
}
protected:
void AddStandardComponent(WORD type, LPCSTR caption, DWORD style, DWORD exStyle,
int x, int y, int w, int h, WORD id, LPSTR font = NULL, WORD fontSize = 8) {
DLGITEMTEMPLATE item;
// DWORD align the beginning of the component data
AlignData(sizeof(DWORD));
item.style = style;
if (font != NULL) {
item.style |= DS_SETFONT;
}
item.x = (short)x;
item.y = (short)y;
item.cx = (short)w;
item.cy = (short)h;
item.id = id;
item.dwExtendedStyle = exStyle;
AppendData(&item, sizeof(DLGITEMTEMPLATE));
WORD preType = 0xFFFF;
AppendData(&preType, sizeof(WORD));
AppendData(&type, sizeof(WORD));
AppendString(caption);
if (font != NULL) {
AppendData(&fontSize, sizeof(WORD));
AppendString(font);
}
// Increment the component count
dialogTemplate->cdit++;
}
void AlignData(int size) {
int paddingSize = usedBufferLength % size;
if (paddingSize != 0) {
EnsureSpace(paddingSize);
usedBufferLength += paddingSize;
}
}
void AppendString(LPCSTR string) {
int length = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0);
WCHAR* wideString = (WCHAR*)malloc(sizeof(WCHAR) * length);
MultiByteToWideChar(CP_ACP, 0, string, -1, wideString, length);
AppendData(wideString, length * sizeof(WCHAR));
free(wideString);
}
void AppendData(const void* data, int dataLength) {
EnsureSpace(dataLength);
memcpy((char*)dialogTemplate + usedBufferLength, data, dataLength);
usedBufferLength += dataLength;
}
void EnsureSpace(int length) {
if (length + usedBufferLength > totalBufferLength) {
totalBufferLength += length * 2;
void* newBuffer = malloc(totalBufferLength);
memcpy(newBuffer, dialogTemplate, usedBufferLength);
free(dialogTemplate);
dialogTemplate = (DLGTEMPLATE*)newBuffer;
}
}
private:
DLGTEMPLATE* dialogTemplate;
int totalBufferLength;
int usedBufferLength;
};
struct PromptParams {
const char* message;
const char* title;
};
/**
* Constants for controls.
*/
#define IDC_MESSAGE 1000
#define IDC_BUTTON0 2000
INT_PTR CALLBACK PromptDlgProc(HWND hDlg, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_INITDIALOG:
{
PromptParams *params = (PromptParams*)lParam;
::SetWindowTextA(::GetDlgItem(hDlg, IDC_MESSAGE), params->message);
::SetFocus(::GetDlgItem(hDlg, IDC_BUTTON0));
SetWindowTextA(hDlg, params->title);
HFONT hfont =
CreateFontA(16, 0, 0, 0, FW_NORMAL,
FALSE, FALSE, FALSE,
ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
PROOF_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New");
SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE,0));
break;
}
case WM_COMMAND:
{
int choiceNumber = LOWORD(wParam) - IDC_BUTTON0;
if ((choiceNumber >= 0) && (choiceNumber < 10)) {
EndDialog(hDlg, choiceNumber);
return TRUE;
}
}
break;
case WM_NCDESTROY:
// Under SDL 1.2.6 we get a NCDESTROY message for no reason and the
// window is immediately closed. This is here to debug the problem.
(void)0;
break;
}
return FALSE;
}
}; // namespace _internal
using namespace _internal;
/**
* Show a dialog prompt.
*/
static int guiPrompt(
const char* windowTitle,
const char* prompt,
const char** choice,
int numChoices) {
int width = 280;
int height = 128;
const int buttonSpacing = 2;
const int buttonWidth =
(width - buttonSpacing * 2 -
buttonSpacing * (numChoices - 1)) / numChoices;
const int buttonHeight = 13;
DialogTemplate dialogTemplate(
windowTitle,
WS_CAPTION | DS_CENTER | WS_SYSMENU,
10, 10, width, height,
"Tahoma");
dialogTemplate.AddEditBox(
"Edit", WS_VISIBLE | ES_READONLY | ES_OEMCONVERT | ES_MULTILINE | WS_TABSTOP, WS_EX_STATICEDGE,
2, 2, width - 4, height - buttonHeight - 7, IDC_MESSAGE);
int i;
for (i = 0; i < numChoices; i++) {
int x = buttonSpacing + i * (buttonWidth + buttonSpacing);
int y = height - buttonHeight - buttonSpacing;
dialogTemplate.AddButton(choice[i], WS_VISIBLE | WS_TABSTOP, 0,
x, y, buttonWidth, buttonHeight, IDC_BUTTON0 + (WORD)i);
}
// Convert all single \n characters to \r\n for proper printing
int strLen = 0;
const char* pStr = prompt;
while (*pStr != '\0') {
if ((*pStr == '\n') && (pStr != prompt)) {
if (*(pStr - 1) != '\r') {
++strLen;
}
}
++strLen;
++pStr;
}
char* newStr = (char*)malloc(strLen + 1);
const char* pStr2 = prompt;
char* pNew = newStr;
while (*pStr2 != '\0') {
if ((*pStr2 == '\n') && (pStr2 != prompt)) {
if (*(pStr2 - 1) != '\r') {
*pNew = '\r';
++pNew;
}
}
*pNew = *pStr2;
++pNew;
++pStr2;
}
*pNew = '\0';
PromptParams params;
params.message = newStr;;
params.title = windowTitle;
HMODULE module = GetModuleHandle(0);
int ret = DialogBoxIndirectParam(module, dialogTemplate, NULL, (DLGPROC) PromptDlgProc, (DWORD)&params);
free(newStr);
/*
For debugging when DialogBoxIndirectParam fails:
// The last error value. (Which is preserved across the call).
DWORD lastErr = GetLastError();
// The decoded message from FormatMessage
LPTSTR formatMsg = NULL;
if (NULL == formatMsg) {
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lastErr,
0,
(LPTSTR)&formatMsg,
0,
NULL);
}
// Make sure the message got translated into something.
LPTSTR realLastErr;
if (NULL != formatMsg) {
realLastErr = formatMsg;
} else {
realLastErr = "Last error code does not exist.";
}
// Get rid of the allocated memory from FormatMessage.
if (NULL != formatMsg) {
LocalFree((LPVOID)formatMsg);
}
*/
return ret;
}
#endif
#endif /* G3DFIX: exclude GUI prompt code */
/**
* Show a prompt on stdout
*/
static int textPrompt(
const char* windowTitle,
const char* prompt,
const char** choice,
int numChoices) {
printf("\n___________________________________________________\n");
printf("%s\n", windowTitle);
printf("%s", prompt);
if (numChoices > 10) {
numChoices = 10;
}
int c = -1;
if (numChoices > 1) {
printf("\n");
printf("Choose an option by number:");
while ((c < 0) || (c >= numChoices)) {
printf("\n");
for (int i = 0; i < numChoices; i++) {
if (numChoices <= 3) {
printf(" (%d) %s ", i, choice[i]);
} else {
printf(" (%d) %s\n", i, choice[i]);
}
}
printf("\n> ");
c = _getch() - '0';
if ((c < 0) || (c >= numChoices)) {
printf("'%d' is not a valid choice.", c);
} else {
printf("%d", c);
}
}
} else if (numChoices == 1) {
printf("\nPress any key for '%s'...", choice[0]);
_getch();
c = 0;
} else {
printf("\nPress any key...");
_getch();
c = 0;
}
printf("\n___________________________________________________\n");
return c;
}
#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_OSX
// See http://developer.apple.com/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html
#define CARBON_COMMANDID_START 128
#define CARBON_BUTTON_SPACING 12
#define CARBON_BUTTON_HEIGHT 20
#define CARBON_BUTTON_MINWIDTH 69
#define CARBON_WINDOW_PADDING 20
struct CallbackData {
WindowRef refWindow;
/** Index of this particular button */
int myIndex;
/** Buttons store their index into here when pressed. */
int* whichButton;
};
/**
Assumes that userData is a pointer to a carbon_evt_data_t.
*/
static pascal OSStatus DoCommandEvent(EventHandlerCallRef handlerRef, EventRef event, void* userData) {
// See http://developer.apple.com/documentation/Carbon/Conceptual/HandlingWindowsControls/index.html
CallbackData& callbackData = *(CallbackData*)userData;
# pragma unused(handlerRef)
callbackData.whichButton[0] = callbackData.myIndex;
// If we get here we can close the window
::QuitAppModalLoopForWindow(callbackData.refWindow);
// Return noErr to indicate that we handled the event
return noErr;
}
static int guiPrompt
(const char* windowTitle,
const char* prompt,
const char** choice,
int numChoices) {
WindowRef window;
int iNumButtonRows = 0;
int iButtonWidth = -1;
OSStatus err = noErr;
// Determine number of rows of buttons
while (iButtonWidth < CARBON_BUTTON_MINWIDTH) {
++iNumButtonRows;
iButtonWidth =
(550 - (CARBON_WINDOW_PADDING*2 +
(CARBON_BUTTON_SPACING*numChoices))) /
(numChoices/iNumButtonRows);
}
// Window Variables
Rect rectWin = {0, 0, 200 + ((iNumButtonRows-1) * (CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)), 550}; // top, left, bottom, right
CFStringRef szWindowTitle = CFStringCreateWithCString(kCFAllocatorDefault, windowTitle, kCFStringEncodingUTF8);
window = NULL;
err = CreateNewWindow(kMovableAlertWindowClass, kWindowStandardHandlerAttribute|kWindowCompositingAttribute, &rectWin, &window);
err = SetWindowTitleWithCFString(window, szWindowTitle);
err = SetThemeWindowBackground(window, kThemeBrushAlertBackgroundActive, false);
assert(err == noErr);
// Event Handler Variables
EventTypeSpec buttonSpec[] = {{ kEventClassControl, kEventControlHit }, { kEventClassCommand, kEventCommandProcess }};
EventHandlerUPP buttonHandler = NewEventHandlerUPP(DoCommandEvent);
// Static Text Variables
Rect rectStatic = {20, 20, 152, 530};
CFStringRef szStaticText = CFStringCreateWithCString(kCFAllocatorDefault, prompt, kCFStringEncodingUTF8);
ControlRef refStaticText = NULL;
err = CreateStaticTextControl(window, &rectStatic, szStaticText, NULL, &refStaticText);
// Button Variables
Rect bounds[numChoices];
CFStringRef caption[numChoices];
ControlRef button[numChoices];
int whichButton=-1;
CallbackData callbackData[numChoices];
// Create the Buttons and assign event handlers
for (int i = 0; i < numChoices; ++i) {
bounds[i].top = 160 + ((CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)*(i%iNumButtonRows));
bounds[i].right = 530 - ((iButtonWidth+CARBON_BUTTON_SPACING)*(i/iNumButtonRows));
bounds[i].left = bounds[i].right - iButtonWidth;
bounds[i].bottom = bounds[i].top + CARBON_BUTTON_HEIGHT;
// Convert the button captions to Apple strings
caption[i] = CFStringCreateWithCString(kCFAllocatorDefault, choice[i], kCFStringEncodingUTF8);
err = CreatePushButtonControl(window, &bounds[i], caption[i], &button[i]);
assert(err == noErr);
err = SetControlCommandID(button[i], CARBON_COMMANDID_START + i);
assert(err == noErr);
callbackData[i].refWindow = window;
callbackData[i].myIndex = i;
callbackData[i].whichButton = &whichButton;
err = InstallControlEventHandler(button[i], buttonHandler,
GetEventTypeCount(buttonSpec), buttonSpec,
&callbackData[i], NULL);
assert(err == noErr);
}
// Show Dialog
err = RepositionWindow(window, NULL, kWindowCenterOnMainScreen);
ShowWindow(window);
BringToFront(window);
err = ActivateWindow(window, true);
// Hack to get our window/process to the front...
ProcessSerialNumber psn = { 0, kCurrentProcess};
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess (&psn);
// Run in Modal State
err = RunAppModalLoopForWindow(window);
// Dispose of Button Related Data
for (int i = 0; i < numChoices; ++i) {
// Dispose of controls
DisposeControl(button[i]);
// Release CFStrings
CFRelease(caption[i]);
}
// Dispose of Other Controls
DisposeControl(refStaticText);
// Dispose of Event Handlers
DisposeEventHandlerUPP(buttonHandler);
// Dispose of Window
DisposeWindow(window);
// Release CFStrings
CFRelease(szWindowTitle);
CFRelease(szStaticText);
// Return Selection
return whichButton;
}
#endif
#endif /* G3DFIX: exclude GUI prompt code */
int prompt(
const char* windowTitle,
const char* prompt,
const char** choice,
int numChoices,
bool useGui) {
#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_WIN32
if (useGui) {
// Build the message box
return guiPrompt(windowTitle, prompt, choice, numChoices);
}
#endif
#ifdef G3D_OSX
if (useGui){
//Will default to text prompt if numChoices > 4
return guiPrompt(windowTitle, prompt, choice, numChoices);
}
#endif
#endif /* G3DFIX: exclude GUI prompt code */
return textPrompt(windowTitle, prompt, choice, numChoices);
}
void msgBox(
const std::string& message,
const std::string& title) {
const char *choice[] = {"Ok"};
prompt(title.c_str(), message.c_str(), choice, 1, true);
}
#ifndef G3D_WIN32
#undef _getch
#endif
};// namespace

View File

@@ -0,0 +1,275 @@
/**
@file stringutils.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2000-09-09
@edited 2008-01-10
*/
#include "G3D/platform.h"
#include "G3D/stringutils.h"
#include "G3D/BinaryInput.h"
#include <algorithm>
namespace G3D {
#ifdef _MSC_VER
// disable: "C++ exception handler used"
# pragma warning (push)
# pragma warning (disable : 4530)
#endif
#ifdef G3D_WIN32
const char* NEWLINE = "\r\n";
#else
const char* NEWLINE = "\n";
static bool iswspace(int ch) { return (ch==' ' || ch=='\t' || ch=='\n' || ch=='\r'); }
#endif
void parseCommaSeparated(const std::string s, Array<std::string>& array, bool stripQuotes) {
array.fastClear();
if (s == "") {
return;
}
size_t begin = 0;
const char delimiter = ',';
const char quote = '\"';
do {
size_t end = begin;
// Find the next comma, or the end of the string
bool inQuotes = false;
while ((end < s.length()) && (inQuotes || (s[end] != delimiter))) {
if (s[end] == quote) {
if ((end < s.length() - 2) && (s[end + 1] == quote) && (s[end + 2]) == quote) {
// Skip over the superquote
end += 2;
}
inQuotes = ! inQuotes;
}
++end;
}
array.append(s.substr(begin, end - begin));
begin = end + 1;
} while (begin < s.length());
if (stripQuotes) {
for (int i = 0; i < array.length(); ++i) {
std::string& t = array[i];
int L = t.length();
if ((L > 1) && (t[0] == quote) && (t[L - 1] == quote)) {
if ((L > 6) && (t[1] == quote) && (t[2] == quote) && (t[L - 3] == quote) && (t[L - 2] == quote)) {
// Triple-quote
t = t.substr(3, L - 6);
} else {
// Double-quote
t = t.substr(1, L - 2);
}
}
}
}
}
bool beginsWith(
const std::string& test,
const std::string& pattern) {
if (test.size() >= pattern.size()) {
for (int i = 0; i < (int)pattern.size(); ++i) {
if (pattern[i] != test[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
bool endsWith(
const std::string& test,
const std::string& pattern) {
if (test.size() >= pattern.size()) {
int te = test.size() - 1;
int pe = pattern.size() - 1;
for (int i = pattern.size() - 1; i >= 0; --i) {
if (pattern[pe - i] != test[te - i]) {
return false;
}
}
return true;
} else {
return false;
}
}
std::string wordWrap(
const std::string& input,
int numCols) {
std::string output;
size_t c = 0;
int len;
// Don't make lines less than this length
int minLength = numCols / 4;
size_t inLen = input.size();
bool first = true;
while (c < inLen) {
if (first) {
first = false;
} else {
output += NEWLINE;
}
if ((int)inLen - (int)c - 1 < numCols) {
// The end
output += input.substr(c, inLen - c);
break;
}
len = numCols;
// Look at character c + numCols, see if it is a space.
while ((len > minLength) &&
(input[c + len] != ' ')) {
len--;
}
if (len == minLength) {
// Just crop
len = numCols;
}
output += input.substr(c, len);
c += len;
if (c < input.size()) {
// Collapse multiple spaces.
while ((input[c] == ' ') && (c < input.size())) {
c++;
}
}
}
return output;
}
int stringCompare(
const std::string& s1,
const std::string& s2) {
return stringPtrCompare(&s1, &s2);
}
int stringPtrCompare(
const std::string* s1,
const std::string* s2) {
return s1->compare(*s2);
}
std::string toUpper(const std::string& x) {
std::string result = x;
std::transform(result.begin(), result.end(), result.begin(), toupper);
return result;
}
std::string toLower(const std::string& x) {
std::string result = x;
std::transform(result.begin(), result.end(), result.begin(), tolower);
return result;
}
Array<std::string> stringSplit(
const std::string& x,
char splitChar) {
Array<std::string> out;
// Pointers to the beginning and end of the substring
const char* start = x.c_str();
const char* stop = start;
while ((stop = strchr(start, splitChar))) {
out.append(std::string(start, stop - start));
start = stop + 1;
}
// Append the last one
out.append(std::string(start));
return out;
}
std::string stringJoin(
const Array<std::string>& a,
char joinChar) {
std::string out;
for (int i = 0; i < (int)a.size() - 1; ++i) {
out += a[i] + joinChar;
}
if (a.size() > 0) {
return out + a.last();
} else {
return out;
}
}
std::string stringJoin(
const Array<std::string>& a,
const std::string& joinStr) {
std::string out;
for (int i = 0; i < (int)a.size() - 1; ++i) {
out += a[i] + joinStr;
}
if (a.size() > 0) {
return out + a.last();
} else {
return out;
}
}
std::string trimWhitespace(
const std::string& s) {
size_t left = 0;
// Trim from left
while ((left < s.length()) && iswspace(s[left])) {
++left;
}
int right = s.length() - 1;
// Trim from right
while ((right > (int)left) && iswspace(s[right])) {
--right;
}
return s.substr(left, right - left + 1);
}
}; // namespace
#undef NEWLINE
#ifdef _MSC_VER
# pragma warning (pop)
#endif

View File

@@ -0,0 +1,155 @@
/**
@file uint128.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@author Kyle Whitson
@created 2008-07-17
@edited 2008-07-17
*/
#include "G3D/uint128.h"
namespace G3D {
/** Adds two 64-bit integers, placing the result and the overflow into 64-bit integers.*/
static void addAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
// Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
// This eliminates the need to and with 0xFFFFFFFF.
uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
uint64 tmp = uint64(a[0]) + b[0];
result = tmp & 0xFFFFFFFF;
uint32 c = tmp >> 32;
tmp = uint64(c) + a[1] + b[1];
result += tmp << 32;
carry = (tmp >> 32);
}
/** Multiplies two unsigned 64-bit integers, placing the result into one 64-bit int and the overflow into another.*/
void multiplyAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
// Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
// This eliminates the need to and with 0xFFFFFFFF.
uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
uint64 prod [2][2];
for(int i = 0; i < 2; ++i) {
for(int j = 0; j < 2; ++j) {
prod[i][j] = uint64(a[i]) * b[j];
}
}
// The product of the low bits of a and b will always fit into the result
result = prod[0][0];
// The product of the high bits of a and b will never fit into the result
carry = prod[1][1];
// The high 32 bits of prod[0][1] and prod[1][0] will never fit into the result
carry += prod[0][1] >> 32;
carry += prod[1][0] >> 32;
uint64 tmp;
addAndCarry(result, (prod[0][1] << 32), tmp, result);
carry += tmp;
addAndCarry(result, (prod[1][0] << 32), tmp, result);
carry += tmp;
}
uint128::uint128(const uint64& hi, const uint64& lo) : hi(hi), lo(lo) {
}
uint128::uint128(const uint64& lo) : hi(0), lo(lo) {
}
uint128& uint128::operator+=(const uint128& x) {
G3D::uint64 carry;
addAndCarry(lo, x.lo, carry, lo);
// Adding the carry will change hi. Save the old hi bits in case this == x.
const uint64 xHi = x.hi;
hi += carry;
hi += xHi;
return *this;
}
uint128& uint128::operator*=(const uint128& x) {
// The low bits will get overwritten when doing the multiply, so back up both (in case &x == this)
const uint64 oldLo = lo;
const uint64 oldXLo = x.lo;
G3D::uint64 carry;
multiplyAndCarry(oldLo, oldXLo, carry, lo);
// Overflow doesn't matter here because the result is going into hi - any overflow will exceed the capacity of a 128-bit number
// Note: hi * x.hi will always overflow, since (x * 2^64) * (y * 2^64) = x*y*(2^128). The largest number expressable in 128 bits is
// 2^128 - 1.
hi = carry + (oldLo * x.hi) + (hi * oldXLo);
return *this;
}
uint128& uint128::operator^=(const uint128& x) {
hi ^= x.hi;
lo ^= x.lo;
return *this;
}
uint128& uint128::operator&=(const uint128& x) {
hi &= x.hi;
lo &= x.lo;
return *this;
}
uint128& uint128::operator|=(const uint128& x) {
hi |= x.hi;
lo |= x.lo;
return *this;
}
bool uint128::operator==(const uint128& x) {
return (hi == x.hi) && (lo == x.lo);
}
uint128& uint128::operator>>=(const int x) {
//Before shifting, mask out the bits that will be shifted out of hi.
//Put a 1 in the first bit that will not be lost in the shift, then subtract 1 to get the mask.
uint64 mask = ((uint64)1L << x) - 1;
uint64 tmp = hi & mask;
hi >>= x;
//Shift lo and add the bits shifted down from hi
lo = (lo >> x) + (tmp << (64 - x));
return *this;
}
uint128& uint128::operator<<=(const int x) {
//Before shifting, mask out the bits that will be shifted out of lo.
//Put a 1 in the last bit that will be lost in the shift, then subtract 1 to get the logical inverse of the mask.
//A bitwise NOT will then produce the correct mask.
uint64 mask = ~((((uint64)1L) << (64 - x)) - 1);
uint64 tmp = lo & mask;
lo <<= x;
//Shift hi and add the bits shifted up from lo
hi = (hi << x) + (tmp >> (64 - x));
return *this;
}
uint128 uint128::operator&(const uint128& x) {
return uint128(hi & x.hi, lo & x.lo);
}
}