Merge branch 'master' of https://github.com/highfidelity/hifi into gpuStreamizing

This commit is contained in:
ZappoMan 2015-01-19 13:34:28 -08:00
commit 41c039f283
115 changed files with 3333 additions and 1055 deletions
BUILD_WIN.md
cmake
domain-server/resources/web/js
examples
interface
libraries

View file

@ -9,8 +9,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
Currently building on Windows has been tested using the following compilers:
* Visual Studio 2013
(If anyone can test using Visual Studio 2013 Express then please update this document)
* Visual Studio 2013 Express
####Visual Studio 2013

View file

@ -42,6 +42,11 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
set(GLPROFILE MAC_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
elseif (UNIX)
set(GLPROFILE LINUX_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
else (APPLE)
set(GLPROFILE PC_GL)

View file

@ -8,11 +8,9 @@
#
macro(INCLUDE_BULLET)
find_package(Bullet)
if (BULLET_FOUND)
include_directories("${BULLET_INCLUDE_DIRS}")
if (APPLE OR UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BULLET_PHYSICS -isystem ${BULLET_INCLUDE_DIRS}")
endif ()
endif (BULLET_FOUND)
find_package(Bullet REQUIRED)
include_directories("${BULLET_INCLUDE_DIRS}")
if (APPLE OR UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${BULLET_INCLUDE_DIRS}")
endif()
endmacro(INCLUDE_BULLET)

View file

@ -0,0 +1,93 @@
# - Try to find the Bullet physics engine
#
# This module defines the following variables
#
# BULLET_FOUND - Was bullet found
# BULLET_INCLUDE_DIRS - the Bullet include directories
# BULLET_LIBRARIES - Link to this, by default it includes
# all bullet components (Dynamics,
# Collision, LinearMath, & SoftBody)
#
# This module accepts the following variables
#
# BULLET_ROOT - Can be set to bullet install path or Windows build path
#
# Modified on 2015.01.15 by Andrew Meadows
# This is an adapted version of the FindBullet.cmake module distributed with Cmake 2.8.12.2
# The original license for that file is displayed below.
#
#=============================================================================
# Copyright 2009 Kitware, Inc.
# Copyright 2009 Philip Lowman <philip@yhbt.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("bullet")
macro(_FIND_BULLET_LIBRARY _var)
find_library(${_var}
NAMES
${ARGN}
HINTS
${BULLET_SEARCH_DIRS}
$ENV{BULLET_ROOT_DIR}
${BULLET_ROOT}
${BULLET_ROOT}/out/release8/libs
${BULLET_ROOT}/out/debug8/libs
PATH_SUFFIXES lib lib/Release lib/Debug
)
mark_as_advanced(${_var})
endmacro()
macro(_BULLET_APPEND_LIBRARIES _list _release)
set(_debug ${_release}_DEBUG)
if(${_debug})
set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}})
else()
set(${_list} ${${_list}} ${${_release}})
endif()
endmacro()
find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h
HINTS
${BULLET_SEARCH_DIRS}/include
$ENV{BULLET_ROOT_DIR}
${BULLET_ROOT}/include
${BULLET_ROOT}/src
PATH_SUFFIXES bullet
)
# Find the libraries
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics)
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_Debug BulletDynamics_d)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_Debug LinearMath_d)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d)
find_package_handle_standard_args(Bullet "Could NOT find Bullet, try to set the path to Bullet root folder in the system variable BULLET_ROOT_DIR"
BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY
BULLET_INCLUDE_DIR
)
set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR})
if(BULLET_FOUND)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY)
endif()

View file

@ -0,0 +1,33 @@
# Try to find the RSSDK library
#
# You must provide a RSSDK_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# RSSDK_FOUND - system found RSSDK
# RSSDK_INCLUDE_DIRS - the RSSDK include directory
# RSSDK_LIBRARIES - Link this to use RSSDK
#
# Created on 12/7/2014 by Thijs Wenker
# Copyright (c) 2014 High Fidelity
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("rssdk")
find_path(RSSDK_INCLUDE_DIRS pxcbase.h PATH_SUFFIXES include HINTS ${RSSDK_SEARCH_DIRS})
if (WIN32)
find_library(RSSDK_LIBRARY_DEBUG libpxc_d PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
find_library(RSSDK_LIBRARY_RELEASE libpxc PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
endif ()
include(SelectLibraryConfigurations)
select_library_configurations(RSSDK)
set(RSSDK_LIBRARIES "${RSSDK_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(RSSDK DEFAULT_MSG RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES)
mark_as_advanced(RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES RSSDK_SEARCH_DIRS)

View file

@ -667,7 +667,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>"
domain_select = $("<select id='domain-name-select' class='form-control'></select>")
_.each(data.data.domains, function(domain){
domain_select.append("<option value='" + domain.id + "'>" + domain.name + "</option>")
domain_select.append("<option value='" + domain.id + "'>(" + domain.id + ")" + (domain.names.length > 0 ? " [" + domain.names + "]" : "") + "</option>");
})
modal_body += "<label for='domain-name-select'>Domains</label>" + domain_select[0].outerHTML
modal_buttons["success"] = {

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// Set the following variables to the right value
var NUM_AC = 3; // This is the number of AC. Their ID need to be unique and between 0 (included) and NUM_AC (excluded)

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// Set the following variables to the values needed
var filename = HIFI_PUBLIC_BUCKET + "ozan/bartender.rec";

View file

@ -20,7 +20,7 @@
//
//For procedural walk animation
Script.include("../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include(HIFI_PUBLIC_BUCKET + "scripts/proceduralAnimationAPI.js");
var procAnimAPI = new ProcAnimAPI();

View file

@ -11,7 +11,7 @@
//
//For procedural walk animation
Script.include("../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include("proceduralAnimationAPI.js");
var procAnimAPI = new ProcAnimAPI();

View file

@ -12,7 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;

View file

@ -12,7 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var clapAnimation = HIFI_PUBLIC_BUCKET + "animations/ClapAnimations/ClapHands_Standing.fbx";
var ANIMATION_FRAMES_PER_CLAP = 10.0;

View file

@ -10,7 +10,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
function length(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);

View file

@ -10,7 +10,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
function length(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);

View file

@ -15,7 +15,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include("../../libraries/toolBars.js");
const LEFT_PALM = 0;

View file

@ -14,7 +14,28 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var RED = { red: 255, green: 0, blue: 0 };
var LASER_WIDTH = 2;
var pointer = [];
pointer.push(Overlays.addOverlay("line3d", {
start: { x: 0, y: 0, z: 0 },
end: { x: 0, y: 0, z: 0 },
color: RED,
alpha: 1,
visible: true,
lineWidth: LASER_WIDTH
}));
pointer.push(Overlays.addOverlay("line3d", {
start: { x: 0, y: 0, z: 0 },
end: { x: 0, y: 0, z: 0 },
color: RED,
alpha: 1,
visible: true,
lineWidth: LASER_WIDTH
}));
function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;
@ -26,7 +47,7 @@ var yawFromMouse = 0;
var pitchFromMouse = 0;
var isMouseDown = false;
var BULLET_VELOCITY = 20.0;
var BULLET_VELOCITY = 5.0;
var MIN_THROWER_DELAY = 1000;
var MAX_THROWER_DELAY = 1000;
var LEFT_BUTTON_3 = 3;
@ -106,8 +127,6 @@ if (showScore) {
});
}
function printVector(string, vector) {
print(string + " " + vector.x + ", " + vector.y + ", " + vector.z);
}
@ -115,7 +134,7 @@ function printVector(string, vector) {
function shootBullet(position, velocity) {
var BULLET_SIZE = 0.07;
var BULLET_LIFETIME = 10.0;
var BULLET_GRAVITY = -0.02;
var BULLET_GRAVITY = 0.0;
bulletID = Entities.addEntity(
{ type: "Sphere",
position: position,
@ -124,6 +143,8 @@ function shootBullet(position, velocity) {
velocity: velocity,
lifetime: BULLET_LIFETIME,
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
damping: 0.01,
density: 5000,
ignoreCollisions: false,
collisionsWillMove: true
});
@ -146,11 +167,11 @@ function shootBullet(position, velocity) {
function shootTarget() {
var TARGET_SIZE = 0.50;
var TARGET_GRAVITY = -0.25;
var TARGET_GRAVITY = 0.0;
var TARGET_LIFETIME = 300.0;
var TARGET_UP_VELOCITY = 0.5;
var TARGET_FWD_VELOCITY = 1.0;
var DISTANCE_TO_LAUNCH_FROM = 3.0;
var TARGET_UP_VELOCITY = 0.0;
var TARGET_FWD_VELOCITY = 0.0;
var DISTANCE_TO_LAUNCH_FROM = 5.0;
var ANGLE_RANGE_FOR_LAUNCH = 20.0;
var camera = Camera.getPosition();
//printVector("camera", camera);
@ -166,13 +187,14 @@ function shootTarget() {
targetID = Entities.addEntity(
{ type: "Box",
position: newPosition,
dimensions: { x: TARGET_SIZE, y: TARGET_SIZE, z: TARGET_SIZE },
color: { red: 0, green: 200, blue: 200 },
//angularVelocity: { x: 1, y: 0, z: 0 },
dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 },
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
velocity: velocity,
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
lifetime: TARGET_LIFETIME,
damping: 0.0001,
rotation: Camera.getOrientation(),
damping: 0.1,
density: 100.0,
collisionsWillMove: true });
// Record start time
@ -183,8 +205,6 @@ function shootTarget() {
Audio.playSound(targetLaunchSound, audioOptions);
}
function entityCollisionWithEntity(entity1, entity2, collision) {
if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) &&
@ -212,7 +232,7 @@ function keyPressEvent(event) {
if (event.text == "t") {
var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
Script.setTimeout(shootTarget, time);
} else if (event.text == ".") {
} else if ((event.text == ".") || (event.text == "SPACE")) {
shootFromMouse();
} else if (event.text == "r") {
playLoadSound();
@ -254,7 +274,7 @@ function takeFiringPose() {
}
}
//MyAvatar.attach(gunModel, "RightHand", {x:0.02, y: 0.11, z: 0.04}, Quat.fromPitchYawRollDegrees(-0, -160, -79), 0.20);
MyAvatar.attach(gunModel, "RightHand", {x:0.02, y: 0.11, z: 0.04}, Quat.fromPitchYawRollDegrees(-0, -160, -79), 0.20);
MyAvatar.attach(gunModel, "LeftHand", {x:-0.02, y: 0.11, z: 0.04}, Quat.fromPitchYawRollDegrees(0, 0, 79), 0.20);
// Give a bit of time to load before playing sound
@ -262,7 +282,6 @@ Script.setTimeout(playLoadSound, 2000);
function update(deltaTime) {
if (bulletID && !bulletID.isKnownID) {
print("Trying to identify bullet");
bulletID = Entities.identifyEntity(bulletID);
}
if (targetID && !targetID.isKnownID) {
@ -304,15 +323,6 @@ function update(deltaTime) {
}
}
// Check hydra controller for launch button press
if (!isLaunchButtonPressed && Controller.isButtonPressed(LEFT_BUTTON_3)) {
isLaunchButtonPressed = true;
var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
Script.setTimeout(shootTarget, time);
} else if (isLaunchButtonPressed && !Controller.isButtonPressed(LEFT_BUTTON_3)) {
isLaunchButtonPressed = false;
}
// check for trigger press
@ -335,14 +345,21 @@ function update(deltaTime) {
shootABullet = true;
}
}
var palmController = t * controllersPerTrigger;
var palmPosition = Controller.getSpatialControlPosition(palmController);
var fingerTipController = palmController + 1;
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition);
// Update Lasers
Overlays.editOverlay(pointer[t], {
start: palmPosition,
end: laserTip,
alpha: 1
});
if (shootABullet) {
var palmController = t * controllersPerTrigger;
var palmPosition = Controller.getSpatialControlPosition(palmController);
var fingerTipController = palmController + 1;
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
var palmToFingerTipVector =
{ x: (fingerTipPosition.x - palmPosition.x),
y: (fingerTipPosition.y - palmPosition.y),
@ -361,20 +378,8 @@ function update(deltaTime) {
}
}
function mousePressEvent(event) {
isMouseDown = true;
lastX = event.x;
lastY = event.y;
if (Overlays.getOverlayAtPoint({ x: event.x, y: event.y }) === offButton) {
Script.stop();
} else {
shootFromMouse();
}
}
function shootFromMouse() {
var DISTANCE_FROM_CAMERA = 2.0;
var DISTANCE_FROM_CAMERA = 1.0;
var camera = Camera.getPosition();
var forwardVector = Quat.getFront(Camera.getOrientation());
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
@ -402,6 +407,8 @@ function mouseMoveEvent(event) {
function scriptEnding() {
Overlays.deleteOverlay(reticle);
Overlays.deleteOverlay(offButton);
Overlays.deleteOverlay(pointer[0]);
Overlays.deleteOverlay(pointer[1]);
Overlays.deleteOverlay(text);
MyAvatar.detachOne(gunModel);
clearPose();
@ -410,7 +417,6 @@ function scriptEnding() {
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.keyPressEvent.connect(keyPressEvent);

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var rightHandAnimation = HIFI_PUBLIC_BUCKET + "animations/RightHandAnimPhilip.fbx";
var leftHandAnimation = HIFI_PUBLIC_BUCKET + "animations/LeftHandAnimPhilip.fbx";

View file

@ -15,7 +15,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// maybe we should make these constants...
var LEFT_PALM = 0;

View file

@ -15,7 +15,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
const KBD_UPPERCASE_DEFAULT = 0;
const KBD_LOWERCASE_DEFAULT = 1;

View file

@ -11,7 +11,7 @@
Script.load("lookWithTouch.js");
Script.load("editEntities.js");
Script.load("selectAudioDevice.js");
Script.load("hydraMove.js");
Script.load("controllers/hydra/hydraMove.js");
Script.load("headMove.js");
Script.load("inspect.js");
Script.load("lobby.js");

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include("libraries/stringHelpers.js");
Script.include("libraries/dataviewHelpers.js");
Script.include("libraries/httpMultiPart.js");
@ -527,8 +527,15 @@ function mousePressEvent(event) {
var highlightedEntityID = { isKnownID: false };
var mouseCapturedByTool = false;
var lastMousePosition = null;
var idleMouseTimerId = null;
var IDLE_MOUSE_TIMEOUT = 200;
function mouseMoveEvent(event) {
if (idleMouseTimerId) {
Script.clearTimeout(idleMouseTimerId);
}
mouseHasMovedSincePress = true;
if (isActive) {
// allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing
@ -536,36 +543,48 @@ function mouseMoveEvent(event) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var entityIntersection = Entities.findRayIntersection(pickRay);
if (entityIntersection.accurate) {
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
selectionDisplay.unhighlightSelectable(highlightedEntityID);
highlightedEntityID = { id: -1, isKnownID: false };
}
lastMousePosition = { x: event.x, y: event.y };
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
entityIntersection.properties.position)) * 180 / 3.14;
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
if (entityIntersection.entityID.isKnownID && sizeOK) {
if (wantEntityGlow) {
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
}
highlightedEntityID = entityIntersection.entityID;
selectionDisplay.highlightSelectable(entityIntersection.entityID);
}
}
highlightEntityUnderCursor(lastMousePosition, false);
idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT);
} else {
cameraManager.mouseMoveEvent(event);
}
}
function handleIdleMouse() {
idleMouseTimerId = null;
highlightEntityUnderCursor(lastMousePosition, true);
}
function highlightEntityUnderCursor(position, accurateRay) {
var pickRay = Camera.computePickRay(position.x, position.y);
var entityIntersection = Entities.findRayIntersection(pickRay, accurateRay);
if (entityIntersection.accurate) {
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
selectionDisplay.unhighlightSelectable(highlightedEntityID);
highlightedEntityID = { id: -1, isKnownID: false };
}
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
entityIntersection.properties.position)) * 180 / 3.14;
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
if (entityIntersection.entityID.isKnownID && sizeOK) {
if (wantEntityGlow) {
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
}
highlightedEntityID = entityIntersection.entityID;
selectionDisplay.highlightSelectable(entityIntersection.entityID);
}
}
}
function mouseReleaseEvent(event) {
if (isActive && selectionManager.hasSelection()) {

View file

@ -13,7 +13,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/mexicanWhipoorwill.raw");
var CHANCE_OF_PLAYING_SOUND = 0.01;

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var modelURL = HIFI_PUBLIC_BUCKET + "models/entities/radio/Speakers.fbx";
var soundURL = HIFI_PUBLIC_BUCKET + "sounds/family.stereo.raw";

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var count = 0;
var moveUntil = 2000;

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var count = 0;
var stopAfter = 1000;

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var iteration = 0;

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// The "Swatches" example of this script will create 9 different image overlays, that use the color feature to
// display different colors as color swatches. The overlays can be clicked on, to change the "selectedSwatch" variable

View file

@ -181,10 +181,10 @@
elLocked.checked = properties.locked;
if (properties.locked) {
disableChildren(document.getElementById("properties-table"), 'input');
disableChildren(document.getElementById("properties-list"), 'input');
elLocked.removeAttribute('disabled');
} else {
enableChildren(document.getElementById("properties-table"), 'input');
enableChildren(document.getElementById("properties-list"), 'input');
}
elVisible.checked = properties.visible;
@ -231,7 +231,7 @@
}
} else {
for (var i = 0; i < elBoxSections.length; i++) {
elBoxSections[i].style.display = 'table-row';
elBoxSections[i].style.display = 'block';
}
elBoxColorRed.value = properties.color.red;
@ -245,7 +245,7 @@
}
} else {
for (var i = 0; i < elModelSections.length; i++) {
elModelSections[i].style.display = 'table-row';
elModelSections[i].style.display = 'block';
}
elModelURL.value = properties.modelURL;
@ -264,7 +264,7 @@
}
} else {
for (var i = 0; i < elTextSections.length; i++) {
elTextSections[i].style.display = 'table-row';
elTextSections[i].style.display = 'block';
}
elTextText.value = properties.text;
@ -283,7 +283,7 @@
}
} else {
for (var i = 0; i < elLightSections.length; i++) {
elLightSections[i].style.display = 'table-row';
elLightSections[i].style.display = 'block';
}
elLightDiffuseRed.value = properties.diffuseColor.red;
@ -442,117 +442,60 @@
percentage: parseInt(elRescaleDimensionsPct.value),
}));
});
var resizing = false;
var startX = 0;
var originalWidth = 0;
var resizeHandleWidth = 10;
var col1 = document.querySelector("#col-label");
document.body.addEventListener('mousemove', function(event) {
if (resizing) {
var dX = event.x - startX;
col1.style.width = (originalWidth + dX) + "px";
}
});
document.body.addEventListener('mouseup', function(event) {
resizing = false;
});
document.body.addEventListener('mouseleave', function(event) {
resizing = false;
});
var els = document.querySelectorAll("#properties-table td");
for (var i = 0; i < els.length; i++) {
var el = els[i];
el.addEventListener('mousemove', function(event) {
if (!resizing) {
var distance = this.offsetWidth - event.offsetX;
if (distance < resizeHandleWidth) {
document.body.style.cursor = "ew-resize";
} else {
document.body.style.cursor = "initial";
}
}
});
el.addEventListener('mousedown', function(event) {
var distance = this.offsetWidth - event.offsetX;
if (distance < resizeHandleWidth) {
startX = event.x;
originalWidth = this.offsetWidth;
resizing = true;
target = this;
}
});
}
}
</script>
</head>
<body onload='loaded();'>
<div class="section-header">
<label>Entity Properties</label>
</div>
<table id="properties-table">
<colgroup>
<col id="col-label">
<col>
</colgroup>
<tr>
<td class="label">
ID
</td>
<td>
<body class="properties" onload='loaded();'>
<div id="properties-list">
<div id="type" class="property">
<div class="label">
<label>Type: </label><span id="property-type"></span>
</div>
</div>
<div class="property">
<div class="value">
<label id="property-id" class="selectable"></label>
</td>
</tr>
<tr>
<td class="label">
Type
</td>
<td>
<label id="property-type"></label>
</td>
</tr>
<tr>
<td class="label">Locked</td>
<td>
</div>
</div>
<div class="property">
<div class="label">Locked</div>
<div class="value">
<input type='checkbox' id="property-locked">
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Visible</td>
<td>
<div class="property">
<div class="label">Visible</div>
<div class="value">
<input type='checkbox' id="property-visible">
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Position</td>
<td>
<div class="input-area">X <input class="coord" type='number' id="property-pos-x"></input></div>
<div class="input-area">Y <input class="coord" type='number' id="property-pos-y"></input></div>
<div class="input-area">Z <input class="coord" type='number' id="property-pos-z"></input></div>
<div class="property">
<div class="label">Position</div>
<div class="value">
<div class="input-area">X <br><input class="coord" type='number' id="property-pos-x"></input></div>
<div class="input-area">Y <br><input class="coord" type='number' id="property-pos-y"></input></div>
<div class="input-area">Z <br><input class="coord" type='number' id="property-pos-z"></input></div>
<div>
<input type="button" id="move-selection-to-grid" value="Selection to Grid">
<input type="button" id="move-all-to-grid" value="All to Grid">
</div>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Registration</td>
<td>
<div class="property">
<div class="label">Registration</div>
<div class="value">
<div class="input-area">X <input class="coord" type='number' id="property-reg-x"></input></div>
<div class="input-area">Y <input class="coord" type='number' id="property-reg-y"></input></div>
<div class="input-area">Z <input class="coord" type='number' id="property-reg-z"></input></div>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Dimensions</td>
<td>
<div class="property">
<div class="label">Dimensions</div>
<div class="value">
<div class="input-area">X <input class="coord" type='number' id="property-dim-x"></input></div>
<div class="input-area">Y <input class="coord" type='number' id="property-dim-y"></input></div>
<div class="input-area">Z <input class="coord" type='number' id="property-dim-z"></input></div>
@ -560,248 +503,255 @@
<input type="button" id="reset-to-natural-dimensions" value="Reset to Natural Dimensions">
</div>
<div class="input-area">
<input class="coord" type='number' id="dimension-rescale-pct" value=100></input>%
<input class="" type='number' id="dimension-rescale-pct" value=100></input>%
</div>
<span>
<input type="button" id="dimension-rescale-button" value="Rescale"></input>
</span>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Rotation</td>
<td>
<div class="property">
<div class="label">Rotation</div>
<div class="value">
<div class="input-area">Pitch <input class="coord" type='number' id="property-rot-x"></input></div>
<div class="input-area">Yaw <input class="coord" type='number' id="property-rot-y"></input></div>
<div class="input-area">Roll <input class="coord" type='number' id="property-rot-z"></input></div>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Linear Velocity</td>
<td>
<div class="property">
<div class="label">Linear Velocity</div>
<div class="value">
<div class="input-area">X <input class="coord" type='number' id="property-lvel-x"></input></div>
<div class="input-area">Y <input class="coord" type='number' id="property-lvel-y"></input></div>
<div class="input-area">Z <input class="coord" type='number' id="property-lvel-z"></input></div>
</td>
</tr>
<tr>
<td class="label">Linear Damping</td>
<td>
</div>
</div>
<div class="property">
<div class="label">Linear Damping</div>
<div class="value">
<input class="coord" type='number' id="property-ldamping"></input>
</td>
</tr>
<tr>
<td class="label">Angular Velocity</td>
<td>
</div>
</div>
<div class="property">
<div class="label">Angular Velocity</div>
<div class="value">
<div class="input-area">Pitch <input class="coord" type='number' id="property-avel-x"></input></div>
<div class="input-area">Yaw <input class="coord" type='number' id="property-avel-y"></input></div>
<div class="input-area">Roll <input class="coord" type='number' id="property-avel-z"></input></div>
</td>
</tr>
<tr>
<td class="label">Angular Damping</td>
<td>
</div>
</div>
<div class="property">
<div class="label">Angular Damping</div>
<div class="value">
<input class="coord" type='number' id="property-adamping"></input>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Gravity</td>
<td>
<div class="property">
<div class="label">Gravity</div>
<div class="value">
<div class="input-area">X <input class="coord" type='number' id="property-grav-x"></input></div>
<div class="input-area">Y <input class="coord" type='number' id="property-grav-y"></input></div>
<div class="input-area">Z <input class="coord" type='number' id="property-grav-z"></input></div>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Density</td>
<td>
<div class="property">
<div class="label">Mass</div>
<div class="value">
<input type='number' id="property-mass"></input>
</div>
</div>
<div>
<div class="label">Density</div>
<div>
<input type='number' id="property-density"></input>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Ignore For Collisions</td>
<td>
<div class="property">
<div class="label">Ignore For Collisions</div>
<div class="value">
<input type='checkbox' id="property-ignore-for-collisions"></input>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Collisions Will Move</td>
<td>
<div class="property">
<div class="label">Collisions Will Move</div>
<div class="value">
<input type='checkbox' id="property-collisions-will-move"></input>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Lifetime</td>
<td>
<div class="property">
<div class="label">Lifetime</div>
<div class="value">
<input type='number' id="property-lifetime"></input>
</td>
</tr>
</div>
</div>
<tr>
<td class="label">Script URL</td>
<td>
<div class="property">
<div class="label">Script URL</div>
<div class="value">
<input id="property-script-url" class="url"></input>
</td>
</tr>
</div>
</div>
<tr class="box-section">
<td class="label">Color</td>
<td>
<div class="box-section property">
<div class="label">Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-box-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-box-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-box-blue"></input></div>
</td>
</tr>
</div>
</div>
<tr class="model-section">
<td class="label">Model URL</td>
<td>
<div class="model-section property">
<div class="label">Model URL</div>
<div class="value">
<input type="text" id="property-model-url" class="url"></input>
</td>
</tr>
<tr class="model-section">
<td class="label">Animation URL</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Animation URL</div>
<div class="value">
<input type="text" id="property-model-animation-url" class="url"></input>
</td>
</tr>
<tr class="model-section">
<td class="label">Animation Playing</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Animation Playing</div>
<div class="value">
<input type='checkbox' id="property-model-animation-playing">
</td>
</tr>
<tr class="model-section">
<td class="label">Animation FPS</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Animation FPS</div>
<div class="value">
<input class="coord" type='number' id="property-model-animation-fps"></input>
</td>
</tr>
<tr class="model-section">
<td class="label">Animation Frame</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Animation Frame</div>
<div class="value">
<input class="coord" type='number' id="property-model-animation-frame"></input>
</td>
</tr>
<tr class="model-section">
<td class="label">Animation Settings</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Animation Settings</div>
<div class="value">
<textarea id="property-model-animation-settings" value='asdfasdf'></textarea>
</td>
</tr>
<tr class="model-section">
<td class="label">Textures</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Textures</div>
<div class="value">
<textarea id="property-model-textures" value='asdfasdf'></textarea>
</td>
</tr>
<tr class="model-section">
<td class="label">Original Textures</td>
<td>
</div>
</div>
<div class="model-section property">
<div class="label">Original Textures</div>
<div class="value">
<textarea id="property-model-original-textures" readonly value='asdfasdf'></textarea>
</td>
</tr>
</div>
</div>
<tr class="text-section">
<td class="label">Text</td>
<td>
<div class="text-section property">
<div class="label">Text</div>
<div class="value">
<input type="text" id="property-text-text"></input>
</td>
</tr>
<tr class="text-section">
<td class="label">Line Height</td>
<td>
</div>
</div>
<div class="text-section property">
<div class="label">Line Height</div>
<div class="value">
<input class="coord" type='number' id="property-text-line-height"></input>
</td>
</tr>
<tr class="text-section">
<td class="label">Text Color</td>
<td>
</div>
</div>
<div class="text-section property">
<div class="label">Text Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-text-text-color-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-text-text-color-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-text-text-color-blue"></input></div>
</td>
</tr>
<tr class="text-section">
<td class="label">Background Color</td>
<td>
</div>
</div>
<div class="text-section property">
<div class="label">Background Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-text-background-color-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-text-background-color-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-text-background-color-blue"></input></div>
</td>
</tr>
</div>
</div>
<tr class="light-section">
<td class="label">Spot Light</td>
<td>
<div class="light-section property">
<div class="label">Spot Light</div>
<div class="value">
<input type='checkbox' id="property-light-spot-light">
</td>
</tr>
<tr class="light-section">
<td class="label">Diffuse</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Diffuse</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-light-diffuse-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-diffuse-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-diffuse-blue"></input></div>
</td>
</tr>
<tr class="light-section">
<td class="label">Ambient</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Ambient</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-light-ambient-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-ambient-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-ambient-blue"></input></div>
</td>
</tr>
<tr class="light-section">
<td class="label">Specular</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Specular</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-light-specular-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-specular-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-specular-blue"></input></div>
</td>
</tr>
<tr class="light-section">
<td class="label">Constant Attenuation</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Constant Attenuation</div>
<div class="value">
<input class="coord" type='number' id="property-light-constant-attenuation"></input>
</td>
</tr>
<tr class="light-section">
<td class="label">Linear Attenuation</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Linear Attenuation</div>
<div class="value">
<input class="coord" type='number' id="property-light-linear-attenuation"></input>
</td>
</tr>
<tr class="light-section">
<td class="label">Quadratic Attenuation</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Quadratic Attenuation</div>
<div class="value">
<input class="coord" type='number' id="property-light-quadratic-attenuation"></input>
</td>
</tr>
<tr class="light-section">
<td class="label">Exponent</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Exponent</div>
<div class="value">
<input class="coord" type='number' id="property-light-exponent"></input>
</td>
</tr>
<tr class="light-section">
<td class="label">Cutoff (degrees)</td>
<td>
</div>
</div>
<div class="light-section property">
<div class="label">Cutoff (degrees)</div>
<div class="value">
<input class="coord" type='number' id="property-light-cutoff"></input>
</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>

