Merge pull request #3145 from samcake/19734

19734 - Leapmotion support
This commit is contained in:
Brad Hefta-Gaub 2014-07-17 08:45:55 -07:00
commit d8d6d08958
36 changed files with 2410 additions and 0 deletions

View file

@ -262,3 +262,8 @@ If you need to debug Interface, you can run interface from within Visual Studio
* In the Solution Explorer, right click interface and click Set as StartUp Project
* Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug
* Now you can run and debug interface through Visual Studio
#### Devices
You can support external input/output devices such as Leap Motion, Faceplus, Faceshift PrioVR, RTmidi, SixSense and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in /hifi/interface/external/ for the detailed explanation of the requirements to use the device.

View file

@ -0,0 +1,47 @@
# Try to find the LeapMotion library
#
# You must provide a LEAPMOTION_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# LEAPMOTION_FOUND - system found LEAPMOTION
# LEAPMOTION_INCLUDE_DIRS - the LEAPMOTION include directory
# LEAPMOTION_LIBRARIES - Link this to use LEAPMOTION
#
# Created on 6/2/2014 by Sam Cake
# Copyright (c) 2014 High Fidelity
#
if (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS)
# in cache already
set(LEAPMOTION_FOUND TRUE)
else (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS)
set(LEAPMOTION_SEARCH_DIRS "${LEAPMOTION_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/leapmotion")
find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS})
if (WIN32)
find_library(LEAPMOTION_LIBRARIES "lib/x86/Leap.lib" HINTS ${LEAPMOTION_SEARCH_DIRS})
endif (WIN32)
if (APPLE)
find_library(LEAPMOTION_LIBRARIES "lib/libLeap.dylib" HINTS ${LEAPMOTION_SEARCH_DIRS})
endif (APPLE)
if (LEAPMOTION_INCLUDE_DIRS AND LEAPMOTION_LIBRARIES)
set(LEAPMOTION_FOUND TRUE)
endif (LEAPMOTION_INCLUDE_DIRS AND LEAPMOTION_LIBRARIES)
if (LEAPMOTION_FOUND)
if (NOT LEAPMOTION_FIND_QUIETLY)
message(STATUS "Found LEAPMOTION... ${LEAPMOTION_LIBRARIES}")
endif (NOT LEAPMOTION_FIND_QUIETLY)
else ()
if (LEAPMOTION_FIND_REQUIRED)
message(FATAL_ERROR "Could not find LEAPMOTION")
endif (LEAPMOTION_FIND_REQUIRED)
endif ()
# show the LEAPMOTION_INCLUDE_DIRS and LEAPMOTION_LIBRARIES variables only in the advanced view
mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES)
endif (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS)

258
examples/leapOfFaith.js Normal file
View file

@ -0,0 +1,258 @@
//
// leapOfFaith.js
// examples
//
// Created by Sam Cake on 6/22/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var jointList = MyAvatar.getJointNames();
var jointMappings = "\n# Joint list start";
for (var i = 0; i < jointList.length; i++) {
jointMappings = jointMappings + "\njointIndex = " + jointList[i] + " = " + i;
}
print(jointMappings + "\n# Joint list end");
function vec3ToString( v ) {
return ("(" + v.x +", " + v.y + ", " + v.z + ")" );
}
function quatToString( q ) {
return ("(" + q.x +", " + q.y + ", " + q.z + ", " + q.w + ")" );
}
function printSpatialEvent( label, spatialEvent ) {
if ( false ){//label == "RightHandIndex1" ) {
var dataString = label + " " +
/*vec3ToString( spatialEvent.locTranslation ) + " " +
quatToString( spatialEvent.locRotation ) + " " +*/
vec3ToString( spatialEvent.absTranslation ) + " " +
quatToString( spatialEvent.absRotation );
print( dataString );
}
}
function avatarToWorldPos( apos ) {
// apply offset ?
var offset = { x: 0, y: 0.5, z: -0.5 };
var lpos = Vec3.sum(apos, offset);
var wpos = Vec3.sum( MyAvatar.position , Vec3.multiplyQbyV(MyAvatar.orientation, lpos) );
return wpos;
}
function avatarToWorldQuat( aori) {
var wori = Quat.multiply(MyAvatar.orientation, aori);
return wori;
}
function controlerToSkeletonOri( jointName, isRightSide, event ) {
var qAvatarRootOffset = Quat.angleAxis( -180, {x:0, y:1, z:0});
var qAxisOffset = Quat.angleAxis( -( 2 * isRightSide - 1) * 90, {x:0, y:1, z:0});
var qAbsJoint = event.absRotation;
return Quat.multiply( qAvatarRootOffset, Quat.multiply( qAbsJoint, qAxisOffset ) );
}
var jointParticles = [];
function updateJointParticle( joint, pos, ori, look ) {
/* print( "debug 1" );
var jointID = jointParticles[ joint ];
if ( jointID == null ) {
print( "debug create " + joint );
*/
var radius = 0.005* look.r;
var ballProperties = {
position: pos,
velocity: { x: 0, y: 0, z: 0},
gravity: { x: 0, y: 0, z: 0 },
damping: 0,
radius : radius,
color: look.c,
lifetime: 0.05
};
var atomPos = Particles.addParticle(ballProperties);
/* // Zaxis
var Zaxis = Vec3.multiply( Quat.getFront( ori ), - 1.5 * radius ) ;
ballProperties.position = Vec3.sum(pos, Zaxis );
ballProperties.radius = 0.35* radius;
ballProperties.color= { red: 255, green: 255, blue: 255 };
var atomZ = Particles.addParticle(ballProperties);
var up = Vec3.multiply( Quat.getUp( ori ), 1.5 * radius ) ;
ballProperties.position = Vec3.sum(pos, up) ;
ballProperties.radius = 0.35* radius;
ballProperties.color= { red: 0, green: 255, blue: 0 };
var atomY = Particles.addParticle(ballProperties);
var right = Vec3.multiply( Quat.getRight( ori ), 1.5 * radius ) ;
ballProperties.position = Vec3.sum(pos, right) ;
ballProperties.radius = 0.35* radius;
ballProperties.color= { red: 255, green: 0, blue: 225 };
var atomX = Particles.addParticle(ballProperties);
*/
// jointParticles[ joint ] = { p: atomPos, x: atomX, y: atomY, z: atomZ };
/*
} else {
//print( "debug update " + joint );
var p = Particles.getParticleProperties( jointID.p );
p.position = pos;
// p.lifetime = 1.0;
Particles.editParticle( jointID.p, p );
}*/
}
function evalArmBoneLook( isRightSide, bone ) {
return { c: { red: (255 * ( 1 - isRightSide )),
green: 255 * ( ((bone)) / 2 ),
blue: (255 * isRightSide) },
r: 3 ,
side: isRightSide };
}
function evalFingerBoneLook( isRightSide, finger, bone ) {
return { c: { red: (255 * ( 1 - isRightSide )),
green: 255 * ( ((bone - 1)) / 3 ),
blue: (255 * isRightSide) },
r: (5 + (5 - (finger-1))) / 10.0,
side: isRightSide };
}
var leapJoints = [
{ n: "joint_L_elbow", l: evalArmBoneLook( 0, 2) },
{ n: "joint_L_hand", l: evalArmBoneLook( 0, 1) },
{ n: "joint_L_wrist", l: evalArmBoneLook( 0, 0) },
{ n: "joint_L_thumb2", l: evalFingerBoneLook( 0, 1, 2) },
{ n: "joint_L_thumb3", l: evalFingerBoneLook( 0, 1, 3) },
{ n: "joint_L_thumb4", l: evalFingerBoneLook( 0, 1, 4) },
{ n: "joint_L_index1", l: evalFingerBoneLook( 0, 2, 1) },
{ n: "joint_L_index2", l: evalFingerBoneLook( 0, 2, 2) },
{ n: "joint_L_index3", l: evalFingerBoneLook( 0, 2, 3) },
{ n: "joint_L_index4", l: evalFingerBoneLook( 0, 2, 4) },
{ n: "joint_L_middle1", l: evalFingerBoneLook( 0, 3, 1) },
{ n: "joint_L_middle2", l: evalFingerBoneLook( 0, 3, 2) },
{ n: "joint_L_middle3", l: evalFingerBoneLook( 0, 3, 3) },
{ n: "joint_L_middle4", l: evalFingerBoneLook( 0, 3, 4) },
{ n: "joint_L_ring1", l: evalFingerBoneLook( 0, 4, 1) },
{ n: "joint_L_ring2", l: evalFingerBoneLook( 0, 4, 2) },
{ n: "joint_L_ring3", l: evalFingerBoneLook( 0, 4, 3) },
{ n: "joint_L_ring4", l: evalFingerBoneLook( 0, 4, 4) },
{ n: "joint_L_pinky1", l: evalFingerBoneLook( 0, 5, 1) },
{ n: "joint_L_pinky2", l: evalFingerBoneLook( 0, 5, 2) },
{ n: "joint_L_pinky3", l: evalFingerBoneLook( 0, 5, 3) },
{ n: "joint_L_pinky4", l: evalFingerBoneLook( 0, 5, 4) },
{ n: "joint_R_elbow", l: evalArmBoneLook( 1, 2) },
{ n: "joint_R_hand", l: evalArmBoneLook( 1, 1) },
{ n: "joint_R_wrist", l: evalArmBoneLook( 1, 0) },
{ n: "joint_R_thumb2", l: evalFingerBoneLook( 1, 1, 2) },
{ n: "joint_R_thumb3", l: evalFingerBoneLook( 1, 1, 3) },
{ n: "joint_R_thumb4", l: evalFingerBoneLook( 1, 1, 4) },
{ n: "joint_R_index1", l: evalFingerBoneLook( 1, 2, 1) },
{ n: "joint_R_index2", l: evalFingerBoneLook( 1, 2, 2) },
{ n: "joint_R_index3", l: evalFingerBoneLook( 1, 2, 3) },
{ n: "joint_R_index4", l: evalFingerBoneLook( 1, 2, 4) },
{ n: "joint_R_middle1", l: evalFingerBoneLook( 1, 3, 1) },
{ n: "joint_R_middle2", l: evalFingerBoneLook( 1, 3, 2) },
{ n: "joint_R_middle3", l: evalFingerBoneLook( 1, 3, 3) },
{ n: "joint_R_middle4", l: evalFingerBoneLook( 1, 3, 4) },
{ n: "joint_R_ring1", l: evalFingerBoneLook( 1, 4, 1) },
{ n: "joint_R_ring2", l: evalFingerBoneLook( 1, 4, 2) },
{ n: "joint_R_ring3", l: evalFingerBoneLook( 1, 4, 3) },
{ n: "joint_R_ring4", l: evalFingerBoneLook( 1, 4, 4) },
{ n: "joint_R_pinky1", l: evalFingerBoneLook( 1, 5, 1) },
{ n: "joint_R_pinky2", l: evalFingerBoneLook( 1, 5, 2) },
{ n: "joint_R_pinky3", l: evalFingerBoneLook( 1, 5, 3) },
{ n: "joint_R_pinky4", l: evalFingerBoneLook( 1, 5, 4) },
];
function onSpatialEventHandler( jointName, look ) {
var _jointName = jointName;
var _look = look;
var _side = look.side;
return (function( spatialEvent ) {
// THis should be the call to update the skeleton joint from
// the absolute joint transform coming from the Leap controller
// WE need a new method on MyAvatar which will ultimately call
// state.setRotationFromBindFrame(rotation, priority) in
// Avatar::simulate=>void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority)
// MyAvatar.setJointRotationFromBindSpace(_jointName, controlerToSkeletonOri( _jointName, _side, spatialEvent ));
updateJointParticle(_jointName,
avatarToWorldPos( spatialEvent.absTranslation ),
avatarToWorldQuat( spatialEvent.absRotation ),
_look );
printSpatialEvent(_jointName, spatialEvent );
});
}
var isPullingSpatialData = true;
var jointControllers = [];
for ( i in leapJoints ) {
print( leapJoints[i].n );
// In the current implementation, the LEapmotion is the only "Spatial" device
// Each jointTracker is retreived from the joint name following the rigging convention
var controller = Controller.createInputController( "Spatial", leapJoints[i].n );
var handler = onSpatialEventHandler( leapJoints[i].n, leapJoints[i].l );
jointControllers.push( { c: controller, h: handler } );
if ( ! isPullingSpatialData ) {
controller.spatialEvent.connect( handler );
}
}
Script.update.connect(function(deltaTime) {
if ( isPullingSpatialData )
{
for ( i in jointControllers ) {
if ( jointControllers[i].c.isActive() ) {
var spatialEvent = { absTranslation: jointControllers[i].c.getAbsTranslation(),
absRotation: jointControllers[i].c.getAbsRotation(),
locTranslation: jointControllers[i].c.getLocTranslation(),
locRotation: jointControllers[i].c.getLocRotation() };
jointControllers[i].h( spatialEvent );
}
}
}
});
Script.scriptEnding.connect(function() {
});

