implement motion controllers and vr drawing with brush preview

This commit is contained in:
2018-11-02 13:49:15 +01:00
parent 3b73e84fe9
commit 3ee10bb88d
13 changed files with 202 additions and 46 deletions

View File

@@ -116,6 +116,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);
LOG("initializing assets load uvs texture");
LOG("initializing assets completed");
}

View File

@@ -52,6 +52,7 @@ public:
Sampler sampler_stencil;
Sampler sampler_linear;
ui::Plane m_face_plane;
//ui::Sphere sphere;
LayoutManager layout;
NodeMessageBox* msgbox;
NodeSettings* settings;
@@ -82,6 +83,10 @@ public:
bool redraw = true;
bool animate = false;
bool ui_visible = true;
bool vr_active = false;
glm::mat4 vr_controller;
glm::vec3 vr_controller_pos;
float vr_pressure = 1.f;
glm::mat4 vr_rot{ 0 };
glm::mat4 vr_uirot{ 0 };
glm::vec2 cursor{ 0, 0 };

View File

@@ -219,6 +219,41 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat
m_face_plane.draw_fill();
}
// draw the motion controller sphere
/*
{
auto mvp = proj * camera * glm::translate(glm::normalize(vr_controller_pos));
ui::ShaderManager::use(kShader::Color);
ui::ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 });
ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp * glm::scale(glm::vec3(.025)));
sphere.draw_fill();
}
*/
// draw the motion controller brush
{
auto pos = glm::translate(glm::normalize(vr_controller_pos));
ui::ShaderManager::use(ui::kShader::StrokePreview);
ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0);
ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, canvas->m_canvas->m_current_brush.m_tip_flow);
auto tip_color = glm::vec4(glm::vec3(canvas->m_canvas->m_current_brush.m_tip_color), 1);
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, tip_color);
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP,
proj * camera * pos *
glm::inverse(glm::lookAt({ 0, 0, 0 }, vr_controller_pos, { 0, 1, 0 })) *
//glm::scale(glm::vec3(0.1)) *
glm::scale(glm::vec3(canvas->m_canvas->m_current_brush.m_tip_size * 800.f / App::I.height)) *
glm::eulerAngleZ(canvas->m_canvas->m_current_brush.m_tip_angle * (float)(M_PI * 2.0))
);
glEnable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
auto& tex = TextureManager::get(canvas->m_canvas->m_current_brush.m_tex_id);
tex.bind();
sampler_linear.bind(0);
m_face_plane.draw_fill();
tex.unbind();
}
/*
for (auto& mode : *m_canvas->m_mode)
mode->on_Draw(ortho_proj, proj, camera);

View File

@@ -9,7 +9,7 @@ void ui::BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::ma
for (const auto& s : samples)
{
auto mvp = proj *
glm::translate(glm::vec3(s.pos, 0)) *
glm::translate(s.pos) *
glm::scale(glm::vec3(s.size, s.size, 1)) *
glm::eulerAngleZ(s.angle);
attributes.emplace_back(instance_t{ mvp, s.flow });
@@ -126,12 +126,12 @@ bool ui::BrushMesh::create()
return true;
}
ui::StrokeSample ui::Stroke::randomize_sample(const glm::vec2& pos, float pressure, float curve_angle)
ui::StrokeSample ui::Stroke::randomize_sample(const glm::vec3& pos, float pressure, float curve_angle)
{
auto rnd_nor = [&] { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
//auto rnd_neg = [&] { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1]
auto rnd_rad = [&] { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi]
auto rnd_vec = [&] { float rad = rnd_rad(); return glm::vec2(cosf(rad), sinf(rad)); }; // normalized direction vector
auto rnd_vec = [&] { float rad = rnd_rad(); return glm::vec3(cosf(rad), sinf(rad), 0); }; // normalized direction vector
float size_dyn = m_brush.m_tip_size_pressure ? pressure : 1.f;
float flow_dyn = m_brush.m_tip_flow_pressure ? pressure : 1.f;
@@ -200,7 +200,7 @@ void ui::Stroke::reset(bool clear_keypoints /*= false*/)
if (clear_keypoints)
m_keypoints.clear();
}
void ui::Stroke::add_point(glm::vec2 pos, float pressure)
void ui::Stroke::add_point(glm::vec3 pos, float pressure)
{
#ifdef __IOS__
m_curve = glm::min(m_curve + 0.1f, 1.f);

View File

@@ -42,8 +42,8 @@ public:
struct StrokeSample
{
glm::vec3 col = { 0, 0, 0 };
glm::vec2 pos = { 0, 0 };
glm::vec2 origin = { 0,0 };
glm::vec3 pos = { 0, 0, 0 };
glm::vec3 origin = { 0, 0, 0 };
float size = 0;
float flow = 0;
float angle = 0;
@@ -76,7 +76,7 @@ class Stroke
public:
struct Keypoint
{
glm::vec2 pos = { 0, 0 };
glm::vec3 pos = { 0, 0, 0 };
float pressure = 0;
float dist = 0;
};
@@ -96,16 +96,16 @@ public:
cbuffer<glm::vec3, 3> m_hsv_jitter;
StrokeSample m_prev_sample;
std::vector<Keypoint> m_keypoints;
std::vector<std::pair<glm::vec2, float>> m_hold_points;
std::vector<std::pair<glm::vec3, float>> m_hold_points;
std::vector<StrokeSample> m_samples;
int m_last_kp;
std::minstd_rand prng;
void start(const ui::Brush& brush);
void add_point(glm::vec2 pos, float pressure);
void add_point(glm::vec3 pos, float pressure);
void reset(bool clear_keypoints = false);
bool has_sample();
std::vector<StrokeSample> compute_samples();
StrokeSample randomize_sample(const glm::vec2& pos, float pressure, float curve_angle);
StrokeSample randomize_sample(const glm::vec3& pos, float pressure, float curve_angle);
};
NS_END

View File

@@ -361,7 +361,7 @@ void ui::Canvas::stroke_draw()
glm::vec2 bb_max(0, 0);
for (int j = 0; j < 4; j++)
{
auto p = (m_mixer_sample.pos + off[j] * glm::orientate2(-s.angle) + glm::vec2(0, 1)) / zoom;
auto p = (xy(m_mixer_sample.pos) + off[j] * glm::orientate2(-s.angle) + glm::vec2(0, 1)) / zoom;
UV2[j] = p / sz;
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p));
bb_max = glm::min(sz, glm::max(bb_max, p));
@@ -427,8 +427,18 @@ void ui::Canvas::stroke_draw()
for (int j = 0; j < 4; j++)
{
glm::vec3 ray_origin, ray_dir;
point_unproject(s.pos + off[j] * glm::orientate2(-s.angle), { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir);
if (s.pos.z == 0)
{
point_unproject(xy(s.pos) + off[j] * glm::orientate2(-s.angle), { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir);
}
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::vec3 hit;
if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit))
{
@@ -498,7 +508,7 @@ void ui::Canvas::stroke_draw()
}
m_mixer_sample = s;
}
}
glDisable(GL_BLEND);
@@ -742,11 +752,11 @@ void ui::Canvas::stroke_commit()
action->m_stroke = std::move(m_current_stroke);
ActionManager::add(action);
}
void ui::Canvas::stroke_update(glm::vec2 point, float pressure)
void ui::Canvas::stroke_update(glm::vec3 point, float pressure)
{
m_current_stroke->add_point(point, pressure);
}
void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush)
void ui::Canvas::stroke_start(glm::vec3 point, float pressure, const ui::Brush& brush)
{
// need to commit this now before starting a new stroke
if (m_current_stroke && m_commit_delayed)

View File

@@ -189,8 +189,8 @@ public:
void layer_add(std::string name);
void layer_order(int idx, int pos);
void layer_merge(int source_idx, int dest_idx);
void stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush);
void stroke_update(glm::vec2 point, float pressure);
void stroke_start(glm::vec3 point, float pressure, const ui::Brush& brush);
void stroke_update(glm::vec3 point, float pressure);
void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz);
void stroke_draw();
void stroke_end();

View File

@@ -46,7 +46,7 @@ void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
case kEventType::MouseMove:
if (m_draggingR)
{
auto dir = App::I.has_vr ? glm::vec2(1, 1) : glm::vec2(-1, -1);
auto dir = (App::I.has_vr && App::I.vr_active) ? glm::vec2(1, 1) : glm::vec2(-1, -1);
canvas->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * dir * (canvas->m_cam_fov / 85.f);
auto angle = canvas->m_pan * 0.003f;
canvas->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x);
@@ -108,7 +108,7 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
}
else
{
canvas->stroke_start(loc, me->m_pressure, canvas->m_current_brush);
canvas->stroke_start({ loc, 0 }, me->m_pressure, canvas->m_current_brush);
}
m_dragging = true;
node->mouse_capture();
@@ -150,7 +150,7 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
break;
case kEventType::MouseMove:
if (m_dragging && !m_picking && !m_resizing)
canvas->stroke_update(loc, me->m_pressure);
canvas->stroke_update({ loc, 0 }, me->m_pressure);
if (m_dragging && m_picking)
{
glm::vec4 pix = canvas->pick_get(loc);
@@ -250,8 +250,8 @@ void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
node->mouse_release();
if (m_dragging)
{
canvas->stroke_start(m_drag_start, 1.f, canvas->m_current_brush);
canvas->stroke_update(m_drag_pos, 1.f);
canvas->stroke_start({ m_drag_start, 0 }, 1.f, canvas->m_current_brush);
canvas->stroke_update({ m_drag_pos, 0 }, 1.f);
canvas->stroke_end();
}
m_dragging = false;

View File

@@ -9,6 +9,14 @@ Vive::Vive()
controllers[i].active = true;
}
std::string Vive::ReadPropString(vr::TrackedDeviceIndex_t nDevice, vr::ETrackedDeviceProperty p)
{
size_t len = hmd->GetStringTrackedDeviceProperty(nDevice, p, nullptr, 0);
std::string str(len, '\0');
hmd->GetStringTrackedDeviceProperty(nDevice, p, (char*)str.data(), len);
return str;
}
bool Vive::Initialize()
{
vr::EVRInitError error;
@@ -18,6 +26,8 @@ bool Vive::Initialize()
comp = vr::VRCompositor();
if (!comp)
return false;
settings = vr::VRSettings();
hmd->GetRecommendedRenderTargetSize(&eyeWidth, &eyeHeight);
LOG("Eye target resolution: %dx%d\n", eyeWidth, eyeHeight);
@@ -37,22 +47,20 @@ bool Vive::Initialize()
{
if (hmd->GetTrackedDeviceClass(nDevice) == vr::TrackedDeviceClass_HMD)
{
auto read_string = [nDevice, this](vr::ETrackedDeviceProperty p) -> std::string {
size_t len = hmd->GetStringTrackedDeviceProperty(nDevice, p, nullptr, 0);
std::string str(len, '\0');
hmd->GetStringTrackedDeviceProperty(nDevice, p, (char*)str.data(), len);
return str;
};
dev_tracksys = read_string(vr::Prop_TrackingSystemName_String);
dev_serial = read_string(vr::Prop_SerialNumber_String);
dev_model = read_string(vr::Prop_ModelNumber_String);
dev_manufacturer = read_string(vr::Prop_ManufacturerName_String);
dev_tracksys = ReadPropString(nDevice, vr::Prop_TrackingSystemName_String);
dev_serial = ReadPropString(nDevice, vr::Prop_SerialNumber_String);
dev_model = ReadPropString(nDevice, vr::Prop_ModelNumber_String);
dev_manufacturer = ReadPropString(nDevice, vr::Prop_ManufacturerName_String);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
attempts--;
} while (attempts > 0 && dev_serial.empty());
vr::CVRSettingHelper s(settings);
float timeout = s.GetFloat(vr::k_pch_Power_Section, vr::k_pch_Power_TurnOffScreensTimeout_Float);
s.SetFloat(vr::k_pch_Power_Section, vr::k_pch_Power_TurnOffScreensTimeout_Float, 0.5);
return true;
}
@@ -66,9 +74,9 @@ void Vive::ResetYaw()
yaw_offset = euler.y;
}
void Vive::Draw()
void Vive::Update()
{
if (!(hmd && comp && comp_attempts < 10))
if (!(hmd && comp))
return;
vr::TrackedDevicePose_t poses[vr::k_unMaxTrackedDeviceCount];
@@ -77,9 +85,6 @@ void Vive::Draw()
for (int eye = 0; eye < 2; ++eye)
{
eyes[eye].bindFramebuffer();
eyes[eye].clear(background);
glViewport(0, 0, eyes[eye].getWidth(), eyes[eye].getHeight());
// Get view and projection matrices
vr::HmdMatrix44_t p = hmd->GetProjectionMatrix((vr::EVREye)eye, 0.2f, 1000.f);
@@ -110,10 +115,11 @@ void Vive::Draw()
auto mat_proj = glm::make_mat4((float*)eyeProj);
auto mat_pose = glm::inverse(glm::make_mat4((float*)hmdPose));
auto mat_eye = glm::inverse(glm::make_mat4((float*)eyePose));
auto hmd_position = glm::vec3(h.m[0][3], h.m[1][3], h.m[2][3]);
proj[eye] = mat_proj;
view[eye] = mat_eye * mat_pose;
pose = glm::make_mat4((float*)hmdPose);
pose = mat_pose;
for (auto& c : controllers)
c.valid = false;
@@ -146,13 +152,32 @@ void Vive::Draw()
c.active = true;
c.valid = true;
hmd->GetControllerState(nDevice, &c.state, sizeof(vr::VRControllerState_t));
c.update(glm::make_mat4((float*)mat));
auto c_mat = glm::translate(-hmd_position) * glm::make_mat4((float*)mat);
c.update(c_mat);
c.buttons_bits = c.state.ulButtonPressed;
c.role = controllerRole;
for (int axi = 0; axi < 5; axi++)
{
c.axis[axi].x = c.state.rAxis[axi].x;
c.axis[axi].y = c.state.rAxis[axi].y;
for (auto b : { Controller::ButtonAxis::Trigger })
{
if (axi != (uint8_t)b)
continue;
if (glm::compMax(glm::abs(c.axis[axi])) > 0.f && !c.buttons_axis[(uint8_t)b])
{
c.buttons_axis[(uint8_t)b] = true;
if (on_button_axis)
on_button_axis(c, b, Controller::Action::Press);
}
if (glm::compMax(glm::abs(c.axis[axi])) == 0.f && c.buttons_axis[(uint64_t)b])
{
c.buttons_axis[(uint8_t)b] = false;
if (on_button_axis)
on_button_axis(c, b, Controller::Action::Release);
}
}
}
auto btn = c.state.ulButtonPressed;
@@ -175,7 +200,11 @@ void Vive::Draw()
}
break;
case vr::TrackedDeviceClass_HMD:
break;
{
auto level = hmd->GetTrackedDeviceActivityLevel(nDevice);
active = (level == vr::k_EDeviceActivityLevel_UserInteraction);
}
break;
case vr::TrackedDeviceClass_Invalid:
break;
case vr::TrackedDeviceClass_GenericTracker:
@@ -199,12 +228,26 @@ void Vive::Draw()
{
std::swap(controllers[0], controllers[1]);
}
}
}
void Vive::Draw()
{
if (!(hmd && comp && comp_attempts < 10))
return;
for (int eye = 0; eye < 2; ++eye)
{
eyes[eye].bindFramebuffer();
eyes[eye].clear(background);
glViewport(0, 0, eyes[eye].getWidth(), eyes[eye].getHeight());
if (on_draw)
on_draw(proj[eye], view[eye], mat_pose);
on_draw(proj[eye], view[eye], pose);
eyes[eye].unbindFramebuffer();
}
vr::Texture_t eyeTexture0 = { (void*)eyes[0].getTextureID(), vr::ETextureType::TextureType_OpenGL, vr::ColorSpace_Linear };
if (auto err = comp->Submit(vr::EVREye::Eye_Left, &eyeTexture0) != vr::EVRCompositorError::VRCompositorError_None)
{

View File

@@ -10,6 +10,11 @@ struct Controller
Menu = 0x000000002,
Grip = 0x000000004,
};
enum class ButtonAxis : uint8_t
{
Trigger = 1,
Pad = 0,
};
enum class Action
{
Press,
@@ -18,6 +23,7 @@ struct Controller
int index = 0;
bool active = false;
std::map<uint64_t, bool> buttons;
std::map<uint8_t, float> buttons_axis;
uint64_t buttons_bits{ 0 };
glm::vec2 axis[5];
glm::mat4 xform{ 1 };
@@ -46,6 +52,7 @@ struct Vive
RTT eyes[2];
vr::IVRSystem* hmd = nullptr;
vr::IVRCompositor* comp = nullptr;
vr::IVRSettings* settings = nullptr;
glm::vec3 euler{ 0 };
glm::vec4 background{ .5, .2, .2, 1 };
float yaw_offset = 0;
@@ -55,18 +62,22 @@ struct Vive
Controller controllers[32];
Controller trackers[32];
bool swap_controllers = false;
bool active = false;
std::string dev_serial{ "0000001" };
std::string dev_model{ "Vive" };
std::string dev_tracksys{ "lighthouse" };
std::string dev_manufacturer{ "HTC" };
std::function<void(const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose)> on_draw = nullptr;
std::function<void(const Controller&, Controller::Button, Controller::Action)> on_button = nullptr;
std::function<void(const Controller&, Controller::ButtonAxis, Controller::Action)> on_button_axis = nullptr;
Vive();
bool Initialize();
void Terminate();
void ResetYaw();
void Update();
void Draw();
std::string ReadPropString(vr::TrackedDeviceIndex_t nDevice, vr::ETrackedDeviceProperty p);
bool Valid();
};

View File

@@ -5,6 +5,7 @@
#include "texture.h"
#include "image.h"
#include "app.h"
#include "canvas.h"
#include "keymap.h"
#include "hmd.h"
#include "../resource.h"
@@ -715,6 +716,32 @@ int main(int argc, char** argv)
BT_SetTerminate();
LOG("start hmd render thread");
App::I.has_vr = true;
bool trigger_down = false;
cbuffer<glm::vec3, 10> controller_points;
glm::vec3 controller_last_point;
vive->on_button_axis = [&](const Controller& c, Controller::ButtonAxis b, Controller::Action a) {
if (b == Controller::ButtonAxis::Trigger)
{
if (a == Controller::Action::Press)
{
async_lock();
ui::Canvas::I->stroke_start(vive->controllers[0].pos * 800.f, vive->controllers[0].axis[1].x, ui::Canvas::I->m_current_brush);
async_unlock();
controller_last_point = vive->controllers[0].pos * 800.f;
controller_points.clear();
trigger_down = true;
}
if (a == Controller::Action::Release)
{
trigger_down = false;
async_lock();
ui::Canvas::I->stroke_end();
async_unlock();
}
}
};
const float target_tick_rate = 90;
unsigned long t0 = GetTickCount();
while (running && vive->Valid())
@@ -723,14 +750,37 @@ int main(int argc, char** argv)
unsigned long t1 = GetTickCount();
float dt = (float)(t1 - t0) / 1000.0f;
async_lock();
vive->Draw();
async_unlock();
vive->Update();
App::I.vr_active = vive->active;
App::I.vr_controller = vive->controllers[0].xform;
App::I.vr_controller_pos = vive->controllers[0].pos;
if (trigger_down)
{
controller_points.add(vive->controllers[0].pos * 800.f);
auto p = controller_points.average();
if (glm::distance(p, controller_last_point) > 1)
{
async_lock();
ui::Canvas::I->stroke_update(p, vive->controllers[0].axis[1].x);
async_unlock();
controller_last_point = p;
App::I.redraw = true;
}
}
if (vive->active)
{
async_lock();
vive->Draw();
async_unlock();
}
const int framerate = (1.f / target_tick_rate) * 1000;
const int diff = framerate - (t1 - t0);
hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
}
App::I.vr_active = false;
App::I.has_vr = false;
async_lock();
vive->Terminate();

View File

@@ -148,7 +148,7 @@ void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size)
m_stroke.reset();
m_stroke.start(m_brush);
for (int i = 0; i < 20; i++)
m_stroke.add_point(BezierCurve::Bezier2D(kp, i / 20.f), 1.f);
m_stroke.add_point(glm::vec3(BezierCurve::Bezier2D(kp, i / 20.f), 0), 1.f);
m_rtt.destroy();
m_rtt.create((int)new_size.x, (int)new_size.y);

View File

@@ -108,6 +108,7 @@
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtx/intersect.hpp>
#include <glm/gtx/component_wise.hpp>
#include <tinyxml2.h>
#include <jpge.h>