View file

@ -6,8 +6,8 @@ body {
padding: 0;
background-color: #efefef;
font-family: Sans-Serif;
font-size: 12px;
font-family: Arial;
font-size: 11.5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
@ -17,6 +17,12 @@ body {
user-select: none;
}
body.properties {
background-color: rgb(76, 76, 76);
color: rgb(204, 204, 204);
font-size: 11px;
}
.selectable {
-webkit-touch-callout: text;
-webkit-user-select: text;
@ -85,12 +91,10 @@ input[type=button] {
border: 0;
color: #fff;
font-weight: bold;
margin: 0 2px;
margin-top: 5px;
font-size: .9em;
}
textarea, input {
margin: 0;
padding: 2px;
border: 1px solid #999;
background-color: #eee;
@ -105,10 +109,15 @@ input.url {
width: 100%;
}
input.coord {
width: 7em;
display: block;
}
table#entity-table {
border-collapse: collapse;
font-family: Sans-Serif;
font-size: 12px;
/* font-size: 12px; */
width: 100%;
}
@ -145,31 +154,62 @@ th#entity-url {
div.input-area {
display: inline-block;
font-size: 10px;
}
input {
}
#type {
font-size: 14px;
}
#type label {
color: rgb(150, 150, 150);
}
#properties-list input, #properties-list textarea {
background-color: rgb(102, 102, 102);
color: rgb(204, 204, 204);
border: none;
font-size: 10px;
}
#properties-list input[type=button] {
cursor: pointer;
background-color: rgb(51, 102, 102);
border-color: #608e96;
border-radius: 5px;
padding: 5px 10px;
border: 0;
color: rgb(204, 204, 204);
}
#properties-list .property {
padding: 8px 8px;
border-top: 1px solid rgb(63, 63, 63);
min-height: 1em;
}
table#properties-table {
table#properties-list {
border: none;
border-collapse: collapse;
width: 100%;
background-color: #efefef;
font-family: Arial;
font-size: 12px;
table-layout: fixed;
}
#properties-table tr {
#properties-list > div {
margin: 4px 0;
}
#properties-list {
border-bottom: 1px solid #e5e5e5;
}
#properties-table td.label {
padding-right: 10px;
border-right: 1px solid #999;
text-align: right;
#properties-list .label {
font-weight: bold;
white-space: nowrap;
overflow: hidden;
@ -179,8 +219,8 @@ table#properties-table {
height: 1.2em;
}
#properties-table td {
padding: 5px;
#properties-list .value > div{
padding: 4px 0;
}
col#col-label {

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
SPACE_LOCAL = "local";
SPACE_WORLD = "world";

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var panelWall = false;
var orbShell = false;
@ -66,20 +66,20 @@ function textOverlayPosition() {
Vec3.multiply(Quat.getUp(Camera.orientation), TEXT_DISTANCE_DOWN));
}
var panelLocationOrder = [
var panelPlaceOrder = [
7, 8, 9, 10, 11, 12, 13,
0, 1, 2, 3, 4, 5, 6,
14, 15, 16, 17, 18, 19, 20
];
// Location index is 0-based
function locationIndexToPanelIndex(locationIndex) {
return panelLocationOrder.indexOf(locationIndex) + 1;
// place index is 0-based
function placeIndexToPanelIndex(placeIndex) {
return panelPlaceOrder.indexOf(placeIndex) + 1;
}
// Panel index is 1-based
function panelIndexToLocationIndex(panelIndex) {
return panelLocationOrder[panelIndex - 1];
function panelIndexToPlaceIndex(panelIndex) {
return panelPlaceOrder[panelIndex - 1];
}
var MAX_NUM_PANELS = 21;
@ -148,25 +148,24 @@ function drawLobby() {
}
}
var locations = {};
var places = {};
function changeLobbyTextures() {
var req = new XMLHttpRequest();
req.open("GET", "https://data.highfidelity.io/api/v1/locations?limit=21", false);
req.open("GET", "https://data.highfidelity.io/api/v1/places?limit=21", false);
req.send();
locations = JSON.parse(req.responseText).data.locations;
places = JSON.parse(req.responseText).data.places;
var NUM_PANELS = locations.length;
var NUM_PANELS = places.length;
var textureProp = {
textures: {}
};
for (var j = 0; j < NUM_PANELS; j++) {
var panelIndex = locationIndexToPanelIndex(j);
textureProp["textures"]["file" + panelIndex] = HIFI_PUBLIC_BUCKET + "images/locations/"
+ locations[j].id + "/hifi-location-" + locations[j].id + "_640x360.jpg";
var panelIndex = placeIndexToPanelIndex(j);
textureProp["textures"]["file" + panelIndex] = places[j].previews.lobby;
};
Overlays.editOverlay(panelWall, textureProp);
@ -234,7 +233,7 @@ function cleanupLobby() {
Audio.stopInjector(currentMuzakInjector);
currentMuzakInjector = null;
locations = {};
places = {};
toggleEnvironmentRendering(true);
MyAvatar.detachOne(HELMET_ATTACHMENT_URL);
@ -252,14 +251,14 @@ function actionStartEvent(event) {
var panelStringIndex = panelName.indexOf("Panel");
if (panelStringIndex != -1) {
var panelIndex = parseInt(panelName.slice(5));
var locationIndex = panelIndexToLocationIndex(panelIndex);
if (locationIndex < locations.length) {
var actionLocation = locations[locationIndex];
var placeIndex = panelIndexToPlaceIndex(panelIndex);
if (placeIndex < places.length) {
var actionPlace = places[placeIndex];
print("Jumping to " + actionLocation.name + " at " + actionLocation.path
+ " in " + actionLocation.domain.name + " after click on panel " + panelIndex + " with location index " + locationIndex);
print("Jumping to " + actionPlace.name + " at " + actionPlace.address
+ " after click on panel " + panelIndex + " with place index " + placeIndex);
Window.location = actionLocation;
Window.location = actionPlace.address;
maybeCleanupLobby();
}
}
@ -302,15 +301,15 @@ function handleLookAt(pickRay) {
var panelStringIndex = panelName.indexOf("Panel");
if (panelStringIndex != -1) {
var panelIndex = parseInt(panelName.slice(5));
var locationIndex = panelIndexToLocationIndex(panelIndex);
if (locationIndex < locations.length) {
var actionLocation = locations[locationIndex];
var placeIndex = panelIndexToPlaceIndex(panelIndex);
if (placeIndex < places.length) {
var actionPlace = places[placeIndex];
if (actionLocation.description == "") {
Overlays.editOverlay(descriptionText, { text: actionLocation.name, visible: showText });
if (actionPlace.description == "") {
Overlays.editOverlay(descriptionText, { text: actionPlace.name, visible: showText });
} else {
// handle line wrapping
var allWords = actionLocation.description.split(" ");
var allWords = actionPlace.description.split(" ");
var currentGoodLine = "";
var currentTestLine = "";
var formatedDescription = "";

View file

@ -259,14 +259,16 @@ function checkSize(){
// Triggers notification if a user logs on or off
function onOnlineUsersChanged(users) {
for (user in users) {
if (last_users.indexOf(users[user]) == -1.0) {
createNotification(users[user] + " has joined");
if (!isStartingUp()) { // Skip user notifications at startup.
for (user in users) {
if (last_users.indexOf(users[user]) == -1.0) {
createNotification(users[user] + " has joined");
}
}
}
for (user in last_users) {
if (users.indexOf(last_users[user]) == -1.0) {
createNotification(last_users[user] + " has left");
for (user in last_users) {
if (users.indexOf(last_users[user]) == -1.0) {
createNotification(last_users[user] + " has left");
}
}
}
last_users = users;
@ -348,17 +350,38 @@ function dismiss(firstNoteOut, firstButOut, firstOut) {
myAlpha.splice(firstOut,1);
}
// This is meant to show users online at startup but currently shows 0 users.
function onConnected() {
// This reports the number of users online at startup
function reportUsers() {
var numUsers = GlobalServices.onlineUsers.length;
var welcome = "Welcome! There are " + numUsers + " users online now.";
createNotification(welcome);
}
var STARTUP_TIMEOUT = 500, // ms
startingUp = true,
startupTimer = null;
function finishStartup() {
startingUp = false;
Script.clearTimeout(startupTimer);
reportUsers();
}
function isStartingUp() {
// Is starting up until get no checks that it is starting up for STARTUP_TIMEOUT
if (startingUp) {
if (startupTimer) {
Script.clearTimeout(startupTimer);
}
startupTimer = Script.setTimeout(finishStartup, STARTUP_TIMEOUT);
}
return startingUp;
}
AudioDevice.muteToggled.connect(onMuteStateChanged);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.mousePressEvent.connect(mousePressEvent);
GlobalServices.connected.connect(onConnected);
GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged);
GlobalServices.incomingMessage.connect(onIncomingMessage);
Controller.keyReleaseEvent.connect(keyReleaseEvent);

525
examples/realsenseHands.js Normal file
View file

@ -0,0 +1,525 @@
//
// realsenseHands.js
// examples
//
// Created by Thijs Wenker on 18 Dec 2014.
// Copyright 2014 High Fidelity, Inc.
//
// This is an example script that uses the Intel RealSense to make the avatar's hands replicate the user's hand actions.
// Most of this script is copied from leapHands.js
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var leapHands = (function () {
var isOnHMD,
MotionTracker = "RealSense",
LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD",
LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip
HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
hasHandAndWristJoints,
handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position
HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle
hands,
wrists,
NUM_HANDS = 2, // 0 = left; 1 = right
fingers,
NUM_FINGERS = 5, // 0 = thumb; ...; 4 = pinky
THUMB = 0,
MIDDLE_FINGER = 2,
NUM_FINGER_JOINTS = 3, // 0 = metacarpal(hand)-proximal(finger) joint; ...; 2 = intermediate-distal joint
MAX_HAND_INACTIVE_COUNT = 20,
calibrationStatus,
UNCALIBRATED = 0,
CALIBRATING = 1,
CALIBRATED = 2,
CALIBRATION_TIME = 1000, // milliseconds
avatarScale,
avatarFaceModelURL,
avatarSkeletonModelURL,
settingsTimer;
function printSkeletonJointNames() {
var jointNames,
i;
print(MyAvatar.skeletonModelURL);
print("Skeleton joint names ...");
jointNames = MyAvatar.getJointNames();
for (i = 0; i < jointNames.length; i += 1) {
print(i + ": " + jointNames[i]);
}
print("... skeleton joint names");
/*
http://public.highfidelity.io/models/skeletons/ron_standing.fst
Skeleton joint names ...
0: Hips
1: RightUpLeg
2: RightLeg
3: RightFoot
4: RightToeBase
5: RightToe_End
6: LeftUpLeg
7: LeftLeg
8: LeftFoot
9: LeftToeBase
10: LeftToe_End
11: Spine
12: Spine1
13: Spine2
14: RightShoulder
15: RightArm
16: RightForeArm
17: RightHand
18: RightHandPinky1
19: RightHandPinky2
20: RightHandPinky3
21: RightHandPinky4
22: RightHandRing1
23: RightHandRing2
24: RightHandRing3
25: RightHandRing4
26: RightHandMiddle1
27: RightHandMiddle2
28: RightHandMiddle3
29: RightHandMiddle4
30: RightHandIndex1
31: RightHandIndex2
32: RightHandIndex3
33: RightHandIndex4
34: RightHandThumb1
35: RightHandThumb2
36: RightHandThumb3
37: RightHandThumb4
38: LeftShoulder
39: LeftArm
40: LeftForeArm
41: LeftHand
42: LeftHandPinky1
43: LeftHandPinky2
44: LeftHandPinky3
45: LeftHandPinky4
46: LeftHandRing1
47: LeftHandRing2
48: LeftHandRing3
49: LeftHandRing4
50: LeftHandMiddle1
51: LeftHandMiddle2
52: LeftHandMiddle3
53: LeftHandMiddle4
54: LeftHandIndex1
55: LeftHandIndex2
56: LeftHandIndex3
57: LeftHandIndex4
58: LeftHandThumb1
59: LeftHandThumb2
60: LeftHandThumb3
61: LeftHandThumb4
62: Neck
63: Head
64: HeadTop_End
65: body
... skeleton joint names
*/
}
function finishCalibration() {
var avatarPosition,
handPosition,
middleFingerPosition,
leapHandHeight,
h;
if (!isOnHMD) {
if (hands[0].controller.isActive() && hands[1].controller.isActive()) {
leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0;
} else {
calibrationStatus = UNCALIBRATED;
return;
}
}
avatarPosition = MyAvatar.position;
for (h = 0; h < NUM_HANDS; h += 1) {
handPosition = MyAvatar.getJointPosition(hands[h].jointName);
if (!hasHandAndWristJoints) {
middleFingerPosition = MyAvatar.getJointPosition(fingers[h][MIDDLE_FINGER][0].jointName);
handToWristOffset[h] = Vec3.multiply(Vec3.subtract(handPosition, middleFingerPosition), 1.0 - HAND_OFFSET);
}
if (isOnHMD) {
// Offset of Leap Motion origin from physical eye position
hands[h].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET };
} else {
hands[h].zeroPosition = {
x: handPosition.x - avatarPosition.x,
y: handPosition.y - avatarPosition.y,
z: avatarPosition.z - handPosition.z
};
hands[h].zeroPosition = Vec3.multiplyQbyV(MyAvatar.orientation, hands[h].zeroPosition);
hands[h].zeroPosition.y = hands[h].zeroPosition.y - leapHandHeight;
}
}
MyAvatar.clearJointData("LeftHand");
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
calibrationStatus = CALIBRATED;
print("Leap Motion: Calibrated");
}
function calibrate() {
var jointNames,
i;
calibrationStatus = CALIBRATING;
avatarScale = MyAvatar.scale;
avatarFaceModelURL = MyAvatar.faceModelURL;
avatarSkeletonModelURL = MyAvatar.skeletonModelURL;
// Does this skeleton have both wrist and hand joints?
hasHandAndWristJoints = false;
jointNames = MyAvatar.getJointNames();
for (i = 0; i < jointNames.length; i += 1) {
hasHandAndWristJoints = hasHandAndWristJoints || jointNames[i].toLowerCase() === "leftwrist";
}
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0));
MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 90.0));
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
// Wait for arms to assume their positions before calculating
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
}
function checkCalibration() {
if (calibrationStatus === CALIBRATED) {
return true;
}
if (calibrationStatus !== CALIBRATING) {
calibrate();
}
return false;
}
function setIsOnHMD() {
isOnHMD = Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM);
print("Leap Motion: " + (isOnHMD ? "Is on HMD" : "Is on desk"));
}
function checkSettings() {
if (calibrationStatus > UNCALIBRATED && (MyAvatar.scale !== avatarScale
|| MyAvatar.faceModelURL !== avatarFaceModelURL
|| MyAvatar.skeletonModelURL !== avatarSkeletonModelURL
|| Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM) !== isOnHMD)) {
print("Leap Motion: Recalibrate...");
calibrationStatus = UNCALIBRATED;
setIsOnHMD();
}
}
function setUp() {
wrists = [
{
jointName: "LeftWrist",
controller: Controller.createInputController(MotionTracker, "joint_L_wrist")
},
{
jointName: "RightWrist",
controller: Controller.createInputController(MotionTracker, "joint_R_wrist")
}
];
hands = [
{
jointName: "LeftHand",
controller: Controller.createInputController(MotionTracker, "joint_L_hand"),
inactiveCount: 0
},
{
jointName: "RightHand",
controller: Controller.createInputController(MotionTracker, "joint_R_hand"),
inactiveCount: 0
}
];
// The Leap controller's first joint is the hand-metacarpal joint but this joint's data is not used because it's too
// dependent on the model skeleton exactly matching the Leap skeleton; using just the second and subsequent joints
// seems to work better over all.
fingers = [{}, {}];
fingers[0] = [
[
{ jointName: "LeftHandThumb1", controller: Controller.createInputController(MotionTracker, "joint_L_thumb2") },
{ jointName: "LeftHandThumb2", controller: Controller.createInputController(MotionTracker, "joint_L_thumb3") },
{ jointName: "LeftHandThumb3", controller: Controller.createInputController(MotionTracker, "joint_L_thumb4") }
],
[
{ jointName: "LeftHandIndex1", controller: Controller.createInputController(MotionTracker, "joint_L_index2") },
{ jointName: "LeftHandIndex2", controller: Controller.createInputController(MotionTracker, "joint_L_index3") },
{ jointName: "LeftHandIndex3", controller: Controller.createInputController(MotionTracker, "joint_L_index4") }
],
[
{ jointName: "LeftHandMiddle1", controller: Controller.createInputController(MotionTracker, "joint_L_middle2") },
{ jointName: "LeftHandMiddle2", controller: Controller.createInputController(MotionTracker, "joint_L_middle3") },
{ jointName: "LeftHandMiddle3", controller: Controller.createInputController(MotionTracker, "joint_L_middle4") }
],
[
{ jointName: "LeftHandRing1", controller: Controller.createInputController(MotionTracker, "joint_L_ring2") },
{ jointName: "LeftHandRing2", controller: Controller.createInputController(MotionTracker, "joint_L_ring3") },
{ jointName: "LeftHandRing3", controller: Controller.createInputController(MotionTracker, "joint_L_ring4") }
],
[
{ jointName: "LeftHandPinky1", controller: Controller.createInputController(MotionTracker, "joint_L_pinky2") },
{ jointName: "LeftHandPinky2", controller: Controller.createInputController(MotionTracker, "joint_L_pinky3") },
{ jointName: "LeftHandPinky3", controller: Controller.createInputController(MotionTracker, "joint_L_pinky4") }
]
];
fingers[1] = [
[
{ jointName: "RightHandThumb1", controller: Controller.createInputController(MotionTracker, "joint_R_thumb2") },
{ jointName: "RightHandThumb2", controller: Controller.createInputController(MotionTracker, "joint_R_thumb3") },
{ jointName: "RightHandThumb3", controller: Controller.createInputController(MotionTracker, "joint_R_thumb4") }
],
[
{ jointName: "RightHandIndex1", controller: Controller.createInputController(MotionTracker, "joint_R_index2") },
{ jointName: "RightHandIndex2", controller: Controller.createInputController(MotionTracker, "joint_R_index3") },
{ jointName: "RightHandIndex3", controller: Controller.createInputController(MotionTracker, "joint_R_index4") }
],
[
{ jointName: "RightHandMiddle1", controller: Controller.createInputController(MotionTracker, "joint_R_middle2") },
{ jointName: "RightHandMiddle2", controller: Controller.createInputController(MotionTracker, "joint_R_middle3") },
{ jointName: "RightHandMiddle3", controller: Controller.createInputController(MotionTracker, "joint_R_middle4") }
],
[
{ jointName: "RightHandRing1", controller: Controller.createInputController(MotionTracker, "joint_R_ring2") },
{ jointName: "RightHandRing2", controller: Controller.createInputController(MotionTracker, "joint_R_ring3") },
{ jointName: "RightHandRing3", controller: Controller.createInputController(MotionTracker, "joint_R_ring4") }
],
[
{ jointName: "RightHandPinky1", controller: Controller.createInputController(MotionTracker, "joint_R_pinky2") },
{ jointName: "RightHandPinky2", controller: Controller.createInputController(MotionTracker, "joint_R_pinky3") },
{ jointName: "RightHandPinky3", controller: Controller.createInputController(MotionTracker, "joint_R_pinky4") }
]
];
setIsOnHMD();
settingsTimer = Script.setInterval(checkSettings, 2000);
calibrationStatus = UNCALIBRATED;
}
function moveHands() {
var h,
i,
j,
side,
handOffset,
wristOffset,
handRotation,
locRotation,
cameraOrientation,
inverseAvatarOrientation;
for (h = 0; h < NUM_HANDS; h += 1) {
side = h === 0 ? -1.0 : 1.0;
if (hands[h].controller.isActive()) {
// Calibrate if necessary.
if (!checkCalibration()) {
return;
}
// Hand position ...
handOffset = hands[h].controller.getAbsTranslation();
handRotation = hands[h].controller.getAbsRotation();
if (isOnHMD) {
// Adjust to control wrist position if "hand" joint is at wrist ...
if (!hasHandAndWristJoints) {
wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]);
handOffset = Vec3.sum(handOffset, wristOffset);
}
// Hand offset in camera coordinates ...
handOffset = {
x: hands[h].zeroPosition.x - handOffset.x,
y: hands[h].zeroPosition.y - handOffset.z,
z: hands[h].zeroPosition.z + handOffset.y
};
handOffset.z = -handOffset.z;
// Hand offset in world coordinates ...
cameraOrientation = Camera.getOrientation();
handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset));
// Hand offset in avatar coordinates ...
inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation);
handOffset = Vec3.subtract(handOffset, MyAvatar.position);
handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset);
handOffset.z = -handOffset.z;
handOffset.x = -handOffset.x;
// Hand rotation in camera coordinates ...
handRotation = {
x: handRotation.z,
y: handRotation.y,
z: handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
if (h === 0) {
handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation);
} else {
handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation);
}
cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation);
handRotation = Quat.multiply(inverseAvatarOrientation, handRotation);
} else {
// Hand offset in camera coordinates ...
handOffset = {
x: -handOffset.x,
y: hands[h].zeroPosition.y + handOffset.y,
z: hands[h].zeroPosition.z - handOffset.z
};
// Hand rotation in camera coordinates ...
handRotation = {
x: handRotation.y,
y: handRotation.z,
z: -handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
if (h === 0) {
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 1, y: 0, z: 0 }),
handRotation);
} else {
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 1, y: 0, z: 0 }),
handRotation);
}
}
// Set hand position and orientation ...
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
// Set finger joints ...
for (i = 0; i < NUM_FINGERS; i += 1) {
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
if (fingers[h][i][j].controller !== null) {
locRotation = fingers[h][i][j].controller.getLocRotation();
if (i === THUMB) {
locRotation = {
x: side * locRotation.y,
y: side * -locRotation.z,
z: side * -locRotation.x,
w: locRotation.w
};
} else {
locRotation = {
x: -locRotation.x,
y: -locRotation.z,
z: -locRotation.y,
w: locRotation.w
};
}
MyAvatar.setJointData(fingers[h][i][j].jointName, locRotation);
}
}
}
hands[h].inactiveCount = 0;
} else {
if (hands[h].inactiveCount < MAX_HAND_INACTIVE_COUNT) {
hands[h].inactiveCount += 1;
if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) {
if (h === 0) {
MyAvatar.clearJointData("LeftHand");
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
} else {
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
}
}
}
}
}
}
function tearDown() {
var h,
i,
j;
Script.clearInterval(settingsTimer);
for (h = 0; h < NUM_HANDS; h += 1) {
Controller.releaseInputController(hands[h].controller);
Controller.releaseInputController(wrists[h].controller);
for (i = 0; i < NUM_FINGERS; i += 1) {
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
if (fingers[h][i][j].controller !== null) {
Controller.releaseInputController(fingers[h][i][j].controller);
}
}
}
}
}
return {
printSkeletonJointNames: printSkeletonJointNames,
setUp : setUp,
moveHands : moveHands,
tearDown : tearDown
};
}());
//leapHands.printSkeletonJointNames();
leapHands.setUp();
Script.update.connect(leapHands.moveHands);
Script.scriptEnding.connect(leapHands.tearDown);

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/220Sine.wav");

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// A few sample files you may want to try:

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var soundClip = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail%20Party%20Snippets/Walken1.wav");

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/globals.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include("libraries/toolBars.js");
var recordingFile = "recording.rec";