BIN
images/bg_hr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

BIN
images/blacktocat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/body-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
images/highlight-bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
images/hr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
images/icon_download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/octocat-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/sprite_download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
images/tar-gz-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/zip-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

52
index.html Normal file
View file

@ -0,0 +1,52 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>Hifi by highfidelity</title>
<link rel="stylesheet" href="stylesheets/styles.css">
<link rel="stylesheet" href="stylesheets/pygment_trac.css">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="wrapper">
<header>
<h1>Hifi</h1>
<p>Open, decentralized virtual worlds using sensors to control avatars and dynamically assigned devices as servers. San Francisco based startup, we are hiring: http://highfidelity.io/jobs You can also contribute by doing jobs listed at http://worklist.net -</p>
<p class="view"><a href="https://github.com/highfidelity/hifi">View the Project on GitHub <small>highfidelity/hifi</small></a></p>
<ul>
<li><a href="https://github.com/highfidelity/hifi/zipball/master">Download <strong>ZIP File</strong></a></li>
<li><a href="https://github.com/highfidelity/hifi/tarball/master">Download <strong>TAR Ball</strong></a></li>
<li><a href="https://github.com/highfidelity/hifi">View On <strong>GitHub</strong></a></li>
</ul>
</header>
<section>
<h3>
<a name="avatar-documentation" class="anchor" href="#avatar-documentation"><span class="octicon octicon-link"></span></a>Avatar Documentation</h3>
<ul>
<li><a href="https://github.com/highfidelity/hifi/wiki/Exporting-Your-Rigged-Avatar-From-Faceshift">Exporting your Rigged Avatar from Faceshift</a></li>
<li><a href="https://github.com/highfidelity/hifi/wiki/Creating-Blendshapes-for-your-Avatar">Creating Blendshapes for your Avatar</a></li>
<li><a href="https://github.com/highfidelity/hifi/wiki/How-to-Rig-a-Character-for-Faceshift">How to Rig a Character for Faceshift</a></li>
<li><a href="https://github.com/highfidelity/hifi/wiki/Naming-Your-Skeletal-Joints">Naming your Skeletal Joints</a></li>
<li><a href="https://github.com/highfidelity/hifi/wiki/The-FST-file">The FST File</a></li>
<li><a href="https://github.com/highfidelity/hifi/wiki/Training-in-Faceshift">Training in Faceshift</a></li>
<li><a href="https://github.com/highfidelity/hifi/wiki/Uploading-Your-Models">Uploading your Models</a></li>
</ul>
</section>
<footer>
<p>This project is maintained by <a href="https://github.com/highfidelity">highfidelity</a></p>
<p><small>Hosted on GitHub Pages &mdash; Theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
</footer>
</div>
<script src="javascripts/scale.fix.js"></script>
</body>
</html>

View file

