From 898afe9052ae3cf44d4512bb40dbd75198f201b7 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Mon, 6 May 2019 21:03:22 +0200 Subject: [PATCH] vr paint --- src/app.cpp | 2 +- src/app.h | 2 +- src/app_vr.cpp | 2 -- src/canvas.cpp | 89 ++++++++++++++++++++++++++++++++------------------ src/canvas.h | 2 +- src/hmd.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++ src/hmd.h | 37 +++++++++++++++++++++ src/main.cpp | 43 ++++++++++++++++++++++++ 8 files changed, 216 insertions(+), 36 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 33902eb..0b31265 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -127,7 +127,7 @@ void App::initAssets() sampler_stencil.create(GL_LINEAR, GL_REPEAT); sampler_linear.create(GL_LINEAR); m_face_plane.create<1>(2, 2); - //sphere.create<8, 8>(1); + sphere.create<8, 8>(1); LOG("initializing assets load uvs texture"); LOG("initializing assets completed"); } diff --git a/src/app.h b/src/app.h index f10fea5..f032a0a 100644 --- a/src/app.h +++ b/src/app.h @@ -53,7 +53,7 @@ public: Sampler sampler_stencil; Sampler sampler_linear; Plane m_face_plane; - //Sphere sphere; + Sphere sphere; LayoutManager layout; NodeMessageBox* msgbox; NodeSettings* settings; diff --git a/src/app_vr.cpp b/src/app_vr.cpp index f827755..f501f91 100644 --- a/src/app_vr.cpp +++ b/src/app_vr.cpp @@ -261,7 +261,6 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat } // draw the motion controller sphere -/* { auto mvp = proj * camera * glm::translate(glm::normalize(vr_controller_pos)); ShaderManager::use(kShader::Color); @@ -269,7 +268,6 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat ShaderManager::u_mat4(kShaderUniform::MVP, mvp * glm::scale(glm::vec3(.025))); sphere.draw_fill(); } -*/ // draw the motion controller brush { diff --git a/src/canvas.cpp b/src/canvas.cpp index 8090636..6d3f94c 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -277,7 +277,7 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) gl.restore(); } -std::array, 6> Canvas::stroke_draw_project(std::array& B) const +std::array, 6> Canvas::stroke_draw_project(std::array& B, bool project_3d /*= false*/, glm::mat4 mv /*= glm::mat4(1)*/) const { // intersect P with the current face to clip diverging points from the plane const auto unp_vp = zw(m_box); @@ -285,59 +285,69 @@ std::array, 6> Canvas::stroke_draw_project(std::array, 6> ret; for (int i = 0; i < 6; i++) { - auto P = poly_intersect(B.data(), B.data() + 4, m_plane_shape[i]); - glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); - int intersections = 0; - for (int j = 0; j < P.size(); j++) + struct ray_t { + glm::vec3 o; + glm::vec3 d; + vertex_t v; + ray_t(glm::vec3 o, glm::vec3 d, vertex_t v) : o(o), d(d), v(v) { } + }; + std::vector rays; + if (project_3d) { - glm::vec3 ray_origin, ray_dir; - //if (s.pos.z == 0) + rays.reserve(B.size()); + for (auto const& b : B) + rays.emplace_back(glm::vec3(0), b.pos, b); + } + else + { + auto P = poly_intersect(B.data(), B.data() + 4, m_plane_shape[i]); + rays.reserve(P.size()); + for (auto const& p : P) { - //point_unproject(P[j].pos, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir); - - auto clip_space = glm::vec2(P[j].pos.x, unp_vp.y - P[j].pos.y - 1.f) / unp_vp * 2.f - 1.f; + glm::vec3 ray_origin, ray_dir; + auto clip_space = glm::vec2(p.pos.x, unp_vp.y - p.pos.y - 1.f) / unp_vp * 2.f - 1.f; auto wp0 = unp_inv * glm::vec4(clip_space, 0, 1); auto wp1 = unp_inv * glm::vec4(clip_space, .5, 1); ray_origin = xyz(wp0 / wp0.w); ray_dir = glm::normalize(xyz(wp1 / wp1.w) - ray_origin); + rays.emplace_back(ray_origin, ray_dir, p); } - //else - //{ - // auto m = glm::inverse(glm::lookAt({ 0, 0, 0 }, s.pos, { 0, 1, 0 })); - // glm::vec3 off_3d = m * glm::vec4(off[j], 0, 1); - // ray_origin = glm::vec3(0); - // ray_dir = s.pos + off_3d; - //} - + } + glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); + std::vector face_ret; + face_ret.reserve(rays.size()); + for (auto const& r : rays) + { glm::vec3 hit; float hit_t; - if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t)) + if (ray_intersect(r.o, r.d, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t)) { glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); //P[j].uvs2 = xy(P[j].pos) / glm::vec2(App::I.width, App::I.height); - P[j].pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width; - P[j].pos.y = (plane_local.y * 0.5f + 0.5f) * m_height; + vertex_t v; + v.pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width; + v.pos.y = (plane_local.y * 0.5f + 0.5f) * m_height; // Black magic - BEWARE! // interpolation perspective correction, use the current camera projection to correct the interpolation // because the new shape will have z fixed with an ortho projection when drawn to the face // we need to imitate the same perspective as the once in the camera // see: https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes - auto hit_cam = m_mv * glm::vec4(hit, 1); - P[j].pos.z = 0; - P[j].pos.w = hit_cam.z; - P[j].uvs *= hit_cam.z; - P[j].uvs2 *= hit_cam.z; - intersections++; + auto hit_cam = mv * glm::vec4(hit, 1); + v.pos.z = 0; + v.pos.w = hit_cam.z; + v.uvs = r.v.uvs * hit_cam.z; + v.uvs2 = r.v.uvs2 * hit_cam.z; + face_ret.emplace_back(v); } else { break; } } - if (intersections >= 3) - ret[i] = P; + if (face_ret.size() >= 3) + ret[i] = std::move(face_ret); } return ret; } @@ -443,7 +453,16 @@ std::vector Canvas::stroke_draw_compute(Stroke& stroke) con mixer_bb_min = glm::max({ 0, 0 }, glm::min(mixer_bb_min, p)); mixer_bb_max = glm::min(mixer_sz, glm::max(mixer_bb_max, p)); - B[j].pos = glm::vec4(xy(s.pos) + App::I.zoom * s.scale * off[j] * glm::orientate2(-s.angle) - glm::vec2(0, 1), 1, 1); + if (s.pos.z == 0.f) + { + B[j].pos = glm::vec4(xy(s.pos) + App::I.zoom * s.scale * off[j] * glm::orientate2(-s.angle) - glm::vec2(0, 1), 1, 1); + } + else + { + auto m = glm::inverse(glm::lookAt({ 0, 0, 0 }, s.pos, { 0, 1, 0 })); + glm::vec3 off_3d = m * glm::vec4(off[j], 0, 1); + B[j].pos = glm::vec4(s.pos + off_3d, 1.f); + } B[j].uvs2 = p / mixer_sz; } @@ -451,7 +470,15 @@ std::vector Canvas::stroke_draw_compute(Stroke& stroke) con f.col = glm::vec4(s.col, 1); f.flow = s.flow; f.opacity = s.opacity; - f.shapes = stroke_draw_project(B); + if (s.pos.z == 0.f) + { + f.shapes = stroke_draw_project(B, false, m_mv); + } + else + { + auto m = glm::lookAt({ 0, 0, 0 }, s.pos, { 0, 1, 0 }); + f.shapes = stroke_draw_project(B, true, m); + } prev = s; } diff --git a/src/canvas.h b/src/canvas.h index c90e7d8..3997d43 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -242,7 +242,7 @@ public: void stroke_start(glm::vec3 point, float pressure); void stroke_update(glm::vec3 point, float pressure); void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz); - std::array, 6> stroke_draw_project(std::array& B) const; + std::array, 6> stroke_draw_project(std::array& B, bool project_3d = false, glm::mat4 mv = glm::mat4(1)) const; // return rect {origin, size} glm::vec4 stroke_draw_samples(int i, std::vector& P); std::vector stroke_draw_compute(Stroke& stroke) const; diff --git a/src/hmd.cpp b/src/hmd.cpp index 8e52e2f..e58fb08 100644 --- a/src/hmd.cpp +++ b/src/hmd.cpp @@ -3,6 +3,10 @@ #include "log.h" #include +std::map ViveController::m_mask{ + { ViveController::kButton::Trigger, ViveController::kButtonMask::TriggerBit }, +}; + bool Vive::Initialize() { vr::EVRInitError error; @@ -79,18 +83,78 @@ void Vive::Update() auto mat_proj = glm::make_mat4(data_eye_proj); auto mat_pose = glm::inverse(glm::make_mat4(data_hmd_pose)); auto mat_eye = glm::inverse(glm::make_mat4(data_eye_pose)); + glm::vec3 head_position(h.m[0][3], h.m[1][3], h.m[2][3]); m_proj[eye] = mat_proj; m_view[eye] = mat_eye * mat_pose; m_pose = mat_pose; + // invalidate controller state + std::for_each(m_controllers, m_controllers + 1, [&](ViveController& c) { c.m_valid = false; }); + int controller_index = 0; + auto pose_inv = glm::inverse(m_pose); + for (int id = 0; id < vr::k_unMaxTrackedDeviceCount; id++) { + // device not tracking + if (!poses[id].bPoseIsValid) + continue; + switch (m_hmd->GetTrackedDeviceClass(id)) { case vr::TrackedDeviceClass_HMD: m_active = (m_hmd->GetTrackedDeviceActivityLevel(id) == vr::k_EDeviceActivityLevel_UserInteraction); break; + case vr::TrackedDeviceClass_Controller: + { + m_controllers[controller_index].m_valid = true; + m_hmd->GetControllerState(id, &m_controllers[controller_index].m_state, sizeof(vr::VRControllerState_t)); + m_controllers[controller_index].m_mat = glm::translate(-head_position) * Pose2Mat(poses[id].mDeviceToAbsoluteTracking); + + std::array button_ids{ + ViveController::kButton::Trigger, + //ViveController::kButton::Pad, // unused + //ViveController::kButton::Menu, // unused + //ViveController::kButton::Grip, // unused + }; + + for (auto b : button_ids) + { + glm::vec2 force = { m_controllers[controller_index].m_state.rAxis[(int)b].x, + m_controllers[controller_index].m_state.rAxis[(int)b].y }; + if (glm::compMax(glm::abs(force)) > 0.f && !m_controllers[controller_index].m_analog_buttons[(uint8_t)b]) + { + m_controllers[controller_index].m_analog_buttons[(uint8_t)b] = true; + if (on_analog_button) + on_analog_button(m_controllers[controller_index], b, ViveController::kAction::Press); + } + if (glm::compMax(glm::abs(force)) == 0.f && m_controllers[controller_index].m_analog_buttons[(uint64_t)b]) + { + m_controllers[controller_index].m_analog_buttons[(uint8_t)b] = false; + if (on_analog_button) + on_analog_button(m_controllers[controller_index], b, ViveController::kAction::Release); + } + } + + auto pressed_mask = m_controllers[controller_index].m_state.ulButtonPressed; + for (auto b : button_ids) + { + bool b_pressed = pressed_mask & (uint64_t)m_controllers[controller_index].m_mask[b]; + if (b_pressed == true && !m_controllers[controller_index].m_buttons[(uint64_t)b]) + { + m_controllers[controller_index].m_buttons[(uint64_t)b] = true; + if (on_button) + on_button(m_controllers[controller_index], b, ViveController::kAction::Press); + } + if (b_pressed == false && m_controllers[controller_index].m_buttons[(uint64_t)b]) + { + m_controllers[controller_index].m_buttons[(uint64_t)b] = false; + if (on_button) + on_button(m_controllers[controller_index], b, ViveController::kAction::Release); + } + } + controller_index++; + } default: break; } @@ -138,3 +202,14 @@ bool Vive::Valid() { return m_hmd && m_comp && comp_attempts < 10; } + +glm::mat4 Vive::Pose2Mat(vr::HmdMatrix34_t const& m) +{ + float data[16] = { + m.m[0][0], m.m[1][0], m.m[2][0], 0, + m.m[0][1], m.m[1][1], m.m[2][1], 0, + m.m[0][2], m.m[1][2], m.m[2][2], 0, + m.m[0][3], m.m[1][3], m.m[2][3], 1, + }; + return glm::make_mat4(data); +} diff --git a/src/hmd.h b/src/hmd.h index 0dee07d..837110d 100644 --- a/src/hmd.h +++ b/src/hmd.h @@ -1,6 +1,39 @@ #pragma once #include "rtt.h" +struct ViveController +{ + enum class kButton : uint8_t + { + Pad, + Trigger, + Menu, + Grip, + COUNT, + }; + enum class kButtonMask : uint64_t + { + TriggerBit = 0x200000000, + PadBit = 0x100000000, + MenuBit = 0x000000002, + GripBit = 0x000000004, + }; + enum class kAction + { + Press, + Release, + }; + static std::map m_mask; + std::array m_buttons; + std::array m_analog_buttons; + glm::mat4 m_mat; + vr::VRControllerState_t m_state{ 0 }; + bool m_valid = false; + glm::vec2 axis(kButton button) const { + return { m_state.rAxis[(int)button].x, m_state.rAxis[(int)button].y }; + } +}; + struct Vive { int comp_attempts = 0; @@ -9,15 +42,19 @@ struct Vive vr::IVRSystem* m_hmd = nullptr; vr::IVRCompositor* m_comp = nullptr; vr::IVRSettings* m_settings = nullptr; + ViveController m_controllers[2]; glm::mat4 m_view[2]; glm::mat4 m_proj[2]; glm::mat4 m_pose; bool m_active = false; std::function on_draw = nullptr; + std::function on_button = nullptr; + std::function on_analog_button = nullptr; bool Initialize(); void Terminate(); void Update(); void Draw(); bool Valid(); + glm::mat4 Pose2Mat(vr::HmdMatrix34_t const& m); }; diff --git a/src/main.cpp b/src/main.cpp index 4a78984..3533836 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -478,6 +478,33 @@ bool win32_vr_start() App::I.has_vr = true; vr_running = true; + bool trigger_down = false; + cbuffer controller_points(10); + glm::vec3 controller_last_point; + vive->on_analog_button = [&](const ViveController& c, ViveController::kButton b, ViveController::kAction a) { + if (b == ViveController::kButton::Trigger) + { + if (a == ViveController::kAction::Press) + { + glm::vec3 pos = glm::normalize(xyz(vive->m_controllers[0].m_mat[3])) * 800.f; + float force = vive->m_controllers[0].axis(b).x; + async_lock(); + Canvas::I->stroke_start(pos, force); + async_unlock(); + controller_last_point = pos; + controller_points.clear(); + trigger_down = true; + } + if (a == ViveController::kAction::Release) + { + trigger_down = false; + async_lock(); + Canvas::I->stroke_end(); + async_unlock(); + } + } + }; + const float target_tick_rate = 90; unsigned long t0 = GetTickCount(); while (vr_running && running == 1 && vive->Valid()) @@ -488,7 +515,23 @@ bool win32_vr_start() vive->Update(); App::I.vr_active = vive->m_active; + App::I.vr_controller = vive->m_controllers[0].m_mat; + App::I.vr_controller_pos = glm::normalize(xyz(vive->m_controllers[0].m_mat[3])); + if (trigger_down) + { + controller_points.add(App::I.vr_controller_pos * 800.f); + auto p = controller_points.average(); + if (glm::distance(p, controller_last_point) > 1) + { + async_lock(); + Canvas::I->stroke_update(p, vive->m_controllers[0].axis(ViveController::kButton::Trigger).x); + async_unlock(); + controller_last_point = p; + App::I.redraw = true; + } + } + if (vr_running && vive->m_active) { async_lock();