View file

@ -2,7 +2,7 @@ set(TARGET_NAME interface)
project(${TARGET_NAME})
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "LeapMotion" "RtMidi" "Qxmpp" "SDL2" "Gverb")
set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "LeapMotion" "RtMidi" "Qxmpp" "SDL2" "Gverb" "RSSDK")
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)

9
interface/external/rssdk/readme.txt vendored Normal file
View file

@ -0,0 +1,9 @@
Instructions for adding the Intel Realsense (RSSDK) to Interface
Thijs Wenker, December 19, 2014
This is Windows only for now.
1. Download the SDK at https://software.intel.com/en-us/intel-realsense-sdk/download
2. Copy the `lib` and `include` folder inside this directory

View file

@ -91,6 +91,7 @@
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "devices/Leapmotion.h"
#include "devices/RealSense.h"
#include "devices/MIDIManager.h"
#include "devices/OculusManager.h"
#include "devices/TV3DManager.h"
@ -189,9 +190,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_frameCount(0),
_fps(60.0f),
_justStarted(true),
#ifdef USE_BULLET_PHYSICS
_physicsEngine(glm::vec3(0.0f)),
#endif // USE_BULLET_PHYSICS
_entities(true, this, this),
_entityCollisionSystem(),
_entityClipboardRenderer(false, this, this),
@ -256,6 +255,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
_bookmarks = new Bookmarks(); // Before setting up the menu
// call Menu getInstance static method to set up the menu
_window->setMenuBar(Menu::getInstance());
@ -335,6 +336,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// use our MyAvatar position and quat for address manager path
addressManager->setPositionGetter(getPositionForPath);
addressManager->setOrientationGetter(getOrientationForPath);
connect(addressManager.data(), &AddressManager::rootPlaceNameChanged, this, &Application::updateWindowTitle);
_settings = new QSettings(this);
_numChangedSettings = 0;
@ -913,13 +916,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
Menu::getInstance()->triggerOption(MenuOption::Chat);
break;
case Qt::Key_N:
if (isMeta) {
Menu::getInstance()->triggerOption(MenuOption::NameLocation);
}
break;
case Qt::Key_Up:
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
if (!isShifted) {
@ -1685,6 +1681,7 @@ void Application::init() {
DependencyManager::get<Visage>()->init();
Leapmotion::init();
RealSense::init();
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
@ -1726,12 +1723,10 @@ void Application::init() {
// save settings when avatar changes
connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::bumpSettings);
#ifdef USE_BULLET_PHYSICS
EntityTree* tree = _entities.getTree();
_physicsEngine.setEntityTree(tree);
tree->setSimulation(&_physicsEngine);
_physicsEngine.init(&_entityEditSender);
#endif // USE_BULLET_PHYSICS
// make sure our texture cache knows about window size changes
DependencyManager::get<TextureCache>()->associateWithWidget(glCanvas.data());
@ -2042,12 +2037,10 @@ void Application::update(float deltaTime) {
updateDialogs(deltaTime); // update various stats dialogs if present
updateCursor(deltaTime); // Handle cursor updates
#ifdef USE_BULLET_PHYSICS
{
PerformanceTimer perfTimer("physics");
_physicsEngine.stepSimulation();
}
#endif // USE_BULLET_PHYSICS
if (!_aboutToQuit) {
PerformanceTimer perfTimer("entities");
@ -2135,12 +2128,17 @@ void Application::updateMyAvatar(float deltaTime) {
_myAvatar->update(deltaTime);
{
quint64 now = usecTimestampNow();
quint64 dt = now - _lastSendAvatarDataTime;
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS) {
// send head/hand data to the avatar mixer and voxel server
PerformanceTimer perfTimer("send");
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
packet.append(_myAvatar->toByteArray());
controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
_lastSendAvatarDataTime = now;
}
}
@ -2793,7 +2791,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
}
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
DependencyManager::get<DeferredLightingEffect>()->prepare();
if (!selfAvatarOnly) {
@ -2844,6 +2842,8 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
{
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
PROFILE_RANGE("DeferredLighting");
PerformanceTimer perfTimer("lighting");
DependencyManager::get<DeferredLightingEffect>()->render();
@ -3142,8 +3142,14 @@ void Application::updateWindowTitle(){
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) ";
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
QString currentPlaceName = DependencyManager::get<AddressManager>()->getRootPlaceName();
if (currentPlaceName.isEmpty()) {
currentPlaceName = nodeList->getDomainHandler().getHostname();
}
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
+ DependencyManager::get<AddressManager>()->getCurrentDomain() + connectionStatus + buildVersion;
+ currentPlaceName + connectionStatus + buildVersion;
#ifndef WIN32
// crashes with vs2013/win32
@ -3155,23 +3161,34 @@ void Application::updateWindowTitle(){
void Application::updateLocationInServer() {
AccountManager& accountManager = AccountManager::getInstance();
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
if (accountManager.isLoggedIn() && domainHandler.isConnected() && !domainHandler.getUUID().isNull()) {
if (accountManager.isLoggedIn() && domainHandler.isConnected()
&& (!addressManager->getRootPlaceID().isNull() || !domainHandler.getUUID().isNull())) {
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
QJsonObject locationObject;
QString pathString = DependencyManager::get<AddressManager>()->currentPath();
QString pathString = addressManager->currentPath();
const QString LOCATION_KEY_IN_ROOT = "location";
const QString PATH_KEY_IN_LOCATION = "path";
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION, domainHandler.getUUID().toString());
if (!addressManager->getRootPlaceID().isNull()) {
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
} else {
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
}
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
@ -3659,7 +3676,6 @@ void Application::openUrl(const QUrl& url) {
void Application::updateMyAvatarTransform() {
bumpSettings();
#ifdef USE_BULLET_PHYSICS
const float SIMULATION_OFFSET_QUANTIZATION = 16.0f; // meters
glm::vec3 avatarPosition = _myAvatar->getPosition();
glm::vec3 physicsWorldOffset = _physicsEngine.getOriginOffset();
@ -3673,7 +3689,6 @@ void Application::updateMyAvatarTransform() {
// TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine
_physicsEngine.setOriginOffset(newOriginOffset);
}
#endif // USE_BULLET_PHYSICS
}
void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {
@ -3954,3 +3969,31 @@ float Application::getRenderResolutionScale() const {
return 1.0f;
}
}
int Application::getRenderAmbientLight() const {
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLightGlobal)) {
return -1;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight0)) {
return 0;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight1)) {
return 1;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight2)) {
return 2;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight3)) {
return 3;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight4)) {
return 4;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight5)) {
return 5;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight6)) {
return 6;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight7)) {
return 7;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight8)) {
return 8;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight9)) {
return 9;
} else {
return -1;
}
}

View file

@ -33,11 +33,13 @@
#include <NodeList.h>
#include <OctreeQuery.h>
#include <PacketHeaders.h>
#include <PhysicsEngine.h>
#include <ScriptEngine.h>
#include <TextureCache.h>
#include <ViewFrustum.h>
#include "Audio.h"
#include "Bookmarks.h"
#include "Camera.h"
#include "DatagramProcessor.h"
#include "Environment.h"
@ -111,6 +113,10 @@ static const float MIRROR_REARVIEW_DISTANCE = 0.722f;
static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f;
static const float MIRROR_FIELD_OF_VIEW = 30.0f;
// 70 times per second - target is 60hz, but this helps account for any small deviations
// in the update loop
static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = (1000 * 1000) / 70;
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
static const QString INFO_HELP_PATH = "html/interface-welcome-allsvg.html";
@ -282,6 +288,7 @@ public:
bool isLookingAtMyAvatar(Avatar* avatar);
float getRenderResolutionScale() const;
int getRenderAmbientLight() const;
unsigned int getRenderTargetFramerate() const;
bool isVSyncOn() const;
@ -296,6 +303,8 @@ public:
QRect getDesirableApplicationGeometry();
RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
Bookmarks* getBookmarks() const { return _bookmarks; }
signals:
/// Fired when we're simulating; allows external parties to hook in.
@ -454,9 +463,7 @@ private:
bool _justStarted;
Stars _stars;
#ifdef USE_BULLET_PHYSICS
PhysicsEngine _physicsEngine;
#endif // USE_BULLET_PHYSICS
EntityTreeRenderer _entities;
EntityCollisionSystem _entityCollisionSystem;
@ -567,9 +574,13 @@ private:
quint64 _lastNackTime;
quint64 _lastSendDownstreamAudioStats;
quint64 _lastSendAvatarDataTime;
bool _isVSyncOn;
bool _aboutToQuit;
Bookmarks* _bookmarks;
};
#endif // hifi_Application_h

View file

@ -0,0 +1,73 @@
//
// Bookmarks.cpp
// interface/src
//
// Created by David Rowe on 13 Jan 2015.
// Copyright 2015 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 <QFile>
#include <QJsonDocument>
#include <QStandardPaths>
#include "Bookmarks.h"
Bookmarks::Bookmarks() {
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME;
readFromFile();
}
void Bookmarks::insert(const QString& name, const QString& address) {
_bookmarks.insert(name, address);
if (contains(name)) {
qDebug() << "Added bookmark:" << name << "," << address;
persistToFile();
} else {
qWarning() << "Couldn't add bookmark: " << name << "," << address;
}
}
void Bookmarks::remove(const QString& name) {
_bookmarks.remove(name);
if (!contains(name)) {
qDebug() << "Deleted bookmark:" << name;
persistToFile();
} else {
qWarning() << "Couldn't delete bookmark:" << name;
}
}
bool Bookmarks::contains(const QString& name) const {
return _bookmarks.contains(name);
}
void Bookmarks::readFromFile() {
QFile loadFile(_bookmarksFilename);
if (!loadFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open bookmarks file for reading");
return;
}
QByteArray data = loadFile.readAll();
QJsonDocument json(QJsonDocument::fromJson(data));
_bookmarks = json.object().toVariantMap();
}
void Bookmarks::persistToFile() {
QFile saveFile(_bookmarksFilename);
if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning("Couldn't open bookmarks file for writing");
return;
}
QJsonDocument json(QJsonObject::fromVariantMap(_bookmarks));
QByteArray data = json.toJson();
saveFile.write(data);
}

41
interface/src/Bookmarks.h Normal file
View file

@ -0,0 +1,41 @@
//
// Bookmarks.h
// interface/src
//
// Created by David Rowe on 13 Jan 2015.
// Copyright 2015 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_Bookmarks_h
#define hifi_Bookmarks_h
#include <QJsonObject>
#include <QDebug>
#include <QMap>
#include <QObject>
class Bookmarks: public QObject {
Q_OBJECT
public:
Bookmarks();
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
void remove(const QString& name);
bool contains(const QString& name) const;
QVariantMap* getBookmarks() { return &_bookmarks; };
private:
QVariantMap _bookmarks; // { name: address, ... }
const QString BOOKMARKS_FILENAME = "bookmarks.json";
QString _bookmarksFilename;
void readFromFile();
void persistToFile();
};
#endif // hifi_Bookmarks_h

View file

@ -13,6 +13,7 @@
#include <QBoxLayout>
#include <QColorDialog>
#include <QClipboard>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFileDialog>
@ -43,6 +44,7 @@
#include "Audio.h"
#include "audio/AudioIOStatsRenderer.h"
#include "audio/AudioScope.h"
#include "devices/RealSense.h"
#include "devices/Visage.h"
#include "Menu.h"
#include "scripting/LocationScriptingInterface.h"
@ -122,22 +124,25 @@ Menu::Menu() :
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
appInstance, SLOT(toggleRunningScriptsWidget()));
addDisabledActionAndSeparator(fileMenu, "Go");
addActionToQMenuAndActionHash(fileMenu,
MenuOption::NameLocation,
Qt::CTRL | Qt::Key_N,
this,
SLOT(nameLocation()));
addActionToQMenuAndActionHash(fileMenu,
MenuOption::MyLocations,
Qt::CTRL | Qt::Key_K,
this,
SLOT(toggleLocationList()));
addDisabledActionAndSeparator(fileMenu, "Location");
addActionToQMenuAndActionHash(fileMenu, MenuOption::BookmarkLocation, 0,
this, SLOT(bookmarkLocation()));
_bookmarksMenu = fileMenu->addMenu(MenuOption::Bookmarks);
_bookmarksMenu->setEnabled(false);
_deleteBookmarksMenu = addActionToQMenuAndActionHash(fileMenu,
MenuOption::DeleteBookmark, 0,
this, SLOT(deleteBookmark()));
_deleteBookmarksMenu->setEnabled(false);
loadBookmarks();
addActionToQMenuAndActionHash(fileMenu,
MenuOption::AddressBar,
Qt::Key_Enter,
this,
SLOT(toggleAddressBar()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0,
this, SLOT(copyAddress()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0,
this, SLOT(copyPath()));
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0,
@ -261,7 +266,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
avatar, SLOT(updateMotionBehavior()));
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With...");
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With");
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false,
avatar, SLOT(onToggleRagdoll()));
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithAvatars,
@ -338,6 +343,21 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DisableAutoAdjustLOD);
QMenu* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
ambientLightGroup->setExclusive(true);
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight0, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight1, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight2, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight3, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight4, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight5, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight6, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
@ -439,6 +459,11 @@ Menu::Menu() :
QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
#ifdef HAVE_RSSDK
QMenu* realSenseOptionsMenu = handOptionsMenu->addMenu("RealSense");
addActionToQMenuAndActionHash(realSenseOptionsMenu, MenuOption::LoadRSSDKFile, 0, this, SLOT(loadRSSDKFile()));
#endif
QMenu* networkMenu = developerMenu->addMenu("Network");
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
addCheckableActionToQMenuAndActionHash(networkMenu,
@ -1006,10 +1031,143 @@ void Menu::toggleAddressBar() {
}
}
void Menu::copyAddress() {
auto addressManager = DependencyManager::get<AddressManager>();
QString address = addressManager->currentAddress().toString();
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(address);
}
void Menu::copyPath() {
auto addressManager = DependencyManager::get<AddressManager>();
QString path = addressManager->currentPath();
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(path);
}
void Menu::changeVSync() {
Application::getInstance()->setVSyncEnabled(isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn));
}
void Menu::loadBookmarks() {
QVariantMap* bookmarks = Application::getInstance()->getBookmarks()->getBookmarks();
if (bookmarks->count() > 0) {
QMapIterator<QString, QVariant> i(*bookmarks);
while (i.hasNext()) {
i.next();
QString bookmarkName = i.key();
QString bookmarkAddress = i.value().toString();
QAction* teleportAction = new QAction(getMenu(MenuOption::Bookmarks));
teleportAction->setData(bookmarkAddress);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, bookmarkName, 0, QAction::NoRole);
}
_bookmarksMenu->setEnabled(true);
_deleteBookmarksMenu->setEnabled(true);
}
}
void Menu::bookmarkLocation() {
QInputDialog bookmarkLocationDialog(Application::getInstance()->getWindow());
bookmarkLocationDialog.setWindowTitle("Bookmark Location");
bookmarkLocationDialog.setLabelText("Name:");
bookmarkLocationDialog.setInputMode(QInputDialog::TextInput);
bookmarkLocationDialog.resize(400, 200);
if (bookmarkLocationDialog.exec() == QDialog::Rejected) {
return;
}
QString bookmarkName = bookmarkLocationDialog.textValue().trimmed();
bookmarkName = bookmarkName.replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
if (bookmarkName.length() == 0) {
return;
}
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks* bookmarks = Application::getInstance()->getBookmarks();
if (bookmarks->contains(bookmarkName)) {
QMessageBox duplicateBookmarkMessage;
duplicateBookmarkMessage.setIcon(QMessageBox::Warning);
duplicateBookmarkMessage.setText("The bookmark name you entered already exists in your list.");
duplicateBookmarkMessage.setInformativeText("Would you like to overwrite it?");
duplicateBookmarkMessage.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
duplicateBookmarkMessage.setDefaultButton(QMessageBox::Yes);
if (duplicateBookmarkMessage.exec() == QMessageBox::No) {
return;
}
removeAction(_bookmarksMenu, bookmarkName);
}
QAction* teleportAction = new QAction(getMenu(MenuOption::Bookmarks));
teleportAction->setData(bookmarkAddress);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
QList<QAction*> menuItems = _bookmarksMenu->actions();
int position = 0;
while (position < menuItems.count() && bookmarkName > menuItems[position]->text()) {
position += 1;
}
addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, bookmarkName, 0,
QAction::NoRole, position);
bookmarks->insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
_bookmarksMenu->setEnabled(true);
_deleteBookmarksMenu->setEnabled(true);
}
void Menu::teleportToBookmark() {
QAction *action = qobject_cast<QAction *>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void Menu::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> menuItems = _bookmarksMenu->actions();
for (int i = 0; i < menuItems.count(); i += 1) {
bookmarkList.append(menuItems[i]->text());
}
QInputDialog deleteBookmarkDialog(Application::getInstance()->getWindow());
deleteBookmarkDialog.setWindowTitle("Delete Bookmark");
deleteBookmarkDialog.setLabelText("Select the bookmark to delete");
deleteBookmarkDialog.resize(400, 400);
deleteBookmarkDialog.setOption(QInputDialog::UseListViewForComboBoxItems);
deleteBookmarkDialog.setComboBoxItems(bookmarkList);
deleteBookmarkDialog.setOkButtonText("Delete");
if (deleteBookmarkDialog.exec() == QDialog::Rejected) {
return;
}
QString bookmarkName = deleteBookmarkDialog.textValue().trimmed();
if (bookmarkName.length() == 0) {
return;
}
removeAction(_bookmarksMenu, bookmarkName);
Bookmarks* bookmarks = Application::getInstance()->getBookmarks();
bookmarks->remove(bookmarkName);
if (_bookmarksMenu->actions().count() == 0) {
_bookmarksMenu->setEnabled(false);
_deleteBookmarksMenu->setEnabled(false);
}
}
void Menu::displayNameLocationResponse(const QString& errorString) {
if (!errorString.isEmpty()) {
@ -1019,66 +1177,6 @@ void Menu::displayNameLocationResponse(const QString& errorString) {
}
}
void Menu::toggleLocationList() {
if (!_userLocationsDialog) {
JavascriptObjectMap locationObjectMap;
locationObjectMap.insert("InterfaceLocation", DependencyManager::get<AddressManager>().data());
_userLocationsDialog = DataWebDialog::dialogForPath("/user/locations", locationObjectMap);
}
if (!_userLocationsDialog->isVisible()) {
_userLocationsDialog->show();
}
_userLocationsDialog->raise();
_userLocationsDialog->activateWindow();
_userLocationsDialog->showNormal();
}
void Menu::nameLocation() {
// check if user is logged in or show login dialog if not
AccountManager& accountManager = AccountManager::getInstance();
if (!accountManager.isLoggedIn()) {
QMessageBox msgBox;
msgBox.setText("We need to tie this location to your username.");
msgBox.setInformativeText("Please login first, then try naming the location again.");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.button(QMessageBox::Ok)->setText("Login");
if (msgBox.exec() == QMessageBox::Ok) {
loginForCurrentDomain();
}
return;
}
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
if (domainHandler.getUUID().isNull()) {
const QString UNREGISTERED_DOMAIN_MESSAGE = "This domain is not registered with High Fidelity."
"\n\nYou cannot create a global location in an unregistered domain.";
QMessageBox::critical(this, "Unregistered Domain", UNREGISTERED_DOMAIN_MESSAGE);
return;
}
if (!_newLocationDialog) {
JavascriptObjectMap locationObjectMap;
locationObjectMap.insert("InterfaceLocation", DependencyManager::get<AddressManager>().data());
_newLocationDialog = DataWebDialog::dialogForPath("/user/locations/new", locationObjectMap);
}
if (!_newLocationDialog->isVisible()) {
_newLocationDialog->show();
}
_newLocationDialog->raise();
_newLocationDialog->activateWindow();
_newLocationDialog->showNormal();
}
void Menu::toggleLoginMenuItem() {
AccountManager& accountManager = AccountManager::getInstance();
@ -1150,6 +1248,10 @@ void Menu::showChat() {
}
}
void Menu::loadRSSDKFile() {
RealSense::getInstance()->loadRSSDKFile();
}
void Menu::toggleChat() {
#ifdef HAVE_QXMPP
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());

View file

@ -177,6 +177,8 @@ public slots:
void importSettings();
void exportSettings();
void toggleAddressBar();
void copyAddress();
void copyPath();
void toggleLoginMenuItem();
void toggleSixense(bool shouldEnable);
@ -200,8 +202,9 @@ private slots:
void editAttachments();
void editAnimations();
void changePrivateKey();
void nameLocation();
void toggleLocationList();
void bookmarkLocation();
void teleportToBookmark();
void deleteBookmark();
void hmdToolsClosed();
void runTests();
void showMetavoxelEditor();
@ -214,6 +217,7 @@ private slots:
void audioMuteToggled();
void displayNameLocationResponse(const QString& errorString);
void changeVSync();
void loadRSSDKFile();
private:
static Menu* _instance;
@ -286,8 +290,6 @@ private:
QPointer<AttachmentsDialog> _attachmentsDialog;
QPointer<BandwidthDialog> _bandwidthDialog;
QPointer<CachesSizeDialog> _cachesSizeDialog;
QPointer<DataWebDialog> _newLocationDialog;
QPointer<DataWebDialog> _userLocationsDialog;
QPointer<HMDToolsDialog> _hmdToolsDialog;
QPointer<LodToolsDialog> _lodToolsDialog;
QPointer<LoginDialog> _loginDialog;
@ -307,6 +309,10 @@ private:
bool _shouldRenderTableNeedsRebuilding = true;
QMap<float, float> _shouldRenderTable;
void loadBookmarks();
QMenu* _bookmarksMenu;
QAction* _deleteBookmarksMenu;
};
namespace MenuOption {
@ -334,6 +340,8 @@ namespace MenuOption {
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
const QString BookmarkLocation = "Bookmark Location";
const QString Bookmarks = "Bookmarks";
const QString CascadedShadows = "Cascaded";
const QString CachesSize = "Caches Size";
const QString Chat = "Chat...";
@ -343,7 +351,10 @@ namespace MenuOption {
const QString CollideWithEnvironment = "Collide With World Boundaries";
const QString Collisions = "Collisions";
const QString Console = "Console...";
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString ControlWithSpeech = "Control With Speech";
const QString DeleteBookmark = "Delete Bookmark...";
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
const QString DontDoPrecisionPicking = "Don't Do Precision Picking";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
@ -387,6 +398,7 @@ namespace MenuOption {
const QString LeapMotionOnHMD = "Leap Motion on HMD";
const QString LoadScript = "Open and Run Script File...";
const QString LoadScriptURL = "Open and Run Script from URL...";
const QString LoadRSSDKFile = "Load .rssdk file";
const QString LodTools = "LOD Tools";
const QString Login = "Login";
const QString Log = "Log";
@ -397,8 +409,6 @@ namespace MenuOption {
const QString Mirror = "Mirror";
const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment";
const QString MyLocations = "My Locations...";
const QString NameLocation = "Name this location";
const QString NetworkSimulator = "Network Simulator...";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity";
@ -430,6 +440,18 @@ namespace MenuOption {
const QString RenderResolutionHalf = "1/2";
const QString RenderResolutionThird = "1/3";
const QString RenderResolutionQuarter = "1/4";
const QString RenderAmbientLight = "Ambient Light";
const QString RenderAmbientLightGlobal = "Global";
const QString RenderAmbientLight0 = "OLD_TOWN_SQUARE";
const QString RenderAmbientLight1 = "GRACE_CATHEDRAL";
const QString RenderAmbientLight2 = "EUCALYPTUS_GROVE";
const QString RenderAmbientLight3 = "ST_PETERS_BASILICA";
const QString RenderAmbientLight4 = "UFFIZI_GALLERY";
const QString RenderAmbientLight5 = "GALILEOS_TOMB";
const QString RenderAmbientLight6 = "VINE_STREET_KITCHEN";
const QString RenderAmbientLight7 = "BREEZEWAY";
const QString RenderAmbientLight8 = "CAMPUS_SUNSET";
const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSensors = "Reset Sensors";
const QString RunningScripts = "Running Scripts";

View file

@ -31,20 +31,30 @@ static const QString IS_TRUNCATED_NAME = "IsTruncated";
static const QString CONTAINER_NAME = "Contents";
static const QString KEY_NAME = "Key";
ScriptItem::ScriptItem(const QString& filename, const QString& fullPath) :
_filename(filename),
_fullPath(fullPath) {
TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNodeType type) :
_parent(parent),
_name(name),
_type(type) {
};
TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) :
TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT),
_localPath(localPath),
_fullPath(fullPath),
_origin(origin) {
};
TreeNodeFolder::TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent) :
TreeNodeBase(parent, foldername, TREE_NODE_TYPE_FOLDER) {
};
ScriptsModel::ScriptsModel(QObject* parent) :
QAbstractListModel(parent),
QAbstractItemModel(parent),
_loadingScripts(false),
_localDirectory(),
_fsWatcher(),
_localFiles(),
_remoteFiles()
_treeNodes()
{
_localDirectory.setFilter(QDir::Files | QDir::Readable);
_localDirectory.setNameFilters(QStringList("*.js"));
@ -57,31 +67,61 @@ ScriptsModel::ScriptsModel(QObject* parent) :
reloadRemoteFiles();
}
QVariant ScriptsModel::data(const QModelIndex& index, int role) const {
const QList<ScriptItem*>* files = NULL;
int row = 0;
bool isLocal = index.row() < _localFiles.size();
if (isLocal) {
files = &_localFiles;
row = index.row();
} else {
files = &_remoteFiles;
row = index.row() - _localFiles.size();
}
ScriptsModel::~ScriptsModel() {
for (int i = _treeNodes.size() - 1; i >= 0; i--) {
delete _treeNodes.at(i);
}
_treeNodes.clear();
}
if (role == Qt::DisplayRole) {
return QVariant((*files)[row]->getFilename() + (isLocal ? " (local)" : ""));
} else if (role == ScriptPath) {
return QVariant((*files)[row]->getFullPath());
TreeNodeBase* ScriptsModel::getTreeNodeFromIndex(const QModelIndex& index) const {
if (index.isValid()) {
return static_cast<TreeNodeBase*>(index.internalPointer());
}
return NULL;
}
QModelIndex ScriptsModel::index(int row, int column, const QModelIndex& parent) const {
if (row < 0 || column < 0) {
return QModelIndex();
}
return createIndex(row, column, getFolderNodes(static_cast<TreeNodeFolder*>(getTreeNodeFromIndex(parent))).at(row));
}
QModelIndex ScriptsModel::parent(const QModelIndex& child) const {
TreeNodeFolder* parent = (static_cast<TreeNodeBase*>(child.internalPointer()))->getParent();
if (!parent) {
return QModelIndex();
}
TreeNodeFolder* grandParent = parent->getParent();
int row = getFolderNodes(grandParent).indexOf(parent);
return createIndex(row, 0, parent);
}
QVariant ScriptsModel::data(const QModelIndex& index, int role) const {
TreeNodeBase* node = getTreeNodeFromIndex(index);
if (node->getType() == TREE_NODE_TYPE_SCRIPT) {
TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
if (role == Qt::DisplayRole) {
return QVariant(script->getName() + (script->getOrigin() == SCRIPT_ORIGIN_LOCAL ? " (local)" : ""));
} else if (role == ScriptPath) {
return QVariant(script->getFullPath());
}
} else if (node->getType() == TREE_NODE_TYPE_FOLDER) {
TreeNodeFolder* folder = static_cast<TreeNodeFolder*>(node);
if (role == Qt::DisplayRole) {
return QVariant(folder->getName());
}
}
return QVariant();
}
int ScriptsModel::rowCount(const QModelIndex& parent) const {
if (parent.isValid()) {
return 0;
}
return _localFiles.length() + _remoteFiles.length();
return getFolderNodes(static_cast<TreeNodeFolder*>(getTreeNodeFromIndex(parent))).count();
}
int ScriptsModel::columnCount(const QModelIndex& parent) const {
return 1;
}
void ScriptsModel::updateScriptsLocation(const QString& newPath) {
@ -93,7 +133,7 @@ void ScriptsModel::updateScriptsLocation(const QString& newPath) {
if (!_localDirectory.absolutePath().isEmpty()) {
_fsWatcher.addPath(_localDirectory.absolutePath());
}
}
}
reloadLocalFiles();
}
@ -101,8 +141,14 @@ void ScriptsModel::updateScriptsLocation(const QString& newPath) {
void ScriptsModel::reloadRemoteFiles() {
if (!_loadingScripts) {
_loadingScripts = true;
while (!_remoteFiles.isEmpty()) {
delete _remoteFiles.takeFirst();
for (int i = _treeNodes.size() - 1; i >= 0; i--) {
TreeNodeBase* node = _treeNodes.at(i);
if (node->getType() == TREE_NODE_TYPE_SCRIPT &&
static_cast<TreeNodeScript*>(node)->getOrigin() == SCRIPT_ORIGIN_REMOTE)
{
delete node;
_treeNodes.removeAt(i);
}
}
requestRemoteFiles();
}
@ -121,7 +167,6 @@ void ScriptsModel::requestRemoteFiles(QString marker) {
QNetworkRequest request(url);
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
}
void ScriptsModel::downloadFinished() {
@ -170,7 +215,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
xml.readNext();
lastKey = xml.text().toString();
if (jsRegex.exactMatch(xml.text().toString())) {
_remoteFiles.append(new ScriptItem(lastKey.mid(MODELS_LOCATION.length()), S3_URL + "/" + lastKey));
_treeNodes.append(new TreeNodeScript(lastKey.mid(MODELS_LOCATION.length()), S3_URL + "/" + lastKey, SCRIPT_ORIGIN_REMOTE));
}
}
xml.readNext();
@ -178,7 +223,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
}
xml.readNext();
}
rebuildTree();
endResetModel();
// Error handling
@ -198,8 +243,14 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
void ScriptsModel::reloadLocalFiles() {
beginResetModel();
while (!_localFiles.isEmpty()) {
delete _localFiles.takeFirst();
for (int i = _treeNodes.size() - 1; i >= 0; i--) {
TreeNodeBase* node = _treeNodes.at(i);
if (node->getType() == TREE_NODE_TYPE_SCRIPT &&
static_cast<TreeNodeScript*>(node)->getOrigin() == SCRIPT_ORIGIN_LOCAL)
{
delete node;
_treeNodes.removeAt(i);
}
}
_localDirectory.refresh();
@ -207,8 +258,53 @@ void ScriptsModel::reloadLocalFiles() {
const QFileInfoList localFiles = _localDirectory.entryInfoList();
for (int i = 0; i < localFiles.size(); i++) {
QFileInfo file = localFiles[i];
_localFiles.append(new ScriptItem(file.fileName(), file.absoluteFilePath()));
_treeNodes.append(new TreeNodeScript(file.fileName(), file.absoluteFilePath(), SCRIPT_ORIGIN_LOCAL));
}
rebuildTree();
endResetModel();
}
void ScriptsModel::rebuildTree() {
for (int i = _treeNodes.size() - 1; i >= 0; i--) {
if (_treeNodes.at(i)->getType() == TREE_NODE_TYPE_FOLDER) {
delete _treeNodes.at(i);
_treeNodes.removeAt(i);
}
}
QHash<QString, TreeNodeFolder*> folders;
for (int i = 0; i < _treeNodes.size(); i++) {
TreeNodeBase* node = _treeNodes.at(i);
if (node->getType() == TREE_NODE_TYPE_SCRIPT) {
TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
TreeNodeFolder* parent = NULL;
QString hash;
QStringList pathList = script->getLocalPath().split(tr("/"));
pathList.removeLast();
QStringList::const_iterator pathIterator;
for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) {
hash.append(*pathIterator + "/");
if (!folders.contains(hash)) {
folders[hash] = new TreeNodeFolder(*pathIterator, parent);
}
parent = folders[hash];
}
script->setParent(parent);
}
}
QHash<QString, TreeNodeFolder*>::const_iterator folderIterator;
for (folderIterator = folders.constBegin(); folderIterator != folders.constEnd(); ++folderIterator) {
_treeNodes.append(*folderIterator);
}
folders.clear();
}
QList<TreeNodeBase*> ScriptsModel::getFolderNodes(TreeNodeFolder* parent) const {
QList<TreeNodeBase*> result;
for (int i = 0; i < _treeNodes.size(); i++) {
TreeNodeBase* node = _treeNodes.at(i);
if (node->getParent() == parent) {
result.append(node);
}
}
return result;
}

View file

@ -12,30 +12,69 @@
#ifndef hifi_ScriptsModel_h
#define hifi_ScriptsModel_h
#include <QAbstractListModel>
#include <QAbstractItemModel>
#include <QDir>
#include <QNetworkReply>
#include <QFileSystemWatcher>
class ScriptItem {
public:
ScriptItem(const QString& filename, const QString& fullPath);
class TreeNodeFolder;
const QString& getFilename() { return _filename; };
const QString& getFullPath() { return _fullPath; };
private:
QString _filename;
QString _fullPath;
enum ScriptOrigin {
SCRIPT_ORIGIN_LOCAL,
SCRIPT_ORIGIN_REMOTE
};
class ScriptsModel : public QAbstractListModel {
enum TreeNodeType {
TREE_NODE_TYPE_SCRIPT,
TREE_NODE_TYPE_FOLDER
};
class TreeNodeBase {
public:
TreeNodeFolder* getParent() const { return _parent; }
void setParent(TreeNodeFolder* parent) { _parent = parent; }
TreeNodeType getType() { return _type; }
const QString& getName() { return _name; };
private:
TreeNodeFolder* _parent;
TreeNodeType _type;
protected:
QString _name;
TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNodeType type);
};
class TreeNodeScript : public TreeNodeBase {
public:
TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin);
const QString& getLocalPath() { return _localPath; }
const QString& getFullPath() { return _fullPath; };
const ScriptOrigin getOrigin() { return _origin; };
private:
QString _localPath;
QString _fullPath;
ScriptOrigin _origin;
};
class TreeNodeFolder : public TreeNodeBase {
public:
TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent);
};
class ScriptsModel : public QAbstractItemModel {
Q_OBJECT
public:
ScriptsModel(QObject* parent = NULL);
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
~ScriptsModel();
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QModelIndex parent(const QModelIndex& child) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
TreeNodeBase* getTreeNodeFromIndex(const QModelIndex& index) const;
QList<TreeNodeBase*> getFolderNodes(TreeNodeFolder* parent) const;
enum Role {
ScriptPath = Qt::UserRole,
@ -50,13 +89,13 @@ protected slots:
protected:
void requestRemoteFiles(QString marker = QString());
bool parseXML(QByteArray xmlFile);
void rebuildTree();
private:
bool _loadingScripts;
QDir _localDirectory;
QFileSystemWatcher _fsWatcher;
QList<ScriptItem*> _localFiles;
QList<ScriptItem*> _remoteFiles;
QList<TreeNodeBase*> _treeNodes;
};
#endif // hifi_ScriptsModel_h

View file

@ -0,0 +1,44 @@
//
// ScriptsModelFilter.cpp
// interface/src
//
// Created by Thijs Wenker on 01/11/15.
// Copyright 2015 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 "ScriptsModelFilter.h"
ScriptsModelFilter::ScriptsModelFilter(QObject *parent) :
QSortFilterProxyModel(parent) {
}
bool ScriptsModelFilter::lessThan(const QModelIndex& left, const QModelIndex& right) const {
ScriptsModel* scriptsModel = static_cast<ScriptsModel*>(sourceModel());
TreeNodeBase* leftNode = scriptsModel->getTreeNodeFromIndex(left);
TreeNodeBase* rightNode = scriptsModel->getTreeNodeFromIndex(right);
if (leftNode->getType() != rightNode->getType()) {
return leftNode->getType() == TREE_NODE_TYPE_FOLDER;
}
return leftNode->getName() < rightNode->getName();
}
bool ScriptsModelFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const {
if (!filterRegExp().isEmpty()) {
ScriptsModel* scriptsModel = static_cast<ScriptsModel*>(sourceModel());
TreeNodeBase* node = scriptsModel->getFolderNodes(
static_cast<TreeNodeFolder*>(scriptsModel->getTreeNodeFromIndex(sourceParent))).at(sourceRow);
QModelIndex sourceIndex = sourceModel()->index(sourceRow, this->filterKeyColumn(), sourceParent);
if (node->getType() == TREE_NODE_TYPE_FOLDER) {
int rows = scriptsModel->rowCount(sourceIndex);
for (int i = 0; i < rows; i++) {
if (filterAcceptsRow(i, sourceIndex)) {
return true;
}
}
}
}
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}

View file

@ -0,0 +1,27 @@
//
// ScriptsModelFilter.h
// interface/src
//
// Created by Thijs Wenker on 01/11/15.
// Copyright 2015 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_ScriptsModelFilter_h
#define hifi_ScriptsModelFilter_h
#include "ScriptsModel.h"
#include <QSortFilterProxyModel>
class ScriptsModelFilter : public QSortFilterProxyModel {
Q_OBJECT
public:
ScriptsModelFilter(QObject *parent = NULL);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
};
#endif // hifi_ScriptsModelFilter_h

View file

@ -0,0 +1,250 @@
//
// RealSense.cpp
// interface/src/devices
//
// Created by Thijs Wenker on 12/10/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 "RealSense.h"
#include "MainWindow.h"
#include "Menu.h"
#include "SharedUtil.h"
const int PALMROOT_NUM_JOINTS = 2;
const int FINGER_NUM_JOINTS = 4;
const DeviceTracker::Name RealSense::NAME = "RealSense";
// 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..1] <=> [ hand, forearm]
MotionTracker::Index evalRealSenseJointIndex(bool isRightSide, int finger, int bone) {
#ifdef HAVE_RSSDK
MotionTracker::Index offset = 1 // start after root
+ (int(isRightSide) * PXCHandData::NUMBER_OF_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;
}
#else
return -1;
#endif // HAVE_RSSDK
}
// static
void RealSense::init() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new RealSense and register it
RealSense* realSense = new RealSense();
DeviceTracker::registerDevice(NAME, realSense);
}
}
// static
RealSense* RealSense::getInstance() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new RealSense and register it
RealSense* realSense = new RealSense();
DeviceTracker::registerDevice(NAME, realSense);
}
return dynamic_cast< RealSense* > (device);
}
RealSense::RealSense() :
MotionTracker(),
_active(false)
{
#ifdef HAVE_RSSDK
_handData = NULL;
_session = PXCSession_Create();
initSession(false, NULL);
// Create the RealSense joint hierarchy
std::vector< Semantic > sides;
sides.push_back("joint_L_");
sides.push_back("joint_R_");
std::vector< Semantic > rootBones;
rootBones.push_back("wrist");
rootBones.push_back("hand");
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);
}
}
}
#endif // HAVE_RSSDK
}
RealSense::~RealSense() {
#ifdef HAVE_RSSDK
_manager->Release();
#endif // HAVE_RSSDK
}
void RealSense::initSession(bool playback, QString filename) {
#ifdef HAVE_RSSDK
_active = false;
_properlyInitialized = false;
if (_handData != NULL) {
_handData->Release();
_handController->Release();
_session->Release();
_config->Release();
}
_manager = _session->CreateSenseManager();
if (playback) {
_manager->QueryCaptureManager()->SetFileName(filename.toStdWString().c_str(), false);
}
_manager->QueryCaptureManager()->SetRealtime(!playback);
_manager->EnableHand(0);
_handController = _manager->QueryHand();
if (_manager->Init() == PXC_STATUS_NO_ERROR) {
_handData = _handController->CreateOutput();
PXCCapture::Device *device = _manager->QueryCaptureManager()->QueryDevice();
PXCCapture::DeviceInfo dinfo;
_manager->QueryCaptureManager()->QueryDevice()->QueryDeviceInfo(&dinfo);
if (dinfo.model == PXCCapture::DEVICE_MODEL_IVCAM)
{
device->SetDepthConfidenceThreshold(1);
device->SetMirrorMode(PXCCapture::Device::MIRROR_MODE_DISABLED);
device->SetIVCAMFilterOption(6);
}
_properlyInitialized = true;
}
_config = _handController->CreateActiveConfiguration();
_config->EnableStabilizer(true);
_config->SetTrackingMode(PXCHandData::TRACKING_MODE_FULL_HAND);
_config->ApplyChanges();
#endif // HAVE_RSSDK
}
#ifdef HAVE_RSSDK
glm::quat quatFromPXCPoint4DF32(const PXCPoint4DF32& basis) {
return glm::normalize(glm::quat(basis.w, basis.x, basis.y, basis.z) * glm::quat(glm::vec3(0, M_PI, 0)));
}
glm::vec3 vec3FromPXCPoint3DF32(const PXCPoint3DF32& vec) {
return glm::vec3(-vec.x, vec.y, -vec.z);
}
#endif // HAVE_RSSDK
void RealSense::update() {
#ifdef HAVE_RSSDK
bool wasActive = _active;
_active = _manager->IsConnected() && _properlyInitialized;
if (_active || wasActive) {
// Go through all the joints and increment their counter since last update.
// Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive.
// 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();
}
}
if (!_active) {
return;
}
pxcStatus sts = _manager->AcquireFrame(true);
_handData->Update();
PXCHandData::JointData nodes[2][PXCHandData::NUMBER_OF_JOINTS] = {};
PXCHandData::ExtremityData extremitiesPointsNodes[2][PXCHandData::NUMBER_OF_EXTREMITIES] = {};
for (pxcI32 i = 0; i < _handData->QueryNumberOfHands(); i++) {
PXCHandData::IHand* handData;
if (_handData->QueryHandData(PXCHandData::ACCESS_ORDER_BY_TIME, i, handData) == PXC_STATUS_NO_ERROR) {
int rightSide = handData->QueryBodySide() == PXCHandData::BODY_SIDE_RIGHT;
PXCHandData::JointData jointData;
JointTracker* parentJointTracker = _jointsArray.data();
//Iterate Joints
int rootBranchIndex = -1;
JointTracker* palmJoint = NULL;
for (int j = 0; j < PXCHandData::NUMBER_OF_JOINTS; j++) {
handData->QueryTrackedJoint((PXCHandData::JointType)j, jointData);
nodes[i][j] = jointData;
if (j == PXCHandData::JOINT_WRIST) {
JointTracker* wrist = editJointTracker(evalRealSenseJointIndex(rightSide, rootBranchIndex, 1)); // 1 is the index of the wrist joint
wrist->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
wrist->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
wrist->updateLocFromAbsTransform(parentJointTracker);
wrist->activeFrame();
parentJointTracker = wrist;
continue;
} else if (j == PXCHandData::JOINT_CENTER) {
palmJoint = editJointTracker(evalRealSenseJointIndex(rightSide, rootBranchIndex, 0)); // 0 is the index of the palm joint
palmJoint->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
palmJoint->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
palmJoint->updateLocFromAbsTransform(parentJointTracker);
palmJoint->activeFrame();
parentJointTracker = palmJoint;
continue;
}
int finger_index = j - PALMROOT_NUM_JOINTS;
int finger = finger_index / FINGER_NUM_JOINTS;
int finger_bone = finger_index % FINGER_NUM_JOINTS;
JointTracker* ljointTracker = editJointTracker(evalRealSenseJointIndex(rightSide, finger, finger_bone));
if (jointData.confidence > 0) {
ljointTracker->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
ljointTracker->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
ljointTracker->activeFrame();
}
if (finger_bone == (FINGER_NUM_JOINTS - 1)) {
parentJointTracker = palmJoint;
continue;
}
parentJointTracker = ljointTracker;
}
}
}
_manager->ReleaseFrame();
#endif // HAVE_RSSDK
}
void RealSense::loadRSSDKFile() {
QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation));
QString fileNameString = QFileDialog::getOpenFileName(Application::getInstance()->getWindow(), tr("Open RSSDK clip"),
locationDir,
tr("RSSDK Recordings (*.rssdk)"));
if (!fileNameString.isEmpty()) {
initSession(true, fileNameString);
}
}

