vr paint
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -277,67 +277,77 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
|
||||
gl.restore();
|
||||
}
|
||||
|
||||
std::array<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vertex_t, 4>& B) const
|
||||
std::array<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vertex_t, 4>& 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);
|
||||
const auto unp_inv = glm::inverse(m_proj * m_mv);
|
||||
std::array<std::vector<vertex_t>, 6> ret;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
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<ray_t> rays;
|
||||
if (project_3d)
|
||||
{
|
||||
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]);
|
||||
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++)
|
||||
rays.reserve(P.size());
|
||||
for (auto const& p : P)
|
||||
{
|
||||
glm::vec3 ray_origin, ray_dir;
|
||||
//if (s.pos.z == 0)
|
||||
{
|
||||
//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;
|
||||
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<vertex_t> 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::StrokeFrame> 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));
|
||||
|
||||
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::StrokeFrame> 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;
|
||||
}
|
||||
|
||||
@@ -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<std::vector<vertex_t>, 6> stroke_draw_project(std::array<vertex_t, 4>& B) const;
|
||||
std::array<std::vector<vertex_t>, 6> stroke_draw_project(std::array<vertex_t, 4>& 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<vertex_t>& P);
|
||||
std::vector<StrokeFrame> stroke_draw_compute(Stroke& stroke) const;
|
||||
|
||||
75
src/hmd.cpp
75
src/hmd.cpp
@@ -3,6 +3,10 @@
|
||||
#include "log.h"
|
||||
#include <array>
|
||||
|
||||
std::map<ViveController::kButton, ViveController::kButtonMask> 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<ViveController::kButton, 4> 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);
|
||||
}
|
||||
|
||||
37
src/hmd.h
37
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<kButton, kButtonMask> m_mask;
|
||||
std::array<bool, (int)kButton::COUNT> m_buttons;
|
||||
std::array<bool, (int)kButton::COUNT> 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<void(const glm::mat4& m_proj, const glm::mat4& m_view, const glm::mat4& m_pose)> on_draw = nullptr;
|
||||
std::function<void(const ViveController&, ViveController::kButton, ViveController::kAction)> on_button = nullptr;
|
||||
std::function<void(const ViveController&, ViveController::kButton, ViveController::kAction)> on_analog_button = nullptr;
|
||||
|
||||
bool Initialize();
|
||||
void Terminate();
|
||||
void Update();
|
||||
void Draw();
|
||||
bool Valid();
|
||||
glm::mat4 Pose2Mat(vr::HmdMatrix34_t const& m);
|
||||
};
|
||||
|
||||
43
src/main.cpp
43
src/main.cpp
@@ -478,6 +478,33 @@ bool win32_vr_start()
|
||||
App::I.has_vr = true;
|
||||
vr_running = true;
|
||||
|
||||
bool trigger_down = false;
|
||||
cbuffer<glm::vec3> 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,6 +515,22 @@ 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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user