@ -18,8 +18,10 @@ set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus")
set(PRIOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/priovr")
set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense")
set(VISAGE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/visage")
set(LEAPMOTION_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/leapmotion")
set(RTMIDI_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/rtmidi")
find_package(Qt5LinguistTools REQUIRED)
find_package(Qt5LinguistToolsMacros)
@ -139,6 +141,7 @@ find_package(PrioVR)
find_package(SDL)
find_package(Sixense)
find_package(Visage)
find_package(LeapMotion)
find_package(ZLIB)
find_package(Qxmpp)
find_package(RtMidi)
@ -199,6 +202,17 @@ if (PRIOVR_FOUND AND NOT DISABLE_PRIOVR)
target_link_libraries(${TARGET_NAME} "${PRIOVR_LIBRARIES}")
endif (PRIOVR_FOUND AND NOT DISABLE_PRIOVR)
# and with LeapMotion library
if (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION)
add_definitions(-DHAVE_LEAPMOTION)
include_directories(SYSTEM "${LEAPMOTION_INCLUDE_DIRS}")
if (APPLE OR UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LEAPMOTION_INCLUDE_DIRS}")
endif ()
target_link_libraries(${TARGET_NAME} "${LEAPMOTION_LIBRARIES}")
endif (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION)
# and with SDL for joysticks
if (SDL_FOUND AND NOT DISABLE_SDL)
add_definitions(-DHAVE_SDL)

View file

@ -0,0 +1,33 @@
Instructions for adding the Leap Motion library (LeapSDK) to Interface
Sam Cake, June 10, 2014
You can download the Leap Developer Kit from https://developer.leapmotion.com/ (account creation required).
Interface has been tested with SDK versions:
- LeapDeveloperKit_2.0.3+17004_win
- LeapDeveloperKit_2.0.3+17004_mac
1. Copy the LeapSDK folders from the LeapDeveloperKit installation directory (Lib, Include) into the interface/externals/leapmotion folder.
This readme.txt should be there as well.
The files neeeded in the folders are:
include/
- Leap.h
- Leap.i
- LeapMath.h
lib/
x86/
- Leap.dll
- Leap.lib
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
- lipLeap.dylib
libc++/
-libLeap.dylib
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'leapmotion' that contains the 2 folders mentioned above (Include, Lib).
2. Clear your build directory, run cmake and build, and you should be all set.

View file

@ -89,6 +89,8 @@
#include "ui/Stats.h"
#include "ui/TextRenderer.h"
#include "devices/Leapmotion.h"
using namespace std;
// Starfield information
@ -1720,6 +1722,8 @@ void Application::init() {
_faceplus.init();
_visage.init();
Leapmotion::init();
// fire off an immediate domain-server check in now that settings are loaded
NodeList::getInstance()->sendDomainServerCheckIn();
@ -2053,11 +2057,13 @@ void Application::update(float deltaTime) {
updateMouseRay(); // check what's under the mouse and update the mouse voxel
{
PerformanceTimer perfTimer("devices");
DeviceTracker::updateAll();
updateFaceshift();
updateVisage();
_sixenseManager.update(deltaTime);
_joystickManager.update();
_prioVR.update(deltaTime);
}
{
PerformanceTimer perfTimer("myAvatar");
@ -2065,6 +2071,10 @@ void Application::update(float deltaTime) {
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
}
// Dispatch input events
_controllerScriptingInterface.updateInputControllers();
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
@ -3177,6 +3187,7 @@ void Application::resetSensors() {
OculusManager::reset();
_prioVR.reset();
//_leapmotion.reset();
QCursor::setPos(_mouseX, _mouseY);
_myAvatar->reset();

View file

@ -0,0 +1,88 @@
//
// DeviceTracker.cpp
// interface/src/devices
//
// Created by Sam Cake on 6/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DeviceTracker.h"
DeviceTracker::SingletonData::~SingletonData() {
// Destroy all the device registered
//TODO C++11 for (auto device = _devicesVector.begin(); device != _devicesVector.end(); device++) {
for (Vector::iterator device = _devicesVector.begin(); device != _devicesVector.end(); device++) {
delete (*device);
}
}
int DeviceTracker::getNumDevices() {
return Singleton::get()->_devicesMap.size();
}
DeviceTracker::ID DeviceTracker::getDeviceID(const Name& name) {
//TODO C++11 auto deviceIt = Singleton::get()->_devicesMap.find(name);
Map::iterator deviceIt = Singleton::get()->_devicesMap.find(name);
if (deviceIt != Singleton::get()->_devicesMap.end()) {
return (*deviceIt).second;
} else {
return INVALID_DEVICE;
}
}
DeviceTracker* DeviceTracker::getDevice(const Name& name) {
return getDevice(getDeviceID(name));
}
DeviceTracker* DeviceTracker::getDevice(DeviceTracker::ID deviceID) {
if ((deviceID >= 0) && (deviceID < Singleton::get()->_devicesVector.size())) {
return Singleton::get()->_devicesVector[ deviceID ];
} else {
return NULL;
}
}
DeviceTracker::ID DeviceTracker::registerDevice(const Name& name, DeviceTracker* device) {
// Check that the device exists, if not exit
if (!device) {
return INVALID_DEVICE;
}
// Look if the name is not already used
ID deviceID = getDeviceID(name);
if (deviceID >= 0) {
return INVALID_DEVICE_NAME;
}
// Good to register the device
deviceID = Singleton::get()->_devicesVector.size();
Singleton::get()->_devicesMap.insert(Map::value_type(name, deviceID));
Singleton::get()->_devicesVector.push_back(device);
device->assignIDAndName(deviceID, name);
return deviceID;
}
void DeviceTracker::updateAll() {
//TODO C++11 for (auto deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) {
for (Vector::iterator deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) {
if ((*deviceIt))
(*deviceIt)->update();
}
}
// Core features of the Device Tracker
DeviceTracker::DeviceTracker() :
_ID(INVALID_DEVICE),
_name("Unkown")
{
}
DeviceTracker::~DeviceTracker() {
}
void DeviceTracker::update() {
}

View file

@ -0,0 +1,114 @@
//
// DeviceTracker.h
// interface/src/devices
//
// Created by Sam Cake on 6/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DeviceTracker_h
#define hifi_DeviceTracker_h
#include <string>
#include <vector>
#include <map>
// Singleton template class
template < typename T >
class TemplateSingleton {
public:
static T* get() {
if ( !_singleton._one ) {
_singleton._one = new T();
}
return _singleton._one;
}
TemplateSingleton() :
_one(0)
{
}
~TemplateSingleton() {
if ( _one ) {
delete _one;
_one = 0;
}
}
private:
static TemplateSingleton< T > _singleton;
T* _one;
};
template <typename T>
TemplateSingleton<T> TemplateSingleton<T>::_singleton;
/// Base class for device trackers.
class DeviceTracker {
public:
// THe ID and Name types used to manage the pool of devices
typedef std::string Name;
typedef int ID;
static const ID INVALID_DEVICE = -1;
static const ID INVALID_DEVICE_NAME = -2;
// Singleton interface to register and query the devices currently connected
static int getNumDevices();
static ID getDeviceID(const Name& name);
static DeviceTracker* getDevice(ID deviceID);
static DeviceTracker* getDevice(const Name& name);
/// Update all the devices calling for their update() function
/// This should be called every frame by the main loop to update all the devices that pull their state
static void updateAll();
/// Register a device tracker to the factory
/// Right after creating a new DeviceTracker, it should be registered
/// This is why, it's recommended to use a factory static call in the specialized class
/// to create a new input device
///
/// \param name The Name under wich registering the device
/// \param parent The DeviceTracker
///
/// \return The Index of the newly registered device.
/// Valid if everything went well.
/// INVALID_DEVICE if the device is not valid (NULL)
/// INVALID_DEVICE_NAME if the name is already taken
static ID registerDevice(const Name& name, DeviceTracker* tracker);
// DeviceTracker interface
virtual void update();
/// Get the ID assigned to the Device when registered after creation, or INVALID_DEVICE if it hasn't been registered which should not happen.
ID getID() const { return _ID; }
/// Get the name assigned to the Device when registered after creation, or "Unknown" if it hasn't been registered which should not happen.
const Name& getName() const { return _name; }
protected:
DeviceTracker();
virtual ~DeviceTracker();
private:
ID _ID;
Name _name;
// this call is used by the singleton when the device tracker is currently beeing registered and beeing assigned an ID
void assignIDAndName( const ID id, const Name& name ) { _ID = id; _name = name; }
typedef std::vector< DeviceTracker* > Vector;
typedef std::map< Name, ID > Map;
struct SingletonData {
Map _devicesMap;
Vector _devicesVector;
~SingletonData();
};
typedef TemplateSingleton< SingletonData > Singleton;
};
#endif // hifi_DeviceTracker_h

View file

@ -0,0 +1,230 @@
//
// Leapmotion.cpp
// interface/src/devices
//
// Created by Sam Cake on 6/2/2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SharedUtil.h"
#include "Leapmotion.h"
const int PALMROOT_NUM_JOINTS = 3;
const int FINGER_NUM_JOINTS = 4;
const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS;
const DeviceTracker::Name Leapmotion::NAME = "Leapmotion";
// find the index of a joint from
// the side: true = right
// the finger & the bone:
// finger in [0..4] : bone in [0..3] a finger phalange
// [-1] up the hand branch : bone in [0..2] <=> [ hand, forearm, arm]
MotionTracker::Index evalJointIndex(bool isRightSide, int finger, int bone) {
MotionTracker::Index offset = 1 // start after root
+ (int(isRightSide) * HAND_NUM_JOINTS) // then offset for side
+ PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain
if (finger >= 0) {
// from there go down in the correct finger and bone
return offset + (finger * FINGER_NUM_JOINTS) + bone;
} else {
// or go back up for the correct root bone
return offset - 1 - bone;
}
}
// static
void Leapmotion::init() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new Leapmotion and register it
Leapmotion* leap = new Leapmotion();
DeviceTracker::registerDevice(NAME, leap);
}
}
// static
Leapmotion* Leapmotion::getInstance() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new Leapmotion and register it
device = new Leapmotion();
DeviceTracker::registerDevice(NAME, device);
}
return dynamic_cast< Leapmotion* > (device);
}
Leapmotion::Leapmotion() :
MotionTracker(),
_active(false)
{
// Create the Leapmotion joint hierarchy
std::vector< Semantic > sides;
sides.push_back("joint_L_");
sides.push_back("joint_R_");
std::vector< Semantic > rootBones;
rootBones.push_back("elbow");
rootBones.push_back("hand");
rootBones.push_back("wrist");
std::vector< Semantic > fingers;
fingers.push_back("thumb");
fingers.push_back("index");
fingers.push_back("middle");
fingers.push_back("ring");
fingers.push_back("pinky");
std::vector< Semantic > fingerBones;
fingerBones.push_back("1");
fingerBones.push_back("2");
fingerBones.push_back("3");
fingerBones.push_back("4");
std::vector< Index > palms;
for (unsigned int s = 0; s < sides.size(); s++) {
Index rootJoint = 0;
for (unsigned int rb = 0; rb < rootBones.size(); rb++) {
rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint);
}
// capture the hand index for debug
palms.push_back(rootJoint);
for (unsigned int f = 0; f < fingers.size(); f++) {
for (unsigned int b = 0; b < fingerBones.size(); b++) {
rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint);
}
}
}
}
Leapmotion::~Leapmotion() {
}
#ifdef HAVE_LEAPMOTION
glm::quat quatFromLeapBase(float sideSign, const Leap::Matrix& basis) {
// fix the handness to right and always...
glm::vec3 xAxis = glm::normalize(sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z));
glm::vec3 yAxis = glm::normalize(glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z));
glm::vec3 zAxis = glm::normalize(glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z));
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
return orientation;
}
glm::vec3 vec3FromLeapVector(const Leap::Vector& vec) {
return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER);
}
#endif
void Leapmotion::update() {
#ifdef HAVE_LEAPMOTION
// Check that the controller is actually active
_active = _controller.isConnected();
if (!_active) {
return;
}
// go through all the joints and increment their counter since last update
// TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
(*jointIt).tickNewFrame();
}
// Get the most recent frame and report some basic information
const Leap::Frame frame = _controller.frame();
static int64_t lastFrameID = -1;
int64_t newFrameID = frame.id();
// If too soon then exit
if (lastFrameID >= newFrameID)
return;
glm::vec3 delta(0.f);
glm::quat handOri;
if (!frame.hands().isEmpty()) {
for (int handNum = 0; handNum < frame.hands().count(); handNum++) {
const Leap::Hand hand = frame.hands()[handNum];
int side = (hand.isRight() ? 1 : -1);
JointTracker* parentJointTracker = _jointsArray.data();
int rootBranchIndex = -1;
Leap::Arm arm = hand.arm();
if (arm.isValid()) {
glm::quat ori = quatFromLeapBase(float(side), arm.basis());
glm::vec3 pos = vec3FromLeapVector(arm.elbowPosition());
JointTracker* elbow = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 2)); // 2 is the index of the elbow joint
elbow->editAbsFrame().setTranslation(pos);
elbow->editAbsFrame().setRotation(ori);
elbow->updateLocFromAbsTransform(parentJointTracker);
elbow->activeFrame();
parentJointTracker = elbow;
pos = vec3FromLeapVector(arm.wristPosition());
JointTracker* wrist = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 1)); // 1 is the index of the wrist joint
wrist->editAbsFrame().setTranslation(pos);
wrist->editAbsFrame().setRotation(ori);
wrist->updateLocFromAbsTransform(parentJointTracker);
wrist->activeFrame();
parentJointTracker = wrist;
}
JointTracker* palmJoint = NULL;
{
glm::vec3 pos = vec3FromLeapVector(hand.palmPosition());
glm::quat ori = quatFromLeapBase(float(side), hand.basis());
palmJoint = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 0)); // 0 is the index of the palm joint
palmJoint->editAbsFrame().setTranslation(pos);
palmJoint->editAbsFrame().setRotation(ori);
palmJoint->updateLocFromAbsTransform(parentJointTracker);
palmJoint->activeFrame();
}
// Check if the hand has any fingers
const Leap::FingerList fingers = hand.fingers();
if (!fingers.isEmpty()) {
// For every fingers in the list
for (int i = 0; i < fingers.count(); ++i) {
// Reset the parent joint to the palmJoint for every finger traversal
parentJointTracker = palmJoint;
// surprisingly, Leap::Finger::Type start at 0 for thumb a until 4 for the pinky
Index fingerIndex = evalJointIndex((side > 0), int(fingers[i].type()), 0);
// let's update the finger's joints
for (int b = 0; b < FINGER_NUM_JOINTS; b++) {
Leap::Bone::Type type = Leap::Bone::Type(b + Leap::Bone::TYPE_METACARPAL);
Leap::Bone bone = fingers[i].bone(type);
JointTracker* ljointTracker = editJointTracker(fingerIndex + b);
if (bone.isValid()) {
Leap::Vector bp = bone.nextJoint();
ljointTracker->editAbsFrame().setTranslation(vec3FromLeapVector(bp));
ljointTracker->editAbsFrame().setRotation(quatFromLeapBase(float(side), bone.basis()));
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
ljointTracker->activeFrame();
}
parentJointTracker = ljointTracker;
}
}
}
}
}
lastFrameID = newFrameID;
#endif
}