View file

@ -0,0 +1,63 @@
//
// RealSense.h
// interface/src/devices
//
// Created by Thijs Wenker on 12/10/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_RealSense_h
#define hifi_RealSense_h
#include <QFileDialog>
#include "MotionTracker.h"
#ifdef HAVE_RSSDK
#include <pxcsession.h>
#include <pxchandmodule.h>
#include <pxchandconfiguration.h>
#include <pxcsensemanager.h>
#include <pxchanddata.h>
#endif
/// Handles interaction with the RealSense skeleton tracking suit.
class RealSense : public QObject, public MotionTracker {
Q_OBJECT
public:
static const Name NAME;
static void init();
/// RealSense MotionTracker factory
static RealSense* getInstance();
bool isActive() const { return _active; }
virtual void update();
void loadRSSDKFile();
protected:
RealSense();
virtual ~RealSense();
void initSession(bool playback, QString filename);
private:
#ifdef HAVE_RSSDK
PXCSession* _session;
PXCSenseManager* _manager;
PXCHandModule* _handController;
PXCHandData* _handData;
PXCHandConfiguration* _config;
#endif
bool _properlyInitialized;
bool _active;
};
#endif // hifi_RealSense_h

View file

@ -25,16 +25,9 @@ QScriptValue LocationScriptingInterface::locationGetter(QScriptContext* context,
QScriptValue LocationScriptingInterface::locationSetter(QScriptContext* context, QScriptEngine* engine) {
const QVariant& argumentVariant = context->argument(0).toVariant();
if (argumentVariant.canConvert(QMetaType::QVariantMap)) {
// this argument is a variant map, so we'll assume it's an address map
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "goToAddressFromObject",
Q_ARG(const QVariantMap&, argumentVariant.toMap()));
} else {
// just try and convert the argument to a string, should be a hifi:// address
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
Q_ARG(const QString&, argumentVariant.toString()));
}
// just try and convert the argument to a string, should be a hifi:// address
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
Q_ARG(const QString&, argumentVariant.toString()));
return QScriptValue::UndefinedValue;
}

View file

@ -33,7 +33,7 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
Qt::WindowCloseButtonHint),
ui(new Ui::RunningScriptsWidget),
_signalMapper(this),
_proxyModel(this),
_scriptsModelFilter(this),
_scriptsModel(this) {
ui->setupUi(this);
@ -41,46 +41,49 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
ui->filterLineEdit->installEventFilter(this);
connect(&_proxyModel, &QSortFilterProxyModel::modelReset,
connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset,
this, &RunningScriptsWidget::selectFirstInList);
QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText);
ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts.");
_proxyModel.setSourceModel(&_scriptsModel);
_proxyModel.sort(0, Qt::AscendingOrder);
_proxyModel.setDynamicSortFilter(true);
ui->scriptListView->setModel(&_proxyModel);
_scriptsModelFilter.setSourceModel(&_scriptsModel);
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
_scriptsModelFilter.setDynamicSortFilter(true);
ui->scriptTreeView->setModel(&_scriptsModelFilter);
connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter);
connect(ui->scriptListView, &QListView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList);
connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList);
connect(ui->reloadAllButton, &QPushButton::clicked,
Application::getInstance(), &Application::reloadAllScripts);
connect(ui->stopAllButton, &QPushButton::clicked,
this, &RunningScriptsWidget::allScriptsStopped);
connect(ui->loadScriptButton, &QPushButton::clicked,
connect(ui->loadScriptFromDiskButton, &QPushButton::clicked,
Application::getInstance(), &Application::loadDialog);
connect(ui->loadScriptFromURLButton, &QPushButton::clicked,
Application::getInstance(), &Application::loadScriptURLDialog);
connect(&_signalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&)));
}
RunningScriptsWidget::~RunningScriptsWidget() {
delete ui;
_scriptsModel.deleteLater();
}
void RunningScriptsWidget::updateFileFilter(const QString& filter) {
QRegExp regex("^.*" + QRegExp::escape(filter) + ".*$", Qt::CaseInsensitive);
_proxyModel.setFilterRegExp(regex);
_scriptsModelFilter.setFilterRegExp(regex);
selectFirstInList();
}
void RunningScriptsWidget::loadScriptFromList(const QModelIndex& index) {
QVariant scriptFile = _proxyModel.data(index, ScriptsModel::ScriptPath);
QVariant scriptFile = _scriptsModelFilter.data(index, ScriptsModel::ScriptPath);
Application::getInstance()->loadScript(scriptFile.toString());
}
void RunningScriptsWidget::loadSelectedScript() {
QModelIndex selectedIndex = ui->scriptListView->currentIndex();
QModelIndex selectedIndex = ui->scriptTreeView->currentIndex();
if (selectedIndex.isValid()) {
loadScriptFromList(selectedIndex);
}
@ -165,8 +168,8 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) {
}
void RunningScriptsWidget::selectFirstInList() {
if (_proxyModel.rowCount() > 0) {
ui->scriptListView->setCurrentIndex(_proxyModel.index(0, 0));
if (_scriptsModelFilter.rowCount() > 0) {
ui->scriptTreeView->setCurrentIndex(_scriptsModelFilter.index(0, 0));
}
}
@ -177,7 +180,7 @@ bool RunningScriptsWidget::eventFilter(QObject* sender, QEvent* event) {
}
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
QModelIndex selectedIndex = ui->scriptListView->currentIndex();
QModelIndex selectedIndex = ui->scriptTreeView->currentIndex();
if (selectedIndex.isValid()) {
loadScriptFromList(selectedIndex);
}

View file

@ -18,6 +18,7 @@
#include <QSortFilterProxyModel>
#include "ScriptsModel.h"
#include "ScriptsModelFilter.h"
#include "ScriptsTableWidget.h"
namespace Ui {
@ -54,7 +55,7 @@ private slots:
private:
Ui::RunningScriptsWidget* ui;
QSignalMapper _signalMapper;
QSortFilterProxyModel _proxyModel;
ScriptsModelFilter _scriptsModelFilter;
ScriptsModel _scriptsModel;
ScriptsTableWidget* _recentlyLoadedScriptsTable;
QStringList _recentlyLoadedScripts;

View file

@ -41,7 +41,9 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
_rotation(base3DOverlay->_rotation),
_isSolid(base3DOverlay->_isSolid),
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection)
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
_drawInFront(base3DOverlay->_drawInFront),
_drawOnHUD(base3DOverlay->_drawOnHUD)
{
}

View file

@ -238,8 +238,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render()
QFontMetrics fontMetrics(font);
const float TEXT_SCALE_ADJUST = 1.02f; // Experimentally detemined for the specified font
const int TEXT_HEIGHT_ADJUST = -6;
const float TEXT_SCALE_ADJUST = 1.025f; // Experimentally detemined for the specified font
const int TEXT_HEIGHT_ADJUST = -10;
float scaleFactor = _lineHeight * TEXT_SCALE_ADJUST * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE;
QStringList lines = text.split(QRegExp("\r\n|\r|\n"));

View file

@ -245,8 +245,8 @@ font: bold 16px;
<rect>
<x>0</x>
<y>0</y>
<width>328</width>
<height>18</height>
<width>334</width>
<height>20</height>
</rect>
</property>
<property name="sizePolicy">
@ -373,7 +373,7 @@ font: bold 16px;
font: bold 16px;</string>
</property>
<property name="text">
<string>Scripts</string>
<string>Load Scripts</string>
</property>
</widget>
</item>
@ -391,9 +391,16 @@ font: bold 16px;</string>
</spacer>
</item>
<item>
<widget class="QPushButton" name="loadScriptButton">
<widget class="QPushButton" name="loadScriptFromURLButton">
<property name="text">
<string>Load script</string>
<string>from URL</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loadScriptFromDiskButton">
<property name="text">
<string>from Disk</string>
</property>
</widget>
</item>
@ -429,7 +436,7 @@ font: bold 16px;</string>
</widget>
</item>
<item>
<widget class="QListView" name="scriptListView">
<widget class="QTreeView" name="scriptTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@ -442,6 +449,12 @@ font: bold 16px;</string>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="rootIsDecorated">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>

View file

@ -691,15 +691,7 @@ void EntityItem::simulate(const quint64& now) {
}
}
#ifdef USE_BULLET_PHYSICS
// When Bullet is available we assume that "zero velocity" means "at rest"
// because of collision conditions this simulation does not know about
// so we don't fall in for the non-zero gravity case here.
if (hasVelocity()) {
#else // !USE_BULLET_PHYSICS
if (hasVelocity() || hasGravity()) {
#endif // USE_BULLET_PHYSICS
// linear damping
glm::vec3 velocity = getVelocity();
if (_damping > 0.0f) {
@ -733,14 +725,6 @@ void EntityItem::simulate(const quint64& now) {
// handle bounces off the ground... We bounce at the distance to the bottom of our entity
if (position.y <= getDistanceToBottomOfEntity()) {
velocity = velocity * glm::vec3(1,-1,1);
#ifndef USE_BULLET_PHYSICS
// if we've slowed considerably, then just stop moving, but only if no BULLET
if (glm::length(velocity) <= ENTITY_ITEM_EPSILON_VELOCITY_LENGTH) {
velocity = ENTITY_ITEM_ZERO_VEC3;
}
#endif // !USE_BULLET_PHYSICS
position.y = getDistanceToBottomOfEntity();
}
@ -756,14 +740,8 @@ void EntityItem::simulate(const quint64& now) {
}
}
#ifdef USE_BULLET_PHYSICS
// When Bullet is available we assume that it will tell us when velocities go to zero...
#else // !USE_BULLET_PHYSICS
// ... otherwise we help things come to rest by clamping small velocities.
if (glm::length(velocity) <= ENTITY_ITEM_EPSILON_VELOCITY_LENGTH) {
velocity = ENTITY_ITEM_ZERO_VEC3;
}
#endif // USE_BULLET_PHYSICS
// NOTE: we don't zero out very small velocities --> we rely on a remote Bullet simulation
// to tell us when the entity has stopped.
// NOTE: the simulation should NOT set any DirtyFlags on this entity
setPosition(position); // this will automatically recalculate our collision shape
@ -780,14 +758,56 @@ void EntityItem::simulate(const quint64& now) {
_lastSimulated = now;
}
void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) {
if (hasAngularVelocity()) {
// angular damping
glm::vec3 angularVelocity = getAngularVelocity();
if (_angularDamping > 0.0f) {
angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
setAngularVelocity(angularVelocity);
}
float angularSpeed = glm::length(_angularVelocity);
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; //
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
setAngularVelocity(ENTITY_ITEM_ZERO_VEC3);
} else {
// NOTE: angularSpeed is currently in degrees/sec!!!
// TODO: Andrew to convert to radians/sec
float angle = timeElapsed * glm::radians(angularSpeed);
glm::vec3 axis = _angularVelocity / angularSpeed;
glm::quat dQ = glm::angleAxis(angle, axis);
glm::quat rotation = getRotation();
rotation = glm::normalize(dQ * rotation);
setRotation(rotation);
}
}
if (hasVelocity()) {
// linear damping
glm::vec3 velocity = getVelocity();
if (_damping > 0.0f) {
velocity *= powf(1.0f - _damping, timeElapsed);
}
// integrate position forward
glm::vec3 position = getPosition() + (velocity * timeElapsed);
// apply gravity
if (hasGravity()) {
// handle resting on surface case, this is definitely a bit of a hack, and it only works on the
// "ground" plane of the domain, but for now it's what we've got
velocity += getGravity() * timeElapsed;
}
// NOTE: the simulation should NOT set any DirtyFlags on this entity
setPosition(position); // this will automatically recalculate our collision shape
setVelocity(velocity);
}
}
bool EntityItem::isMoving() const {
#ifdef USE_BULLET_PHYSICS
// When Bullet is available we assume that "zero velocity" means "at rest"
// because of collision conditions this simulation does not know about.
return hasVelocity() || hasAngularVelocity();
#else // !USE_BULLET_PHYSICS
return hasVelocity() || (hasGravity() && !isRestingOnSurface()) || hasAngularVelocity();
#endif //USE_BULLET_PHYSICS
}
bool EntityItem::lifetimeHasExpired() const {

View file

@ -128,6 +128,8 @@ public:
// perform linear extrapolation for SimpleEntitySimulation
void simulate(const quint64& now);
void simulateSimpleKinematicMotion(float timeElapsed);
virtual bool needsToCallUpdate() const { return false; }

View file

@ -496,13 +496,16 @@ void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) {
#if defined(Q_OS_MAC)
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
#else
// NOT working so we ll stick to the uniform float array until we move to core profile
// GLuint bo = getBufferID(*uniformBuffer);
//glUniformBufferEXT(_shader._program, slot, bo);
#elif defined(Q_OS_WIN)
GLuint bo = getBufferID(*uniformBuffer);
glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize);
// glUniformBufferEXT(_shader._program, slot, bo);
//glBindBufferBase(GL_UNIFORM_BUFFER, slot, bo);
#else
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
#endif
CHECK_GL_ERROR();
}

View file

@ -22,7 +22,8 @@
#include "AddressManager.h"
AddressManager::AddressManager() :
_currentDomain(),
_rootPlaceName(),
_rootPlaceID(),
_positionGetter(NULL),
_orientationGetter(NULL)
{
@ -37,7 +38,7 @@ const QUrl AddressManager::currentAddress() const {
QUrl hifiURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
hifiURL.setHost(_currentDomain);
hifiURL.setHost(_rootPlaceName);
hifiURL.setPath(currentPath());
return hifiURL;
@ -88,11 +89,6 @@ const QString AddressManager::currentPath(bool withOrientation) const {
}
}
QString AddressManager::getDomainID() const {
const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
return domainID.isNull() ? "" : uuidStringWithoutCurlyBraces(domainID);
}
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
static bool hasSetupParameters = false;
static JSONCallbackParameters callbackParams;
@ -173,67 +169,83 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
emit lookupResultsFinished();
}
void AddressManager::goToAddressFromObject(const QVariantMap& addressMap) {
const QString ADDRESS_API_DOMAIN_KEY = "domain";
const QString ADDRESS_API_ONLINE_KEY = "online";
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
if (!addressMap.contains(ADDRESS_API_ONLINE_KEY)
|| addressMap[ADDRESS_API_ONLINE_KEY].toBool()) {
const QString DATA_OBJECT_PLACE_KEY = "place";
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
QVariantMap locationMap;
if (dataObject.contains(DATA_OBJECT_PLACE_KEY)) {
locationMap = dataObject[DATA_OBJECT_PLACE_KEY].toMap();
} else {
locationMap = dataObject[DATA_OBJECT_USER_LOCATION_KEY].toMap();
}
if (!locationMap.isEmpty()) {
const QString LOCATION_API_ROOT_KEY = "root";
const QString LOCATION_API_DOMAIN_KEY = "domain";
const QString LOCATION_API_ONLINE_KEY = "online";
if (addressMap.contains(ADDRESS_API_DOMAIN_KEY)) {
QVariantMap domainObject = addressMap[ADDRESS_API_DOMAIN_KEY].toMap();
if (!locationMap.contains(LOCATION_API_ONLINE_KEY)
|| locationMap[LOCATION_API_ONLINE_KEY].toBool()) {
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
emit possibleDomainChangeRequired(domainHostname, DEFAULT_DOMAIN_SERVER_PORT);
} else {
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
const QString DOMAIN_ID_KEY = "id";
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
QUuid domainID(domainIDString);
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
QVariantMap rootMap = locationMap[LOCATION_API_ROOT_KEY].toMap();
if (rootMap.isEmpty()) {
rootMap = locationMap;
}
// set our current domain to the name that came back
const QString DOMAIN_NAME_KEY = "name";
QVariantMap domainObject = rootMap[LOCATION_API_DOMAIN_KEY].toMap();
_currentDomain = domainObject[DOMAIN_NAME_KEY].toString();
// take the path that came back
const QString LOCATION_KEY = "location";
const QString LOCATION_PATH_KEY = "path";
QString returnedPath;
if (domainObject.contains(LOCATION_PATH_KEY)) {
returnedPath = domainObject[LOCATION_PATH_KEY].toString();
} else if (domainObject.contains(LOCATION_KEY)) {
returnedPath = domainObject[LOCATION_KEY].toMap()[LOCATION_PATH_KEY].toString();
} else if (addressMap.contains(LOCATION_PATH_KEY)) {
returnedPath = addressMap[LOCATION_PATH_KEY].toString();
}
bool shouldFaceViewpoint = addressMap.contains(ADDRESS_API_ONLINE_KEY);
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
if (!domainObject.isEmpty()) {
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
emit possibleDomainChangeRequired(domainHostname, DEFAULT_DOMAIN_SERVER_PORT);
} else {
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
const QString DOMAIN_ID_KEY = "id";
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
QUuid domainID(domainIDString);
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
}
// set our current root place id to the ID that came back
const QString PLACE_ID_KEY = "id";
_rootPlaceID = rootMap[PLACE_ID_KEY].toUuid();
// set our current root place name to the name that came back
const QString PLACE_NAME_KEY = "name";
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
setRootPlaceName(newRootPlaceName);
// take the path that came back
const QString PLACE_PATH_KEY = "path";
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
}
}
} else {
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
qDebug() << locationMap;
}
} else {
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
qDebug() << addressMap;
// we've been told that this result exists but is offline, emit our signal so the application can handle
emit lookupResultIsOffline();
}
} else {
// we've been told that this result exists but is offline, emit our signal so the application can handle
emit lookupResultIsOffline();
qDebug() << "Received an address manager API response with no location key or place key. Cannot parse.";
qDebug() << locationMap;
}
}
@ -365,9 +377,20 @@ bool AddressManager::handleUsername(const QString& lookupString) {
return false;
}
void AddressManager::setRootPlaceName(const QString& rootPlaceName) {
if (rootPlaceName != _rootPlaceName) {
_rootPlaceName = rootPlaceName;
emit rootPlaceNameChanged(_rootPlaceName);
}
}
void AddressManager::setDomainInfo(const QString& hostname, quint16 port, const QString& domainName) {
_currentDomain = domainName.isEmpty() ? hostname : domainName;
void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
_rootPlaceName = hostname;
_rootPlaceID = QUuid();
qDebug() << "Possible domain change required to connect to domain at" << hostname << "on" << port;
emit possibleDomainChangeRequired(hostname, port);
}

View file

@ -33,9 +33,8 @@ class AddressManager : public QObject, public Dependency {
Q_PROPERTY(bool isConnected READ isConnected)
Q_PROPERTY(QUrl href READ currentAddress)
Q_PROPERTY(QString protocol READ getProtocol)
Q_PROPERTY(QString hostname READ getCurrentDomain)
Q_PROPERTY(QString hostname READ getRootPlaceName)
Q_PROPERTY(QString pathname READ currentPath)
Q_PROPERTY(QString domainID READ getDomainID)
public:
bool isConnected();
const QString& getProtocol() { return HIFI_URL_SCHEME; };
@ -43,8 +42,10 @@ public:
const QUrl currentAddress() const;
const QString currentPath(bool withOrientation = true) const;
const QString& getCurrentDomain() const { return _currentDomain; }
QString getDomainID() const;
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
const QString& getRootPlaceName() const { return _rootPlaceName; }
void setRootPlaceName(const QString& rootPlaceName);
void attemptPlaceNameLookup(const QString& lookupString);
@ -69,13 +70,14 @@ signals:
void locationChangeRequired(const glm::vec3& newPosition,
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
void rootPlaceNameChanged(const QString& newRootPlaceName);
protected:
AddressManager();
private slots:
void handleAPIResponse(QNetworkReply& requestReply);
void handleAPIError(QNetworkReply& errorReply);
private:
void setDomainInfo(const QString& hostname, quint16 port, const QString& domainName = QString());
void setDomainInfo(const QString& hostname, quint16 port);
const JSONCallbackParameters& apiCallbackParameters();
@ -85,7 +87,8 @@ private:
bool handleRelativeViewpoint(const QString& pathSubsection, bool shouldFace = false);
bool handleUsername(const QString& lookupString);
QString _currentDomain;
QString _rootPlaceName;
QUuid _rootPlaceID;
PositionGetter _positionGetter;
OrientationGetter _orientationGetter;
};

View file

@ -41,6 +41,7 @@ const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io");
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
_sessionUUID(),
_nodeHash(),
_nodeMutex(QReadWriteLock::Recursive),
_nodeSocket(this),
_dtlsSocket(NULL),
_localSockAddr(),

View file

@ -12,8 +12,6 @@
#ifndef hifi_BulletUtil_h
#define hifi_BulletUtil_h
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -34,5 +32,4 @@ inline btQuaternion glmToBullet(const glm::quat& g) {
return btQuaternion(g.x, g.y, g.z, g.w);
}
#endif // USE_BULLET_PHYSICS
#endif // hifi_BulletUtil_h

View file

@ -12,10 +12,10 @@
#include <EntityItem.h>
#include <EntityEditPacketSender.h>
#ifdef USE_BULLET_PHYSICS
#include "BulletUtil.h"
#endif // USE_BULLET_PHYSICS
#include "EntityMotionState.h"
#include "SimpleEntityKinematicController.h"
QSet<EntityItem*>* _outgoingEntityList;
@ -40,22 +40,35 @@ EntityMotionState::~EntityMotionState() {
assert(_entity);
_entity->setPhysicsInfo(NULL);
_entity = NULL;
delete _kinematicController;
_kinematicController = NULL;
}
MotionType EntityMotionState::computeMotionType() const {
// HACK: According to EntityTree the meaning of "static" is "not moving" whereas
// to Bullet it means "can't move". For demo purposes we temporarily interpret
// Entity::weightless to mean Bullet::static.
return _entity->getCollisionsWillMove() ? MOTION_TYPE_DYNAMIC : MOTION_TYPE_STATIC;
if (_entity->getCollisionsWillMove()) {
return MOTION_TYPE_DYNAMIC;
}
return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC;
}
void EntityMotionState::addKinematicController() {
if (!_kinematicController) {
_kinematicController = new SimpleEntityKinematicController(_entity);
_kinematicController->start();
} else {
_kinematicController->start();
}
}
#ifdef USE_BULLET_PHYSICS
// This callback is invoked by the physics simulation in two cases:
// (1) when the RigidBody is first added to the world
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
// (2) at the beginning of each simulation frame for KINEMATIC RigidBody's --
// it is an opportunity for outside code to update the object's simulation position
void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
if (_kinematicController && _kinematicController->isRunning()) {
_kinematicController->stepForward();
}
worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset()));
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
}
@ -77,10 +90,8 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
EntityMotionState::enqueueOutgoingEntity(_entity);
}
#endif // USE_BULLET_PHYSICS
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
#ifdef USE_BULLET_PHYSICS
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
if (flags & EntityItem::DIRTY_POSITION) {
_sentPosition = _entity->getPositionInMeters() - ObjectMotionState::getWorldOffset();
@ -116,11 +127,9 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
_body->updateInertiaTensor();
}
_body->activate();
#endif // USE_BULLET_PHYSICS
};
void EntityMotionState::updateObjectVelocities() {
#ifdef USE_BULLET_PHYSICS
if (_body) {
_sentVelocity = _entity->getVelocityInMeters();
setVelocity(_sentVelocity);
@ -134,7 +143,6 @@ void EntityMotionState::updateObjectVelocities() {
_body->setActivationState(ACTIVE_TAG);
}
#endif // USE_BULLET_PHYSICS
}
void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) {
@ -146,7 +154,9 @@ float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const {
}
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) {
#ifdef USE_BULLET_PHYSICS
if (!_entity->isKnownID()) {
return; // never update entities that are unknown
}
if (_outgoingPacketFlags) {
EntityItemProperties properties = _entity->getProperties();
@ -213,5 +223,17 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
_sentFrame = frame;
}
#endif // USE_BULLET_PHYSICS
}
uint32_t EntityMotionState::getIncomingDirtyFlags() const {
uint32_t dirtyFlags = _entity->getDirtyFlags();
// we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings
int bodyFlags = _body->getCollisionFlags();
bool isMoving = _entity->isMoving();
if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) ||
(bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) {
dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
}
return dirtyFlags;
}

View file

@ -14,15 +14,8 @@
#include <AACube.h>
#include "KinematicController.h"
#include "ObjectMotionState.h"
#ifndef USE_BULLET_PHYSICS
// ObjectMotionState stubbery
class ObjectMotionState {
public:
// so that this stub implementation is not completely empty we give the class a data member
bool _stubData;
};
#endif // USE_BULLET_PHYSICS
class EntityItem;
@ -39,19 +32,21 @@ public:
static void setOutgoingEntityList(QSet<EntityItem*>* list);
static void enqueueOutgoingEntity(EntityItem* entity);
EntityMotionState() = delete; // prevent compiler from making default ctor
EntityMotionState(EntityItem* item);
virtual ~EntityMotionState();
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
MotionType computeMotionType() const;
#ifdef USE_BULLET_PHYSICS
// virtual override for ObjectMotionState
void addKinematicController();
// this relays incoming position/rotation to the RigidBody
void getWorldTransform(btTransform& worldTrans) const;
// this relays outgoing position/rotation to the EntityItem
void setWorldTransform(const btTransform& worldTrans);
#endif // USE_BULLET_PHYSICS
// these relay incoming values to the RigidBody
void updateObjectEasy(uint32_t flags, uint32_t frame);
@ -62,7 +57,7 @@ public:
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame);
uint32_t getIncomingDirtyFlags() const { return _entity->getDirtyFlags(); }
uint32_t getIncomingDirtyFlags() const;
void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
protected:

View file

@ -0,0 +1,22 @@
//
// KinematicController.cpp
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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 "KinematicController.h"
#include "PhysicsEngine.h"
KinematicController::KinematicController() {
_lastFrame = PhysicsEngine::getFrameCount();
}
void KinematicController::start() {
_enabled = true;
_lastFrame = PhysicsEngine::getFrameCount();
}

View file

@ -0,0 +1,36 @@
//
// KinematicController.h
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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_KinematicController_h
#define hifi_KinematicController_h
#include <stdint.h>
/// KinematicController defines an API for derived classes.
class KinematicController {
public:
KinematicController();
virtual ~KinematicController() {}
virtual void stepForward() = 0;
void start();
void stop() { _enabled = false; }
bool isRunning() const { return _enabled; }
protected:
bool _enabled = false;
uint32_t _lastFrame;
};
#endif // hifi_KinematicController_h

View file

