/*
Copyright (C) 2010, Heikki Salo
All rights reserved.
Distributed under the BSD license:
http://www.opensource.org/licenses/bsd-license.php
*/
#include "stdafx.h"
#include "DPMatrix.hpp"
#include "DPUtils.hpp"
DPMatrix::DPMatrix(python::object& source)
{
python::extract<DPMatrix&> isMatrix(source);
if (isMatrix.check()) {
*this = isMatrix;
}
else {
fromSequence(source);
}
}
void DPMatrix::fromSequence(python::object& source)
{
const Py_ssize_t size = python::len(source);
if (size == 4) {
//Assume nested sequences
for (UINT row=0; row < 4; ++row) {
for (UINT col=0; col < 4; ++col) {
matrix(row, col) = python::extract<float>(source[row][col]);
}
}
}
else if (size == 16) {
//A sequence
for (UINT i=0; i < 16; ++i) {
UINT row = i / 4;
UINT col = i % 4;
matrix(row, col) = python::extract<float>(source[i]);
}
}
else {
DPThrow("Unknown sequence argument");
}
}
void DPMatrix::identity()
{
matrix = XMMatrixIdentity();
}
void DPMatrix::scale(python::object& s)
{
XMVECTOR v = PyToXMV(s);
matrix = XMMatrixScaling(v.x, v.y, v.z);
}
void DPMatrix::rotate(python::object& r)
{
XMVECTOR v = PyToXMV(r);
matrix = XMMatrixRotationRollPitchYaw(v.x, v.y, v.z);
}
void DPMatrix::translate(python::object& t)
{
XMVECTOR v = PyToXMV(t);
matrix = XMMatrixTranslation(v.x, v.y, v.z);
}
void DPMatrix::transpose()
{
matrix = XMMatrixTranspose(matrix);
}
void DPMatrix::transformation(python::object& tr, python::object& rot, python::object& scale,
python::object& rorigin, python::object& sorigin, python::object& sorientation)
{
XMVECTOR trans = PyToXMV(tr);
XMVECTOR rotQuat= XMQuaternionRotationRollPitchYawFromVector(PyToXMV(rot));
XMVECTOR scaling = PyToXMV(scale);
XMVECTOR rotOrigin = PyToXMV(rorigin);
XMVECTOR scalingOrigin = PyToXMV(sorigin);
XMVECTOR scalingOrientationQuat = XMQuaternionRotationRollPitchYawFromVector(PyToXMV(sorientation));
matrix = XMMatrixTransformation(scalingOrigin, scalingOrientationQuat,
scaling, rotOrigin, rotQuat, trans);
}
float DPMatrix::inverse()
{
XMVECTOR determinant;
XMMATRIX inversed = XMMatrixInverse(&determinant, matrix);
if (XMMatrixIsInfinite(inversed))
DPThrow("Inversion failed");
matrix = inversed;
return determinant.x;
}
DPVector DPMatrix::transformVector(python::object& pyvector)
{
XMVECTOR v = PyToXMV(pyvector);
return XMVector3TransformCoord(v, matrix);
}
DPVector DPMatrix::transformNormal(python::object& pyvector)
{
XMVECTOR v = PyToXMV(pyvector);
return XMVector3TransformNormal(v, matrix);
}
void DPMatrix::lookAtLH(python::object& pEye, python::object& pLookat, python::object& pUp)
{
XMVECTOR eye = PyToXMV(pEye);
XMVECTOR lookat = PyToXMV(pLookat);
XMVECTOR up = PyToXMV(pUp);
matrix = XMMatrixLookAtLH(eye, lookat, up);
}
void DPMatrix::perspectiveFovLH(float fovy, float aspect, float zmin, float zmax)
{
matrix = XMMatrixPerspectiveFovLH(fovy, aspect, zmin, zmax);
}
void DPMatrix::orthoLH(float w, float h, float zmin, float zmax)
{
matrix = XMMatrixOrthographicLH(w, h, zmin, zmax);
}
DPMatrixRow DPMatrix::getitem(UINT row)
{
if (row >= 4)
throw std::out_of_range("Invalid row index");
//Works when using "plain" matrices. The row
//will know what data to modify and 'with_custodian_and_ward_postcall'
//is used to make sure that the matrix does not go away while any rows
//are still alive.
DPMatrixRow matrixRow;
matrixRow.data = ((float*)&matrix._11) + (row * 4);
return matrixRow;
}
float DPMatrix::getitem_pair(python::tuple& coords)
{
UINT row = python::extract<UINT>(coords[0]);
UINT col = python::extract<UINT>(coords[1]);
if (row >= 4 || col >= 4)
throw std::out_of_range("Invalid index");
return matrix(row, col);
}
void DPMatrix::setitem_pair(python::object& coords, float value)
{
UINT row = python::extract<UINT>(coords[0]);
UINT col = python::extract<UINT>(coords[1]);
if (row >= 4 || col >= 4)
throw std::out_of_range("Invalid index");
matrix(row, col) = value;
}
python::str DPMatrix::str()
{
std::ostringstream ss;
ss << std::fixed;
for (UINT row=0; row < 4; ++row) {
ss << '[';
for (UINT col=0; col < 4; ++col) {
ss << matrix(row, col);
if (col < 3)
ss << ", ";
}
ss << "]\n";
}
return python::str(ss.str().c_str());
}
python::object DPMatrix::toBytes() const
{
PyObject* value = PyBytes_FromStringAndSize((char*)&matrix, sizeof(matrix));
if (value == 0)
python::throw_error_already_set();
return python::object(python::handle<>(value));
}
python::object DPMatrix::toList() const
{
python::list result;
for (UINT row=0; row < 4; ++row) {
for (UINT col=0; col < 4; ++col) {
result.append(matrix(row, col));
}
}
return result;
}
struct DPMatrixPickleSuite : python::pickle_suite {
static python::tuple getinitargs(DPMatrix const& m)
{
return python::make_tuple(m.toList());
}
};
void ExportDPMatrix()
{
using namespace boost::python;
python::object zeroTuple = make_tuple(0, 0, 0);
python::object oneTuple = make_tuple(1, 1, 1);
class_<DPMatrix>("Matrix")
.def(init<python::object&>())
.def("toBytes", &DPMatrix::toBytes) //Undocumented.
.def("toList", &DPMatrix::toList)
.def("identity", &DPMatrix::identity)
.def("invert", &DPMatrix::inverse)
.def("scale", &DPMatrix::scale)
.def("rotate", &DPMatrix::rotate)
.def("translate", &DPMatrix::translate)
.def("transpose", &DPMatrix::transpose)
.def("transformation", &DPMatrix::transformation, (arg("translation") = zeroTuple,
arg("rotation") = zeroTuple, arg("scaling") = oneTuple, arg("rotorigin") = zeroTuple,
arg("scaleorigin") = zeroTuple, arg("scaleorientation") = zeroTuple ))
.def("transformVector", &DPMatrix::transformVector)
.def("transformNormal", &DPMatrix::transformNormal)
.def("lookAt", &DPMatrix::lookAtLH)
.def("perspectiveFov", &DPMatrix::perspectiveFovLH)
.def("orthographic", &DPMatrix::orthoLH)
.def("__str__", &DPMatrix::str)
.def("__getitem__", &DPMatrix::getitem, with_custodian_and_ward_postcall<0, 1>())
.def("__getitem__", &DPMatrix::getitem_pair)
.def("__setitem__", &DPMatrix::setitem_pair)
.def_pickle(DPMatrixPickleSuite())
.def(self * self)
.def(self *= self)
;
class_<DPMatrixRow>("MatrixRow")
.def("__getitem__", &DPMatrixRow::getitem)
.def("__setitem__", &DPMatrixRow::setitem)
;
}