View file

@ -0,0 +1,50 @@
//
// Leapmotion.h
// interface/src/devices
//
// Created by Sam Cake on 6/2/2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Leapmotion_h
#define hifi_Leapmotion_h
#include <QDateTime>
#include "MotionTracker.h"
#ifdef HAVE_LEAPMOTION
#include <Leap.h>
#endif
/// Handles interaction with the Leapmotion skeleton tracking suit.
class Leapmotion : public MotionTracker {
public:
static const Name NAME;
static void init();
/// Leapmotion MotionTracker factory
static Leapmotion* getInstance();
bool isActive() const { return _active; }
virtual void update();
protected:
Leapmotion();
virtual ~Leapmotion();
private:
#ifdef HAVE_LEAPMOTION
Leap::Listener _listener;
Leap::Controller _controller;
#endif
bool _active;
};
#endif // hifi_Leapmotion_h

View file

@ -0,0 +1,186 @@
//
// MotionTracker.cpp
// interface/src/devices
//
// Created by Sam Cake on 6/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MotionTracker.h"
// glm::mult(mat43, mat43) just the composition of the 2 matrices assuming they are in fact mat44 with the last raw = { 0, 0, 0, 1 }
namespace glm {
mat4x3 mult(const mat4& lhs, const mat4x3& rhs) {
vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x);
vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y);
vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z);
return mat4x3(
dot(lrx, rhs[0]),
dot(lry, rhs[0]),
dot(lrz, rhs[0]),
dot(lrx, rhs[1]),
dot(lry, rhs[1]),
dot(lrz, rhs[1]),
dot(lrx, rhs[2]),
dot(lry, rhs[2]),
dot(lrz, rhs[2]),
dot(lrx, rhs[3]) + lhs[3].x,
dot(lry, rhs[3]) + lhs[3].y,
dot(lrz, rhs[3]) + lhs[3].z
);
}
mat4x3 mult(const mat4x3& lhs, const mat4x3& rhs) {
vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x);
vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y);
vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z);
return mat4x3(
dot(lrx, rhs[0]),
dot(lry, rhs[0]),
dot(lrz, rhs[0]),
dot(lrx, rhs[1]),
dot(lry, rhs[1]),
dot(lrz, rhs[1]),
dot(lrx, rhs[2]),
dot(lry, rhs[2]),
dot(lrz, rhs[2]),
dot(lrx, rhs[3]) + lhs[3].x,
dot(lry, rhs[3]) + lhs[3].y,
dot(lrz, rhs[3]) + lhs[3].z
);
}
}
// MotionTracker
MotionTracker::MotionTracker() :
DeviceTracker()
{
_jointsArray.resize(1);
_jointsMap.insert(JointTracker::Map::value_type(Semantic("Root"), 0));
}
MotionTracker::~MotionTracker()
{
}
bool MotionTracker::isConnected() const {
return false;
}
MotionTracker::Index MotionTracker::addJoint(const Semantic& semantic, Index parent) {
// Check the parent
if (int(parent) < 0)
return INVALID_PARENT;
// Check that the semantic is not already in use
Index foundIndex = findJointIndex(semantic);
if (foundIndex >= 0)
return INVALID_SEMANTIC;
// All good then allocate the joint
Index newIndex = _jointsArray.size();
_jointsArray.push_back(JointTracker(semantic, parent));
_jointsMap.insert(JointTracker::Map::value_type(semantic, newIndex));
return newIndex;
}
MotionTracker::Index MotionTracker::findJointIndex(const Semantic& semantic) const {
// TODO C++11 auto jointIt = _jointsMap.find(semantic);
JointTracker::Map::const_iterator jointIt = _jointsMap.find(semantic);
if (jointIt != _jointsMap.end())
return (*jointIt).second;
return INVALID_SEMANTIC;
}
void MotionTracker::updateAllAbsTransform() {
_jointsArray[0].updateAbsFromLocTransform(0);
// Because we know the hierarchy is stored from root down the branches let's just traverse and update
for (Index i = 1; i < (Index)(_jointsArray.size()); i++) {
JointTracker* joint = _jointsArray.data() + i;
joint->updateAbsFromLocTransform(_jointsArray.data() + joint->getParent());
}
}
// MotionTracker::JointTracker
MotionTracker::JointTracker::JointTracker() :
_locFrame(),
_absFrame(),
_semantic(""),
_parent(INVALID_PARENT),
_lastUpdate(0)
{
}
MotionTracker::JointTracker::JointTracker(const Semantic& semantic, Index parent) :
_semantic(semantic),
_parent(parent),
_lastUpdate(0)
{
}
MotionTracker::JointTracker::JointTracker(const JointTracker& tracker) :
_locFrame(tracker._locFrame),
_absFrame(tracker._absFrame),
_semantic(tracker._semantic),
_parent(tracker._parent),
_lastUpdate(tracker._lastUpdate)
{
}
void MotionTracker::JointTracker::updateAbsFromLocTransform(const JointTracker* parentJoint) {
if (parentJoint) {
editAbsFrame()._transform = (parentJoint->getAbsFrame()._transform * getLocFrame()._transform);
} else {
editAbsFrame()._transform = getLocFrame()._transform;
}
}
void MotionTracker::JointTracker::updateLocFromAbsTransform(const JointTracker* parentJoint) {
if (parentJoint) {
glm::mat4 ip = glm::inverse(parentJoint->getAbsFrame()._transform);
editLocFrame()._transform = (ip * getAbsFrame()._transform);
} else {
editLocFrame()._transform = getAbsFrame()._transform;
}
}
//--------------------------------------------------------------------------------------
// MotionTracker::Frame
//--------------------------------------------------------------------------------------
MotionTracker::Frame::Frame() :
_transform()
{
}
void MotionTracker::Frame::setRotation(const glm::quat& rotation) {
glm::mat3x3 rot = glm::mat3_cast(rotation);
_transform[0] = glm::vec4(rot[0], 0.f);
_transform[1] = glm::vec4(rot[1], 0.f);
_transform[2] = glm::vec4(rot[2], 0.f);
}
void MotionTracker::Frame::getRotation(glm::quat& rotation) const {
rotation = glm::quat_cast( _transform);
}
void MotionTracker::Frame::setTranslation(const glm::vec3& translation) {
_transform[3] = glm::vec4(translation, 1.f);
}
void MotionTracker::Frame::getTranslation(glm::vec3& translation) const {
translation = glm::vec3(_transform[3]);
}

