Get rid of the old basics!
This commit is contained in:
parent
ef9b9d04c8
commit
6c23fa9f32
2 changed files with 0 additions and 752 deletions
536
src/basics.cc
536
src/basics.cc
|
@ -1,536 +0,0 @@
|
|||
/* basics.c
|
||||
*
|
||||
* Definition of basic types.
|
||||
*
|
||||
* - Vector3 is a three tuple vector of x, y, and z.
|
||||
* - Ray is a vector plus a direction.
|
||||
* - Color is a four tuple of red, green, blue, and alpha.
|
||||
*
|
||||
* Eryn Wells <eryn@erynwells.me>
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "basics.h"
|
||||
|
||||
#pragma mark - Vectors
|
||||
|
||||
const Vector3 Vector3::Zero = Vector3();
|
||||
const Vector3 Vector3::X = Vector3(1, 0, 0);
|
||||
const Vector3 Vector3::Y = Vector3(0, 1, 0);
|
||||
const Vector3 Vector3::Z = Vector3(0, 0, 1);
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::Vector3 --
|
||||
*
|
||||
* Default constructor. Create a zero vector.
|
||||
*/
|
||||
Vector3::Vector3()
|
||||
: Vector3(0.0, 0.0, 0.0)
|
||||
{ }
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::Vector3 --
|
||||
*
|
||||
* Constructor. Create a vector consisting of the given coordinates.
|
||||
*/
|
||||
Vector3::Vector3(Double _x, Double _y, Double _z)
|
||||
: x(_x), y(_y), z(_z)
|
||||
{ }
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::operator= --
|
||||
*
|
||||
* Copy the given vector's values into this vector. Return a reference to this vector.
|
||||
*/
|
||||
Vector3 &
|
||||
Vector3::operator=(const Vector3 &v)
|
||||
{
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::operator*= --
|
||||
* Vector3::operator/= --
|
||||
* Vector3::operator+= --
|
||||
* Vector3::operator-= --
|
||||
*
|
||||
* Perform the corresponding arithmetic operation on this vector and the given vector. These methods are destructive and
|
||||
* a reference to this vector is returned.
|
||||
*/
|
||||
Vector3 &
|
||||
Vector3::operator*=(const Double &rhs)
|
||||
{
|
||||
x *= rhs;
|
||||
y *= rhs;
|
||||
z *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3 &
|
||||
Vector3::operator/=(const Double &rhs)
|
||||
{
|
||||
return *this *= (1.0f / rhs);
|
||||
}
|
||||
|
||||
Vector3 &
|
||||
Vector3::operator+=(const Vector3 &rhs)
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
z += rhs.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3 &
|
||||
Vector3::operator-=(const Vector3 &rhs)
|
||||
{
|
||||
return *this += -rhs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::operator* --
|
||||
* Vector3::operator/ --
|
||||
* Vector3::operator+ --
|
||||
* Vector3::operator- --
|
||||
*
|
||||
* Perform the corresponding operation on a copy of this vector. Return a new vector.
|
||||
*/
|
||||
Vector3
|
||||
Vector3::operator*(const Double &rhs)
|
||||
const
|
||||
{
|
||||
return Vector3(*this) *= rhs;
|
||||
}
|
||||
|
||||
Vector3
|
||||
Vector3::operator/(const Double &rhs)
|
||||
const
|
||||
{
|
||||
return Vector3(*this) /= rhs;
|
||||
}
|
||||
|
||||
Vector3
|
||||
Vector3::operator+(const Vector3 &rhs)
|
||||
const
|
||||
{
|
||||
return Vector3(*this) += rhs;
|
||||
}
|
||||
|
||||
Vector3
|
||||
Vector3::operator-(const Vector3 &rhs)
|
||||
const
|
||||
{
|
||||
return Vector3(*this) -= rhs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::operator- --
|
||||
*
|
||||
* Negate this vector. Return a new vector.
|
||||
*/
|
||||
Vector3
|
||||
Vector3::operator-()
|
||||
const
|
||||
{
|
||||
return Vector3(-x, -y, -z);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::operator== --
|
||||
* Vector3::operator!= --
|
||||
*
|
||||
* Compute boolean equality and non-equality of this and the given vectors.
|
||||
*/
|
||||
bool
|
||||
Vector3::operator==(const Vector3 &rhs)
|
||||
const
|
||||
{
|
||||
return x == rhs.x && y == rhs.y && z == rhs.z;
|
||||
}
|
||||
|
||||
bool
|
||||
Vector3::operator!=(const Vector3 &rhs)
|
||||
const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::length2 --
|
||||
*
|
||||
* Compute and return the length-squared of this vector.
|
||||
*/
|
||||
Double
|
||||
Vector3::length2()
|
||||
const
|
||||
{
|
||||
return x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::length --
|
||||
*
|
||||
* Compute and return the length of this vector.
|
||||
*/
|
||||
Double
|
||||
Vector3::length()
|
||||
const
|
||||
{
|
||||
return sqrt(length2());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::dot --
|
||||
*
|
||||
* Compute and return the dot product of this and the given vectors.
|
||||
*/
|
||||
Double
|
||||
Vector3::dot(const Vector3 &v)
|
||||
const
|
||||
{
|
||||
return x*v.x + y*v.y + z*v.z;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::cross --
|
||||
*
|
||||
* Compute and return the cross product of this and the given vectors.
|
||||
*/
|
||||
Vector3
|
||||
Vector3::cross(const Vector3 &v)
|
||||
const
|
||||
{
|
||||
return Vector3(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::normalize --
|
||||
*/
|
||||
Vector3 &
|
||||
Vector3::normalize()
|
||||
{
|
||||
// Use the overloaded /= compound operator to do this.
|
||||
return *this /= length();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Vector3::normalized --
|
||||
*/
|
||||
Vector3
|
||||
Vector3::normalized()
|
||||
const
|
||||
{
|
||||
return *this / length();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* operator* --
|
||||
*
|
||||
* Multiply the given float by the given vector. Return a new vector.
|
||||
*/
|
||||
const Vector3
|
||||
operator*(const Double &lhs, const Vector3 &rhs)
|
||||
{
|
||||
return rhs * lhs;
|
||||
}
|
||||
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const Vector3 &v)
|
||||
{
|
||||
// Stream the vector like this: <x, y, z>
|
||||
os << "<" << v.x << ", " << v.y << ", " << v.z << ">";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
Vector3
|
||||
LinearCombination(const Double k1, const Vector3& v1,
|
||||
const Double k2, const Vector3& v2,
|
||||
const Double k3, const Vector3& v3)
|
||||
{
|
||||
return Vector3(k1 * v1.x + k2 * v2.x + k3 * v3.x,
|
||||
k1 * v1.y + k2 * v2.y + k3 * v3.y,
|
||||
k1 * v1.z + k2 * v2.z + k3 * v3.z);
|
||||
}
|
||||
|
||||
#pragma mark - Matrices
|
||||
|
||||
#if 0
|
||||
/* static */ Matrix4
|
||||
Matrix4::Zero()
|
||||
{
|
||||
Matrix4 m;
|
||||
memset(m.mCells, 0, 16 * sizeof(Double));
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/* static */ Matrix4
|
||||
Matrix4::Identity()
|
||||
{
|
||||
Matrix4 m = Zero();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
m.mCells[i * 4 + i] = 1.0;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/* static */ Matrix4
|
||||
Matrix4::Translation(Double x,
|
||||
Double y,
|
||||
Double z)
|
||||
{
|
||||
Matrix4 m = Identity();
|
||||
m.mCells[3] = x;
|
||||
m.mCells[7] = y;
|
||||
m.mCells[11] = z;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/* static */ Matrix4
|
||||
Matrix4::Rotation(Double x,
|
||||
Double y,
|
||||
Double z)
|
||||
{
|
||||
Matrix4 m = Identity();
|
||||
|
||||
if (x == 0.0 && y == 0.0 && z == 0.0) {
|
||||
/* No rotation, just return the identity matrix. */
|
||||
} else if (x != 0.0 && y == 0.0 && z == 0.0) {
|
||||
/*
|
||||
* Fill in m with values for an X rotation matrix.
|
||||
*
|
||||
* [1 0 0 0]
|
||||
* [0 cos(x) -sin(x) 0]
|
||||
* [0 sin(x) cos(x) 0]
|
||||
* [0 0 0 1]
|
||||
*/
|
||||
Double cosX = std::cos(x);
|
||||
Double sinX = std::sin(x);
|
||||
m.mCells[5] = cosX;
|
||||
m.mCells[6] = -sinX;
|
||||
m.mCells[9] = sinX;
|
||||
m.mCells[10] = cosX;
|
||||
} else if (x == 0.0 && y != 0.0 && z == 0.0) {
|
||||
/*
|
||||
* Fill in m with values for a Y rotation matrix.
|
||||
*
|
||||
* [ cos(y) 0 sin(y) 0]
|
||||
* [ 0 1 0 0]
|
||||
* [-sin(y) 0 cos(y) 0]
|
||||
* [ 0 0 0 1]
|
||||
*/
|
||||
Double cosY = std::cos(y);
|
||||
Double sinY = std::sin(y);
|
||||
m.mCells[0] = cosY;
|
||||
m.mCells[2] = sinY;
|
||||
m.mCells[8] = -sinY;
|
||||
m.mCells[10] = cosY;
|
||||
} else if (x == 0.0 && y == 0.0 && z != 0.0) {
|
||||
/*
|
||||
* Fill in m with values for a Z rotation matrix.
|
||||
*
|
||||
* [cos(z) -sin(z) 0 0]
|
||||
* [sin(z) cos(z) 0 0]
|
||||
* [ 0 0 1 0]
|
||||
* [ 0 0 0 1]
|
||||
*/
|
||||
Double cosZ = std::cos(z);
|
||||
Double sinZ = std::sin(z);
|
||||
m.mCells[0] = cosZ;
|
||||
m.mCells[1] = -sinZ;
|
||||
m.mCells[4] = sinZ;
|
||||
m.mCells[5] = cosZ;
|
||||
} else {
|
||||
/*
|
||||
* TODO: Rotation in more than one dimension. So do a general rotation
|
||||
* matrix. There's some magic way to do this with matrix multiplication
|
||||
* that avoids gimbal lock. I should figure out how to do it properly.
|
||||
*/
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::Matrix4 --
|
||||
*/
|
||||
Matrix4::Matrix4()
|
||||
: mCells()
|
||||
{ }
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::Matrix4 --
|
||||
*/
|
||||
Matrix4::Matrix4(const Double cells[16])
|
||||
: mCells()
|
||||
{
|
||||
memcpy(mCells, cells, 16 * sizeof(Double));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::Matrix4 --
|
||||
*/
|
||||
Matrix4::Matrix4(const Matrix4& rhs)
|
||||
: Matrix4(rhs.mCells)
|
||||
{ }
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::operator() --
|
||||
*/
|
||||
Double&
|
||||
Matrix4::operator()(const unsigned int row,
|
||||
const unsigned int col)
|
||||
{
|
||||
assert(row < 4 && col < 4);
|
||||
return mCells[4*row + col];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::operator* --
|
||||
*/
|
||||
Matrix4
|
||||
Matrix4::operator*(const Double rhs)
|
||||
const
|
||||
{
|
||||
return Matrix4(*this) *= rhs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::operator*= --
|
||||
*/
|
||||
Matrix4&
|
||||
Matrix4::operator*=(const Double rhs)
|
||||
{
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mCells[i] *= rhs;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::operator* --
|
||||
*/
|
||||
Matrix4
|
||||
Matrix4::operator*(const Matrix4& rhs)
|
||||
const
|
||||
{
|
||||
return Matrix4(*this) *= rhs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::operator*=
|
||||
*/
|
||||
Matrix4&
|
||||
Matrix4::operator*=(const Matrix4& rhs)
|
||||
{
|
||||
Matrix4 lhs(*this);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
/* Each cell is Sigma(k=0, 4)(lhs[ik] * rhs[kj]) */
|
||||
const int cell = i*4 + j;
|
||||
mCells[cell] = 0.0;
|
||||
for (int k = 0; k < 4; k++) {
|
||||
mCells[cell] += lhs.mCells[i*4 + k] * rhs.mCells[k*4 + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Matrix4::CArray --
|
||||
*/
|
||||
const Double*
|
||||
Matrix4::CArray()
|
||||
const
|
||||
{
|
||||
return mCells;
|
||||
}
|
||||
|
||||
|
||||
Matrix4
|
||||
operator*(const Double rhs,
|
||||
const Matrix4& lhs)
|
||||
{
|
||||
/* Scalar multiplication is commutative. */
|
||||
return lhs * rhs;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Rays
|
||||
|
||||
/*
|
||||
* Ray::Ray --
|
||||
*
|
||||
* Default constructor. Create a ray at the origin (0, 0, 0) with direction (0, 0, 0).
|
||||
*/
|
||||
Ray::Ray()
|
||||
: Ray(Vector3::Zero, Vector3::Zero)
|
||||
{ }
|
||||
|
||||
|
||||
/*
|
||||
* Ray::Ray --
|
||||
*
|
||||
* Constructor. Create a ray with the given origin and direction.
|
||||
*/
|
||||
Ray::Ray(Vector3 o, Vector3 d)
|
||||
: origin(o), direction(d)
|
||||
{ }
|
||||
|
||||
|
||||
/*
|
||||
* Ray::parameterize --
|
||||
*
|
||||
* Compute and return the point given by parameterizing this Ray by time t.
|
||||
*/
|
||||
Vector3
|
||||
Ray::parameterize(const Double& t)
|
||||
const
|
||||
{
|
||||
return origin + t * direction;
|
||||
}
|
||||
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const Ray &r)
|
||||
{
|
||||
os << "[Ray " << r.origin << " " << r.direction << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
#pragma mark - Colors
|
216
src/basics.h
216
src/basics.h
|
@ -1,216 +0,0 @@
|
|||
/* basics.h
|
||||
* vim: set tw=80:
|
||||
* Eryn Wells <eryn@erynwells.me>
|
||||
*/
|
||||
|
||||
#ifndef __BASICS_H__
|
||||
#define __BASICS_H__
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
/*
|
||||
* XXX: THIS SHOULD NOT BE HERE. REMOVE IT WHEN MOVING TO CHARLES NAMESPACE IS
|
||||
* DONE.
|
||||
*/
|
||||
using charles::Double;
|
||||
|
||||
|
||||
/**
|
||||
* A very small constant. If a value is between EPSILON and 0.0, it is
|
||||
* considered to be zero.
|
||||
*/
|
||||
const Double EPSILON = 1.0e-10;
|
||||
|
||||
/**
|
||||
* The maximum distance a ray can travel. This is the maximum value t can be.
|
||||
*/
|
||||
const Double MAX_DISTANCE = 1.0e7;
|
||||
|
||||
/**
|
||||
* Determine if the value is "close enough" to zero. Takes the absolute value
|
||||
* and compares it to `EPSILON`.
|
||||
*
|
||||
* @see EPSILON
|
||||
*
|
||||
* @param [in] value The value to check
|
||||
* @returns `true` if the value is close enough to zero
|
||||
*/
|
||||
template <typename T>
|
||||
inline bool
|
||||
NearZero(const T value)
|
||||
{
|
||||
return std::fabs(value) < EPSILON;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if two values are "close enough" to be considered equal. Subtracts
|
||||
* one from the other and checks if the result is near zero.
|
||||
*
|
||||
* @see NearZero
|
||||
*
|
||||
* @param [in] left The left parameter
|
||||
* @param [in] right The right parameter
|
||||
* @returns `true` if the values are close enough to be equal
|
||||
*/
|
||||
template <typename T>
|
||||
inline bool
|
||||
NearlyEqual(const T left,
|
||||
const T right)
|
||||
{
|
||||
return NearZero(left - right);
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
TooFar(const Double& value)
|
||||
{
|
||||
return value > MAX_DISTANCE;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
struct Vector3
|
||||
{
|
||||
Vector3();
|
||||
Vector3(Double x, Double y, Double z);
|
||||
|
||||
Vector3 &operator=(const Vector3 &v);
|
||||
Vector3 &operator*=(const Double &rhs);
|
||||
Vector3 &operator/=(const Double &rhs);
|
||||
Vector3 &operator+=(const Vector3 &rhs);
|
||||
Vector3 &operator-=(const Vector3 &rhs);
|
||||
Vector3 operator*(const Double &rhs) const;
|
||||
Vector3 operator/(const Double &rhs) const;
|
||||
Vector3 operator+(const Vector3 &rhs) const;
|
||||
Vector3 operator-(const Vector3 &rhs) const;
|
||||
Vector3 operator-() const;
|
||||
|
||||
bool operator==(const Vector3 &rhs) const;
|
||||
bool operator!=(const Vector3 &rhs) const;
|
||||
|
||||
Double length2() const;
|
||||
Double length() const;
|
||||
Double dot(const Vector3 &v) const;
|
||||
Vector3 cross(const Vector3 &v) const;
|
||||
|
||||
/** Normalize and return a reference to this vector. */
|
||||
Vector3 &normalize();
|
||||
|
||||
/** Return a copy of this vector, normalized. Does not modify this vector. */
|
||||
Vector3 normalized() const;
|
||||
|
||||
static const Vector3 Zero;
|
||||
// Unit vectors in each of the three cartesian directions.
|
||||
static const Vector3 X, Y, Z;
|
||||
|
||||
Double x, y, z;
|
||||
};
|
||||
|
||||
const Vector3 operator*(const Double &lhs, const Vector3 &rhs);
|
||||
std::ostream &operator<<(std::ostream &os, const Vector3 &v);
|
||||
|
||||
|
||||
Vector3 LinearCombination(const Double k1, const Vector3& v1,
|
||||
const Double k2, const Vector3& v2,
|
||||
const Double k3, const Vector3& v3);
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
struct Matrix4
|
||||
{
|
||||
/** Create a 4x4 zero matrix. That is, all cells are 0. */
|
||||
static Matrix4 Zero();
|
||||
|
||||
/** Create a 4x4 identity matrix. */
|
||||
static Matrix4 Identity();
|
||||
|
||||
Matrix4();
|
||||
Matrix4(const Double cells[16]);
|
||||
Matrix4(const Matrix4& rhs);
|
||||
|
||||
/**
|
||||
* Create a 4x4 translation matrix. A translation matrix looks like this:
|
||||
*
|
||||
* [1 0 0 x]
|
||||
* [0 1 0 y]
|
||||
* [0 0 1 z]
|
||||
* [0 0 0 1]
|
||||
*
|
||||
* @param [in] x X translation
|
||||
* @param [in] y Y translation
|
||||
* @param [in] z Z translation
|
||||
* @returns The translation matrix
|
||||
*/
|
||||
static Matrix4 Translation(Double x, Double y, Double z);
|
||||
|
||||
/**
|
||||
* Create a 4x4 rotation matrix. A rotation matrices are quite complicated.
|
||||
*
|
||||
* @param [in] x X rotation angle in radians
|
||||
* @param [in] y Y rotation angle in radians
|
||||
* @param [in] z Z rotation angle in radians
|
||||
* @returns The rotation matrix
|
||||
*/
|
||||
static Matrix4 Rotation(Double x, Double y, Double z);
|
||||
|
||||
/**
|
||||
* Get the value of the cell at (row, col).
|
||||
*
|
||||
* @param [in] row The row, must be less than the matrix's width
|
||||
* @param [in] col The column, must be less than the matrix's height
|
||||
* @returns The value of the cell at (row, col)
|
||||
*/
|
||||
Double& operator()(const unsigned int row, const unsigned int col);
|
||||
|
||||
/**
|
||||
* Scalar multiplication.
|
||||
*
|
||||
* @param [in] rhs The scalar factor
|
||||
* @returns A copy of this matrix, multiplied by the scalar
|
||||
*/
|
||||
Matrix4 operator*(const Double rhs) const;
|
||||
|
||||
/**
|
||||
* Scalar multiplication. Multiplies this matrix by the given factor.
|
||||
*
|
||||
* @param [in] rhs The scalar factor
|
||||
* @returns *this
|
||||
*/
|
||||
Matrix4& operator*=(const Double rhs);
|
||||
|
||||
Matrix4 operator*(const Matrix4& rhs) const;
|
||||
Matrix4& operator*=(const Matrix4& rhs);
|
||||
|
||||
const Double* CArray() const;
|
||||
|
||||
private:
|
||||
Double mCells[16];
|
||||
};
|
||||
|
||||
|
||||
Matrix4 operator*(const Double lhs, const Matrix4& rhs);
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
struct Ray
|
||||
{
|
||||
Ray();
|
||||
Ray(Vector3 o, Vector3 d);
|
||||
|
||||
Vector3 parameterize(const Double& t) const;
|
||||
|
||||
Vector3 origin, direction;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Ray &r);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue