#include "hands.h" #include #include #include #include #include #include #include #include #include "../terrain/generator.h" #include "../world.h" namespace { struct { uf::Object left, right; } hands; struct { uf::Object left, right; } lines; } EXT_OBJECT_REGISTER_CPP(Hands) void ext::Hands::initialize() { uf::Object::initialize(); uf::Serializer& metadata = this->getComponent(); { this->addChild(::hands.left); this->addChild(::hands.right); this->addChild(::lines.left); this->addChild(::lines.right); } { bool loaded = true; for ( auto it = metadata["hands"].begin(); it != metadata["hands"].end(); ++it ) { std::string key = it.key().asString(); if ( !ext::openvr::requestRenderModel(metadata["hands"][key]["controller"]["model"].asString()) ) loaded = false; } if ( !loaded ) { this->addHook( "VR:Model.Loaded", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string name = json["name"].asString(); uf::Object* pointer = NULL; if ( name == metadata["hands"]["left"]["controller"]["model"].asString() ) pointer = &hands.left; else if ( name == metadata["hands"]["right"]["controller"]["model"].asString() ) pointer = &hands.right; else return "false"; { uf::Object& hand = *pointer; uf::Mesh& mesh = (hand.getComponent() = ext::openvr::getRenderModel( name )); mesh.graphic.process = true; mesh.graphic.material.attachShader("./data/shaders/base.stereo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); mesh.graphic.material.attachShader("./data/shaders/base.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); /* mesh.graphic.initializeShaders({ {"./data/shaders/base.stereo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, {"./data/shaders/base.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} }); mesh.graphic.description.rasterMode.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; mesh.generate(); mesh.graphic.bindUniform(); mesh.graphic.initialize(); mesh.graphic.autoAssign(); */ hand.initialize(); } { std::string hand = pointer == &hands.left ? "left" : "right"; auto& line = pointer == &hands.left ? lines.left : lines.right; line.addAlias(); pod::Transform<>& transform = line.getComponent>(); transform.orientation = uf::quaternion::axisAngle( { metadata["hands"][hand]["pointer"]["orientation"]["axis"][0].asFloat(), metadata["hands"][hand]["pointer"]["orientation"]["axis"][1].asFloat(), metadata["hands"][hand]["pointer"]["orientation"]["axis"][2].asFloat() }, metadata["hands"][hand]["pointer"]["orientation"]["angle"].asFloat() * 3.14159f / 180.0f ); transform.position = { metadata["hands"][hand]["pointer"]["offset"][0].asFloat(), metadata["hands"][hand]["pointer"]["offset"][1].asFloat(), metadata["hands"][hand]["pointer"]["offset"][2].asFloat() }; auto& mesh = line.getComponent(); mesh.vertices = { { {0.0f, 0.0f, 0.0f} }, { {0.0f, 0.0f, metadata["hands"][hand]["pointer"]["length"].asFloat()} }, }; mesh.initialize(true); mesh.graphic.initialize(); mesh.graphic.material.attachShader("./data/shaders/line.stereo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); mesh.graphic.material.attachShader("./data/shaders/line.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); mesh.graphic.descriptor.topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; mesh.graphic.descriptor.fill = VK_POLYGON_MODE_LINE; mesh.graphic.descriptor.lineWidth = metadata["hands"][hand]["pointer"]["width"].asFloat(); /* mesh.graphic.initializeShaders({ {"./data/shaders/line.stereo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, {"./data/shaders/line.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} }); mesh.generate(); mesh.graphic.bindUniform(); mesh.graphic.description.rasterMode.topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; mesh.graphic.description.rasterMode.fill = VK_POLYGON_MODE_LINE; mesh.graphic.description.rasterMode.lineWidth = metadata["hands"][hand]["pointer"]["width"].asFloat(); mesh.graphic.initialize(); mesh.graphic.autoAssign(); */ line.initialize(); } return "true"; }); } std::vector vHands = { &::hands.left, &::hands.right }; for ( auto pointer : vHands ) { auto& hand = *pointer; hand.addHook("VR:Input.Digital", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string side = &hand == &hands.left ? "left" : "right"; if ( json["hand"].asString() != side ) return "false"; // fire mouse click if ( json["name"].asString() == "click" ) { uf::Serializer payload; payload["type"] = "window:Mouse.Click"; payload["invoker"] = "vr"; payload["mouse"]["position"]["x"] = metadata["hands"][side]["cursor"]["position"][0].asFloat(); payload["mouse"]["position"]["y"] = metadata["hands"][side]["cursor"]["position"][1].asFloat(); payload["mouse"]["delta"]["x"] = 0; payload["mouse"]["delta"]["y"] = 0; payload["mouse"]["button"] = side == "left" ? "Right" : "Left"; payload["mouse"]["state"] = json["state"].asBool() ? "Down": "Up"; uf::hooks.call( payload["type"].asString(), payload ); } return "true"; }); } } } void ext::Hands::tick() { uf::Object::tick(); auto& scene = uf::scene::getCurrentScene(); auto& controller = *scene.getController(); auto& camera = controller.getComponent(); { pod::Transform<>& transform = hands.left.getComponent>(); transform.position = ext::openvr::controllerPosition( vr::Controller_Hand::Hand_Left ); transform.orientation = ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Left ); transform.scale = { 1, 1, 1 }; // transform.reference = &camera.getTransform(); } { pod::Transform<>& transform = hands.right.getComponent>(); transform.position = ext::openvr::controllerPosition( vr::Controller_Hand::Hand_Right ); transform.orientation = ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Right ); transform.scale = { 1, 1, 1 }; // transform.reference = &camera.getTransform(); } { pod::Transform<>& transform = lines.left.getComponent>(); transform.position = ext::openvr::controllerPosition( vr::Controller_Hand::Hand_Left, true ); transform.orientation = ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Left, true ); transform.scale = { 1, 1, 1 }; // transform.reference = hands.left.getComponentPointer>(); } { pod::Transform<>& transform = lines.right.getComponent>(); transform.position = ext::openvr::controllerPosition( vr::Controller_Hand::Hand_Right, true ); transform.orientation = ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Right, true ); transform.scale = { 1, 1, 1 }; // transform.reference = hands.right.getComponentPointer>(); } // raytrace pointer / hand collision { std::vector handPointers = { &::hands.left, &::hands.right }; for ( auto pointer : handPointers ) { auto& hand = *pointer; std::string side = &hand == &hands.left ? "left" : "right"; if ( !ext::openvr::controllerActive( side == "left" ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right ) ) continue; { pod::Transform<>& transform = (side == "left" ? lines.left : lines.right).getComponent>(); struct { pod::Vector3f origin; pod::Vector3f direction; } ray; struct { pod::Vector3f center; pod::Vector3f normal; } plane; transform = uf::transform::reorient( transform ); ray.origin = transform.position; ray.direction = transform.forward; pod::Transform<> gtransform; pod::Matrix4f mvp; uf::Serializer& cMetadata = controller.getComponent(); if ( cMetadata["overlay"]["position"].isArray() ) gtransform.position = { cMetadata["overlay"]["position"][0].asFloat(), cMetadata["overlay"]["position"][1].asFloat(), cMetadata["overlay"]["position"][2].asFloat(), }; if ( cMetadata["overlay"]["scale"].isArray() ) gtransform.scale = { cMetadata["overlay"]["scale"][0].asFloat(), cMetadata["overlay"]["scale"][1].asFloat(), cMetadata["overlay"]["scale"][2].asFloat(), }; if ( cMetadata["overlay"]["orientation"].isArray() ) gtransform.orientation = { cMetadata["overlay"]["orientation"][0].asFloat(), cMetadata["overlay"]["orientation"][1].asFloat(), cMetadata["overlay"]["orientation"][2].asFloat(), cMetadata["overlay"]["orientation"][3].asFloat(), }; plane.center = gtransform.position; { auto rotated = uf::quaternion::multiply( gtransform.orientation, pod::Vector4f{ 0, 0, 1, 1 } ); plane.normal.x = rotated.x; plane.normal.y = rotated.y; plane.normal.z = rotated.z; plane.normal = uf::vector::normalize( plane.normal ); } float denom = uf::vector::dot(plane.normal, ray.direction); if (abs(denom) > 0.0001f) { float t = uf::vector::dot( uf::vector::subtract(plane.center, ray.origin), plane.normal ) / denom; if ( t >= 0 ) { pod::Vector3f hit = ray.origin + (ray.direction * t); pod::Vector3f translated = uf::matrix::multiply( uf::matrix::inverse( uf::matrix::scale( uf::matrix::identity(), gtransform.scale ) ), uf::vector::subtract( plane.center, hit ) ); { auto& metadata = this->getComponent(); cMetadata["overlay"]["cursor"]["type"] = "vr"; cMetadata["overlay"]["cursor"]["position"][0] = translated.x; cMetadata["overlay"]["cursor"]["position"][1] = translated.y; cMetadata["overlay"]["cursor"]["position"][2] = translated.z; metadata["hands"][side]["cursor"] = cMetadata["overlay"]["cursor"]; } } } } #define DEBUG_MARKER() std::cout << side << ": " << __LINE__ << std::endl; /* Collision against world */ { bool local = true; bool sort = false; // pod::Thread& thread = uf::thread::fetchWorker(); pod::Thread& thread = uf::thread::has("Physics") ? uf::thread::get("Physics") : uf::thread::create( "Physics", true, false ); auto function = [&]() -> int { if ( !controller.hasParent() ) return 0; if ( controller.getParent().getName() != "Region" ) return 0; pod::Transform<> transform = hand.getComponent>(); transform.position = uf::quaternion::rotate( controller.getComponent>().orientation, transform.position ); transform.position += controller.getComponent>().position; transform.position += camera.getTransform().position; uf::Entity& parent = controller.getParent(); ext::TerrainGenerator& generator = parent.getComponent(); uf::Serializer& rMetadata = parent.getComponent(); pod::Transform<>& rTransform = parent.getComponent>(); pod::Vector3ui size; { size.x = rMetadata["region"]["size"][0].asUInt(); size.y = rMetadata["region"]["size"][1].asUInt(); size.z = rMetadata["region"]["size"][2].asUInt(); } pod::Vector3f voxelPosition = transform.position - rTransform.position; voxelPosition.x += size.x / 2.0f; voxelPosition.y += size.y / 2.0f + 1; voxelPosition.z += size.z / 2.0f; uf::CollisionBody pCollider; std::vector positions = { { voxelPosition.x, voxelPosition.y, voxelPosition.z }, { voxelPosition.x - 1, voxelPosition.y, voxelPosition.z }, { voxelPosition.x + 1, voxelPosition.y, voxelPosition.z }, { voxelPosition.x, voxelPosition.y - 1, voxelPosition.z }, { voxelPosition.x, voxelPosition.y + 1, voxelPosition.z }, { voxelPosition.x, voxelPosition.y, voxelPosition.z - 1 }, { voxelPosition.x, voxelPosition.y, voxelPosition.z + 1 }, }; { auto& metadata = this->getComponent(); // bottom uint16_t uid = generator.getVoxel( voxelPosition.x, voxelPosition.y, voxelPosition.z ); auto light = generator.getLight( voxelPosition.x, voxelPosition.y, voxelPosition.z ); metadata["hands"][side]["controller"]["color"][0] = ((light >> 12) & 0xF) / (float) (0xF); metadata["hands"][side]["controller"]["color"][1] = ((light >> 8) & 0xF) / (float) (0xF); metadata["hands"][side]["controller"]["color"][2] = ((light >> 4) & 0xF) / (float) (0xF); metadata["hands"][side]["controller"]["color"][3] = ((light ) & 0xF) / (float) (0xF); } for ( auto& position : positions ) { ext::TerrainVoxel voxel = ext::TerrainVoxel::atlas( generator.getVoxel( position.x, position.y, position.z ) ); pod::Vector3 offset = rTransform.position; offset.x += position.x - (size.x / 2.0f); offset.y += position.y - (size.y / 2.0f); offset.z += position.z - (size.z / 2.0f); if ( !voxel.solid() ) continue; uf::Collider* box = new uf::AABBox( offset, {0.5, 0.5, 0.5} ); pCollider.add(box); } uf::CollisionBody& collider = hand.getComponent(); { collider.clear(); uf::Collider* box = new uf::AABBox( transform.position, {0.25, 0.25, 0.25} ); collider.add(box); } pod::Physics& physics = hand.getComponent(); auto result = pCollider.intersects(collider); uf::Collider::Manifold strongest; strongest.depth = 0.001; for ( auto manifold : result ) { if ( manifold.colliding && manifold.depth > 0 ) { if ( strongest.depth < manifold.depth ) strongest = manifold; } } if ( strongest.colliding ) { pod::Vector3 correction = uf::vector::normalize(strongest.normal) * -(strongest.depth * strongest.depth * 1.001); { float mag = uf::vector::magnitude( correction ); uf::Serializer payload; payload["delay"] = 0.0f; payload["duration"] = uf::physics::time::delta; payload["frequency"] = 1.0f; payload["amplitude"] = fmin(1.0f, 1000.0f * mag); payload["side"] = side; uf::hooks.call( "VR:Haptics." + side, payload ); } /* transform.position += correction; if ( strongest.normal.x == 1 || strongest.normal.x == -1 ) physics.linear.velocity.x = 0; if ( strongest.normal.y == 1 || strongest.normal.y == -1 ) physics.linear.velocity.y = 0; if ( strongest.normal.z == 1 || strongest.normal.z == -1 ) physics.linear.velocity.z = 0; */ } return 0; }; if ( local ) function(); else uf::thread::add( thread, function, true ); } } } } void ext::Hands::render() { uf::Object::render(); uf::Serializer& metadata = this->getComponent(); auto& scene = uf::scene::getCurrentScene(); auto& controller = *scene.getController(); auto& camera = controller.getComponent(); pod::Matrix4f cameraModel = uf::matrix::translate( uf::matrix::identity(), camera.getTransform().position + controller.getComponent>().position ) * uf::quaternion::matrix( controller.getComponent>().orientation * pod::Vector4f{1,1,1,-1} ); if ( hands.left.hasComponent() ) { auto& mesh = hands.left.getComponent(); auto& transform = hands.left.getComponent>(); mesh.graphic.process = ext::openvr::controllerActive( vr::Controller_Hand::Hand_Left ); pod::Matrix4f model = cameraModel * ext::openvr::controllerModelMatrix( vr::Controller_Hand::Hand_Left, false ); if ( mesh.generated ) { // auto& uniforms = mesh.graphic.uniforms(); auto& uniforms = mesh.graphic.material.shaders.front().uniforms.front().get(); uniforms.matrices.model = model; for ( std::size_t i = 0; i < 2; ++i ) { uniforms.matrices.view[i] = camera.getView( i ); uniforms.matrices.projection[i] = camera.getProjection( i ); } uniforms.color[0] = metadata["hands"]["left"]["controller"]["color"][0].asFloat(); uniforms.color[1] = metadata["hands"]["left"]["controller"]["color"][1].asFloat(); uniforms.color[2] = metadata["hands"]["left"]["controller"]["color"][2].asFloat(); uniforms.color[3] = metadata["hands"]["left"]["controller"]["color"][3].asFloat(); // mesh.graphic.updateBuffer( uniforms, 0, false ); mesh.graphic.material.shaders.front().updateBuffer( uniforms, 0, false ); } } if ( hands.right.hasComponent() ) { auto& mesh = hands.right.getComponent(); auto& transform = hands.right.getComponent>(); mesh.graphic.process = ext::openvr::controllerActive( vr::Controller_Hand::Hand_Right ); pod::Matrix4f model = cameraModel * ext::openvr::controllerModelMatrix( vr::Controller_Hand::Hand_Right, false ); if ( mesh.generated ) { // auto& uniforms = mesh.graphic.uniforms(); auto& uniforms = mesh.graphic.material.shaders.front().uniforms.front().get(); uniforms.matrices.model = model; for ( std::size_t i = 0; i < 2; ++i ) { uniforms.matrices.view[i] = camera.getView( i ); uniforms.matrices.projection[i] = camera.getProjection( i ); } uniforms.color[0] = metadata["hands"]["right"]["controller"]["color"][0].asFloat(); uniforms.color[1] = metadata["hands"]["right"]["controller"]["color"][1].asFloat(); uniforms.color[2] = metadata["hands"]["right"]["controller"]["color"][2].asFloat(); uniforms.color[3] = metadata["hands"]["right"]["controller"]["color"][3].asFloat(); // mesh.graphic.updateBuffer( uniforms, 0, false ); mesh.graphic.material.shaders.front().updateBuffer( uniforms, 0, false ); } } if ( lines.left.hasComponent() ) { auto& mesh = lines.left.getComponent(); auto& transform = lines.left.getComponent>(); mesh.graphic.process = ext::openvr::controllerActive( vr::Controller_Hand::Hand_Left ); pod::Matrix4f model = cameraModel * ext::openvr::controllerModelMatrix( vr::Controller_Hand::Hand_Left, true ); if ( mesh.generated ) { // auto& uniforms = mesh.graphic.uniforms(); auto& uniforms = mesh.graphic.material.shaders.front().uniforms.front().get(); uniforms.matrices.model = model; for ( std::size_t i = 0; i < 2; ++i ) { uniforms.matrices.view[i] = camera.getView( i ); uniforms.matrices.projection[i] = camera.getProjection( i ); } uniforms.color[0] = metadata["hands"]["left"]["pointer"]["color"][0].asFloat(); uniforms.color[1] = metadata["hands"]["left"]["pointer"]["color"][1].asFloat(); uniforms.color[2] = metadata["hands"]["left"]["pointer"]["color"][2].asFloat(); uniforms.color[3] = metadata["hands"]["left"]["pointer"]["color"][3].asFloat(); // mesh.graphic.updateBuffer( uniforms, 0, false ); mesh.graphic.material.shaders.front().updateBuffer( uniforms, 0, false ); } } if ( lines.right.hasComponent() ) { auto& mesh = lines.right.getComponent(); auto& transform = lines.right.getComponent>(); mesh.graphic.process = ext::openvr::controllerActive( vr::Controller_Hand::Hand_Right ); pod::Matrix4f model = cameraModel * ext::openvr::controllerModelMatrix( vr::Controller_Hand::Hand_Right, true ); if ( mesh.generated ) { // auto& uniforms = mesh.graphic.uniforms(); auto& uniforms = mesh.graphic.material.shaders.front().uniforms.front().get(); uniforms.matrices.model = model; for ( std::size_t i = 0; i < 2; ++i ) { uniforms.matrices.view[i] = camera.getView( i ); uniforms.matrices.projection[i] = camera.getProjection( i ); } uniforms.color[0] = metadata["hands"]["right"]["pointer"]["color"][0].asFloat(); uniforms.color[1] = metadata["hands"]["right"]["pointer"]["color"][1].asFloat(); uniforms.color[2] = metadata["hands"]["right"]["pointer"]["color"][2].asFloat(); uniforms.color[3] = metadata["hands"]["right"]["pointer"]["color"][3].asFloat(); // mesh.graphic.updateBuffer( uniforms, 0, false ); mesh.graphic.material.shaders.front().updateBuffer( uniforms, 0, false ); } } /* if ( lines.right.hasComponent() ) { auto& mesh = lines.right.getComponent(); // mesh.graphic.process = ext::openvr::controllerActive( vr::Controller_Hand::Hand_Right ); auto& transform = lines.right.getComponent>(); if ( mesh.generated ) { auto& uniforms = mesh.graphic.uniforms(); uniforms.matrices.model = cameraModel * ext::openvr::controllerMatrix( vr::Controller_Hand::Hand_Right, true );//uf::transform::model( transform ); for ( std::size_t i = 0; i < 2; ++i ) { uniforms.matrices.view[i] = camera.getView( i ); uniforms.matrices.projection[i] = camera.getProjection( i ); } uniforms.color[0] = metadata["hands"]["right"]["pointer"]["color"][0].asFloat(); uniforms.color[1] = metadata["hands"]["right"]["pointer"]["color"][1].asFloat(); uniforms.color[2] = metadata["hands"]["right"]["pointer"]["color"][2].asFloat(); uniforms.color[3] = metadata["hands"]["right"]["pointer"]["color"][3].asFloat(); // mesh.graphic.updateBuffer( uniforms, 0, false ); mesh.graphic.material.shaders.front().updateBuffer( uniforms, 0, false ); } } */ }