View file

@ -0,0 +1,121 @@
//
// MotionTracker.h
// interface/src/devices
//
// Created by Sam Cake on 6/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MotionTracker_h
#define hifi_MotionTracker_h
#include "DeviceTracker.h"
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
/// Base class for device trackers.
class MotionTracker : public DeviceTracker {
public:
class Frame {
public:
Frame();
glm::mat4 _transform;
void setRotation(const glm::quat& rotation);
void getRotation(glm::quat& rotatio) const;
void setTranslation(const glm::vec3& translation);
void getTranslation(glm::vec3& translation) const;
};
// Semantic and Index types to retreive the JointTrackers of this MotionTracker
typedef std::string Semantic;
typedef int Index;
static const Index INVALID_SEMANTIC = -1;
static const Index INVALID_PARENT = -2;
class JointTracker {
public:
typedef std::vector< JointTracker > Vector;
typedef std::map< Semantic, Index > Map;
JointTracker();
JointTracker(const JointTracker& tracker);
JointTracker(const Semantic& semantic, Index parent);
const Frame& getLocFrame() const { return _locFrame; }
Frame& editLocFrame() { return _locFrame; }
void setLocFrame(const Frame& frame) { editLocFrame() = frame; }
const Frame& getAbsFrame() const { return _absFrame; }
Frame& editAbsFrame() { return _absFrame; }
void setAbsFrame(const Frame& frame) { editAbsFrame() = frame; }
const Semantic& getSemantic() const { return _semantic; }
const Index& getParent() const { return _parent; }
bool isActive() const { return (_lastUpdate <= 0); }
void tickNewFrame() { _lastUpdate++; }
void activeFrame() { _lastUpdate = 0; }
/// Update the loc/abs transform for this joint from the current abs/loc value and the specified parent joint abs frame
void updateLocFromAbsTransform(const JointTracker* parentJoint);
void updateAbsFromLocTransform(const JointTracker* parentJoint);
protected:
Frame _locFrame;
Frame _absFrame;
Semantic _semantic;
Index _parent;
int _lastUpdate;
};
virtual bool isConnected() const;
Index numJointTrackers() const { return _jointsArray.size(); }
/// Access a Joint from it's index.
/// Index 0 is always the "Root".
/// if the index is Invalid then returns NULL.
const JointTracker* getJointTracker(Index index) const { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); }
JointTracker* editJointTracker(Index index) { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); }
/// From a semantic, find the Index of the Joint.
/// \return the index of the mapped Joint or INVALID_SEMANTIC if the semantic is not knowned.
Index findJointIndex(const Semantic& semantic) const;
protected:
MotionTracker();
virtual ~MotionTracker();
JointTracker::Vector _jointsArray;
JointTracker::Map _jointsMap;
/// Adding joint is only done from the specialized Motion Tracker, hence this function is protected.
/// The hierarchy of joints must be created from the top down to the branches.
/// The "Root" node is at index 0 and exists at creation of the Motion Tracker.
///
/// \param semantic A joint is defined by it's semantic, the unique name mapping to it
/// \param parent The parent's index, the parent must be valid and correspond to a Joint that has been previously created
///
/// \return The Index of the newly created Joint.
/// Valid if everything went well.
/// INVALID_SEMANTIC if the semantic is already in use
/// INVALID_PARENT if the parent is not valid
Index addJoint(const Semantic& semantic, Index parent);
/// Update the absolute transform stack traversing the hierarchy from the root down the branches
/// This is a generic way to update all the Joint's absFrame by combining the locFrame going down the hierarchy branch.
void updateAllAbsTransform();
};
#endif // hifi_MotionTracker_h

View file