@ -9,20 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifdef USE_BULLET_PHYSICS
#include <math.h>
#include "BulletUtil.h"
#include "KinematicController.h"
#include "ObjectMotionState.h"
const float MIN_DENSITY = 200.0f;
const float DEFAULT_DENSITY = 1000.0f;
const float MAX_DENSITY = 20000.0f;
const float MIN_VOLUME = 0.001f;
const float DEFAULT_VOLUME = 1.0f;
const float MAX_VOLUME = 1000000.0f;
#include "PhysicsEngine.h"
const float DEFAULT_FRICTION = 0.5f;
const float MAX_FRICTION = 10.0f;
@ -64,6 +56,10 @@ ObjectMotionState::ObjectMotionState() :
ObjectMotionState::~ObjectMotionState() {
// NOTE: you MUST remove this MotionState from the world before you call the dtor.
assert(_body == NULL);
if (_kinematicController) {
delete _kinematicController;
_kinematicController = NULL;
}
}
void ObjectMotionState::setFriction(float friction) {
@ -110,30 +106,23 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const {
return !_body->isActive() && _numNonMovingUpdates > MAX_NUM_NON_MOVING_UPDATES;
}
const float FIXED_SUBSTEP = 1.0f / 60.0f;
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
assert(_body);
float dt = (float)(simulationFrame - _sentFrame) * FIXED_SUBSTEP;
float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP;
_sentFrame = simulationFrame;
bool isActive = _body->isActive();
if (isActive) {
const float MAX_UPDATE_PERIOD_FOR_ACTIVE_THINGS = 10.0f;
if (dt > MAX_UPDATE_PERIOD_FOR_ACTIVE_THINGS) {
return true;
}
} else if (_sentMoving) {
if (!isActive) {
if (!isActive) {
if (_sentMoving) {
// this object just went inactive so send an update immediately
return true;
}
} else {
const float NON_MOVING_UPDATE_PERIOD = 1.0f;
if (dt > NON_MOVING_UPDATE_PERIOD && _numNonMovingUpdates < MAX_NUM_NON_MOVING_UPDATES) {
// RELIABLE_SEND_HACK: since we're not yet using a reliable method for non-moving update packets we repeat these
// at a faster rate than the MAX period above, and only send a limited number of them.
return true;
} else {
const float NON_MOVING_UPDATE_PERIOD = 1.0f;
if (dt > NON_MOVING_UPDATE_PERIOD && _numNonMovingUpdates < MAX_NUM_NON_MOVING_UPDATES) {
// RELIABLE_SEND_HACK: since we're not yet using a reliable method for non-moving update packets we repeat these
// at a faster rate than the MAX period above, and only send a limited number of them.
return true;
}
}
}
@ -175,4 +164,9 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
return (fabsf(glm::dot(actualRotation, _sentRotation)) < MIN_ROTATION_DOT);
}
#endif // USE_BULLET_PHYSICS
void ObjectMotionState::removeKinematicController() {
if (_kinematicController) {
delete _kinematicController;
_kinematicController = NULL;
}
}

View file

@ -12,8 +12,13 @@
#ifndef hifi_ObjectMotionState_h
#define hifi_ObjectMotionState_h
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <EntityItem.h>
#include "ShapeInfo.h"
enum MotionType {
MOTION_TYPE_STATIC, // no motion
MOTION_TYPE_DYNAMIC, // motion according to physical laws
@ -22,7 +27,6 @@ enum MotionType {
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
// and re-added to the physics engine and "easy" which just updates the body properties.
typedef unsigned int uint32_t;
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY |
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP);
@ -33,15 +37,9 @@ const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSI
// These are the outgoing flags that the PhysicsEngine can affect:
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY;
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <EntityItem.h> // for EntityItem::DIRTY_FOO bitmasks
#include "ShapeInfo.h"
class OctreeEditPacketSender;
class KinematicController;
class ObjectMotionState : public btMotionState {
public:
@ -87,6 +85,9 @@ public:
virtual MotionType computeMotionType() const = 0;
virtual void addKinematicController() = 0;
virtual void removeKinematicController();
friend class PhysicsEngine;
protected:
// TODO: move these materials properties to EntityItem
@ -110,7 +111,8 @@ protected:
glm::vec3 _sentVelocity;
glm::vec3 _sentAngularVelocity; // radians per second
glm::vec3 _sentAcceleration;
KinematicController* _kinematicController = NULL;
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ObjectMotionState_h

View file

@ -10,12 +10,15 @@
//
#include "PhysicsEngine.h"
#ifdef USE_BULLET_PHYSICS
#include "ShapeInfoUtil.h"
#include "ThreadSafeDynamicsWorld.h"
class EntityTree;
static uint32_t _frameCount;
// static
uint32_t PhysicsEngine::getFrameCount() {
return _frameCount;
}
PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
: _collisionConfig(NULL),
@ -24,8 +27,7 @@ PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
_constraintSolver(NULL),
_dynamicsWorld(NULL),
_originOffset(offset),
_entityPacketSender(NULL),
_frameCount(0) {
_entityPacketSender(NULL) {
}
PhysicsEngine::~PhysicsEngine() {
@ -196,8 +198,6 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
EntityMotionState::setOutgoingEntityList(&_entitiesToBeSorted);
}
const float FIXED_SUBSTEP = 1.0f / 60.0f;
void PhysicsEngine::stepSimulation() {
lock();
// NOTE: the grand order of operations is:
@ -210,13 +210,13 @@ void PhysicsEngine::stepSimulation() {
relayIncomingChangesToSimulation();
const int MAX_NUM_SUBSTEPS = 4;
const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * FIXED_SUBSTEP;
const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP;
float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds());
_clock.reset();
float timeStep = btMin(dt, MAX_TIMESTEP);
// This is step (2).
int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, FIXED_SUBSTEP);
int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
_frameCount += (uint32_t)numSubSteps;
unlock();
@ -257,9 +257,12 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
body->setActivationState(DISABLE_DEACTIVATION);
body->updateInertiaTensor();
motionState->_body = body;
motionState->addKinematicController();
const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec
const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec
body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD);
break;
}
case MOTION_TYPE_DYNAMIC: {
@ -271,9 +274,9 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
motionState->updateObjectVelocities();
// NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds.
// (the 2 seconds is determined by: static btRigidBody::gDeactivationTime
const float LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
const float ANGULAR_VELOCITY_THRESHOLD = 0.087266f; // ~5 deg/sec
body->setSleepingThresholds(LINEAR_VELOCITY_THRESHOLD, ANGULAR_VELOCITY_THRESHOLD);
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
const float DYNAMIC_ANGULAR_VELOCITY_THRESHOLD = 0.087266f; // ~5 deg/sec
body->setSleepingThresholds(DYNAMIC_LINEAR_VELOCITY_THRESHOLD, DYNAMIC_ANGULAR_VELOCITY_THRESHOLD);
break;
}
case MOTION_TYPE_STATIC:
@ -307,6 +310,7 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
_shapeManager.releaseShape(shapeInfo);
delete body;
motionState->_body = NULL;
motionState->removeKinematicController();
return true;
}
return false;
@ -361,6 +365,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f));
body->updateInertiaTensor();
motionState->addKinematicController();
break;
}
case MOTION_TYPE_DYNAMIC: {
@ -377,6 +382,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->updateInertiaTensor();
}
body->forceActivationState(ACTIVE_TAG);
motionState->removeKinematicController();
break;
}
default: {
@ -391,6 +397,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
motionState->removeKinematicController();
break;
}
}
@ -400,5 +407,3 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->activate();
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,9 +12,9 @@
#ifndef hifi_PhysicsEngine_h
#define hifi_PhysicsEngine_h
typedef unsigned int uint32_t;
#include <stdint.h>
#ifdef USE_BULLET_PHYSICS
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f;
#include <QSet>
#include <btBulletDynamicsCommon.h>
@ -29,8 +29,11 @@ typedef unsigned int uint32_t;
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
class ObjectMotionState;
class PhysicsEngine : public EntitySimulation {
public:
static uint32_t getFrameCount();
PhysicsEngine(const glm::vec3& offset);
@ -68,9 +71,6 @@ public:
/// \return duration of fixed simulation substep
float getFixedSubStep() const;
/// \return number of simulation frames the physics engine has taken
uint32_t getFrameCount() const { return _frameCount; }
protected:
void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
@ -92,13 +92,6 @@ private:
QSet<ObjectMotionState*> _outgoingPackets; // MotionStates with pending changes that need to be sent over wire
EntityEditPacketSender* _entityPacketSender;
uint32_t _frameCount;
};
#else // USE_BULLET_PHYSICS
// PhysicsEngine stubbery until Bullet is required
class PhysicsEngine {
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_PhysicsEngine_h

View file

@ -21,10 +21,6 @@
#include <CollisionInfo.h>
#include <RayIntersectionInfo.h>
#ifdef USE_BULLET_PHYSICS
#include "PhysicsEngine.h"
#endif // USE_BULLET_PHYSICS
class Shape;
class PhysicsSimulation;

View file

@ -15,9 +15,6 @@
#include "ShapeInfoUtil.h"
#include "BulletUtil.h"
#ifdef USE_BULLET_PHYSICS
int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
int bulletShapeType = INVALID_SHAPE_PROXYTYPE;
switch(shapeInfoType) {
@ -168,5 +165,3 @@ DoubleHashKey ShapeInfoUtil::computeHash(const ShapeInfo& info) {
key._hash2 = (int)hash;
return key;
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,8 +12,6 @@
#ifndef hifi_ShapeInfoUtil_h
#define hifi_ShapeInfoUtil_h
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
@ -35,5 +33,4 @@ namespace ShapeInfoUtil {
int fromBulletShapeType(int bulletShapeType);
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ShapeInfoUtil_h

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifdef USE_BULLET_PHYSICS
#include <glm/gtx/norm.hpp>
#include "ShapeInfoUtil.h"
@ -104,6 +102,3 @@ int ShapeManager::getNumReferences(const ShapeInfo& info) const {
}
return -1;
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,8 +12,6 @@
#ifndef hifi_ShapeManager_h
#define hifi_ShapeManager_h
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <LinearMath/btHashMap.h>
@ -52,5 +50,4 @@ private:
btAlignedObjectArray<DoubleHashKey> _pendingGarbage;
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ShapeManager_h

View file

@ -0,0 +1,21 @@
//
// SimpleEntityKinematicController.cpp
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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 "PhysicsEngine.h"
#include "SimpleEntityKinematicController.h"
void SimpleEntityKinematicController:: stepForward() {
uint32_t frame = PhysicsEngine::getFrameCount();
float dt = (frame - _lastFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP;
_entity->simulateSimpleKinematicMotion(dt);
_lastFrame = frame;
}

View file

@ -0,0 +1,38 @@
//
// SimpleEntityKinematicController.h
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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_SimpleEntityKinematicController_h
#define hifi_SimpleEntityKinematicController_h
/// SimpleKinematicConstroller performs simple exrapolation of velocities.
#include <assert.h>
#include <glm/glm.hpp>
#include <EntityItem.h>
#include "KinematicController.h"
class SimpleEntityKinematicController : public KinematicController {
public:
SimpleEntityKinematicController() = delete; // prevent compiler from making a default ctor
SimpleEntityKinematicController(EntityItem* entity) : KinematicController(), _entity(entity) { assert(entity); }
~SimpleEntityKinematicController() { _entity = NULL; }
void stepForward();
private:
EntityItem* _entity;
};
#endif // hifi_SimpleEntityKinematicController_h

View file

@ -17,7 +17,6 @@
#include "ThreadSafeDynamicsWorld.h"
#ifdef USE_BULLET_PHYSICS
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
btDispatcher* dispatcher,
btBroadphaseInterface* pairCache,
@ -82,4 +81,3 @@ int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps,
return subSteps;
}
#endif // USE_BULLET_PHYSICS

View file

@ -18,7 +18,6 @@
#ifndef hifi_ThreadSafeDynamicsWorld_h
#define hifi_ThreadSafeDynamicsWorld_h
#ifdef USE_BULLET_PHYSICS
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld {
@ -40,13 +39,4 @@ public:
float getLocalTimeAccumulation() const { return m_localTime; }
};
#else // USE_BULLET_PHYSICS
// stubbery for ThreadSafeDynamicsWorld when Bullet not available
class ThreadSafeDynamicsWorld {
public:
ThreadSafeDynamicsWorld() {}
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ThreadSafeDynamicsWorld_h

View file

@ -11,7 +11,6 @@
<@if not DEFERRED_BUFFER_SLH@>
<@def DEFERRED_BUFFER_SLH@>
// the diffuse texture
uniform sampler2D diffuseMap;
@ -43,6 +42,10 @@ struct DeferredFragment {
vec4 specularVal;
vec4 position;
vec3 normal;
vec3 diffuse;
float opacity;
vec3 specular;
float gloss;
};
DeferredFragment unpackDeferredFragment(vec2 texcoord) {
@ -59,6 +62,11 @@ DeferredFragment unpackDeferredFragment(vec2 texcoord) {
// Unpack the normal from the map
frag.normal = normalize(frag.normalVal.xyz * 2.0 - vec3(1.0));
frag.diffuse = frag.diffuseVal.xyz;
frag.opacity = frag.diffuseVal.w;
frag.specular = frag.specularVal.xyz;
frag.gloss = frag.specularVal.w;
return frag;
}

View file

@ -0,0 +1,43 @@
<!
// DeferredBufferWrite.slh
// libraries/render-utils/src
//
// Created by Sam Gateau on 1/12/15.
// Copyright 2013 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
!>
<@if not DEFERRED_BUFFER_WRITE_SLH@>
<@def DEFERRED_BUFFER_WRITE_SLH@>
// the glow intensity
uniform float glowIntensity;
// the alpha threshold
uniform float alphaThreshold;
float evalOpaqueFinalAlpha(float alpha, float mapAlpha) {
return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold));
}
void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
gl_FragData[0] = vec4(diffuse.rgb, alpha);
gl_FragData[1] = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(specular, shininess / 128.0);
}
void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) {
gl_FragData[0] = vec4(diffuse.rgb, alpha);
//gl_FragData[1] = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[1] = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5);
gl_FragData[2] = vec4(emissive, shininess / 128.0);
}
void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
gl_FragData[0] = vec4(diffuse.rgb, alpha);
// gl_FragData[1] = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
// gl_FragData[2] = vec4(specular, shininess / 128.0);
}
<@endif@>

View file

@ -0,0 +1,96 @@
<!
// DeferredLighting.slh
// libraries/render-utils/src
//
// Created by Sam Gateau on 1/15/15.
// Copyright 2013 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
!>
<@if not DEFERRED_LIGHTING_SLH@>
<@def DEFERRED_LIGHTING_SLH@>
struct SphericalHarmonics {
vec4 L00;
vec4 L1m1;
vec4 L10;
vec4 L11;
vec4 L2m2;
vec4 L2m1;
vec4 L20;
vec4 L21;
vec4 L22;
};
vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) {
const float C1 = 0.429043;
const float C2 = 0.511664;
const float C3 = 0.743125;
const float C4 = 0.886227;
const float C5 = 0.247708;
vec4 value = C1 * sh.L22 * (direction.x * direction.x - direction.y * direction.y) +
C3 * sh.L20 * direction.z * direction.z +
C4 * sh.L00 - C5 * sh.L20 +
2.0 * C1 * ( sh.L2m2 * direction.x * direction.y +
sh.L21 * direction.x * direction.z +
sh.L2m1 * direction.y * direction.z ) +
2.0 * C2 * ( sh.L11 * direction.x +
sh.L1m1 * direction.y +
sh.L10 * direction.z ) ;
return value;
}
uniform SphericalHarmonics ambientSphere;
vec3 evalAmbientColor(vec3 normal, vec3 diffuse, vec3 specular, float gloss) {
return diffuse.rgb * gl_FrontLightProduct[0].ambient.rgb;
}
vec3 evalAmbientSphereColor(vec3 normal, vec3 diffuse, vec3 specular, float gloss) {
vec3 ambientLight = 0.5 * evalSphericalLight(ambientSphere, normal).xyz;
return diffuse.rgb * ambientLight;
}
vec3 evalDirectionalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) {
// Diffuse Lighting
float diffuseDot = dot(normal, gl_LightSource[0].position.xyz);
float facingLight = step(0.0, diffuseDot) * shadowAttenuation;
vec3 diffuseColor = diffuse * (gl_FrontLightModelProduct.sceneColor.rgb + gl_FrontLightProduct[0].diffuse.rgb * (diffuseDot * facingLight));
// compute the specular multiplier (sans exponent)
float specularPower = facingLight * max(0.0,
dot(normalize(gl_LightSource[0].position.xyz - normalize(position)), normal));
vec3 specularColor = pow(specularPower, gloss * 128.0) * specular;
// add specular contribution
return vec3(diffuseColor + specularColor);
}
vec3 evalLightmappedColor(float shadowAttenuation, vec3 normal, vec3 diffuse, vec3 lightmap) {
float diffuseDot = dot(normal, gl_LightSource[0].position.xyz);
// need to catch normals perpendicular to the projection plane hence the magic number for the threshold
// it should be just 0, but we have innacurracy so we need to overshoot
const float PERPENDICULAR_THRESHOLD = -0.005;
float facingLight = step(PERPENDICULAR_THRESHOLD, diffuseDot);
// evaluate the shadow test but only relevant for light facing fragments
float lightAttenuation = (1 - facingLight) + facingLight * shadowAttenuation;
// diffuse light is the lightmap dimmed by shadow
vec3 diffuseLight = lightAttenuation * lightmap;
// ambient is a tiny percentage of the lightmap and only when in the shadow
vec3 ambientLight = (1 - lightAttenuation) * 0.5 * lightmap;
return diffuse * (ambientLight + diffuseLight);
}
<@endif@>

View file

@ -35,9 +35,152 @@
#include "directional_light_shadow_map_frag.h"
#include "directional_light_cascaded_shadow_map_frag.h"
#include "directional_ambient_light_frag.h"
#include "directional_ambient_light_shadow_map_frag.h"
#include "directional_ambient_light_cascaded_shadow_map_frag.h"
#include "point_light_frag.h"
#include "spot_light_frag.h"
class SphericalHarmonics {
public:
glm::vec3 L00 ; float spare0;
glm::vec3 L1m1 ; float spare1;
glm::vec3 L10 ; float spare2;
glm::vec3 L11 ; float spare3;
glm::vec3 L2m2 ; float spare4;
glm::vec3 L2m1 ; float spare5;
glm::vec3 L20 ; float spare6;
glm::vec3 L21 ; float spare7;
glm::vec3 L22 ; float spare8;
static const int NUM_COEFFICIENTS = 9;
void assignPreset(int p) {
switch (p) {
case DeferredLightingEffect::OLD_TOWN_SQUARE: {
L00 = glm::vec3( 0.871297f, 0.875222f, 0.864470f);
L1m1 = glm::vec3( 0.175058f, 0.245335f, 0.312891f);
L10 = glm::vec3( 0.034675f, 0.036107f, 0.037362f);
L11 = glm::vec3(-0.004629f,-0.029448f,-0.048028f);
L2m2 = glm::vec3(-0.120535f,-0.121160f,-0.117507f);
L2m1 = glm::vec3( 0.003242f, 0.003624f, 0.007511f);
L20 = glm::vec3(-0.028667f,-0.024926f,-0.020998f);
L21 = glm::vec3(-0.077539f,-0.086325f,-0.091591f);
L22 = glm::vec3(-0.161784f,-0.191783f,-0.219152f);
}
break;
case DeferredLightingEffect::GRACE_CATHEDRAL: {
L00 = glm::vec3( 0.79f, 0.44f, 0.54f);
L1m1 = glm::vec3( 0.39f, 0.35f, 0.60f);
L10 = glm::vec3(-0.34f, -0.18f, -0.27f);
L11 = glm::vec3(-0.29f, -0.06f, 0.01f);
L2m2 = glm::vec3(-0.11f, -0.05f, -0.12f);
L2m1 = glm::vec3(-0.26f, -0.22f, -0.47f);
L20 = glm::vec3(-0.16f, -0.09f, -0.15f);
L21 = glm::vec3( 0.56f, 0.21f, 0.14f);
L22 = glm::vec3( 0.21f, -0.05f, -0.30f);
}
break;
case DeferredLightingEffect::EUCALYPTUS_GROVE: {
L00 = glm::vec3( 0.38f, 0.43f, 0.45f);
L1m1 = glm::vec3( 0.29f, 0.36f, 0.41f);
L10 = glm::vec3( 0.04f, 0.03f, 0.01f);
L11 = glm::vec3(-0.10f, -0.10f, -0.09f);
L2m2 = glm::vec3(-0.06f, -0.06f, -0.04f);
L2m1 = glm::vec3( 0.01f, -0.01f, -0.05f);
L20 = glm::vec3(-0.09f, -0.13f, -0.15f);
L21 = glm::vec3(-0.06f, -0.05f, -0.04f);
L22 = glm::vec3( 0.02f, 0.00f, -0.05f);
}
break;
case DeferredLightingEffect::ST_PETERS_BASILICA: {
L00 = glm::vec3( 0.36f, 0.26f, 0.23f);
L1m1 = glm::vec3( 0.18f, 0.14f, 0.13f);
L10 = glm::vec3(-0.02f, -0.01f, 0.00f);
L11 = glm::vec3( 0.03f, 0.02f, -0.00f);
L2m2 = glm::vec3( 0.02f, 0.01f, -0.00f);
L2m1 = glm::vec3(-0.05f, -0.03f, -0.01f);
L20 = glm::vec3(-0.09f, -0.08f, -0.07f);
L21 = glm::vec3( 0.01f, 0.00f, 0.00f);
L22 = glm::vec3(-0.08f, -0.03f, -0.00f);
}
break;
case DeferredLightingEffect::UFFIZI_GALLERY: {
L00 = glm::vec3( 0.32f, 0.31f, 0.35f);
L1m1 = glm::vec3( 0.37f, 0.37f, 0.43f);
L10 = glm::vec3( 0.00f, 0.00f, 0.00f);
L11 = glm::vec3(-0.01f, -0.01f, -0.01f);
L2m2 = glm::vec3(-0.02f, -0.02f, -0.03f);
L2m1 = glm::vec3(-0.01f, -0.01f, -0.01f);
L20 = glm::vec3(-0.28f, -0.28f, -0.32f);
L21 = glm::vec3( 0.00f, 0.00f, 0.00f);
L22 = glm::vec3(-0.24f, -0.24f, -0.28f);
}
break;
case DeferredLightingEffect::GALILEOS_TOMB: {
L00 = glm::vec3( 1.04f, 0.76f, 0.71f);
L1m1 = glm::vec3( 0.44f, 0.34f, 0.34f);
L10 = glm::vec3(-0.22f, -0.18f, -0.17f);
L11 = glm::vec3( 0.71f, 0.54f, 0.56f);
L2m2 = glm::vec3( 0.64f, 0.50f, 0.52f);
L2m1 = glm::vec3(-0.12f, -0.09f, -0.08f);
L20 = glm::vec3(-0.37f, -0.28f, -0.32f);
L21 = glm::vec3(-0.17f, -0.13f, -0.13f);
L22 = glm::vec3( 0.55f, 0.42f, 0.42f);
}
break;
case DeferredLightingEffect::VINE_STREET_KITCHEN: {
L00 = glm::vec3( 0.64f, 0.67f, 0.73f);
L1m1 = glm::vec3( 0.28f, 0.32f, 0.33f);
L10 = glm::vec3( 0.42f, 0.60f, 0.77f);
L11 = glm::vec3(-0.05f, -0.04f, -0.02f);
L2m2 = glm::vec3(-0.10f, -0.08f, -0.05f);
L2m1 = glm::vec3( 0.25f, 0.39f, 0.53f);
L20 = glm::vec3( 0.38f, 0.54f, 0.71f);
L21 = glm::vec3( 0.06f, 0.01f, -0.02f);
L22 = glm::vec3(-0.03f, -0.02f, -0.03f);
}
break;
case DeferredLightingEffect::BREEZEWAY: {
L00 = glm::vec3( 0.32f, 0.36f, 0.38f);
L1m1 = glm::vec3( 0.37f, 0.41f, 0.45f);
L10 = glm::vec3(-0.01f, -0.01f, -0.01f);
L11 = glm::vec3(-0.10f, -0.12f, -0.12f);
L2m2 = glm::vec3(-0.13f, -0.15f, -0.17f);
L2m1 = glm::vec3(-0.01f, -0.02f, 0.02f);
L20 = glm::vec3(-0.07f, -0.08f, -0.09f);
L21 = glm::vec3( 0.02f, 0.03f, 0.03f);
L22 = glm::vec3(-0.29f, -0.32f, -0.36f);
}
break;
case DeferredLightingEffect::CAMPUS_SUNSET: {
L00 = glm::vec3( 0.79f, 0.94f, 0.98f);
L1m1 = glm::vec3( 0.44f, 0.56f, 0.70f);
L10 = glm::vec3(-0.10f, -0.18f, -0.27f);
L11 = glm::vec3( 0.45f, 0.38f, 0.20f);
L2m2 = glm::vec3( 0.18f, 0.14f, 0.05f);
L2m1 = glm::vec3(-0.14f, -0.22f, -0.31f);
L20 = glm::vec3(-0.39f, -0.40f, -0.36f);
L21 = glm::vec3( 0.09f, 0.07f, 0.04f);
L22 = glm::vec3( 0.67f, 0.67f, 0.52f);
}
break;
case DeferredLightingEffect::FUNSTON_BEACH_SUNSET: {
L00 = glm::vec3( 0.68f, 0.69f, 0.70f);
L1m1 = glm::vec3( 0.32f, 0.37f, 0.44f);
L10 = glm::vec3(-0.17f, -0.17f, -0.17f);
L11 = glm::vec3(-0.45f, -0.42f, -0.34f);
L2m2 = glm::vec3(-0.17f, -0.17f, -0.15f);
L2m1 = glm::vec3(-0.08f, -0.09f, -0.10f);
L20 = glm::vec3(-0.03f, -0.02f, -0.01f);
L21 = glm::vec3( 0.16f, 0.14f, 0.10f);
L22 = glm::vec3( 0.37f, 0.31f, 0.20f);
}
break;
}
}
};
void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
_viewState = viewState;
@ -54,6 +197,13 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
_directionalLightShadowMapLocations);
loadLightProgram(directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap,
_directionalLightCascadedShadowMapLocations);
loadLightProgram(directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
loadLightProgram(directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap,
_directionalAmbientSphereLightShadowMapLocations);
loadLightProgram(directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap,
_directionalAmbientSphereLightCascadedShadowMapLocations);
loadLightProgram(point_light_frag, true, _pointLight, _pointLightLocations);
loadLightProgram(spot_light_frag, true, _spotLight, _spotLightLocations);
}
@ -167,7 +317,8 @@ void DeferredLightingEffect::render() {
QOpenGLFramebufferObject* freeFBO = DependencyManager::get<GlowEffect>()->getFreeFramebufferObject();
freeFBO->bind();
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_FRAMEBUFFER_SRGB);
glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());
glActiveTexture(GL_TEXTURE1);
@ -203,18 +354,43 @@ void DeferredLightingEffect::render() {
if (_viewState->getCascadeShadowsEnabled()) {
program = &_directionalLightCascadedShadowMap;
locations = &_directionalLightCascadedShadowMapLocations;
_directionalLightCascadedShadowMap.bind();
_directionalLightCascadedShadowMap.setUniform(locations->shadowDistances, _viewState->getShadowDistances());
if (_ambientLightMode > -1) {
program = &_directionalAmbientSphereLightCascadedShadowMap;
locations = &_directionalAmbientSphereLightCascadedShadowMapLocations;
}
program->bind();
program->setUniform(locations->shadowDistances, _viewState->getShadowDistances());
} else {
if (_ambientLightMode > -1) {
program = &_directionalAmbientSphereLightShadowMap;
locations = &_directionalAmbientSphereLightShadowMapLocations;
}
program->bind();
}
program->setUniformValue(locations->shadowScale,
1.0f / textureCache->getShadowFramebufferObject()->width());
} else {
if (_ambientLightMode > -1) {
program = &_directionalAmbientSphereLight;
locations = &_directionalAmbientSphereLightLocations;
}
program->bind();
}
if (locations->ambientSphere >= 0) {
SphericalHarmonics sh;
if (_ambientLightMode < NUM_PRESET) {
sh.assignPreset(_ambientLightMode);
} else {
sh.assignPreset(0);
}
for (int i =0; i <SphericalHarmonics::NUM_COEFFICIENTS; i++) {
program->setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i));
}
}
float left, right, bottom, top, nearVal, farVal;
glm::vec4 nearClipPlane, farClipPlane;
@ -369,6 +545,7 @@ void DeferredLightingEffect::render() {
glBindTexture(GL_TEXTURE_2D, 0);
freeFBO->release();
glDisable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_CULL_FACE);
@ -429,5 +606,12 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit
locations.depthTexCoordOffset = program.uniformLocation("depthTexCoordOffset");
locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale");
locations.radius = program.uniformLocation("radius");
locations.ambientSphere = program.uniformLocation("ambientSphere.L00");
program.release();
}
void DeferredLightingEffect::setAmbientLightMode(int preset) {
if ((preset >= -1) && (preset < NUM_PRESET)) {
_ambientLightMode = preset;
}
}

