diff options
Diffstat (limited to 'src/libmatrix')
32 files changed, 5432 insertions, 0 deletions
diff --git a/src/libmatrix/COPYING b/src/libmatrix/COPYING new file mode 100644 index 0000000..3e4d480 --- /dev/null +++ b/src/libmatrix/COPYING @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2010 Linaro Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/libmatrix/Makefile b/src/libmatrix/Makefile new file mode 100644 index 0000000..418311f --- /dev/null +++ b/src/libmatrix/Makefile @@ -0,0 +1,42 @@ +CXXFLAGS = -Wall -Werror -pedantic -O3 +LIBMATRIX = libmatrix.a +LIBSRCS = mat.cc program.cc log.cc util.cc shader-source.cc +LIBOBJS = $(LIBSRCS:.cc=.o) +TESTDIR = test +LIBMATRIX_TESTS = $(TESTDIR)/libmatrix_test +TESTSRCS = $(TESTDIR)/options.cc \ + $(TESTDIR)/const_vec_test.cc \ + $(TESTDIR)/inverse_test.cc \ + $(TESTDIR)/transpose_test.cc \ + $(TESTDIR)/shader_source_test.cc \ + $(TESTDIR)/util_split_test.cc \ + $(TESTDIR)/libmatrix_test.cc +TESTOBJS = $(TESTSRCS:.cc=.o) + +# Make sure to build both the library targets and the tests, and generate +# a make failure if the tests don't pass. +default: $(LIBMATRIX) $(LIBMATRIX_TESTS) run_tests + +# Main library targets here. +mat.o : mat.cc mat.h vec.h +program.o: program.cc program.h mat.h vec.h +log.o: log.cc log.h +util.o: util.cc util.h +shader-source.o: shader-source.cc shader-source.h mat.h vec.h util.h +libmatrix.a : mat.o stack.h program.o log.o util.o shader-source.o + $(AR) -r $@ $(LIBOBJS) + +# Tests and execution targets here. +$(TESTDIR)/options.o: $(TESTDIR)/options.cc $(TESTDIR)/libmatrix_test.h +$(TESTDIR)/libmatrix_test.o: $(TESTDIR)/libmatrix_test.cc $(TESTDIR)/libmatrix_test.h $(TESTDIR)/inverse_test.h $(TESTDIR)/transpose_test.h +$(TESTDIR)/const_vec_test.o: $(TESTDIR)/const_vec_test.cc $(TESTDIR)/const_vec_test.h $(TESTDIR)/libmatrix_test.h vec.h +$(TESTDIR)/inverse_test.o: $(TESTDIR)/inverse_test.cc $(TESTDIR)/inverse_test.h $(TESTDIR)/libmatrix_test.h mat.h +$(TESTDIR)/transpose_test.o: $(TESTDIR)/transpose_test.cc $(TESTDIR)/transpose_test.h $(TESTDIR)/libmatrix_test.h mat.h +$(TESTDIR)/shader_source_test.o: $(TESTDIR)/shader_source_test.cc $(TESTDIR)/shader_source_test.h $(TESTDIR)/libmatrix_test.h shader-source.h +$(TESTDIR)/util_split_test.o: $(TESTDIR)/util_split_test.cc $(TESTDIR)/util_split_test.h $(TESTDIR)/libmatrix_test.h util.h +$(TESTDIR)/libmatrix_test: $(TESTOBJS) libmatrix.a + $(CXX) -o $@ $^ +run_tests: $(LIBMATRIX_TESTS) + $(LIBMATRIX_TESTS) +clean : + $(RM) $(LIBOBJS) $(TESTOBJS) $(LIBMATRIX) $(LIBMATRIX_TESTS) diff --git a/src/libmatrix/README b/src/libmatrix/README new file mode 100644 index 0000000..17229f6 --- /dev/null +++ b/src/libmatrix/README @@ -0,0 +1,11 @@ +libmatrix +========= + +A simple C++ template library that provides containers and arithmetic +operations for vectors, matrices and matrix stacks of 2, 3 and 4 dimensions. +Additionally, it provides implementations of the more common matrix +transformations described by the OpenGL programming guide. libmatrix does +not make any OpenGL calls, it merely replaces a portion of the fixed-function +vertex processing API that have been removed from newer releases of core OpenGL +and omitted from OpenGL ES. The goal is simply to provide developers an easier +point of entry into developing for OpenGL and OpenGL ES. diff --git a/src/libmatrix/gl-if.h b/src/libmatrix/gl-if.h new file mode 100644 index 0000000..89b84e9 --- /dev/null +++ b/src/libmatrix/gl-if.h @@ -0,0 +1,18 @@ +// +// Copyright (c) 2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef GL_IF_H_ +#define GL_IF_H_ +// Inclusion abstraction to provide project specific interface headers for +// whatever flavor of OpenGL(|ES) is appropriate. For core libmatrix, this +// is GLEW. +#include "gl-headers.h" +#endif // GL_IF_H_ diff --git a/src/libmatrix/log.cc b/src/libmatrix/log.cc new file mode 100644 index 0000000..10603ae --- /dev/null +++ b/src/libmatrix/log.cc @@ -0,0 +1,173 @@ +// +// Copyright (c) 2010-2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis <alexandros.frantzis@linaro.org> +// Jesse Barker <jesse.barker@linaro.org> +// +#include <unistd.h> +#include <cstdio> +#include <cstdarg> +#include <string> +#include <sstream> +#include <iostream> +#include "log.h" + +#ifdef ANDROID +#include <android/log.h> +#endif + +using std::string; + +const string Log::continuation_prefix("\x10"); +string Log::appname_; +bool Log::do_debug_(false); +std::ostream* Log::extra_out_(0); + +static const string terminal_color_normal("\033[0m"); +static const string terminal_color_red("\033[1;31m"); +static const string terminal_color_cyan("\033[36m"); +static const string terminal_color_yellow("\033[33m"); +static const string empty; + +static void +print_prefixed_message(std::ostream& stream, const string& color, const string& prefix, + const string& fmt, va_list ap) +{ + va_list aq; + + /* Estimate message size */ + va_copy(aq, ap); + int msg_size = vsnprintf(NULL, 0, fmt.c_str(), aq); + va_end(aq); + + /* Create the buffer to hold the message */ + char *buf = new char[msg_size + 1]; + + /* Store the message in the buffer */ + va_copy(aq, ap); + vsnprintf(buf, msg_size + 1, fmt.c_str(), aq); + va_end(aq); + + /* + * Print the message lines prefixed with the supplied prefix. + * If the target stream is a terminal make the prefix colored. + */ + string linePrefix; + if (!prefix.empty()) + { + static const string colon(": "); + string start_color; + string end_color; + if (!color.empty()) + { + start_color = color; + end_color = terminal_color_normal; + } + linePrefix = start_color + prefix + end_color + colon; + } + + std::string line; + std::stringstream ss(buf); + + while(std::getline(ss, line)) { + /* + * If this line is a continuation of a previous log message + * just print the line plainly. + */ + if (line[0] == Log::continuation_prefix[0]) { + stream << line.c_str() + 1; + } + else { + /* Normal line, emit the prefix. */ + stream << linePrefix << line; + } + + /* Only emit a newline if the original message has it. */ + if (!(ss.rdstate() & std::stringstream::eofbit)) + stream << std::endl; + } + + delete[] buf; +} + + +void +Log::info(const char *fmt, ...) +{ + static const string infoprefix("Info"); + const string& prefix(do_debug_ ? infoprefix : empty); + va_list ap; + va_start(ap, fmt); + +#ifndef ANDROID + static const string& infocolor(isatty(fileno(stdout)) ? terminal_color_cyan : empty); + const string& color(do_debug_ ? infocolor : empty); + print_prefixed_message(std::cout, color, prefix, fmt, ap); +#else + __android_log_vprint(ANDROID_LOG_INFO, appname_.c_str(), fmt, ap); +#endif + + if (extra_out_) + print_prefixed_message(*extra_out_, empty, prefix, fmt, ap); + + va_end(ap); +} + +void +Log::debug(const char *fmt, ...) +{ + static const string dbgprefix("Debug"); + if (!do_debug_) + return; + va_list ap; + va_start(ap, fmt); + +#ifndef ANDROID + static const string& dbgcolor(isatty(fileno(stdout)) ? terminal_color_yellow : empty); + print_prefixed_message(std::cout, dbgcolor, dbgprefix, fmt, ap); +#else + __android_log_vprint(ANDROID_LOG_DEBUG, appname_.c_str(), fmt, ap); +#endif + + if (extra_out_) + print_prefixed_message(*extra_out_, empty, dbgprefix, fmt, ap); + + va_end(ap); +} + +void +Log::error(const char *fmt, ...) +{ + static const string errprefix("Error"); + va_list ap; + va_start(ap, fmt); + +#ifndef ANDROID + static const string& errcolor(isatty(fileno(stderr)) ? terminal_color_red : empty); + print_prefixed_message(std::cerr, errcolor, errprefix, fmt, ap); +#else + __android_log_vprint(ANDROID_LOG_ERROR, appname_.c_str(), fmt, ap); +#endif + + if (extra_out_) + print_prefixed_message(*extra_out_, empty, errprefix, fmt, ap); + + va_end(ap); +} + +void +Log::flush() +{ +#ifndef ANDROID + std::cout.flush(); + std::cerr.flush(); +#endif + if (extra_out_) + extra_out_->flush(); +} diff --git a/src/libmatrix/log.h b/src/libmatrix/log.h new file mode 100644 index 0000000..9054323 --- /dev/null +++ b/src/libmatrix/log.h @@ -0,0 +1,51 @@ +// +// Copyright (c) 2010-2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis <alexandros.frantzis@linaro.org> +// Jesse Barker <jesse.barker@linaro.org> +// +#ifndef LOG_H_ +#define LOG_H_ + +#include <string> +#include <iostream> + +class Log +{ +public: + static void init(const std::string& appname, bool do_debug = false, + std::ostream *extra_out = 0) + { + appname_ = appname; + do_debug_ = do_debug; + extra_out_ = extra_out; + } + // Emit an informational message + static void info(const char *fmt, ...); + // Emit a debugging message + static void debug(const char *fmt, ...); + // Emit an error message + static void error(const char *fmt, ...); + // Explicit flush of the log buffer + static void flush(); + // A prefix constant that informs the logging infrastructure that the log + // message is a continuation of a previous log message to be put on the + // same line. + static const std::string continuation_prefix; +private: + // A constant for identifying the log messages as originating from a + // particular application. + static std::string appname_; + // Indicates whether debug level messages should generate any output + static bool do_debug_; + // Extra stream to output log messages to + static std::ostream *extra_out_; +}; + +#endif /* LOG_H_ */ diff --git a/src/libmatrix/mat.cc b/src/libmatrix/mat.cc new file mode 100644 index 0000000..93ef63c --- /dev/null +++ b/src/libmatrix/mat.cc @@ -0,0 +1,173 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <math.h> +#include "mat.h" + +namespace LibMatrix +{ +namespace Mat4 +{ + +mat4 +translate(float x, float y, float z) +{ + mat4 t; + t[0][3] = x; + t[1][3] = y; + t[2][3] = z; + return t; +} + +mat4 +scale(float x, float y, float z) +{ + mat4 s; + s[0][0] = x; + s[1][1] = y; + s[2][2] = z; + return s; +} + +// +// As per the OpenGL "red book" definition of rotation, from the appendix +// on Homogeneous Coordinates and Transformation Matrices, the "upper left" +// 3x3 portion of the result matrix is formed by: +// +// M = uuT + (cos a)(I - uuT) + (sin a)S +// +// where u is the normalized input vector, uuT is the outer product of that +// vector and its transpose, I is the identity matrix and S is the matrix: +// +// | 0 -z' y' | +// | z' 0 -x' | +// | -y' x' 0 | +// +// where x', y' and z' are the elements of u +// +mat4 +rotate(float angle, float x, float y, float z) +{ + vec3 u(x, y, z); + u.normalize(); + mat3 uuT = outer(u, u); + mat3 s; + s[0][0] = 0; + s[0][1] = -u.z(); + s[0][2] = u.y(); + s[1][0] = u.z(); + s[1][1] = 0; + s[1][2] = -u.x(); + s[2][0] = -u.y(); + s[2][1] = u.x(); + s[2][2] = 0; + mat3 i; + i -= uuT; + // degrees to radians + float angleRadians(angle * M_PI / 180.0); + i *= cos(angleRadians); + s *= sin(angleRadians); + i += s; + mat3 m = uuT + i; + mat4 r; + r[0][0] = m[0][0]; + r[0][1] = m[0][1]; + r[0][2] = m[0][2]; + r[1][0] = m[1][0]; + r[1][1] = m[1][1]; + r[1][2] = m[1][2]; + r[2][0] = m[2][0]; + r[2][1] = m[2][1]; + r[2][2] = m[2][2]; + return r; +} + +mat4 +frustum(float left, float right, float bottom, float top, float near, float far) +{ + float twiceNear(2 * near); + float width(right - left); + float height(top - bottom); + float depth(far - near); + mat4 f; + f[0][0] = twiceNear / width; + f[0][2] = (right + left) / width; + f[1][1] = twiceNear / height; + f[1][2] = (top + bottom) / height; + f[2][2] = -(far + near) / depth; + f[2][3] = -(twiceNear * far) / depth; + f[3][2] = -1; + f[3][3] = 0; + return f; +} + +mat4 +ortho(float left, float right, float bottom, float top, float near, float far) +{ + float width(right - left); + float height(top - bottom); + float depth(far - near); + mat4 o; + o[0][0] = 2 / width; + o[0][3] = (right + left) / width; + o[1][1] = 2 / height; + o[1][3] = (top + bottom) / height; + o[2][2] = -2 / depth; + o[2][3] = (far + near) / depth; + return o; +} + +mat4 +perspective(float fovy, float aspect, float zNear, float zFar) +{ + // degrees to radians + float fovyRadians(fovy * M_PI / 180.0); + // cotangent(x) = 1/tan(x) + float f = 1/tan(fovyRadians / 2); + float depth(zNear - zFar); + mat4 p; + p[0][0] = f / aspect; + p[1][1] = f; + p[2][2] = (zFar + zNear) / depth; + p[2][3] = (2 * zFar * zNear) / depth; + p[3][2] = -1; + p[3][3] = 0; + return p; +} + +mat4 lookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) +{ + vec3 f(centerX - eyeX, centerY - eyeY, centerZ - eyeZ); + f.normalize(); + vec3 up(upX, upY, upZ); + vec3 s = vec3::cross(f, up); + vec3 u = vec3::cross(s, f); + s.normalize(); + u.normalize(); + mat4 la; + la[0][0] = s.x(); + la[0][1] = s.y(); + la[0][2] = s.z(); + la[1][0] = u.x(); + la[1][1] = u.y(); + la[1][2] = u.z(); + la[2][0] = -f.x(); + la[2][1] = -f.y(); + la[2][2] = -f.z(); + la *= translate(-eyeX, -eyeY, -eyeZ); + return la; +} + +} // namespace Mat4 + +} // namespace LibMatrix diff --git a/src/libmatrix/mat.h b/src/libmatrix/mat.h new file mode 100644 index 0000000..a55cd45 --- /dev/null +++ b/src/libmatrix/mat.h @@ -0,0 +1,1221 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef MAT_H_ +#define MAT_H_ +#include <stdexcept> +#include <iostream> +#include <iomanip> +#include "vec.h" +#ifndef USE_EXCEPTIONS +// If we're not throwing exceptions, we'll need the logger to make sure the +// caller is informed of errors. +#include "log.h" +#endif // USE_EXCEPTIONS + +namespace LibMatrix +{ +// Proxy class for providing the functionality of a doubly-dimensioned array +// representation of matrices. Each matrix class defines its operator[] +// to return an ArrayProxy. The ArrayProxy then returns the appropriate item +// from its operator[]. +template<typename T, unsigned int dimension> +class ArrayProxy +{ +public: + ArrayProxy(T* data) { data_ = data; } + ~ArrayProxy() { data_ = 0; } + T& operator[](int index) + { + return data_[index * dimension]; + } + const T& operator[](int index) const + { + return data_[index * dimension]; + } +private: + T* data_; +}; + + +// Programming interfaces to all matrix objects are represented row-centric +// (i.e. C/C++ style references to the data appear as matrix[row][column]). +// However, the internal data representation is column-major, so when using +// the raw data access member to treat the data as a singly-dimensioned array, +// it does not have to be transposed. +// +// A template class for creating, managing and operating on a 2x2 matrix +// of any type you like (intended for built-in types, but as long as it +// supports the basic arithmetic and assignment operators, any type should +// work). +template<typename T> +class tmat2 +{ +public: + tmat2() + { + setIdentity(); + } + tmat2(const tmat2& m) + { + m_[0] = m.m_[0]; + m_[1] = m.m_[1]; + m_[2] = m.m_[2]; + m_[3] = m.m_[3]; + } + tmat2(const T& c0r0, const T& c0r1, const T& c1r0, const T& c1r1) + { + m_[0] = c0r0; + m_[1] = c0r1; + m_[2] = c1r0; + m_[3] = c1r1; + } + ~tmat2() {} + + // Reset this to the identity matrix. + void setIdentity() + { + m_[0] = 1; + m_[1] = 0; + m_[2] = 0; + m_[3] = 1; + } + + // Transpose this. Return a reference to this. + tmat2& transpose() + { + T tmp_val = m_[1]; + m_[1] = m_[2]; + m_[2] = tmp_val; + return *this; + } + + // Compute the determinant of this and return it. + T determinant() + { + return (m_[0] * m_[3]) - (m_[2] * m_[1]); + } + + // Invert this. Return a reference to this. + // + // NOTE: If this is non-invertible, we will + // throw to avoid undefined behavior. + tmat2& inverse() +#ifdef USE_EXCEPTIONS + throw(std::runtime_error) +#endif // USE_EXCEPTIONS + { + T d(determinant()); + if (d == static_cast<T>(0)) + { +#ifdef USE_EXCEPTIONS + throw std::runtime_error("Matrix is noninvertible!!!!"); +#else // !USE_EXCEPTIONS + Log::error("Matrix is noninvertible!!!!\n"); + return *this; +#endif // USE_EXCEPTIONS + } + T c0r0(m_[3] / d); + T c0r1(-m_[1] / d); + T c1r0(-m_[2] / d); + T c1r1(m_[0] / d); + m_[0] = c0r0; + m_[1] = c0r1; + m_[2] = c1r0; + m_[3] = c1r1; + return *this; + } + + // Print the elements of the matrix to standard out. + // Really only useful for debug and test. + void print() const + { + static const int precision(6); + // row 0 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[0]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[2]; + std::cout << " |" << std::endl; + // row 1 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[1]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[3]; + std::cout << " |" << std::endl; + } + + // Allow raw data access for API calls and the like. + // For example, it is valid to pass a tmat2<float> into a call to + // the OpenGL command "glUniformMatrix2fv()". + operator const T*() const { return &m_[0];} + + // Test if 'rhs' is equal to this. + bool operator==(const tmat2& rhs) const + { + return m_[0] == rhs.m_[0] && + m_[1] == rhs.m_[1] && + m_[2] == rhs.m_[2] && + m_[3] == rhs.m_[3]; + } + + // Test if 'rhs' is not equal to this. + bool operator!=(const tmat2& rhs) const + { + return !(*this == rhs); + } + + // A direct assignment of 'rhs' to this. Return a reference to this. + tmat2& operator=(const tmat2& rhs) + { + if (this != &rhs) + { + m_[0] = rhs.m_[0]; + m_[1] = rhs.m_[1]; + m_[2] = rhs.m_[2]; + m_[3] = rhs.m_[3]; + } + return *this; + } + + // Add another matrix to this. Return a reference to this. + tmat2& operator+=(const tmat2& rhs) + { + m_[0] += rhs.m_[0]; + m_[1] += rhs.m_[1]; + m_[2] += rhs.m_[2]; + m_[3] += rhs.m_[3]; + return *this; + } + + // Add another matrix to a copy of this. Return the copy. + const tmat2 operator+(const tmat2& rhs) + { + return tmat2(*this) += rhs; + } + + // Subtract another matrix from this. Return a reference to this. + tmat2& operator-=(const tmat2& rhs) + { + m_[0] -= rhs.m_[0]; + m_[1] -= rhs.m_[1]; + m_[2] -= rhs.m_[2]; + m_[3] -= rhs.m_[3]; + return *this; + } + + // Subtract another matrix from a copy of this. Return the copy. + const tmat2 operator-(const tmat2& rhs) + { + return tmat2(*this) += rhs; + } + + // Multiply this by another matrix. Return a reference to this. + tmat2& operator*=(const tmat2& rhs) + { + T c0r0((m_[0] * rhs.m_[0]) + (m_[2] * rhs.m_[1])); + T c0r1((m_[1] * rhs.m_[0]) + (m_[3] * rhs.m_[1])); + T c1r0((m_[0] * rhs.m_[2]) + (m_[2] * rhs.m_[3])); + T c1r1((m_[1] * rhs.m_[2]) + (m_[3] * rhs.m_[3])); + m_[0] = c0r0; + m_[1] = c0r1; + m_[2] = c1r0; + m_[3] = c1r1; + return *this; + } + + // Multiply a copy of this by another matrix. Return the copy. + const tmat2 operator*(const tmat2& rhs) + { + return tmat2(*this) *= rhs; + } + + // Multiply this by a scalar. Return a reference to this. + tmat2& operator*=(const T& rhs) + { + m_[0] *= rhs; + m_[1] *= rhs; + m_[2] *= rhs; + m_[3] *= rhs; + return *this; + } + + // Multiply a copy of this by a scalar. Return the copy. + const tmat2 operator*(const T& rhs) + { + return tmat2(*this) *= rhs; + } + + // Divide this by a scalar. Return a reference to this. + tmat2& operator/=(const T& rhs) + { + m_[0] /= rhs; + m_[1] /= rhs; + m_[2] /= rhs; + m_[3] /= rhs; + return *this; + } + + // Divide a copy of this by a scalar. Return the copy. + const tmat2 operator/(const T& rhs) + { + return tmat2(*this) /= rhs; + } + + // Use an instance of the ArrayProxy class to support double-indexed + // references to a matrix (i.e., m[1][1]). See comments above the + // ArrayProxy definition for more details. + ArrayProxy<T, 2> operator[](int index) + { + return ArrayProxy<T, 2>(&m_[index]); + } + const ArrayProxy<T, 2> operator[](int index) const + { + return ArrayProxy<T, 2>(const_cast<T*>(&m_[index])); + } + +private: + T m_[4]; +}; + +// Multiply a scalar and a matrix just like the member operator, but allow +// the scalar to be the left-hand operand. +template<typename T> +const tmat2<T> operator*(const T& lhs, const tmat2<T>& rhs) +{ + return tmat2<T>(rhs) * lhs; +} + +// Multiply a copy of a vector and a matrix (matrix is right-hand operand). +// Return the copy. +template<typename T> +const tvec2<T> operator*(const tvec2<T>& lhs, const tmat2<T>& rhs) +{ + T x((lhs.x() * rhs[0][0]) + (lhs.y() * rhs[1][0])); + T y((lhs.x() * rhs[0][1]) + (lhs.y() * rhs[1][1])); + return tvec2<T>(x,y); +} + +// Multiply a copy of a vector and a matrix (matrix is left-hand operand). +// Return the copy. +template<typename T> +const tvec2<T> operator*(const tmat2<T>& lhs, const tvec2<T>& rhs) +{ + T x((lhs[0][0] * rhs.x()) + (lhs[0][1] * rhs.y())); + T y((lhs[1][0] * rhs.x()) + (lhs[1][1] * rhs.y())); + return tvec2<T>(x, y); +} + +// Compute the outer product of two vectors. Return the resultant matrix. +template<typename T> +const tmat2<T> outer(const tvec2<T>& a, const tvec2<T>& b) +{ + tmat2<T> product; + product[0][0] = a.x() * b.x(); + product[0][1] = a.x() * b.y(); + product[1][0] = a.y() * b.x(); + product[1][1] = a.y() * b.y(); + return product; +} + +// A template class for creating, managing and operating on a 3x3 matrix +// of any type you like (intended for built-in types, but as long as it +// supports the basic arithmetic and assignment operators, any type should +// work). +template<typename T> +class tmat3 +{ +public: + tmat3() + { + setIdentity(); + } + tmat3(const tmat3& m) + { + m_[0] = m.m_[0]; + m_[1] = m.m_[1]; + m_[2] = m.m_[2]; + m_[3] = m.m_[3]; + m_[4] = m.m_[4]; + m_[5] = m.m_[5]; + m_[6] = m.m_[6]; + m_[7] = m.m_[7]; + m_[8] = m.m_[8]; + } + tmat3(const T& c0r0, const T& c0r1, const T& c0r2, + const T& c1r0, const T& c1r1, const T& c1r2, + const T& c2r0, const T& c2r1, const T& c2r2) + { + m_[0] = c0r0; + m_[1] = c0r1; + m_[2] = c0r2; + m_[3] = c1r0; + m_[4] = c1r1; + m_[5] = c1r2; + m_[6] = c2r0; + m_[7] = c2r1; + m_[8] = c2r2; + } + ~tmat3() {} + + // Reset this to the identity matrix. + void setIdentity() + { + m_[0] = 1; + m_[1] = 0; + m_[2] = 0; + m_[3] = 0; + m_[4] = 1; + m_[5] = 0; + m_[6] = 0; + m_[7] = 0; + m_[8] = 1; + } + + // Transpose this. Return a reference to this. + tmat3& transpose() + { + T tmp_val = m_[1]; + m_[1] = m_[3]; + m_[3] = tmp_val; + tmp_val = m_[2]; + m_[2] = m_[6]; + m_[6] = tmp_val; + tmp_val = m_[5]; + m_[5] = m_[7]; + m_[7] = tmp_val; + return *this; + } + + // Compute the determinant of this and return it. + T determinant() + { + tmat2<T> minor0(m_[4], m_[5], m_[7], m_[8]); + tmat2<T> minor3(m_[1], m_[2], m_[7], m_[8]); + tmat2<T> minor6(m_[1], m_[2], m_[4], m_[5]); + return (m_[0] * minor0.determinant()) - + (m_[3] * minor3.determinant()) + + (m_[6] * minor6.determinant()); + } + + // Invert this. Return a reference to this. + // + // NOTE: If this is non-invertible, we will + // throw to avoid undefined behavior. + tmat3& inverse() +#ifdef USE_EXCEPTIONS + throw(std::runtime_error) +#endif // USE_EXCEPTIONS + { + T d(determinant()); + if (d == static_cast<T>(0)) + { +#ifdef USE_EXCEPTIONS + throw std::runtime_error("Matrix is noninvertible!!!!"); +#else // !USE_EXCEPTIONS + Log::error("Matrix is noninvertible!!!!\n"); + return *this; +#endif // USE_EXCEPTIONS + } + tmat2<T> minor0(m_[4], m_[5], m_[7], m_[8]); + tmat2<T> minor1(m_[7], m_[8], m_[1], m_[2]); + tmat2<T> minor2(m_[1], m_[2], m_[4], m_[5]); + tmat2<T> minor3(m_[6], m_[8], m_[3], m_[5]); + tmat2<T> minor4(m_[0], m_[2], m_[6], m_[8]); + tmat2<T> minor5(m_[3], m_[5], m_[0], m_[2]); + tmat2<T> minor6(m_[3], m_[4], m_[6], m_[7]); + tmat2<T> minor7(m_[6], m_[7], m_[0], m_[1]); + tmat2<T> minor8(m_[0], m_[1], m_[3], m_[4]); + m_[0] = minor0.determinant() / d; + m_[1] = minor1.determinant() / d; + m_[2] = minor2.determinant() / d; + m_[3] = minor3.determinant() / d; + m_[4] = minor4.determinant() / d; + m_[5] = minor5.determinant() / d; + m_[6] = minor6.determinant() / d; + m_[7] = minor7.determinant() / d; + m_[8] = minor8.determinant() / d; + return *this; + } + + // Print the elements of the matrix to standard out. + // Really only useful for debug and test. + void print() const + { + static const int precision(6); + // row 0 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[0]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[3]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[6]; + std::cout << " |" << std::endl; + // row 1 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[1]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[4]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[7]; + std::cout << " |" << std::endl; + // row 2 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[2]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[5]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[8]; + std::cout << " |" << std::endl; + } + + // Allow raw data access for API calls and the like. + // For example, it is valid to pass a tmat3<float> into a call to + // the OpenGL command "glUniformMatrix3fv()". + operator const T*() const { return &m_[0];} + + // Test if 'rhs' is equal to this. + bool operator==(const tmat3& rhs) const + { + return m_[0] == rhs.m_[0] && + m_[1] == rhs.m_[1] && + m_[2] == rhs.m_[2] && + m_[3] == rhs.m_[3] && + m_[4] == rhs.m_[4] && + m_[5] == rhs.m_[5] && + m_[6] == rhs.m_[6] && + m_[7] == rhs.m_[7] && + m_[8] == rhs.m_[8]; + } + + // Test if 'rhs' is not equal to this. + bool operator!=(const tmat3& rhs) const + { + return !(*this == rhs); + } + + // A direct assignment of 'rhs' to this. Return a reference to this. + tmat3& operator=(const tmat3& rhs) + { + if (this != &rhs) + { + m_[0] = rhs.m_[0]; + m_[1] = rhs.m_[1]; + m_[2] = rhs.m_[2]; + m_[3] = rhs.m_[3]; + m_[4] = rhs.m_[4]; + m_[5] = rhs.m_[5]; + m_[6] = rhs.m_[6]; + m_[7] = rhs.m_[7]; + m_[8] = rhs.m_[8]; + } + return *this; + } + + // Add another matrix to this. Return a reference to this. + tmat3& operator+=(const tmat3& rhs) + { + m_[0] += rhs.m_[0]; + m_[1] += rhs.m_[1]; + m_[2] += rhs.m_[2]; + m_[3] += rhs.m_[3]; + m_[4] += rhs.m_[4]; + m_[5] += rhs.m_[5]; + m_[6] += rhs.m_[6]; + m_[7] += rhs.m_[7]; + m_[8] += rhs.m_[8]; + return *this; + } + + // Add another matrix to a copy of this. Return the copy. + const tmat3 operator+(const tmat3& rhs) + { + return tmat3(*this) += rhs; + } + + // Subtract another matrix from this. Return a reference to this. + tmat3& operator-=(const tmat3& rhs) + { + m_[0] -= rhs.m_[0]; + m_[1] -= rhs.m_[1]; + m_[2] -= rhs.m_[2]; + m_[3] -= rhs.m_[3]; + m_[4] -= rhs.m_[4]; + m_[5] -= rhs.m_[5]; + m_[6] -= rhs.m_[6]; + m_[7] -= rhs.m_[7]; + m_[8] -= rhs.m_[8]; + return *this; + } + + // Subtract another matrix from a copy of this. Return the copy. + const tmat3 operator-(const tmat3& rhs) + { + return tmat3(*this) -= rhs; + } + + // Multiply this by another matrix. Return a reference to this. + tmat3& operator*=(const tmat3& rhs) + { + T c0r0((m_[0] * rhs.m_[0]) + (m_[3] * rhs.m_[1]) + (m_[6] * rhs.m_[2])); + T c0r1((m_[1] * rhs.m_[0]) + (m_[4] * rhs.m_[1]) + (m_[7] * rhs.m_[2])); + T c0r2((m_[2] * rhs.m_[0]) + (m_[5] * rhs.m_[1]) + (m_[8] * rhs.m_[2])); + T c1r0((m_[0] * rhs.m_[3]) + (m_[3] * rhs.m_[4]) + (m_[6] * rhs.m_[5])); + T c1r1((m_[1] * rhs.m_[3]) + (m_[4] * rhs.m_[4]) + (m_[7] * rhs.m_[5])); + T c1r2((m_[2] * rhs.m_[3]) + (m_[5] * rhs.m_[4]) + (m_[8] * rhs.m_[5])); + T c2r0((m_[0] * rhs.m_[6]) + (m_[3] * rhs.m_[7]) + (m_[6] * rhs.m_[8])); + T c2r1((m_[1] * rhs.m_[6]) + (m_[4] * rhs.m_[7]) + (m_[7] * rhs.m_[8])); + T c2r2((m_[2] * rhs.m_[6]) + (m_[5] * rhs.m_[7]) + (m_[8] * rhs.m_[8])); + m_[0] = c0r0; + m_[1] = c0r1; + m_[2] = c0r2; + m_[3] = c1r0; + m_[4] = c1r1; + m_[5] = c1r2; + m_[6] = c2r0; + m_[7] = c2r1; + m_[8] = c2r2; + return *this; + } + + // Multiply a copy of this by another matrix. Return the copy. + const tmat3 operator*(const tmat3& rhs) + { + return tmat3(*this) *= rhs; + } + + // Multiply this by a scalar. Return a reference to this. + tmat3& operator*=(const T& rhs) + { + m_[0] *= rhs; + m_[1] *= rhs; + m_[2] *= rhs; + m_[3] *= rhs; + m_[4] *= rhs; + m_[5] *= rhs; + m_[6] *= rhs; + m_[7] *= rhs; + m_[8] *= rhs; + return *this; + } + + // Multiply a copy of this by a scalar. Return the copy. + const tmat3 operator*(const T& rhs) + { + return tmat3(*this) *= rhs; + } + + // Divide this by a scalar. Return a reference to this. + tmat3& operator/=(const T& rhs) + { + m_[0] /= rhs; + m_[1] /= rhs; + m_[2] /= rhs; + m_[3] /= rhs; + m_[4] /= rhs; + m_[5] /= rhs; + m_[6] /= rhs; + m_[7] /= rhs; + m_[8] /= rhs; + return *this; + } + + // Divide a copy of this by a scalar. Return the copy. + const tmat3 operator/(const T& rhs) + { + return tmat3(*this) /= rhs; + } + + // Use an instance of the ArrayProxy class to support double-indexed + // references to a matrix (i.e., m[1][1]). See comments above the + // ArrayProxy definition for more details. + ArrayProxy<T, 3> operator[](int index) + { + return ArrayProxy<T, 3>(&m_[index]); + } + const ArrayProxy<T, 3> operator[](int index) const + { + return ArrayProxy<T, 3>(const_cast<T*>(&m_[index])); + } + +private: + T m_[9]; +}; + +// Multiply a scalar and a matrix just like the member operator, but allow +// the scalar to be the left-hand operand. +template<typename T> +const tmat3<T> operator*(const T& lhs, const tmat3<T>& rhs) +{ + return tmat3<T>(rhs) * lhs; +} + +// Multiply a copy of a vector and a matrix (matrix is right-hand operand). +// Return the copy. +template<typename T> +const tvec3<T> operator*(const tvec3<T>& lhs, const tmat3<T>& rhs) +{ + T x((lhs.x() * rhs[0][0]) + (lhs.y() * rhs[1][0]) + (lhs.z() * rhs[2][0])); + T y((lhs.x() * rhs[0][1]) + (lhs.y() * rhs[1][1]) + (lhs.z() * rhs[2][1])); + T z((lhs.x() * rhs[0][2]) + (lhs.y() * rhs[1][2]) + (lhs.z() * rhs[2][2])); + return tvec3<T>(x, y, z); +} + +// Multiply a copy of a vector and a matrix (matrix is left-hand operand). +// Return the copy. +template<typename T> +const tvec3<T> operator*(const tmat3<T>& lhs, const tvec3<T>& rhs) +{ + T x((lhs[0][0] * rhs.x()) + (lhs[0][1] * rhs.y()) + (lhs[0][2] * rhs.z())); + T y((lhs[1][0] * rhs.x()) + (lhs[1][1] * rhs.y()) + (lhs[1][2] * rhs.z())); + T z((lhs[2][0] * rhs.x()) + (lhs[2][1] * rhs.y()) + (lhs[2][2] * rhs.z())); + return tvec3<T>(x, y, z); +} + +// Compute the outer product of two vectors. Return the resultant matrix. +template<typename T> +const tmat3<T> outer(const tvec3<T>& a, const tvec3<T>& b) +{ + tmat3<T> product; + product[0][0] = a.x() * b.x(); + product[0][1] = a.x() * b.y(); + product[0][2] = a.x() * b.z(); + product[1][0] = a.y() * b.x(); + product[1][1] = a.y() * b.y(); + product[1][2] = a.y() * b.z(); + product[2][0] = a.z() * b.x(); + product[2][1] = a.z() * b.y(); + product[2][2] = a.z() * b.z(); + return product; +} + +// A template class for creating, managing and operating on a 4x4 matrix +// of any type you like (intended for built-in types, but as long as it +// supports the basic arithmetic and assignment operators, any type should +// work). +template<typename T> +class tmat4 +{ +public: + tmat4() + { + setIdentity(); + } + tmat4(const tmat4& m) + { + m_[0] = m.m_[0]; + m_[1] = m.m_[1]; + m_[2] = m.m_[2]; + m_[3] = m.m_[3]; + m_[4] = m.m_[4]; + m_[5] = m.m_[5]; + m_[6] = m.m_[6]; + m_[7] = m.m_[7]; + m_[8] = m.m_[8]; + m_[9] = m.m_[9]; + m_[10] = m.m_[10]; + m_[11] = m.m_[11]; + m_[12] = m.m_[12]; + m_[13] = m.m_[13]; + m_[14] = m.m_[14]; + m_[15] = m.m_[15]; + } + ~tmat4() {} + + // Reset this to the identity matrix. + void setIdentity() + { + m_[0] = 1; + m_[1] = 0; + m_[2] = 0; + m_[3] = 0; + m_[4] = 0; + m_[5] = 1; + m_[6] = 0; + m_[7] = 0; + m_[8] = 0; + m_[9] = 0; + m_[10] = 1; + m_[11] = 0; + m_[12] = 0; + m_[13] = 0; + m_[14] = 0; + m_[15] = 1; + } + + // Transpose this. Return a reference to this. + tmat4& transpose() + { + T tmp_val = m_[1]; + m_[1] = m_[4]; + m_[4] = tmp_val; + tmp_val = m_[2]; + m_[2] = m_[8]; + m_[8] = tmp_val; + tmp_val = m_[3]; + m_[3] = m_[12]; + m_[12] = tmp_val; + tmp_val = m_[6]; + m_[6] = m_[9]; + m_[9] = tmp_val; + tmp_val = m_[7]; + m_[7] = m_[13]; + m_[13] = tmp_val; + tmp_val = m_[11]; + m_[11] = m_[14]; + m_[14] = tmp_val; + return *this; + } + + // Compute the determinant of this and return it. + T determinant() + { + tmat3<T> minor0(m_[5], m_[6], m_[7], m_[9], m_[10], m_[11], m_[13], m_[14], m_[15]); + tmat3<T> minor4(m_[1], m_[2], m_[3], m_[9], m_[10], m_[11], m_[13], m_[14], m_[15]); + tmat3<T> minor8(m_[1], m_[2], m_[3], m_[5], m_[6], m_[7], m_[13], m_[14], m_[15]); + tmat3<T> minor12(m_[1], m_[2], m_[3], m_[5], m_[6], m_[7], m_[9], m_[10], m_[11]); + return (m_[0] * minor0.determinant()) - + (m_[4] * minor4.determinant()) + + (m_[8] * minor8.determinant()) - + (m_[12] * minor12.determinant()); + } + + // Invert this. Return a reference to this. + // + // NOTE: If this is non-invertible, we will + // throw to avoid undefined behavior. + tmat4& inverse() +#ifdef USE_EXCEPTIONS + throw(std::runtime_error) +#endif // USE_EXCEPTIONS + { + T d(determinant()); + if (d == static_cast<T>(0)) + { +#ifdef USE_EXCEPTIONS + throw std::runtime_error("Matrix is noninvertible!!!!"); +#else // !USE_EXCEPTIONS + Log::error("Matrix is noninvertible!!!!\n"); + return *this; +#endif // USE_EXCEPTIONS + } + tmat3<T> minor0(m_[5], m_[6], m_[7], m_[9], m_[10], m_[11], m_[13], m_[14], m_[15]); + tmat3<T> minor1(m_[1], m_[2], m_[3], m_[13], m_[14], m_[15], m_[9], m_[10], m_[11]); + tmat3<T> minor2(m_[1], m_[2], m_[3], m_[5], m_[6], m_[7], m_[13], m_[14], m_[15]); + tmat3<T> minor3(m_[1], m_[2], m_[3], m_[9], m_[10], m_[11], m_[5], m_[6], m_[7]); + + tmat3<T> minor4(m_[4], m_[6], m_[7], m_[12], m_[14], m_[15], m_[8], m_[10], m_[11]); + tmat3<T> minor5(m_[0], m_[2], m_[3], m_[8], m_[10], m_[11], m_[12], m_[14], m_[15]); + tmat3<T> minor6(m_[0], m_[2], m_[3], m_[12], m_[14], m_[15], m_[4], m_[6], m_[7]); + tmat3<T> minor7(m_[0], m_[2], m_[3], m_[4], m_[6], m_[7], m_[8], m_[10], m_[11]); + + tmat3<T> minor8(m_[4], m_[5], m_[7], m_[8], m_[9], m_[11], m_[12], m_[13], m_[15]); + tmat3<T> minor9(m_[0], m_[1], m_[3], m_[12], m_[13], m_[15], m_[8], m_[9], m_[11]); + tmat3<T> minor10(m_[0], m_[1], m_[3], m_[4], m_[5], m_[7], m_[12], m_[13], m_[15]); + tmat3<T> minor11(m_[0], m_[1], m_[3], m_[8], m_[9], m_[11], m_[4], m_[5], m_[7]); + + tmat3<T> minor12(m_[4], m_[5], m_[6], m_[12], m_[13], m_[14], m_[8], m_[9], m_[10]); + tmat3<T> minor13(m_[0], m_[1], m_[2], m_[8], m_[9], m_[10], m_[12], m_[13], m_[14]); + tmat3<T> minor14(m_[0], m_[1], m_[2], m_[12], m_[13], m_[14], m_[4], m_[5], m_[6]); + tmat3<T> minor15(m_[0], m_[1], m_[2], m_[4], m_[5], m_[6], m_[8], m_[9], m_[10]); + m_[0] = minor0.determinant() / d; + m_[1] = minor1.determinant() / d; + m_[2] = minor2.determinant() / d; + m_[3] = minor3.determinant() / d; + m_[4] = minor4.determinant() / d; + m_[5] = minor5.determinant() / d; + m_[6] = minor6.determinant() / d; + m_[7] = minor7.determinant() / d; + m_[8] = minor8.determinant() / d; + m_[9] = minor9.determinant() / d; + m_[10] = minor10.determinant() / d; + m_[11] = minor11.determinant() / d; + m_[12] = minor12.determinant() / d; + m_[13] = minor13.determinant() / d; + m_[14] = minor14.determinant() / d; + m_[15] = minor15.determinant() / d; + return *this; + } + + // Print the elements of the matrix to standard out. + // Really only useful for debug and test. + void print() const + { + static const int precision(6); + // row 0 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[0]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[4]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[8]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[12]; + std::cout << " |" << std::endl; + // row 1 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[1]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[5]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[9]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[13]; + std::cout << " |" << std::endl; + // row 2 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[2]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[6]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[10]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[14]; + std::cout << " |" << std::endl; + // row 3 + std::cout << "| "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[3]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[7]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[11]; + std::cout << " "; + std::cout << std::fixed << std::showpoint << std::setprecision(precision) << m_[15]; + std::cout << " |" << std::endl; + } + + // Allow raw data access for API calls and the like. + // For example, it is valid to pass a tmat4<float> into a call to + // the OpenGL command "glUniformMatrix4fv()". + operator const T*() const { return &m_[0];} + + // Test if 'rhs' is equal to this. + bool operator==(const tmat4& rhs) const + { + return m_[0] == rhs.m_[0] && + m_[1] == rhs.m_[1] && + m_[2] == rhs.m_[2] && + m_[3] == rhs.m_[3] && + m_[4] == rhs.m_[4] && + m_[5] == rhs.m_[5] && + m_[6] == rhs.m_[6] && + m_[7] == rhs.m_[7] && + m_[8] == rhs.m_[8] && + m_[9] == rhs.m_[9] && + m_[10] == rhs.m_[10] && + m_[11] == rhs.m_[11] && + m_[12] == rhs.m_[12] && + m_[13] == rhs.m_[13] && + m_[14] == rhs.m_[14] && + m_[15] == rhs.m_[15]; + } + + // Test if 'rhs' is not equal to this. + bool operator!=(const tmat4& rhs) const + { + return !(*this == rhs); + } + + // A direct assignment of 'rhs' to this. Return a reference to this. + tmat4& operator=(const tmat4& rhs) + { + if (this != &rhs) + { + m_[0] = rhs.m_[0]; + m_[1] = rhs.m_[1]; + m_[2] = rhs.m_[2]; + m_[3] = rhs.m_[3]; + m_[4] = rhs.m_[4]; + m_[5] = rhs.m_[5]; + m_[6] = rhs.m_[6]; + m_[7] = rhs.m_[7]; + m_[8] = rhs.m_[8]; + m_[9] = rhs.m_[9]; + m_[10] = rhs.m_[10]; + m_[11] = rhs.m_[11]; + m_[12] = rhs.m_[12]; + m_[13] = rhs.m_[13]; + m_[14] = rhs.m_[14]; + m_[15] = rhs.m_[15]; + } + return *this; + } + + // Add another matrix to this. Return a reference to this. + tmat4& operator+=(const tmat4& rhs) + { + m_[0] += rhs.m_[0]; + m_[1] += rhs.m_[1]; + m_[2] += rhs.m_[2]; + m_[3] += rhs.m_[3]; + m_[4] += rhs.m_[4]; + m_[5] += rhs.m_[5]; + m_[6] += rhs.m_[6]; + m_[7] += rhs.m_[7]; + m_[8] += rhs.m_[8]; + m_[9] += rhs.m_[9]; + m_[10] += rhs.m_[10]; + m_[11] += rhs.m_[11]; + m_[12] += rhs.m_[12]; + m_[13] += rhs.m_[13]; + m_[14] += rhs.m_[14]; + m_[15] += rhs.m_[15]; + return *this; + } + + // Add another matrix to a copy of this. Return the copy. + const tmat4 operator+(const tmat4& rhs) + { + return tmat4(*this) += rhs; + } + + // Subtract another matrix from this. Return a reference to this. + tmat4& operator-=(const tmat4& rhs) + { + m_[0] -= rhs.m_[0]; + m_[1] -= rhs.m_[1]; + m_[2] -= rhs.m_[2]; + m_[3] -= rhs.m_[3]; + m_[4] -= rhs.m_[4]; + m_[5] -= rhs.m_[5]; + m_[6] -= rhs.m_[6]; + m_[7] -= rhs.m_[7]; + m_[8] -= rhs.m_[8]; + m_[9] -= rhs.m_[9]; + m_[10] -= rhs.m_[10]; + m_[11] -= rhs.m_[11]; + m_[12] -= rhs.m_[12]; + m_[13] -= rhs.m_[13]; + m_[14] -= rhs.m_[14]; + m_[15] -= rhs.m_[15]; + return *this; + } + + // Subtract another matrix from a copy of this. Return the copy. + const tmat4 operator-(const tmat4& rhs) + { + return tmat4(*this) -= rhs; + } + + // Multiply this by another matrix. Return a reference to this. + tmat4& operator*=(const tmat4& rhs) + { + T c0r0((m_[0] * rhs.m_[0]) + (m_[4] * rhs.m_[1]) + (m_[8] * rhs.m_[2]) + (m_[12] * rhs.m_[3])); + T c0r1((m_[1] * rhs.m_[0]) + (m_[5] * rhs.m_[1]) + (m_[9] * rhs.m_[2]) + (m_[13] * rhs.m_[3])); + T c0r2((m_[2] * rhs.m_[0]) + (m_[6] * rhs.m_[1]) + (m_[10] * rhs.m_[2]) + (m_[14] * rhs.m_[3])); + T c0r3((m_[3] * rhs.m_[0]) + (m_[7] * rhs.m_[1]) + (m_[11] * rhs.m_[2]) + (m_[15] * rhs.m_[3])); + T c1r0((m_[0] * rhs.m_[4]) + (m_[4] * rhs.m_[5]) + (m_[8] * rhs.m_[6]) + (m_[12] * rhs.m_[7])); + T c1r1((m_[1] * rhs.m_[4]) + (m_[5] * rhs.m_[5]) + (m_[9] * rhs.m_[6]) + (m_[13] * rhs.m_[7])); + T c1r2((m_[2] * rhs.m_[4]) + (m_[6] * rhs.m_[5]) + (m_[10] * rhs.m_[6]) + (m_[14] * rhs.m_[7])); + T c1r3((m_[3] * rhs.m_[4]) + (m_[7] * rhs.m_[5]) + (m_[11] * rhs.m_[6]) + (m_[15] * rhs.m_[7])); + T c2r0((m_[0] * rhs.m_[8]) + (m_[4] * rhs.m_[9]) + (m_[8] * rhs.m_[10]) + (m_[12] * rhs.m_[11])); + T c2r1((m_[1] * rhs.m_[8]) + (m_[5] * rhs.m_[9]) + (m_[9] * rhs.m_[10]) + (m_[13] * rhs.m_[11])); + T c2r2((m_[2] * rhs.m_[8]) + (m_[6] * rhs.m_[9]) + (m_[10] * rhs.m_[10]) + (m_[14] * rhs.m_[11])); + T c2r3((m_[3] * rhs.m_[8]) + (m_[7] * rhs.m_[9]) + (m_[11] * rhs.m_[10]) + (m_[15] * rhs.m_[11])); + T c3r0((m_[0] * rhs.m_[12]) + (m_[4] * rhs.m_[13]) + (m_[8] * rhs.m_[14]) + (m_[12] * rhs.m_[15])); + T c3r1((m_[1] * rhs.m_[12]) + (m_[5] * rhs.m_[13]) + (m_[9] * rhs.m_[14]) + (m_[13] * rhs.m_[15])); + T c3r2((m_[2] * rhs.m_[12]) + (m_[6] * rhs.m_[13]) + (m_[10] * rhs.m_[14]) + (m_[14] * rhs.m_[15])); + T c3r3((m_[3] * rhs.m_[12]) + (m_[7] * rhs.m_[13]) + (m_[11] * rhs.m_[14]) + (m_[15] * rhs.m_[15])); + m_[0] = c0r0; + m_[1] = c0r1; + m_[2] = c0r2; + m_[3] = c0r3; + m_[4] = c1r0; + m_[5] = c1r1; + m_[6] = c1r2; + m_[7] = c1r3; + m_[8] = c2r0; + m_[9] = c2r1; + m_[10] = c2r2; + m_[11] = c2r3; + m_[12] = c3r0; + m_[13] = c3r1; + m_[14] = c3r2; + m_[15] = c3r3; + return *this; + } + + // Multiply a copy of this by another matrix. Return the copy. + const tmat4 operator*(const tmat4& rhs) + { + return tmat4(*this) *= rhs; + } + + // Multiply this by a scalar. Return a reference to this. + tmat4& operator*=(const T& rhs) + { + m_[0] *= rhs; + m_[1] *= rhs; + m_[2] *= rhs; + m_[3] *= rhs; + m_[4] *= rhs; + m_[5] *= rhs; + m_[6] *= rhs; + m_[7] *= rhs; + m_[8] *= rhs; + m_[9] *= rhs; + m_[10] *= rhs; + m_[11] *= rhs; + m_[12] *= rhs; + m_[13] *= rhs; + m_[14] *= rhs; + m_[15] *= rhs; + return *this; + } + + // Multiply a copy of this by a scalar. Return the copy. + const tmat4 operator*(const T& rhs) + { + return tmat4(*this) *= rhs; + } + + // Divide this by a scalar. Return a reference to this. + tmat4& operator/=(const T& rhs) + { + m_[0] /= rhs; + m_[1] /= rhs; + m_[2] /= rhs; + m_[3] /= rhs; + m_[4] /= rhs; + m_[5] /= rhs; + m_[6] /= rhs; + m_[7] /= rhs; + m_[8] /= rhs; + m_[9] /= rhs; + m_[10] /= rhs; + m_[11] /= rhs; + m_[12] /= rhs; + m_[13] /= rhs; + m_[14] /= rhs; + m_[15] /= rhs; + return *this; + } + + // Divide a copy of this by a scalar. Return the copy. + const tmat4 operator/(const T& rhs) + { + return tmat4(*this) /= rhs; + } + + // Use an instance of the ArrayProxy class to support double-indexed + // references to a matrix (i.e., m[1][1]). See comments above the + // ArrayProxy definition for more details. + ArrayProxy<T, 4> operator[](int index) + { + return ArrayProxy<T, 4>(&m_[index]); + } + const ArrayProxy<T, 4> operator[](int index) const + { + return ArrayProxy<T, 4>(const_cast<T*>(&m_[index])); + } + +private: + T m_[16]; +}; + +// Multiply a scalar and a matrix just like the member operator, but allow +// the scalar to be the left-hand operand. +template<typename T> +const tmat4<T> operator*(const T& lhs, const tmat4<T>& rhs) +{ + return tmat4<T>(rhs) * lhs; +} + +// Multiply a copy of a vector and a matrix (matrix is right-hand operand). +// Return the copy. +template<typename T> +const tvec4<T> operator*(const tvec4<T>& lhs, const tmat4<T>& rhs) +{ + T x((lhs.x() * rhs[0][0]) + (lhs.y() * rhs[1][0]) + (lhs.z() * rhs[2][0]) + (lhs.w() * rhs[3][0])); + T y((lhs.x() * rhs[0][1]) + (lhs.y() * rhs[1][1]) + (lhs.z() * rhs[2][1]) + (lhs.w() * rhs[3][1])); + T z((lhs.x() * rhs[0][2]) + (lhs.y() * rhs[1][2]) + (lhs.z() * rhs[2][2]) + (lhs.w() * rhs[3][2])); + T w((lhs.x() * rhs[0][3]) + (lhs.y() * rhs[1][3]) + (lhs.z() * rhs[2][3]) + (lhs.w() * rhs[3][3])); + return tvec4<T>(x, y, z, w); +} + +// Multiply a copy of a vector and a matrix (matrix is left-hand operand). +// Return the copy. +template<typename T> +const tvec4<T> operator*(const tmat4<T>& lhs, const tvec4<T>& rhs) +{ + T x((lhs[0][0] * rhs.x()) + (lhs[0][1] * rhs.y()) + (lhs[0][2] * rhs.z()) + (lhs[0][3] * rhs.w())); + T y((lhs[1][0] * rhs.x()) + (lhs[1][1] * rhs.y()) + (lhs[1][2] * rhs.z()) + (lhs[1][3] * rhs.w())); + T z((lhs[2][0] * rhs.x()) + (lhs[2][1] * rhs.y()) + (lhs[2][2] * rhs.z()) + (lhs[2][3] * rhs.w())); + T w((lhs[3][0] * rhs.x()) + (lhs[3][1] * rhs.y()) + (lhs[3][2] * rhs.z()) + (lhs[3][3] * rhs.w())); + return tvec4<T>(x, y, z, w); +} + +// Compute the outer product of two vectors. Return the resultant matrix. +template<typename T> +const tmat4<T> outer(const tvec4<T>& a, const tvec4<T>& b) +{ + tmat4<T> product; + product[0][0] = a.x() * b.x(); + product[0][1] = a.x() * b.y(); + product[0][2] = a.x() * b.z(); + product[0][3] = a.x() * b.w(); + product[1][0] = a.y() * b.x(); + product[1][1] = a.y() * b.y(); + product[1][2] = a.y() * b.z(); + product[1][3] = a.y() * b.w(); + product[2][0] = a.z() * b.x(); + product[2][1] = a.z() * b.y(); + product[2][2] = a.z() * b.z(); + product[2][3] = a.z() * b.w(); + product[3][0] = a.w() * b.x(); + product[3][1] = a.w() * b.y(); + product[3][2] = a.w() * b.z(); + product[3][3] = a.w() * b.w(); + return product; +} + +// +// Convenience typedefs. These are here to present a homogeneous view of these +// objects with respect to shader source. +// +typedef tmat2<float> mat2; +typedef tmat3<float> mat3; +typedef tmat4<float> mat4; + +typedef tmat2<double> dmat2; +typedef tmat3<double> dmat3; +typedef tmat4<double> dmat4; + +typedef tmat2<int> imat2; +typedef tmat3<int> imat3; +typedef tmat4<int> imat4; + +typedef tmat2<unsigned int> umat2; +typedef tmat3<unsigned int> umat3; +typedef tmat4<unsigned int> umat4; + +typedef tmat2<bool> bmat2; +typedef tmat3<bool> bmat3; +typedef tmat4<bool> bmat4; + +namespace Mat4 +{ + +// +// Some functions to generate transformation matrices that used to be provided +// by OpenGL. +// +mat4 translate(float x, float y, float z); +mat4 scale(float x, float y, float z); +mat4 rotate(float angle, float x, float y, float z); +mat4 frustum(float left, float right, float bottom, float top, float near, float far); +mat4 ortho(float left, float right, float bottom, float top, float near, float far); +mat4 perspective(float fovy, float aspect, float zNear, float zFar); +mat4 lookAt(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ); + +} // namespace Mat4 +} // namespace LibMatrix +#endif // MAT_H_ diff --git a/src/libmatrix/program.cc b/src/libmatrix/program.cc new file mode 100644 index 0000000..b27298b --- /dev/null +++ b/src/libmatrix/program.cc @@ -0,0 +1,360 @@ +// +// Copyright (c) 2011-2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <string> +#include <vector> +#include <sstream> +#include <fstream> +#include <iostream> +#include "gl-if.h" +#include "program.h" + +using std::string; +using LibMatrix::mat4; +using LibMatrix::mat3; +using LibMatrix::vec2; +using LibMatrix::vec3; +using LibMatrix::vec4; + +Shader::Shader(unsigned int type, const string& source) : + handle_(0), + type_(type), + source_(source), + ready_(false), + valid_(false) +{ + // Create our shader and setup the source code. + handle_ = glCreateShader(type); + if (!handle_) + { + message_ = string("Failed to create the new shader."); + return; + } + const GLchar* shaderSource = source_.c_str(); + glShaderSource(handle_, 1, &shaderSource, NULL); + GLint param = 0; + glGetShaderiv(handle_, GL_SHADER_SOURCE_LENGTH, ¶m); + if (static_cast<unsigned int>(param) != source_.length() + 1) + { + std::ostringstream o(string("Expected shader source length ")); + o << source_.length() << ", but got " << param << std::endl; + message_ = o.str(); + return; + } + valid_ = true; +} + +Shader::~Shader() +{ + handle_ = 0; + type_ = 0; + ready_ = false; + valid_ = false; +} + +void +Shader::compile() +{ + // Make sure we have a good shader and haven't already compiled it. + if (!valid_ || ready_) + { + return; + } + glCompileShader(handle_); + GLint param = 0; + glGetShaderiv(handle_, GL_COMPILE_STATUS, ¶m); + if (param == GL_FALSE) + { + glGetShaderiv(handle_, GL_INFO_LOG_LENGTH, ¶m); + GLchar* infoLog = new GLchar[param + 1]; + glGetShaderInfoLog(handle_, param + 1, NULL, infoLog); + message_ = infoLog; + delete [] infoLog; + return; + } + ready_ = true; +} + +void +Shader::attach(unsigned int program) +{ + // Shader must be valid and compiled to be attached to a program. + if (!valid_ || !ready_) + { + return; + } + glAttachShader(program, handle_); +} + +void +Shader::release() +{ + if (handle_) + { + glDeleteShader(handle_); + } + handle_ = 0; + type_ = 0; + ready_ = false; + valid_ = false; +} + +Program::Program() : + handle_(0), + ready_(false), + valid_(false) +{ +} + +Program::~Program() +{ + // First release all of the shader resources attached to us and clean up + // our handle. + release(); +} + +void +Program::init() +{ + handle_ = glCreateProgram(); + if (!handle_) + { + message_ = string("Failed to create the new program"); + return; + } + + valid_ = true; +} + +void +Program::release() +{ + // First delete all of the shader resources attached to us. + for (std::vector<Shader>::iterator shaderIt = shaders_.begin(); shaderIt != shaders_.end(); shaderIt++) + { + shaderIt->release(); + } + + // Clear out the shader vector so we're ready to reuse it. + shaders_.clear(); + + // Clear out the error string to make sure we don't return anything stale. + message_.clear(); + + // Release all of the symbol map resources. + for (std::map<string, Symbol*>::iterator symbolIt = symbols_.begin(); symbolIt != symbols_.end(); symbolIt++) + { + delete (*symbolIt).second; + } + symbols_.clear(); + + if (handle_) + { + glDeleteProgram(handle_); + } + handle_ = 0; + ready_ = false; + valid_ = false; +} +void +Program::addShader(unsigned int type, const string& source) +{ + if (!valid_) + { + return; + } + + Shader shader(type, source); + if (!shader.valid()) + { + message_ = shader.errorMessage(); + valid_ = false; + return; + } + + shader.compile(); + + if (!shader.ready()) + { + message_ = shader.errorMessage(); + valid_ = false; + return; + } + + shader.attach(handle_); + shaders_.push_back(shader); + return; +} + +void +Program::build() +{ + if (!valid_ || ready_) + { + return; + } + + if (shaders_.empty()) + { + message_ = string("There are no shaders attached to this program"); + return; + } + + glLinkProgram(handle_); + GLint param = 1; + glGetProgramiv(handle_, GL_LINK_STATUS, ¶m); + if (param == GL_FALSE) + { + glGetProgramiv(handle_, GL_INFO_LOG_LENGTH, ¶m); + GLchar* infoLog = new GLchar[param + 1]; + glGetProgramInfoLog(handle_, param + 1, NULL, infoLog); + message_ = infoLog; + delete [] infoLog; + return; + } + ready_ = true; +} + +void +Program::start() +{ + if (!valid_ || !ready_) + { + return; + } + glUseProgram(handle_); +} + +void +Program::stop() +{ + glUseProgram(0); +} + + +int +Program::getUniformLocation(const string& name) +{ + GLint location = glGetUniformLocation(handle_, name.c_str()); + if (location < 0) + { + message_ = string("Failed to get uniform location for \"") + name + + string("\""); + } + return location; +} + +int +Program::getAttribIndex(const string& name) +{ + GLint index = glGetAttribLocation(handle_, name.c_str()); + if (index < 0) + { + message_ = string("Failed to get attribute location for \"") + name + + string("\""); + } + return index; +} + +Program::Symbol& +Program::Symbol::operator=(const mat4& m) +{ + if (type_ == Uniform) + { + // Our matrix representation is column-major, so transpose is false here. + glUniformMatrix4fv(location_, 1, GL_FALSE, m); + } + return *this; +} + +Program::Symbol& +Program::Symbol::operator=(const mat3& m) +{ + if (type_ == Uniform) + { + // Our matrix representation is column-major, so transpose is false here. + glUniformMatrix3fv(location_, 1, GL_FALSE, m); + } + return *this; +} + +Program::Symbol& +Program::Symbol::operator=(const vec2& v) +{ + if (type_ == Uniform) + { + glUniform2fv(location_, 1, v); + } + return *this; +} + +Program::Symbol& +Program::Symbol::operator=(const vec3& v) +{ + if (type_ == Uniform) + { + glUniform3fv(location_, 1, v); + } + return *this; +} + +Program::Symbol& +Program::Symbol::operator=(const vec4& v) +{ + if (type_ == Uniform) + { + glUniform4fv(location_, 1, v); + } + return *this; +} + +Program::Symbol& +Program::Symbol::operator=(const float& f) +{ + if (type_ == Uniform) + { + glUniform1f(location_, f); + } + return *this; +} + +Program::Symbol& +Program::Symbol::operator=(const int& i) +{ + if (type_ == Uniform) + { + glUniform1i(location_, i); + } + return *this; +} + +Program::Symbol& +Program::operator[](const std::string& name) +{ + std::map<std::string, Symbol*>::iterator mapIt = symbols_.find(name); + if (mapIt == symbols_.end()) + { + Program::Symbol::SymbolType type(Program::Symbol::Attribute); + int location = getAttribIndex(name); + if (location < 0) + { + // No attribute found by that name. Let's try a uniform... + type = Program::Symbol::Uniform; + location = getUniformLocation(name); + if (location < 0) + { + type = Program::Symbol::None; + } + } + mapIt = symbols_.insert(mapIt, std::make_pair(name, new Symbol(name, location, type))); + } + return *(*mapIt).second; +} diff --git a/src/libmatrix/program.h b/src/libmatrix/program.h new file mode 100644 index 0000000..b7ba3df --- /dev/null +++ b/src/libmatrix/program.h @@ -0,0 +1,165 @@ +// +// Copyright (c) 2011-2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef PROGRAM_H_ +#define PROGRAM_H_ + +#include <string> +#include <vector> +#include <map> +#include "mat.h" + +// Simple shader container. Abstracts all of the OpenGL bits, but leaves +// much of the semantics intact. This is typically only referenced directly +// by the program object. +class Shader +{ +public: + Shader() : + handle_(0), + type_(0), + ready_(false), + valid_(false) {} + Shader(const Shader& shader) : + handle_(shader.handle_), + type_(shader.type_), + source_(shader.source_), + message_(shader.message_), + ready_(shader.ready_), + valid_(shader.valid_) {} + Shader(unsigned int type, const std::string& source); + ~Shader(); + + // Compiles the shader source so that it can be linked into a + // program. + // + // Make sure the shader is "valid" before calling this one. + void compile(); + + // Attaches a compiled shader to a program in preparation for + // linking. + // + // Make sure the shader is "ready" before calling this one. + void attach(unsigned int program); + + // Release any resources associated with this shader back to + // OpenGL + void release(); + + // If "valid" then the shader has successfully been created. + // If "ready" then the shader has successfully been compiled. + // If either is false, then additional information can be obtained + // from the error message. + bool valid() const { return valid_; } + bool ready() const { return ready_; } + const std::string& errorMessage() const { return message_; } + +private: + unsigned int handle_; + unsigned int type_; + std::string source_; + std::string message_; + bool ready_; + bool valid_; +}; + +// Simple program container. Abstracts all of the OpenGL bits, but leaves +// much of the semantics intact. +class Program +{ +public: + Program(); + ~Program(); + + // Initialize the program object for use. + void init(); + + // Release any resources associated with this program back to + // OpenGL + void release(); + + // Create a new shader of the given type and source, compile it and + // attach it to the program. + // + // Make sure the program is "valid" before calling this one. + void addShader(unsigned int type, const std::string& source); + + // Link all of the attached shaders into a runnable program for use + // in a rendering operation. + // + // Make sure the program is "valid" and that at least one shader + // has been successfully added before calling this one. + void build(); + + // Bind the program for use by the rendering context (i.e. actually + // run it). + // + // Make sure the program is "ready" before calling this one. + void start(); + + // Unbind the program from use by the rendering context (i.e. stop + // using it). + void stop(); + + class Symbol + { +public: + enum SymbolType + { + None, + Attribute, + Uniform + }; + Symbol(const std::string& name, int location, SymbolType type) : + type_(type), + location_(location), + name_(name) {} + int location() const { return location_; } + // These members cause data to be bound to program variables, so + // the program must be bound for use for these to be effective. + Symbol& operator=(const LibMatrix::mat4& m); + Symbol& operator=(const LibMatrix::mat3& m); + Symbol& operator=(const LibMatrix::vec2& v); + Symbol& operator=(const LibMatrix::vec3& v); + Symbol& operator=(const LibMatrix::vec4& v); + Symbol& operator=(const float& f); + Symbol& operator=(const int& i); +private: + Symbol(); + SymbolType type_; + GLint location_; + std::string name_; + }; + // Get the handle to a named program input (the location in OpenGL + // vernacular). Typically used in conjunction with various VertexAttrib + // interfaces. Equality operators are used to load uniform data. + Symbol& operator[](const std::string& name); + + // If "valid" then the program has successfully been created. + // If "ready" then the program has successfully been built. + // If either is false, then additional information can be obtained + // from the error message. + bool valid() const { return valid_; } + bool ready() const { return ready_; } + const std::string& errorMessage() const { return message_; } + +private: + int getAttribIndex(const std::string& name); + int getUniformLocation(const std::string& name); + unsigned int handle_; + std::map<std::string, Symbol*> symbols_; + std::vector<Shader> shaders_; + std::string message_; + bool ready_; + bool valid_; +}; + +#endif // PROGRAM_H_ diff --git a/src/libmatrix/shader-source.cc b/src/libmatrix/shader-source.cc new file mode 100644 index 0000000..bf80d21 --- /dev/null +++ b/src/libmatrix/shader-source.cc @@ -0,0 +1,615 @@ +// +// Copyright (c) 2010-2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis <alexandros.frantzis@linaro.org> +// Jesse Barker <jesse.barker@linaro.org> +// +#include <istream> +#include <memory> + +#include "shader-source.h" +#include "log.h" +#include "vec.h" +#include "util.h" + +/** + * Holds default precision values for all shader types + * (even the unknown type, which is hardwired to default precision values) + */ +std::vector<ShaderSource::Precision> +ShaderSource::default_precision_(ShaderSource::ShaderTypeUnknown + 1); + +/** + * Loads the contents of a file into a string. + * + * @param filename the name of the file + * @param str the string to put the contents of the file into + */ +bool +ShaderSource::load_file(const std::string& filename, std::string& str) +{ + std::auto_ptr<std::istream> is_ptr(Util::get_resource(filename)); + std::istream& inputFile(*is_ptr); + + if (!inputFile) + { + Log::error("Failed to open \"%s\"\n", filename.c_str()); + return false; + } + + std::string curLine; + while (getline(inputFile, curLine)) + { + str += curLine; + str += '\n'; + } + + return true; +} + + +/** + * Appends a string to the shader source. + * + * @param str the string to append + */ +void +ShaderSource::append(const std::string &str) +{ + source_ << str; +} + +/** + * Appends the contents of a file to the shader source. + * + * @param filename the name of the file to append + */ +void +ShaderSource::append_file(const std::string &filename) +{ + std::string source; + if (load_file(filename, source)) + source_ << source; +} + +/** + * Replaces a string in the source with another string. + * + * @param remove the string to replace + * @param insert the string to replace with + */ +void +ShaderSource::replace(const std::string &remove, const std::string &insert) +{ + std::string::size_type pos = 0; + std::string str(source_.str()); + + while ((pos = str.find(remove, pos)) != std::string::npos) { + str.replace(pos, remove.size(), insert); + pos++; + } + + source_.clear(); + source_.str(str); +} + +/** + * Replaces a string in the source with the contents of a file. + * + * @param remove the string to replace + * @param filename the name of the file to read from + */ +void +ShaderSource::replace_with_file(const std::string &remove, const std::string &filename) +{ + std::string source; + if (load_file(filename, source)) + replace(remove, source); +} + +/** + * Adds a string (usually containing a constant definition) at + * global (per shader) scope. + * + * The string is placed after any default precision qualifiers. + * + * @param str the string to add + */ +void +ShaderSource::add_global(const std::string &str) +{ + std::string::size_type pos = 0; + std::string source(source_.str()); + + /* Find the last precision qualifier */ + pos = source.rfind("precision"); + + if (pos != std::string::npos) { + /* + * Find the next #endif line of a preprocessor block that contains + * the precision qualifier. + */ + std::string::size_type pos_if = source.find("#if", pos); + std::string::size_type pos_endif = source.find("#endif", pos); + + if (pos_endif != std::string::npos && pos_endif < pos_if) + pos = pos_endif; + + /* Go to the next line */ + pos = source.find("\n", pos); + if (pos != std::string::npos) + pos++; + } + else + pos = 0; + + source.insert(pos, str); + + source_.clear(); + source_.str(source); +} + +/** + * Adds a string (usually containing a constant definition) at + * global (per shader) scope. + * + * The string is placed after any default precision qualifiers. + * + * @param function the function to add the string into + * @param str the string to add + */ +void +ShaderSource::add_local(const std::string &str, const std::string &function) +{ + std::string::size_type pos = 0; + std::string source(source_.str()); + + /* Find the function */ + pos = source.find(function); + pos = source.find('{', pos); + + /* Go to the next line */ + pos = source.find("\n", pos); + if (pos != std::string::npos) + pos++; + + source.insert(pos, str); + + source_.clear(); + source_.str(source); +} + +/** + * Adds a string (usually containing a constant definition) to a shader source + * + * If the function parameter is empty, the string will be added to global + * scope, after any precision definitions. + * + * @param str the string to add + * @param function if not empty, the function to add the string into + */ +void +ShaderSource::add(const std::string &str, const std::string &function) +{ + if (!function.empty()) + add_local(str, function); + else + add_global(str); +} + +/** + * Adds a float constant definition. + * + * @param name the name of the constant + * @param f the value of the constant + * @param function if not empty, the function to put the definition in + */ +void +ShaderSource::add_const(const std::string &name, float f, + const std::string &function) +{ + std::stringstream ss; + + ss << "const float " << name << " = " << std::fixed << f << ";" << std::endl; + + add(ss.str(), function); +} + +/** + * Adds a float array constant definition. + * + * Note that various GLSL versions (including ES) don't support + * array constants. + * + * @param name the name of the constant + * @param v the value of the constant + * @param function if not empty, the function to put the definition in + */ +void +ShaderSource::add_const(const std::string &name, std::vector<float> &array, + const std::string &function) +{ + std::stringstream ss; + + ss << "const float " << name << "[" << array.size() << "] = {" << std::fixed; + for(std::vector<float>::const_iterator iter = array.begin(); + iter != array.end(); + iter++) + { + ss << *iter; + if (iter + 1 != array.end()) + ss << ", " << std::endl; + } + + ss << "};" << std::endl; + + add(ss.str(), function); +} + +/** + * Adds a vec2 constant definition. + * + * @param name the name of the constant + * @param v the value of the constant + * @param function if not empty, the function to put the definition in + */ +void +ShaderSource::add_const(const std::string &name, const LibMatrix::vec2 &v, + const std::string &function) +{ + std::stringstream ss; + + ss << "const vec2 " << name << " = vec2(" << std::fixed; + ss << v.x() << ", " << v.y() << ");" << std::endl; + + add(ss.str(), function); +} + +/** + * Adds a vec3 constant definition. + * + * @param name the name of the constant + * @param v the value of the constant + * @param function if not empty, the function to put the definition in + */ +void +ShaderSource::add_const(const std::string &name, const LibMatrix::vec3 &v, + const std::string &function) +{ + std::stringstream ss; + + ss << "const vec3 " << name << " = vec3(" << std::fixed; + ss << v.x() << ", " << v.y() << ", " << v.z() << ");" << std::endl; + + add(ss.str(), function); +} + +/** + * Adds a vec4 constant definition. + * + * @param name the name of the constant + * @param v the value of the constant + * @param function if not empty, the function to put the definition in + */ +void +ShaderSource::add_const(const std::string &name, const LibMatrix::vec4 &v, + const std::string &function) +{ + std::stringstream ss; + + ss << "const vec4 " << name << " = vec4(" << std::fixed; + ss << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << ");" << std::endl; + + add(ss.str(), function); +} + +/** + * Adds a mat3 constant definition. + * + * @param name the name of the constant + * @param v the value of the constant + * @param function if not empty, the function to put the definition in + */ +void +ShaderSource::add_const(const std::string &name, const LibMatrix::mat3 &m, + const std::string &function) +{ + std::stringstream ss; + + ss << "const mat3 " << name << " = mat3(" << std::fixed; + ss << m[0][0] << ", " << m[1][0] << ", " << m[2][0] << "," << std::endl; + ss << m[0][1] << ", " << m[1][1] << ", " << m[2][1] << "," << std::endl; + ss << m[0][2] << ", " << m[1][2] << ", " << m[2][2] << std::endl; + ss << ");" << std::endl; + + add(ss.str(), function); +} + +/** + * Adds a float array declaration and initialization. + * + * @param name the name of the array + * @param array the array values + * @param init_function the function to put the initialization in + * @param decl_function if not empty, the function to put the declaration in + */ +void +ShaderSource::add_array(const std::string &name, std::vector<float> &array, + const std::string &init_function, + const std::string &decl_function) +{ + if (init_function.empty() || name.empty()) + return; + + std::stringstream ss; + ss << "float " << name << "[" << array.size() << "];" << std::endl; + + std::string decl(ss.str()); + + ss.clear(); + ss.str(""); + ss << std::fixed; + + for(std::vector<float>::const_iterator iter = array.begin(); + iter != array.end(); + iter++) + { + ss << name << "[" << iter - array.begin() << "] = " << *iter << ";" << std::endl; + } + + add(ss.str(), init_function); + + add(decl, decl_function); +} + +/** + * Gets the ShaderType for this ShaderSource. + * + * If the ShaderType is unknown, an attempt is made to infer + * the type from the shader source contents. + * + * @return the ShaderType + */ +ShaderSource::ShaderType +ShaderSource::type() +{ + /* Try to infer the type from the source contents */ + if (type_ == ShaderSource::ShaderTypeUnknown) { + std::string source(source_.str()); + + if (source.find("gl_FragColor") != std::string::npos) + type_ = ShaderSource::ShaderTypeFragment; + else if (source.find("gl_Position") != std::string::npos) + type_ = ShaderSource::ShaderTypeVertex; + else + Log::debug("Cannot infer shader type from contents. Leaving it Unknown.\n"); + } + + return type_; +} + +/** + * Helper function that emits a precision statement. + * + * @param ss the stringstream to add the statement to + * @param val the precision value + * @param type_str the variable type to apply the precision value to + */ +void +ShaderSource::emit_precision(std::stringstream& ss, ShaderSource::PrecisionValue val, + const std::string& type_str) +{ + static const char *precision_map[] = { + "lowp", "mediump", "highp", NULL + }; + + if (val == ShaderSource::PrecisionValueHigh) { + if (type_ == ShaderSource::ShaderTypeFragment) + ss << "#ifdef GL_FRAGMENT_PRECISION_HIGH" << std::endl; + + ss << "precision highp " << type_str << ";" << std::endl; + + if (type_ == ShaderSource::ShaderTypeFragment) { + ss << "#else" << std::endl; + ss << "precision mediump " << type_str << ";" << std::endl; + ss << "#endif" << std::endl; + } + } + else if (val >= 0 && val < ShaderSource::PrecisionValueDefault) { + ss << "precision " << precision_map[val] << " "; + ss << type_str << ";" << std::endl; + } + + /* There is no default precision in the fragment shader, so set it to mediump */ + if (val == ShaderSource::PrecisionValueDefault + && type_str == "float" && type_ == ShaderSource::ShaderTypeFragment) + { + ss << "precision mediump float;" << std::endl; + } +} + +/** + * Gets a string containing the complete shader source. + * + * Precision statements are applied at this point. + * + * @return the shader source + */ +std::string +ShaderSource::str() +{ + /* Decide which precision values to use */ + ShaderSource::Precision precision; + + /* Ensure we have tried to infer the type from the contents */ + type(); + + if (precision_has_been_set_) + precision = precision_; + else + precision = default_precision(type_); + + /* Create the precision statements */ + std::stringstream ss; + + emit_precision(ss, precision.int_precision, "int"); + emit_precision(ss, precision.float_precision, "float"); + emit_precision(ss, precision.sampler2d_precision, "sampler2D"); + emit_precision(ss, precision.samplercube_precision, "samplerCube"); + + std::string precision_str(ss.str()); + if (!precision_str.empty()) { + precision_str.insert(0, "#ifdef GL_ES\n"); + precision_str.insert(precision_str.size(), "#endif\n"); + } + + return precision_str + source_.str(); +} + +/** + * Sets the precision that will be used for this shader. + * + * This overrides any default values set with ShaderSource::default_*_precision(). + * + * @param precision the precision to set + */ +void +ShaderSource::precision(const ShaderSource::Precision& precision) +{ + precision_ = precision; + precision_has_been_set_ = true; +} + +/** + * Gets the precision that will be used for this shader. + * + * @return the precision + */ +const ShaderSource::Precision& +ShaderSource::precision() +{ + return precision_; +} + +/** + * Sets the default precision that will be used for a shaders type. + * + * If type is ShaderTypeUnknown the supplied precision is used for all + * shader types. + * + * This can be overriden per ShaderSource object by using ::precision(). + * + * @param precision the default precision to set + * @param type the ShaderType to use the precision for + */ +void +ShaderSource::default_precision(const ShaderSource::Precision& precision, + ShaderSource::ShaderType type) +{ + if (type < 0 || type > ShaderSource::ShaderTypeUnknown) + type = ShaderSource::ShaderTypeUnknown; + + if (type == ShaderSource::ShaderTypeUnknown) { + for (size_t i = 0; i < ShaderSource::ShaderTypeUnknown; i++) + default_precision_[i] = precision; + } + else { + default_precision_[type] = precision; + } +} + +/** + * Gets the default precision that will be used for a shader type. + * + * It is valid to use a type of ShaderTypeUnknown. This will always + * return a Precision with default values. + * + * @param type the ShaderType to get the precision of + * + * @return the precision + */ +const ShaderSource::Precision& +ShaderSource::default_precision(ShaderSource::ShaderType type) +{ + if (type < 0 || type > ShaderSource::ShaderTypeUnknown) + type = ShaderSource::ShaderTypeUnknown; + + return default_precision_[type]; +} + +/**************************************** + * ShaderSource::Precision constructors * + ****************************************/ + +/** + * Creates a ShaderSource::Precision with default precision values. + */ +ShaderSource::Precision::Precision() : + int_precision(ShaderSource::PrecisionValueDefault), + float_precision(ShaderSource::PrecisionValueDefault), + sampler2d_precision(ShaderSource::PrecisionValueDefault), + samplercube_precision(ShaderSource::PrecisionValueDefault) +{ +} + +/** + * Creates a ShaderSource::Precision using the supplied precision values. + */ +ShaderSource::Precision::Precision(ShaderSource::PrecisionValue int_p, + ShaderSource::PrecisionValue float_p, + ShaderSource::PrecisionValue sampler2d_p, + ShaderSource::PrecisionValue samplercube_p) : + int_precision(int_p), float_precision(float_p), + sampler2d_precision(sampler2d_p), samplercube_precision(samplercube_p) +{ +} + +/** + * Creates a ShaderSource::Precision from a string representation of + * precision values. + * + * The string format is: + * "<int>,<float>,<sampler2d>,<samplercube>" + * + * Each precision value is one of "high", "medium", "low" or "default". + * + * @param precision_values the string representation of the precision values + */ +ShaderSource::Precision::Precision(const std::string& precision_values) : + int_precision(ShaderSource::PrecisionValueDefault), + float_precision(ShaderSource::PrecisionValueDefault), + sampler2d_precision(ShaderSource::PrecisionValueDefault), + samplercube_precision(ShaderSource::PrecisionValueDefault) +{ + std::vector<std::string> elems; + + Util::split(precision_values, ',', elems, Util::SplitModeNormal); + + for (size_t i = 0; i < elems.size() && i < 4; i++) { + const std::string& pstr(elems[i]); + ShaderSource::PrecisionValue pval; + + if (pstr == "high") + pval = ShaderSource::PrecisionValueHigh; + else if (pstr == "medium") + pval = ShaderSource::PrecisionValueMedium; + else if (pstr == "low") + pval = ShaderSource::PrecisionValueLow; + else + pval = ShaderSource::PrecisionValueDefault; + + switch(i) { + case 0: int_precision = pval; break; + case 1: float_precision = pval; break; + case 2: sampler2d_precision = pval; break; + case 3: samplercube_precision = pval; break; + default: break; + } + } +} diff --git a/src/libmatrix/shader-source.h b/src/libmatrix/shader-source.h new file mode 100644 index 0000000..4dc1d1d --- /dev/null +++ b/src/libmatrix/shader-source.h @@ -0,0 +1,103 @@ +// +// Copyright (c) 2010-2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis <alexandros.frantzis@linaro.org> +// Jesse Barker <jesse.barker@linaro.org> +// +#include <string> +#include <sstream> +#include <vector> +#include "vec.h" +#include "mat.h" + +/** + * Helper class for loading and manipulating shader sources. + */ +class ShaderSource +{ +public: + enum ShaderType { + ShaderTypeVertex, + ShaderTypeFragment, + ShaderTypeUnknown + }; + + ShaderSource(ShaderType type = ShaderTypeUnknown) : + precision_has_been_set_(false), type_(type) {} + ShaderSource(const std::string &filename, ShaderType type = ShaderTypeUnknown) : + precision_has_been_set_(false), type_(type) { append_file(filename); } + + void append(const std::string &str); + void append_file(const std::string &filename); + + void replace(const std::string &remove, const std::string &insert); + void replace_with_file(const std::string &remove, const std::string &filename); + + void add(const std::string &str, const std::string &function = ""); + + void add_const(const std::string &name, float f, + const std::string &function = ""); + void add_const(const std::string &name, std::vector<float> &f, + const std::string &function = ""); + void add_const(const std::string &name, const LibMatrix::vec2 &v, + const std::string &function = ""); + void add_const(const std::string &name, const LibMatrix::vec3 &v, + const std::string &function = ""); + void add_const(const std::string &name, const LibMatrix::vec4 &v, + const std::string &function = ""); + void add_const(const std::string &name, const LibMatrix::mat3 &m, + const std::string &function = ""); + + void add_array(const std::string &name, std::vector<float> &array, + const std::string &init_function, + const std::string &decl_function = ""); + + ShaderType type(); + std::string str(); + + enum PrecisionValue { + PrecisionValueLow, + PrecisionValueMedium, + PrecisionValueHigh, + PrecisionValueDefault + }; + + struct Precision { + Precision(); + Precision(PrecisionValue int_p, PrecisionValue float_p, + PrecisionValue sampler2d_p, PrecisionValue samplercube_p); + Precision(const std::string& list); + + PrecisionValue int_precision; + PrecisionValue float_precision; + PrecisionValue sampler2d_precision; + PrecisionValue samplercube_precision; + }; + + void precision(const Precision& precision); + const Precision& precision(); + + static void default_precision(const Precision& precision, + ShaderType type = ShaderTypeUnknown); + static const Precision& default_precision(ShaderType type); + +private: + void add_global(const std::string &str); + void add_local(const std::string &str, const std::string &function); + bool load_file(const std::string& filename, std::string& str); + void emit_precision(std::stringstream& ss, ShaderSource::PrecisionValue val, + const std::string& type_str); + + std::stringstream source_; + Precision precision_; + bool precision_has_been_set_; + ShaderType type_; + + static std::vector<Precision> default_precision_; +}; diff --git a/src/libmatrix/stack.h b/src/libmatrix/stack.h new file mode 100644 index 0000000..c14cec3 --- /dev/null +++ b/src/libmatrix/stack.h @@ -0,0 +1,106 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef STACK_H_ +#define STACK_H_ + +#include <vector> +#include "mat.h" + +namespace LibMatrix +{ +// +// Simple matrix stack implementation suitable for tracking OpenGL matrix +// state. Default construction puts an identity matrix on the top of the +// stack. +// +template<typename T> +class MatrixStack +{ +public: + MatrixStack() + { + theStack_.push_back(T()); + } + MatrixStack(const T& matrix) + { + theStack_.push_back(matrix); + } + ~MatrixStack() {} + + const T& getCurrent() const { return theStack_.back(); } + + void push() + { + theStack_.push_back(theStack_.back()); + } + void pop() + { + theStack_.pop_back(); + } + void loadIdentity() + { + theStack_.back().setIdentity(); + } + T& operator*=(const T& rhs) + { + T& curMatrix = theStack_.back(); + curMatrix *= rhs; + return curMatrix; + } + void print() const + { + const T& curMatrix = theStack_.back(); + curMatrix.print(); + } + unsigned int getDepth() const { return theStack_.size(); } +private: + std::vector<T> theStack_; +}; + +class Stack4 : public MatrixStack<mat4> +{ +public: + void translate(float x, float y, float z) + { + *this *= Mat4::translate(x, y, z); + } + void scale(float x, float y, float z) + { + *this *= Mat4::scale(x, y, z); + } + void rotate(float angle, float x, float y, float z) + { + *this *= Mat4::rotate(angle, x, y, z); + } + void frustum(float left, float right, float bottom, float top, float near, float far) + { + *this *= Mat4::frustum(left, right, bottom, top, near, far); + } + void ortho(float left, float right, float bottom, float top, float near, float far) + { + *this *= Mat4::ortho(left, right, bottom, top, near, far); + } + void perspective(float fovy, float aspect, float zNear, float zFar) + { + *this *= Mat4::perspective(fovy, aspect, zNear, zFar); + } + void lookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) + { + *this *= Mat4::lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + } +}; + +} // namespace LibMatrix + +#endif // STACK_H_ diff --git a/src/libmatrix/test/basic-global-const.vert b/src/libmatrix/test/basic-global-const.vert new file mode 100644 index 0000000..18dc358 --- /dev/null +++ b/src/libmatrix/test/basic-global-const.vert @@ -0,0 +1,15 @@ +const vec4 ConstantColor = vec4(1.000000, 1.000000, 1.000000, 1.000000); +attribute vec3 position; + +uniform mat4 modelview; +uniform mat4 projection; + +varying vec4 color; + +void +main(void) +{ + vec4 curVertex = vec4(position, 1.0); + gl_Position = projection * modelview * curVertex; + color = ConstantColor; +} diff --git a/src/libmatrix/test/basic.frag b/src/libmatrix/test/basic.frag new file mode 100644 index 0000000..1b648db --- /dev/null +++ b/src/libmatrix/test/basic.frag @@ -0,0 +1,7 @@ +varying vec4 color; + +void +main(void) +{ + gl_FragColor = color; +} diff --git a/src/libmatrix/test/basic.vert b/src/libmatrix/test/basic.vert new file mode 100644 index 0000000..d93289e --- /dev/null +++ b/src/libmatrix/test/basic.vert @@ -0,0 +1,14 @@ +attribute vec3 position; + +uniform mat4 modelview; +uniform mat4 projection; + +varying vec4 color; + +void +main(void) +{ + vec4 curVertex = vec4(position, 1.0); + gl_Position = projection * modelview * curVertex; + color = ConstantColor; +} diff --git a/src/libmatrix/test/const_vec_test.cc b/src/libmatrix/test/const_vec_test.cc new file mode 100644 index 0000000..879d4e6 --- /dev/null +++ b/src/libmatrix/test/const_vec_test.cc @@ -0,0 +1,60 @@ +// +// Copyright (c) 2011 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <iostream> +#include "libmatrix_test.h" +#include "const_vec_test.h" +#include "../vec.h" + +using LibMatrix::vec2; +using LibMatrix::vec3; +using LibMatrix::vec4; +using std::cout; +using std::endl; + +void +Vec2TestConstOperator::run(const Options& options) +{ + const vec2 a(1.0, 1.0); + const vec2 b(2.0, 2.0); + vec2 aplusb(a + b); + vec2 aminusb(a - b); + vec2 atimesb(a * b); + vec2 adivb(a / b); + const float s(2.5); + vec2 stimesb(s * b); +} + +void +Vec3TestConstOperator::run(const Options& options) +{ + const vec3 a(1.0, 1.0, 1.0); + const vec3 b(2.0, 2.0, 2.0); + vec3 aplusb(a + b); + vec3 aminusb(a - b); + vec3 atimesb(a * b); + vec3 adivb(a / b); + const float s(2.5); + vec3 stimesb(s * b); +} + +void +Vec4TestConstOperator::run(const Options& options) +{ + const vec4 a(1.0, 1.0, 1.0, 1.0); + const vec4 b(2.0, 2.0, 2.0, 2.0); + vec4 aplusb(a + b); + vec4 aminusb(a - b); + vec4 atimesb(a * b); + vec4 adivb(a / b); + const float s(2.5); + vec4 stimesb(s * b); +} diff --git a/src/libmatrix/test/const_vec_test.h b/src/libmatrix/test/const_vec_test.h new file mode 100644 index 0000000..834f4a8 --- /dev/null +++ b/src/libmatrix/test/const_vec_test.h @@ -0,0 +1,39 @@ +// +// Copyright (c) 2011 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef CONST_VEC_TEST_H_ +#define CONST_VEC_TEST_H_ + +class MatrixTest; +class Options; + +class Vec2TestConstOperator : public MatrixTest +{ +public: + Vec2TestConstOperator() : MatrixTest("vec2::const") {} + virtual void run(const Options& options); +}; + +class Vec3TestConstOperator : public MatrixTest +{ +public: + Vec3TestConstOperator() : MatrixTest("vec3::const") {} + virtual void run(const Options& options); +}; + +class Vec4TestConstOperator : public MatrixTest +{ +public: + Vec4TestConstOperator() : MatrixTest("vec4::const") {} + virtual void run(const Options& options); +}; + +#endif // CONST_VEC_TEST_H_ diff --git a/src/libmatrix/test/inverse_test.cc b/src/libmatrix/test/inverse_test.cc new file mode 100644 index 0000000..51a8072 --- /dev/null +++ b/src/libmatrix/test/inverse_test.cc @@ -0,0 +1,172 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <iostream> +#include "libmatrix_test.h" +#include "inverse_test.h" +#include "../mat.h" + +using LibMatrix::mat2; +using LibMatrix::mat3; +using LibMatrix::mat4; +using std::cout; +using std::endl; + +void +MatrixTest2x2Inverse::run(const Options& options) +{ + mat2 m; + + if (options.beVerbose()) + { + cout << "Starting with mat2 (should be identity): " << endl << endl; + m.print(); + } + + m[0][1] = -2.5; + + if (options.beVerbose()) + { + cout << endl << "Matrix should now have (0, 1) == -2.500000" << endl << endl; + m.print(); + } + + mat2 mi(m); + + if (options.beVerbose()) + { + cout << endl << "Copy of previous matrix (should have (0, 1) == -2.500000)" << endl << endl; + mi.print(); + } + + mi.inverse(); + + if (options.beVerbose()) + { + cout << endl << "Inverse of copy: " << endl << endl; + mi.print(); + } + + mat2 i = m * mi; + + if (options.beVerbose()) + { + cout << endl << "Product of original and inverse (should be identity): " << endl << endl; + i.print(); + } + + mat2 ident; + if (i == ident) + { + pass_ = true; + } +} + +void +MatrixTest3x3Inverse::run(const Options& options) +{ + mat3 m; + + if (options.beVerbose()) + { + cout << "Starting with mat3 (should be identity): " << endl << endl; + m.print(); + } + + m[1][2] = -2.5; + + if (options.beVerbose()) + { + cout << endl << "Matrix should now have (1, 2) == -2.500000" << endl << endl; + m.print(); + } + + mat3 mi(m); + + if (options.beVerbose()) + { + cout << endl << "Copy of previous matrix (should have (1, 2) == -2.500000)" << endl << endl; + mi.print(); + } + + mi.inverse(); + + if (options.beVerbose()) + { + cout << endl << "Inverse of copy: " << endl << endl; + mi.print(); + } + + mat3 i = m * mi; + + if (options.beVerbose()) + { + cout << endl << "Product of original and inverse (should be identity): " << endl << endl; + i.print(); + } + + mat3 ident; + if (i == ident) + { + pass_ = true; + } +} + +void +MatrixTest4x4Inverse::run(const Options& options) +{ + mat4 m; + + if (options.beVerbose()) + { + cout << "Starting with mat4 (should be identity): " << endl << endl; + m.print(); + } + + m[2][3] = -2.5; + + if (options.beVerbose()) + { + cout << endl << "Matrix should now have (2, 3) == -2.500000" << endl << endl; + m.print(); + } + + mat4 mi(m); + + if (options.beVerbose()) + { + cout << endl << "Copy of previous matrix (should have (2, 3) == -2.500000)" << endl << endl; + mi.print(); + } + + mi.inverse(); + + if (options.beVerbose()) + { + cout << endl << "Inverse of copy: " << endl << endl; + mi.print(); + } + + mat4 i = m * mi; + + if (options.beVerbose()) + { + cout << endl << "Product of original and inverse (should be identity): " << endl << endl; + i.print(); + } + + mat4 ident; + if (i == ident) + { + pass_ = true; + } +} + diff --git a/src/libmatrix/test/inverse_test.h b/src/libmatrix/test/inverse_test.h new file mode 100644 index 0000000..4c5b584 --- /dev/null +++ b/src/libmatrix/test/inverse_test.h @@ -0,0 +1,39 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef INVERSE_TEST_H_ +#define INVERSE_TEST_H_ + +class MatrixTest; +class Options; + +class MatrixTest2x2Inverse : public MatrixTest +{ +public: + MatrixTest2x2Inverse() : MatrixTest("mat2::inverse") {} + virtual void run(const Options& options); +}; + +class MatrixTest3x3Inverse : public MatrixTest +{ +public: + MatrixTest3x3Inverse() : MatrixTest("mat3::inverse") {} + virtual void run(const Options& options); +}; + +class MatrixTest4x4Inverse : public MatrixTest +{ +public: + MatrixTest4x4Inverse() : MatrixTest("mat4::inverse") {} + virtual void run(const Options& options); +}; + +#endif // INVERSE_TEST_H_ diff --git a/src/libmatrix/test/libmatrix_test.cc b/src/libmatrix/test/libmatrix_test.cc new file mode 100644 index 0000000..5e4ff12 --- /dev/null +++ b/src/libmatrix/test/libmatrix_test.cc @@ -0,0 +1,72 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// Alexandros Frantzis - Util::split tests +// +#include <iostream> +#include <string> +#include <vector> +#include "libmatrix_test.h" +#include "inverse_test.h" +#include "transpose_test.h" +#include "const_vec_test.h" +#include "shader_source_test.h" +#include "util_split_test.h" + +using std::cerr; +using std::cout; +using std::endl; + +int +main(int argc, char** argv) +{ + Options testOptions("matrix_test"); + testOptions.parseArgs(argc, argv); + if (testOptions.showHelp()) + { + testOptions.printUsage(); + return 0; + } + + using std::vector; + vector<MatrixTest*> testVec; + testVec.push_back(new MatrixTest2x2Inverse()); + testVec.push_back(new MatrixTest3x3Inverse()); + testVec.push_back(new MatrixTest4x4Inverse()); + testVec.push_back(new MatrixTest2x2Transpose()); + testVec.push_back(new MatrixTest3x3Transpose()); + testVec.push_back(new MatrixTest4x4Transpose()); + testVec.push_back(new ShaderSourceBasic()); + testVec.push_back(new UtilSplitTestNormal()); + testVec.push_back(new UtilSplitTestQuoted()); + + for (vector<MatrixTest*>::iterator testIt = testVec.begin(); + testIt != testVec.end(); + testIt++) + { + MatrixTest* curTest = *testIt; + if (testOptions.beVerbose()) + { + cout << "Running test " << curTest->name() << endl; + } + curTest->run(testOptions); + if (!curTest->passed()) + { + cerr << curTest->name() << " does not work!" << endl; + return 1; + } + if (testOptions.beVerbose()) + { + cout << curTest->name() << " is okay!" << endl; + } + } + + return 0; +} diff --git a/src/libmatrix/test/libmatrix_test.h b/src/libmatrix/test/libmatrix_test.h new file mode 100644 index 0000000..4ae0a8a --- /dev/null +++ b/src/libmatrix/test/libmatrix_test.h @@ -0,0 +1,51 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef LIBMATRIX_TEST_H_ +#define LIBMATRIX_TEST_H_ + +class Options +{ + Options(); + static const std::string verbose_name_; + static const std::string help_name_; + std::string app_name_; + bool show_help_; + bool verbose_; +public: + Options(const std::string& app_name) : + app_name_(app_name), + show_help_(false), + verbose_(false) {} + ~Options() {} + bool beVerbose() const { return verbose_; } + bool showHelp() const { return show_help_; } + void parseArgs(int argc, char** argv); + void printUsage(); +}; + +class MatrixTest +{ + std::string name_; +protected: + bool pass_; + MatrixTest(); +public: + MatrixTest(const std::string& name) : + name_(name), + pass_(false) {} + ~MatrixTest(); + const std::string& name() const { return name_; } + virtual void run(const Options& options) = 0; + const bool passed() const { return pass_; } +}; + +#endif // LIBMATRIX_TEST_H_ diff --git a/src/libmatrix/test/options.cc b/src/libmatrix/test/options.cc new file mode 100644 index 0000000..ec15f3c --- /dev/null +++ b/src/libmatrix/test/options.cc @@ -0,0 +1,76 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <iostream> +#include <iomanip> +#include <getopt.h> +#include "libmatrix_test.h" + +using std::cout; +using std::endl; + +const std::string Options::verbose_name_("verbose"); +const std::string Options::help_name_("help"); + +void +Options::parseArgs(int argc, char** argv) +{ + static struct option long_options[] = { + {"verbose", 0, 0, 0}, + {"help", 0, 0, 0}, + {0, 0, 0, 0} + }; + int option_index(0); + int c = getopt_long(argc, argv, "", long_options, &option_index); + while (c != -1) + { + // getopt_long() returns '?' and prints an "unrecognized option" error + // to stderr if it does not recognize an option. Just trigger + // the help/usage message, stop processing and get out. + if (c == '?') + { + show_help_ = true; + break; + } + + std::string optname(long_options[option_index].name); + + if (optname == verbose_name_) + { + verbose_ = true; + } + else if (optname == help_name_) + { + show_help_ = true; + } + c = getopt_long(argc, argv, "", + long_options, &option_index); + } +} + + +static void +emitColumnOne(const std::string& text) +{ + cout << std::setw(16) << text; +} + +void +Options::printUsage() +{ + cout << app_name_ << ": directed functional test utility for libmatrix." << endl; + cout << "Options:" << endl; + emitColumnOne("--verbose"); + cout << std::setw(0) << " Enable verbose output during test runs." << endl; + emitColumnOne("--help"); + cout << std::setw(0) << " Print this usage text." << endl; +} + diff --git a/src/libmatrix/test/shader_source_test.cc b/src/libmatrix/test/shader_source_test.cc new file mode 100644 index 0000000..31d0265 --- /dev/null +++ b/src/libmatrix/test/shader_source_test.cc @@ -0,0 +1,49 @@ +// +// Copyright (c) 2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <string> +#include "libmatrix_test.h" +#include "shader_source_test.h" +#include "../shader-source.h" +#include "../vec.h" + +using std::string; +using LibMatrix::vec4; + +void +ShaderSourceBasic::run(const Options& options) +{ + static const string vtx_shader_filename("test/basic.vert"); + + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource vtx_source2(vtx_shader_filename); + + pass_ = (vtx_source.str() == vtx_source2.str()); +} + +void +ShaderSourceAddConstGlobal::run(const Options& options) +{ + // Load the original shader source. + static const string src_shader_filename("test/basic.vert"); + ShaderSource src_shader(src_shader_filename); + + // Add constant at global scope + static const vec4 constantColor(1.0, 1.0, 1.0, 1.0); + src_shader.add_const("ConstantColor", constantColor); + + // Load the pre-modified shader + static const string result_shader_filename("test/basic-global-const.vert"); + ShaderSource result_shader(result_shader_filename); + + // Compare the output strings to confirm the results. + pass_ = (src_shader.str() == result_shader.str()); +} diff --git a/src/libmatrix/test/shader_source_test.h b/src/libmatrix/test/shader_source_test.h new file mode 100644 index 0000000..cf49766 --- /dev/null +++ b/src/libmatrix/test/shader_source_test.h @@ -0,0 +1,32 @@ +// +// Copyright (c) 2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef SHADER_SOURCE_TEST_H_ +#define SHADER_SOURCE_TEST_H_ + +class MatrixTest; +class Options; + +class ShaderSourceBasic : public MatrixTest +{ +public: + ShaderSourceBasic() : MatrixTest("ShaderSource::basic") {} + virtual void run(const Options& options); +}; + +class ShaderSourceAddConstGlobal : public MatrixTest +{ +public: + ShaderSourceAddConstGlobal() : MatrixTest("ShaderSource::AddConstGlobal") {} + virtual void run(const Options& options); +}; + +#endif // SHADER_SOURCE_TEST_H diff --git a/src/libmatrix/test/transpose_test.cc b/src/libmatrix/test/transpose_test.cc new file mode 100644 index 0000000..574486d --- /dev/null +++ b/src/libmatrix/test/transpose_test.cc @@ -0,0 +1,297 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#include <iostream> +#include "libmatrix_test.h" +#include "transpose_test.h" +#include "../mat.h" + +using LibMatrix::mat2; +using LibMatrix::mat3; +using LibMatrix::mat4; +using std::cout; +using std::endl; + +void +MatrixTest2x2Transpose::run(const Options& options) +{ + // First, a simple test to ensure that the transpose of the identity is + // the identity. + if (options.beVerbose()) + { + cout << endl << "Assertion 1: Transpose of the identity is the identity." << endl << endl; + } + + mat2 m; + + if (options.beVerbose()) + { + cout << "Starting with mat2 (should be identity): " << endl << endl; + m.print(); + } + + m.transpose(); + + if (options.beVerbose()) + { + cout << endl << "Transpose of identity (should be identity): " << endl << endl; + m.print(); + } + + mat2 mi; + if (m != mi) + { + // FAIL! Transpose of the identity is the identity. + return; + } + + // At this point, we have 2 identity matrices. + // Next, set an element in the matrix and transpose twice. We should see + // the original matrix (with i,j set). + if (options.beVerbose()) + { + cout << endl << "Assertion 2: Transposing a matrix twice yields the original matrix." << endl << endl; + } + + m[0][1] = 6.3; + + if (options.beVerbose()) + { + cout << "Matrix should now have (0, 1) == 6.300000" << endl << endl; + m.print(); + } + + mi = m; + + m.transpose().transpose(); + + if (options.beVerbose()) + { + cout << endl << "Matrix should now have (0, 1) == 6.300000" << endl << endl; + m.print(); + } + + if (m != mi) + { + // FAIL! Transposing the same matrix twice should yield the original. + return; + } + + // Next, reset mi back to the identity. Set element element j,i in this + // matrix and transpose m. They should now be equal. + if (options.beVerbose()) + { + cout << endl << "Assertion 3: Transpose of matrix (i,j) == x is equal to matrix (j,i) == x." << endl << endl; + } + + mi.setIdentity(); + mi[1][0] = 6.3; + + m.transpose(); + + if (options.beVerbose()) + { + cout << "Matrix should now have (1, 0) == 6.300000" << endl << endl; + m.print(); + cout << endl; + } + + if (m == mi) + { + pass_ = true; + } + + // FAIL! Transposing the same matrix twice should yield the original. +} + +void +MatrixTest3x3Transpose::run(const Options& options) +{ + // First, a simple test to ensure that the transpose of the identity is + // the identity. + if (options.beVerbose()) + { + cout << endl << "Assertion 1: Transpose of the identity is the identity." << endl << endl; + } + + mat3 m; + + if (options.beVerbose()) + { + cout << "Starting with mat2 (should be identity): " << endl << endl; + m.print(); + } + + m.transpose(); + + if (options.beVerbose()) + { + cout << endl << "Transpose of identity (should be identity): " << endl << endl; + m.print(); + } + + mat3 mi; + if (m != mi) + { + // FAIL! Transpose of the identity is the identity. + return; + } + + // At this point, we have 2 identity matrices. + // Next, set an element in the matrix and transpose twice. We should see + // the original matrix (with i,j set). + if (options.beVerbose()) + { + cout << endl << "Assertion 2: Transposing a matrix twice yields the original matrix." << endl << endl; + } + + m[0][1] = 6.3; + + if (options.beVerbose()) + { + cout << "Matrix should now have (0, 1) == 6.300000" << endl << endl; + m.print(); + } + + mi = m; + + m.transpose().transpose(); + + if (options.beVerbose()) + { + cout << endl << "Matrix should now have (0, 1) == 6.300000" << endl << endl; + m.print(); + } + + if (m != mi) + { + // FAIL! Transposing the same matrix twice should yield the original. + return; + } + + // Next, reset mi back to the identity. Set element element j,i in this + // matrix and transpose m. They should now be equal. + if (options.beVerbose()) + { + cout << endl << "Assertion 3: Transpose of matrix (i,j) == x is equal to matrix (j,i) == x." << endl << endl; + } + + mi.setIdentity(); + mi[1][0] = 6.3; + + m.transpose(); + + if (options.beVerbose()) + { + cout << "Matrix should now have (1, 0) == 6.300000" << endl << endl; + m.print(); + cout << endl; + } + + if (m == mi) + { + pass_ = true; + } + + // FAIL! Transposing the same matrix twice should yield the original. +} + +void +MatrixTest4x4Transpose::run(const Options& options) +{ + // First, a simple test to ensure that the transpose of the identity is + // the identity. + if (options.beVerbose()) + { + cout << endl << "Assertion 1: Transpose of the identity is the identity." << endl << endl; + } + + mat4 m; + + if (options.beVerbose()) + { + cout << "Starting with mat2 (should be identity): " << endl << endl; + m.print(); + } + + m.transpose(); + + if (options.beVerbose()) + { + cout << endl << "Transpose of identity (should be identity): " << endl << endl; + m.print(); + } + + mat4 mi; + if (m != mi) + { + // FAIL! Transpose of the identity is the identity. + return; + } + + // At this point, we have 2 identity matrices. + // Next, set an element in the matrix and transpose twice. We should see + // the original matrix (with i,j set). + if (options.beVerbose()) + { + cout << endl << "Assertion 2: Transposing a matrix twice yields the original matrix." << endl << endl; + } + + m[0][1] = 6.3; + + if (options.beVerbose()) + { + cout << "Matrix should now have (0, 1) == 6.300000" << endl << endl; + m.print(); + } + + mi = m; + + m.transpose().transpose(); + + if (options.beVerbose()) + { + cout << endl << "Matrix should now have (0, 1) == 6.300000" << endl << endl; + m.print(); + } + + if (m != mi) + { + // FAIL! Transposing the same matrix twice should yield the original. + return; + } + + // Next, reset mi back to the identity. Set element element j,i in this + // matrix and transpose m. They should now be equal. + if (options.beVerbose()) + { + cout << endl << "Assertion 3: Transpose of matrix (i,j) == x is equal to matrix (j,i) == x." << endl << endl; + } + + mi.setIdentity(); + mi[1][0] = 6.3; + + m.transpose(); + + if (options.beVerbose()) + { + cout << "Matrix should now have (1, 0) == 6.300000" << endl << endl; + m.print(); + cout << endl; + } + + if (m == mi) + { + pass_ = true; + } + + // FAIL! Transposing the same matrix twice should yield the original. +} diff --git a/src/libmatrix/test/transpose_test.h b/src/libmatrix/test/transpose_test.h new file mode 100644 index 0000000..3a4398f --- /dev/null +++ b/src/libmatrix/test/transpose_test.h @@ -0,0 +1,38 @@ +// +// Copyright (c) 2010 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef TRANSPOSE_TEST_H_ +#define TRANSPOSE_TEST_H_ + +class MatrixTest; +class Options; + +class MatrixTest2x2Transpose : public MatrixTest +{ +public: + MatrixTest2x2Transpose() : MatrixTest("mat2::transpose") {} + virtual void run(const Options& options); +}; + +class MatrixTest3x3Transpose : public MatrixTest +{ +public: + MatrixTest3x3Transpose() : MatrixTest("mat3::transpose") {} + virtual void run(const Options& options); +}; + +class MatrixTest4x4Transpose : public MatrixTest +{ +public: + MatrixTest4x4Transpose() : MatrixTest("mat4::transpose") {} + virtual void run(const Options& options); +}; +#endif // TRANSPOSE_TEST_H_ diff --git a/src/libmatrix/test/util_split_test.cc b/src/libmatrix/test/util_split_test.cc new file mode 100644 index 0000000..4399f77 --- /dev/null +++ b/src/libmatrix/test/util_split_test.cc @@ -0,0 +1,180 @@ +// +// Copyright (c) 2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis - original implementation. +// +#include <iostream> +#include <string> +#include <vector> +#include "libmatrix_test.h" +#include "util_split_test.h" +#include "../util.h" + +using std::cout; +using std::endl; +using std::string; +using std::vector; + +template <typename T> static bool +areVectorsEqual(vector<T>& vec1, vector<T>& vec2) +{ + if (vec1.size() != vec2.size()) + return false; + + for (unsigned int i = 0; i < vec1.size(); i++) + { + if (vec1[i] != vec2[i]) + return false; + } + + return true; +} + +template <typename T> static void +printVector(vector<T>& vec) +{ + cout << "["; + for (unsigned int i = 0; i < vec.size(); i++) + { + cout << '"' << vec[i] << '"'; + if (i < vec.size() - 1) + cout << ", "; + } + cout << "]"; +} + +void +UtilSplitTestNormal::run(const Options& options) +{ + const string test1("abc def ghi"); + const string test2(" abc: def :ghi "); + vector<string> expected1; + vector<string> expected2; + vector<string> results; + + expected1.push_back("abc"); + expected1.push_back("def"); + expected1.push_back("ghi"); + + expected2.push_back(" abc"); + expected2.push_back(" def "); + expected2.push_back("ghi "); + + if (options.beVerbose()) + { + cout << "Testing string \"" << test1 << "\"" << endl; + } + + Util::split(test1, ' ', results, Util::SplitModeNormal); + + if (options.beVerbose()) + { + cout << "Split result: "; + printVector(results); + cout << endl << "Expected: "; + printVector(expected1); + cout << endl; + } + + if (!areVectorsEqual(results, expected1)) + { + return; + } + + results.clear(); + + if (options.beVerbose()) + { + cout << "Testing string \"" << test2 << "\"" << endl; + } + + Util::split(test2, ':', results, Util::SplitModeNormal); + + if (options.beVerbose()) + { + cout << "Split result: "; + printVector(results); + cout << endl << "Expected: "; + printVector(expected2); + cout << endl; + } + + if (!areVectorsEqual(results, expected2)) + { + return; + } + + pass_ = true; +} + +void +UtilSplitTestQuoted::run(const Options& options) +{ + const string test1("abc \"def' ghi\" klm\\ nop -b qr:title='123 \"456'"); + const string test2("abc: def='1:2:3:'ghi : \":jk\""); + vector<string> expected1; + vector<string> expected2; + vector<string> results; + + expected1.push_back("abc"); + expected1.push_back("def' ghi"); + expected1.push_back("klm nop"); + expected1.push_back("-b"); + expected1.push_back("qr:title=123 \"456"); + + expected2.push_back("abc"); + expected2.push_back(" def=1:2:3:ghi "); + expected2.push_back(" :jk"); + + if (options.beVerbose()) + { + cout << "Testing string \"" << test1 << "\"" << endl; + } + + Util::split(test1, ' ', results, Util::SplitModeQuoted); + + if (options.beVerbose()) + { + cout << "Split result: "; + printVector(results); + cout << endl << "Expected: "; + printVector(expected1); + cout << endl; + } + + if (!areVectorsEqual(results, expected1)) + { + return; + } + + results.clear(); + + if (options.beVerbose()) + { + cout << "Testing string \"" << test2 << "\"" << endl; + } + + Util::split(test2, ':', results, Util::SplitModeQuoted); + + if (options.beVerbose()) + { + cout << "Split result: "; + printVector(results); + cout << endl << "Expected: "; + printVector(expected2); + cout << endl; + } + + if (!areVectorsEqual(results, expected2)) + { + return; + } + + pass_ = true; +} diff --git a/src/libmatrix/test/util_split_test.h b/src/libmatrix/test/util_split_test.h new file mode 100644 index 0000000..a8023ff --- /dev/null +++ b/src/libmatrix/test/util_split_test.h @@ -0,0 +1,31 @@ +// +// Copyright (c) 2012 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis - original implementation. +// +#ifndef UTIL_SPLIT_TEST_H_ +#define UTIL_SPLIT_TEST_H_ + +class MatrixTest; +class Options; + +class UtilSplitTestNormal : public MatrixTest +{ +public: + UtilSplitTestNormal() : MatrixTest("Util::split::normal") {} + virtual void run(const Options& options); +}; + +class UtilSplitTestQuoted : public MatrixTest +{ +public: + UtilSplitTestQuoted() : MatrixTest("Util::split::quoted") {} + virtual void run(const Options& options); +}; +#endif // UTIL_SPLIT_TEST_H_ diff --git a/src/libmatrix/util.cc b/src/libmatrix/util.cc new file mode 100644 index 0000000..d96f393 --- /dev/null +++ b/src/libmatrix/util.cc @@ -0,0 +1,343 @@ +// +// Copyright (c) 2010-2011 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis <alexandros.frantzis@linaro.org> +// Jesse Barker <jesse.barker@linaro.org> +// +#include <sstream> +#include <fstream> +#include <sys/time.h> +#ifdef ANDROID +#include <android/asset_manager.h> +#else +#include <dirent.h> +#endif + +#include "log.h" +#include "util.h" + +using std::string; +using std::vector; + +/* + * State machine for bash-like quoted string escaping: + * + * \ + * -----------> +---------+ + * | ---------- | Escaped | + * | | *,ESC +---------+ + * | | + * | v ' + * +--------+ ---> +--------------+ ----- + * | Normal | <--- | SingleQuoted | | *, ESC + * +--------+ ' +--------------+ <---- + * | ^ + * | | + * | | " +--------------+ ---- + * | ---------- | DoubleQuoted | | *, ESC + * -----------> +--------------+ <--- + * " | ^ + * \ | | *, ESC + * v | + * +---------------------+ + * | DoubleQuotedEscaped | + * +---------------------+ + * + * ESC: Mark character as Escaped + */ +static void +fill_escape_vector(const string &str, vector<bool> &esc_vec) +{ + enum State { + StateNormal, + StateEscaped, + StateDoubleQuoted, + StateDoubleQuotedEscaped, + StateSingleQuoted + }; + + State state = StateNormal; + + for (string::const_iterator iter = str.begin(); + iter != str.end(); + iter++) + { + const char c(*iter); + bool esc = false; + + switch (state) { + case StateNormal: + if (c == '"') + state = StateDoubleQuoted; + else if (c == '\\') + state = StateEscaped; + else if (c == '\'') + state = StateSingleQuoted; + break; + case StateEscaped: + esc = true; + state = StateNormal; + break; + case StateDoubleQuoted: + if (c == '"') + state = StateNormal; + else if (c == '\\') + state = StateDoubleQuotedEscaped; + else + esc = true; + break; + case StateDoubleQuotedEscaped: + esc = true; + state = StateDoubleQuoted; + break; + case StateSingleQuoted: + if (c == '\'') + state = StateNormal; + else + esc = true; + default: + break; + } + + esc_vec.push_back(esc); + } +} + +static void +split_normal(const string& src, char delim, vector<string>& elementVec) +{ + std::stringstream ss(src); + string item; + while(std::getline(ss, item, delim)) + elementVec.push_back(item); +} + +static void +split_fuzzy(const string& src, char delim, vector<string>& elementVec) +{ + // Fuzzy case: Initialize our delimiter string based upon the caller's plus + // a space to allow for more flexibility. + string delimiter(" "); + delimiter += delim; + // Starting index into the string of the first token (by definition, if + // we're parsing a string, there is at least one token). + string::size_type startPos(0); + // string::find_first_of() looks for any character in the string provided, + // it is not treated as a sub-string, so regardless of where the space or + // comma is or how many there are, the result is the same. + string str(src); + string::size_type endPos = str.find_first_of(delimiter); + while (endPos != string::npos) + { + // Push back the current element starting at startPos for + // (endPos - startPos) characters. std::string takes care of + // terminators, etc. + elementVec.push_back(string(str, startPos, endPos - startPos)); + // Index of the next element after any delimiter characters. Same + // caveat applies to find_first_not_of() that applies to + // find_first_of(); endPos tells it where to start the search. + string::size_type nextPos = str.find_first_not_of(delimiter, endPos); + // Erase the part of the string we've already parsed. + str = str.erase(startPos, nextPos - startPos); + // Look for the next delimiter. If there isn't one, we bail out. + endPos = str.find_first_of(delimiter); + } + // Regardless of whether we initially had one element or many, 'str' now + // only contains one. + elementVec.push_back(str); +} + +static void +split_quoted(const string& src, char delim, vector<string>& elementVec) +{ + std::stringstream ss; + vector<bool> escVec; + + /* Mark characters in the string as escaped or not */ + fill_escape_vector(src, escVec); + + /* Sanity check... */ + if (src.length() != escVec.size()) + return; + + for (vector<bool>::const_iterator iter = escVec.begin(); + iter != escVec.end(); + iter++) + { + bool escaped = static_cast<bool>(*iter); + char c = src[iter - escVec.begin()]; + + /* Output all characters, except unescaped ",\,' */ + if ((c != '"' && c != '\\' && c != '\'') || escaped) { + /* If we reach an unescaped delimiter character, do a split */ + if (c == delim && !escaped) { + elementVec.push_back(ss.str()); + ss.str(""); + ss.clear(); + } + else { + ss << c; + } + } + + } + + /* Handle final element, delimited by end of string */ + const string &finalElement(ss.str()); + if (!finalElement.empty()) + elementVec.push_back(finalElement); +} + +void +Util::split(const string& src, char delim, vector<string>& elementVec, + Util::SplitMode mode) +{ + // Trivial rejection + if (src.empty()) + { + return; + } + + switch (mode) + { + case Util::SplitModeNormal: + return split_normal(src, delim, elementVec); + case Util::SplitModeFuzzy: + return split_fuzzy(src, delim, elementVec); + case Util::SplitModeQuoted: + return split_quoted(src, delim, elementVec); + default: + break; + } +} + +uint64_t +Util::get_timestamp_us() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t now = static_cast<uint64_t>(tv.tv_sec) * 1000000 + + static_cast<double>(tv.tv_usec); + return now; +} + +std::string +Util::appname_from_path(const std::string& path) +{ + std::string::size_type slashPos = path.rfind("/"); + std::string::size_type startPos(0); + if (slashPos != std::string::npos) + { + startPos = slashPos + 1; + } + return std::string(path, startPos, std::string::npos); +} + +#ifndef ANDROID + +std::istream * +Util::get_resource(const std::string &path) +{ + std::ifstream *ifs = new std::ifstream(path.c_str()); + + return static_cast<std::istream *>(ifs); +} + +void +Util::list_files(const std::string& dirName, std::vector<std::string>& fileVec) +{ + DIR* dir = opendir(dirName.c_str()); + if (!dir) + { + Log::error("Failed to open models directory '%s'\n", dirName.c_str()); + return; + } + + struct dirent* entry = readdir(dir); + while (entry) + { + std::string pathname(dirName + "/"); + pathname += std::string(entry->d_name); + // Skip '.' and '..' + if (entry->d_name[0] != '.') + { + fileVec.push_back(pathname); + } + entry = readdir(dir); + } + closedir(dir); +} + +#else + +AAssetManager *Util::android_asset_manager = 0; + +void +Util::android_set_asset_manager(AAssetManager *asset_manager) +{ + Util::android_asset_manager = asset_manager; +} + +AAssetManager * +Util::android_get_asset_manager() +{ + return Util::android_asset_manager; +} + +std::istream * +Util::get_resource(const std::string &path) +{ + std::string path2(path); + /* Remove leading '/' from path name, it confuses the AssetManager */ + if (path2.size() > 0 && path2[0] == '/') + path2.erase(0, 1); + + std::stringstream *ss = new std::stringstream; + AAsset *asset = AAssetManager_open(Util::android_asset_manager, + path2.c_str(), AASSET_MODE_RANDOM); + if (asset) { + ss->write(reinterpret_cast<const char *>(AAsset_getBuffer(asset)), + AAsset_getLength(asset)); + Log::debug("Load asset %s\n", path2.c_str()); + AAsset_close(asset); + } + else { + Log::error("Couldn't load asset %s\n", path2.c_str()); + } + + return static_cast<std::istream *>(ss); +} + +void +Util::list_files(const std::string& dirName, std::vector<std::string>& fileVec) +{ + AAssetManager *mgr(Util::android_get_asset_manager()); + std::string dir_name(dirName); + + /* Remove leading '/' from path, it confuses the AssetManager */ + if (dir_name.size() > 0 && dir_name[0] == '/') + dir_name.erase(0, 1); + + AAssetDir* dir = AAssetManager_openDir(mgr, dir_name.c_str()); + if (!dir) + { + Log::error("Failed to open models directory '%s'\n", dir_name.c_str()); + return; + } + + const char *filename(0); + while ((filename = AAssetDir_getNextFileName(dir)) != 0) + { + std::string pathname(dir_name + "/"); + pathname += std::string(filename); + fileVec.push_back(pathname); + } + AAssetDir_close(dir); +} +#endif diff --git a/src/libmatrix/util.h b/src/libmatrix/util.h new file mode 100644 index 0000000..2b0f0f0 --- /dev/null +++ b/src/libmatrix/util.h @@ -0,0 +1,142 @@ +// +// Copyright (c) 2010-2011 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Alexandros Frantzis <alexandros.frantzis@linaro.org> +// Jesse Barker <jesse.barker@linaro.org> +// +#ifndef UTIL_H_ +#define UTIL_H_ + +#include <string> +#include <vector> +#include <istream> +#include <sstream> +#include <stdint.h> + +#ifdef ANDROID +#include <android/asset_manager_jni.h> +#endif + +struct Util { + /** + * How to perform the split() operation + */ + enum SplitMode { + /** Normal split operation */ + SplitModeNormal, + /** Allow for spaces and multiple consecutive occurences of the delimiter */ + SplitModeFuzzy, + /** Take into account bash-like quoting and escaping rules */ + SplitModeQuoted + }; + + /** + * split() - Splits a string into elements using a provided delimiter + * + * @s: the string to split + * @delim: the delimiter to use + * @elems: the string vector to populate + * @mode: the SplitMode to use + * + * Using @delim to determine field boundaries, splits @s into separate + * string elements. These elements are returned in the string vector + * @elems. As long as @s is non-empty, there will be at least one + * element in @elems. + */ + static void split(const std::string& src, char delim, + std::vector<std::string>& elems, + Util::SplitMode mode); + /** + * get_timestamp_us() - Returns the current time in microseconds + */ + static uint64_t get_timestamp_us(); + /** + * get_resource() - Gets an input filestream for a given file. + * + * @path: the path to the file + * + * Returns a pointer to an input stream, which must be deleted when no + * longer in use. + */ + static std::istream *get_resource(const std::string &path); + /** + * list_files() - Get a list of the files in a given directory. + * + * @dirName: the directory path to be listed. + * @fileVec: the string vector to populate. + * + * Obtains a list of the files in @dirName, and returns them in the string + * vector @fileVec. + */ + static void list_files(const std::string& dirName, std::vector<std::string>& fileVec); + /** + * dispose_pointer_vector() - cleans up a vector of pointers + * + * @vec: vector of pointers to objects or plain-old-data + * + * Iterates across @vec and deletes the data pointed to by each of the + * elements. Clears the vector, resetting it for reuse. + */ + template <class T> static void dispose_pointer_vector(std::vector<T*> &vec) + { + for (typename std::vector<T*>::const_iterator iter = vec.begin(); + iter != vec.end(); + iter++) + { + delete *iter; + } + + vec.clear(); + } + /** + * toString() - Converts a string to a plain-old-data type. + * + * @asString: a string representation of plain-old-data. + */ + template<typename T> + static T + fromString(const std::string& asString) + { + std::stringstream ss(asString); + T retVal = T(); + ss >> retVal; + return retVal; + } + /** + * toString() - Converts a plain-old-data type to a string. + * + * @t: a simple value to be converted to a string + */ + template<typename T> + static std::string + toString(const T t) + { + std::stringstream ss; + ss << t; + return ss.str(); + } + /** + * appname_from_path() - get the name of an executable from an absolute path + * + * @path: absolute path of the running application (argv[0]) + * + * Returns the last portion of @path (everything after the final '/'). + */ + static std::string + appname_from_path(const std::string& path); + +#ifdef ANDROID + static void android_set_asset_manager(AAssetManager *asset_manager); + static AAssetManager *android_get_asset_manager(void); +private: + static AAssetManager *android_asset_manager; +#endif +}; + +#endif /* UTIL_H */ diff --git a/src/libmatrix/vec.h b/src/libmatrix/vec.h new file mode 100644 index 0000000..2680ebc --- /dev/null +++ b/src/libmatrix/vec.h @@ -0,0 +1,716 @@ +// +// Copyright (c) 2010-2011 Linaro Limited +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License which accompanies +// this distribution, and is available at +// http://www.opensource.org/licenses/mit-license.php +// +// Contributors: +// Jesse Barker - original implementation. +// +#ifndef VEC_H_ +#define VEC_H_ + +#include <iostream> // only needed for print() functions... +#include <math.h> + +namespace LibMatrix +{ +// A template class for creating, managing and operating on a 2-element vector +// of any type you like (intended for built-in types, but as long as it +// supports the basic arithmetic and assignment operators, any type should +// work). +template<typename T> +class tvec2 +{ +public: + tvec2() : + x_(0), + y_(0) {} + tvec2(const T t) : + x_(t), + y_(t) {} + tvec2(const T x, const T y) : + x_(x), + y_(y) {} + tvec2(const tvec2& v) : + x_(v.x_), + y_(v.y_) {} + ~tvec2() {} + + // Print the elements of the vector to standard out. + // Really only useful for debug and test. + void print() const + { + std::cout << "| " << x_ << " " << y_ << " |" << std::endl; + } + + // Allow raw data access for API calls and the like. + // For example, it is valid to pass a tvec2<float> into a call to + // the OpenGL command "glUniform2fv()". + operator const T*() const { return &x_;} + + // Get and set access members for the individual elements. + const T x() const { return x_; } + const T y() const { return y_; } + + void x(const T& val) { x_ = val; } + void y(const T& val) { y_ = val; } + + // A direct assignment of 'rhs' to this. Return a reference to this. + tvec2& operator=(const tvec2& rhs) + { + if (this != &rhs) + { + x_ = rhs.x_; + y_ = rhs.y_; + } + return *this; + } + + // Divide this by a scalar. Return a reference to this. + tvec2& operator/=(const T& rhs) + { + x_ /= rhs; + y_ /= rhs; + return *this; + } + + // Divide a copy of this by a scalar. Return the copy. + const tvec2 operator/(const T& rhs) const + { + return tvec2(*this) /= rhs; + } + + // Component-wise divide of this by another vector. + // Return a reference to this. + tvec2& operator/=(const tvec2& rhs) + { + x_ /= rhs.x_; + y_ /= rhs.y_; + return *this; + } + + // Component-wise divide of a copy of this by another vector. + // Return the copy. + const tvec2 operator/(const tvec2& rhs) const + { + return tvec2(*this) /= rhs; + } + + // Multiply this by a scalar. Return a reference to this. + tvec2& operator*=(const T& rhs) + { + x_ *= rhs; + y_ *= rhs; + return *this; + } + + // Multiply a copy of this by a scalar. Return the copy. + const tvec2 operator*(const T& rhs) const + { + return tvec2(*this) *= rhs; + } + + // Component-wise multiply of this by another vector. + // Return a reference to this. + tvec2& operator*=(const tvec2& rhs) + { + x_ *= rhs.x_; + y_ *= rhs.y_; + return *this; + } + + // Component-wise multiply of a copy of this by another vector. + // Return the copy. + const tvec2 operator*(const tvec2& rhs) const + { + return tvec2(*this) *= rhs; + } + + // Add a scalar to this. Return a reference to this. + tvec2& operator+=(const T& rhs) + { + x_ += rhs; + y_ += rhs; + return *this; + } + + // Add a scalar to a copy of this. Return the copy. + const tvec2 operator+(const T& rhs) const + { + return tvec2(*this) += rhs; + } + + // Component-wise addition of another vector to this. + // Return a reference to this. + tvec2& operator+=(const tvec2& rhs) + { + x_ += rhs.x_; + y_ += rhs.y_; + return *this; + } + + // Component-wise addition of another vector to a copy of this. + // Return the copy. + const tvec2 operator+(const tvec2& rhs) const + { + return tvec2(*this) += rhs; + } + + // Subtract a scalar from this. Return a reference to this. + tvec2& operator-=(const T& rhs) + { + x_ -= rhs; + y_ -= rhs; + return *this; + } + + // Subtract a scalar from a copy of this. Return the copy. + const tvec2 operator-(const T& rhs) const + { + return tvec2(*this) -= rhs; + } + + // Component-wise subtraction of another vector from this. + // Return a reference to this. + tvec2& operator-=(const tvec2& rhs) + { + x_ -= rhs.x_; + y_ -= rhs.y_; + return *this; + } + + // Component-wise subtraction of another vector from a copy of this. + // Return the copy. + const tvec2 operator-(const tvec2& rhs) const + { + return tvec2(*this) -= rhs; + } + + // Compute the length of this and return it. + float length() const + { + return sqrt(dot(*this, *this)); + } + + // Make this a unit vector. + void normalize() + { + float l = length(); + x_ /= l; + y_ /= l; + } + + // Compute the dot product of two vectors. + static T dot(const tvec2& v1, const tvec2& v2) + { + return (v1.x_ * v2.x_) + (v1.y_ * v2.y_); + } + +private: + T x_; + T y_; +}; + +// A template class for creating, managing and operating on a 3-element vector +// of any type you like (intended for built-in types, but as long as it +// supports the basic arithmetic and assignment operators, any type should +// work). +template<typename T> +class tvec3 +{ +public: + tvec3() : + x_(0), + y_(0), + z_(0) {} + tvec3(const T t) : + x_(t), + y_(t), + z_(t) {} + tvec3(const T x, const T y, const T z) : + x_(x), + y_(y), + z_(z) {} + tvec3(const tvec3& v) : + x_(v.x_), + y_(v.y_), + z_(v.z_) {} + ~tvec3() {} + + // Print the elements of the vector to standard out. + // Really only useful for debug and test. + void print() const + { + std::cout << "| " << x_ << " " << y_ << " " << z_ << " |" << std::endl; + } + + // Allow raw data access for API calls and the like. + // For example, it is valid to pass a tvec3<float> into a call to + // the OpenGL command "glUniform3fv()". + operator const T*() const { return &x_;} + + // Get and set access members for the individual elements. + const T x() const { return x_; } + const T y() const { return y_; } + const T z() const { return z_; } + + void x(const T& val) { x_ = val; } + void y(const T& val) { y_ = val; } + void z(const T& val) { z_ = val; } + + // A direct assignment of 'rhs' to this. Return a reference to this. + tvec3& operator=(const tvec3& rhs) + { + if (this != &rhs) + { + x_ = rhs.x_; + y_ = rhs.y_; + z_ = rhs.z_; + } + return *this; + } + + // Divide this by a scalar. Return a reference to this. + tvec3& operator/=(const T& rhs) + { + x_ /= rhs; + y_ /= rhs; + z_ /= rhs; + return *this; + } + + // Divide a copy of this by a scalar. Return the copy. + const tvec3 operator/(const T& rhs) const + { + return tvec3(*this) /= rhs; + } + + // Component-wise divide of this by another vector. + // Return a reference to this. + tvec3& operator/=(const tvec3& rhs) + { + x_ /= rhs.x_; + y_ /= rhs.y_; + z_ /= rhs.z_; + return *this; + } + + // Component-wise divide of a copy of this by another vector. + // Return the copy. + const tvec3 operator/(const tvec3& rhs) const + { + return tvec3(*this) /= rhs; + } + + // Multiply this by a scalar. Return a reference to this. + tvec3& operator*=(const T& rhs) + { + x_ *= rhs; + y_ *= rhs; + z_ *= rhs; + return *this; + } + + // Multiply a copy of this by a scalar. Return the copy. + const tvec3 operator*(const T& rhs) const + { + return tvec3(*this) *= rhs; + } + + // Component-wise multiply of this by another vector. + // Return a reference to this. + tvec3& operator*=(const tvec3& rhs) + { + x_ *= rhs.x_; + y_ *= rhs.y_; + z_ *= rhs.z_; + return *this; + } + + // Component-wise multiply of a copy of this by another vector. + // Return the copy. + const tvec3 operator*(const tvec3& rhs) const + { + return tvec3(*this) *= rhs; + } + + // Add a scalar to this. Return a reference to this. + tvec3& operator+=(const T& rhs) + { + x_ += rhs; + y_ += rhs; + z_ += rhs; + return *this; + } + + // Add a scalar to a copy of this. Return the copy. + const tvec3 operator+(const T& rhs) const + { + return tvec3(*this) += rhs; + } + + // Component-wise addition of another vector to this. + // Return a reference to this. + tvec3& operator+=(const tvec3& rhs) + { + x_ += rhs.x_; + y_ += rhs.y_; + z_ += rhs.z_; + return *this; + } + + // Component-wise addition of another vector to a copy of this. + // Return the copy. + const tvec3 operator+(const tvec3& rhs) const + { + return tvec3(*this) += rhs; + } + + // Subtract a scalar from this. Return a reference to this. + tvec3& operator-=(const T& rhs) + { + x_ -= rhs; + y_ -= rhs; + z_ -= rhs; + return *this; + } + + // Subtract a scalar from a copy of this. Return the copy. + const tvec3 operator-(const T& rhs) const + { + return tvec3(*this) -= rhs; + } + + // Component-wise subtraction of another vector from this. + // Return a reference to this. + tvec3& operator-=(const tvec3& rhs) + { + x_ -= rhs.x_; + y_ -= rhs.y_; + z_ -= rhs.z_; + return *this; + } + + // Component-wise subtraction of another vector from a copy of this. + // Return the copy. + const tvec3 operator-(const tvec3& rhs) const + { + return tvec3(*this) -= rhs; + } + + // Compute the length of this and return it. + float length() const + { + return sqrt(dot(*this, *this)); + } + + // Make this a unit vector. + void normalize() + { + float l = length(); + x_ /= l; + y_ /= l; + z_ /= l; + } + + // Compute the dot product of two vectors. + static T dot(const tvec3& v1, const tvec3& v2) + { + return (v1.x_ * v2.x_) + (v1.y_ * v2.y_) + (v1.z_ * v2.z_); + } + + // Compute the cross product of two vectors. + static tvec3 cross(const tvec3& u, const tvec3& v) + { + return tvec3((u.y_ * v.z_) - (u.z_ * v.y_), + (u.z_ * v.x_) - (u.x_ * v.z_), + (u.x_ * v.y_) - (u.y_ * v.x_)); + } + +private: + T x_; + T y_; + T z_; +}; + +// A template class for creating, managing and operating on a 4-element vector +// of any type you like (intended for built-in types, but as long as it +// supports the basic arithmetic and assignment operators, any type should +// work). +template<typename T> +class tvec4 +{ +public: + tvec4() : + x_(0), + y_(0), + z_(0), + w_(0) {} + tvec4(const T t) : + x_(t), + y_(t), + z_(t), + w_(t) {} + tvec4(const T x, const T y, const T z, const T w) : + x_(x), + y_(y), + z_(z), + w_(w) {} + tvec4(const tvec4& v) : + x_(v.x_), + y_(v.y_), + z_(v.z_), + w_(v.w_) {} + ~tvec4() {} + + // Print the elements of the vector to standard out. + // Really only useful for debug and test. + void print() const + { + std::cout << "| " << x_ << " " << y_ << " " << z_ << " " << w_ << " |" << std::endl; + } + + // Allow raw data access for API calls and the like. + // For example, it is valid to pass a tvec4<float> into a call to + // the OpenGL command "glUniform4fv()". + operator const T*() const { return &x_;} + + // Get and set access members for the individual elements. + const T x() const { return x_; } + const T y() const { return y_; } + const T z() const { return z_; } + const T w() const { return w_; } + + void x(const T& val) { x_ = val; } + void y(const T& val) { y_ = val; } + void z(const T& val) { z_ = val; } + void w(const T& val) { w_ = val; } + + // A direct assignment of 'rhs' to this. Return a reference to this. + tvec4& operator=(const tvec4& rhs) + { + if (this != &rhs) + { + x_ = rhs.x_; + y_ = rhs.y_; + z_ = rhs.z_; + w_ = rhs.w_; + } + return *this; + } + + // Divide this by a scalar. Return a reference to this. + tvec4& operator/=(const T& rhs) + { + x_ /= rhs; + y_ /= rhs; + z_ /= rhs; + w_ /= rhs; + return *this; + } + + // Divide a copy of this by a scalar. Return the copy. + const tvec4 operator/(const T& rhs) const + { + return tvec4(*this) /= rhs; + } + + // Component-wise divide of this by another vector. + // Return a reference to this. + tvec4& operator/=(const tvec4& rhs) + { + x_ /= rhs.x_; + y_ /= rhs.y_; + z_ /= rhs.z_; + w_ /= rhs.w_; + return *this; + } + + // Component-wise divide of a copy of this by another vector. + // Return the copy. + const tvec4 operator/(const tvec4& rhs) const + { + return tvec4(*this) /= rhs; + } + + // Multiply this by a scalar. Return a reference to this. + tvec4& operator*=(const T& rhs) + { + x_ *= rhs; + y_ *= rhs; + z_ *= rhs; + w_ *= rhs; + return *this; + } + + // Multiply a copy of this by a scalar. Return the copy. + const tvec4 operator*(const T& rhs) const + { + return tvec4(*this) *= rhs; + } + + // Component-wise multiply of this by another vector. + // Return a reference to this. + tvec4& operator*=(const tvec4& rhs) + { + x_ *= rhs.x_; + y_ *= rhs.y_; + z_ *= rhs.z_; + w_ *= rhs.w_; + return *this; + } + + // Component-wise multiply of a copy of this by another vector. + // Return the copy. + const tvec4 operator*(const tvec4& rhs) const + { + return tvec4(*this) *= rhs; + } + + // Add a scalar to this. Return a reference to this. + tvec4& operator+=(const T& rhs) + { + x_ += rhs; + y_ += rhs; + z_ += rhs; + w_ += rhs; + return *this; + } + + // Add a scalar to a copy of this. Return the copy. + const tvec4 operator+(const T& rhs) const + { + return tvec4(*this) += rhs; + } + + // Component-wise addition of another vector to this. + // Return a reference to this. + tvec4& operator+=(const tvec4& rhs) + { + x_ += rhs.x_; + y_ += rhs.y_; + z_ += rhs.z_; + w_ += rhs.w_; + return *this; + } + + // Component-wise addition of another vector to a copy of this. + // Return the copy. + const tvec4 operator+(const tvec4& rhs) const + { + return tvec4(*this) += rhs; + } + + // Subtract a scalar from this. Return a reference to this. + tvec4& operator-=(const T& rhs) + { + x_ -= rhs; + y_ -= rhs; + z_ -= rhs; + w_ -= rhs; + return *this; + } + + // Subtract a scalar from a copy of this. Return the copy. + const tvec4 operator-(const T& rhs) const + { + return tvec4(*this) -= rhs; + } + + // Component-wise subtraction of another vector from this. + // Return a reference to this. + tvec4& operator-=(const tvec4& rhs) + { + x_ -= rhs.x_; + y_ -= rhs.y_; + z_ -= rhs.z_; + w_ -= rhs.w_; + return *this; + } + + // Component-wise subtraction of another vector from a copy of this. + // Return the copy. + const tvec4 operator-(const tvec4& rhs) const + { + return tvec4(*this) -= rhs; + } + + // Compute the length of this and return it. + float length() const + { + return sqrt(dot(*this, *this)); + } + + // Make this a unit vector. + void normalize() + { + float l = length(); + x_ /= l; + y_ /= l; + z_ /= l; + w_ /= l; + } + + // Compute the dot product of two vectors. + static T dot(const tvec4& v1, const tvec4& v2) + { + return (v1.x_ * v2.x_) + (v1.y_ * v2.y_) + (v1.z_ * v2.z_) + (v1.w_ * v2.w_); + } + +private: + T x_; + T y_; + T z_; + T w_; +}; + +// +// Convenience typedefs. These are here to present a homogeneous view of these +// objects with respect to shader source. +// +typedef tvec2<float> vec2; +typedef tvec3<float> vec3; +typedef tvec4<float> vec4; + +typedef tvec2<double> dvec2; +typedef tvec3<double> dvec3; +typedef tvec4<double> dvec4; + +typedef tvec2<int> ivec2; +typedef tvec3<int> ivec3; +typedef tvec4<int> ivec4; + +typedef tvec2<unsigned int> uvec2; +typedef tvec3<unsigned int> uvec3; +typedef tvec4<unsigned int> uvec4; + +typedef tvec2<bool> bvec2; +typedef tvec3<bool> bvec3; +typedef tvec4<bool> bvec4; + +} // namespace LibMatrix + +// Global operators to allow for things like defining a new vector in terms of +// a product of a scalar and a vector +template<typename T> +const LibMatrix::tvec2<T> operator*(const T t, const LibMatrix::tvec2<T>& v) +{ + return v * t; +} + +template<typename T> +const LibMatrix::tvec3<T> operator*(const T t, const LibMatrix::tvec3<T>& v) +{ + return v * t; +} + +template<typename T> +const LibMatrix::tvec4<T> operator*(const T t, const LibMatrix::tvec4<T>& v) +{ + return v * t; +} + +#endif // VEC_H_ |