@ -13,6 +13,7 @@
#include "Application.h"
#include "devices/SixenseManager.h"
#include "ControllerScriptingInterface.h"
#include "devices/MotionTracker.h"
ControllerScriptingInterface::ControllerScriptingInterface() :
_mouseCaptured(false),
@ -258,3 +259,84 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
QGLWidget* widget = Application::getInstance()->getGLWidget();
return glm::vec2(widget->width(), widget->height());
}
AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retreive the Device Tracker category and then the sub tracker within it
//TODO C++11 auto icIt = _inputControllers.find(0);
InputControllerMap::iterator icIt = _inputControllers.find(0);
if (icIt != _inputControllers.end()) {
return (*icIt).second;
} else {
// Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
if (deviceID < 0) {
deviceID = 0;
}
// TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
// in the near future we need to change that to a real mapping between the devices and the deviceName
// ALso we need to expand the spec so we can fall back on the "default" controller per categories
if (deviceID >= 0) {
// TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
if (motionTracker) {
MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
if (trackerID >= 0) {
AbstractInputController* inputController = new InputController(deviceID, trackerID, this);
_inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController));
return inputController;
}
}
}
return 0;
}
}
void ControllerScriptingInterface::updateInputControllers() {
//TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
(*it).second->update();
}
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
AbstractInputController(),
_deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId)
{
}
void InputController::update() {
_isActive = false;
// TODO for now the InputController is only supporting a JointTracker from a MotionTracker
MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId));
if (motionTracker) {
if (_subTrackerId < motionTracker->numJointTrackers()) {
const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId);
if (joint->isActive()) {
joint->getAbsFrame().getTranslation(_eventCache.absTranslation);
joint->getAbsFrame().getRotation(_eventCache.absRotation);
joint->getLocFrame().getTranslation(_eventCache.locTranslation);
joint->getLocFrame().getRotation(_eventCache.locRotation);
_isActive = true;
emit spatialEvent(_eventCache);
}
}
}
}
const unsigned int INPUTCONTROLLER_KEY_DEVICE_OFFSET = 16;
const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
InputController::Key InputController::getKey() const {
return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
}

View file

@ -17,6 +17,37 @@
#include <AbstractControllerScriptingInterface.h>
class PalmData;
class InputController : public AbstractInputController {
Q_OBJECT
public:
InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL);
virtual void update();
virtual Key getKey() const;
public slots:
virtual bool isActive() const { return _isActive; }
virtual glm::vec3 getAbsTranslation() const { return _eventCache.absTranslation; }
virtual glm::quat getAbsRotation() const { return _eventCache.absRotation; }
virtual glm::vec3 getLocTranslation() const { return _eventCache.locTranslation; }
virtual glm::quat getLocRotation() const { return _eventCache.locRotation; }
private:
int _deviceTrackerId;
int _subTrackerId;
// cache for the spatial
SpatialEvent _eventCache;
bool _isActive;
signals:
};
/// handles scripting of input controller commands from JS
class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
Q_OBJECT
@ -43,6 +74,10 @@ public:
bool isWheelCaptured() const { return _wheelCaptured; }
bool isJoystickCaptured(int joystickIndex) const;
void updateInputControllers();
void releaseInputController(AbstractInputController* input);
public slots:
virtual bool isPrimaryButtonPressed() const;
virtual glm::vec2 getPrimaryJoystickPosition() const;
@ -78,6 +113,9 @@ public slots:
virtual glm::vec2 getViewportDimensions() const;
/// Factory to create an InputController
virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker);
private:
const PalmData* getPrimaryPalm() const;
const PalmData* getPalm(int palmIndex) const;
@ -89,6 +127,9 @@ private:
bool _wheelCaptured;
QMultiMap<int,KeyEvent> _capturedKeys;
QSet<int> _capturedJoysticks;
typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap;
InputControllerMap _inputControllers;
};
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
@ -98,4 +139,5 @@ const int NUMBER_OF_BUTTONS_PER_PALM = 6;
const int PALM_SPATIALCONTROL = 0;
const int TIP_SPATIALCONTROL = 1;
#endif // hifi_ControllerScriptingInterface_h

1
javascripts/main.js Normal file
View file

@ -0,0 +1 @@
console.log('This would be the main JS file.');

17
javascripts/scale.fix.js Normal file
View file

@ -0,0 +1,17 @@
var metas = document.getElementsByTagName('meta');
var i;
if (navigator.userAgent.match(/iPhone/i)) {
for (i=0; i<metas.length; i++) {
if (metas[i].name == "viewport") {
metas[i].content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
}
}
document.addEventListener("gesturestart", gestureStart, false);
}
function gestureStart() {
for (i=0; i<metas.length; i++) {
if (metas[i].name == "viewport") {
metas[i].content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
}
}
}

View file

@ -19,6 +19,28 @@
#include "EventTypes.h"
class AbstractInputController : public QObject {
Q_OBJECT
public:
typedef unsigned int Key;
virtual void update() = 0;
virtual Key getKey() const = 0;
public slots:
virtual bool isActive() const = 0;
virtual glm::vec3 getAbsTranslation() const = 0;
virtual glm::quat getAbsRotation() const = 0;
virtual glm::vec3 getLocTranslation() const = 0;
virtual glm::quat getLocRotation() const = 0;
signals:
void spatialEvent(const SpatialEvent& event);
};
/// handles scripting of input controller commands from JS
class AbstractControllerScriptingInterface : public QObject {
@ -60,6 +82,9 @@ public slots:
virtual glm::vec2 getViewportDimensions() const = 0;
virtual AbstractInputController* createInputController( const QString& category, const QString& tracker ) = 0;
signals:
void keyPressEvent(const KeyEvent& event);
void keyReleaseEvent(const KeyEvent& event);

View file

@ -19,6 +19,7 @@ void registerEventTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue);
qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue);
qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue);
qScriptRegisterMetaType(engine, spatialEventToScriptValue, spatialEventFromScriptValue);
}
KeyEvent::KeyEvent() :
@ -600,3 +601,34 @@ void wheelEventFromScriptValue(const QScriptValue& object, WheelEvent& event) {
}
SpatialEvent::SpatialEvent() :
locTranslation(0.0f),
locRotation(),
absTranslation(0.0f),
absRotation()
{
};
SpatialEvent::SpatialEvent(const SpatialEvent& event) {
locTranslation = event.locTranslation;
locRotation = event.locRotation;
absTranslation = event.absTranslation;
absRotation = event.absRotation;
}
QScriptValue spatialEventToScriptValue(QScriptEngine* engine, const SpatialEvent& event) {
QScriptValue obj = engine->newObject();
obj.setProperty("locTranslation", vec3toScriptValue(engine, event.locTranslation) );
obj.setProperty("locRotation", quatToScriptValue(engine, event.locRotation) );
obj.setProperty("absTranslation", vec3toScriptValue(engine, event.absTranslation) );
obj.setProperty("absRotation", quatToScriptValue(engine, event.absRotation) );
return obj;
}
void spatialEventFromScriptValue(const QScriptValue& object,SpatialEvent& event) {
// nothing for now...
}

View file

@ -13,6 +13,7 @@
#define hifi_EventTypes_h
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QtScript/QScriptEngine>
@ -107,10 +108,25 @@ public:
bool isAlt;
};
class SpatialEvent {
public:
SpatialEvent();
SpatialEvent(const SpatialEvent& other);
glm::vec3 locTranslation;
glm::quat locRotation;
glm::vec3 absTranslation;
glm::quat absRotation;
private:
};
Q_DECLARE_METATYPE(KeyEvent)
Q_DECLARE_METATYPE(MouseEvent)
Q_DECLARE_METATYPE(TouchEvent)
Q_DECLARE_METATYPE(WheelEvent)
Q_DECLARE_METATYPE(SpatialEvent)
void registerEventTypes(QScriptEngine* engine);
@ -126,4 +142,7 @@ void touchEventFromScriptValue(const QScriptValue& object, TouchEvent& event);
QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event);
void wheelEventFromScriptValue(const QScriptValue& object, WheelEvent& event);
QScriptValue spatialEventToScriptValue(QScriptEngine* engine, const SpatialEvent& event);
void spatialEventFromScriptValue(const QScriptValue& object, SpatialEvent& event);
#endif // hifi_EventTypes_h

View file