View file

@ -71,6 +71,23 @@ public:
void prepare();
void render();
enum AmbientLightPreset {
OLD_TOWN_SQUARE = 0,
GRACE_CATHEDRAL,
EUCALYPTUS_GROVE,
ST_PETERS_BASILICA,
UFFIZI_GALLERY,
GALILEOS_TOMB,
VINE_STREET_KITCHEN,
BREEZEWAY,
CAMPUS_SUNSET,
FUNSTON_BEACH_SUNSET,
NUM_PRESET,
};
void setAmbientLightMode(int preset);
private:
DeferredLightingEffect() { }
virtual ~DeferredLightingEffect() { }
@ -84,6 +101,7 @@ private:
int depthTexCoordOffset;
int depthTexCoordScale;
int radius;
int ambientSphere;
};
static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations);
@ -91,12 +109,20 @@ private:
ProgramObject _simpleProgram;
int _glowIntensityLocation;
ProgramObject _directionalAmbientSphereLight;
LightLocations _directionalAmbientSphereLightLocations;
ProgramObject _directionalAmbientSphereLightShadowMap;
LightLocations _directionalAmbientSphereLightShadowMapLocations;
ProgramObject _directionalAmbientSphereLightCascadedShadowMap;
LightLocations _directionalAmbientSphereLightCascadedShadowMapLocations;
ProgramObject _directionalLight;
LightLocations _directionalLightLocations;
ProgramObject _directionalLightShadowMap;
LightLocations _directionalLightShadowMapLocations;
ProgramObject _directionalLightCascadedShadowMap;
LightLocations _directionalLightCascadedShadowMapLocations;
ProgramObject _pointLight;
LightLocations _pointLightLocations;
ProgramObject _spotLight;
@ -126,6 +152,8 @@ private:
QVector<PostLightingRenderable*> _postLightingRenderables;
AbstractViewStateInterface* _viewState;
int _ambientLightMode = 0;
};
/// Simple interface for objects that require something to be rendered after deferred lighting.

View file

@ -0,0 +1,63 @@
<!
// Material.slh
// fragment shader
//
// Created by Sam Gateau on 12/16/14.
// Copyright 2013 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
!>
<@if not MATERIAL_SLH@>
<@def MATERIAL_SLH@>
struct Material {
vec4 _diffuse;
vec4 _specular;
};
float getMaterialOpacity(Material m) { return m._diffuse.a; }
vec3 getMaterialDiffuse(Material m) { return m._diffuse.rgb; }
vec3 getMaterialSpecular(Material m) { return m._specular.rgb; }
float getMaterialShininess(Material m) { return m._specular.a; }
<@if GLPROFILE == PC_GL@>
uniform materialBuffer {
Material mat;
};
Material getMaterial() {
return mat;
}
<@elif GLPROFILE == MAC_GL@>
uniform vec4 materialBuffer[2];
Material getMaterial() {
Material mat;
mat._diffuse = materialBuffer[0];
mat._specular = materialBuffer[1];
return mat;
}
<!/* tryed and failed...
bindable uniform struct {
Material mat;
} materialBuffer;
Material getMaterial() {
return materialBuffer.mat;
}
*/!>
<@else@>
uniform vec4 materialBuffer[2];
Material getMaterial() {
Material mat;
mat._diffuse = materialBuffer[0];
mat._specular = materialBuffer[1];
return mat;
}
<@endif@>
<@endif@>

View file

@ -178,7 +178,7 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, boo
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
locations.texcoordMatrices = program.uniformLocation("texcoordMatrices");
locations.emissiveParams = program.uniformLocation("emissiveParams");
locations.glowIntensity = program.uniformLocation("glowIntensity");
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
@ -198,6 +198,35 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, boo
locations.emissiveTextureUnit = -1;
}
// bindable uniform version
#if defined(Q_OS_MAC)
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#elif defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "materialBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, 1);
locations.materialBufferUnit = 1;
} else {
locations.materialBufferUnit = -1;
}
#else
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#endif
if (!program.isLinked()) {
program.release();
}
program.release();
}
@ -2348,6 +2377,7 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
for (int j = 0; j < networkMesh.parts.size(); j++) {
const NetworkMeshPart& networkPart = networkMesh.parts.at(j);
const FBXMeshPart& part = mesh.parts.at(j);
model::MaterialPointer material = part._material;
if ((networkPart.isTranslucent() || part.opacity != 1.0f) != translucent) {
offset += (part.quadIndices.size() + part.triangleIndices.size()) * sizeof(int);
continue;
@ -2365,16 +2395,17 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
qDebug() << "part INDEX:" << j;
qDebug() << "NEW part.materialID:" << part.materialID;
}
glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity);
if (!(translucent && alphaThreshold == 0.0f)) {
GLBATCH(glAlphaFunc)(GL_EQUAL, diffuse.a = glowEffect->getIntensity());
if (locations->glowIntensity >= 0) {
GLBATCH(glUniform1f)(locations->glowIntensity, glowEffect->getIntensity());
}
if (!(translucent && alphaThreshold == 0.0f)) {
GLBATCH(glAlphaFunc)(GL_EQUAL, glowEffect->getIntensity());
}
if (locations->materialBufferUnit >= 0) {
batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
}
glm::vec4 specular = glm::vec4(part.specularColor, 1.0f);
GLBATCH(glMaterialfv)(GL_FRONT, GL_AMBIENT, (const float*)&diffuse);
GLBATCH(glMaterialfv)(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse);
GLBATCH(glMaterialfv)(GL_FRONT, GL_SPECULAR, (const float*)&specular);
GLBATCH(glMaterialf)(GL_FRONT, GL_SHININESS, (part.shininess > 128.0f ? 128.0f: part.shininess));
Texture* diffuseMap = networkPart.diffuseTexture.data();
if (mesh.isEye && diffuseMap) {

View file

@ -338,6 +338,8 @@ private:
int specularTextureUnit;
int emissiveTextureUnit;
int emissiveParams;
int glowIntensity;
int materialBufferUnit;
};
static Locations _locations;

View file

@ -0,0 +1,42 @@
<@include Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/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
//
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredLighting.slh@>
void main(void) {
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
gl_FragColor = vec4( evalLightmappedColor(
1.0,
frag.normal,
frag.diffuse,
frag.specularVal.xyz),
1.0);
} else {
vec3 color = evalAmbientSphereColor(frag.normal, frag.diffuse, frag.specular, frag.gloss)
+ evalDirectionalColor(1.0,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
gl_FragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -0,0 +1,49 @@
<@include Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/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
//
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredLighting.slh@>
// Everything about shadow
<@include Shadow.slh@>
void main(void) {
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
// Eval shadow Texcoord and then Attenuation
vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position);
float shadowAttenuation = evalShadowAttenuation(shadowTexcoord);
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
gl_FragColor = vec4(evalLightmappedColor(
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz),
1.0);
} else {
vec3 color = evalAmbientSphereColor(frag.normal, frag.diffuse, frag.specular, frag.gloss)
+ evalDirectionalColor(shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
gl_FragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -0,0 +1,50 @@
<@include Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/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
//
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredLighting.slh@>
// Everything about shadow
<@include Shadow.slh@>
void main(void) {
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
// Eval shadow Texcoord and then Attenuation
vec4 shadowTexcoord = evalShadowTexcoord(frag.position);
float shadowAttenuation = evalShadowAttenuation(shadowTexcoord);
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
gl_FragColor = vec4(evalLightmappedColor(
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz),
1.0);
} else {
vec3 color = evalAmbientSphereColor(frag.normal, frag.diffuse, frag.specular, frag.gloss)
+ evalDirectionalColor(shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
gl_FragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -15,29 +15,28 @@
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredLighting.slh@>
void main(void) {
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
vec4 normalVal = frag.normalVal;
vec4 diffuseVal = frag.diffuseVal;
vec4 specularVal = frag.specularVal;
// Light mapped or not ?
if ((normalVal.a >= 0.45) && (normalVal.a <= 0.55)) {
gl_FragColor = vec4(diffuseVal.rgb * specularVal.rgb, 1.0);
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
gl_FragColor = vec4( evalLightmappedColor(
1.0,
frag.normal,
frag.diffuse,
frag.specularVal.xyz),
1.0);
} else {
// compute the base color based on OpenGL lighting model
float diffuse = dot(frag.normal, gl_LightSource[0].position.xyz);
float facingLight = step(0.0, diffuse);
vec3 baseColor = diffuseVal.rgb * (gl_FrontLightModelProduct.sceneColor.rgb +
gl_FrontLightProduct[0].ambient.rgb + gl_FrontLightProduct[0].diffuse.rgb * (diffuse * facingLight));
// compute the specular multiplier (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position.xyz - normalize(frag.position.xyz)),
frag.normal));
// add specular contribution
vec4 specularColor = specularVal;
gl_FragColor = vec4(baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb, normalVal.a);
vec3 color = evalAmbientColor(frag.normal, frag.diffuse, frag.specular, frag.gloss)
+ evalDirectionalColor(1.0,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
gl_FragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -15,55 +15,35 @@
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredLighting.slh@>
// Everything about shadow
<@include Shadow.slh@>
void main(void) {
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
vec4 normalVal = frag.normalVal;
vec4 diffuseVal = frag.diffuseVal;
vec4 specularVal = frag.specularVal;
// Eval shadow Texcoord and then Attenuation
vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position);
float shadowAttenuation = evalShadowAttenuation(shadowTexcoord);
// how much this fragment faces the light direction
float diffuse = dot(frag.normal, gl_LightSource[0].position.xyz);
// Light mapped or not ?
if ((normalVal.a >= 0.45) && (normalVal.a <= 0.55)) {
normalVal.a = 1.0;
// need to catch normals perpendicular to the projection plane hence the magic number for the threshold
// it should be just 0, but we have innacurracy so we need to overshoot
const float PERPENDICULAR_THRESHOLD = -0.005;
float facingLight = step(PERPENDICULAR_THRESHOLD, diffuse);
// evaluate the shadow test but only relevant for light facing fragments
float lightAttenuation = (1 - facingLight) + facingLight * shadowAttenuation;
// diffuse light is the lightmap dimmed by shadow
vec3 diffuseLight = lightAttenuation * specularVal.rgb;
// ambient is a tiny percentage of the lightmap and only when in the shadow
vec3 ambientLight = (1 - lightAttenuation) * 0.5 * specularVal.rgb;
gl_FragColor = vec4(diffuseVal.rgb * (ambientLight + diffuseLight), 1.0);
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
gl_FragColor = vec4(evalLightmappedColor(
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz),
1.0);
} else {
vec3 color = evalAmbientColor(frag.normal, frag.diffuse, frag.specular, frag.gloss)
+ evalDirectionalColor(shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
// average values from the shadow map
float facingLight = step(0.0, diffuse) * shadowAttenuation;
// compute the base color based on OpenGL lighting model
vec3 baseColor = diffuseVal.rgb * (gl_FrontLightModelProduct.sceneColor.rgb +
gl_FrontLightProduct[0].ambient.rgb + gl_FrontLightProduct[0].diffuse.rgb * (diffuse * facingLight));
// compute the specular multiplier (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position.xyz - normalize(frag.position.xyz)),
frag.normal));
// add specular contribution
vec4 specularColor = specularVal;
gl_FragColor = vec4(baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb, normalVal.a);
gl_FragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -15,54 +15,36 @@
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredLighting.slh@>
// Everything about shadow
<@include Shadow.slh@>
<@include Shadow.slh@>
void main(void) {
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
vec4 normalVal = frag.normalVal;
vec4 diffuseVal = frag.diffuseVal;
vec4 specularVal = frag.specularVal;
// Eval shadow Texcoord and then Attenuation
vec4 shadowTexcoord = evalShadowTexcoord(frag.position);
float shadowAttenuation = evalShadowAttenuation(shadowTexcoord);
// how much this fragment faces the light direction
float diffuse = dot(frag.normal, gl_LightSource[0].position.xyz);
// Light mapped or not ?
if ((normalVal.a >= 0.45) && (normalVal.a <= 0.55)) {
normalVal.a = 0.0;
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
gl_FragColor = vec4(evalLightmappedColor(
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz),
1.0);
} else {
vec3 color = evalAmbientColor(frag.normal, frag.diffuse, frag.specular, frag.gloss)
+ evalDirectionalColor(shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
// need to catch normals perpendicular to the projection plane hence the magic number for the threshold
// it should be just 0, be we have innacurracy so we need to overshoot
const float PERPENDICULAR_THRESHOLD = -0.005;
float facingLight = step(PERPENDICULAR_THRESHOLD, diffuse);
// evaluate the shadow test but only relevant for light facing fragments
float lightAttenuation = (1 - facingLight) + facingLight * shadowAttenuation;
// diffuse light is the lightmap dimmed by shadow
vec3 diffuseLight = lightAttenuation * specularVal.rgb;
// ambient is a tiny percentage of the lightmap and only when in the shadow
vec3 ambientLight = (1 - lightAttenuation) * 0.5 * specularVal.rgb;
gl_FragColor = vec4(diffuseVal.rgb * (ambientLight + diffuseLight), 1.0);
} else {
// average values from the shadow map
float facingLight = step(0.0, diffuse) * shadowAttenuation;
// compute the base color based on OpenGL lighting model
vec3 baseColor = diffuseVal.rgb * (gl_FrontLightModelProduct.sceneColor.rgb +
gl_FrontLightProduct[0].ambient.rgb + gl_FrontLightProduct[0].diffuse.rgb * (diffuse * facingLight));
// compute the specular multiplier (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position.xyz - normalize(frag.position.xyz)),
frag.normal));
// add specular contribution
vec4 specularColor = specularVal;
gl_FragColor = vec4(baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb, normalVal.a);
gl_FragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -11,19 +11,26 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include Material.slh@>
// the diffuse texture
uniform sampler2D diffuseMap;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 normal;
void main(void) {
// set the diffuse, normal, specular data
// Fetch diffuse map
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb, mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb, gl_FrontMaterial.shininess / 128.0);
Material mat = getMaterial();
packDeferredFragment(
normalize(normal.xyz),
evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a),
getMaterialDiffuse(mat) * diffuse.rgb,
getMaterialSpecular(mat),
getMaterialShininess(mat));
}

View file

@ -23,7 +23,7 @@ void main(void) {
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
// pass along the diffuse color
gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse;
gl_FrontColor = gl_Color;
// and the texture coordinates
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);

View file

@ -12,6 +12,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include Material.slh@>
// the diffuse texture
uniform sampler2D diffuseMap;
@ -19,9 +23,6 @@ uniform sampler2D diffuseMap;
uniform sampler2D emissiveMap;
uniform vec2 emissiveParams;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 normal;
@ -32,7 +33,14 @@ void main(void) {
// set the diffuse, normal, specular data
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 emissive = texture2D(emissiveMap, interpolatedTexcoord1.st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb, mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5);
gl_FragData[2] = vec4((vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb), gl_FrontMaterial.shininess / 128.0);
Material mat = getMaterial();
packDeferredFragmentLightmap(
normalize(normal.xyz),
evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a),
getMaterialDiffuse(mat) * diffuse.rgb,
getMaterialSpecular(mat),
getMaterialShininess(mat),
(vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb));
}

View file

@ -29,7 +29,7 @@ void main(void) {
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
// pass along the diffuse color
gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse;
gl_FrontColor = gl_Color;
// and the texture coordinates
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);

View file

@ -12,6 +12,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include Material.slh@>
// the diffuse texture
uniform sampler2D diffuseMap;
@ -22,9 +26,6 @@ uniform sampler2D normalMap;
uniform sampler2D emissiveMap;
uniform vec2 emissiveParams;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 interpolatedNormal;
@ -45,7 +46,14 @@ void main(void) {
// set the diffuse, normal, specular data
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 emissive = texture2D(emissiveMap, interpolatedTexcoord1.st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb * (vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb), mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = viewNormal + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb, gl_FrontMaterial.shininess / 128.0);
Material mat = getMaterial();
packDeferredFragmentLightmap(
normalize(viewNormal.xyz),
evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a),
getMaterialDiffuse(mat) * diffuse.rgb,
getMaterialSpecular(mat),
getMaterialShininess(mat),
(vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb));
}

Some files were not shown because too many files have changed in this diff Show more