From ba53125472a21a664de65e1bc5a1594d030034a3 Mon Sep 17 00:00:00 2001 From: DEC05EBA Date: Mon, 16 Dec 2019 00:26:22 +0100 Subject: initial commit --- .gitignore | 5 + LICENSE | 57 + README.md | 3 + config/hellovr_actions.json | 59 + config/hellovr_bindings_generic.json | 94 ++ config/hellovr_bindings_vive_controller.json | 94 ++ project.conf | 14 + shared/Matrices.cpp | 581 +++++++ shared/Matrices.h | 909 +++++++++++ shared/Vectors.h | 530 +++++++ src/main.cpp | 2130 ++++++++++++++++++++++++++ tests/main.cpp | 7 + 12 files changed, 4483 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 config/hellovr_actions.json create mode 100644 config/hellovr_bindings_generic.json create mode 100644 config/hellovr_bindings_vive_controller.json create mode 100644 project.conf create mode 100644 shared/Matrices.cpp create mode 100644 shared/Matrices.h create mode 100644 shared/Vectors.h create mode 100644 src/main.cpp create mode 100644 tests/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..636c6b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Compiled sibs files +sibs-build/ +compile_commands.json +tests/sibs-build/ +tests/compile_commands.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c6b1dc1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,57 @@ +Original license, from openvr samples (https://github.com/ValveSoftware/openvr/tree/master/samples): +Copyright (c) 2015, Valve Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License of modified code: +Copyright (c) 2019, DEC05EBA +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e4c1f1 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +A virtual reality window manager for Linux running X11, based on Valve's openvr `hellovr_opengl` sample code: https://github.com/ValveSoftware/openvr/tree/master/samples\ +Still early in development, not ready to be used yet. + diff --git a/config/hellovr_actions.json b/config/hellovr_actions.json new file mode 100644 index 0000000..dab1e9a --- /dev/null +++ b/config/hellovr_actions.json @@ -0,0 +1,59 @@ +{ + "default_bindings": [ + { + "controller_type" : "vive_controller", + "binding_url" : "hellovr_bindings_vive_controller.json" + }, + { + "controller_type" : "generic", + "binding_url" : "hellovr_bindings_generic.json" + } + ], + "actions": [ + { + "name": "/actions/demo/in/HideCubes", + "type": "boolean" + }, + { + "name": "/actions/demo/in/HideThisController", + "type": "boolean" + }, + { + "name": "/actions/demo/in/triggerhaptic", + "type": "boolean" + }, + { + "name": "/actions/demo/in/AnalogInput", + "type": "vector2" + }, + { + "name": "/actions/demo/in/Hand_Right", + "type": "pose" + }, + { + "name": "/actions/demo/in/Hand_Left", + "type": "pose" + }, + { + "name": "/actions/demo/out/haptic_left", + "type": "vibration" + }, + { + "name": "/actions/demo/out/haptic_right", + "type": "vibration" + } + ], + "localization" : [ + { + "language_tag": "en_US", + "/actions/demo/in/HideCubes" : "Hide Cubes", + "/actions/demo/in/HideThisController" : "Hide this Controller", + "/actions/demo/in/triggerhaptic" : "Trigger Haptic Pulse", + "/actions/demo/in/AnalogInput" : "Analog Input", + "/actions/demo/in/Hand_Right" : "Right Hand Pose", + "/actions/demo/in/Hand_Left" : "Left Hand Pose", + "/actions/demo/out/haptic_left" : "Left Haptic Feedback", + "/actions/demo/out/haptic_right" : "Right Haptic Feedback" + } + ] +} diff --git a/config/hellovr_bindings_generic.json b/config/hellovr_bindings_generic.json new file mode 100644 index 0000000..ae5fb46 --- /dev/null +++ b/config/hellovr_bindings_generic.json @@ -0,0 +1,94 @@ +{ + "bindings" : { + "/actions/demo" : { + "poses" : [ + { + "output" : "/actions/demo/in/hand_left", + "path" : "/user/hand/left/pose/raw" + }, + { + "output" : "/actions/demo/in/hand_right", + "path" : "/user/hand/right/pose/raw" + } + ], + "haptics" : [ + { + "output" : "/actions/demo/out/haptic_right", + "path" : "/user/hand/right/output/haptic" + }, + { + "output" : "/actions/demo/out/haptic_left", + "path" : "/user/hand/left/output/haptic" + } + ], + "sources" : [ + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidecubes" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/trigger" + }, + { + "inputs" : { + "position" : { + "output" : "/actions/demo/in/analoginput" + } + }, + "mode" : "trackpad", + "path" : "/user/hand/right/input/trackpad" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/triggerhaptic" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/grip" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidecubes" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/trigger" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/triggerhaptic" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/grip" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidethiscontroller" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/application_menu" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidethiscontroller" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/application_menu" + } + ] + } + }, + "controller_type" : "generic", + "description" : "Bindings for the OpenVR SDK \"hellovr_opengl\" demo for a generic controller", + "name" : "HelloVR bindings for a generic controller" +} diff --git a/config/hellovr_bindings_vive_controller.json b/config/hellovr_bindings_vive_controller.json new file mode 100644 index 0000000..0a95efd --- /dev/null +++ b/config/hellovr_bindings_vive_controller.json @@ -0,0 +1,94 @@ +{ + "bindings" : { + "/actions/demo" : { + "poses" : [ + { + "output" : "/actions/demo/in/hand_left", + "path" : "/user/hand/left/pose/raw" + }, + { + "output" : "/actions/demo/in/hand_right", + "path" : "/user/hand/right/pose/raw" + } + ], + "haptics" : [ + { + "output" : "/actions/demo/out/haptic_right", + "path" : "/user/hand/right/output/haptic" + }, + { + "output" : "/actions/demo/out/haptic_left", + "path" : "/user/hand/left/output/haptic" + } + ], + "sources" : [ + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidecubes" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/trigger" + }, + { + "inputs" : { + "position" : { + "output" : "/actions/demo/in/analoginput" + } + }, + "mode" : "trackpad", + "path" : "/user/hand/right/input/trackpad" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/triggerhaptic" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/grip" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidecubes" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/trigger" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/triggerhaptic" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/grip" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidethiscontroller" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/application_menu" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/demo/in/hidethiscontroller" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/application_menu" + } + ] + } + }, + "controller_type" : "vive_controller", + "description" : "Bindings for the OpenVR SDK \"hellovr_opengl\" demo for the Vive controller", + "name" : "HelloVR bindings for Vive Controller" +} diff --git a/project.conf b/project.conf new file mode 100644 index 0000000..559a408 --- /dev/null +++ b/project.conf @@ -0,0 +1,14 @@ +[package] +name = "vr_window_manager" +type = "executable" +version = "0.1.0" +platforms = ["linux"] + +[dependencies] +glm = "0" +glx = "1" +glew = "2" +sdl2 = "2" +openvr = "1" +x11 = "1" +xcomposite = ">=0.2" diff --git a/shared/Matrices.cpp b/shared/Matrices.cpp new file mode 100644 index 0000000..582b285 --- /dev/null +++ b/shared/Matrices.cpp @@ -0,0 +1,581 @@ +/////////////////////////////////////////////////////////////////////////////// +// Matrice.cpp +// =========== +// NxN Matrix Math classes +// +// The elements of the matrix are stored as column major order. +// | 0 2 | | 0 3 6 | | 0 4 8 12 | +// | 1 3 | | 1 4 7 | | 1 5 9 13 | +// | 2 5 8 | | 2 6 10 14 | +// | 3 7 11 15 | +// +// AUTHOR: Song Ho Ahn (song.ahn@gmail.com) +// CREATED: 2005-06-24 +// UPDATED: 2014-09-21 +// +// Copyright (C) 2005 Song Ho Ahn +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "Matrices.h" + +const float DEG2RAD = 3.141593f / 180; +const float EPSILON = 0.00001f; + + + +/////////////////////////////////////////////////////////////////////////////// +// transpose 2x2 matrix +/////////////////////////////////////////////////////////////////////////////// +Matrix2& Matrix2::transpose() +{ + std::swap(m[1], m[2]); + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// return the determinant of 2x2 matrix +/////////////////////////////////////////////////////////////////////////////// +float Matrix2::getDeterminant() +{ + return m[0] * m[3] - m[1] * m[2]; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// inverse of 2x2 matrix +// If cannot find inverse, set identity matrix +/////////////////////////////////////////////////////////////////////////////// +Matrix2& Matrix2::invert() +{ + float determinant = getDeterminant(); + if(fabs(determinant) <= EPSILON) + { + return identity(); + } + + float tmp = m[0]; // copy the first element + float invDeterminant = 1.0f / determinant; + m[0] = invDeterminant * m[3]; + m[1] = -invDeterminant * m[1]; + m[2] = -invDeterminant * m[2]; + m[3] = invDeterminant * tmp; + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// transpose 3x3 matrix +/////////////////////////////////////////////////////////////////////////////// +Matrix3& Matrix3::transpose() +{ + std::swap(m[1], m[3]); + std::swap(m[2], m[6]); + std::swap(m[5], m[7]); + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// return determinant of 3x3 matrix +/////////////////////////////////////////////////////////////////////////////// +float Matrix3::getDeterminant() +{ + return m[0] * (m[4] * m[8] - m[5] * m[7]) - + m[1] * (m[3] * m[8] - m[5] * m[6]) + + m[2] * (m[3] * m[7] - m[4] * m[6]); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// inverse 3x3 matrix +// If cannot find inverse, set identity matrix +/////////////////////////////////////////////////////////////////////////////// +Matrix3& Matrix3::invert() +{ + float determinant, invDeterminant; + float tmp[9]; + + tmp[0] = m[4] * m[8] - m[5] * m[7]; + tmp[1] = m[2] * m[7] - m[1] * m[8]; + tmp[2] = m[1] * m[5] - m[2] * m[4]; + tmp[3] = m[5] * m[6] - m[3] * m[8]; + tmp[4] = m[0] * m[8] - m[2] * m[6]; + tmp[5] = m[2] * m[3] - m[0] * m[5]; + tmp[6] = m[3] * m[7] - m[4] * m[6]; + tmp[7] = m[1] * m[6] - m[0] * m[7]; + tmp[8] = m[0] * m[4] - m[1] * m[3]; + + // check determinant if it is 0 + determinant = m[0] * tmp[0] + m[1] * tmp[3] + m[2] * tmp[6]; + if(fabs(determinant) <= EPSILON) + { + return identity(); // cannot inverse, make it idenety matrix + } + + // divide by the determinant + invDeterminant = 1.0f / determinant; + m[0] = invDeterminant * tmp[0]; + m[1] = invDeterminant * tmp[1]; + m[2] = invDeterminant * tmp[2]; + m[3] = invDeterminant * tmp[3]; + m[4] = invDeterminant * tmp[4]; + m[5] = invDeterminant * tmp[5]; + m[6] = invDeterminant * tmp[6]; + m[7] = invDeterminant * tmp[7]; + m[8] = invDeterminant * tmp[8]; + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// transpose 4x4 matrix +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::transpose() +{ + std::swap(m[1], m[4]); + std::swap(m[2], m[8]); + std::swap(m[3], m[12]); + std::swap(m[6], m[9]); + std::swap(m[7], m[13]); + std::swap(m[11], m[14]); + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// inverse 4x4 matrix +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::invert() +{ + // If the 4th row is [0,0,0,1] then it is affine matrix and + // it has no projective transformation. + if(m[3] == 0 && m[7] == 0 && m[11] == 0 && m[15] == 1) + this->invertAffine(); + else + { + this->invertGeneral(); + /*@@ invertProjective() is not optimized (slower than generic one) + if(fabs(m[0]*m[5] - m[1]*m[4]) > EPSILON) + this->invertProjective(); // inverse using matrix partition + else + this->invertGeneral(); // generalized inverse + */ + } + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// compute the inverse of 4x4 Euclidean transformation matrix +// +// Euclidean transformation is translation, rotation, and reflection. +// With Euclidean transform, only the position and orientation of the object +// will be changed. Euclidean transform does not change the shape of an object +// (no scaling). Length and angle are reserved. +// +// Use inverseAffine() if the matrix has scale and shear transformation. +// +// M = [ R | T ] +// [ --+-- ] (R denotes 3x3 rotation/reflection matrix) +// [ 0 | 1 ] (T denotes 1x3 translation matrix) +// +// y = M*x -> y = R*x + T -> x = R^-1*(y - T) -> x = R^T*y - R^T*T +// (R is orthogonal, R^-1 = R^T) +// +// [ R | T ]-1 [ R^T | -R^T * T ] (R denotes 3x3 rotation matrix) +// [ --+-- ] = [ ----+--------- ] (T denotes 1x3 translation) +// [ 0 | 1 ] [ 0 | 1 ] (R^T denotes R-transpose) +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::invertEuclidean() +{ + // transpose 3x3 rotation matrix part + // | R^T | 0 | + // | ----+-- | + // | 0 | 1 | + float tmp; + tmp = m[1]; m[1] = m[4]; m[4] = tmp; + tmp = m[2]; m[2] = m[8]; m[8] = tmp; + tmp = m[6]; m[6] = m[9]; m[9] = tmp; + + // compute translation part -R^T * T + // | 0 | -R^T x | + // | --+------- | + // | 0 | 0 | + float x = m[12]; + float y = m[13]; + float z = m[14]; + m[12] = -(m[0] * x + m[4] * y + m[8] * z); + m[13] = -(m[1] * x + m[5] * y + m[9] * z); + m[14] = -(m[2] * x + m[6] * y + m[10]* z); + + // last row should be unchanged (0,0,0,1) + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// compute the inverse of a 4x4 affine transformation matrix +// +// Affine transformations are generalizations of Euclidean transformations. +// Affine transformation includes translation, rotation, reflection, scaling, +// and shearing. Length and angle are NOT preserved. +// M = [ R | T ] +// [ --+-- ] (R denotes 3x3 rotation/scale/shear matrix) +// [ 0 | 1 ] (T denotes 1x3 translation matrix) +// +// y = M*x -> y = R*x + T -> x = R^-1*(y - T) -> x = R^-1*y - R^-1*T +// +// [ R | T ]-1 [ R^-1 | -R^-1 * T ] +// [ --+-- ] = [ -----+---------- ] +// [ 0 | 1 ] [ 0 + 1 ] +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::invertAffine() +{ + // R^-1 + Matrix3 r(m[0],m[1],m[2], m[4],m[5],m[6], m[8],m[9],m[10]); + r.invert(); + m[0] = r[0]; m[1] = r[1]; m[2] = r[2]; + m[4] = r[3]; m[5] = r[4]; m[6] = r[5]; + m[8] = r[6]; m[9] = r[7]; m[10]= r[8]; + + // -R^-1 * T + float x = m[12]; + float y = m[13]; + float z = m[14]; + m[12] = -(r[0] * x + r[3] * y + r[6] * z); + m[13] = -(r[1] * x + r[4] * y + r[7] * z); + m[14] = -(r[2] * x + r[5] * y + r[8] * z); + + // last row should be unchanged (0,0,0,1) + //m[3] = m[7] = m[11] = 0.0f; + //m[15] = 1.0f; + + return * this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// inverse matrix using matrix partitioning (blockwise inverse) +// It devides a 4x4 matrix into 4 of 2x2 matrices. It works in case of where +// det(A) != 0. If not, use the generic inverse method +// inverse formula. +// M = [ A | B ] A, B, C, D are 2x2 matrix blocks +// [ --+-- ] det(M) = |A| * |D - ((C * A^-1) * B)| +// [ C | D ] +// +// M^-1 = [ A' | B' ] A' = A^-1 - (A^-1 * B) * C' +// [ ---+--- ] B' = (A^-1 * B) * -D' +// [ C' | D' ] C' = -D' * (C * A^-1) +// D' = (D - ((C * A^-1) * B))^-1 +// +// NOTE: I wrap with () if it it used more than once. +// The matrix is invertable even if det(A)=0, so must check det(A) before +// calling this function, and use invertGeneric() instead. +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::invertProjective() +{ + // partition + Matrix2 a(m[0], m[1], m[4], m[5]); + Matrix2 b(m[8], m[9], m[12], m[13]); + Matrix2 c(m[2], m[3], m[6], m[7]); + Matrix2 d(m[10], m[11], m[14], m[15]); + + // pre-compute repeated parts + a.invert(); // A^-1 + Matrix2 ab = a * b; // A^-1 * B + Matrix2 ca = c * a; // C * A^-1 + Matrix2 cab = ca * b; // C * A^-1 * B + Matrix2 dcab = d - cab; // D - C * A^-1 * B + + // check determinant if |D - C * A^-1 * B| = 0 + //NOTE: this function assumes det(A) is already checked. if |A|=0 then, + // cannot use this function. + float determinant = dcab[0] * dcab[3] - dcab[1] * dcab[2]; + if(fabs(determinant) <= EPSILON) + { + return identity(); + } + + // compute D' and -D' + Matrix2 d1 = dcab; // (D - C * A^-1 * B) + d1.invert(); // (D - C * A^-1 * B)^-1 + Matrix2 d2 = -d1; // -(D - C * A^-1 * B)^-1 + + // compute C' + Matrix2 c1 = d2 * ca; // -D' * (C * A^-1) + + // compute B' + Matrix2 b1 = ab * d2; // (A^-1 * B) * -D' + + // compute A' + Matrix2 a1 = a - (ab * c1); // A^-1 - (A^-1 * B) * C' + + // assemble inverse matrix + m[0] = a1[0]; m[4] = a1[2]; /*|*/ m[8] = b1[0]; m[12]= b1[2]; + m[1] = a1[1]; m[5] = a1[3]; /*|*/ m[9] = b1[1]; m[13]= b1[3]; + /*-----------------------------+-----------------------------*/ + m[2] = c1[0]; m[6] = c1[2]; /*|*/ m[10]= d1[0]; m[14]= d1[2]; + m[3] = c1[1]; m[7] = c1[3]; /*|*/ m[11]= d1[1]; m[15]= d1[3]; + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// compute the inverse of a general 4x4 matrix using Cramer's Rule +// If cannot find inverse, return indentity matrix +// M^-1 = adj(M) / det(M) +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::invertGeneral() +{ + // get cofactors of minor matrices + float cofactor0 = getCofactor(m[5],m[6],m[7], m[9],m[10],m[11], m[13],m[14],m[15]); + float cofactor1 = getCofactor(m[4],m[6],m[7], m[8],m[10],m[11], m[12],m[14],m[15]); + float cofactor2 = getCofactor(m[4],m[5],m[7], m[8],m[9], m[11], m[12],m[13],m[15]); + float cofactor3 = getCofactor(m[4],m[5],m[6], m[8],m[9], m[10], m[12],m[13],m[14]); + + // get determinant + float determinant = m[0] * cofactor0 - m[1] * cofactor1 + m[2] * cofactor2 - m[3] * cofactor3; + if(fabs(determinant) <= EPSILON) + { + return identity(); + } + + // get rest of cofactors for adj(M) + float cofactor4 = getCofactor(m[1],m[2],m[3], m[9],m[10],m[11], m[13],m[14],m[15]); + float cofactor5 = getCofactor(m[0],m[2],m[3], m[8],m[10],m[11], m[12],m[14],m[15]); + float cofactor6 = getCofactor(m[0],m[1],m[3], m[8],m[9], m[11], m[12],m[13],m[15]); + float cofactor7 = getCofactor(m[0],m[1],m[2], m[8],m[9], m[10], m[12],m[13],m[14]); + + float cofactor8 = getCofactor(m[1],m[2],m[3], m[5],m[6], m[7], m[13],m[14],m[15]); + float cofactor9 = getCofactor(m[0],m[2],m[3], m[4],m[6], m[7], m[12],m[14],m[15]); + float cofactor10= getCofactor(m[0],m[1],m[3], m[4],m[5], m[7], m[12],m[13],m[15]); + float cofactor11= getCofactor(m[0],m[1],m[2], m[4],m[5], m[6], m[12],m[13],m[14]); + + float cofactor12= getCofactor(m[1],m[2],m[3], m[5],m[6], m[7], m[9], m[10],m[11]); + float cofactor13= getCofactor(m[0],m[2],m[3], m[4],m[6], m[7], m[8], m[10],m[11]); + float cofactor14= getCofactor(m[0],m[1],m[3], m[4],m[5], m[7], m[8], m[9], m[11]); + float cofactor15= getCofactor(m[0],m[1],m[2], m[4],m[5], m[6], m[8], m[9], m[10]); + + // build inverse matrix = adj(M) / det(M) + // adjugate of M is the transpose of the cofactor matrix of M + float invDeterminant = 1.0f / determinant; + m[0] = invDeterminant * cofactor0; + m[1] = -invDeterminant * cofactor4; + m[2] = invDeterminant * cofactor8; + m[3] = -invDeterminant * cofactor12; + + m[4] = -invDeterminant * cofactor1; + m[5] = invDeterminant * cofactor5; + m[6] = -invDeterminant * cofactor9; + m[7] = invDeterminant * cofactor13; + + m[8] = invDeterminant * cofactor2; + m[9] = -invDeterminant * cofactor6; + m[10]= invDeterminant * cofactor10; + m[11]= -invDeterminant * cofactor14; + + m[12]= -invDeterminant * cofactor3; + m[13]= invDeterminant * cofactor7; + m[14]= -invDeterminant * cofactor11; + m[15]= invDeterminant * cofactor15; + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// return determinant of 4x4 matrix +/////////////////////////////////////////////////////////////////////////////// +float Matrix4::getDeterminant() +{ + return m[0] * getCofactor(m[5],m[6],m[7], m[9],m[10],m[11], m[13],m[14],m[15]) - + m[1] * getCofactor(m[4],m[6],m[7], m[8],m[10],m[11], m[12],m[14],m[15]) + + m[2] * getCofactor(m[4],m[5],m[7], m[8],m[9], m[11], m[12],m[13],m[15]) - + m[3] * getCofactor(m[4],m[5],m[6], m[8],m[9], m[10], m[12],m[13],m[14]); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// compute cofactor of 3x3 minor matrix without sign +// input params are 9 elements of the minor matrix +// NOTE: The caller must know its sign. +/////////////////////////////////////////////////////////////////////////////// +float Matrix4::getCofactor(float m0, float m1, float m2, + float m3, float m4, float m5, + float m6, float m7, float m8) +{ + return m0 * (m4 * m8 - m5 * m7) - + m1 * (m3 * m8 - m5 * m6) + + m2 * (m3 * m7 - m4 * m6); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// translate this matrix by (x, y, z) +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::translate(const Vector3& v) +{ + return translate(v.x, v.y, v.z); +} + +Matrix4& Matrix4::translate(float x, float y, float z) +{ + m[0] += m[3] * x; m[4] += m[7] * x; m[8] += m[11]* x; m[12]+= m[15]* x; + m[1] += m[3] * y; m[5] += m[7] * y; m[9] += m[11]* y; m[13]+= m[15]* y; + m[2] += m[3] * z; m[6] += m[7] * z; m[10]+= m[11]* z; m[14]+= m[15]* z; + + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// uniform scale +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::scale(float s) +{ + return scale(s, s, s); +} + +Matrix4& Matrix4::scale(float x, float y, float z) +{ + m[0] *= x; m[4] *= x; m[8] *= x; m[12] *= x; + m[1] *= y; m[5] *= y; m[9] *= y; m[13] *= y; + m[2] *= z; m[6] *= z; m[10]*= z; m[14] *= z; + return *this; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// build a rotation matrix with given angle(degree) and rotation axis, then +// multiply it with this object +/////////////////////////////////////////////////////////////////////////////// +Matrix4& Matrix4::rotate(float angle, const Vector3& axis) +{ + return rotate(angle, axis.x, axis.y, axis.z); +} + +Matrix4& Matrix4::rotate(float angle, float x, float y, float z) +{ + float c = cosf(angle * DEG2RAD); // cosine + float s = sinf(angle * DEG2RAD); // sine + float c1 = 1.0f - c; // 1 - c + float m0 = m[0], m4 = m[4], m8 = m[8], m12= m[12], + m1 = m[1], m5 = m[5], m9 = m[9], m13= m[13], + m2 = m[2], m6 = m[6], m10= m[10], m14= m[14]; + + // build rotation matrix + float r0 = x * x * c1 + c; + float r1 = x * y * c1 + z * s; + float r2 = x * z * c1 - y * s; + float r4 = x * y * c1 - z * s; + float r5 = y * y * c1 + c; + float r6 = y * z * c1 + x * s; + float r8 = x * z * c1 + y * s; + float r9 = y * z * c1 - x * s; + float r10= z * z * c1 + c; + + // multiply rotation matrix + m[0] = r0 * m0 + r4 * m1 + r8 * m2; + m[1] = r1 * m0 + r5 * m1 + r9 * m2; + m[2] = r2 * m0 + r6 * m1 + r10* m2; + m[4] = r0 * m4 + r4 * m5 + r8 * m6; + m[5] = r1 * m4 + r5 * m5 + r9 * m6; + m[6] = r2 * m4 + r6 * m5 + r10* m6; + m[8] = r0 * m8 + r4 * m9 + r8 * m10; + m[9] = r1 * m8 + r5 * m9 + r9 * m10; + m[10]= r2 * m8 + r6 * m9 + r10* m10; + m[12]= r0 * m12+ r4 * m13+ r8 * m14; + m[13]= r1 * m12+ r5 * m13+ r9 * m14; + m[14]= r2 * m12+ r6 * m13+ r10* m14; + + return *this; +} + +Matrix4& Matrix4::rotateX(float angle) +{ + float c = cosf(angle * DEG2RAD); + float s = sinf(angle * DEG2RAD); + float m1 = m[1], m2 = m[2], + m5 = m[5], m6 = m[6], + m9 = m[9], m10= m[10], + m13= m[13], m14= m[14]; + + m[1] = m1 * c + m2 *-s; + m[2] = m1 * s + m2 * c; + m[5] = m5 * c + m6 *-s; + m[6] = m5 * s + m6 * c; + m[9] = m9 * c + m10*-s; + m[10]= m9 * s + m10* c; + m[13]= m13* c + m14*-s; + m[14]= m13* s + m14* c; + + return *this; +} + +Matrix4& Matrix4::rotateY(float angle) +{ + float c = cosf(angle * DEG2RAD); + float s = sinf(angle * DEG2RAD); + float m0 = m[0], m2 = m[2], + m4 = m[4], m6 = m[6], + m8 = m[8], m10= m[10], + m12= m[12], m14= m[14]; + + m[0] = m0 * c + m2 * s; + m[2] = m0 *-s + m2 * c; + m[4] = m4 * c + m6 * s; + m[6] = m4 *-s + m6 * c; + m[8] = m8 * c + m10* s; + m[10]= m8 *-s + m10* c; + m[12]= m12* c + m14* s; + m[14]= m12*-s + m14* c; + + return *this; +} + +Matrix4& Matrix4::rotateZ(float angle) +{ + float c = cosf(angle * DEG2RAD); + float s = sinf(angle * DEG2RAD); + float m0 = m[0], m1 = m[1], + m4 = m[4], m5 = m[5], + m8 = m[8], m9 = m[9], + m12= m[12], m13= m[13]; + + m[0] = m0 * c + m1 *-s; + m[1] = m0 * s + m1 * c; + m[4] = m4 * c + m5 *-s; + m[5] = m4 * s + m5 * c; + m[8] = m8 * c + m9 *-s; + m[9] = m8 * s + m9 * c; + m[12]= m12* c + m13*-s; + m[13]= m12* s + m13* c; + + return *this; +} diff --git a/shared/Matrices.h b/shared/Matrices.h new file mode 100644 index 0000000..3515f54 --- /dev/null +++ b/shared/Matrices.h @@ -0,0 +1,909 @@ +/////////////////////////////////////////////////////////////////////////////// +// Matrice.h +// ========= +// NxN Matrix Math classes +// +// The elements of the matrix are stored as column major order. +// | 0 2 | | 0 3 6 | | 0 4 8 12 | +// | 1 3 | | 1 4 7 | | 1 5 9 13 | +// | 2 5 8 | | 2 6 10 14 | +// | 3 7 11 15 | +// +// AUTHOR: Song Ho Ahn (song.ahn@gmail.com) +// CREATED: 2005-06-24 +// UPDATED: 2013-09-30 +// +// Copyright (C) 2005 Song Ho Ahn +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MATH_MATRICES_H +#define MATH_MATRICES_H + +#include +#include +#include "Vectors.h" + +/////////////////////////////////////////////////////////////////////////// +// 2x2 matrix +/////////////////////////////////////////////////////////////////////////// +class Matrix2 +{ +public: + // constructors + Matrix2(); // init with identity + Matrix2(const float src[4]); + Matrix2(float m0, float m1, float m2, float m3); + + void set(const float src[4]); + void set(float m0, float m1, float m2, float m3); + void setRow(int index, const float row[2]); + void setRow(int index, const Vector2& v); + void setColumn(int index, const float col[2]); + void setColumn(int index, const Vector2& v); + + const float* get() const; + float getDeterminant(); + + Matrix2& identity(); + Matrix2& transpose(); // transpose itself and return reference + Matrix2& invert(); + + // operators + Matrix2 operator+(const Matrix2& rhs) const; // add rhs + Matrix2 operator-(const Matrix2& rhs) const; // subtract rhs + Matrix2& operator+=(const Matrix2& rhs); // add rhs and update this object + Matrix2& operator-=(const Matrix2& rhs); // subtract rhs and update this object + Vector2 operator*(const Vector2& rhs) const; // multiplication: v' = M * v + Matrix2 operator*(const Matrix2& rhs) const; // multiplication: M3 = M1 * M2 + Matrix2& operator*=(const Matrix2& rhs); // multiplication: M1' = M1 * M2 + bool operator==(const Matrix2& rhs) const; // exact compare, no epsilon + bool operator!=(const Matrix2& rhs) const; // exact compare, no epsilon + float operator[](int index) const; // subscript operator v[0], v[1] + float& operator[](int index); // subscript operator v[0], v[1] + + friend Matrix2 operator-(const Matrix2& m); // unary operator (-) + friend Matrix2 operator*(float scalar, const Matrix2& m); // pre-multiplication + friend Vector2 operator*(const Vector2& vec, const Matrix2& m); // pre-multiplication + friend std::ostream& operator<<(std::ostream& os, const Matrix2& m); + +protected: + +private: + float m[4]; + +}; + + + +/////////////////////////////////////////////////////////////////////////// +// 3x3 matrix +/////////////////////////////////////////////////////////////////////////// +class Matrix3 +{ +public: + // constructors + Matrix3(); // init with identity + Matrix3(const float src[9]); + Matrix3(float m0, float m1, float m2, // 1st column + float m3, float m4, float m5, // 2nd column + float m6, float m7, float m8); // 3rd column + + void set(const float src[9]); + void set(float m0, float m1, float m2, // 1st column + float m3, float m4, float m5, // 2nd column + float m6, float m7, float m8); // 3rd column + void setRow(int index, const float row[3]); + void setRow(int index, const Vector3& v); + void setColumn(int index, const float col[3]); + void setColumn(int index, const Vector3& v); + + const float* get() const; + float getDeterminant(); + + Matrix3& identity(); + Matrix3& transpose(); // transpose itself and return reference + Matrix3& invert(); + + // operators + Matrix3 operator+(const Matrix3& rhs) const; // add rhs + Matrix3 operator-(const Matrix3& rhs) const; // subtract rhs + Matrix3& operator+=(const Matrix3& rhs); // add rhs and update this object + Matrix3& operator-=(const Matrix3& rhs); // subtract rhs and update this object + Vector3 operator*(const Vector3& rhs) const; // multiplication: v' = M * v + Matrix3 operator*(const Matrix3& rhs) const; // multiplication: M3 = M1 * M2 + Matrix3& operator*=(const Matrix3& rhs); // multiplication: M1' = M1 * M2 + bool operator==(const Matrix3& rhs) const; // exact compare, no epsilon + bool operator!=(const Matrix3& rhs) const; // exact compare, no epsilon + float operator[](int index) const; // subscript operator v[0], v[1] + float& operator[](int index); // subscript operator v[0], v[1] + + friend Matrix3 operator-(const Matrix3& m); // unary operator (-) + friend Matrix3 operator*(float scalar, const Matrix3& m); // pre-multiplication + friend Vector3 operator*(const Vector3& vec, const Matrix3& m); // pre-multiplication + friend std::ostream& operator<<(std::ostream& os, const Matrix3& m); + +protected: + +private: + float m[9]; + +}; + + + +/////////////////////////////////////////////////////////////////////////// +// 4x4 matrix +/////////////////////////////////////////////////////////////////////////// +class Matrix4 +{ +public: + // constructors + Matrix4(); // init with identity + Matrix4(const float src[16]); + Matrix4(float m00, float m01, float m02, float m03, // 1st column + float m04, float m05, float m06, float m07, // 2nd column + float m08, float m09, float m10, float m11, // 3rd column + float m12, float m13, float m14, float m15);// 4th column + + void set(const float src[16]); + void set(float m00, float m01, float m02, float m03, // 1st column + float m04, float m05, float m06, float m07, // 2nd column + float m08, float m09, float m10, float m11, // 3rd column + float m12, float m13, float m14, float m15);// 4th column + void setRow(int index, const float row[4]); + void setRow(int index, const Vector4& v); + void setRow(int index, const Vector3& v); + void setColumn(int index, const float col[4]); + void setColumn(int index, const Vector4& v); + void setColumn(int index, const Vector3& v); + + const float* get() const; + const float* getTranspose(); // return transposed matrix + float getDeterminant(); + + Matrix4& identity(); + Matrix4& transpose(); // transpose itself and return reference + Matrix4& invert(); // check best inverse method before inverse + Matrix4& invertEuclidean(); // inverse of Euclidean transform matrix + Matrix4& invertAffine(); // inverse of affine transform matrix + Matrix4& invertProjective(); // inverse of projective matrix using partitioning + Matrix4& invertGeneral(); // inverse of generic matrix + + // transform matrix + Matrix4& translate(float x, float y, float z); // translation by (x,y,z) + Matrix4& translate(const Vector3& v); // + Matrix4& rotate(float angle, const Vector3& axis); // rotate angle(degree) along the given axix + Matrix4& rotate(float angle, float x, float y, float z); + Matrix4& rotateX(float angle); // rotate on X-axis with degree + Matrix4& rotateY(float angle); // rotate on Y-axis with degree + Matrix4& rotateZ(float angle); // rotate on Z-axis with degree + Matrix4& scale(float scale); // uniform scale + Matrix4& scale(float sx, float sy, float sz); // scale by (sx, sy, sz) on each axis + + // operators + Matrix4 operator+(const Matrix4& rhs) const; // add rhs + Matrix4 operator-(const Matrix4& rhs) const; // subtract rhs + Matrix4& operator+=(const Matrix4& rhs); // add rhs and update this object + Matrix4& operator-=(const Matrix4& rhs); // subtract rhs and update this object + Vector4 operator*(const Vector4& rhs) const; // multiplication: v' = M * v + Vector3 operator*(const Vector3& rhs) const; // multiplication: v' = M * v + Matrix4 operator*(const Matrix4& rhs) const; // multiplication: M3 = M1 * M2 + Matrix4& operator*=(const Matrix4& rhs); // multiplication: M1' = M1 * M2 + bool operator==(const Matrix4& rhs) const; // exact compare, no epsilon + bool operator!=(const Matrix4& rhs) const; // exact compare, no epsilon + float operator[](int index) const; // subscript operator v[0], v[1] + float& operator[](int index); // subscript operator v[0], v[1] + + friend Matrix4 operator-(const Matrix4& m); // unary operator (-) + friend Matrix4 operator*(float scalar, const Matrix4& m); // pre-multiplication + friend Vector3 operator*(const Vector3& vec, const Matrix4& m); // pre-multiplication + friend Vector4 operator*(const Vector4& vec, const Matrix4& m); // pre-multiplication + friend std::ostream& operator<<(std::ostream& os, const Matrix4& m); + +protected: + +private: + float getCofactor(float m0, float m1, float m2, + float m3, float m4, float m5, + float m6, float m7, float m8); + + float m[16]; + float tm[16]; // transpose m + +}; + + + +/////////////////////////////////////////////////////////////////////////// +// inline functions for Matrix2 +/////////////////////////////////////////////////////////////////////////// +inline Matrix2::Matrix2() +{ + // initially identity matrix + identity(); +} + + + +inline Matrix2::Matrix2(const float src[4]) +{ + set(src); +} + + + +inline Matrix2::Matrix2(float m0, float m1, float m2, float m3) +{ + set(m0, m1, m2, m3); +} + + + +inline void Matrix2::set(const float src[4]) +{ + m[0] = src[0]; m[1] = src[1]; m[2] = src[2]; m[3] = src[3]; +} + + + +inline void Matrix2::set(float m0, float m1, float m2, float m3) +{ + m[0]= m0; m[1] = m1; m[2] = m2; m[3]= m3; +} + + + +inline void Matrix2::setRow(int index, const float row[2]) +{ + m[index] = row[0]; m[index + 2] = row[1]; +} + + + +inline void Matrix2::setRow(int index, const Vector2& v) +{ + m[index] = v.x; m[index + 2] = v.y; +} + + + +inline void Matrix2::setColumn(int index, const float col[2]) +{ + m[index*2] = col[0]; m[index*2 + 1] = col[1]; +} + + + +inline void Matrix2::setColumn(int index, const Vector2& v) +{ + m[index*2] = v.x; m[index*2 + 1] = v.y; +} + + + +inline const float* Matrix2::get() const +{ + return m; +} + + + +inline Matrix2& Matrix2::identity() +{ + m[0] = m[3] = 1.0f; + m[1] = m[2] = 0.0f; + return *this; +} + + + +inline Matrix2 Matrix2::operator+(const Matrix2& rhs) const +{ + return Matrix2(m[0]+rhs[0], m[1]+rhs[1], m[2]+rhs[2], m[3]+rhs[3]); +} + + + +inline Matrix2 Matrix2::operator-(const Matrix2& rhs) const +{ + return Matrix2(m[0]-rhs[0], m[1]-rhs[1], m[2]-rhs[2], m[3]-rhs[3]); +} + + + +inline Matrix2& Matrix2::operator+=(const Matrix2& rhs) +{ + m[0] += rhs[0]; m[1] += rhs[1]; m[2] += rhs[2]; m[3] += rhs[3]; + return *this; +} + + + +inline Matrix2& Matrix2::operator-=(const Matrix2& rhs) +{ + m[0] -= rhs[0]; m[1] -= rhs[1]; m[2] -= rhs[2]; m[3] -= rhs[3]; + return *this; +} + + + +inline Vector2 Matrix2::operator*(const Vector2& rhs) const +{ + return Vector2(m[0]*rhs.x + m[2]*rhs.y, m[1]*rhs.x + m[3]*rhs.y); +} + + + +inline Matrix2 Matrix2::operator*(const Matrix2& rhs) const +{ + return Matrix2(m[0]*rhs[0] + m[2]*rhs[1], m[1]*rhs[0] + m[3]*rhs[1], + m[0]*rhs[2] + m[2]*rhs[3], m[1]*rhs[2] + m[3]*rhs[3]); +} + + + +inline Matrix2& Matrix2::operator*=(const Matrix2& rhs) +{ + *this = *this * rhs; + return *this; +} + + + +inline bool Matrix2::operator==(const Matrix2& rhs) const +{ + return (m[0] == rhs[0]) && (m[1] == rhs[1]) && (m[2] == rhs[2]) && (m[3] == rhs[3]); +} + + + +inline bool Matrix2::operator!=(const Matrix2& rhs) const +{ + return (m[0] != rhs[0]) || (m[1] != rhs[1]) || (m[2] != rhs[2]) || (m[3] != rhs[3]); +} + + + +inline float Matrix2::operator[](int index) const +{ + return m[index]; +} + + + +inline float& Matrix2::operator[](int index) +{ + return m[index]; +} + + + +inline Matrix2 operator-(const Matrix2& rhs) +{ + return Matrix2(-rhs[0], -rhs[1], -rhs[2], -rhs[3]); +} + + + +inline Matrix2 operator*(float s, const Matrix2& rhs) +{ + return Matrix2(s*rhs[0], s*rhs[1], s*rhs[2], s*rhs[3]); +} + + + +inline Vector2 operator*(const Vector2& v, const Matrix2& rhs) +{ + return Vector2(v.x*rhs[0] + v.y*rhs[1], v.x*rhs[2] + v.y*rhs[3]); +} + + + +inline std::ostream& operator<<(std::ostream& os, const Matrix2& m) +{ + os << std::fixed << std::setprecision(5); + os << "[" << std::setw(10) << m[0] << " " << std::setw(10) << m[2] << "]\n" + << "[" << std::setw(10) << m[1] << " " << std::setw(10) << m[3] << "]\n"; + os << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); + return os; +} +// END OF MATRIX2 INLINE ////////////////////////////////////////////////////// + + + + +/////////////////////////////////////////////////////////////////////////// +// inline functions for Matrix3 +/////////////////////////////////////////////////////////////////////////// +inline Matrix3::Matrix3() +{ + // initially identity matrix + identity(); +} + + + +inline Matrix3::Matrix3(const float src[9]) +{ + set(src); +} + + + +inline Matrix3::Matrix3(float m0, float m1, float m2, + float m3, float m4, float m5, + float m6, float m7, float m8) +{ + set(m0, m1, m2, m3, m4, m5, m6, m7, m8); +} + + + +inline void Matrix3::set(const float src[9]) +{ + m[0] = src[0]; m[1] = src[1]; m[2] = src[2]; + m[3] = src[3]; m[4] = src[4]; m[5] = src[5]; + m[6] = src[6]; m[7] = src[7]; m[8] = src[8]; +} + + + +inline void Matrix3::set(float m0, float m1, float m2, + float m3, float m4, float m5, + float m6, float m7, float m8) +{ + m[0] = m0; m[1] = m1; m[2] = m2; + m[3] = m3; m[4] = m4; m[5] = m5; + m[6] = m6; m[7] = m7; m[8] = m8; +} + + + +inline void Matrix3::setRow(int index, const float row[3]) +{ + m[index] = row[0]; m[index + 3] = row[1]; m[index + 6] = row[2]; +} + + + +inline void Matrix3::setRow(int index, const Vector3& v) +{ + m[index] = v.x; m[index + 3] = v.y; m[index + 6] = v.z; +} + + + +inline void Matrix3::setColumn(int index, const float col[3]) +{ + m[index*3] = col[0]; m[index*3 + 1] = col[1]; m[index*3 + 2] = col[2]; +} + + + +inline void Matrix3::setColumn(int index, const Vector3& v) +{ + m[index*3] = v.x; m[index*3 + 1] = v.y; m[index*3 + 2] = v.z; +} + + + +inline const float* Matrix3::get() const +{ + return m; +} + + + +inline Matrix3& Matrix3::identity() +{ + m[0] = m[4] = m[8] = 1.0f; + m[1] = m[2] = m[3] = m[5] = m[6] = m[7] = 0.0f; + return *this; +} + + + +inline Matrix3 Matrix3::operator+(const Matrix3& rhs) const +{ + return Matrix3(m[0]+rhs[0], m[1]+rhs[1], m[2]+rhs[2], + m[3]+rhs[3], m[4]+rhs[4], m[5]+rhs[5], + m[6]+rhs[6], m[7]+rhs[7], m[8]+rhs[8]); +} + + + +inline Matrix3 Matrix3::operator-(const Matrix3& rhs) const +{ + return Matrix3(m[0]-rhs[0], m[1]-rhs[1], m[2]-rhs[2], + m[3]-rhs[3], m[4]-rhs[4], m[5]-rhs[5], + m[6]-rhs[6], m[7]-rhs[7], m[8]-rhs[8]); +} + + + +inline Matrix3& Matrix3::operator+=(const Matrix3& rhs) +{ + m[0] += rhs[0]; m[1] += rhs[1]; m[2] += rhs[2]; + m[3] += rhs[3]; m[4] += rhs[4]; m[5] += rhs[5]; + m[6] += rhs[6]; m[7] += rhs[7]; m[8] += rhs[8]; + return *this; +} + + + +inline Matrix3& Matrix3::operator-=(const Matrix3& rhs) +{ + m[0] -= rhs[0]; m[1] -= rhs[1]; m[2] -= rhs[2]; + m[3] -= rhs[3]; m[4] -= rhs[4]; m[5] -= rhs[5]; + m[6] -= rhs[6]; m[7] -= rhs[7]; m[8] -= rhs[8]; + return *this; +} + + + +inline Vector3 Matrix3::operator*(const Vector3& rhs) const +{ + return Vector3(m[0]*rhs.x + m[3]*rhs.y + m[6]*rhs.z, + m[1]*rhs.x + m[4]*rhs.y + m[7]*rhs.z, + m[2]*rhs.x + m[5]*rhs.y + m[8]*rhs.z); +} + + + +inline Matrix3 Matrix3::operator*(const Matrix3& rhs) const +{ + return Matrix3(m[0]*rhs[0] + m[3]*rhs[1] + m[6]*rhs[2], m[1]*rhs[0] + m[4]*rhs[1] + m[7]*rhs[2], m[2]*rhs[0] + m[5]*rhs[1] + m[8]*rhs[2], + m[0]*rhs[3] + m[3]*rhs[4] + m[6]*rhs[5], m[1]*rhs[3] + m[4]*rhs[4] + m[7]*rhs[5], m[2]*rhs[3] + m[5]*rhs[4] + m[8]*rhs[5], + m[0]*rhs[6] + m[3]*rhs[7] + m[6]*rhs[8], m[1]*rhs[6] + m[4]*rhs[7] + m[7]*rhs[8], m[2]*rhs[6] + m[5]*rhs[7] + m[8]*rhs[8]); +} + + + +inline Matrix3& Matrix3::operator*=(const Matrix3& rhs) +{ + *this = *this * rhs; + return *this; +} + + + +inline bool Matrix3::operator==(const Matrix3& rhs) const +{ + return (m[0] == rhs[0]) && (m[1] == rhs[1]) && (m[2] == rhs[2]) && + (m[3] == rhs[3]) && (m[4] == rhs[4]) && (m[5] == rhs[5]) && + (m[6] == rhs[6]) && (m[7] == rhs[7]) && (m[8] == rhs[8]); +} + + + +inline bool Matrix3::operator!=(const Matrix3& rhs) const +{ + return (m[0] != rhs[0]) || (m[1] != rhs[1]) || (m[2] != rhs[2]) || + (m[3] != rhs[3]) || (m[4] != rhs[4]) || (m[5] != rhs[5]) || + (m[6] != rhs[6]) || (m[7] != rhs[7]) || (m[8] != rhs[8]); +} + + + +inline float Matrix3::operator[](int index) const +{ + return m[index]; +} + + + +inline float& Matrix3::operator[](int index) +{ + return m[index]; +} + + + +inline Matrix3 operator-(const Matrix3& rhs) +{ + return Matrix3(-rhs[0], -rhs[1], -rhs[2], -rhs[3], -rhs[4], -rhs[5], -rhs[6], -rhs[7], -rhs[8]); +} + + + +inline Matrix3 operator*(float s, const Matrix3& rhs) +{ + return Matrix3(s*rhs[0], s*rhs[1], s*rhs[2], s*rhs[3], s*rhs[4], s*rhs[5], s*rhs[6], s*rhs[7], s*rhs[8]); +} + + + +inline Vector3 operator*(const Vector3& v, const Matrix3& m) +{ + return Vector3(v.x*m[0] + v.y*m[1] + v.z*m[2], v.x*m[3] + v.y*m[4] + v.z*m[5], v.x*m[6] + v.y*m[7] + v.z*m[8]); +} + + + +inline std::ostream& operator<<(std::ostream& os, const Matrix3& m) +{ + os << std::fixed << std::setprecision(5); + os << "[" << std::setw(10) << m[0] << " " << std::setw(10) << m[3] << " " << std::setw(10) << m[6] << "]\n" + << "[" << std::setw(10) << m[1] << " " << std::setw(10) << m[4] << " " << std::setw(10) << m[7] << "]\n" + << "[" << std::setw(10) << m[2] << " " << std::setw(10) << m[5] << " " << std::setw(10) << m[8] << "]\n"; + os << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); + return os; +} +// END OF MATRIX3 INLINE ////////////////////////////////////////////////////// + + + + +/////////////////////////////////////////////////////////////////////////// +// inline functions for Matrix4 +/////////////////////////////////////////////////////////////////////////// +inline Matrix4::Matrix4() +{ + // initially identity matrix + identity(); +} + + + +inline Matrix4::Matrix4(const float src[16]) +{ + set(src); +} + + + +inline Matrix4::Matrix4(float m00, float m01, float m02, float m03, + float m04, float m05, float m06, float m07, + float m08, float m09, float m10, float m11, + float m12, float m13, float m14, float m15) +{ + set(m00, m01, m02, m03, m04, m05, m06, m07, m08, m09, m10, m11, m12, m13, m14, m15); +} + + + +inline void Matrix4::set(const float src[16]) +{ + m[0] = src[0]; m[1] = src[1]; m[2] = src[2]; m[3] = src[3]; + m[4] = src[4]; m[5] = src[5]; m[6] = src[6]; m[7] = src[7]; + m[8] = src[8]; m[9] = src[9]; m[10]= src[10]; m[11]= src[11]; + m[12]= src[12]; m[13]= src[13]; m[14]= src[14]; m[15]= src[15]; +} + + + +inline void Matrix4::set(float m00, float m01, float m02, float m03, + float m04, float m05, float m06, float m07, + float m08, float m09, float m10, float m11, + float m12, float m13, float m14, float m15) +{ + m[0] = m00; m[1] = m01; m[2] = m02; m[3] = m03; + m[4] = m04; m[5] = m05; m[6] = m06; m[7] = m07; + m[8] = m08; m[9] = m09; m[10]= m10; m[11]= m11; + m[12]= m12; m[13]= m13; m[14]= m14; m[15]= m15; +} + + + +inline void Matrix4::setRow(int index, const float row[4]) +{ + m[index] = row[0]; m[index + 4] = row[1]; m[index + 8] = row[2]; m[index + 12] = row[3]; +} + + + +inline void Matrix4::setRow(int index, const Vector4& v) +{ + m[index] = v.x; m[index + 4] = v.y; m[index + 8] = v.z; m[index + 12] = v.w; +} + + + +inline void Matrix4::setRow(int index, const Vector3& v) +{ + m[index] = v.x; m[index + 4] = v.y; m[index + 8] = v.z; +} + + + +inline void Matrix4::setColumn(int index, const float col[4]) +{ + m[index*4] = col[0]; m[index*4 + 1] = col[1]; m[index*4 + 2] = col[2]; m[index*4 + 3] = col[3]; +} + + + +inline void Matrix4::setColumn(int index, const Vector4& v) +{ + m[index*4] = v.x; m[index*4 + 1] = v.y; m[index*4 + 2] = v.z; m[index*4 + 3] = v.w; +} + + + +inline void Matrix4::setColumn(int index, const Vector3& v) +{ + m[index*4] = v.x; m[index*4 + 1] = v.y; m[index*4 + 2] = v.z; +} + + + +inline const float* Matrix4::get() const +{ + return m; +} + + + +inline const float* Matrix4::getTranspose() +{ + tm[0] = m[0]; tm[1] = m[4]; tm[2] = m[8]; tm[3] = m[12]; + tm[4] = m[1]; tm[5] = m[5]; tm[6] = m[9]; tm[7] = m[13]; + tm[8] = m[2]; tm[9] = m[6]; tm[10]= m[10]; tm[11]= m[14]; + tm[12]= m[3]; tm[13]= m[7]; tm[14]= m[11]; tm[15]= m[15]; + return tm; +} + + + +inline Matrix4& Matrix4::identity() +{ + m[0] = m[5] = m[10] = m[15] = 1.0f; + m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0.0f; + return *this; +} + + + +inline Matrix4 Matrix4::operator+(const Matrix4& rhs) const +{ + return Matrix4(m[0]+rhs[0], m[1]+rhs[1], m[2]+rhs[2], m[3]+rhs[3], + m[4]+rhs[4], m[5]+rhs[5], m[6]+rhs[6], m[7]+rhs[7], + m[8]+rhs[8], m[9]+rhs[9], m[10]+rhs[10], m[11]+rhs[11], + m[12]+rhs[12], m[13]+rhs[13], m[14]+rhs[14], m[15]+rhs[15]); +} + + + +inline Matrix4 Matrix4::operator-(const Matrix4& rhs) const +{ + return Matrix4(m[0]-rhs[0], m[1]-rhs[1], m[2]-rhs[2], m[3]-rhs[3], + m[4]-rhs[4], m[5]-rhs[5], m[6]-rhs[6], m[7]-rhs[7], + m[8]-rhs[8], m[9]-rhs[9], m[10]-rhs[10], m[11]-rhs[11], + m[12]-rhs[12], m[13]-rhs[13], m[14]-rhs[14], m[15]-rhs[15]); +} + + + +inline Matrix4& Matrix4::operator+=(const Matrix4& rhs) +{ + m[0] += rhs[0]; m[1] += rhs[1]; m[2] += rhs[2]; m[3] += rhs[3]; + m[4] += rhs[4]; m[5] += rhs[5]; m[6] += rhs[6]; m[7] += rhs[7]; + m[8] += rhs[8]; m[9] += rhs[9]; m[10]+= rhs[10]; m[11]+= rhs[11]; + m[12]+= rhs[12]; m[13]+= rhs[13]; m[14]+= rhs[14]; m[15]+= rhs[15]; + return *this; +} + + + +inline Matrix4& Matrix4::operator-=(const Matrix4& rhs) +{ + m[0] -= rhs[0]; m[1] -= rhs[1]; m[2] -= rhs[2]; m[3] -= rhs[3]; + m[4] -= rhs[4]; m[5] -= rhs[5]; m[6] -= rhs[6]; m[7] -= rhs[7]; + m[8] -= rhs[8]; m[9] -= rhs[9]; m[10]-= rhs[10]; m[11]-= rhs[11]; + m[12]-= rhs[12]; m[13]-= rhs[13]; m[14]-= rhs[14]; m[15]-= rhs[15]; + return *this; +} + + + +inline Vector4 Matrix4::operator*(const Vector4& rhs) const +{ + return Vector4(m[0]*rhs.x + m[4]*rhs.y + m[8]*rhs.z + m[12]*rhs.w, + m[1]*rhs.x + m[5]*rhs.y + m[9]*rhs.z + m[13]*rhs.w, + m[2]*rhs.x + m[6]*rhs.y + m[10]*rhs.z + m[14]*rhs.w, + m[3]*rhs.x + m[7]*rhs.y + m[11]*rhs.z + m[15]*rhs.w); +} + + + +inline Vector3 Matrix4::operator*(const Vector3& rhs) const +{ + return Vector3(m[0]*rhs.x + m[4]*rhs.y + m[8]*rhs.z, + m[1]*rhs.x + m[5]*rhs.y + m[9]*rhs.z, + m[2]*rhs.x + m[6]*rhs.y + m[10]*rhs.z); +} + + + +inline Matrix4 Matrix4::operator*(const Matrix4& n) const +{ + return Matrix4(m[0]*n[0] + m[4]*n[1] + m[8]*n[2] + m[12]*n[3], m[1]*n[0] + m[5]*n[1] + m[9]*n[2] + m[13]*n[3], m[2]*n[0] + m[6]*n[1] + m[10]*n[2] + m[14]*n[3], m[3]*n[0] + m[7]*n[1] + m[11]*n[2] + m[15]*n[3], + m[0]*n[4] + m[4]*n[5] + m[8]*n[6] + m[12]*n[7], m[1]*n[4] + m[5]*n[5] + m[9]*n[6] + m[13]*n[7], m[2]*n[4] + m[6]*n[5] + m[10]*n[6] + m[14]*n[7], m[3]*n[4] + m[7]*n[5] + m[11]*n[6] + m[15]*n[7], + m[0]*n[8] + m[4]*n[9] + m[8]*n[10] + m[12]*n[11], m[1]*n[8] + m[5]*n[9] + m[9]*n[10] + m[13]*n[11], m[2]*n[8] + m[6]*n[9] + m[10]*n[10] + m[14]*n[11], m[3]*n[8] + m[7]*n[9] + m[11]*n[10] + m[15]*n[11], + m[0]*n[12] + m[4]*n[13] + m[8]*n[14] + m[12]*n[15], m[1]*n[12] + m[5]*n[13] + m[9]*n[14] + m[13]*n[15], m[2]*n[12] + m[6]*n[13] + m[10]*n[14] + m[14]*n[15], m[3]*n[12] + m[7]*n[13] + m[11]*n[14] + m[15]*n[15]); +} + + + +inline Matrix4& Matrix4::operator*=(const Matrix4& rhs) +{ + *this = *this * rhs; + return *this; +} + + + +inline bool Matrix4::operator==(const Matrix4& n) const +{ + return (m[0] == n[0]) && (m[1] == n[1]) && (m[2] == n[2]) && (m[3] == n[3]) && + (m[4] == n[4]) && (m[5] == n[5]) && (m[6] == n[6]) && (m[7] == n[7]) && + (m[8] == n[8]) && (m[9] == n[9]) && (m[10]== n[10]) && (m[11]== n[11]) && + (m[12]== n[12]) && (m[13]== n[13]) && (m[14]== n[14]) && (m[15]== n[15]); +} + + + +inline bool Matrix4::operator!=(const Matrix4& n) const +{ + return (m[0] != n[0]) || (m[1] != n[1]) || (m[2] != n[2]) || (m[3] != n[3]) || + (m[4] != n[4]) || (m[5] != n[5]) || (m[6] != n[6]) || (m[7] != n[7]) || + (m[8] != n[8]) || (m[9] != n[9]) || (m[10]!= n[10]) || (m[11]!= n[11]) || + (m[12]!= n[12]) || (m[13]!= n[13]) || (m[14]!= n[14]) || (m[15]!= n[15]); +} + + + +inline float Matrix4::operator[](int index) const +{ + return m[index]; +} + + + +inline float& Matrix4::operator[](int index) +{ + return m[index]; +} + + + +inline Matrix4 operator-(const Matrix4& rhs) +{ + return Matrix4(-rhs[0], -rhs[1], -rhs[2], -rhs[3], -rhs[4], -rhs[5], -rhs[6], -rhs[7], -rhs[8], -rhs[9], -rhs[10], -rhs[11], -rhs[12], -rhs[13], -rhs[14], -rhs[15]); +} + + + +inline Matrix4 operator*(float s, const Matrix4& rhs) +{ + return Matrix4(s*rhs[0], s*rhs[1], s*rhs[2], s*rhs[3], s*rhs[4], s*rhs[5], s*rhs[6], s*rhs[7], s*rhs[8], s*rhs[9], s*rhs[10], s*rhs[11], s*rhs[12], s*rhs[13], s*rhs[14], s*rhs[15]); +} + + + +inline Vector4 operator*(const Vector4& v, const Matrix4& m) +{ + return Vector4(v.x*m[0] + v.y*m[1] + v.z*m[2] + v.w*m[3], v.x*m[4] + v.y*m[5] + v.z*m[6] + v.w*m[7], v.x*m[8] + v.y*m[9] + v.z*m[10] + v.w*m[11], v.x*m[12] + v.y*m[13] + v.z*m[14] + v.w*m[15]); +} + + + +inline Vector3 operator*(const Vector3& v, const Matrix4& m) +{ + return Vector3(v.x*m[0] + v.y*m[1] + v.z*m[2], v.x*m[4] + v.y*m[5] + v.z*m[6], v.x*m[8] + v.y*m[9] + v.z*m[10]); +} + + + +inline std::ostream& operator<<(std::ostream& os, const Matrix4& m) +{ + os << std::fixed << std::setprecision(5); + os << "[" << std::setw(10) << m[0] << " " << std::setw(10) << m[4] << " " << std::setw(10) << m[8] << " " << std::setw(10) << m[12] << "]\n" + << "[" << std::setw(10) << m[1] << " " << std::setw(10) << m[5] << " " << std::setw(10) << m[9] << " " << std::setw(10) << m[13] << "]\n" + << "[" << std::setw(10) << m[2] << " " << std::setw(10) << m[6] << " " << std::setw(10) << m[10] << " " << std::setw(10) << m[14] << "]\n" + << "[" << std::setw(10) << m[3] << " " << std::setw(10) << m[7] << " " << std::setw(10) << m[11] << " " << std::setw(10) << m[15] << "]\n"; + os << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); + return os; +} +// END OF MATRIX4 INLINE ////////////////////////////////////////////////////// +#endif diff --git a/shared/Vectors.h b/shared/Vectors.h new file mode 100644 index 0000000..2efb840 --- /dev/null +++ b/shared/Vectors.h @@ -0,0 +1,530 @@ +/////////////////////////////////////////////////////////////////////////////// +// Vectors.h +// ========= +// 2D/3D/4D vectors +// +// AUTHOR: Song Ho Ahn (song.ahn@gmail.com) +// CREATED: 2007-02-14 +// UPDATED: 2013-01-20 +// +// Copyright (C) 2007-2013 Song Ho Ahn +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef VECTORS_H_DEF +#define VECTORS_H_DEF + +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// 2D vector +/////////////////////////////////////////////////////////////////////////////// +struct Vector2 +{ + float x; + float y; + + // ctors + Vector2() : x(0), y(0) {}; + Vector2(float x, float y) : x(x), y(y) {}; + + // utils functions + void set(float x, float y); + float length() const; // + float distance(const Vector2& vec) const; // distance between two vectors + Vector2& normalize(); // + float dot(const Vector2& vec) const; // dot product + bool equal(const Vector2& vec, float e) const; // compare with epsilon + + // operators + Vector2 operator-() const; // unary operator (negate) + Vector2 operator+(const Vector2& rhs) const; // add rhs + Vector2 operator-(const Vector2& rhs) const; // subtract rhs + Vector2& operator+=(const Vector2& rhs); // add rhs and update this object + Vector2& operator-=(const Vector2& rhs); // subtract rhs and update this object + Vector2 operator*(const float scale) const; // scale + Vector2 operator*(const Vector2& rhs) const; // multiply each element + Vector2& operator*=(const float scale); // scale and update this object + Vector2& operator*=(const Vector2& rhs); // multiply each element and update this object + Vector2 operator/(const float scale) const; // inverse scale + Vector2& operator/=(const float scale); // scale and update this object + bool operator==(const Vector2& rhs) const; // exact compare, no epsilon + bool operator!=(const Vector2& rhs) const; // exact compare, no epsilon + bool operator<(const Vector2& rhs) const; // comparison for sort + float operator[](int index) const; // subscript operator v[0], v[1] + float& operator[](int index); // subscript operator v[0], v[1] + + friend Vector2 operator*(const float a, const Vector2 vec); + friend std::ostream& operator<<(std::ostream& os, const Vector2& vec); +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// 3D vector +/////////////////////////////////////////////////////////////////////////////// +struct Vector3 +{ + float x; + float y; + float z; + + // ctors + Vector3() : x(0), y(0), z(0) {}; + Vector3(float x, float y, float z) : x(x), y(y), z(z) {}; + + // utils functions + void set(float x, float y, float z); + float length() const; // + float distance(const Vector3& vec) const; // distance between two vectors + Vector3& normalize(); // + float dot(const Vector3& vec) const; // dot product + Vector3 cross(const Vector3& vec) const; // cross product + bool equal(const Vector3& vec, float e) const; // compare with epsilon + + // operators + Vector3 operator-() const; // unary operator (negate) + Vector3 operator+(const Vector3& rhs) const; // add rhs + Vector3 operator-(const Vector3& rhs) const; // subtract rhs + Vector3& operator+=(const Vector3& rhs); // add rhs and update this object + Vector3& operator-=(const Vector3& rhs); // subtract rhs and update this object + Vector3 operator*(const float scale) const; // scale + Vector3 operator*(const Vector3& rhs) const; // multiplay each element + Vector3& operator*=(const float scale); // scale and update this object + Vector3& operator*=(const Vector3& rhs); // product each element and update this object + Vector3 operator/(const float scale) const; // inverse scale + Vector3& operator/=(const float scale); // scale and update this object + bool operator==(const Vector3& rhs) const; // exact compare, no epsilon + bool operator!=(const Vector3& rhs) const; // exact compare, no epsilon + bool operator<(const Vector3& rhs) const; // comparison for sort + float operator[](int index) const; // subscript operator v[0], v[1] + float& operator[](int index); // subscript operator v[0], v[1] + + friend Vector3 operator*(const float a, const Vector3 vec); + friend std::ostream& operator<<(std::ostream& os, const Vector3& vec); +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// 4D vector +/////////////////////////////////////////////////////////////////////////////// +struct Vector4 +{ + float x; + float y; + float z; + float w; + + // ctors + Vector4() : x(0), y(0), z(0), w(0) {}; + Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}; + + // utils functions + void set(float x, float y, float z, float w); + float length() const; // + float distance(const Vector4& vec) const; // distance between two vectors + Vector4& normalize(); // + float dot(const Vector4& vec) const; // dot product + bool equal(const Vector4& vec, float e) const; // compare with epsilon + + // operators + Vector4 operator-() const; // unary operator (negate) + Vector4 operator+(const Vector4& rhs) const; // add rhs + Vector4 operator-(const Vector4& rhs) const; // subtract rhs + Vector4& operator+=(const Vector4& rhs); // add rhs and update this object + Vector4& operator-=(const Vector4& rhs); // subtract rhs and update this object + Vector4 operator*(const float scale) const; // scale + Vector4 operator*(const Vector4& rhs) const; // multiply each element + Vector4& operator*=(const float scale); // scale and update this object + Vector4& operator*=(const Vector4& rhs); // multiply each element and update this object + Vector4 operator/(const float scale) const; // inverse scale + Vector4& operator/=(const float scale); // scale and update this object + bool operator==(const Vector4& rhs) const; // exact compare, no epsilon + bool operator!=(const Vector4& rhs) const; // exact compare, no epsilon + bool operator<(const Vector4& rhs) const; // comparison for sort + float operator[](int index) const; // subscript operator v[0], v[1] + float& operator[](int index); // subscript operator v[0], v[1] + + friend Vector4 operator*(const float a, const Vector4 vec); + friend std::ostream& operator<<(std::ostream& os, const Vector4& vec); +}; + + + +// fast math routines from Doom3 SDK +inline float invSqrt(float x) +{ + float xhalf = 0.5f * x; + int i = *(int*)&x; // get bits for floating value + i = 0x5f3759df - (i>>1); // gives initial guess + x = *(float*)&i; // convert bits back to float + x = x * (1.5f - xhalf*x*x); // Newton step + return x; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// inline functions for Vector2 +/////////////////////////////////////////////////////////////////////////////// +inline Vector2 Vector2::operator-() const { + return Vector2(-x, -y); +} + +inline Vector2 Vector2::operator+(const Vector2& rhs) const { + return Vector2(x+rhs.x, y+rhs.y); +} + +inline Vector2 Vector2::operator-(const Vector2& rhs) const { + return Vector2(x-rhs.x, y-rhs.y); +} + +inline Vector2& Vector2::operator+=(const Vector2& rhs) { + x += rhs.x; y += rhs.y; return *this; +} + +inline Vector2& Vector2::operator-=(const Vector2& rhs) { + x -= rhs.x; y -= rhs.y; return *this; +} + +inline Vector2 Vector2::operator*(const float a) const { + return Vector2(x*a, y*a); +} + +inline Vector2 Vector2::operator*(const Vector2& rhs) const { + return Vector2(x*rhs.x, y*rhs.y); +} + +inline Vector2& Vector2::operator*=(const float a) { + x *= a; y *= a; return *this; +} + +inline Vector2& Vector2::operator*=(const Vector2& rhs) { + x *= rhs.x; y *= rhs.y; return *this; +} + +inline Vector2 Vector2::operator/(const float a) const { + return Vector2(x/a, y/a); +} + +inline Vector2& Vector2::operator/=(const float a) { + x /= a; y /= a; return *this; +} + +inline bool Vector2::operator==(const Vector2& rhs) const { + return (x == rhs.x) && (y == rhs.y); +} + +inline bool Vector2::operator!=(const Vector2& rhs) const { + return (x != rhs.x) || (y != rhs.y); +} + +inline bool Vector2::operator<(const Vector2& rhs) const { + if(x < rhs.x) return true; + if(x > rhs.x) return false; + if(y < rhs.y) return true; + if(y > rhs.y) return false; + return false; +} + +inline float Vector2::operator[](int index) const { + return (&x)[index]; +} + +inline float& Vector2::operator[](int index) { + return (&x)[index]; +} + +inline void Vector2::set(float x_, float y_) { + this->x = x_; this->y = y_; +} + +inline float Vector2::length() const { + return sqrtf(x*x + y*y); +} + +inline float Vector2::distance(const Vector2& vec) const { + return sqrtf((vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y)); +} + +inline Vector2& Vector2::normalize() { + //@@const float EPSILON = 0.000001f; + float xxyy = x*x + y*y; + //@@if(xxyy < EPSILON) + //@@ return *this; + + //float invLength = invSqrt(xxyy); + float invLength = 1.0f / sqrtf(xxyy); + x *= invLength; + y *= invLength; + return *this; +} + +inline float Vector2::dot(const Vector2& rhs) const { + return (x*rhs.x + y*rhs.y); +} + +inline bool Vector2::equal(const Vector2& rhs, float epsilon) const { + return fabs(x - rhs.x) < epsilon && fabs(y - rhs.y) < epsilon; +} + +inline Vector2 operator*(const float a, const Vector2 vec) { + return Vector2(a*vec.x, a*vec.y); +} + +inline std::ostream& operator<<(std::ostream& os, const Vector2& vec) { + os << "(" << vec.x << ", " << vec.y << ")"; + return os; +} +// END OF VECTOR2 ///////////////////////////////////////////////////////////// + + + + +/////////////////////////////////////////////////////////////////////////////// +// inline functions for Vector3 +/////////////////////////////////////////////////////////////////////////////// +inline Vector3 Vector3::operator-() const { + return Vector3(-x, -y, -z); +} + +inline Vector3 Vector3::operator+(const Vector3& rhs) const { + return Vector3(x+rhs.x, y+rhs.y, z+rhs.z); +} + +inline Vector3 Vector3::operator-(const Vector3& rhs) const { + return Vector3(x-rhs.x, y-rhs.y, z-rhs.z); +} + +inline Vector3& Vector3::operator+=(const Vector3& rhs) { + x += rhs.x; y += rhs.y; z += rhs.z; return *this; +} + +inline Vector3& Vector3::operator-=(const Vector3& rhs) { + x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; +} + +inline Vector3 Vector3::operator*(const float a) const { + return Vector3(x*a, y*a, z*a); +} + +inline Vector3 Vector3::operator*(const Vector3& rhs) const { + return Vector3(x*rhs.x, y*rhs.y, z*rhs.z); +} + +inline Vector3& Vector3::operator*=(const float a) { + x *= a; y *= a; z *= a; return *this; +} + +inline Vector3& Vector3::operator*=(const Vector3& rhs) { + x *= rhs.x; y *= rhs.y; z *= rhs.z; return *this; +} + +inline Vector3 Vector3::operator/(const float a) const { + return Vector3(x/a, y/a, z/a); +} + +inline Vector3& Vector3::operator/=(const float a) { + x /= a; y /= a; z /= a; return *this; +} + +inline bool Vector3::operator==(const Vector3& rhs) const { + return (x == rhs.x) && (y == rhs.y) && (z == rhs.z); +} + +inline bool Vector3::operator!=(const Vector3& rhs) const { + return (x != rhs.x) || (y != rhs.y) || (z != rhs.z); +} + +inline bool Vector3::operator<(const Vector3& rhs) const { + if(x < rhs.x) return true; + if(x > rhs.x) return false; + if(y < rhs.y) return true; + if(y > rhs.y) return false; + if(z < rhs.z) return true; + if(z > rhs.z) return false; + return false; +} + +inline float Vector3::operator[](int index) const { + return (&x)[index]; +} + +inline float& Vector3::operator[](int index) { + return (&x)[index]; +} + +inline void Vector3::set(float x_, float y_, float z_) { + this->x = x_; this->y = y_; this->z = z_; +} + +inline float Vector3::length() const { + return sqrtf(x*x + y*y + z*z); +} + +inline float Vector3::distance(const Vector3& vec) const { + return sqrtf((vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y) + (vec.z-z)*(vec.z-z)); +} + +inline Vector3& Vector3::normalize() { + //@@const float EPSILON = 0.000001f; + float xxyyzz = x*x + y*y + z*z; + //@@if(xxyyzz < EPSILON) + //@@ return *this; // do nothing if it is ~zero vector + + //float invLength = invSqrt(xxyyzz); + float invLength = 1.0f / sqrtf(xxyyzz); + x *= invLength; + y *= invLength; + z *= invLength; + return *this; +} + +inline float Vector3::dot(const Vector3& rhs) const { + return (x*rhs.x + y*rhs.y + z*rhs.z); +} + +inline Vector3 Vector3::cross(const Vector3& rhs) const { + return Vector3(y*rhs.z - z*rhs.y, z*rhs.x - x*rhs.z, x*rhs.y - y*rhs.x); +} + +inline bool Vector3::equal(const Vector3& rhs, float epsilon) const { + return fabs(x - rhs.x) < epsilon && fabs(y - rhs.y) < epsilon && fabs(z - rhs.z) < epsilon; +} + +inline Vector3 operator*(const float a, const Vector3 vec) { + return Vector3(a*vec.x, a*vec.y, a*vec.z); +} + +inline std::ostream& operator<<(std::ostream& os, const Vector3& vec) { + os << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")"; + return os; +} +// END OF VECTOR3 ///////////////////////////////////////////////////////////// + + + +/////////////////////////////////////////////////////////////////////////////// +// inline functions for Vector4 +/////////////////////////////////////////////////////////////////////////////// +inline Vector4 Vector4::operator-() const { + return Vector4(-x, -y, -z, -w); +} + +inline Vector4 Vector4::operator+(const Vector4& rhs) const { + return Vector4(x+rhs.x, y+rhs.y, z+rhs.z, w+rhs.w); +} + +inline Vector4 Vector4::operator-(const Vector4& rhs) const { + return Vector4(x-rhs.x, y-rhs.y, z-rhs.z, w-rhs.w); +} + +inline Vector4& Vector4::operator+=(const Vector4& rhs) { + x += rhs.x; y += rhs.y; z += rhs.z; w += rhs.w; return *this; +} + +inline Vector4& Vector4::operator-=(const Vector4& rhs) { + x -= rhs.x; y -= rhs.y; z -= rhs.z; w -= rhs.w; return *this; +} + +inline Vector4 Vector4::operator*(const float a) const { + return Vector4(x*a, y*a, z*a, w*a); +} + +inline Vector4 Vector4::operator*(const Vector4& rhs) const { + return Vector4(x*rhs.x, y*rhs.y, z*rhs.z, w*rhs.w); +} + +inline Vector4& Vector4::operator*=(const float a) { + x *= a; y *= a; z *= a; w *= a; return *this; +} + +inline Vector4& Vector4::operator*=(const Vector4& rhs) { + x *= rhs.x; y *= rhs.y; z *= rhs.z; w *= rhs.w; return *this; +} + +inline Vector4 Vector4::operator/(const float a) const { + return Vector4(x/a, y/a, z/a, w/a); +} + +inline Vector4& Vector4::operator/=(const float a) { + x /= a; y /= a; z /= a; w /= a; return *this; +} + +inline bool Vector4::operator==(const Vector4& rhs) const { + return (x == rhs.x) && (y == rhs.y) && (z == rhs.z) && (w == rhs.w); +} + +inline bool Vector4::operator!=(const Vector4& rhs) const { + return (x != rhs.x) || (y != rhs.y) || (z != rhs.z) || (w != rhs.w); +} + +inline bool Vector4::operator<(const Vector4& rhs) const { + if(x < rhs.x) return true; + if(x > rhs.x) return false; + if(y < rhs.y) return true; + if(y > rhs.y) return false; + if(z < rhs.z) return true; + if(z > rhs.z) return false; + if(w < rhs.w) return true; + if(w > rhs.w) return false; + return false; +} + +inline float Vector4::operator[](int index) const { + return (&x)[index]; +} + +inline float& Vector4::operator[](int index) { + return (&x)[index]; +} + +inline void Vector4::set(float x_, float y_, float z_, float w_) { + this->x = x_; this->y = y_; this->z = z_; this->w = w_; +} + +inline float Vector4::length() const { + return sqrtf(x*x + y*y + z*z + w*w); +} + +inline float Vector4::distance(const Vector4& vec) const { + return sqrtf((vec.x-x)*(vec.x-x) + (vec.y-y)*(vec.y-y) + (vec.z-z)*(vec.z-z) + (vec.w-w)*(vec.w-w)); +} + +inline Vector4& Vector4::normalize() { + //NOTE: leave w-component untouched + //@@const float EPSILON = 0.000001f; + float xxyyzz = x*x + y*y + z*z; + //@@if(xxyyzz < EPSILON) + //@@ return *this; // do nothing if it is zero vector + + //float invLength = invSqrt(xxyyzz); + float invLength = 1.0f / sqrtf(xxyyzz); + x *= invLength; + y *= invLength; + z *= invLength; + return *this; +} + +inline float Vector4::dot(const Vector4& rhs) const { + return (x*rhs.x + y*rhs.y + z*rhs.z + w*rhs.w); +} + +inline bool Vector4::equal(const Vector4& rhs, float epsilon) const { + return fabs(x - rhs.x) < epsilon && fabs(y - rhs.y) < epsilon && + fabs(z - rhs.z) < epsilon && fabs(w - rhs.w) < epsilon; +} + +inline Vector4 operator*(const float a, const Vector4 vec) { + return Vector4(a*vec.x, a*vec.y, a*vec.z, a*vec.w); +} + +inline std::ostream& operator<<(std::ostream& os, const Vector4& vec) { + os << "(" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ")"; + return os; +} +// END OF VECTOR4 ///////////////////////////////////////////////////////////// + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ba98f14 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,2130 @@ +//========= Copyright Valve Corporation ============// +//Original BSD 3 License by Valve Corporation: +//Copyright (c) 2015, Valve Corporation +//All rights reserved. + +//Redistribution and use in source and binary forms, with or without modification, +//are permitted provided that the following conditions are met: + +//1. Redistributions of source code must retain the above copyright notice, this +//list of conditions and the following disclaimer. + +//2. Redistributions in binary form must reproduce the above copyright notice, +//this list of conditions and the following disclaimer in the documentation and/or +//other materials provided with the distribution. + +//3. Neither the name of the copyright holder nor the names of its contributors +//may be used to endorse or promote products derived from this software without +//specific prior written permission. + +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +//ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Modified by: DEC05EBA + +#include +#include +#include +#include +#include +#define GLX_GLXEXT_PROTOTYPES +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "../shared/Matrices.h" + +#ifndef _countof +#define _countof(x) (sizeof(x)/sizeof((x)[0])) +#endif + +void ThreadSleep( unsigned long nMilliseconds ) +{ + usleep( nMilliseconds * 1000 ); +} + +class CGLRenderModel +{ +public: + CGLRenderModel( const std::string & sRenderModelName ); + ~CGLRenderModel(); + + bool BInit( const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture ); + void Cleanup(); + void Draw(); + const std::string & GetName() const { return m_sModelName; } + +private: + GLuint m_glVertBuffer; + GLuint m_glIndexBuffer; + GLuint m_glVertArray; + GLuint m_glTexture; + GLsizei m_unVertexCount; + std::string m_sModelName; +}; + +static bool g_bPrintf = true; + +//----------------------------------------------------------------------------- +// Purpose: +//------------------------------------------------------------------------------ +class CMainApplication +{ +public: + CMainApplication( int argc, char *argv[] ); + virtual ~CMainApplication(); + + bool BInit(); + bool BInitGL(); + bool BInitCompositor(); + + void Shutdown(); + + void RunMainLoop(); + bool HandleInput(); + void ProcessVREvent( const vr::VREvent_t & event ); + void RenderFrame(); + + bool SetupTexturemaps(); + + void SetupScene(); + void AddCubeToScene( Matrix4 mat, std::vector &vertdata ); + void AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector &vertdata ); + + void RenderControllerAxes(); + + bool SetupStereoRenderTargets(); + void SetupCompanionWindow(); + void SetupCameras(); + + void RenderStereoTargets(); + void RenderCompanionWindow(); + void RenderScene( vr::Hmd_Eye nEye ); + + Matrix4 GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye ); + Matrix4 GetHMDMatrixPoseEye( vr::Hmd_Eye nEye ); + Matrix4 GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye ); + void UpdateHMDMatrixPose(); + + Matrix4 ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose ); + + GLuint CompileGLShader( const char *pchShaderName, const char *pchVertexShader, const char *pchFragmentShader ); + bool CreateAllShaders(); + + CGLRenderModel *FindOrLoadRenderModel( const char *pchRenderModelName ); + +private: + bool m_bDebugOpenGL; + bool m_bVerbose; + bool m_bPerf; + bool m_bVblank; + bool m_bGlFinishHack; + + vr::IVRSystem *m_pHMD; + std::string m_strDriver; + std::string m_strDisplay; + vr::TrackedDevicePose_t m_rTrackedDevicePose[ vr::k_unMaxTrackedDeviceCount ]; + Matrix4 m_rmat4DevicePose[ vr::k_unMaxTrackedDeviceCount ]; + + struct ControllerInfo_t + { + vr::VRInputValueHandle_t m_source = vr::k_ulInvalidInputValueHandle; + vr::VRActionHandle_t m_actionPose = vr::k_ulInvalidActionHandle; + vr::VRActionHandle_t m_actionHaptic = vr::k_ulInvalidActionHandle; + Matrix4 m_rmat4Pose; + CGLRenderModel *m_pRenderModel = nullptr; + std::string m_sRenderModelName; + bool m_bShowController; + }; + + enum EHand + { + Left = 0, + Right = 1, + }; + ControllerInfo_t m_rHand[2]; + +private: // SDL bookkeeping + SDL_Window *m_pCompanionWindow; + uint32_t m_nCompanionWindowWidth; + uint32_t m_nCompanionWindowHeight; + + SDL_GLContext m_pContext; + +private: // OpenGL bookkeeping + int m_iTrackedControllerCount; + int m_iTrackedControllerCount_Last; + int m_iValidPoseCount; + int m_iValidPoseCount_Last; + bool m_bShowCubes; + Vector2 m_vAnalogValue; + + std::string m_strPoseClasses; // what classes we saw poses for this frame + char m_rDevClassChar[ vr::k_unMaxTrackedDeviceCount ]; // for each device, a character representing its class + + int m_iSceneVolumeWidth; + int m_iSceneVolumeHeight; + int m_iSceneVolumeDepth; + float m_fScaleSpacing; + float m_fScale; + + int m_iSceneVolumeInit; // if you want something other than the default 20x20x20 + + float m_fNearClip; + float m_fFarClip; + + GLuint m_iTexture; + + unsigned int m_uiVertcount; + + GLuint m_glSceneVertBuffer; + GLuint m_unSceneVAO; + GLuint m_unCompanionWindowVAO; + GLuint m_glCompanionWindowIDVertBuffer; + GLuint m_glCompanionWindowIDIndexBuffer; + unsigned int m_uiCompanionWindowIndexSize; + + GLuint m_glControllerVertBuffer; + GLuint m_unControllerVAO; + unsigned int m_uiControllerVertcount; + + Matrix4 m_mat4HMDPose; + Matrix4 m_mat4eyePosLeft; + Matrix4 m_mat4eyePosRight; + + Matrix4 m_resetPos; + + Matrix4 m_mat4ProjectionCenter; + Matrix4 m_mat4ProjectionLeft; + Matrix4 m_mat4ProjectionRight; + + struct VertexDataScene + { + Vector3 position; + Vector2 texCoord; + }; + + struct VertexDataWindow + { + Vector2 position; + Vector2 texCoord; + + VertexDataWindow( const Vector2 & pos, const Vector2 tex ) : position(pos), texCoord(tex) { } + }; + + GLuint m_unSceneProgramID; + GLuint m_unCompanionWindowProgramID; + GLuint m_unControllerTransformProgramID; + GLuint m_unRenderModelProgramID; + + GLint m_nSceneMatrixLocation; + GLint m_nControllerMatrixLocation; + GLint m_nRenderModelMatrixLocation; + + struct FramebufferDesc + { + GLuint m_nDepthBufferId; + GLuint m_nRenderTextureId; + GLuint m_nRenderFramebufferId; + GLuint m_nResolveTextureId; + GLuint m_nResolveFramebufferId; + }; + FramebufferDesc leftEyeDesc; + FramebufferDesc rightEyeDesc; + + bool CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc ); + + uint32_t m_nRenderWidth; + uint32_t m_nRenderHeight; + + std::vector< CGLRenderModel * > m_vecRenderModels; + + vr::VRActionHandle_t m_actionHideCubes = vr::k_ulInvalidActionHandle; + vr::VRActionHandle_t m_actionHideThisController = vr::k_ulInvalidActionHandle; + vr::VRActionHandle_t m_actionTriggerHaptic = vr::k_ulInvalidActionHandle; + vr::VRActionHandle_t m_actionAnalongInput = vr::k_ulInvalidActionHandle; + + vr::VRActionSetHandle_t m_actionsetDemo = vr::k_ulInvalidActionSetHandle; + +private: // X compositor + Display *x_display = nullptr; + Window src_window_id; + Pixmap src_window_pixmap; + GLXFBConfig *configs; + GLXPixmap glxpixmap; +}; + + +//--------------------------------------------------------------------------------------------------------------------- +// Purpose: Returns true if the action is active and had a rising edge +//--------------------------------------------------------------------------------------------------------------------- +bool GetDigitalActionRisingEdge(vr::VRActionHandle_t action, vr::VRInputValueHandle_t *pDevicePath = nullptr ) +{ + vr::InputDigitalActionData_t actionData; + vr::VRInput()->GetDigitalActionData(action, &actionData, sizeof(actionData), vr::k_ulInvalidInputValueHandle ); + if (pDevicePath) + { + *pDevicePath = vr::k_ulInvalidInputValueHandle; + if (actionData.bActive) + { + vr::InputOriginInfo_t originInfo; + if (vr::VRInputError_None == vr::VRInput()->GetOriginTrackedDeviceInfo(actionData.activeOrigin, &originInfo, sizeof(originInfo))) + { + *pDevicePath = originInfo.devicePath; + } + } + } + return actionData.bActive && actionData.bChanged && actionData.bState; +} + + +//--------------------------------------------------------------------------------------------------------------------- +// Purpose: Returns true if the action is active and had a falling edge +//--------------------------------------------------------------------------------------------------------------------- +bool GetDigitalActionFallingEdge(vr::VRActionHandle_t action, vr::VRInputValueHandle_t *pDevicePath = nullptr ) +{ + vr::InputDigitalActionData_t actionData; + vr::VRInput()->GetDigitalActionData(action, &actionData, sizeof(actionData), vr::k_ulInvalidInputValueHandle ); + if (pDevicePath) + { + *pDevicePath = vr::k_ulInvalidInputValueHandle; + if (actionData.bActive) + { + vr::InputOriginInfo_t originInfo; + if (vr::VRInputError_None == vr::VRInput()->GetOriginTrackedDeviceInfo(actionData.activeOrigin, &originInfo, sizeof(originInfo))) + { + *pDevicePath = originInfo.devicePath; + } + } + } + return actionData.bActive && actionData.bChanged && !actionData.bState; +} + + +//--------------------------------------------------------------------------------------------------------------------- +// Purpose: Returns true if the action is active and its state is true +//--------------------------------------------------------------------------------------------------------------------- +bool GetDigitalActionState(vr::VRActionHandle_t action, vr::VRInputValueHandle_t *pDevicePath = nullptr ) +{ + vr::InputDigitalActionData_t actionData; + vr::VRInput()->GetDigitalActionData(action, &actionData, sizeof(actionData), vr::k_ulInvalidInputValueHandle ); + if (pDevicePath) + { + *pDevicePath = vr::k_ulInvalidInputValueHandle; + if (actionData.bActive) + { + vr::InputOriginInfo_t originInfo; + if (vr::VRInputError_None == vr::VRInput()->GetOriginTrackedDeviceInfo(actionData.activeOrigin, &originInfo, sizeof(originInfo))) + { + *pDevicePath = originInfo.devicePath; + } + } + } + return actionData.bActive && actionData.bState; +} + +//----------------------------------------------------------------------------- +// Purpose: Outputs a set of optional arguments to debugging output, using +// the printf format setting specified in fmt*. +//----------------------------------------------------------------------------- +void dprintf( const char *fmt, ... ) +{ + va_list args; + char buffer[ 2048 ]; + + va_start( args, fmt ); + vsnprintf( buffer, sizeof(buffer), fmt, args ); + va_end( args ); + + if ( g_bPrintf ) + printf( "%s", buffer ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMainApplication::CMainApplication( int argc, char *argv[] ) + : m_pCompanionWindow(NULL) + , m_pContext(NULL) + , m_nCompanionWindowWidth( 1920 ) + , m_nCompanionWindowHeight( 1080 ) + , m_unSceneProgramID( 0 ) + , m_unCompanionWindowProgramID( 0 ) + , m_unControllerTransformProgramID( 0 ) + , m_unRenderModelProgramID( 0 ) + , m_pHMD( NULL ) + , m_bDebugOpenGL( false ) + , m_bVerbose( false ) + , m_bPerf( false ) + , m_bVblank( false ) + , m_bGlFinishHack( true ) + , m_glControllerVertBuffer( 0 ) + , m_unControllerVAO( 0 ) + , m_unSceneVAO( 0 ) + , m_nSceneMatrixLocation( -1 ) + , m_nControllerMatrixLocation( -1 ) + , m_nRenderModelMatrixLocation( -1 ) + , m_iTrackedControllerCount( 0 ) + , m_iTrackedControllerCount_Last( -1 ) + , m_iValidPoseCount( 0 ) + , m_iValidPoseCount_Last( -1 ) + , m_iSceneVolumeInit( 10 ) + , m_strPoseClasses("") + , m_bShowCubes( true ) +{ + if(argc < 2) { + fprintf(stderr, "usage: vr_window_manager [window_id]\n"); + exit(1); + } + + src_window_id = strtol(argv[1], nullptr, 0); + printf("src window id: %d\n", src_window_id); + +#if 0 + for( int i = 1; i < argc; i++ ) + { + if( !strcmp( argv[i], "-gldebug" ) ) + { + m_bDebugOpenGL = true; + } + else if( !strcmp( argv[i], "-verbose" ) ) + { + m_bVerbose = true; + } + else if( !strcmp( argv[i], "-novblank" ) ) + { + m_bVblank = false; + } + else if( !strcmp( argv[i], "-noglfinishhack" ) ) + { + m_bGlFinishHack = false; + } + else if( !strcmp( argv[i], "-noprintf" ) ) + { + g_bPrintf = false; + } + else if ( !strcmp( argv[i], "-cubevolume" ) && ( argc > i + 1 ) && ( *argv[ i + 1 ] != '-' ) ) + { + m_iSceneVolumeInit = atoi( argv[ i + 1 ] ); + i++; + } + } +#endif + // other initialization tasks are done in BInit + memset(m_rDevClassChar, 0, sizeof(m_rDevClassChar)); +}; + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CMainApplication::~CMainApplication() +{ + // work is done in Shutdown + dprintf( "Shutdown" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Helper to get a string from a tracked device property and turn it +// into a std::string +//----------------------------------------------------------------------------- +std::string GetTrackedDeviceString( vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL ) +{ + uint32_t unRequiredBufferLen = vr::VRSystem()->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError ); + if( unRequiredBufferLen == 0 ) + return ""; + + char *pchBuffer = new char[ unRequiredBufferLen ]; + unRequiredBufferLen = vr::VRSystem()->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError ); + std::string sResult = pchBuffer; + delete [] pchBuffer; + return sResult; +} + +static bool x11_supports_composite_named_window_pixmap(Display *dpy) +{ + int extension_major; + int extension_minor; + if(!XCompositeQueryExtension(dpy, &extension_major, &extension_minor)) + return false; + + int major_version; + int minor_version; + return XCompositeQueryVersion(dpy, &major_version, &minor_version) && (major_version > 0 || minor_version >= 2); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMainApplication::BInit() +{ + x_display = XOpenDisplay(nullptr); + if (!x_display) + { + printf("Failed to open x display\n"); + return false; + } + + bool has_name_pixmap = x11_supports_composite_named_window_pixmap(x_display); + if(!has_name_pixmap) + { + fprintf(stderr, "Error: XComposite name pixmap is not supported by your X11 server\n"); + return false; + } + + int screen_count = ScreenCount(x_display); + for(int i = 0; i < screen_count; ++i) + { + XCompositeRedirectSubwindows(x_display, RootWindow(x_display, i), CompositeRedirectAutomatic); + } + + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0 ) + { + printf("%s - SDL could not initialize! SDL Error: %s\n", __FUNCTION__, SDL_GetError()); + return false; + } + + // Loading the SteamVR Runtime + vr::EVRInitError eError = vr::VRInitError_None; + m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Scene ); + + if ( eError != vr::VRInitError_None ) + { + m_pHMD = NULL; + char buf[1024]; + snprintf( buf, sizeof( buf ), "Unable to init VR runtime: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) ); + SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "VR_Init Failed", buf, NULL ); + return false; + } + + + int nWindowPosX = 700; + int nWindowPosY = 100; + Uint32 unWindowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; + + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 ); + //SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE ); + + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 0 ); + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 0 ); + if( m_bDebugOpenGL ) + SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG ); + + m_pCompanionWindow = SDL_CreateWindow( "hellovr", nWindowPosX, nWindowPosY, m_nCompanionWindowWidth, m_nCompanionWindowHeight, unWindowFlags ); + if (m_pCompanionWindow == NULL) + { + printf( "%s - Window could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); + return false; + } + + m_pContext = SDL_GL_CreateContext(m_pCompanionWindow); + if (m_pContext == NULL) + { + printf( "%s - OpenGL context could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); + return false; + } + + glewExperimental = GL_TRUE; + GLenum nGlewError = glewInit(); + if (nGlewError != GLEW_OK) + { + printf( "%s - Error initializing GLEW! %s\n", __FUNCTION__, glewGetErrorString( nGlewError ) ); + return false; + } + glGetError(); // to clear the error caused deep in GLEW + + if ( SDL_GL_SetSwapInterval( m_bVblank ? 1 : 0 ) < 0 ) + { + printf( "%s - Warning: Unable to set VSync! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); + return false; + } + + + m_strDriver = "No Driver"; + m_strDisplay = "No Display"; + + m_strDriver = GetTrackedDeviceString( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String ); + m_strDisplay = GetTrackedDeviceString( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String ); + + std::string strWindowTitle = "hellovr - " + m_strDriver + " " + m_strDisplay; + SDL_SetWindowTitle( m_pCompanionWindow, strWindowTitle.c_str() ); + + // cube array + m_iSceneVolumeWidth = m_iSceneVolumeInit; + m_iSceneVolumeHeight = m_iSceneVolumeInit; + m_iSceneVolumeDepth = m_iSceneVolumeInit; + + m_fScale = 1.0f; + m_fScaleSpacing = 2.0f; + + m_fNearClip = 0.1f; + m_fFarClip = 30.0f; + + m_iTexture = 0; + m_uiVertcount = 0; + +// m_MillisecondsTimer.start(1, this); +// m_SecondsTimer.start(1000, this); + + if (!BInitGL()) + { + printf("%s - Unable to initialize OpenGL!\n", __FUNCTION__); + return false; + } + + if (!BInitCompositor()) + { + printf("%s - Failed to initialize VR Compositor!\n", __FUNCTION__); + return false; + } + + //char cwd[4096]; + //getcwd(cwd, sizeof(cwd)); + //dirname(cwd); + + vr::VRInput()->SetActionManifestPath("../config/hellovr_actions.json"); + + vr::VRInput()->GetActionHandle( "/actions/demo/in/HideCubes", &m_actionHideCubes ); + vr::VRInput()->GetActionHandle( "/actions/demo/in/HideThisController", &m_actionHideThisController); + vr::VRInput()->GetActionHandle( "/actions/demo/in/TriggerHaptic", &m_actionTriggerHaptic ); + vr::VRInput()->GetActionHandle( "/actions/demo/in/AnalogInput", &m_actionAnalongInput ); + + vr::VRInput()->GetActionSetHandle( "/actions/demo", &m_actionsetDemo ); + + vr::VRInput()->GetActionHandle( "/actions/demo/out/Haptic_Left", &m_rHand[Left].m_actionHaptic ); + vr::VRInput()->GetInputSourceHandle( "/user/hand/left", &m_rHand[Left].m_source ); + vr::VRInput()->GetActionHandle( "/actions/demo/in/Hand_Left", &m_rHand[Left].m_actionPose ); + + vr::VRInput()->GetActionHandle( "/actions/demo/out/Haptic_Right", &m_rHand[Right].m_actionHaptic ); + vr::VRInput()->GetInputSourceHandle( "/user/hand/right", &m_rHand[Right].m_source ); + vr::VRInput()->GetActionHandle( "/actions/demo/in/Hand_Right", &m_rHand[Right].m_actionPose ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Outputs the string in message to debugging output. +// All other parameters are ignored. +// Does not return any meaningful value or reference. +//----------------------------------------------------------------------------- +void DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* message, const void* userParam) +{ + dprintf( "GL Error: %s\n", message ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Initialize OpenGL. Returns true if OpenGL has been successfully +// initialized, false if shaders could not be created. +// If failure occurred in a module other than shaders, the function +// may return true or throw an error. +//----------------------------------------------------------------------------- +bool CMainApplication::BInitGL() +{ + if( m_bDebugOpenGL ) + { + glDebugMessageCallback( (GLDEBUGPROC)DebugCallback, nullptr); + glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE ); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } + + if( !CreateAllShaders() ) + return false; + + if(!SetupTexturemaps()) + return false; + SetupScene(); + SetupCameras(); + if(!SetupStereoRenderTargets()) + return false; + SetupCompanionWindow(); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Initialize Compositor. Returns true if the compositor was +// successfully initialized, false otherwise. +//----------------------------------------------------------------------------- +bool CMainApplication::BInitCompositor() +{ + vr::EVRInitError peError = vr::VRInitError_None; + + if ( !vr::VRCompositor() ) + { + printf( "Compositor initialization failed. See log file for details\n" ); + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::Shutdown() +{ + if( m_pHMD ) + { + vr::VR_Shutdown(); + m_pHMD = NULL; + } + + for( std::vector< CGLRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ ) + { + delete (*i); + } + m_vecRenderModels.clear(); + + if( m_pContext ) + { + if( m_bDebugOpenGL ) + { + glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE ); + glDebugMessageCallback(nullptr, nullptr); + } + glDeleteBuffers(1, &m_glSceneVertBuffer); + + if ( m_unSceneProgramID ) + { + glDeleteProgram( m_unSceneProgramID ); + } + if ( m_unControllerTransformProgramID ) + { + glDeleteProgram( m_unControllerTransformProgramID ); + } + if ( m_unRenderModelProgramID ) + { + glDeleteProgram( m_unRenderModelProgramID ); + } + if ( m_unCompanionWindowProgramID ) + { + glDeleteProgram( m_unCompanionWindowProgramID ); + } + + glDeleteRenderbuffers( 1, &leftEyeDesc.m_nDepthBufferId ); + glDeleteTextures( 1, &leftEyeDesc.m_nRenderTextureId ); + glDeleteFramebuffers( 1, &leftEyeDesc.m_nRenderFramebufferId ); + glDeleteTextures( 1, &leftEyeDesc.m_nResolveTextureId ); + glDeleteFramebuffers( 1, &leftEyeDesc.m_nResolveFramebufferId ); + + glDeleteRenderbuffers( 1, &rightEyeDesc.m_nDepthBufferId ); + glDeleteTextures( 1, &rightEyeDesc.m_nRenderTextureId ); + glDeleteFramebuffers( 1, &rightEyeDesc.m_nRenderFramebufferId ); + glDeleteTextures( 1, &rightEyeDesc.m_nResolveTextureId ); + glDeleteFramebuffers( 1, &rightEyeDesc.m_nResolveFramebufferId ); + + if( m_unCompanionWindowVAO != 0 ) + { + glDeleteVertexArrays( 1, &m_unCompanionWindowVAO ); + } + if( m_unSceneVAO != 0 ) + { + glDeleteVertexArrays( 1, &m_unSceneVAO ); + } + if( m_unControllerVAO != 0 ) + { + glDeleteVertexArrays( 1, &m_unControllerVAO ); + } + } + + if( m_pCompanionWindow ) + { + SDL_DestroyWindow(m_pCompanionWindow); + m_pCompanionWindow = NULL; + } + + SDL_Quit(); + + if (x_display) { + glXReleaseTexImageEXT(x_display, glxpixmap, GLX_FRONT_EXT); + glXDestroyPixmap(x_display, glxpixmap); + XFree(configs); + XFreePixmap(x_display, src_window_pixmap); + XCloseDisplay(x_display); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMainApplication::HandleInput() +{ + SDL_Event sdlEvent; + bool bRet = false; + + while ( SDL_PollEvent( &sdlEvent ) != 0 ) + { + if ( sdlEvent.type == SDL_QUIT ) + { + bRet = true; + } + else if ( sdlEvent.type == SDL_KEYDOWN ) + { + if ( sdlEvent.key.keysym.sym == SDLK_ESCAPE + || sdlEvent.key.keysym.sym == SDLK_q ) + { + bRet = true; + } + if( sdlEvent.key.keysym.sym == SDLK_c ) + { + m_bShowCubes = !m_bShowCubes; + } + } + } + + XEvent x_event; + +#if 0 + if(XCheckTypedWindowEvent(x_display, src_window_id, damage_event + XDamageNotify, &x_event)) { + } +#endif + + + + // Process SteamVR events + vr::VREvent_t event; + while( m_pHMD->PollNextEvent( &event, sizeof( event ) ) ) + { + ProcessVREvent( event ); + } + + // Process SteamVR action state + // UpdateActionState is called each frame to update the state of the actions themselves. The application + // controls which action sets are active with the provided array of VRActiveActionSet_t structs. + vr::VRActiveActionSet_t actionSet = { 0 }; + actionSet.ulActionSet = m_actionsetDemo; + vr::VRInput()->UpdateActionState( &actionSet, sizeof(actionSet), 1 ); + + m_bShowCubes = !GetDigitalActionState( m_actionHideCubes ); + if(GetDigitalActionState( m_actionHideCubes )) { + printf("reset position!\n"); + //m_resetPos = m_mat4HMDPose; + } + + vr::VRInputValueHandle_t ulHapticDevice; + if ( GetDigitalActionRisingEdge( m_actionTriggerHaptic, &ulHapticDevice ) ) + { + if ( ulHapticDevice == m_rHand[Left].m_source ) + { + vr::VRInput()->TriggerHapticVibrationAction( m_rHand[Left].m_actionHaptic, 0, 1, 4.f, 1.0f, vr::k_ulInvalidInputValueHandle ); + } + if ( ulHapticDevice == m_rHand[Right].m_source ) + { + vr::VRInput()->TriggerHapticVibrationAction( m_rHand[Right].m_actionHaptic, 0, 1, 4.f, 1.0f, vr::k_ulInvalidInputValueHandle ); + } + } + + vr::InputAnalogActionData_t analogData; + if ( vr::VRInput()->GetAnalogActionData( m_actionAnalongInput, &analogData, sizeof( analogData ), vr::k_ulInvalidInputValueHandle ) == vr::VRInputError_None && analogData.bActive ) + { + m_vAnalogValue[0] = analogData.x; + m_vAnalogValue[1] = analogData.y; + } + + m_rHand[Left].m_bShowController = true; + m_rHand[Right].m_bShowController = true; + + vr::VRInputValueHandle_t ulHideDevice; + if ( GetDigitalActionState( m_actionHideThisController, &ulHideDevice ) ) + { + if ( ulHideDevice == m_rHand[Left].m_source ) + { + m_rHand[Left].m_bShowController = false; + } + if ( ulHideDevice == m_rHand[Right].m_source ) + { + m_rHand[Right].m_bShowController = false; + } + } + + for ( EHand eHand = Left; eHand <= Right; ((int&)eHand)++ ) + { + vr::InputPoseActionData_t poseData; + if ( vr::VRInput()->GetPoseActionDataForNextFrame( m_rHand[eHand].m_actionPose, vr::TrackingUniverseStanding, &poseData, sizeof( poseData ), vr::k_ulInvalidInputValueHandle ) != vr::VRInputError_None + || !poseData.bActive || !poseData.pose.bPoseIsValid ) + { + m_rHand[eHand].m_bShowController = false; + } + else + { + m_rHand[eHand].m_rmat4Pose = ConvertSteamVRMatrixToMatrix4( poseData.pose.mDeviceToAbsoluteTracking ); + + vr::InputOriginInfo_t originInfo; + if ( vr::VRInput()->GetOriginTrackedDeviceInfo( poseData.activeOrigin, &originInfo, sizeof( originInfo ) ) == vr::VRInputError_None + && originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid ) + { + std::string sRenderModelName = GetTrackedDeviceString( originInfo.trackedDeviceIndex, vr::Prop_RenderModelName_String ); + if ( sRenderModelName != m_rHand[eHand].m_sRenderModelName ) + { + m_rHand[eHand].m_pRenderModel = FindOrLoadRenderModel( sRenderModelName.c_str() ); + m_rHand[eHand].m_sRenderModelName = sRenderModelName; + } + } + } + } + + return bRet; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::RunMainLoop() +{ + bool bQuit = false; + + SDL_StartTextInput(); + SDL_ShowCursor( SDL_DISABLE ); + + while ( !bQuit ) + { + bQuit = HandleInput(); + + RenderFrame(); + } + + SDL_StopTextInput(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Processes a single VR event +//----------------------------------------------------------------------------- +void CMainApplication::ProcessVREvent( const vr::VREvent_t & event ) +{ + switch( event.eventType ) + { + case vr::VREvent_TrackedDeviceDeactivated: + { + dprintf( "Device %u detached.\n", event.trackedDeviceIndex ); + } + break; + case vr::VREvent_TrackedDeviceUpdated: + { + dprintf( "Device %u updated.\n", event.trackedDeviceIndex ); + } + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::RenderFrame() +{ + // for now as fast as possible + if ( m_pHMD ) + { + RenderControllerAxes(); + RenderStereoTargets(); + RenderCompanionWindow(); + + vr::Texture_t leftEyeTexture = {(void*)(uintptr_t)leftEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma }; + vr::VRCompositor()->Submit(vr::Eye_Left, &leftEyeTexture ); + vr::Texture_t rightEyeTexture = {(void*)(uintptr_t)rightEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma }; + vr::VRCompositor()->Submit(vr::Eye_Right, &rightEyeTexture ); + } + + if ( m_bVblank && m_bGlFinishHack ) + { + //$ HACKHACK. From gpuview profiling, it looks like there is a bug where two renders and a present + // happen right before and after the vsync causing all kinds of jittering issues. This glFinish() + // appears to clear that up. Temporary fix while I try to get nvidia to investigate this problem. + // 1/29/2014 mikesart + glFinish(); + } + + // SwapWindow + { + SDL_GL_SwapWindow( m_pCompanionWindow ); + } + + // Clear + { + // We want to make sure the glFinish waits for the entire present to complete, not just the submission + // of the command. So, we do a clear here right here so the glFinish will wait fully for the swap. + glClearColor( 0, 0, 0, 1 ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + } + + // Flush and wait for swap. + if ( m_bVblank ) + { + glFlush(); + glFinish(); + } + + // Spew out the controller and pose count whenever they change. + if ( m_iTrackedControllerCount != m_iTrackedControllerCount_Last || m_iValidPoseCount != m_iValidPoseCount_Last ) + { + m_iValidPoseCount_Last = m_iValidPoseCount; + m_iTrackedControllerCount_Last = m_iTrackedControllerCount; + + dprintf( "PoseCount:%d(%s) Controllers:%d\n", m_iValidPoseCount, m_strPoseClasses.c_str(), m_iTrackedControllerCount ); + } + + UpdateHMDMatrixPose(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Compiles a GL shader program and returns the handle. Returns 0 if +// the shader couldn't be compiled for some reason. +//----------------------------------------------------------------------------- +GLuint CMainApplication::CompileGLShader( const char *pchShaderName, const char *pchVertexShader, const char *pchFragmentShader ) +{ + GLuint unProgramID = glCreateProgram(); + + GLuint nSceneVertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource( nSceneVertexShader, 1, &pchVertexShader, NULL); + glCompileShader( nSceneVertexShader ); + + GLint vShaderCompiled = GL_FALSE; + glGetShaderiv( nSceneVertexShader, GL_COMPILE_STATUS, &vShaderCompiled); + if ( vShaderCompiled != GL_TRUE) + { + dprintf("%s - Unable to compile vertex shader %d!\n", pchShaderName, nSceneVertexShader); + glDeleteProgram( unProgramID ); + glDeleteShader( nSceneVertexShader ); + return 0; + } + glAttachShader( unProgramID, nSceneVertexShader); + glDeleteShader( nSceneVertexShader ); // the program hangs onto this once it's attached + + GLuint nSceneFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource( nSceneFragmentShader, 1, &pchFragmentShader, NULL); + glCompileShader( nSceneFragmentShader ); + + GLint fShaderCompiled = GL_FALSE; + glGetShaderiv( nSceneFragmentShader, GL_COMPILE_STATUS, &fShaderCompiled); + if (fShaderCompiled != GL_TRUE) + { + dprintf("%s - Unable to compile fragment shader %d!\n", pchShaderName, nSceneFragmentShader ); + glDeleteProgram( unProgramID ); + glDeleteShader( nSceneFragmentShader ); + return 0; + } + + glAttachShader( unProgramID, nSceneFragmentShader ); + glDeleteShader( nSceneFragmentShader ); // the program hangs onto this once it's attached + + glLinkProgram( unProgramID ); + + GLint programSuccess = GL_TRUE; + glGetProgramiv( unProgramID, GL_LINK_STATUS, &programSuccess); + if ( programSuccess != GL_TRUE ) + { + dprintf("%s - Error linking program %d!\n", pchShaderName, unProgramID); + glDeleteProgram( unProgramID ); + return 0; + } + + glUseProgram( unProgramID ); + glUseProgram( 0 ); + + return unProgramID; +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates all the shaders used by HelloVR SDL +//----------------------------------------------------------------------------- +bool CMainApplication::CreateAllShaders() +{ + m_unSceneProgramID = CompileGLShader( + "Scene", + + // Vertex Shader + "#version 410\n" + "uniform mat4 matrix;\n" + "layout(location = 0) in vec4 position;\n" + "layout(location = 1) in vec2 v2UVcoordsIn;\n" + "layout(location = 2) in vec3 v3NormalIn;\n" + "out vec2 v2UVcoords;\n" + "void main()\n" + "{\n" + " v2UVcoords = v2UVcoordsIn;\n" + " gl_Position = matrix * position;\n" + "}\n", + + // Fragment Shader + "#version 410 core\n" + "uniform sampler2D mytexture;\n" + "in vec2 v2UVcoords;\n" + "out vec4 outputColor;\n" + "void main()\n" + "{\n" + " vec4 col = texture(mytexture, v2UVcoords);\n" + " outputColor = col.rgba;\n" + "}\n" + ); + m_nSceneMatrixLocation = glGetUniformLocation( m_unSceneProgramID, "matrix" ); + if( m_nSceneMatrixLocation == -1 ) + { + dprintf( "Unable to find matrix uniform in scene shader\n" ); + return false; + } + + m_unControllerTransformProgramID = CompileGLShader( + "Controller", + + // vertex shader + "#version 410\n" + "uniform mat4 matrix;\n" + "layout(location = 0) in vec4 position;\n" + "layout(location = 1) in vec3 v3ColorIn;\n" + "out vec4 v4Color;\n" + "void main()\n" + "{\n" + " v4Color.xyz = v3ColorIn; v4Color.a = 1.0;\n" + " gl_Position = matrix * position;\n" + "}\n", + + // fragment shader + "#version 410\n" + "in vec4 v4Color;\n" + "out vec4 outputColor;\n" + "void main()\n" + "{\n" + " outputColor = v4Color;\n" + "}\n" + ); + m_nControllerMatrixLocation = glGetUniformLocation( m_unControllerTransformProgramID, "matrix" ); + if( m_nControllerMatrixLocation == -1 ) + { + dprintf( "Unable to find matrix uniform in controller shader\n" ); + return false; + } + + m_unRenderModelProgramID = CompileGLShader( + "render model", + + // vertex shader + "#version 410\n" + "uniform mat4 matrix;\n" + "layout(location = 0) in vec4 position;\n" + "layout(location = 1) in vec3 v3NormalIn;\n" + "layout(location = 2) in vec2 v2TexCoordsIn;\n" + "out vec2 v2TexCoord;\n" + "void main()\n" + "{\n" + " v2TexCoord = v2TexCoordsIn;\n" + " gl_Position = matrix * vec4(position.xyz, 1);\n" + "}\n", + + //fragment shader + "#version 410 core\n" + "uniform sampler2D diffuse;\n" + "in vec2 v2TexCoord;\n" + "out vec4 outputColor;\n" + "void main()\n" + "{\n" + " outputColor = texture( diffuse, v2TexCoord);\n" + "}\n" + + ); + m_nRenderModelMatrixLocation = glGetUniformLocation( m_unRenderModelProgramID, "matrix" ); + if( m_nRenderModelMatrixLocation == -1 ) + { + dprintf( "Unable to find matrix uniform in render model shader\n" ); + return false; + } + + m_unCompanionWindowProgramID = CompileGLShader( + "CompanionWindow", + + // vertex shader + "#version 410 core\n" + "layout(location = 0) in vec4 position;\n" + "layout(location = 1) in vec2 v2UVIn;\n" + "noperspective out vec2 v2UV;\n" + "void main()\n" + "{\n" + " v2UV = vec2(v2UVIn.x, 1.0 - v2UVIn.y);\n" + " gl_Position = position;\n" + "}\n", + + // fragment shader + "#version 410 core\n" + "uniform sampler2D mytexture;\n" + "noperspective in vec2 v2UV;\n" + "out vec4 outputColor;\n" + "void main()\n" + "{\n" + " vec4 col = texture(mytexture, v2UV);\n" + " outputColor = col.rgba;\n" + "}\n" + ); + + return m_unSceneProgramID != 0 + && m_unControllerTransformProgramID != 0 + && m_unRenderModelProgramID != 0 + && m_unCompanionWindowProgramID != 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMainApplication::SetupTexturemaps() +{ + const int pixmap_config[] = { + GLX_BIND_TO_TEXTURE_RGBA_EXT, True, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + //GLX_BIND_TO_MIPMAP_TEXTURE_EXT, True, + GLX_DOUBLEBUFFER, False, + GLX_Y_INVERTED_EXT, (int)GLX_DONT_CARE, + None + }; + + const int pixmap_attribs[] = { + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, + None + }; + + int c; + configs = glXChooseFBConfig(x_display, 0, pixmap_config, &c); + if (!configs) + { + printf("Failed too choose fb config\n"); + return false; + } + + src_window_pixmap = XCompositeNameWindowPixmap(x_display, src_window_id); + if (!src_window_pixmap) + { + printf("Failed to get pixmap for window %ld\n", src_window_id); + return false; + } + + glxpixmap = glXCreatePixmap(x_display, *configs, src_window_pixmap, pixmap_attribs); + if (!glxpixmap) + { + printf("Failed to create pixmap\n"); + return false; + } + + + glGenTextures(1, &m_iTexture ); + glBindTexture( GL_TEXTURE_2D, m_iTexture ); + + glXBindTexImageEXT(x_display, glxpixmap, GLX_FRONT_EXT, NULL); + + //glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR/*GL_LINEAR_MIPMAP_LINEAR*/ ); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + GLfloat fLargest; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest); + + glBindTexture( GL_TEXTURE_2D, 0 ); + + return ( m_iTexture != 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: create a sea of cubes +//----------------------------------------------------------------------------- +void CMainApplication::SetupScene() +{ + if ( !m_pHMD ) + return; + + std::vector vertdataarray; +#if 0 + Matrix4 matScale; + matScale.scale( m_fScale, m_fScale, m_fScale ); + Matrix4 matTransform; + matTransform.translate( + -( (float)m_iSceneVolumeWidth * m_fScaleSpacing ) / 2.f, + -( (float)m_iSceneVolumeHeight * m_fScaleSpacing ) / 2.f, + -( (float)m_iSceneVolumeDepth * m_fScaleSpacing ) / 2.f); + + Matrix4 mat = matScale * matTransform; + + for( int z = 0; z< m_iSceneVolumeDepth; z++ ) + { + for( int y = 0; y< m_iSceneVolumeHeight; y++ ) + { + for( int x = 0; x< m_iSceneVolumeWidth; x++ ) + { + AddCubeToScene( mat, vertdataarray ); + mat = mat * Matrix4().translate( m_fScaleSpacing, 0, 0 ); + } + mat = mat * Matrix4().translate( -((float)m_iSceneVolumeWidth) * m_fScaleSpacing, m_fScaleSpacing, 0 ); + } + mat = mat * Matrix4().translate( 0, -((float)m_iSceneVolumeHeight) * m_fScaleSpacing, m_fScaleSpacing ); + } +#endif + + Matrix4 matScale; + matScale.scale( m_fScale, m_fScale, m_fScale ); + Matrix4 matTransform; + // matTransform.translate( + // -( (float)m_fScale ) * 2.f, + // -( (float)m_fScale) * 2.f, + // -( (float)m_fScale) * 1.0f); + + Matrix4 mat = matScale * matTransform; + AddCubeToScene( mat, vertdataarray ); + + m_uiVertcount = vertdataarray.size()/5; + + glGenVertexArrays( 1, &m_unSceneVAO ); + glBindVertexArray( m_unSceneVAO ); + + glGenBuffers( 1, &m_glSceneVertBuffer ); + glBindBuffer( GL_ARRAY_BUFFER, m_glSceneVertBuffer ); + glBufferData( GL_ARRAY_BUFFER, sizeof(float) * vertdataarray.size(), &vertdataarray[0], GL_STATIC_DRAW); + + GLsizei stride = sizeof(VertexDataScene); + uintptr_t offset = 0; + + glEnableVertexAttribArray( 0 ); + glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride , (const void *)offset); + + offset += sizeof(Vector3); + glEnableVertexAttribArray( 1 ); + glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, stride, (const void *)offset); + + glBindVertexArray( 0 ); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector &vertdata ) +{ + vertdata.push_back( fl0 ); + vertdata.push_back( fl1 ); + vertdata.push_back( fl2 ); + vertdata.push_back( fl3 ); + vertdata.push_back( fl4 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::AddCubeToScene( Matrix4 mat, std::vector &vertdata ) +{ + // Matrix4 mat( outermat.data() ); + + Vector4 A = mat * Vector4( 0, 0, 0, 1 ); + Vector4 B = mat * Vector4( 1, 0, 0, 1 ); + Vector4 C = mat * Vector4( 1, 1, 0, 1 ); + Vector4 D = mat * Vector4( 0, 1, 0, 1 ); + Vector4 E = mat * Vector4( 0, 0, 1, 1 ); + Vector4 F = mat * Vector4( 1, 0, 1, 1 ); + Vector4 G = mat * Vector4( 1, 1, 1, 1 ); + Vector4 H = mat * Vector4( 0, 1, 1, 1 ); + + // triangles instead of quads + // AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); //Front + // AddCubeVertex( F.x, F.y, F.z, 1, 1, vertdata ); + // AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata ); + // AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata ); + // AddCubeVertex( H.x, H.y, H.z, 0, 0, vertdata ); + // AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); + + AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); //Back + AddCubeVertex( A.x, A.y, A.z, 1, 1, vertdata ); + AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata ); + AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata ); + AddCubeVertex( C.x, C.y, C.z, 0, 0, vertdata ); + AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); +#if 0 + long columns = 32; + long rows = 32; + double angle_x = 3.14; + double angle_y = 3.14; + double radius = 1.0; + double radius_depth = 1.0; + + for(long row = 0; row < rows-1; ++row) { + for(long column = 0; column < columns-1; ++column) { + double z1 = sin((double)column / (double)columns * angle_x); + double z2 = sin((double)(column + 1) / (double)columns * angle_x); + double z3 = z1; + + double z4 = z3; + double z5 = z2; + double z6 = z2; + + double x1 = -cos((double)column / (double)columns * angle_x) ; + double x2 = -cos((double)(column + 1) / (double)columns * angle_x); + double x3 = x1; + + double x4 = x3; + double x5 = x2; + double x6 = x2; + + double y1 = cos((double)row / (double)rows * angle_y) * radius; + double y2 = y1; + double y3 = cos((double)(row + 1) / (double)rows * angle_y) * radius; + + double y4 = y3; + double y5 = y3; + double y6 = y1; + + z1 *= sin((double)row / (double)rows * angle_y) * radius_depth; + z2 *= sin((double)row / (double)rows * angle_y) * radius_depth; + z3 *= sin((double)(row + 1) / (double)rows * angle_y) * radius_depth; + z4 *= sin((double)(row + 1) / (double)rows * angle_y) * radius_depth; + z5 *= sin((double)(row + 1) / (double)rows * angle_y) * radius_depth; + z6 *= sin((double)row / (double)rows * angle_y) * radius_depth; + + x1 *= sin((double)row / (double)rows * angle_y) * radius_depth; + x2 *= sin((double)row / (double)rows * angle_y) * radius_depth; + x3 *= sin((double)(row + 1) / (double)rows * angle_y) * radius_depth; + x4 *= sin((double)(row + 1) / (double)rows * angle_y) * radius_depth; + x5 *= sin((double)(row + 1) / (double)rows * angle_y) * radius_depth; + x6 *= sin((double)row / (double)rows * angle_y) * radius_depth; + + Vector4 v1 = mat * Vector4(x1, y1, z1, 1.0); + Vector4 v2 = mat * Vector4(x2, y2, z2, 1.0); + Vector4 v3 = mat * Vector4(x3, y3, z3, 1.0); + Vector4 v4 = mat * Vector4(x4, y4, z4, 1.0); + Vector4 v5 = mat * Vector4(x5, y5, z5, 1.0); + Vector4 v6 = mat * Vector4(x6, y6, z6, 1.0); + + AddCubeVertex(v1.x, v1.y, v1.z, 1.0 - (double)column / (double)columns, (double)row / (double)rows, vertdata); + AddCubeVertex(v2.x, v2.y, v2.z, 1.0 - (double)(column + 1) / (double)columns, (double)row / (double)rows, vertdata); + AddCubeVertex(v3.x, v3.y, v3.z, 1.0 - (double)column / (double)columns, (double)(row + 1) / (double)rows, vertdata); + + AddCubeVertex(v4.x, v4.y, v4.z, 1.0 - (double)column / (double)columns, (double)(row + 1) / (double)rows, vertdata); + AddCubeVertex(v5.x, v5.y, v5.z, 1.0 - (double)(column + 1) / (double)columns, (double)(row + 1) / (double)rows, vertdata); + AddCubeVertex(v6.x, v6.y, v6.z, 1.0 - (double)(column + 1) / (double)columns, (double)row / (double)rows, vertdata); + } + } +#endif + + // AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); //Top + // AddCubeVertex( G.x, G.y, G.z, 1, 1, vertdata ); + // AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); + // AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); + // AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata ); + // AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); + + // AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Bottom + // AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata ); + // AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata ); + // AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata ); + // AddCubeVertex( E.x, E.y, E.z, 0, 0, vertdata ); + // AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); + + // AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Left + // AddCubeVertex( E.x, E.y, E.z, 1, 1, vertdata ); + // AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata ); + // AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata ); + // AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata ); + // AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); + + // AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); //Right + // AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata ); + // AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); + // AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); + // AddCubeVertex( G.x, G.y, G.z, 0, 0, vertdata ); + // AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw all of the controllers as X/Y/Z lines +//----------------------------------------------------------------------------- +void CMainApplication::RenderControllerAxes() +{ + // Don't attempt to update controllers if input is not available + if( !m_pHMD->IsInputAvailable() ) + return; + + std::vector vertdataarray; + + m_uiControllerVertcount = 0; + m_iTrackedControllerCount = 0; + + for ( EHand eHand = Left; eHand <= Right; ((int&)eHand)++ ) + { + if ( !m_rHand[eHand].m_bShowController ) + continue; + + const Matrix4 & mat = m_rHand[eHand].m_rmat4Pose; + + Vector4 center = mat * Vector4( 0, 0, 0, 1 ); + + for ( int i = 0; i < 3; ++i ) + { + Vector3 color( 0, 0, 0 ); + Vector4 point( 0, 0, 0, 1 ); + point[i] += 0.05f; // offset in X, Y, Z + color[i] = 1.0; // R, G, B + point = mat * point; + vertdataarray.push_back( center.x ); + vertdataarray.push_back( center.y ); + vertdataarray.push_back( center.z ); + + vertdataarray.push_back( color.x ); + vertdataarray.push_back( color.y ); + vertdataarray.push_back( color.z ); + + vertdataarray.push_back( point.x ); + vertdataarray.push_back( point.y ); + vertdataarray.push_back( point.z ); + + vertdataarray.push_back( color.x ); + vertdataarray.push_back( color.y ); + vertdataarray.push_back( color.z ); + + m_uiControllerVertcount += 2; + } + + Vector4 start = mat * Vector4( 0, 0, -0.02f, 1 ); + Vector4 end = mat * Vector4( 0, 0, -39.f, 1 ); + Vector3 color( .92f, .92f, .71f ); + + vertdataarray.push_back( start.x );vertdataarray.push_back( start.y );vertdataarray.push_back( start.z ); + vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z ); + + vertdataarray.push_back( end.x );vertdataarray.push_back( end.y );vertdataarray.push_back( end.z ); + vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z ); + m_uiControllerVertcount += 2; + } + + // Setup the VAO the first time through. + if ( m_unControllerVAO == 0 ) + { + glGenVertexArrays( 1, &m_unControllerVAO ); + glBindVertexArray( m_unControllerVAO ); + + glGenBuffers( 1, &m_glControllerVertBuffer ); + glBindBuffer( GL_ARRAY_BUFFER, m_glControllerVertBuffer ); + + GLuint stride = 2 * 3 * sizeof( float ); + uintptr_t offset = 0; + + glEnableVertexAttribArray( 0 ); + glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset); + + offset += sizeof( Vector3 ); + glEnableVertexAttribArray( 1 ); + glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset); + + glBindVertexArray( 0 ); + } + + glBindBuffer( GL_ARRAY_BUFFER, m_glControllerVertBuffer ); + + // set vertex data if we have some + if( vertdataarray.size() > 0 ) + { + //$ TODO: Use glBufferSubData for this... + glBufferData( GL_ARRAY_BUFFER, sizeof(float) * vertdataarray.size(), &vertdataarray[0], GL_STREAM_DRAW ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::SetupCameras() +{ + m_mat4ProjectionLeft = GetHMDMatrixProjectionEye( vr::Eye_Left ); + m_mat4ProjectionRight = GetHMDMatrixProjectionEye( vr::Eye_Right ); + m_mat4eyePosLeft = GetHMDMatrixPoseEye( vr::Eye_Left ); + m_mat4eyePosRight = GetHMDMatrixPoseEye( vr::Eye_Right ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates a frame buffer. Returns true if the buffer was set up. +// Returns false if the setup failed. +//----------------------------------------------------------------------------- +bool CMainApplication::CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc ) +{ + glGenFramebuffers(1, &framebufferDesc.m_nRenderFramebufferId ); + glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nRenderFramebufferId); + + glGenRenderbuffers(1, &framebufferDesc.m_nDepthBufferId); + glBindRenderbuffer(GL_RENDERBUFFER, framebufferDesc.m_nDepthBufferId); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, nWidth, nHeight ); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, framebufferDesc.m_nDepthBufferId ); + + glGenTextures(1, &framebufferDesc.m_nRenderTextureId ); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebufferDesc.m_nRenderTextureId ); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, nWidth, nHeight, true); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, framebufferDesc.m_nRenderTextureId, 0); + + glGenFramebuffers(1, &framebufferDesc.m_nResolveFramebufferId ); + glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nResolveFramebufferId); + + glGenTextures(1, &framebufferDesc.m_nResolveTextureId ); + glBindTexture(GL_TEXTURE_2D, framebufferDesc.m_nResolveTextureId ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferDesc.m_nResolveTextureId, 0); + + // check FBO status + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + { + return false; + } + + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMainApplication::SetupStereoRenderTargets() +{ + if ( !m_pHMD ) + return false; + + m_pHMD->GetRecommendedRenderTargetSize( &m_nRenderWidth, &m_nRenderHeight ); + + CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, leftEyeDesc ); + CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, rightEyeDesc ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::SetupCompanionWindow() +{ + if ( !m_pHMD ) + return; + + std::vector vVerts; + + // left eye verts + vVerts.push_back( VertexDataWindow( Vector2(-1, -1), Vector2(0, 1)) ); + vVerts.push_back( VertexDataWindow( Vector2(0, -1), Vector2(1, 1)) ); + vVerts.push_back( VertexDataWindow( Vector2(-1, 1), Vector2(0, 0)) ); + vVerts.push_back( VertexDataWindow( Vector2(0, 1), Vector2(1, 0)) ); + + // right eye verts + vVerts.push_back( VertexDataWindow( Vector2(0, -1), Vector2(0, 1)) ); + vVerts.push_back( VertexDataWindow( Vector2(1, -1), Vector2(1, 1)) ); + vVerts.push_back( VertexDataWindow( Vector2(0, 1), Vector2(0, 0)) ); + vVerts.push_back( VertexDataWindow( Vector2(1, 1), Vector2(1, 0)) ); + + GLushort vIndices[] = { 0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6 }; + m_uiCompanionWindowIndexSize = _countof(vIndices); + + glGenVertexArrays( 1, &m_unCompanionWindowVAO ); + glBindVertexArray( m_unCompanionWindowVAO ); + + glGenBuffers( 1, &m_glCompanionWindowIDVertBuffer ); + glBindBuffer( GL_ARRAY_BUFFER, m_glCompanionWindowIDVertBuffer ); + glBufferData( GL_ARRAY_BUFFER, vVerts.size()*sizeof(VertexDataWindow), &vVerts[0], GL_STATIC_DRAW ); + + glGenBuffers( 1, &m_glCompanionWindowIDIndexBuffer ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_glCompanionWindowIDIndexBuffer ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_uiCompanionWindowIndexSize*sizeof(GLushort), &vIndices[0], GL_STATIC_DRAW ); + + glEnableVertexAttribArray( 0 ); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataWindow), (void *)offsetof( VertexDataWindow, position ) ); + + glEnableVertexAttribArray( 1 ); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataWindow), (void *)offsetof( VertexDataWindow, texCoord ) ); + + glBindVertexArray( 0 ); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::RenderStereoTargets() +{ + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glEnable( GL_MULTISAMPLE ); + + // Left Eye + glBindFramebuffer( GL_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId ); + glViewport(0, 0, m_nRenderWidth, m_nRenderHeight ); + RenderScene( vr::Eye_Left ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + glDisable( GL_MULTISAMPLE ); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, leftEyeDesc.m_nResolveFramebufferId ); + + glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight, + GL_COLOR_BUFFER_BIT, + GL_LINEAR ); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 ); + + glEnable( GL_MULTISAMPLE ); + + // Right Eye + glBindFramebuffer( GL_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId ); + glViewport(0, 0, m_nRenderWidth, m_nRenderHeight ); + RenderScene( vr::Eye_Right ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + glDisable( GL_MULTISAMPLE ); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId ); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rightEyeDesc.m_nResolveFramebufferId ); + + glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight, + GL_COLOR_BUFFER_BIT, + GL_LINEAR ); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Renders a scene with respect to nEye. +//----------------------------------------------------------------------------- +void CMainApplication::RenderScene( vr::Hmd_Eye nEye ) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + + if( m_bShowCubes ) + { + glUseProgram( m_unSceneProgramID ); + glUniformMatrix4fv( m_nSceneMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix( nEye ).get() ); + glBindVertexArray( m_unSceneVAO ); + glBindTexture( GL_TEXTURE_2D, m_iTexture ); + glDrawArrays( GL_TRIANGLES, 0, m_uiVertcount ); + glBindVertexArray( 0 ); + } + + bool bIsInputAvailable = m_pHMD->IsInputAvailable(); + + if( bIsInputAvailable ) + { + // draw the controller axis lines + glUseProgram( m_unControllerTransformProgramID ); + glUniformMatrix4fv( m_nControllerMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix( nEye ).get() ); + glBindVertexArray( m_unControllerVAO ); + glDrawArrays( GL_LINES, 0, m_uiControllerVertcount ); + glBindVertexArray( 0 ); + } + + // ----- Render Model rendering ----- + glUseProgram( m_unRenderModelProgramID ); + + for ( EHand eHand = Left; eHand <= Right; ((int&)eHand)++ ) + { + if ( !m_rHand[eHand].m_bShowController || !m_rHand[eHand].m_pRenderModel ) + continue; + + const Matrix4 & matDeviceToTracking = m_rHand[eHand].m_rmat4Pose; + Matrix4 matMVP = GetCurrentViewProjectionMatrix( nEye ) * matDeviceToTracking; + glUniformMatrix4fv( m_nRenderModelMatrixLocation, 1, GL_FALSE, matMVP.get() ); + + m_rHand[eHand].m_pRenderModel->Draw(); + } + + glUseProgram( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::RenderCompanionWindow() +{ + glDisable(GL_DEPTH_TEST); + glViewport( 0, 0, m_nCompanionWindowWidth, m_nCompanionWindowHeight ); + + glBindVertexArray( m_unCompanionWindowVAO ); + glUseProgram( m_unCompanionWindowProgramID ); + + // render left eye (first half of index array ) + glBindTexture(GL_TEXTURE_2D, leftEyeDesc.m_nResolveTextureId ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glDrawElements( GL_TRIANGLES, m_uiCompanionWindowIndexSize/2, GL_UNSIGNED_SHORT, 0 ); + + // render right eye (second half of index array ) + glBindTexture(GL_TEXTURE_2D, rightEyeDesc.m_nResolveTextureId ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glDrawElements( GL_TRIANGLES, m_uiCompanionWindowIndexSize/2, GL_UNSIGNED_SHORT, (const void *)(uintptr_t)(m_uiCompanionWindowIndexSize) ); + + glBindVertexArray( 0 ); + glUseProgram( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets a Matrix Projection Eye with respect to nEye. +//----------------------------------------------------------------------------- +Matrix4 CMainApplication::GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye ) +{ + if ( !m_pHMD ) + return Matrix4(); + + vr::HmdMatrix44_t mat = m_pHMD->GetProjectionMatrix( nEye, m_fNearClip, m_fFarClip ); + + return Matrix4( + mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], + mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1], + mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], + mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3] + ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets an HMDMatrixPoseEye with respect to nEye. +//----------------------------------------------------------------------------- +Matrix4 CMainApplication::GetHMDMatrixPoseEye( vr::Hmd_Eye nEye ) +{ + if ( !m_pHMD ) + return Matrix4(); + + vr::HmdMatrix34_t matEyeRight = m_pHMD->GetEyeToHeadTransform( nEye ); + Matrix4 matrixObj( + matEyeRight.m[0][0], matEyeRight.m[1][0], matEyeRight.m[2][0], 0.0, + matEyeRight.m[0][1], matEyeRight.m[1][1], matEyeRight.m[2][1], 0.0, + matEyeRight.m[0][2], matEyeRight.m[1][2], matEyeRight.m[2][2], 0.0, + matEyeRight.m[0][3], matEyeRight.m[1][3], matEyeRight.m[2][3], 1.0f + ); + + return matrixObj.invert(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets a Current View Projection Matrix with respect to nEye, +// which may be an Eye_Left or an Eye_Right. +//----------------------------------------------------------------------------- +Matrix4 CMainApplication::GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye ) +{ + Matrix4 matMVP; + //glm::mat4 pp(m_mat4HMDPose.get()); + if( nEye == vr::Eye_Left ) + { + matMVP = m_mat4ProjectionLeft * m_mat4eyePosLeft * m_mat4HMDPose; + } + else if( nEye == vr::Eye_Right ) + { + matMVP = m_mat4ProjectionRight * m_mat4eyePosRight * m_mat4HMDPose; + } + + return matMVP; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainApplication::UpdateHMDMatrixPose() +{ + if ( !m_pHMD ) + return; + + vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0 ); + + m_iValidPoseCount = 0; + m_strPoseClasses = ""; + for ( int nDevice = 0; nDevice < vr::k_unMaxTrackedDeviceCount; ++nDevice ) + { + if ( m_rTrackedDevicePose[nDevice].bPoseIsValid ) + { + m_iValidPoseCount++; + m_rmat4DevicePose[nDevice] = ConvertSteamVRMatrixToMatrix4( m_rTrackedDevicePose[nDevice].mDeviceToAbsoluteTracking ); + if (m_rDevClassChar[nDevice]==0) + { + switch (m_pHMD->GetTrackedDeviceClass(nDevice)) + { + case vr::TrackedDeviceClass_Controller: m_rDevClassChar[nDevice] = 'C'; break; + case vr::TrackedDeviceClass_HMD: m_rDevClassChar[nDevice] = 'H'; break; + case vr::TrackedDeviceClass_Invalid: m_rDevClassChar[nDevice] = 'I'; break; + case vr::TrackedDeviceClass_GenericTracker: m_rDevClassChar[nDevice] = 'G'; break; + case vr::TrackedDeviceClass_TrackingReference: m_rDevClassChar[nDevice] = 'T'; break; + default: m_rDevClassChar[nDevice] = '?'; break; + } + } + m_strPoseClasses += m_rDevClassChar[nDevice]; + } + } + + if ( m_rTrackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid ) + { + m_mat4HMDPose = m_rmat4DevicePose[vr::k_unTrackedDeviceIndex_Hmd]; + m_mat4HMDPose.invert(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Finds a render model we've already loaded or loads a new one +//----------------------------------------------------------------------------- +CGLRenderModel *CMainApplication::FindOrLoadRenderModel( const char *pchRenderModelName ) +{ + CGLRenderModel *pRenderModel = NULL; + for( std::vector< CGLRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ ) + { + if( !strcmp( (*i)->GetName().c_str(), pchRenderModelName ) ) + { + pRenderModel = *i; + break; + } + } + + // load the model if we didn't find one + if( !pRenderModel ) + { + vr::RenderModel_t *pModel; + vr::EVRRenderModelError error; + while ( 1 ) + { + error = vr::VRRenderModels()->LoadRenderModel_Async( pchRenderModelName, &pModel ); + if ( error != vr::VRRenderModelError_Loading ) + break; + + ThreadSleep( 1 ); + } + + if ( error != vr::VRRenderModelError_None ) + { + dprintf( "Unable to load render model %s - %s\n", pchRenderModelName, vr::VRRenderModels()->GetRenderModelErrorNameFromEnum( error ) ); + return NULL; // move on to the next tracked device + } + + vr::RenderModel_TextureMap_t *pTexture; + while ( 1 ) + { + error = vr::VRRenderModels()->LoadTexture_Async( pModel->diffuseTextureId, &pTexture ); + if ( error != vr::VRRenderModelError_Loading ) + break; + + ThreadSleep( 1 ); + } + + if ( error != vr::VRRenderModelError_None ) + { + dprintf( "Unable to load render texture id:%d for render model %s\n", pModel->diffuseTextureId, pchRenderModelName ); + vr::VRRenderModels()->FreeRenderModel( pModel ); + return NULL; // move on to the next tracked device + } + + pRenderModel = new CGLRenderModel( pchRenderModelName ); + if ( !pRenderModel->BInit( *pModel, *pTexture ) ) + { + dprintf( "Unable to create GL model from render model %s\n", pchRenderModelName ); + delete pRenderModel; + pRenderModel = NULL; + } + else + { + m_vecRenderModels.push_back( pRenderModel ); + } + vr::VRRenderModels()->FreeRenderModel( pModel ); + vr::VRRenderModels()->FreeTexture( pTexture ); + } + return pRenderModel; +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts a SteamVR matrix to our local matrix class +//----------------------------------------------------------------------------- +Matrix4 CMainApplication::ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose ) +{ + Matrix4 matrixObj( + matPose.m[0][0], matPose.m[1][0], matPose.m[2][0], 0.0, + matPose.m[0][1], matPose.m[1][1], matPose.m[2][1], 0.0, + matPose.m[0][2], matPose.m[1][2], matPose.m[2][2], 0.0, + matPose.m[0][3], matPose.m[1][3], matPose.m[2][3], 1.0f + ); + return matrixObj; +} + + +//----------------------------------------------------------------------------- +// Purpose: Create/destroy GL Render Models +//----------------------------------------------------------------------------- +CGLRenderModel::CGLRenderModel( const std::string & sRenderModelName ) + : m_sModelName( sRenderModelName ) +{ + m_glIndexBuffer = 0; + m_glVertArray = 0; + m_glVertBuffer = 0; + m_glTexture = 0; +} + + +CGLRenderModel::~CGLRenderModel() +{ + Cleanup(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Allocates and populates the GL resources for a render model +//----------------------------------------------------------------------------- +bool CGLRenderModel::BInit( const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture ) +{ + // create and bind a VAO to hold state for this model + glGenVertexArrays( 1, &m_glVertArray ); + glBindVertexArray( m_glVertArray ); + + // Populate a vertex buffer + glGenBuffers( 1, &m_glVertBuffer ); + glBindBuffer( GL_ARRAY_BUFFER, m_glVertBuffer ); + glBufferData( GL_ARRAY_BUFFER, sizeof( vr::RenderModel_Vertex_t ) * vrModel.unVertexCount, vrModel.rVertexData, GL_STATIC_DRAW ); + + // Identify the components in the vertex buffer + glEnableVertexAttribArray( 0 ); + glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, vPosition ) ); + glEnableVertexAttribArray( 1 ); + glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, vNormal ) ); + glEnableVertexAttribArray( 2 ); + glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, rfTextureCoord ) ); + + // Create and populate the index buffer + glGenBuffers( 1, &m_glIndexBuffer ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( uint16_t ) * vrModel.unTriangleCount * 3, vrModel.rIndexData, GL_STATIC_DRAW ); + + glBindVertexArray( 0 ); + + // create and populate the texture + glGenTextures(1, &m_glTexture ); + glBindTexture( GL_TEXTURE_2D, m_glTexture ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, vrDiffuseTexture.unWidth, vrDiffuseTexture.unHeight, + 0, GL_RGBA, GL_UNSIGNED_BYTE, vrDiffuseTexture.rubTextureMapData ); + + // If this renders black ask McJohn what's wrong. + glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + + GLfloat fLargest; + glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest ); + + glBindTexture( GL_TEXTURE_2D, 0 ); + + m_unVertexCount = vrModel.unTriangleCount * 3; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees the GL resources for a render model +//----------------------------------------------------------------------------- +void CGLRenderModel::Cleanup() +{ + if( m_glVertBuffer ) + { + glDeleteBuffers(1, &m_glIndexBuffer); + glDeleteVertexArrays( 1, &m_glVertArray ); + glDeleteBuffers(1, &m_glVertBuffer); + m_glIndexBuffer = 0; + m_glVertArray = 0; + m_glVertBuffer = 0; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Draws the render model +//----------------------------------------------------------------------------- +void CGLRenderModel::Draw() +{ + glBindVertexArray( m_glVertArray ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, m_glTexture ); + + glDrawElements( GL_TRIANGLES, m_unVertexCount, GL_UNSIGNED_SHORT, 0 ); + + glBindVertexArray( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int main(int argc, char *argv[]) +{ + CMainApplication *pMainApplication = new CMainApplication( argc, argv ); + + if (!pMainApplication->BInit()) + { + pMainApplication->Shutdown(); + return 1; + } + + pMainApplication->RunMainLoop(); + + pMainApplication->Shutdown(); + + return 0; +} diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..9ad80a6 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) +{ + printf("hello, world!\n"); + return 0; +} -- cgit v1.2.3