@ -72,6 +72,14 @@ void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) {
out = qobject_cast<AudioInjector*>(object.toQObject());
}
QScriptValue injectorToScriptValueInputController(QScriptEngine *engine, AbstractInputController* const &in) {
return engine->newQObject(in);
}
void injectorFromScriptValueInputController(const QScriptValue &object, AbstractInputController* &out) {
out = qobject_cast<AbstractInputController*>(object.toQObject());
}
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
AbstractControllerScriptingInterface* controllerScriptingInterface) :
@ -262,6 +270,7 @@ void ScriptEngine::init() {
_engine.globalObject().setProperty("LocalVoxels", localVoxelsValue);
qScriptRegisterMetaType(&_engine, injectorToScriptValue, injectorFromScriptValue);
qScriptRegisterMetaType( &_engine, injectorToScriptValueInputController, injectorFromScriptValueInputController);
qScriptRegisterMetaType(&_engine, animationDetailsToScriptValue, animationDetailsFromScriptValue);

1
params.json Normal file
View file

@ -0,0 +1 @@
{"name":"Hifi","tagline":"Open, decentralized virtual worlds using sensors to control avatars and dynamically assigned devices as servers. San Francisco based startup, we are hiring: http://highfidelity.io/jobs You can also contribute by doing jobs listed at http://worklist.net -","body":"### Avatar Documentation\r\n\r\n* [Exporting your Rigged Avatar from Faceshift](https://github.com/highfidelity/hifi/wiki/Exporting-Your-Rigged-Avatar-From-Faceshift)\r\n* [Creating Blendshapes for your Avatar](https://github.com/highfidelity/hifi/wiki/Creating-Blendshapes-for-your-Avatar)\r\n* [How to Rig a Character for Faceshift](https://github.com/highfidelity/hifi/wiki/How-to-Rig-a-Character-for-Faceshift)\r\n* [Naming your Skeletal Joints](https://github.com/highfidelity/hifi/wiki/Naming-Your-Skeletal-Joints)\r\n* [The FST File](https://github.com/highfidelity/hifi/wiki/The-FST-file)\r\n* [Training in Faceshift](https://github.com/highfidelity/hifi/wiki/Training-in-Faceshift)\r\n* [Uploading your Models](https://github.com/highfidelity/hifi/wiki/Uploading-Your-Models)\r\n\r\n\r\n\r\n\r\n","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."}

226
stylesheets/print.css Normal file
View file

@ -0,0 +1,226 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
body {
font-size: 13px;
line-height: 1.5;
font-family: 'Helvetica Neue', Helvetica, Arial, serif;
color: #000;
}
a {
color: #d5000d;
font-weight: bold;
}
header {
padding-top: 35px;
padding-bottom: 10px;
}
header h1 {
font-weight: bold;
letter-spacing: -1px;
font-size: 48px;
color: #303030;
line-height: 1.2;
}
header h2 {
letter-spacing: -1px;
font-size: 24px;
color: #aaa;
font-weight: normal;
line-height: 1.3;
}
#downloads {
display: none;
}
#main_content {
padding-top: 20px;
}
code, pre {
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal;
color: #222;
margin-bottom: 30px;
font-size: 12px;
}
code {
padding: 0 3px;
}
pre {
border: solid 1px #ddd;
padding: 20px;
overflow: auto;
}
pre code {
padding: 0;
}
ul, ol, dl {
margin-bottom: 20px;
}
/* COMMON STYLES */
table {
width: 100%;
border: 1px solid #ebebeb;
}
th {
font-weight: 500;
}
td {
border: 1px solid #ebebeb;
text-align: center;
font-weight: 300;
}
form {
background: #f2f2f2;
padding: 20px;
}
/* GENERAL ELEMENT TYPE STYLES */
h1 {
font-size: 2.8em;
}
h2 {
font-size: 22px;
font-weight: bold;
color: #303030;
margin-bottom: 8px;
}
h3 {
color: #d5000d;
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
h4 {
font-size: 16px;
color: #303030;
font-weight: bold;
}
h5 {
font-size: 1em;
color: #303030;
}
h6 {
font-size: .8em;
color: #303030;
}
p {
font-weight: 300;
margin-bottom: 20px;
}
a {
text-decoration: none;
}
p a {
font-weight: 400;
}
blockquote {
font-size: 1.6em;
border-left: 10px solid #e9e9e9;
margin-bottom: 20px;
padding: 0 0 0 30px;
}
ul li {
list-style: disc inside;
padding-left: 20px;
}
ol li {
list-style: decimal inside;
padding-left: 3px;
}
dl dd {
font-style: italic;
font-weight: 100;
}
footer {
margin-top: 40px;
padding-top: 20px;
padding-bottom: 30px;
font-size: 13px;
color: #aaa;
}
footer a {
color: #666;
}
/* MISC */
.clearfix:after {
clear: both;
content: '.';
display: block;
visibility: hidden;
height: 0;
}
.clearfix {display: inline-block;}
* html .clearfix {height: 1%;}
.clearfix {display: block;}

View file

@ -0,0 +1,69 @@
.highlight { background: #ffffff; }
.highlight .c { color: #999988; font-style: italic } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { font-weight: bold } /* Keyword */
.highlight .o { font-weight: bold } /* Operator */
.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #999999 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { font-weight: bold } /* Keyword.Constant */
.highlight .kd { font-weight: bold } /* Keyword.Declaration */
.highlight .kn { font-weight: bold } /* Keyword.Namespace */
.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #009999 } /* Literal.Number */
.highlight .s { color: #d14 } /* Literal.String */
.highlight .na { color: #008080 } /* Name.Attribute */
.highlight .nb { color: #0086B3 } /* Name.Builtin */
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
.highlight .no { color: #008080 } /* Name.Constant */
.highlight .ni { color: #800080 } /* Name.Entity */
.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
.highlight .nn { color: #555555 } /* Name.Namespace */
.highlight .nt { color: #000080 } /* Name.Tag */
.highlight .nv { color: #008080 } /* Name.Variable */
.highlight .ow { font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #009999 } /* Literal.Number.Float */
.highlight .mh { color: #009999 } /* Literal.Number.Hex */
.highlight .mi { color: #009999 } /* Literal.Number.Integer */
.highlight .mo { color: #009999 } /* Literal.Number.Oct */
.highlight .sb { color: #d14 } /* Literal.String.Backtick */
.highlight .sc { color: #d14 } /* Literal.String.Char */
.highlight .sd { color: #d14 } /* Literal.String.Doc */
.highlight .s2 { color: #d14 } /* Literal.String.Double */
.highlight .se { color: #d14 } /* Literal.String.Escape */
.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
.highlight .si { color: #d14 } /* Literal.String.Interpol */
.highlight .sx { color: #d14 } /* Literal.String.Other */
.highlight .sr { color: #009926 } /* Literal.String.Regex */
.highlight .s1 { color: #d14 } /* Literal.String.Single */
.highlight .ss { color: #990073 } /* Literal.String.Symbol */
.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #008080 } /* Name.Variable.Class */
.highlight .vg { color: #008080 } /* Name.Variable.Global */
.highlight .vi { color: #008080 } /* Name.Variable.Instance */
.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
.type-csharp .highlight .k { color: #0000FF }
.type-csharp .highlight .kt { color: #0000FF }
.type-csharp .highlight .nf { color: #000000; font-weight: normal }
.type-csharp .highlight .nc { color: #2B91AF }
.type-csharp .highlight .nn { color: #000000 }
.type-csharp .highlight .s { color: #A31515 }
.type-csharp .highlight .sc { color: #A31515 }

255
stylesheets/styles.css Normal file
View file

@ -0,0 +1,255 @@
@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);
body {
padding:50px;
font:14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
color:#777;
font-weight:300;
}
h1, h2, h3, h4, h5, h6 {
color:#222;
margin:0 0 20px;
}
p, ul, ol, table, pre, dl {
margin:0 0 20px;
}
h1, h2, h3 {
line-height:1.1;
}
h1 {
font-size:28px;
}
h2 {
color:#393939;
}
h3, h4, h5, h6 {
color:#494949;
}
a {
color:#39c;
font-weight:400;
text-decoration:none;
}
a small {
font-size:11px;
color:#777;
margin-top:-0.6em;
display:block;
}
.wrapper {
width:860px;
margin:0 auto;
}
blockquote {
border-left:1px solid #e5e5e5;
margin:0;
padding:0 0 0 20px;
font-style:italic;
}
code, pre {
font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
color:#333;
font-size:12px;
}
pre {
padding:8px 15px;
background: #f8f8f8;
border-radius:5px;
border:1px solid #e5e5e5;
overflow-x: auto;
}
table {
width:100%;
border-collapse:collapse;
}
th, td {
text-align:left;
padding:5px 10px;
border-bottom:1px solid #e5e5e5;
}
dt {
color:#444;
font-weight:700;
}
th {
color:#444;
}
img {
max-width:100%;
}
header {
width:270px;
float:left;
position:fixed;
}
header ul {
list-style:none;
height:40px;
padding:0;
background: #eee;
background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
border-radius:5px;
border:1px solid #d2d2d2;
box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;
width:270px;
}
header li {
width:89px;
float:left;
border-right:1px solid #d2d2d2;
height:40px;
}
header ul a {
line-height:1;
font-size:11px;
color:#999;
display:block;
text-align:center;
padding-top:6px;
height:40px;
}
strong {
color:#222;
font-weight:700;
}
header ul li + li {
width:88px;
border-left:1px solid #fff;
}
header ul li + li + li {
border-right:none;
width:89px;
}
header ul a strong {
font-size:14px;
display:block;
color:#222;
}
section {
width:500px;
float:right;
padding-bottom:50px;
}
small {
font-size:11px;
}
hr {
border:0;
background:#e5e5e5;
height:1px;
margin:0 0 20px;
}
footer {
width:270px;
float:left;
position:fixed;
bottom:50px;
}
@media print, screen and (max-width: 960px) {
div.wrapper {
width:auto;
margin:0;
}
header, section, footer {
float:none;
position:static;
width:auto;
}
header {
padding-right:320px;
}
section {
border:1px solid #e5e5e5;
border-width:1px 0;
padding:20px 0;
margin:0 0 20px;
}
header a small {
display:inline;
}
header ul {
position:absolute;
right:50px;
top:52px;
}
}
@media print, screen and (max-width: 720px) {
body {
word-wrap:break-word;
}
header {
padding:0;
}
header ul, header p.view {
position:static;
}
pre, code {
word-wrap:normal;
}
}
@media print, screen and (max-width: 480px) {
body {
padding:15px;
}
header ul {
display:none;
}
}
@media print {
body {
padding:0.4in;
font-size:12pt;
color:#444;
}
}

423
stylesheets/stylesheet.css Normal file
View file

@ -0,0 +1,423 @@
/*******************************************************************************
Slate Theme for GitHub Pages
by Jason Costello, @jsncostello
*******************************************************************************/
@import url(pygment_trac.css);
/*******************************************************************************
MeyerWeb Reset
*******************************************************************************/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
ol, ul {
list-style: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/*******************************************************************************
Theme Styles
*******************************************************************************/
body {
box-sizing: border-box;
color:#373737;
background: #212121;
font-size: 16px;
font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
margin: 10px 0;
font-weight: 700;
color:#222222;
font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif;
letter-spacing: -1px;
}
h1 {
font-size: 36px;
font-weight: 700;
}
h2 {
padding-bottom: 10px;
font-size: 32px;
background: url('../images/bg_hr.png') repeat-x bottom;
}
h3 {
font-size: 24px;
}
h4 {
font-size: 21px;
}
h5 {
font-size: 18px;
}
h6 {
font-size: 16px;
}
p {
margin: 10px 0 15px 0;
}
footer p {
color: #f2f2f2;
}
a {
text-decoration: none;
color: #007edf;
text-shadow: none;
transition: color 0.5s ease;
transition: text-shadow 0.5s ease;
-webkit-transition: color 0.5s ease;
-webkit-transition: text-shadow 0.5s ease;
-moz-transition: color 0.5s ease;
-moz-transition: text-shadow 0.5s ease;
-o-transition: color 0.5s ease;
-o-transition: text-shadow 0.5s ease;
-ms-transition: color 0.5s ease;
-ms-transition: text-shadow 0.5s ease;
}
a:hover, a:focus {text-decoration: underline;}
footer a {
color: #F2F2F2;
text-decoration: underline;
}
em {
font-style: italic;
}
strong {
font-weight: bold;
}
img {
position: relative;
margin: 0 auto;
max-width: 739px;
padding: 5px;
margin: 10px 0 10px 0;
border: 1px solid #ebebeb;
box-shadow: 0 0 5px #ebebeb;
-webkit-box-shadow: 0 0 5px #ebebeb;
-moz-box-shadow: 0 0 5px #ebebeb;
-o-box-shadow: 0 0 5px #ebebeb;
-ms-box-shadow: 0 0 5px #ebebeb;
}
p img {
display: inline;
margin: 0;
padding: 0;
vertical-align: middle;
text-align: center;
border: none;
}
pre, code {
width: 100%;
color: #222;
background-color: #fff;
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
font-size: 14px;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
pre {
width: 100%;
padding: 10px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
overflow: auto;
}
code {
padding: 3px;
margin: 0 3px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
}
pre code {
display: block;
box-shadow: none;
}
blockquote {
color: #666;
margin-bottom: 20px;
padding: 0 0 0 20px;
border-left: 3px solid #bbb;
}
ul, ol, dl {
margin-bottom: 15px
}
ul {
list-style: inside;
padding-left: 20px;
}
ol {
list-style: decimal inside;
padding-left: 20px;
}
dl dt {
font-weight: bold;
}
dl dd {
padding-left: 20px;
font-style: italic;
}
dl p {
padding-left: 20px;
font-style: italic;
}
hr {
height: 1px;
margin-bottom: 5px;
border: none;
background: url('../images/bg_hr.png') repeat-x center;
}
table {
border: 1px solid #373737;
margin-bottom: 20px;
text-align: left;
}
th {
font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
padding: 10px;
background: #373737;
color: #fff;
}
td {
padding: 10px;
border: 1px solid #373737;
}
form {
background: #f2f2f2;
padding: 20px;
}
/*******************************************************************************
Full-Width Styles
*******************************************************************************/
.outer {
width: 100%;
}
.inner {
position: relative;
max-width: 640px;
padding: 20px 10px;
margin: 0 auto;
}
#forkme_banner {
display: block;
position: absolute;
top:0;
right: 10px;
z-index: 10;
padding: 10px 50px 10px 10px;
color: #fff;
background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%;
font-weight: 700;
box-shadow: 0 0 10px rgba(0,0,0,.5);
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
#header_wrap {
background: #212121;
background: -moz-linear-gradient(top, #373737, #212121);
background: -webkit-linear-gradient(top, #373737, #212121);
background: -ms-linear-gradient(top, #373737, #212121);
background: -o-linear-gradient(top, #373737, #212121);
background: linear-gradient(top, #373737, #212121);
}
#header_wrap .inner {
padding: 50px 10px 30px 10px;
}
#project_title {
margin: 0;
color: #fff;
font-size: 42px;
font-weight: 700;
text-shadow: #111 0px 0px 10px;
}
#project_tagline {
color: #fff;
font-size: 24px;
font-weight: 300;
background: none;
text-shadow: #111 0px 0px 10px;
}
#downloads {
position: absolute;
width: 210px;
z-index: 10;
bottom: -40px;
right: 0;
height: 70px;
background: url('../images/icon_download.png') no-repeat 0% 90%;
}
.zip_download_link {
display: block;
float: right;
width: 90px;
height:70px;
text-indent: -5000px;
overflow: hidden;
background: url(../images/sprite_download.png) no-repeat bottom left;
}
.tar_download_link {
display: block;
float: right;
width: 90px;
height:70px;
text-indent: -5000px;
overflow: hidden;
background: url(../images/sprite_download.png) no-repeat bottom right;
margin-left: 10px;
}
.zip_download_link:hover {
background: url(../images/sprite_download.png) no-repeat top left;
}
.tar_download_link:hover {
background: url(../images/sprite_download.png) no-repeat top right;
}
#main_content_wrap {
background: #f2f2f2;
border-top: 1px solid #111;
border-bottom: 1px solid #111;
}
#main_content {
padding-top: 40px;
}
#footer_wrap {
background: #212121;
}
/*******************************************************************************
Small Device Styles
*******************************************************************************/
@media screen and (max-width: 480px) {
body {
font-size:14px;
}
#downloads {
display: none;
}
.inner {
min-width: 320px;
max-width: 480px;
}
#project_title {
font-size: 32px;
}
h1 {
font-size: 28px;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 21px;
}
h4 {
font-size: 18px;
}
h5 {
font-size: 14px;
}
h6 {
font-size: 12px;
}
code, pre {
min-width: 320px;
max-width: 480px;
font-size: 11px;
}
}