Merge branch 'correct-target-frame-rate' of https://github.com/howard-stearns/hifi into correct-target-frame-rate

This commit is contained in:
Howard Stearns 2015-12-09 15:43:10 -08:00
commit 5643a6a4c6
95 changed files with 2359 additions and 921 deletions

View file

@ -221,3 +221,36 @@ if (HIFI_MEMORY_DEBUGGING)
MESSAGE("-- Memory debugging is enabled")
endif (UNIX)
endif ()
include_application_version()
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
message(STATUS "+++++ Package for deployment will be generated on this build +++++")
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment)
set(ICONPATH_INTERFACE "$INSTDIR/${PATH_INSTALL_DATA}/interface.ico")
set(ICONPATH_STACK_MANAGER "$INSTDIR/${PATH_INSTALL_DATA}/stack-manager.ico")
string(REPLACE "/" "\\\\" ICONPATH_INTERFACE ${ICONPATH_INTERFACE})
string(REPLACE "/" "\\\\" ICONPATH_STACK_MANAGER ${ICONPATH_STACK_MANAGER})
set(CPACK_PACKAGE_NAME "High Fidelity")
set(CPACK_PACKAGE_VENDOR "High Fidelity, Inc")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Fidelity Interface and Stack")
set(CPACK_PACKAGE_VERSION "${BUILD_SEQ}")
set(CPACK_PACKAGE_VERSION_MAJOR "${BUILD_SEQ}")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "High Fidelity-${BUILD_SEQ}")
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
set(CPACK_PACKAGE_EXECUTABLES
stack-manager "Stack Manager"
interface "Interface"
)
if (WIN32)
install(DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment/ DESTINATION "./")
endif (WIN32)
include(CPack)
endif ()

View file

@ -11,3 +11,4 @@ link_hifi_libraries(
include_application_version()
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -16,6 +16,7 @@
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <AssetClient.h>
#include <AvatarHashMap.h>
#include <AudioInjectorManager.h>
#include <AssetClient.h>
@ -131,6 +132,7 @@ void Agent::run() {
// make sure we request our script once the agent connects to the domain
auto nodeList = DependencyManager::get<NodeList>();
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
@ -223,6 +225,7 @@ void Agent::executeScript() {
// call model URL setters with empty URLs so our avatar, if user, will have the default models
scriptedAvatar->setFaceModelURL(QUrl());
scriptedAvatar->setSkeletonModelURL(QUrl());
// give this AvatarData object to the script engine
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());

View file

@ -438,7 +438,6 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
AudioMixerClientData* listenerNodeData = static_cast<AudioMixerClientData*>(node->getLinkedData());
// zero out the client mix for this node
memset(_preMixSamples, 0, sizeof(_preMixSamples));
memset(_mixSamples, 0, sizeof(_mixSamples));
// loop through all other nodes that have sufficient audio to mix
@ -459,6 +458,9 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
if (otherNodeStream->getType() == PositionalAudioStream::Microphone) {
streamUUID = otherNode->getUUID();
}
// clear out the pre-mix samples before filling it up with this source
memset(_preMixSamples, 0, sizeof(_preMixSamples));
if (*otherNode != *node || otherNodeStream->shouldLoopbackForNode()) {
streamsMixed += addStreamToMixForListeningNodeWithStream(listenerNodeData, streamUUID,

View file

@ -0,0 +1,27 @@
macro(CONSOLIDATE_STACK_COMPONENTS)
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
# Copy all the output for this target into the common deployment location
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/full-stack-deployment
)
# Copy icon files for interface and stack manager
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
if (TARGET_NAME STREQUAL "interface")
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico")
set (ICON_DESTINATION_NAME "interface.ico")
elseif (TARGET_NAME STREQUAL "stack-manager")
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico")
set (ICON_DESTINATION_NAME "stack-manager.ico")
endif ()
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME}
)
endif ()
endif ()
endmacro()

View file

@ -10,10 +10,15 @@
#
macro(INCLUDE_APPLICATION_VERSION)
#
# We are relying on Jenkins defined environment variables to determine the origin of this build
# and will only package if this is a PR or Release build
if (DEFINED ENV{JOB_ID})
set (DEPLOY_PACKAGE 1)
set (BUILD_SEQ $ENV{JOB_ID})
elseif (DEFINED ENV{ghprbPullId})
set (BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}")
set (DEPLOY_PACKAGE 1)
set (BUILD_SEQ "PR-$ENV{ghprbPullId}")
else ()
set(BUILD_SEQ "dev")
endif ()

View file

@ -1,5 +1,5 @@
#
# CopyDllsBesideWindowsExecutable.cmake
# PackageLibrariesForDeployment.cmake
# cmake/macros
#
# Copyright 2015 High Fidelity, Inc.

View file

@ -107,7 +107,7 @@ if (WIN32 AND NOT CYGWIN)
select_library_configurations(SSL_EAY)
set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY})
find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS})
elseif (MINGW)
@ -252,6 +252,15 @@ endif ()
if (WIN32)
add_paths_to_fixup_libs(${OPENSSL_DLL_PATH})
#
# For some reason fixup misses the following DLL and only copies libeay32. There's gotta be a better way to handle this
# but for now resorting to the following interm solution
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy ${OPENSSL_DLL_PATH}/ssleay32.dll ${CMAKE_BINARY_DIR}/full-stack-deployment/
)
endif ()
endif ()
mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_SEARCH_DIRS)

View file

@ -53,4 +53,4 @@ else()
endif()
file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")

View file

@ -38,3 +38,4 @@ endif (UNIX)
include_application_version()
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -0,0 +1,105 @@
{
"assets": {
"crowd-boos.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-boos.wav",
"atp_url": "atp:c632c92b166ade60aa16b23ff1dfdf712856caeb83bd9311980b2d5edac821af.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"crowd-cheers-organ.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-cheers-organ.wav",
"atp_url": "atp:b8044401a846ed29f881a0b9b80cf1ba41f26327180c28fc9c70d144f9b70045.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"crowd-medium.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-medium.wav",
"atp_url": "atp:0821bf2ac60dd2f356dfdd948e8bb89c23984dc3584612f6c815765154f02cae.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"baseball-hitting-bat-1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-1.wav",
"atp_url": "atp:6f0b691a0c9c9ece6557d97fe242b1faec4020fe26efc9c17327993b513c5fe5.wav",
"attribution": "CC BY 3.0 - Credit: https://www.freesound.org/people/SocializedArtist45/"
},
"baseball-hitting-bat-set-1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-1.wav",
"atp_url": "atp:5be5806205158ebdc5c3623ceb7ae73315028b51ffeae24292aff7042e3fa6a9.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"baseball-hitting-bat-set-2.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-2.wav",
"atp_url": "atp:e68661374e2145c480809c26134782aad11e0de456c7802170c7abccc4028873.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"baseball-hitting-bat-set-3.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-3.wav",
"atp_url": "atp:787e3c9af17dd3929527787176ede83d6806260e63ddd5a4cef48cd22e32c6f7.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"baseball-hitting-bat-set-4.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-4.wav",
"atp_url": "atp:fc65383431a6238c7a4749f0f6f061f75a604ed5e17d775ab1b2955609e67ebb.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"chatter-loop.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/chatter-loop.wav",
"atp_url": "atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"zorba-organ.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/zorba-organ.wav",
"atp_url": "atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"charge-organ.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/charge-organ.wav",
"atp_url": "atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"ball-game.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/ball-game.wav",
"atp_url": "atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"clapping.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/clapping.wav",
"atp_url": "atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"pop1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop1.wav",
"atp_url": "atp:a2bf79c95fe74c2c6c9188acc7230f7cd1b0f6008f2c81954ecd93eca0497ec6.wav"
},
"pop2.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop2.wav",
"atp_url": "atp:901067ebc2cda4c0d86ec02fcca2ed901e85f9097ad68bbde78b4cad8eaf2ed7.wav"
},
"pop3.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop3.wav",
"atp_url": "atp:830312930577cb1ea36ba2d743e957debbacceb441b20addead5a6faa05a3771.wav"
},
"pop4.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop4.wav",
"atp_url": "atp:62e80d0a9f084cf731bcc66ca6e9020ee88587417071a281eee3167307b53560.wav"
},
"fire1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire1.wav",
"atp_url": "atp:ee6afe565576c4546c6d6cd89c1af532484c9b60ab30574d6b40c2df022f7260.wav",
"attribution": "CC BY 3.0 - Credir: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
},
"fire2.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire2.wav",
"atp_url": "atp:91ef19ba1c78be82d3fd06530cd05ceb90d1e75f4204c66819c208c55da049ef.wav",
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
},
"fire3.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire3.wav",
"atp_url": "atp:ee56993daf775012cf49293bfd5971eec7e5c396642f8bfbea902ba8f47b56cd.wav",
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
},
"fire4.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire4.wav",
"atp_url": "atp:37775d267f00f82242a7e7f61f3f3d7bf64a54c5a3799e7f2540fa5f6b79bd02.wav",
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
}
}
}

View file

@ -0,0 +1,43 @@
//
// baseballCrowd.js
// examples/acScripts
//
// Created by Stephen Birarda on 10/20/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
//
var chatter = SoundCache.getSound("atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav");
var extras = [
SoundCache.getSound("atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav"), // zorba
SoundCache.getSound("atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav"), // charge
SoundCache.getSound("atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav"), // take me out to the ball game
SoundCache.getSound("atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav") // clapping
];
var CHATTER_VOLUME = 0.20
var EXTRA_VOLUME = 0.25
function playChatter() {
if (chatter.downloaded && !chatter.isPlaying) {
Audio.playSound(chatter, { loop: true, volume: CHATTER_VOLUME });
}
}
chatter.ready.connect(playChatter);
var currentInjector = null;
function playRandomExtras() {
if ((!currentInjector || !currentInjector.isPlaying) && (Math.random() < (1.0 / 1800.0))) {
// play a random extra sound about every 30s
currentInjector = Audio.playSound(
extras[Math.floor(Math.random() * extras.length)],
{ volume: EXTRA_VOLUME }
);
}
}
Script.update.connect(playRandomExtras);

43
examples/baseball/bat.js Normal file
View file

@ -0,0 +1,43 @@
//
// bat.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 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
//
(function() {
Script.include("pitching.js");
var pitchingMachine = null;
this.pitchAndHideAvatar = function() {
if (!pitchingMachine) {
pitchingMachine = getOrCreatePitchingMachine();
Script.update.connect(function(dt) { pitchingMachine.update(dt); });
}
pitchingMachine.start();
MyAvatar.shouldRenderLocally = false;
};
this.startNearGrab = function() {
// send the avatar to the baseball location so that they're ready to bat
location = "/baseball"
this.pitchAndHideAvatar()
};
this.continueNearGrab = function() {
this.pitchAndHideAvatar()
};
this.releaseGrab = function() {
if (pitchingMachine) {
pitchingMachine.stop();
}
MyAvatar.shouldRenderLocally = true;
};
});

View file

@ -0,0 +1,76 @@
//
// createBatButton.js
// examples/baseball/moreBatsButton.js
//
// Created by Stephen Birarda on 10/28/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
//
(function(){
this.clickReleaseOnEntity = function(entityID, mouseEvent) {
if (!mouseEvent.isLeftButton) {
return;
}
this.dropBats();
};
this.startNearTrigger = function() {
this.dropBats();
};
this.startFarTrigger = function() {
this.dropBats();
};
this.dropBats = function() {
// if the bat box is near us, grab it's position
var nearby = Entities.findEntities(this.position, 20);
nearby.forEach(function(id) {
var properties = Entities.getEntityProperties(id, ["name", "position"]);
if (properties.name && properties.name == "Bat Box") {
boxPosition = properties.position;
}
});
var BAT_DROP_HEIGHT = 2.0;
var dropPosition;
if (!boxPosition) {
// we got no bat box position, drop in front of the avatar instead
} else {
// drop the bat above the bat box
dropPosition = Vec3.sum(boxPosition, { x: 0.0, y: BAT_DROP_HEIGHT, z: 0.0});
}
var BAT_MODEL = "atp:c47deaae09cca927f6bc9cca0e8bbe77fc618f8c3f2b49899406a63a59f885cb.fbx";
var BAT_COLLISION_HULL = "atp:9eafceb7510c41d50661130090de7e0632aa4da236ebda84a0059a4be2130e0c.obj";
var SCRIPT_URL = "http://rawgit.com/birarda/hifi/baseball/examples/baseball/bat.js"
var batUserData = {
grabbableKey: {
spatialKey: {
leftRelativePosition: { x: 0.9, y: 0.05, z: -0.05 },
rightRelativePosition: { x: 0.9, y: 0.05, z: 0.05 },
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 45)
}
}
}
// add the fresh bat at the drop position
var bat = Entities.addEntity({
name: 'Bat',
type: "Model",
modelURL: BAT_MODEL,
position: dropPosition,
compoundShapeURL: BAT_COLLISION_HULL,
collisionsWillMove: true,
velocity: { x: 0, y: 0.05, z: 0}, // workaround for gravity not taking effect on add
gravity: { x: 0, y: -9.81, z: 0},
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0),
script: SCRIPT_URL,
userData: JSON.stringify(batUserData)
});
};
});

View file

@ -0,0 +1,138 @@
//
// firework.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 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
//
Script.include("utils.js");
var emitters = [];
var smokeTrailSettings = {
"name":"ParticlesTest Emitter",
"type": "ParticleEffect",
"color":{"red":205,"green":84.41176470588235,"blue":84.41176470588235},
"maxParticles":1000,
"velocity": { x: 0, y: 18.0, z: 0 },
"lifetime": 20,
"lifespan":3,
"emitRate":100,
"emitSpeed":0.5,
"speedSpread":0,
"emitOrientation":{"x":0,"y":0,"z":0,"w":1},
"emitDimensions":{"x":0,"y":0,"z":0},
"emitRadiusStart":0.5,
"polarStart":1,
"polarFinish":1,
"azimuthStart":0,
"azimuthFinish":0,
"emitAcceleration":{"x":0,"y":-0.70000001192092896,"z":0},
"accelerationSpread":{"x":0,"y":0,"z":0},
"particleRadius":0.03999999910593033,
"radiusSpread":0,
"radiusStart":0.13999999910593033,
"radiusFinish":0.14,
"colorSpread":{"red":0,"green":0,"blue":0},
"colorStart":{"red":255,"green":255,"blue":255},
"colorFinish":{"red":255,"green":255,"blue":255},
"alpha":1,
"alphaSpread":0,
"alphaStart":0,
"alphaFinish":1,
"textures":"https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png"
};
var fireworkSettings = {
"name":"ParticlesTest Emitter",
"type": "ParticleEffect",
"color":{"red":205,"green":84.41176470588235,"blue":84.41176470588235},
"maxParticles":1000,
"lifetime": 20,
"lifespan":4,
"emitRate":1000,
"emitSpeed":1.5,
"speedSpread":1.0,
"emitOrientation":{"x":-0.2,"y":0,"z":0,"w":0.7000000000000001},
"emitDimensions":{"x":0,"y":0,"z":0},
"emitRadiusStart":0.5,
"polarStart":1,
"polarFinish":1.2,
"azimuthStart":-Math.PI,
"azimuthFinish":Math.PI,
"emitAcceleration":{"x":0,"y":-0.70000001192092896,"z":0},
"accelerationSpread":{"x":0,"y":0,"z":0},
"particleRadius":0.03999999910593033,
"radiusSpread":0,
"radiusStart":0.13999999910593033,
"radiusFinish":0.14,
"colorSpread":{"red":0,"green":0,"blue":0},
"colorStart":{"red":255,"green":255,"blue":255},
"colorFinish":{"red":255,"green":255,"blue":255},
"alpha":1,
"alphaSpread":0,
"alphaStart":0,
"alphaFinish":1,
"textures":"https://hifi-public.s3.amazonaws.com/alan/Particles/spark_2.png",
};
var popSounds = getSounds([
"atp:a2bf79c95fe74c2c6c9188acc7230f7cd1b0f6008f2c81954ecd93eca0497ec6.wav",
"atp:901067ebc2cda4c0d86ec02fcca2ed901e85f9097ad68bbde78b4cad8eaf2ed7.wav",
"atp:830312930577cb1ea36ba2d743e957debbacceb441b20addead5a6faa05a3771.wav",
"atp:62e80d0a9f084cf731bcc66ca6e9020ee88587417071a281eee3167307b53560.wav"
]);
var launchSounds = getSounds([
"atp:ee6afe565576c4546c6d6cd89c1af532484c9b60ab30574d6b40c2df022f7260.wav",
"atp:91ef19ba1c78be82d3fd06530cd05ceb90d1e75f4204c66819c208c55da049ef.wav",
"atp:ee56993daf775012cf49293bfd5971eec7e5c396642f8bfbea902ba8f47b56cd.wav",
"atp:37775d267f00f82242a7e7f61f3f3d7bf64a54c5a3799e7f2540fa5f6b79bd02.wav"
]);
function playRandomSound(sounds, options) {
Audio.playSound(sounds[randomInt(sounds.length)], options);
}
function shootFirework(position, color, options) {
smokeTrailSettings.position = position;
smokeTrailSettings.velocity = randomVec3(-5, 5, 10, 20, 10, 15);
smokeTrailSettings.gravity = randomVec3(-5, 5, -9.8, -9.8, 20, 40);
playRandomSound(launchSounds, { position: {x: 0, y: 0 , z: 0}, volume: 3.0 });
var smokeID = Entities.addEntity(smokeTrailSettings);
Script.setTimeout(function() {
Entities.editEntity(smokeID, { emitRate: 0 });
var position = Entities.getEntityProperties(smokeID, ['position']).position;
fireworkSettings.position = position;
fireworkSettings.colorStart = color;
fireworkSettings.colorFinish = color;
var burstID = Entities.addEntity(fireworkSettings);
playRandomSound(popSounds, { position: {x: 0, y: 0 , z: 0}, volume: 3.0 });
Script.setTimeout(function() {
Entities.editEntity(burstID, { emitRate: 0 });
}, 500);
Script.setTimeout(function() {
Entities.deleteEntity(smokeID);
Entities.deleteEntity(burstID);
}, 10000);
}, 2000);
}
playFireworkShow = function(position, numberOfFireworks, duration) {
for (var i = 0; i < numberOfFireworks; i++) {
var randomOffset = randomVec3(-15, 15, -3, 3, -1, 1);
var randomPosition = Vec3.sum(position, randomOffset);
Script.setTimeout(function(position) {
return function() {
var color = randomColor(128, 255, 128, 255, 128, 255);
shootFirework(position, color, fireworkSettings);
}
}(randomPosition), Math.random() * duration)
}
}

165
examples/baseball/line.js Normal file
View file

@ -0,0 +1,165 @@
function info(message) {
print("[INFO] " + message);
}
function error(message) {
print("[ERROR] " + message);
}
/******************************************************************************
* PolyLine
*****************************************************************************/
var LINE_DIMENSIONS = { x: 2000, y: 2000, z: 2000 };
var MAX_LINE_LENGTH = 40; // This must be 2 or greater;
var PolyLine = function(position, color, defaultStrokeWidth) {
//info("Creating polyline");
//Vec3.print("New line at", position);
this.position = position;
this.color = color;
this.defaultStrokeWidth = 0.10;
this.points = [
{ x: 0, y: 0, z: 0 },
];
this.strokeWidths = [
this.defaultStrokeWidth,
]
this.normals = [
{ x: 1, y: 0, z: 0 },
]
this.entityID = Entities.addEntity({
type: "PolyLine",
position: position,
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
dimensions: LINE_DIMENSIONS,
color: color,
lifetime: 20,
});
};
PolyLine.prototype.enqueuePoint = function(position) {
if (this.isFull()) {
error("Hit max PolyLine size");
return;
}
//Vec3.print("pos", position);
//info("Number of points: " + this.points.length);
position = Vec3.subtract(position, this.position);
this.points.push(position);
this.normals.push({ x: 1, y: 0, z: 0 });
this.strokeWidths.push(this.defaultStrokeWidth);
Entities.editEntity(this.entityID, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
});
};
PolyLine.prototype.dequeuePoint = function() {
if (this.points.length == 0) {
error("Hit min PolyLine size");
return;
}
this.points = this.points.slice(1);
this.normals = this.normals.slice(1);
this.strokeWidths = this.strokeWidths.slice(1);
Entities.editEntity(this.entityID, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
});
};
PolyLine.prototype.getFirstPoint = function() {
return Vec3.sum(this.position, this.points[0]);
};
PolyLine.prototype.getLastPoint = function() {
return Vec3.sum(this.position, this.points[this.points.length - 1]);
};
PolyLine.prototype.getSize = function() {
return this.points.length;
}
PolyLine.prototype.isFull = function() {
return this.points.length >= MAX_LINE_LENGTH;
};
PolyLine.prototype.destroy = function() {
Entities.deleteEntity(this.entityID);
this.points = [];
};
/******************************************************************************
* InfiniteLine
*****************************************************************************/
InfiniteLine = function(position, color) {
this.position = position;
this.color = color;
this.lines = [new PolyLine(position, color)];
this.size = 0;
};
InfiniteLine.prototype.enqueuePoint = function(position) {
var currentLine;
if (this.lines.length == 0) {
currentLine = new PolyLine(position, this.color);
this.lines.push(currentLine);
} else {
currentLine = this.lines[this.lines.length - 1];
}
if (currentLine.isFull()) {
//info("Current line is full, creating new line");
//Vec3.print("Last line is", currentLine.getLastPoint());
//Vec3.print("New line is", position);
var newLine = new PolyLine(currentLine.getLastPoint(), this.color);
this.lines.push(newLine);
currentLine = newLine;
}
currentLine.enqueuePoint(position);
++this.size;
};
InfiniteLine.prototype.dequeuePoint = function() {
if (this.lines.length == 0) {
error("Trying to dequeue from InfiniteLine when no points are left");
return;
}
var lastLine = this.lines[0];
lastLine.dequeuePoint();
if (lastLine.getSize() <= 1) {
this.lines = this.lines.slice(1);
}
--this.size;
};
InfiniteLine.prototype.getFirstPoint = function() {
return this.lines.length > 0 ? this.lines[0].getFirstPoint() : null;
};
InfiniteLine.prototype.getLastPoint = function() {
return this.lines.length > 0 ? this.lines[lines.length - 1].getLastPoint() : null;
};
InfiniteLine.prototype.destroy = function() {
for (var i = 0; i < this.lines.length; ++i) {
this.lines[i].destroy();
}
this.size = 0;
};

View file

@ -0,0 +1,554 @@
//
// pitching.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 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
//
print("Loading pitching");
Script.include("../libraries/line.js");
Script.include("firework.js");
Script.include("utils.js");
var DISTANCE_BILLBOARD_NAME = "CurrentScore";
var HIGH_SCORE_BILLBOARD_NAME = "HighScore";
var distanceBillboardEntityID = null;
var highScoreBillboardEntityID = null;
function getDistanceBillboardEntityID() {
if (distanceBillboardEntityID === null) {
distanceBillboardEntityID = findEntity({name: DISTANCE_BILLBOARD_NAME }, 1000);
}
return distanceBillboardEntityID;
}
function getHighScoreBillboardEntityID() {
if (highScoreBillboardEntityID === null) {
highScoreBillboardEntityID = findEntity({name: HIGH_SCORE_BILLBOARD_NAME }, 1000);
}
return highScoreBillboardEntityID;
}
var METERS_TO_FEET = 3.28084;
var AUDIO = {
crowdBoos: [
SoundCache.getSound("atp:c632c92b166ade60aa16b23ff1dfdf712856caeb83bd9311980b2d5edac821af.wav", false)
],
crowdCheers: [
SoundCache.getSound("atp:0821bf2ac60dd2f356dfdd948e8bb89c23984dc3584612f6c815765154f02cae.wav", false),
SoundCache.getSound("atp:b8044401a846ed29f881a0b9b80cf1ba41f26327180c28fc9c70d144f9b70045.wav", false),
],
batHit: [
SoundCache.getSound("atp:6f0b691a0c9c9ece6557d97fe242b1faec4020fe26efc9c17327993b513c5fe5.wav", false),
SoundCache.getSound("atp:5be5806205158ebdc5c3623ceb7ae73315028b51ffeae24292aff7042e3fa6a9.wav", false),
SoundCache.getSound("atp:e68661374e2145c480809c26134782aad11e0de456c7802170c7abccc4028873.wav", false),
SoundCache.getSound("atp:787e3c9af17dd3929527787176ede83d6806260e63ddd5a4cef48cd22e32c6f7.wav", false),
SoundCache.getSound("atp:fc65383431a6238c7a4749f0f6f061f75a604ed5e17d775ab1b2955609e67ebb.wav", false),
],
strike: [
SoundCache.getSound("atp:2a258076a85fffde4ba04b5ddc1de9034c7ae7d2af8c5d93d4fed0bcdef3472a.wav", false),
SoundCache.getSound("atp:518363524af3ed9b9ae4ca2ceee61f01aecd37e266a51c5a5f5487efe2520fd5.wav", false),
SoundCache.getSound("atp:d51d38b089574acbdfdf53ef733bfb3ab41d848fb8c0b6659c7790a785240009.wav", false),
],
foul: [
SoundCache.getSound("atp:316fa18ff9eef457f670452b449a8dc5a41ccabd4e948781c50aaafaae63b0ab.wav", false),
SoundCache.getSound("atp:c84d88352d38437edd7414b26dc74e567618712caeb59fec70822398b0c5a279.wav", false),
]
}
var PITCH_THUNK_SOUND_URL = "http://hifi-public.s3.amazonaws.com/sounds/ping_pong_gun/pong_sound.wav";
var pitchSound = SoundCache.getSound(PITCH_THUNK_SOUND_URL, false);
updateBillboard("");
var PITCHING_MACHINE_URL = "atp:87d4879530b698741ecc45f6f31789aac11f7865a2c3bec5fe9b061a182c80d4.fbx";
// This defines an offset to pitch a ball from with respect to the machine's position. The offset is a
// percentage of the machine's dimensions. So, { x: 0.5, y: -1.0, z: 0.0 } would offset on 50% on the
// machine's x axis, -100% on the y axis, and 0% on the z-axis. For the dimensions { x: 100, y: 100, z: 100 },
// that would result in an offset of { x: 50, y: -100, z: 0 }. This makes it easy to calculate an offset if
// the machine's dimensions change.
var PITCHING_MACHINE_OUTPUT_OFFSET_PCT = {
x: 0.0,
y: 0.25,
z: -1.05,
};
var PITCHING_MACHINE_PROPERTIES = {
name: "Pitching Machine",
type: "Model",
position: {
x: -0.93,
y: 0.8,
z: -19.8
},
velocity: {
x: 0,
y: -0.01,
z: 0
},
gravity: {
x: 0.0,
y: -9.8,
z: 0.0
},
registrationPoint: {
x: 0.5,
y: 0.5,
z: 0.5
},
rotation: Quat.fromPitchYawRollDegrees(0, 180, 0),
modelURL: PITCHING_MACHINE_URL,
dimensions: {
x: 0.4,
y: 0.61,
z: 0.39
},
collisionsWillMove: false,
shapeType: "Box"
};
PITCHING_MACHINE_PROPERTIES.dimensions = Vec3.multiply(2.5, PITCHING_MACHINE_PROPERTIES.dimensions);
var DISTANCE_FROM_PLATE = PITCHING_MACHINE_PROPERTIES.position.z;
var PITCH_RATE = 5000;
getOrCreatePitchingMachine = function() {
// Search for pitching machine
var entities = findEntities({ name: PITCHING_MACHINE_PROPERTIES.name }, 1000);
var pitchingMachineID = null;
// Create if it doesn't exist
if (entities.length == 0) {
pitchingMachineID = Entities.addEntity(PITCHING_MACHINE_PROPERTIES);
} else {
pitchingMachineID = entities[0];
}
// Wrap with PitchingMachine object and return
return new PitchingMachine(pitchingMachineID);
}
// The pitching machine wraps an entity ID and uses it's position & rotation to determin where to
// pitch the ball from and in which direction, and uses the dimensions to determine the scale of them ball.
function PitchingMachine(pitchingMachineID) {
this.pitchingMachineID = pitchingMachineID;
this.enabled = false;
this.baseball = null;
this.injector = null;
}
PitchingMachine.prototype = {
pitchBall: function() {
cleanupTrail();
if (!this.enabled) {
return;
}
print("Pitching ball");
var machineProperties = Entities.getEntityProperties(this.pitchingMachineID, ["dimensions", "position", "rotation"]);
var pitchFromPositionBase = machineProperties.position;
var pitchFromOffset = vec3Mult(machineProperties.dimensions, PITCHING_MACHINE_OUTPUT_OFFSET_PCT);
pitchFromOffset = Vec3.multiplyQbyV(machineProperties.rotation, pitchFromOffset);
var pitchFromPosition = Vec3.sum(pitchFromPositionBase, pitchFromOffset);
var pitchDirection = Quat.getFront(machineProperties.rotation);
var ballScale = machineProperties.dimensions.x / PITCHING_MACHINE_PROPERTIES.dimensions.x;
var speed = randomFloat(BASEBALL_MIN_SPEED, BASEBALL_MAX_SPEED);
var velocity = Vec3.multiply(speed, pitchDirection);
this.baseball = new Baseball(pitchFromPosition, velocity, ballScale);
if (!this.injector) {
this.injector = Audio.playSound(pitchSound, {
position: pitchFromPosition,
volume: 1.0
});
} else {
this.injector.restart();
}
},
start: function() {
if (this.enabled) {
return;
}
print("Starting Pitching Machine");
this.enabled = true;
this.pitchBall();
},
stop: function() {
if (!this.enabled) {
return;
}
print("Stopping Pitching Machine");
this.enabled = false;
},
update: function(dt) {
if (this.baseball) {
this.baseball.update(dt);
if (this.baseball.finished()) {
this.baseball = null;
var self = this;
Script.setTimeout(function() { self.pitchBall(); }, 3000);
}
}
}
};
var BASEBALL_MODEL_URL = "atp:7185099f1f650600ca187222573a88200aeb835454bd2f578f12c7fb4fd190fa.fbx";
var BASEBALL_MIN_SPEED = 7.0;
var BASEBALL_MAX_SPEED = 15.0;
var BASEBALL_RADIUS = 0.07468;
var BASEBALL_PROPERTIES = {
name: "Baseball",
type: "Model",
modelURL: BASEBALL_MODEL_URL,
shapeType: "Sphere",
position: {
x: 0,
y: 0,
z: 0
},
dimensions: {
x: BASEBALL_RADIUS,
y: BASEBALL_RADIUS,
z: BASEBALL_RADIUS
},
collisionsWillMove: true,
angularVelocity: {
x: 17.0,
y: 0,
z: -8.0,
x: 0.0,
y: 0,
z: 0.0,
},
angularDamping: 0.0,
damping: 0.0,
restitution: 0.5,
friction: 0.0,
friction: 0.5,
lifetime: 20,
gravity: {
x: 0,
y: 0,
z: 0
}
};
var BASEBALL_STATE = {
PITCHING: 0,
HIT: 1,
HIT_LANDED: 2,
STRIKE: 3,
FOUL: 4
};
function vec3Mult(a, b) {
return {
x: a.x * b.x,
y: a.y * b.y,
z: a.z * b.z,
};
}
function map(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
function orientationOf(vector) {
var RAD_TO_DEG = 180.0 / Math.PI;
var Y_AXIS = { x: 0, y: 1, z: 0 };
var X_AXIS = { x: 1, y: 0, z: 0 };
var direction = Vec3.normalize(vector);
var yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
var pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
return Quat.multiply(yaw, pitch);
}
var ACCELERATION_SPREAD = 0.35;
var TRAIL_COLOR = { red: 128, green: 255, blue: 89 };
var TRAIL_LIFETIME = 20;
var trail = null;
var trailInterval = null;
function cleanupTrail() {
if (trail) {
Script.clearInterval(this.trailInterval);
trailInterval = null;
trail.destroy();
trail = null;
}
}
function setupTrail(entityID, position) {
cleanupTrail();
var lastPosition = position;
trail = new InfiniteLine(position, { red: 128, green: 255, blue: 89 }, 20);
trailInterval = Script.setInterval(function() {
var properties = Entities.getEntityProperties(entityID, ['position']);
if (Vec3.distance(properties.position, lastPosition)) {
var strokeWidth = Math.log(1 + trail.size) * 0.05;
trail.enqueuePoint(properties.position, strokeWidth);
lastPosition = properties.position;
}
}, 50);
}
function Baseball(position, velocity, ballScale) {
var self = this;
this.state = BASEBALL_STATE.PITCHING;
// Setup entity properties
var properties = shallowCopy(BASEBALL_PROPERTIES);
properties.position = position;
properties.velocity = velocity;
properties.dimensions = Vec3.multiply(ballScale, properties.dimensions);
/*
properties.gravity = {
x: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
y: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
z: 0.0,
};
*/
// Create entity
this.entityID = Entities.addEntity(properties);
this.timeSincePitched = 0;
this.timeSinceHit = 0;
this.hitBallAtPosition = null;
this.distanceTravelled = 0;
this.wasHighScore = false;
this.landed = false;
// Listen for collision for the lifetime of the entity
Script.addEventHandler(this.entityID, "collisionWithEntity", function(entityA, entityB, collision) {
self.collisionCallback(entityA, entityB, collision);
});
if (Math.random() < 0.5) {
for (var i = 0; i < 50; i++) {
Script.setTimeout(function() {
Entities.editEntity(entityID, {
gravity: {
x: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
y: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
z: 0.0,
}
})
}, i * 100);
}
}
}
// Update the stadium billboard with the current distance or a text message and update the high score
// if it has been beaten.
function updateBillboard(distanceOrMessage) {
var distanceBillboardEntityID = getDistanceBillboardEntityID();
if (distanceBillboardEntityID) {
Entities.editEntity(distanceBillboardEntityID, {
text: distanceOrMessage
});
}
var highScoreBillboardEntityID = getHighScoreBillboardEntityID();
// If a number was passed in, let's see if it is larger than the current high score
// and update it if so.
if (!isNaN(distanceOrMessage) && highScoreBillboardEntityID) {
var properties = Entities.getEntityProperties(highScoreBillboardEntityID, ["text"]);
var bestDistance = parseInt(properties.text);
if (distanceOrMessage >= bestDistance) {
Entities.editEntity(highScoreBillboardEntityID, {
text: distanceOrMessage,
});
return true;
}
}
return false;
}
var FIREWORKS_SHOW_POSITION = { x: 0, y: 0, z: -78.0 };
var FIREWORK_PER_X_FEET = 100;
var MAX_FIREWORKS = 10;
Baseball.prototype = {
finished: function() {
return this.state == BASEBALL_STATE.FOUL
|| this.state == BASEBALL_STATE.STRIKE
|| this.state == BASEBALL_STATE.HIT_LANDED;
},
update: function(dt) {
this.timeSincePitched += dt;
if (this.state == BASEBALL_STATE.HIT) {
this.timeSinceHit += dt;
var myProperties = Entities.getEntityProperties(this.entityID, ['position', 'velocity']);
var speed = Vec3.length(myProperties.velocity);
this.distanceTravelled = Vec3.distance(this.hitBallAtPosition, myProperties.position) * METERS_TO_FEET;
var wasHighScore = updateBillboard(Math.ceil(this.distanceTravelled));
if (this.landed || this.timeSinceHit > 10 || speed < 1) {
this.wasHighScore = wasHighScore;
this.ballLanded();
}
} else if (this.state == BASEBALL_STATE.PITCHING) {
if (this.timeSincePitched > 10) {
print("TIMED OUT WHILE PITCHING");
this.state = BASEBALL_STATE.STRIKE;
}
}
},
ballLanded: function() {
this.state = BASEBALL_STATE.HIT_LANDED;
var numberOfFireworks = Math.floor(this.distanceTravelled / FIREWORK_PER_X_FEET);
if (numberOfFireworks > 0) {
numberOfFireworks = Math.min(MAX_FIREWORKS, numberOfFireworks);
playFireworkShow(FIREWORKS_SHOW_POSITION, numberOfFireworks, 2000);
}
print("Ball took " + this.timeSinceHit.toFixed(3) + " seconds to land");
print("Ball travelled " + this.distanceTravelled + " feet")
},
collisionCallback: function(entityA, entityB, collision) {
var self = this;
var myProperties = Entities.getEntityProperties(this.entityID, ['position', 'velocity']);
var myPosition = myProperties.position;
var myVelocity = myProperties.velocity;
// Activate gravity
Entities.editEntity(self.entityID, {
gravity: { x: 0, y: -9.8, z: 0 }
});
var name = Entities.getEntityProperties(entityB, ["name"]).name;
if (name == "Bat") {
if (this.state == BASEBALL_STATE.PITCHING) {
print("HIT");
var FOUL_MIN_YAW = -135.0;
var FOUL_MAX_YAW = 135.0;
var yaw = Math.atan2(myVelocity.x, myVelocity.z) * 180 / Math.PI;
var foul = yaw > FOUL_MIN_YAW && yaw < FOUL_MAX_YAW;
var speedMultiplier = 2;
if (foul && myVelocity.z > 0) {
var TUNNELED_PITCH_RANGE = 15.0;
var xzDist = Math.sqrt(myVelocity.x * myVelocity.x + myVelocity.z * myVelocity.z);
var pitch = Math.atan2(myVelocity.y, xzDist) * 180 / Math.PI;
print("Pitch: ", pitch);
// If the pitch is going straight out the back and has a pitch in the range TUNNELED_PITCH_RANGE,
// let's assume the ball tunneled through the bat and reverse its direction.
if (Math.abs(pitch) < TUNNELED_PITCH_RANGE) {
print("Reversing hit");
myVelocity.x *= -1;
myVelocity.y *= -1;
myVelocity.z *= -1;
yaw = Math.atan2(myVelocity.x, myVelocity.z) * 180 / Math.PI;
foul = yaw > FOUL_MIN_YAW && yaw < FOUL_MAX_YAW;
speedMultiplier = 3;
}
}
// Update ball velocity
Entities.editEntity(self.entityID, {
velocity: Vec3.multiply(speedMultiplier, myVelocity)
});
// Setup line update interval
setupTrail(self.entityID, myPosition);
// Setup bat hit sound
playRandomSound(AUDIO.batHit, {
position: myPosition,
volume: 2.0
});
// Setup crowd reaction sound
var speed = Vec3.length(myVelocity);
Script.setTimeout(function() {
playRandomSound((speed < 5.0) ? AUDIO.crowdBoos : AUDIO.crowdCheers, {
position: { x: 0 ,y: 0, z: 0 },
volume: 1.0
});
}, 500);
if (foul) {
print("FOUL, yaw: ", yaw);
updateBillboard("FOUL");
this.state = BASEBALL_STATE.FOUL;
playRandomSound(AUDIO.foul, {
position: myPosition,
volume: 2.0
});
} else {
print("HIT ", yaw);
this.state = BASEBALL_STATE.HIT;
}
}
} else if (name == "stadium") {
//entityCollisionWithGround(entityB, this.entityID, collision);
this.landed = true;
} else if (name == "backstop") {
if (this.state == BASEBALL_STATE.PITCHING) {
print("STRIKE");
this.state = BASEBALL_STATE.STRIKE;
updateBillboard("STRIKE");
playRandomSound(AUDIO.strike, {
position: myPosition,
volume: 2.0
});
}
}
}
}
function entityCollisionWithGround(ground, entity, collision) {
var ZERO_VEC = { x: 0, y: 0, z: 0 };
var dVelocityMagnitude = Vec3.length(collision.velocityChange);
var position = Entities.getEntityProperties(entity, "position").position;
var particleRadius = 0.3;
var speed = map(dVelocityMagnitude, 0.05, 3, 0.02, 0.09);
var displayTime = 400;
var orientationChange = orientationOf(collision.velocityChange);
var dustEffect = Entities.addEntity({
type: "ParticleEffect",
name: "Dust-Puff",
position: position,
color: {red: 195, green: 170, blue: 185},
lifespan: 3,
lifetime: 2,//displayTime/1000 * 2, //So we can fade particle system out gracefully
emitRate: 5,
emitSpeed: speed,
emitAcceleration: ZERO_VEC,
accelerationSpread: ZERO_VEC,
isEmitting: true,
polarStart: Math.PI/2,
polarFinish: Math.PI/2,
emitOrientation: orientationChange,
radiusSpread: 0.1,
radiusStart: particleRadius,
radiusFinish: particleRadius + particleRadius / 2,
particleRadius: particleRadius,
alpha: 0.45,
alphaFinish: 0.001,
textures: "https://hifi-public.s3.amazonaws.com/alan/Playa/Particles/Particle-Sprite-Gen.png"
});
}
Script.scriptEnding.connect(function() {
cleanupTrail();
Entities.deleteEntity(pitchingMachineID);
});

View file

@ -0,0 +1,92 @@
//
// utils.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 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
//
randomInt = function(low, high) {
return Math.floor(randomFloat(low, high));
};
randomFloat = function(low, high) {
if (high === undefined) {
high = low;
low = 0;
}
return low + Math.random() * (high - low);
};
randomColor = function(redMin, redMax, greenMin, greenMax, blueMin, blueMax) {
return {
red: Math.ceil(randomFloat(redMin, redMax)),
green: Math.ceil(randomFloat(greenMin, greenMax)),
blue: Math.ceil(randomFloat(blueMin, blueMax)),
}
};
randomVec3 = function(xMin, xMax, yMin, yMax, zMin, zMax) {
return {
x: randomFloat(xMin, xMax),
y: randomFloat(yMin, yMax),
z: randomFloat(zMin, zMax),
}
};
getSounds = function(soundURLs) {
var sounds = [];
for (var i = 0; i < soundURLs.length; ++i) {
sounds.push(SoundCache.getSound(soundURLs[i], false));
}
return sounds;
};
playRandomSound = function(sounds, options) {
if (options === undefined) {
options = {
volume: 1.0,
position: MyAvatar.position,
}
}
return Audio.playSound(sounds[randomInt(sounds.length)], options);
}
shallowCopy = function(obj) {
var copy = {}
for (var key in obj) {
copy[key] = obj[key];
}
return copy;
}
findEntity = function(properties, searchRadius) {
var entities = findEntities(properties, searchRadius);
return entities.length > 0 ? entities[0] : null;
}
// Return all entities with properties `properties` within radius `searchRadius`
findEntities = function(properties, searchRadius) {
var entities = Entities.findEntities(MyAvatar.position, searchRadius);
var matchedEntities = [];
var keys = Object.keys(properties);
for (var i = 0; i < entities.length; ++i) {
var match = true;
var candidateProperties = Entities.getEntityProperties(entities[i], keys);
for (var key in properties) {
if (candidateProperties[key] != properties[key]) {
// This isn't a match, move to next entity
match = false;
break;
}
}
if (match) {
matchedEntities.push(entities[i]);
}
}
return matchedEntities;
}

View file

@ -201,6 +201,33 @@ function entityIsGrabbedByOther(entityID) {
return false;
}
function getSpatialOffsetPosition(hand, spatialKey) {
if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) {
return spatialKey.leftRelativePosition;
}
if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) {
return spatialKey.rightRelativePosition;
}
if (spatialKey.relativePosition) {
return spatialKey.relativePosition;
}
return Vec3.ZERO;
}
function getSpatialOffsetRotation(hand, spatialKey) {
if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) {
return spatialKey.leftRelativeRotation;
}
if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) {
return spatialKey.rightRelativeRotation;
}
if (spatialKey.relativeRotation) {
return spatialKey.relativeRotation;
}
return Quat.IDENTITY;
}
function MyController(hand) {
this.hand = hand;
@ -224,20 +251,11 @@ function MyController(hand) {
this.triggerValue = 0; // rolling average of trigger value
this.rawTriggerValue = 0;
this.rawBumperValue = 0;
this.overlayLine = null;
this.offsetPosition = {
x: 0.0,
y: 0.0,
z: 0.0
};
this.offsetRotation = {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0
};
this.offsetPosition = Vec3.ZERO;
this.offsetRotation = Quat.IDENTITY;
var _this = this;
@ -835,12 +853,8 @@ function MyController(hand) {
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
// if an object is "equipped" and has a spatialKey, use it.
if (grabbableData.spatialKey.relativePosition) {
this.offsetPosition = grabbableData.spatialKey.relativePosition;
}
if (grabbableData.spatialKey.relativeRotation) {
this.offsetRotation = grabbableData.spatialKey.relativeRotation;
}
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
} else {
var objectRotation = grabbedProperties.rotation;
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
@ -966,23 +980,8 @@ function MyController(hand) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
// use a spring to pull the object to where it will be when equipped
var relativeRotation = {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0
};
var relativePosition = {
x: 0.0,
y: 0.0,
z: 0.0
};
if (grabbableData.spatialKey.relativePosition) {
relativePosition = grabbableData.spatialKey.relativePosition;
}
if (grabbableData.spatialKey.relativeRotation) {
relativeRotation = grabbableData.spatialKey.relativeRotation;
}
var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
var handRotation = this.getHandRotation();
var handPosition = this.getHandPosition();
var targetRotation = Quat.multiply(handRotation, relativeRotation);

View file

@ -20,6 +20,9 @@ var leapHands = (function () {
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
handAnimationStateHandlers,
handAnimationStateFunctions,
handAnimationStateProperties,
hands,
wrists,
NUM_HANDS = 2, // 0 = left; 1 = right
@ -37,7 +40,14 @@ var leapHands = (function () {
avatarScale,
avatarFaceModelURL,
avatarSkeletonModelURL,
settingsTimer;
settingsTimer,
HMD_CAMERA_TO_AVATAR_ROTATION = [
Quat.angleAxis(180.0, { x: 0, y: 0, z: 1 }),
Quat.angleAxis(-180.0, { x: 0, y: 0, z: 1 })
],
DESKTOP_CAMERA_TO_AVATAR_ROTATION =
Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 })),
LEAP_THUMB_ROOT_ADJUST = [Quat.fromPitchYawRollDegrees(0, 0, 20), Quat.fromPitchYawRollDegrees(0, 0, -20)];
function printSkeletonJointNames() {
var jointNames,
@ -125,6 +135,26 @@ var leapHands = (function () {
*/
}
function animateLeftHand() {
var ROTATION_AND_POSITION = 0;
return {
leftHandType: ROTATION_AND_POSITION,
leftHandPosition: hands[0].position,
leftHandRotation: hands[0].rotation
};
}
function animateRightHand() {
var ROTATION_AND_POSITION = 0;
return {
rightHandType: ROTATION_AND_POSITION,
rightHandPosition: hands[1].position,
rightHandRotation: hands[1].rotation
};
}
function finishCalibration() {
var avatarPosition,
handPosition,
@ -166,10 +196,8 @@ var leapHands = (function () {
MyAvatar.clearJointData("LeftHand");
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
calibrationStatus = CALIBRATED;
print("Leap Motion: Calibrated");
@ -193,12 +221,10 @@ var leapHands = (function () {
}
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0));
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0));
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, 90.0));
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollDegrees(0.0, 90.0, 0.0));
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0));
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollDegrees(0.0, -90.0, 0.0));
// Wait for arms to assume their positions before calculating
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
@ -319,6 +345,13 @@ var leapHands = (function () {
]
];
handAnimationStateHandlers = [null, null];
handAnimationStateFunctions = [animateLeftHand, animateRightHand];
handAnimationStateProperties = [
["leftHandType", "leftHandPosition", "leftHandRotation"],
["rightHandType", "rightHandPosition", "rightHandPosition"]
];
setIsOnHMD();
settingsTimer = Script.setInterval(checkSettings, 2000);
@ -348,6 +381,12 @@ var leapHands = (function () {
return;
}
// Hand animation handlers ...
if (handAnimationStateHandlers[h] === null) {
handAnimationStateHandlers[h] = MyAvatar.addAnimationStateHandler(handAnimationStateFunctions[h],
handAnimationStateProperties[h]);
}
// Hand position ...
handOffset = hands[h].controller.getAbsTranslation();
handRotation = hands[h].controller.getAbsRotation();
@ -362,37 +401,41 @@ var leapHands = (function () {
// 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
x: -handOffset.x,
y: -handOffset.z,
z: -handOffset.y - hands[h].zeroPosition.z
};
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 ...
// 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.x,
x: -handRotation.y,
y: -handRotation.z,
z: -handRotation.y,
z: -handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation);
cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation);
handRotation = Quat.multiply(inverseAvatarOrientation, handRotation);
handRotation = Quat.multiply(HMD_CAMERA_TO_AVATAR_ROTATION[h], handRotation);
cameraOrientation = {
x: cameraOrientation.z,
y: cameraOrientation.y,
z: cameraOrientation.x,
w: cameraOrientation.w
};
cameraOrientation = Quat.multiply(cameraOrientation, Quat.inverse(MyAvatar.orientation));
handRotation = Quat.multiply(handRotation, cameraOrientation); // Works!!!
} else {
@ -411,18 +454,19 @@ var leapHands = (function () {
// Hand rotation in camera coordinates ...
handRotation = {
x: -handRotation.x,
y: -handRotation.z,
z: -handRotation.y,
x: handRotation.z,
y: handRotation.y,
z: handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(DESKTOP_CAMERA_TO_AVATAR_ROTATION, handRotation);
}
// Set hand position and orientation ...
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
// Set hand position and orientation for animation state handler ...
hands[h].position = handOffset;
hands[h].rotation = handRotation;
// Set finger joints ...
for (i = 0; i < NUM_FINGERS; i += 1) {
@ -436,6 +480,10 @@ var leapHands = (function () {
z: side * -locRotation.x,
w: locRotation.w
};
if (j === 0) {
// Adjust avatar thumb root joint rotation to make avatar hands look better
locRotation = Quat.multiply(LEAP_THUMB_ROOT_ADJUST[h], locRotation);
}
} else {
locRotation = {
x: -locRotation.x,
@ -458,14 +506,9 @@ var leapHands = (function () {
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");
if (handAnimationStateHandlers[h] !== null) {
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
handAnimationStateHandlers[h] = null;
}
}
}
@ -483,6 +526,9 @@ var leapHands = (function () {
for (h = 0; h < NUM_HANDS; h += 1) {
Controller.releaseInputController(hands[h].controller);
Controller.releaseInputController(wrists[h].controller);
if (handAnimationStateHandlers[h] !== null) {
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
}
for (i = 0; i < NUM_FINGERS; i += 1) {
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
if (fingers[h][i][j].controller !== null) {

View file

@ -226,6 +226,7 @@
var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
var elParentID = document.getElementById("property-parent-id");
var elParentJointIndex = document.getElementById("property-parent-joint-index");
var elRegistrationX = document.getElementById("property-reg-x");
var elRegistrationY = document.getElementById("property-reg-y");
@ -456,6 +457,7 @@
elDimensionsZ.value = properties.dimensions.z.toFixed(2);
elParentID.value = properties.parentID;
elParentJointIndex.value = properties.parentJointIndex;
elRegistrationX.value = properties.registrationPoint.x.toFixed(2);
elRegistrationY.value = properties.registrationPoint.y.toFixed(2);
@ -671,6 +673,7 @@
elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
@ -1067,6 +1070,12 @@
<input type="text" id="property-parent-id">
</div>
</div>
<div class="property">
<span class="label" style="float: left; margin-right: 6px">ParentJointIndex</span>
<div class="value" style="overflow: hidden;">
<input type="text" id="property-parent-joint-index">
</div>
</div>
<div class="property">
<div class="label">Registration</div>

View file

@ -67,12 +67,17 @@
}
var BOW_SPATIAL_KEY = {
relativePosition: {
x: 0,
leftRelativePosition: {
x: 0.05,
y: 0.06,
z: 0.11
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, 90)
rightRelativePosition: {
x: -0.05,
y: 0.06,
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
}

View file

@ -48,12 +48,17 @@ var bow = Entities.addEntity({
grabbableKey: {
invertSolidWhileHeld: true,
spatialKey: {
relativePosition: {
x: 0,
leftRelativePosition: {
x: 0.05,
y: 0.06,
z: 0.11
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, 90)
rightRelativePosition: {
x: -0.05,
y: 0.06,
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
}
}
})

View file

@ -12,31 +12,57 @@
Script.include("cookies.js");
var audioOptions = new AudioEffectOptions({
maxRoomSize: 50,
bandwidth: 10000,
preDelay: 20,
lateDelay: 0,
reverbTime: 2,
earlyDiffusion: 100,
lateDiffusion: 100,
roomSize: 50,
reverbTime: 4,
damping: 0.50,
inputBandwidth: 0.8,
earlyLevel: 0,
tailLevel: 0,
dryLevel: -6,
wetLevel: -6
density: 100,
bassMult: 1.5,
bassFreq: 250,
highGain: -6,
highFreq: 3000,
modRate: 2.3,
modDepth: 50,
earlyGain: 0,
lateGain: 0,
earlyMixLeft: 20,
earlyMixRight: 20,
lateMixLeft: 90,
lateMixRight: 90,
wetDryMix: 50,
});
AudioDevice.setReverbOptions(audioOptions);
AudioDevice.setReverb(true);
print("Reverb is ON.");
var panel = new Panel(10, 200);
var panel = new Panel(10, 160);
var parameters = [
{ name: "roomSize", min: 0, max: 100, units: " feet" },
{ name: "reverbTime", min: 0, max: 10, units: " sec" },
{ name: "damping", min: 0, max: 1, units: " " },
{ name: "inputBandwidth", min: 0, max: 1, units: " " },
{ name: "earlyLevel", min: -48, max: 0, units: " dB" },
{ name: "tailLevel", min: -48, max: 0, units: " dB" },
{ name: "wetLevel", min: -48, max: 0, units: " dB" },
{ name: "bandwidth", min: 1000, max: 12000, units: " Hz" },
{ name: "preDelay", min: 0, max: 333, units: " ms" },
{ name: "lateDelay", min: 0, max: 166, units: " ms" },
{ name: "reverbTime", min: 0.1, max: 10, units: " seconds" },
{ name: "earlyDiffusion", min: 0, max: 100, units: " percent" },
{ name: "lateDiffusion", min: 0, max: 100, units: " percent" },
{ name: "roomSize", min: 0, max: 100, units: " percent" },
{ name: "density", min: 0, max: 100, units: " percent" },
{ name: "bassMult", min: 0.1, max: 4, units: " ratio" },
{ name: "bassFreq", min: 10, max: 500, units: " Hz" },
{ name: "highGain", min: -24, max: 0, units: " dB" },
{ name: "highFreq", min: 1000, max: 12000, units: " Hz" },
{ name: "modRate", min: 0.1, max: 10, units: " Hz" },
{ name: "modDepth", min: 0, max: 100, units: " percent" },
{ name: "earlyGain", min: -96, max: 24, units: " dB" },
{ name: "lateGain", min: -96, max: 24, units: " dB" },
{ name: "earlyMixLeft", min: 0, max: 100, units: " percent" },
{ name: "earlyMixRight", min: 0, max: 100, units: " percent" },
{ name: "lateMixLeft", min: 0, max: 100, units: " percent" },
{ name: "lateMixRight", min: 0, max: 100, units: " percent" },
{ name: "wetDryMix", min: 0, max: 100, units: " percent" },
]
function setter(name) {
@ -48,7 +74,7 @@ function getter(name) {
}
function displayer(units) {
return function(value) { return (value).toFixed(1) + units; };
return function(value) { return (value).toFixed(1) + units; }
}
// create a slider for each parameter

View file

@ -194,7 +194,6 @@ else (APPLE)
# link target to external libraries
if (WIN32)
# target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
else (WIN32)
# Nothing else required on linux apparently
@ -202,3 +201,4 @@ else (APPLE)
endif (APPLE)
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -1077,6 +1077,11 @@ void Application::initializeUi() {
}
void Application::paintGL() {
// paintGL uses a queued connection, so we can get messages from the queue even after we've quit
// and the plugins have shutdown
if (_aboutToQuit) {
return;
}
_frameCount++;
// update fps moving average
@ -1117,29 +1122,6 @@ void Application::paintGL() {
_inPaint = true;
Finally clearFlagLambda([this] { _inPaint = false; });
// Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't
// include time waiting for vsync, and which can report a number above target if we've got the headroom.
// For example, if we're shooting for 75fps and paintWait is 3.3333ms (= 75% * 13.33ms), our deducedNonVSyncFps
// would be 100fps. In principle, a paintWait of zero would have deducedNonVSyncFps=75.
// Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod.
//
// Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing.
// We're using this as a proxy for the time between vsync and displayEnd, below. (Not exact, but tends to be the same over time.)
// This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second.
const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize
// Note that _lastPaintWait (stored at end of last call) is for the same paint cycle.
float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + _marginForDeducedFramePeriod; // plus a some non-zero time for machinery we can't measure
// We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion
// of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles,
// the key part (and not an exagerated part) of _lastPaintWait is accounted for.
const float targetPeriod = getTargetFramePeriod();
if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) {
// Don't use C++ remainder(). It's authors are mathematically insane.
deducedNonVSyncPeriod += fmod(actualPeriod, _lastPaintWait);
}
_lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod;
_lastInstantaneousFps = instantaneousFps;
auto displayPlugin = getActiveDisplayPlugin();
// FIXME not needed anymore?
_offscreenContext->makeCurrent();
@ -1396,7 +1378,6 @@ void Application::paintGL() {
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
uint64_t displayStart = usecTimestampNow();
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
{
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
@ -1405,9 +1386,6 @@ void Application::paintGL() {
}
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
uint64_t displayEnd = usecTimestampNow();
const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs
_lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND;
}
{
@ -1418,6 +1396,14 @@ void Application::paintGL() {
batch.resetStages();
});
}
// Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't
// include time waiting for sync, and which can report a number above target if we've got the headroom.
// In my tests, the following is mostly less than 0.5ms, and never more than 3ms. I don't think its worth measuring during runtime.
static const float paintWaitAndQTTimerAllowance = 0.001; // seconds
// Store both values now for use by next cycle.
_lastInstantaneousFps = instantaneousFps;
_lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance);
}
void Application::runTests() {
@ -2414,6 +2400,9 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
exportTree->addEntity(entityItem->getEntityItemID(), properties);
}
// remap IDs on export so that we aren't publishing the IDs of entities in our domain
exportTree->remapIDs();
exportTree->writeToJSONFile(filename.toLocal8Bit().constData());
// restore the main window's active state
@ -2436,6 +2425,10 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
properties.setPosition(properties.getPosition() - root);
exportTree->addEntity(id, properties);
}
// remap IDs on export so that we aren't publishing the IDs of entities in our domain
exportTree->remapIDs();
exportTree->writeToSVOFile(filename.toLocal8Bit().constData());
} else {
qCDebug(interfaceapp) << "No models were selected";
@ -2480,6 +2473,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
bool success = _entityClipboard->readFromURL(url.toString());
if (success) {
_entityClipboard->remapIDs();
_entityClipboard->reaverageOctreeElements();
}
return success;
@ -3241,8 +3235,6 @@ bool Application::isHMDMode() const {
return getActiveDisplayPlugin()->isHmd();
}
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
float Application::getTargetFramePeriod() { return getActiveDisplayPlugin()->getTargetFramePeriod(); }
bool Application::isVSynchronized() const { return getActiveDisplayPlugin()->isVSynchronized(); }
QRect Application::getDesirableApplicationGeometry() {
QRect applicationGeometry = getWindow()->geometry();
@ -4074,7 +4066,7 @@ bool Application::canAcceptURL(const QString& urlString) {
QString lowerPath = url.path().toLower();
while (i.hasNext()) {
i.next();
if (lowerPath.endsWith(i.key())) {
if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {
return true;
}
}
@ -4094,7 +4086,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
QString lowerPath = url.path().toLower();
while (i.hasNext()) {
i.next();
if (lowerPath.endsWith(i.key())) {
if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {
AcceptURLMethod method = i.value();
return (this->*method)(urlString);
}
@ -4194,7 +4186,7 @@ bool Application::askToUploadAsset(const QString& filename) {
messageBox.setDefaultButton(QMessageBox::Ok);
// Option to drop model in world for models
if (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION)) {
if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive) || filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) {
auto checkBox = new QCheckBox(&messageBox);
checkBox->setText("Add to scene");
messageBox.setCheckBox(checkBox);
@ -4229,7 +4221,8 @@ void Application::modelUploadFinished(AssetUpload* upload, const QString& hash)
auto filename = QFileInfo(upload->getFilename()).fileName();
if ((upload->getError() == AssetUpload::NoError) &&
(filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION))) {
(upload->getExtension().endsWith(FBX_EXTENSION, Qt::CaseInsensitive) ||
upload->getExtension().endsWith(OBJ_EXTENSION, Qt::CaseInsensitive))) {
auto entities = DependencyManager::get<EntityScriptingInterface>();

View file

@ -124,7 +124,6 @@ public:
PickRay computePickRay() const;
bool isThrottleRendering() const;
bool isVSynchronized() const;
Camera* getCamera() { return &_myCamera; }
// Represents the current view frustum of the avatar.
@ -162,11 +161,8 @@ public:
uint32_t getFrameCount() { return _frameCount; }
float getFps() const { return _fps; }
float getTargetFrameRate(); // frames/second
float getTargetFramePeriod(); // seconds
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
float getLastPaintWait() const { return _lastPaintWait; };
float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; }
void setMarginForDeducedFramePeriod(float newValue) { _marginForDeducedFramePeriod = newValue; }
float getLastUnsynchronizedFps() const { return _lastUnsynchronizedFps; }
float getFieldOfView() { return _fieldOfView.get(); }
void setFieldOfView(float fov);
@ -441,9 +437,7 @@ private:
QElapsedTimer _timerStart;
QElapsedTimer _lastTimeUpdated;
float _lastInstantaneousFps { 0.0f };
float _lastPaintWait { 0.0f };
float _lastDeducedNonVSyncFps { 0.0f };
float _marginForDeducedFramePeriod{ 0.002f }; // 2ms, adjustable
float _lastUnsynchronizedFps { 0.0f };
ShapeManager _shapeManager;
PhysicalEntitySimulation _entitySimulation;

View file

@ -21,17 +21,17 @@
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case ACTION_TYPE_NONE:
return nullptr;
return EntityActionPointer();
case ACTION_TYPE_OFFSET:
return (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case ACTION_TYPE_SPRING:
return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case ACTION_TYPE_HOLD:
return (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
return std::make_shared<AvatarActionHold>(id, ownerEntity);
}
assert(false);
return nullptr;
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
return EntityActionPointer();
}

View file

@ -201,6 +201,7 @@ void Avatar::simulate(float deltaTime) {
_skeletonModel.getRig()->copyJointsFromJointData(_jointData);
_skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations);
simulateAttachments(deltaTime);
locationChanged(); // joints changed, so if there are any children, update them.
_hasNewJointRotations = false;
_hasNewJointTranslations = false;
}
@ -868,6 +869,17 @@ glm::vec3 Avatar::getJointTranslation(int index) const {
return translation;
}
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
glm::quat rotation;
_skeletonModel.getAbsoluteJointRotationInRigFrame(index, rotation);
return Quaternions::Y_180 * rotation;
}
glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
glm::vec3 translation;
_skeletonModel.getAbsoluteJointTranslationInRigFrame(index, translation);
return Quaternions::Y_180 * translation;
}
int Avatar::getJointIndex(const QString& name) const {
if (QThread::currentThread() != thread()) {
@ -1145,12 +1157,12 @@ glm::quat Avatar::getRightPalmRotation() {
return rightRotation;
}
void Avatar::setPosition(const glm::vec3 position) {
void Avatar::setPosition(const glm::vec3& position) {
AvatarData::setPosition(position);
updateAttitude();
}
void Avatar::setOrientation(const glm::quat orientation) {
void Avatar::setOrientation(const glm::quat& orientation) {
AvatarData::setOrientation(orientation);
updateAttitude();
}

View file

@ -108,6 +108,9 @@ public:
virtual int getJointIndex(const QString& name) const;
virtual QStringList getJointNames() const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
virtual void setFaceModelURL(const QUrl& faceModelURL);
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
@ -155,8 +158,8 @@ public:
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; }
virtual void setPosition(glm::vec3 position);
virtual void setOrientation(glm::quat orientation);
virtual void setPosition(const glm::vec3& position) override;
virtual void setOrientation(const glm::quat& orientation) override;
public slots:

View file

@ -9,63 +9,72 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "QVariantGLM.h"
#include "avatar/AvatarManager.h"
#include "AvatarActionHold.h"
#include <QVariantGLM.h>
#include "avatar/AvatarManager.h"
const uint16_t AvatarActionHold::holdVersion = 1;
AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectActionSpring(id, ownerEntity),
_relativePosition(glm::vec3(0.0f)),
_relativeRotation(glm::quat()),
_hand("right"),
_holderID(QUuid()) {
ObjectActionSpring(id, ownerEntity)
{
_type = ACTION_TYPE_HOLD;
#if WANT_DEBUG
#if WANT_DEBUG
qDebug() << "AvatarActionHold::AvatarActionHold";
#endif
#endif
}
AvatarActionHold::~AvatarActionHold() {
#if WANT_DEBUG
#if WANT_DEBUG
qDebug() << "AvatarActionHold::~AvatarActionHold";
#endif
#endif
}
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
std::shared_ptr<Avatar> holdingAvatar = nullptr;
auto avatarManager = DependencyManager::get<AvatarManager>();
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
if (!holdingAvatar) {
return holdingAvatar;
}
withTryReadLock([&]{
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID);
holdingAvatar = std::static_pointer_cast<Avatar>(holdingAvatarData);
if (holdingAvatar) {
glm::vec3 offset;
glm::vec3 palmPosition;
glm::quat palmRotation;
if (_hand == "right") {
bool isRightHand = (_hand == "right");
glm::vec3 palmPosition { Vectors::ZERO };
glm::quat palmRotation { Quaternions::IDENTITY };
if (_ignoreIK && holdingAvatar->isMyAvatar()) {
// We cannot ignore other avatars IK and this is not the point of this option
// This is meant to make the grabbing behavior more reactive.
if (isRightHand) {
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::RightHand).getPosition();
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::RightHand).getRotation();
} else {
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition();
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation();
}
} else {
if (isRightHand) {
palmPosition = holdingAvatar->getRightPalmPosition();
palmRotation = holdingAvatar->getRightPalmRotation();
} else {
palmPosition = holdingAvatar->getLeftPalmPosition();
palmRotation = holdingAvatar->getLeftPalmRotation();
}
rotation = palmRotation * _relativeRotation;
offset = rotation * _relativePosition;
position = palmPosition + offset;
}
rotation = palmRotation * _relativeRotation;
position = palmPosition + rotation * _relativePosition;
});
return holdingAvatar;
}
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
glm::quat rotation;
glm::vec3 position;
glm::quat rotation { Quaternions::IDENTITY };
glm::vec3 position { Vectors::ZERO };
bool valid = false;
int holdCount = 0;
@ -168,6 +177,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
QUuid holderID;
bool kinematic;
bool kinematicSetVelocity;
bool ignoreIK;
bool needUpdate = false;
bool somethingChanged = ObjectAction::updateArguments(arguments);
@ -203,14 +213,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
ok = true;
kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
if (!ok) {
_kinematic = false;
kinematic = _kinematic;
}
ok = true;
kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments,
"kinematicSetVelocity", ok, false);
if (!ok) {
_kinematicSetVelocity = false;
kinematicSetVelocity = _kinematicSetVelocity;
}
ok = true;
ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
if (!ok) {
ignoreIK = _ignoreIK;
}
if (somethingChanged ||
@ -220,7 +236,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
hand != _hand ||
holderID != _holderID ||
kinematic != _kinematic ||
kinematicSetVelocity != _kinematicSetVelocity) {
kinematicSetVelocity != _kinematicSetVelocity ||
ignoreIK != _ignoreIK) {
needUpdate = true;
}
});
@ -236,6 +253,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
_holderID = holderID;
_kinematic = kinematic;
_kinematicSetVelocity = kinematicSetVelocity;
_ignoreIK = ignoreIK;
_active = true;
auto ownerEntity = _ownerEntity.lock();
@ -260,6 +278,7 @@ QVariantMap AvatarActionHold::getArguments() {
arguments["hand"] = _hand;
arguments["kinematic"] = _kinematic;
arguments["kinematicSetVelocity"] = _kinematicSetVelocity;
arguments["ignoreIK"] = _ignoreIK;
});
return arguments;
}

View file

@ -38,17 +38,19 @@ public:
std::shared_ptr<Avatar> getTarget(glm::quat& rotation, glm::vec3& position);
private:
void doKinematicUpdate(float deltaTimeStep);
static const uint16_t holdVersion;
glm::vec3 _relativePosition;
glm::quat _relativeRotation;
QString _hand;
glm::vec3 _relativePosition { Vectors::ZERO };
glm::quat _relativeRotation { Quaternions::IDENTITY };
QString _hand { "right" };
QUuid _holderID;
void doKinematicUpdate(float deltaTimeStep);
bool _kinematic { false };
bool _kinematicSetVelocity { false };
bool _previousSet { false };
bool _ignoreIK { false };
glm::vec3 _previousPositionalTarget;
glm::quat _previousRotationalTarget;

View file

@ -111,7 +111,6 @@ void AvatarManager::init() {
_renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
_renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP.
_renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there.
}
void AvatarManager::updateMyAvatar(float deltaTime) {
@ -147,18 +146,16 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
PerformanceTimer perfTimer("otherAvatars");
float distance;
if (qApp->isVSynchronized()) {
if (!qApp->isThrottleRendering()) {
_renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight.
// The PID controller raises the controlled value when the measured value goes up.
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
// goes up.
const float deduced = qApp->getLastDeducedNonVSyncFps();
const float deduced = qApp->getLastUnsynchronizedFps();
distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
} else {
// We could keep the controller running when not vsync'd, if getLastDeducedNonVSyncFps is still meaningful.
// But the basic 2d controller doesn't try to adjust the timer for qt load or getLastPaintWait, so running the
// Here we choose to just use the maximum render cutoff distance if: throttled, running without vsync, or 30-60 "fixed" targets.
// Here we choose to just use the maximum render cutoff distance if throttled.
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
}
_renderDistanceAverage.updateAverage(distance);
@ -417,7 +414,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) {
if (sessionID == _myAvatar->getSessionUUID()) {
return std::static_pointer_cast<Avatar>(_myAvatar);
return _myAvatar;
}
return findAvatar(sessionID);

View file

@ -560,7 +560,7 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
if (!_shouldRender) {
return; // exit early
}
Avatar::render(renderArgs, cameraPosition);
}
@ -967,8 +967,6 @@ void MyAvatar::clearJointData(int index) {
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
return;
}
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
_rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f);
_rig->clearJointAnimationPriority(index);
}
@ -1188,7 +1186,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
if (!_skeletonModel.isRenderable()) {
return; // wait until all models are loaded
}
fixupModelsInScene();
// Render head so long as the camera isn't inside it

View file

@ -254,6 +254,7 @@ public slots:
void setEnableDebugDrawDefaultPose(bool isEnabled);
void setEnableDebugDrawAnimPose(bool isEnabled);
void setEnableDebugDrawPosition(bool isEnabled);
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
void setEnableMeshVisible(bool isEnabled);
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
@ -276,7 +277,7 @@ private:
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
bool getShouldRenderLocally() const { return _shouldRender; }
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
bool isMyAvatar() const override { return true; }

View file

@ -622,7 +622,8 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS
fileDialog.setAcceptMode(acceptMode);
QUrl fileUrl(directory);
if (acceptMode == QFileDialog::AcceptSave) {
fileDialog.setFileMode(QFileDialog::Directory);
// TODO -- Setting this breaks the dialog on Linux. Does it help something on other platforms?
// fileDialog.setFileMode(QFileDialog::Directory);
fileDialog.selectFile(fileUrl.fileName());
}
if (fileDialog.exec()) {

View file

@ -33,8 +33,8 @@
const int PREFERENCES_HEIGHT_PADDING = 20;
PreferencesDialog::PreferencesDialog(QWidget* parent) :
QDialog(parent) {
QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose);
ui.setupUi(this);
@ -48,10 +48,8 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, qApp, &Application::loadDefaultScripts);
connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser);
connect(ui.appearanceDescription, &QLineEdit::textChanged, this, [this](const QString& url) {
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(url, "");
this->fullAvatarURLChanged(url, "");
});
connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL);
connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged);
// move dialog to left side
@ -61,6 +59,11 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
UIUtil::scaleWidgetFontSizes(this);
}
void PreferencesDialog::changeFullAvatarURL() {
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(ui.appearanceDescription->text(), "");
this->fullAvatarURLChanged(ui.appearanceDescription->text(), "");
}
void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
ui.appearanceDescription->setText(newValue);
const QString APPEARANCE_LABEL_TEXT("Appearance: ");
@ -69,9 +72,17 @@ void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QStr
void PreferencesDialog::accept() {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
// if there is an attempted change to the full avatar URL, apply it now
if (QUrl(ui.appearanceDescription->text()) != myAvatar->getFullAvatarURLFromPreferences()) {
changeFullAvatarURL();
}
_lastGoodAvatarURL = myAvatar->getFullAvatarURLFromPreferences();
_lastGoodAvatarName = myAvatar->getFullAvatarModelName();
savePreferences();
close();
delete _marketplaceWindow;
_marketplaceWindow = NULL;

View file

@ -49,6 +49,7 @@ private slots:
void openFullAvatarModelBrowser();
void openSnapshotLocationBrowser();
void openScriptsLocationBrowser();
void changeFullAvatarURL();
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
};

View file

@ -294,6 +294,7 @@ void Rig::clearJointStates() {
void Rig::clearJointAnimationPriority(int index) {
if (isIndexValid(index)) {
_internalPoseSet._overrideFlags[index] = false;
_internalPoseSet._overridePoses[index] = _animSkeleton->getRelativeDefaultPose(index);
}
}
@ -373,6 +374,16 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
}
}
bool Rig::getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const {
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
rotation = _externalPoseSet._absolutePoses[jointIndex].rot;
return true;
} else {
return false;
}
}
bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const {
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._relativePoses.size()) {
@ -383,6 +394,16 @@ bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const {
}
}
bool Rig::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const {
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
translation = _externalPoseSet._absolutePoses[jointIndex].trans;
return true;
} else {
return false;
}
}
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
// AJT: TODO: used by attachments
ASSERT(false);

View file

@ -129,10 +129,12 @@ public:
// geometry space (thread-safe)
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
// geometry space (thread-safe)
bool getJointTranslation(int jointIndex, glm::vec3& translation) const;
// rig space (thread-safe)
bool getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const;
bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const;
// legacy
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;

View file

@ -541,26 +541,40 @@ bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
void AudioClient::configureReverb() {
ReverbParameters p;
_listenerReverb.getParameters(&p);
// for now, reuse the gverb parameters
p.sampleRate = _outputFormat.sampleRate();
p.roomSize = _reverbOptions->getRoomSize();
p.bandwidth = _reverbOptions->getBandwidth();
p.preDelay = _reverbOptions->getPreDelay();
p.lateDelay = _reverbOptions->getLateDelay();
p.reverbTime = _reverbOptions->getReverbTime();
p.highGain = -24.0f * (1.0f - _reverbOptions->getDamping());
p.bandwidth = 10000.0f * _reverbOptions->getInputBandwidth();
p.earlyGain = _reverbOptions->getEarlyLevel();
p.lateGain = _reverbOptions->getTailLevel();
p.wetDryMix = 100.0f * powf(10.0f, _reverbOptions->getWetLevel() * (1/20.0f));
p.earlyDiffusion = _reverbOptions->getEarlyDiffusion();
p.lateDiffusion = _reverbOptions->getLateDiffusion();
p.roomSize = _reverbOptions->getRoomSize();
p.density = _reverbOptions->getDensity();
p.bassMult = _reverbOptions->getBassMult();
p.bassFreq = _reverbOptions->getBassFreq();
p.highGain = _reverbOptions->getHighGain();
p.highFreq = _reverbOptions->getHighFreq();
p.modRate = _reverbOptions->getModRate();
p.modDepth = _reverbOptions->getModDepth();
p.earlyGain = _reverbOptions->getEarlyGain();
p.lateGain = _reverbOptions->getLateGain();
p.earlyMixLeft = _reverbOptions->getEarlyMixLeft();
p.earlyMixRight = _reverbOptions->getEarlyMixRight();
p.lateMixLeft = _reverbOptions->getLateMixLeft();
p.lateMixRight = _reverbOptions->getLateMixRight();
p.wetDryMix = _reverbOptions->getWetDryMix();
_listenerReverb.setParameters(&p);
// used for adding self-reverb to loopback audio
// used only for adding self-reverb to loopback audio
p.wetDryMix = 100.0f;
p.preDelay = 0.0f;
p.earlyGain = -96.0f; // disable ER
p.lateGain -= 12.0f; // quieter than listener reverb
p.lateGain -= 12.0f; // quieter than listener reverb
p.lateMixLeft = 0.0f;
p.lateMixRight = 0.0f;
_sourceReverb.setParameters(&p);
}
@ -572,10 +586,10 @@ void AudioClient::updateReverbOptions() {
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
reverbChanged = true;
}
if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
reverbChanged = true;
}
//if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
// _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
// reverbChanged = true;
//}
if (_reverbOptions != &_zoneReverbOptions) {
_reverbOptions = &_zoneReverbOptions;
@ -602,17 +616,27 @@ void AudioClient::setReverb(bool reverb) {
void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
// Save the new options
_scriptReverbOptions.setMaxRoomSize(options->getMaxRoomSize());
_scriptReverbOptions.setRoomSize(options->getRoomSize());
_scriptReverbOptions.setBandwidth(options->getBandwidth());
_scriptReverbOptions.setPreDelay(options->getPreDelay());
_scriptReverbOptions.setLateDelay(options->getLateDelay());
_scriptReverbOptions.setReverbTime(options->getReverbTime());
_scriptReverbOptions.setDamping(options->getDamping());
_scriptReverbOptions.setSpread(options->getSpread());
_scriptReverbOptions.setInputBandwidth(options->getInputBandwidth());
_scriptReverbOptions.setEarlyLevel(options->getEarlyLevel());
_scriptReverbOptions.setTailLevel(options->getTailLevel());
_scriptReverbOptions.setDryLevel(options->getDryLevel());
_scriptReverbOptions.setWetLevel(options->getWetLevel());
_scriptReverbOptions.setEarlyDiffusion(options->getEarlyDiffusion());
_scriptReverbOptions.setLateDiffusion(options->getLateDiffusion());
_scriptReverbOptions.setRoomSize(options->getRoomSize());
_scriptReverbOptions.setDensity(options->getDensity());
_scriptReverbOptions.setBassMult(options->getBassMult());
_scriptReverbOptions.setBassFreq(options->getBassFreq());
_scriptReverbOptions.setHighGain(options->getHighGain());
_scriptReverbOptions.setHighFreq(options->getHighFreq());
_scriptReverbOptions.setModRate(options->getModRate());
_scriptReverbOptions.setModDepth(options->getModDepth());
_scriptReverbOptions.setEarlyGain(options->getEarlyGain());
_scriptReverbOptions.setLateGain(options->getLateGain());
_scriptReverbOptions.setEarlyMixLeft(options->getEarlyMixLeft());
_scriptReverbOptions.setEarlyMixRight(options->getEarlyMixRight());
_scriptReverbOptions.setLateMixLeft(options->getLateMixLeft());
_scriptReverbOptions.setLateMixRight(options->getLateMixRight());
_scriptReverbOptions.setWetDryMix(options->getWetDryMix());
if (_reverbOptions == &_scriptReverbOptions) {
// Apply them to the reverb instances

View file

@ -10,58 +10,76 @@
#include "AudioEffectOptions.h"
static const QString MAX_ROOM_SIZE_HANDLE = "maxRoomSize";
static const QString ROOM_SIZE_HANDLE = "roomSize";
static const QString BANDWIDTH_HANDLE = "bandwidth";
static const QString PRE_DELAY_HANDLE = "preDelay";
static const QString LATE_DELAY_HANDLE = "lateDelay";
static const QString REVERB_TIME_HANDLE = "reverbTime";
static const QString DAMPIMG_HANDLE = "damping";
static const QString SPREAD_HANDLE = "spread";
static const QString INPUT_BANDWIDTH_HANDLE = "inputBandwidth";
static const QString EARLY_LEVEL_HANDLE = "earlyLevel";
static const QString TAIL_LEVEL_HANDLE = "tailLevel";
static const QString DRY_LEVEL_HANDLE = "dryLevel";
static const QString WET_LEVEL_HANDLE = "wetLevel";
static const QString EARLY_DIFFUSION_HANDLE = "earlyDiffusion";
static const QString LATE_DIFFUSION_HANDLE = "lateDiffusion";
static const QString ROOM_SIZE_HANDLE = "roomSize";
static const QString DENSITY_HANDLE = "density";
static const QString BASS_MULT_HANDLE = "bassMult";
static const QString BASS_FREQ_HANDLE = "bassFreq";
static const QString HIGH_GAIN_HANDLE = "highGain";
static const QString HIGH_FREQ_HANDLE = "highFreq";
static const QString MOD_RATE_HANDLE = "modRate";
static const QString MOD_DEPTH_HANDLE = "modDepth";
static const QString EARLY_GAIN_HANDLE = "earlyGain";
static const QString LATE_GAIN_HANDLE = "lateGain";
static const QString EARLY_MIX_LEFT_HANDLE = "earlyMixLeft";
static const QString EARLY_MIX_RIGHT_HANDLE = "earlyMixRight";
static const QString LATE_MIX_LEFT_HANDLE = "lateMixLeft";
static const QString LATE_MIX_RIGHT_HANDLE = "lateMixRight";
static const QString WET_DRY_MIX_HANDLE = "wetDryMix";
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) :
_maxRoomSize(50.0f),
_roomSize(50.0f),
_reverbTime(4.0f),
_damping(0.5f),
_spread(15.0f),
_inputBandwidth(0.75f),
_earlyLevel(-12.0f),
_tailLevel(-18.0f),
_dryLevel(0.0f),
_wetLevel(0.0f) {
if (arguments.property(MAX_ROOM_SIZE_HANDLE).isNumber()) {
_maxRoomSize = arguments.property(MAX_ROOM_SIZE_HANDLE).toNumber();
}
if (arguments.property(ROOM_SIZE_HANDLE).isNumber()) {
_roomSize = arguments.property(ROOM_SIZE_HANDLE).toNumber();
}
if (arguments.property(REVERB_TIME_HANDLE).isNumber()) {
_reverbTime = arguments.property(REVERB_TIME_HANDLE).toNumber();
}
if (arguments.property(DAMPIMG_HANDLE).isNumber()) {
_damping = arguments.property(DAMPIMG_HANDLE).toNumber();
}
if (arguments.property(SPREAD_HANDLE).isNumber()) {
_spread = arguments.property(SPREAD_HANDLE).toNumber();
}
if (arguments.property(INPUT_BANDWIDTH_HANDLE).isNumber()) {
_inputBandwidth = arguments.property(INPUT_BANDWIDTH_HANDLE).toNumber();
}
if (arguments.property(EARLY_LEVEL_HANDLE).isNumber()) {
_earlyLevel = arguments.property(EARLY_LEVEL_HANDLE).toNumber();
}
if (arguments.property(TAIL_LEVEL_HANDLE).isNumber()) {
_tailLevel = arguments.property(TAIL_LEVEL_HANDLE).toNumber();
}
if (arguments.property(DRY_LEVEL_HANDLE).isNumber()) {
_dryLevel = arguments.property(DRY_LEVEL_HANDLE).toNumber();
}
if (arguments.property(WET_LEVEL_HANDLE).isNumber()) {
_wetLevel = arguments.property(WET_LEVEL_HANDLE).toNumber();
}
static const float BANDWIDTH_DEFAULT = 10000.0f;
static const float PRE_DELAY_DEFAULT = 20.0f;
static const float LATE_DELAY_DEFAULT = 0.0f;
static const float REVERB_TIME_DEFAULT = 2.0f;
static const float EARLY_DIFFUSION_DEFAULT = 100.0f;
static const float LATE_DIFFUSION_DEFAULT = 100.0f;
static const float ROOM_SIZE_DEFAULT = 50.0f;
static const float DENSITY_DEFAULT = 100.0f;
static const float BASS_MULT_DEFAULT = 1.5f;
static const float BASS_FREQ_DEFAULT = 250.0f;
static const float HIGH_GAIN_DEFAULT = -6.0f;
static const float HIGH_FREQ_DEFAULT = 3000.0f;
static const float MOD_RATE_DEFAULT = 2.3f;
static const float MOD_DEPTH_DEFAULT = 50.0f;
static const float EARLY_GAIN_DEFAULT = 0.0f;
static const float LATE_GAIN_DEFAULT = 0.0f;
static const float EARLY_MIX_LEFT_DEFAULT = 20.0f;
static const float EARLY_MIX_RIGHT_DEFAULT = 20.0f;
static const float LATE_MIX_LEFT_DEFAULT = 90.0f;
static const float LATE_MIX_RIGHT_DEFAULT = 90.0f;
static const float WET_DRY_MIX_DEFAULT = 50.0f;
static void setOption(QScriptValue arguments, const QString name, float defaultValue, float& variable) {
variable = arguments.property(name).isNumber() ? arguments.property(name).toNumber() : defaultValue;
}
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth);
setOption(arguments, PRE_DELAY_HANDLE, PRE_DELAY_DEFAULT, _preDelay);
setOption(arguments, LATE_DELAY_HANDLE, LATE_DELAY_DEFAULT, _lateDelay);
setOption(arguments, REVERB_TIME_HANDLE, REVERB_TIME_DEFAULT, _reverbTime);
setOption(arguments, EARLY_DIFFUSION_HANDLE, EARLY_DIFFUSION_DEFAULT, _earlyDiffusion);
setOption(arguments, LATE_DIFFUSION_HANDLE, LATE_DIFFUSION_DEFAULT, _lateDiffusion);
setOption(arguments, ROOM_SIZE_HANDLE, ROOM_SIZE_DEFAULT, _roomSize);
setOption(arguments, DENSITY_HANDLE, DENSITY_DEFAULT, _density);
setOption(arguments, BASS_MULT_HANDLE, BASS_MULT_DEFAULT, _bassMult);
setOption(arguments, BASS_FREQ_HANDLE, BASS_FREQ_DEFAULT, _bassFreq);
setOption(arguments, HIGH_GAIN_HANDLE, HIGH_GAIN_DEFAULT, _highGain);
setOption(arguments, HIGH_FREQ_HANDLE, HIGH_FREQ_DEFAULT, _highFreq);
setOption(arguments, MOD_RATE_HANDLE, MOD_RATE_DEFAULT, _modRate);
setOption(arguments, MOD_DEPTH_HANDLE, MOD_DEPTH_DEFAULT, _modDepth);
setOption(arguments, EARLY_GAIN_HANDLE, EARLY_GAIN_DEFAULT, _earlyGain);
setOption(arguments, LATE_GAIN_HANDLE, LATE_GAIN_DEFAULT, _lateGain);
setOption(arguments, EARLY_MIX_LEFT_HANDLE, EARLY_MIX_LEFT_DEFAULT, _earlyMixLeft);
setOption(arguments, EARLY_MIX_RIGHT_HANDLE, EARLY_MIX_RIGHT_DEFAULT, _earlyMixRight);
setOption(arguments, LATE_MIX_LEFT_HANDLE, LATE_MIX_LEFT_DEFAULT, _lateMixLeft);
setOption(arguments, LATE_MIX_RIGHT_HANDLE, LATE_MIX_RIGHT_DEFAULT, _lateMixRight);
setOption(arguments, WET_DRY_MIX_HANDLE, WET_DRY_MIX_DEFAULT, _wetDryMix);
}
AudioEffectOptions::AudioEffectOptions(const AudioEffectOptions &other) : QObject() {
@ -69,17 +87,28 @@ AudioEffectOptions::AudioEffectOptions(const AudioEffectOptions &other) : QObjec
}
AudioEffectOptions& AudioEffectOptions::operator=(const AudioEffectOptions &other) {
_maxRoomSize = other._maxRoomSize;
_roomSize = other._roomSize;
_bandwidth = other._bandwidth;
_preDelay = other._preDelay;
_lateDelay = other._lateDelay;
_reverbTime = other._reverbTime;
_damping = other._damping;
_spread = other._spread;
_inputBandwidth = other._inputBandwidth;
_earlyLevel = other._earlyLevel;
_tailLevel = other._tailLevel;
_dryLevel = other._dryLevel;
_wetLevel = other._wetLevel;
_earlyDiffusion = other._earlyDiffusion;
_lateDiffusion = other._lateDiffusion;
_roomSize = other._roomSize;
_density = other._density;
_bassMult = other._bassMult;
_bassFreq = other._bassFreq;
_highGain = other._highGain;
_highFreq = other._highFreq;
_modRate = other._modRate;
_modDepth = other._modDepth;
_earlyGain = other._earlyGain;
_lateGain = other._lateGain;
_earlyMixLeft = other._earlyMixLeft;
_earlyMixRight = other._earlyMixRight;
_lateMixLeft = other._lateMixLeft;
_lateMixRight = other._lateMixRight;
_wetDryMix = other._wetDryMix;
return *this;
}

View file

@ -15,32 +15,30 @@
#include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine>
#include "AudioReverb.h"
class AudioEffectOptions : public QObject {
Q_OBJECT
// Meters Square
Q_PROPERTY(float maxRoomSize READ getMaxRoomSize WRITE setMaxRoomSize)
Q_PROPERTY(float roomSize READ getRoomSize WRITE setRoomSize)
// Seconds
Q_PROPERTY(float bandwidth READ getBandwidth WRITE setBandwidth)
Q_PROPERTY(float preDelay READ getPreDelay WRITE setPreDelay)
Q_PROPERTY(float lateDelay READ getLateDelay WRITE setLateDelay)
Q_PROPERTY(float reverbTime READ getReverbTime WRITE setReverbTime)
// Ratio between 0 and 1
Q_PROPERTY(float damping READ getDamping WRITE setDamping)
// (?) Does not appear to be set externally very often
Q_PROPERTY(float spread READ getSpread WRITE setSpread)
// Ratio between 0 and 1
Q_PROPERTY(float inputBandwidth READ getInputBandwidth WRITE setInputBandwidth)
// in dB
Q_PROPERTY(float earlyLevel READ getEarlyLevel WRITE setEarlyLevel)
Q_PROPERTY(float tailLevel READ getTailLevel WRITE setTailLevel)
Q_PROPERTY(float dryLevel READ getDryLevel WRITE setDryLevel)
Q_PROPERTY(float wetLevel READ getWetLevel WRITE setWetLevel)
Q_PROPERTY(float earlyDiffusion READ getEarlyDiffusion WRITE setEarlyDiffusion)
Q_PROPERTY(float lateDiffusion READ getLateDiffusion WRITE setLateDiffusion)
Q_PROPERTY(float roomSize READ getRoomSize WRITE setRoomSize)
Q_PROPERTY(float density READ getDensity WRITE setDensity)
Q_PROPERTY(float bassMult READ getBassMult WRITE setBassMult)
Q_PROPERTY(float bassFreq READ getBassFreq WRITE setBassFreq)
Q_PROPERTY(float highGain READ getHighGain WRITE setHighGain)
Q_PROPERTY(float highFreq READ getHighFreq WRITE setHighFreq)
Q_PROPERTY(float modRate READ getModRate WRITE setModRate)
Q_PROPERTY(float modDepth READ getModDepth WRITE setModDepth)
Q_PROPERTY(float earlyGain READ getEarlyGain WRITE setEarlyGain)
Q_PROPERTY(float lateGain READ getLateGain WRITE setLateGain)
Q_PROPERTY(float earlyMixLeft READ getEarlyMixLeft WRITE setEarlyMixLeft)
Q_PROPERTY(float earlyMixRight READ getEarlyMixRight WRITE setEarlyMixRight)
Q_PROPERTY(float lateMixLeft READ getLateMixLeft WRITE setLateMixLeft)
Q_PROPERTY(float lateMixRight READ getLateMixRight WRITE setLateMixRight)
Q_PROPERTY(float wetDryMix READ getWetDryMix WRITE setWetDryMix)
public:
AudioEffectOptions(QScriptValue arguments = QScriptValue());
@ -49,60 +47,100 @@ public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
float getRoomSize() const { return _roomSize; }
void setRoomSize(float roomSize ) { _roomSize = roomSize; }
float getBandwidth() const { return _bandwidth; }
void setBandwidth(float bandwidth) { _bandwidth = bandwidth; }
float getMaxRoomSize() const { return _maxRoomSize; }
void setMaxRoomSize(float maxRoomSize ) { _maxRoomSize = maxRoomSize; }
float getPreDelay() const { return _preDelay; }
void setPreDelay(float preDelay) { _preDelay = preDelay; }
float getLateDelay() const { return _lateDelay; }
void setLateDelay(float lateDelay) { _lateDelay = lateDelay; }
float getReverbTime() const { return _reverbTime; }
void setReverbTime(float reverbTime ) { _reverbTime = reverbTime; }
void setReverbTime(float reverbTime) { _reverbTime = reverbTime; }
float getDamping() const { return _damping; }
void setDamping(float damping ) { _damping = damping; }
float getEarlyDiffusion() const { return _earlyDiffusion; }
void setEarlyDiffusion(float earlyDiffusion) { _earlyDiffusion = earlyDiffusion; }
float getSpread() const { return _spread; }
void setSpread(float spread ) { _spread = spread; }
float getLateDiffusion() const { return _lateDiffusion; }
void setLateDiffusion(float lateDiffusion) { _lateDiffusion = lateDiffusion; }
float getInputBandwidth() const { return _inputBandwidth; }
void setInputBandwidth(float inputBandwidth ) { _inputBandwidth = inputBandwidth; }
float getRoomSize() const { return _roomSize; }
void setRoomSize(float roomSize) { _roomSize = roomSize; }
float getEarlyLevel() const { return _earlyLevel; }
void setEarlyLevel(float earlyLevel ) { _earlyLevel = earlyLevel; }
float getDensity() const { return _density; }
void setDensity(float density) { _density = density; }
float getTailLevel() const { return _tailLevel; }
void setTailLevel(float tailLevel ) { _tailLevel = tailLevel; }
float getBassMult() const { return _bassMult; }
void setBassMult(float bassMult) { _bassMult = bassMult; }
float getDryLevel() const { return _dryLevel; }
void setDryLevel(float dryLevel) { _dryLevel = dryLevel; }
float getBassFreq() const { return _bassFreq; }
void setBassFreq(float bassFreq) { _bassFreq = bassFreq; }
float getWetLevel() const { return _wetLevel; }
void setWetLevel(float wetLevel) { _wetLevel = wetLevel; }
float getHighGain() const { return _highGain; }
void setHighGain(float highGain) { _highGain = highGain; }
float getHighFreq() const { return _highFreq; }
void setHighFreq(float highFreq) { _highFreq = highFreq; }
float getModRate() const { return _modRate; }
void setModRate(float modRate) { _modRate = modRate; }
float getModDepth() const { return _modDepth; }
void setModDepth(float modDepth) { _modDepth = modDepth; }
float getEarlyGain() const { return _earlyGain; }
void setEarlyGain(float earlyGain) { _earlyGain = earlyGain; }
float getLateGain() const { return _lateGain; }
void setLateGain(float lateGain) { _lateGain = lateGain; }
float getEarlyMixLeft() const { return _earlyMixLeft; }
void setEarlyMixLeft(float earlyMixLeft) { _earlyMixLeft = earlyMixLeft; }
float getEarlyMixRight() const { return _earlyMixRight; }
void setEarlyMixRight(float earlyMixRight) { _earlyMixRight = earlyMixRight; }
float getLateMixLeft() const { return _lateMixLeft; }
void setLateMixLeft(float lateMixLeft) { _lateMixLeft = lateMixLeft; }
float getLateMixRight() const { return _lateMixRight; }
void setLateMixRight(float lateMixRight) { _lateMixRight = lateMixRight; }
float getWetDryMix() const { return _wetDryMix; }
void setWetDryMix(float wetDryMix) { _wetDryMix = wetDryMix; }
private:
// http://wiki.audacityteam.org/wiki/GVerb#Instant_Reverberb_settings
float _bandwidth; // [20, 24000] Hz
// Meters Square
float _maxRoomSize;
float _roomSize;
float _preDelay; // [0, 333] ms
float _lateDelay; // [0, 166] ms
// Seconds
float _reverbTime;
float _reverbTime; // [0.1, 100] seconds
// Ratio between 0 and 1
float _damping;
float _earlyDiffusion; // [0, 100] percent
float _lateDiffusion; // [0, 100] percent
// ? (Does not appear to be set externally very often)
float _spread;
float _roomSize; // [0, 100] percent
float _density; // [0, 100] percent
// Ratio between 0 and 1
float _inputBandwidth;
float _bassMult; // [0.1, 10] ratio
float _bassFreq; // [10, 500] Hz
float _highGain; // [-24, 0] dB
float _highFreq; // [1000, 12000] Hz
// dB
float _earlyLevel;
float _tailLevel;
float _dryLevel;
float _wetLevel;
float _modRate; // [0.1, 10] Hz
float _modDepth; // [0, 100] percent
float _earlyGain; // [-96, +24] dB
float _lateGain; // [-96, +24] dB
float _earlyMixLeft; // [0, 100] percent
float _earlyMixRight; // [0, 100] percent
float _lateMixLeft; // [0, 100] percent
float _lateMixRight; // [0, 100] percent
float _wetDryMix; // [0, 100] percent
};
#endif // hifi_AudioEffectOptions_h

View file

@ -1625,10 +1625,10 @@ void AvatarData::setBodyRoll(float bodyRoll) {
setOrientation(glm::quat(glm::radians(eulerAngles)));
}
void AvatarData::setPosition(const glm::vec3 position) {
void AvatarData::setPosition(const glm::vec3& position) {
SpatiallyNestable::setPosition(position);
}
void AvatarData::setOrientation(const glm::quat orientation) {
void AvatarData::setOrientation(const glm::quat& orientation) {
SpatiallyNestable::setOrientation(orientation);
}

View file

@ -201,8 +201,8 @@ public:
float getBodyRoll() const;
void setBodyRoll(float bodyRoll);
virtual void setPosition(glm::vec3 position);
virtual void setOrientation(glm::quat orientation);
virtual void setPosition(const glm::vec3& position) override;
virtual void setOrientation(const glm::quat& orientation) override;
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
void startCapture(); // start/end of the period in which the latest values are about to be captured for camera, etc.

View file

@ -113,10 +113,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
return shouldThrottle;
}
bool Basic2DWindowOpenGLDisplayPlugin::isVSynchronized() const {
return (_framerateTarget == 0) && !_isThrottled;
}
void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
QAction* checkedFramerate{ nullptr };
foreach(auto action, _framerateActions) {
@ -141,11 +137,8 @@ void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
} else if (_isThrottled) {
_framerateTarget = (float) THROTTLED_FRAMERATE;
}
_inverseFrameRate = _framerateTarget ? 1.0f / (float) _framerateTarget : 1.0f / TARGET_FRAMERATE_Basic2DWindowOpenGL; // not truncated
int newInterval = getDesiredInterval();
// Note: when not isVSynchronized, we are often not likely to hit target with a newInterval timer.
// We could try subtracting an allowance for qApp->getLastPaintWait() and qt timer machinery, but that starts getting complicated.
qDebug() << newInterval;
_timer.start(newInterval);
}

View file

@ -21,7 +21,6 @@ public:
virtual const QString & getName() const override;
virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; }
virtual float getTargetFramePeriod() override { return _inverseFrameRate; }
virtual void activate() override;
@ -30,7 +29,6 @@ public:
virtual void internalPresent() override;
virtual bool isThrottled() const override;
virtual bool isVSynchronized() const override;
protected:
int getDesiredInterval() const;
@ -44,6 +42,5 @@ private:
QAction* _vsyncAction { nullptr };
uint32_t _framerateTarget { 0 };
int _fullscreenTarget{ -1 };
float _inverseFrameRate{ 1.0f }; //seconds
bool _wantVsync { true };
};

View file

@ -30,6 +30,12 @@ class PresentThread : public QThread, public Dependency {
using Lock = std::unique_lock<Mutex>;
public:
PresentThread() {
connect(qApp, &QCoreApplication::aboutToQuit, [this]{
_shutdown = true;
});
}
~PresentThread() {
_shutdown = true;
wait();
@ -99,6 +105,10 @@ public:
_context->doneCurrent();
}
_context->makeCurrent();
if (_activePlugin) {
_activePlugin->uncustomizeContext();
}
_context->doneCurrent();
_context->moveToThread(qApp->thread());
}

View file

@ -23,7 +23,6 @@ public:
virtual bool isHmd() const override { return true; }
virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; }
virtual float getTargetFramePeriod() override { return 1.0f / TARGET_RATE_OpenVr; }
virtual void activate() override;
virtual void deactivate() override;

View file

@ -41,6 +41,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
Q_ASSERT(getType() == EntityTypes::Box);
Q_ASSERT(args->_batch);
if (!_procedural) {
_procedural.reset(new Procedural(this->getUserData()));
_procedural->_vertexSource = simple_vert;
@ -64,4 +65,6 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
}
};
static const auto triCount = DependencyManager::get<GeometryCache>()->getCubeTriangleCount();
args->_details._trianglesRendered += triCount;
}

View file

@ -44,7 +44,7 @@ RenderableModelEntityItem::~RenderableModelEntityItem() {
}
}
void RenderableModelEntityItem::setDimensions(const glm::vec3 value) {
void RenderableModelEntityItem::setDimensions(const glm::vec3& value) {
_dimensionsInitialized = true;
ModelEntityItem::setDimensions(value);
}
@ -565,20 +565,20 @@ bool RenderableModelEntityItem::contains(const glm::vec3& point) const {
return false;
}
glm::quat RenderableModelEntityItem::getJointRotation(int index) const {
glm::quat RenderableModelEntityItem::getAbsoluteJointRotationInObjectFrame(int index) const {
if (_model) {
glm::quat result;
if (_model->getJointRotation(index, result)) {
if (_model->getAbsoluteJointRotationInRigFrame(index, result)) {
return result;
}
}
return glm::quat();
}
glm::vec3 RenderableModelEntityItem::getJointTranslation(int index) const {
glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(int index) const {
if (_model) {
glm::vec3 result;
if (_model->getJointTranslation(index, result)) {
if (_model->getAbsoluteJointTranslationInRigFrame(index, result)) {
return result;
}
}

View file

@ -28,7 +28,7 @@ public:
virtual ~RenderableModelEntityItem();
virtual void setDimensions(const glm::vec3 value) override;
virtual void setDimensions(const glm::vec3& value) override;
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
@ -67,9 +67,9 @@ public:
virtual bool contains(const glm::vec3& point) const override;
// these are in the frame of this object
virtual glm::quat getJointRotation(int index) const override;
virtual glm::vec3 getJointTranslation(int index) const override;
// these are in the frame of this object (model space)
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
private:
void remapTextures();

View file

@ -71,4 +71,6 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
batch.setModelTransform(Transform());
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch, modelTransform, sphereColor);
}
};
static const auto triCount = DependencyManager::get<GeometryCache>()->getSphereTriangleCount();
args->_details._trianglesRendered += triCount;
}

View file

@ -1192,7 +1192,7 @@ const Transform EntityItem::getTransformToCenter() const {
return result;
}
void EntityItem::setDimensions(const glm::vec3 value) {
void EntityItem::setDimensions(const glm::vec3& value) {
if (value.x <= 0.0f || value.y <= 0.0f || value.z <= 0.0f) {
return;
}

View file

@ -178,7 +178,7 @@ public:
/// Dimensions in meters (0.0 - TREE_SCALE)
inline const glm::vec3 getDimensions() const { return getScale(); }
virtual void setDimensions(const glm::vec3 value);
virtual void setDimensions(const glm::vec3& value);
float getGlowLevel() const { return _glowLevel; }
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
@ -379,8 +379,8 @@ public:
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet);
// these are in the frame of this object
virtual glm::quat getJointRotation(int index) const { return glm::quat(); }
virtual glm::vec3 getJointTranslation(int index) const { return glm::vec3(0.0f); }
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); }
protected:

View file

@ -1780,6 +1780,12 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (zPNeighborIDChanged()) {
out += "zPNeighborID";
}
if (parentIDChanged()) {
out += "parentID";
}
if (parentJointIndexChanged()) {
out += "parentJointIndex";
}
getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out);
@ -1790,6 +1796,6 @@ QList<QString> EntityItemProperties::listChangedProperties() {
return out;
}
bool EntityItemProperties::parentDependentPropertyChanged() {
bool EntityItemProperties::parentDependentPropertyChanged() const {
return localPositionChanged() || positionChanged() || localRotationChanged() || rotationChanged();
}

View file

@ -83,7 +83,7 @@ public:
{ return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; }
EntityPropertyFlags getChangedProperties() const;
bool parentDependentPropertyChanged(); // was there a changed in a property that requires parent info to interpret?
bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret?
AACube getMaximumAACube() const;
AABox getAABox() const;

View file

@ -64,7 +64,7 @@ void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) {
}
}
EntityItemProperties convertLocationToScriptSemantics(EntityItemProperties entitySideProperties) {
EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties& entitySideProperties) {
// In EntityTree code, properties.position and properties.rotation are relative to the parent. In javascript,
// they are in world-space. The local versions are put into localPosition and localRotation and position and
// rotation are converted from local to world space.
@ -85,7 +85,7 @@ EntityItemProperties convertLocationToScriptSemantics(EntityItemProperties entit
}
EntityItemProperties convertLocationFromScriptSemantics(EntityItemProperties scriptSideProperties) {
EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperties& scriptSideProperties) {
// convert position and rotation properties from world-space to local, unless localPosition and localRotation
// are set. If they are set, they overwrite position and rotation.
EntityItemProperties entitySideProperties = scriptSideProperties;
@ -190,7 +190,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
return convertLocationToScriptSemantics(results);
}
QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties scriptSideProperties) {
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
EntityItemProperties properties = scriptSideProperties;
EntityItemID entityID(id);
// If we have a local entity tree set, then also update it.
@ -201,17 +201,25 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties script
bool updatedEntity = false;
_entityTree->withWriteLock([&] {
if (scriptSideProperties.parentDependentPropertyChanged()) {
// if the script sets a location property but didn't include parent information, grab the needed
// properties from the entity.
if (!scriptSideProperties.parentIDChanged() || !scriptSideProperties.parentJointIndexChanged()) {
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (entity && !scriptSideProperties.parentIDChanged()) {
properties.setParentID(entity->getParentID());
}
if (entity && !scriptSideProperties.parentJointIndexChanged()) {
properties.setParentJointIndex(entity->getParentJointIndex());
}
if (scriptSideProperties.parentDependentPropertyChanged() ||
scriptSideProperties.parentIDChanged() || scriptSideProperties.parentJointIndexChanged()) {
// All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.
// If any of these changed, pull any missing properties from the entity.
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {
return;
}
if (!scriptSideProperties.parentIDChanged()) {
properties.setParentID(entity->getParentID());
}
if (!scriptSideProperties.parentJointIndexChanged()) {
properties.setParentJointIndex(entity->getParentJointIndex());
}
if (!scriptSideProperties.localPositionChanged() && !scriptSideProperties.positionChanged()) {
properties.setPosition(entity->getPosition());
}
if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) {
properties.setRotation(entity->getOrientation());
}
}
properties = convertLocationFromScriptSemantics(properties);
@ -746,82 +754,75 @@ QVariantMap EntityScriptingInterface::getActionArguments(const QUuid& entityID,
return result;
}
glm::vec3 EntityScriptingInterface::voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords) {
EntityItemPointer EntityScriptingInterface::checkForTreeEntityAndTypeMatch(const QUuid& entityID,
EntityTypes::EntityType entityType) {
if (!_entityTree) {
return glm::vec3(0.0f);
return EntityItemPointer();
}
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::voxelCoordsToWorldCoords no entity with ID" << entityID;
qDebug() << "EntityScriptingInterface::checkForTreeEntityAndTypeMatch - no entity with ID" << entityID;
return entity;
}
if (entityType != EntityTypes::Unknown && entity->getType() != entityType) {
return EntityItemPointer();
}
return entity;
}
glm::vec3 EntityScriptingInterface::voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::PolyVox)) {
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->voxelCoordsToWorldCoords(voxelCoords);
} else {
return glm::vec3(0.0f);
}
EntityTypes::EntityType entityType = entity->getType();
if (entityType != EntityTypes::PolyVox) {
return glm::vec3(0.0f);
}
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->voxelCoordsToWorldCoords(voxelCoords);
}
glm::vec3 EntityScriptingInterface::worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords) {
if (!_entityTree) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::PolyVox)) {
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->worldCoordsToVoxelCoords(worldCoords);
} else {
return glm::vec3(0.0f);
}
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::worldCoordsToVoxelCoords no entity with ID" << entityID;
return glm::vec3(0.0f);
}
EntityTypes::EntityType entityType = entity->getType();
if (entityType != EntityTypes::PolyVox) {
return glm::vec3(0.0f);
}
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->worldCoordsToVoxelCoords(worldCoords);
}
glm::vec3 EntityScriptingInterface::voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords) {
if (!_entityTree) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::PolyVox)) {
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->voxelCoordsToLocalCoords(voxelCoords);
} else {
return glm::vec3(0.0f);
}
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::voxelCoordsToLocalCoords no entity with ID" << entityID;
return glm::vec3(0.0f);
}
EntityTypes::EntityType entityType = entity->getType();
if (entityType != EntityTypes::PolyVox) {
return glm::vec3(0.0f);
}
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->voxelCoordsToLocalCoords(voxelCoords);
}
glm::vec3 EntityScriptingInterface::localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords) {
if (!_entityTree) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::PolyVox)) {
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->localCoordsToVoxelCoords(localCoords);
} else {
return glm::vec3(0.0f);
}
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::localCoordsToVoxelCoords no entity with ID" << entityID;
return glm::vec3(0.0f);
}
EntityTypes::EntityType entityType = entity->getType();
if (entityType != EntityTypes::PolyVox) {
return glm::vec3(0.0f);
}
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
return polyVoxEntity->localCoordsToVoxelCoords(localCoords);
}
glm::vec3 EntityScriptingInterface::getAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
return modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex);
} else {
return glm::vec3(0.0f);
}
}
glm::quat EntityScriptingInterface::getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
return modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex);
} else {
return glm::quat();
}
}

View file

@ -86,7 +86,7 @@ public slots:
/// edits a model updating only the included properties, will return the identified EntityItemID in case of
/// successful edit, if the input entityID is for an unknown model this function will have no effect
Q_INVOKABLE QUuid editEntity(QUuid entityID, EntityItemProperties properties);
Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties);
/// deletes a model
Q_INVOKABLE void deleteEntity(QUuid entityID);
@ -149,6 +149,9 @@ public slots:
Q_INVOKABLE glm::vec3 voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords);
Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords);
Q_INVOKABLE glm::vec3 getAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex);
Q_INVOKABLE glm::quat getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex);
signals:
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
@ -179,6 +182,9 @@ private:
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
EntityTypes::EntityType entityType = EntityTypes::Unknown);
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode

View file

@ -24,6 +24,7 @@
#include "EntitiesLogging.h"
#include "RecurseOctreeToMapOperator.h"
#include "LogHandler.h"
#include "RemapIDOperator.h"
static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50;
@ -740,6 +741,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
changedProperties[index] = QString("userData:") + changeHint;
}
}
if (properties.parentJointIndexChanged()) {
int index = changedProperties.indexOf("parentJointIndex");
if (index >= 0) {
quint16 value = properties.getParentJointIndex();
changedProperties[index] = QString("parentJointIndex:") + QString::number((int)value);
}
}
}
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
@ -1186,6 +1195,11 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra
return true;
}
void EntityTree::remapIDs() {
RemapIDOperator theOperator;
recurseTreeWithOperator(&theOperator);
}
bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues) {
if (! entityDescription.contains("Entities")) {
entityDescription["Entities"] = QVariantList();

View file

@ -196,6 +196,8 @@ public:
bool wantTerseEditLogging() const { return _wantTerseEditLogging; }
void setWantTerseEditLogging(bool value) { _wantTerseEditLogging = value; }
void remapIDs();
bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues);
bool readFromMap(QVariantMap& entityDescription);

View file

@ -40,7 +40,7 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID) : EntityItem(
_cutoff = PI;
}
void LightEntityItem::setDimensions(const glm::vec3 value) {
void LightEntityItem::setDimensions(const glm::vec3& value) {
if (_isSpotlight) {
// If we are a spotlight, treat the z value as our radius or length, and
// recalculate the x/y dimensions to properly encapsulate the spotlight.

View file

@ -23,7 +23,7 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3 value);
virtual void setDimensions(const glm::vec3& value);
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;

View file

@ -121,6 +121,9 @@ public:
virtual bool shouldBePhysical() const;
static void cleanupLoadedAnimations();
virtual glm::vec3 getJointPosition(int jointIndex) const { return glm::vec3(); }
virtual glm::quat getJointRotation(int jointIndex) const { return glm::quat(); }
private:
void setAnimationSettings(const QString& value); // only called for old bitstream format

View file

@ -0,0 +1,33 @@
//
// RemapIDOperator.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-12-6.
// 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 "EntityTree.h"
#include "RemapIDOperator.h"
QUuid RemapIDOperator::remap(const QUuid& oldID) {
if (oldID.isNull()) {
return oldID;
}
if (!_oldToNew.contains(oldID)) {
_oldToNew[oldID] = QUuid::createUuid();
}
return _oldToNew[oldID];
}
bool RemapIDOperator::postRecursion(OctreeElementPointer element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) {
entityItem->setID(remap(entityItem->getID()));
entityItem->setParentID(remap(entityItem->getParentID()));
});
return true;
}

View file

@ -0,0 +1,30 @@
//
// RemapIDOperator.h
// libraries/entities/src
//
// Created by Seth Alves on 2015-12-6.
// 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_RemapIDOperator_h
#define hifi_RemapIDOperator_h
#include "Octree.h"
// this will change all the IDs in an EntityTree. Parent/Child relationships are maintained.
class RemapIDOperator : public RecurseOctreeOperator {
public:
RemapIDOperator() : RecurseOctreeOperator() {}
~RemapIDOperator() {}
virtual bool preRecursion(OctreeElementPointer element) { return true; }
virtual bool postRecursion(OctreeElementPointer element);
private:
QUuid remap(const QUuid& oldID);
QHash<QUuid, QUuid> _oldToNew;
};
#endif // hifi_RemapIDOperator_h

View file

@ -41,7 +41,7 @@ TextEntityItem::TextEntityItem(const EntityItemID& entityItemID) : EntityItem(en
const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
void TextEntityItem::setDimensions(const glm::vec3 value) {
void TextEntityItem::setDimensions(const glm::vec3& value) {
// NOTE: Text Entities always have a "depth" of 1cm.
EntityItem::setDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH));
}

View file

@ -23,7 +23,7 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3 value);
virtual void setDimensions(const glm::vec3& value);
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity

View file

@ -34,7 +34,7 @@ WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(enti
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
void WebEntityItem::setDimensions(const glm::vec3 value) {
void WebEntityItem::setDimensions(const glm::vec3& value) {
// NOTE: Web Entities always have a "depth" of 1cm.
EntityItem::setDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH));
}

View file

@ -22,7 +22,7 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3 value);
virtual void setDimensions(const glm::vec3& value);
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity

View file

@ -337,6 +337,9 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
// a timer with a small interval is used to get better performance.
_updateTimer.setInterval(MIN_TIMER_MS);
connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this]{
disconnect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
});
_updateTimer.start();
_qmlComponent = new QQmlComponent(_qmlEngine);

View file

@ -35,16 +35,19 @@ vr::IVRSystem* acquireOpenVrSystem();
void releaseOpenVrSystem();
const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f,
CONTROLLER_LENGTH_OFFSET / 2.0f,
CONTROLLER_LENGTH_OFFSET * 2.0f);
static const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
static const QString MENU_PARENT = "Avatar";
static const QString MENU_NAME = "Vive Controllers";
static const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
static const QString RENDER_CONTROLLERS = "Render Hand Controllers";
const QString ViveControllerManager::NAME = "OpenVR";
const QString MENU_PARENT = "Avatar";
const QString MENU_NAME = "Vive Controllers";
const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
const QString RENDER_CONTROLLERS = "Render Hand Controllers";
bool ViveControllerManager::isSupported() const {
#ifdef Q_OS_WIN
auto hmd = acquireOpenVrSystem();
@ -320,14 +323,11 @@ void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool
}
void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool left) {
glm::vec3 position = extractTranslation(mat);
glm::quat rotation = glm::quat_cast(mat);
// When the sensor-to-world rotation is identity the coordinate axes look like this:
//
// user
// forward
// z
// -z
// |
// y| user
// y o----x right
@ -372,17 +372,27 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool l
// Q = (deltaQ * QOffset) * (yFlip * quarterTurnAboutX)
//
// Q = (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX)
const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
float sign = left ? -1.0f : 1.0f;
const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
rotation = rotation * offset * yFlip * quarterX;
position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
static const glm::quat yFlip = glm::angleAxis(PI, Vectors::UNIT_Y);
static const glm::quat quarterX = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_X);
static const glm::quat viveToHand = yFlip * quarterX;
static const glm::quat leftQuaterZ = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_Z);
static const glm::quat rightQuaterZ = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_Z);
static const glm::quat eighthX = glm::angleAxis(PI / 4.0f, Vectors::UNIT_X);
static const glm::quat leftRotationOffset = glm::inverse(leftQuaterZ * eighthX) * viveToHand;
static const glm::quat rightRotationOffset = glm::inverse(rightQuaterZ * eighthX) * viveToHand;
static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET;
static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET;
glm::vec3 position = extractTranslation(mat);
glm::quat rotation = glm::quat_cast(mat);
position += rotation * (left ? leftTranslationOffset : rightTranslationOffset);
rotation = rotation * (left ? leftRotationOffset : rightRotationOffset);
_poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation);
}

View file

@ -46,7 +46,7 @@ void AssetResourceRequest::doSend() {
}
connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::progress);
connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) {
connect(_assetRequest, &AssetRequest::finished, this, [this](AssetRequest* req) {
Q_ASSERT(_state == InProgress);
Q_ASSERT(req == _assetRequest);
Q_ASSERT(req->getState() == AssetRequest::Finished);

View file

@ -63,6 +63,7 @@ void AssetUpload::start() {
// file opened, read the data and grab the extension
_extension = QFileInfo(_filename).suffix();
_extension = _extension.toLower();
_data = file.readAll();
} else {

View file

@ -296,8 +296,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
if (matchingNode) {
emit dataReceived(matchingNode->getType(), receivedMessage->getSize());
matchingNode->recordBytesReceived(receivedMessage->getSize());
Node* n = matchingNode.data();
auto addr = n->getActiveSocket();
QMetaMethod metaMethod = listener.method;

View file

@ -22,8 +22,8 @@ static const int HEAD_DATA_SIZE = 512;
ReceivedMessage::ReceivedMessage(const NLPacketList& packetList)
: _data(packetList.getMessage()),
_headData(_data.mid(0, HEAD_DATA_SIZE)),
_sourceID(packetList.getSourceID()),
_numPackets(packetList.getNumPackets()),
_sourceID(packetList.getSourceID()),
_packetType(packetList.getType()),
_packetVersion(packetList.getVersion()),
_senderSockAddr(packetList.getSenderSockAddr()),
@ -34,8 +34,8 @@ ReceivedMessage::ReceivedMessage(const NLPacketList& packetList)
ReceivedMessage::ReceivedMessage(NLPacket& packet)
: _data(packet.readAll()),
_headData(_data.mid(0, HEAD_DATA_SIZE)),
_sourceID(packet.getSourceID()),
_numPackets(1),
_sourceID(packet.getSourceID()),
_packetType(packet.getType()),
_packetVersion(packet.getVersion()),
_senderSockAddr(packet.getSenderSockAddr()),

View file

@ -140,6 +140,7 @@ void ObjectMotionState::updateCCDConfiguration() {
// TODO: Ideally the swept sphere radius would be contained by the object. Using the bounding sphere
// radius works well for spherical objects, but may cause issues with other shapes. For arbitrary
// objects we may want to consider a different approach, such as grouping rigid bodies together.
_body->setCcdSweptSphereRadius(radius);
} else {
// Disable CCD

View file

@ -58,9 +58,7 @@ public:
/// By default, all HMDs are stereo
virtual bool isStereo() const { return isHmd(); }
virtual bool isThrottled() const { return false; }
virtual bool isVSynchronized() const { return true; } // false when throttled or run by non vsync timer
virtual float getTargetFrameRate() { return 0.0f; }
virtual float getTargetFramePeriod() { return 0.0f; }
// Rendering support

View file

@ -214,6 +214,19 @@ VertexVector tesselate(const VertexVector& startingTriangles, int count) {
return triangles;
}
size_t GeometryCache::getShapeTriangleCount(Shape shape) {
return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE;
}
size_t GeometryCache::getSphereTriangleCount() {
return getShapeTriangleCount(Sphere);
}
size_t GeometryCache::getCubeTriangleCount() {
return getShapeTriangleCount(Cube);
}
// FIXME solids need per-face vertices, but smooth shaded
// components do not. Find a way to support using draw elements
// or draw arrays as appropriate
@ -1727,3 +1740,4 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
batch.setPipeline(_standardDrawPipeline);
}
}

View file

@ -151,16 +151,19 @@ public:
void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& transformBuffer, gpu::BufferPointer& colorBuffer);
void renderShape(gpu::Batch& batch, Shape shape);
void renderWireShape(gpu::Batch& batch, Shape shape);
size_t getShapeTriangleCount(Shape shape);
void renderCubeInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer);
void renderWireCubeInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer);
void renderCube(gpu::Batch& batch);
void renderWireCube(gpu::Batch& batch);
size_t getCubeTriangleCount();
void renderSphereInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer);
void renderWireSphereInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer);
void renderSphere(gpu::Batch& batch);
void renderWireSphere(gpu::Batch& batch);
size_t getSphereTriangleCount();
void renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color);
void renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id = UNKNOWN_ID);

View file

@ -544,6 +544,7 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
pendingChanges.removeItem(item);
}
_renderItems.clear();
_renderItemsSet.clear();
_readyWhenAdded = false;
}
@ -765,6 +766,14 @@ bool Model::getJointTranslation(int jointIndex, glm::vec3& translation) const {
return _rig->getJointTranslation(jointIndex, translation);
}
bool Model::getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const {
return _rig->getAbsoluteJointRotationInRigFrame(jointIndex, rotation);
}
bool Model::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const {
return _rig->getAbsoluteJointTranslationInRigFrame(jointIndex, translation);
}
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation);
}

View file

@ -166,6 +166,10 @@ public:
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
bool getJointTranslation(int jointIndex, glm::vec3& translation) const;
// model frame
bool getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const;
bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const;
/// Returns the index of the parent of the indexed joint, or -1 if not found.
int getParentJointIndex(int jointIndex) const;

View file

@ -111,7 +111,7 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName
const auto exception = engine.uncaughtException().toString();
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
engine.clearExceptions();
auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
if (!backtrace.empty()) {
static const auto lineSeparator = "\n ";
@ -326,7 +326,7 @@ void ScriptEngine::init() {
}
_isInitialized = true;
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->init();
@ -392,7 +392,7 @@ void ScriptEngine::init() {
registerGlobalObject("Recording", recordingInterface.data());
registerGlobalObject("Assets", &_assetScriptingInterface);
}
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
@ -401,8 +401,8 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
#endif
QMetaObject::invokeMethod(this, "registerValue",
Q_ARG(const QString&, valueName),
Q_ARG(QScriptValue, value));
Q_ARG(const QString&, valueName),
Q_ARG(QScriptValue, value));
return;
}
@ -428,17 +428,17 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
#endif
#endif
QMetaObject::invokeMethod(this, "registerGlobalObject",
Q_ARG(const QString&, name),
Q_ARG(QObject*, object));
Q_ARG(const QString&, name),
Q_ARG(QObject*, object));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;
#endif
#endif
if (!globalObject().property(name).isValid()) {
if (object) {
@ -452,18 +452,18 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
#endif
#endif
QMetaObject::invokeMethod(this, "registerFunction",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
#endif
#endif
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
globalObject().setProperty(name, scriptFun);
@ -471,18 +471,18 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function
void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name;
#endif
#endif
QMetaObject::invokeMethod(this, "registerFunction",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
#endif
#endif
QScriptValue object = globalObject().property(parent);
if (object.isValid()) {
@ -492,22 +492,22 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name,
}
void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
QScriptEngine::FunctionSignature setter, const QString& parent) {
QScriptEngine::FunctionSignature setter, const QString& parent) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
" name:" << name << "parent:" << parent;
#endif
" name:" << name << "parent:" << parent;
#endif
QMetaObject::invokeMethod(this, "registerGetterSetter",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, getter),
Q_ARG(QScriptEngine::FunctionSignature, setter),
Q_ARG(const QString&, parent));
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, getter),
Q_ARG(QScriptEngine::FunctionSignature, setter),
Q_ARG(const QString&, parent));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
#endif
#endif
QScriptValue setterFunction = newFunction(setter, 1);
QScriptValue getterFunction = newFunction(getter);
@ -527,19 +527,19 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func
// Unregister the handlers for this eventName and entityID.
void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << " eventName:" << eventName;
#endif
"entityID:" << entityID << " eventName:" << eventName;
#endif
QMetaObject::invokeMethod(this, "removeEventHandler",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, eventName),
Q_ARG(QScriptValue, handler));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, eventName),
Q_ARG(QScriptValue, handler));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
#endif
#endif
if (!_registeredHandlers.contains(entityID)) {
return;
@ -557,20 +557,20 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin
// Register the handler.
void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << " eventName:" << eventName;
#endif
"entityID:" << entityID << " eventName:" << eventName;
#endif
QMetaObject::invokeMethod(this, "addEventHandler",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, eventName),
Q_ARG(QScriptValue, handler));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, eventName),
Q_ARG(QScriptValue, handler));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
#endif
#endif
if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...
// Connect up ALL the handlers to the global entities object's signals.
@ -579,7 +579,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
_registeredHandlers.remove(entityID);
});
// Two common cases of event handler, differing only in argument signature.
using SingleEntityHandler = std::function<void(const EntityItemID&)>;
auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {
@ -587,22 +587,22 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });
};
};
using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;
auto makeMouseHandler = [this](QString eventName) -> MouseHandler {
return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
};
};
using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>;
auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler {
return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this),
collisionToScriptValue(this, collision) });
collisionToScriptValue(this, collision) });
};
};
connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));
connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));
@ -635,18 +635,18 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
if (QThread::currentThread() != thread()) {
QScriptValue result;
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
#endif
#endif
QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, sourceCode),
Q_ARG(const QString&, fileName),
Q_ARG(int, lineNumber));
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, sourceCode),
Q_ARG(const QString&, fileName),
Q_ARG(int, lineNumber));
return result;
}
// Check syntax
const QScriptProgram program(sourceCode, fileName, lineNumber);
if (!hasCorrectSyntax(program)) {
@ -656,7 +656,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
++_evaluatesPending;
const auto result = QScriptEngine::evaluate(program);
--_evaluatesPending;
const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName());
if (_wantSignals) {
emit evaluationFinished(result, hadUncaughtException);
@ -668,7 +668,7 @@ void ScriptEngine::run() {
if (_stoppingAllScripts) {
return; // bail early - avoid setting state in init(), as evaluate() will bail too
}
if (!_isInitialized) {
init();
}
@ -899,7 +899,7 @@ void ScriptEngine::print(const QString& message) {
void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
if (_stoppingAllScripts) {
qCDebug(scriptengine) << "Script.include() while shutting down is ignored..."
<< "includeFiles:" << includeFiles << "parent script:" << getFilename();
<< "includeFiles:" << includeFiles << "parent script:" << getFilename();
return; // bail early
}
QList<QUrl> urls;
@ -955,7 +955,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
if (_stoppingAllScripts) {
qCDebug(scriptengine) << "Script.include() while shutting down is ignored... "
<< "includeFile:" << includeFile << "parent script:" << getFilename();
<< "includeFile:" << includeFile << "parent script:" << getFilename();
return; // bail early
}
@ -970,7 +970,7 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
void ScriptEngine::load(const QString& loadFile) {
if (_stoppingAllScripts) {
qCDebug(scriptengine) << "Script.load() while shutting down is ignored... "
<< "loadFile:" << loadFile << "parent script:" << getFilename();
<< "loadFile:" << loadFile << "parent script:" << getFilename();
return; // bail early
}
@ -1014,58 +1014,63 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
// for the download
void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread ["
<< QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload;
#endif
#endif
QMetaObject::invokeMethod(this, "loadEntityScript",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, entityScript),
Q_ARG(bool, forceRedownload));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, entityScript),
Q_ARG(bool, forceRedownload));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] "
"entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload;
#endif
#endif
// If we've been called our known entityScripts should not know about us..
assert(!_entityScripts.contains(entityID));
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread ["
<< QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [=](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
<< QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
}, forceRedownload);
this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
}, forceRedownload);
}
// since all of these operations can be asynch we will always do the actual work in the response handler
// for the download
void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success;
#endif
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread ["
<< QThread::currentThread() << "], invoking on correct thread [" << thread()
<< "] " "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:"
<< contents << "isURL:" << isURL << "success:" << success;
#endif
QMetaObject::invokeMethod(this, "entityScriptContentAvailable",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, scriptOrURL),
Q_ARG(const QString&, contents),
Q_ARG(bool, isURL),
Q_ARG(bool, success));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, scriptOrURL),
Q_ARG(const QString&, contents),
Q_ARG(bool, isURL),
Q_ARG(bool, success));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
#endif
auto scriptCache = DependencyManager::get<ScriptCache>();
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
@ -1088,14 +1093,16 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
if (hadUncaughtExceptions(sandbox, program.fileName())) {
return;
}
if (!testConstructor.isFunction()) {
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
" NOT CONSTRUCTOR\n"
" SCRIPT:" << scriptOrURL;
" NOT CONSTRUCTOR\n"
" SCRIPT:" << scriptOrURL;
if (!isFileUrl) {
scriptCache->addScriptToBadScriptList(scriptOrURL);
}
return; // done processing script
}
@ -1119,19 +1126,19 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID;
#endif
#endif
QMetaObject::invokeMethod(this, "unloadEntityScript",
Q_ARG(const EntityItemID&, entityID));
Q_ARG(const EntityItemID&, entityID));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] "
"entityID:" << entityID;
#endif
#endif
if (_entityScripts.contains(entityID)) {
callEntityScriptMethod(entityID, "unload");
@ -1193,21 +1200,21 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName;
#endif
#endif
QMetaObject::invokeMethod(this, "callEntityScriptMethod",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, methodName),
Q_ARG(const QStringList&, params));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, methodName),
Q_ARG(const QStringList&, params));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName;
#endif
#endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) {
@ -1225,21 +1232,21 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
#endif
#endif
QMetaObject::invokeMethod(this, "callEntityScriptMethod",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, methodName),
Q_ARG(const MouseEvent&, event));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, methodName),
Q_ARG(const MouseEvent&, event));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
#endif
#endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) {
@ -1257,23 +1264,23 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
#endif
#endif
QMetaObject::invokeMethod(this, "callEntityScriptMethod",
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, methodName),
Q_ARG(const EntityItemID&, otherID),
Q_ARG(const Collision&, collision));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(const QString&, methodName),
Q_ARG(const EntityItemID&, otherID),
Q_ARG(const Collision&, collision));
return;
}
#ifdef THREAD_DEBUGGING
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
#endif
#endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID];
@ -1286,4 +1293,4 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
entityScript.property(methodName).call(entityScript, args);
}
}
}
}

View file

@ -67,7 +67,7 @@ public:
/// run the script in the callers thread, exit when stop() is called.
void run();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
// properly ensure they are only called on the correct thread
@ -77,14 +77,14 @@ public:
/// registers a global getter/setter
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
QScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
QScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
/// register a global function
Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
/// register a function as a method on a previously registered global object
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun,
int numArguments = -1);
int numArguments = -1);
/// registers a global object by name
Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value);
@ -93,12 +93,12 @@ public:
Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget
/// if the script engine is not already running, this will download the URL and start the process of seting it up
/// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed
/// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed
/// to scripts. we may not need this to be invokable
void loadURL(const QUrl& scriptURL, bool reload);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - these are intended to be public interfaces available to scripts
// NOTE - these are intended to be public interfaces available to scripts
Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
@ -209,4 +209,4 @@ protected:
static bool _stoppingAllScripts;
};
#endif // hifi_ScriptEngine_h
#endif // hifi_ScriptEngine_h

View file

@ -65,7 +65,7 @@ void PIDController::updateHistory(float measuredValue, float dt, float error, fl
}
}
void PIDController::reportHistory() {
qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled";
qCDebug(shared) << _label << "measured dt || error accumulated changed || p i d controlled";
for (int i = 0; i < _history.size(); i++) {
Row& row = _history[i];
qCDebug(shared) << row.measured << row.dt <<

View file

@ -16,7 +16,7 @@
#include <glm/gtc/quaternion.hpp>
const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 6; // Bullet will start to "lose time" at 10 FPS.
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f;
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 90.0f;
// return incremental rotation (Bullet-style) caused by angularVelocity over timeStep
glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float timeStep);

View file

@ -91,7 +91,7 @@ void SpatiallyNestable::forgetChild(SpatiallyNestablePointer newChild) const {
});
}
void SpatiallyNestable::setParentID(const QUuid parentID) {
void SpatiallyNestable::setParentID(const QUuid& parentID) {
if (_parentID != parentID) {
_parentID = parentID;
_parentKnowsMe = false;
@ -99,7 +99,7 @@ void SpatiallyNestable::setParentID(const QUuid parentID) {
parentChanged();
}
glm::vec3 SpatiallyNestable::worldToLocal(glm::vec3 position, QUuid parentID, int parentJointIndex) {
glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex) {
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
@ -122,7 +122,7 @@ glm::vec3 SpatiallyNestable::worldToLocal(glm::vec3 position, QUuid parentID, in
return result.getTranslation();
}
glm::quat SpatiallyNestable::worldToLocal(glm::quat orientation, QUuid parentID, int parentJointIndex) {
glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex) {
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
@ -144,7 +144,7 @@ glm::quat SpatiallyNestable::worldToLocal(glm::quat orientation, QUuid parentID,
return result.getRotation();
}
glm::vec3 SpatiallyNestable::localToWorld(glm::vec3 position, QUuid parentID, int parentJointIndex) {
glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex) {
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
@ -162,7 +162,7 @@ glm::vec3 SpatiallyNestable::localToWorld(glm::vec3 position, QUuid parentID, in
return result.getTranslation();
}
glm::quat SpatiallyNestable::localToWorld(glm::quat orientation, QUuid parentID, int parentJointIndex) {
glm::quat SpatiallyNestable::localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex) {
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
@ -188,7 +188,7 @@ glm::vec3 SpatiallyNestable::getPosition(int jointIndex) const {
return getTransform(jointIndex).getTranslation();
}
void SpatiallyNestable::setPosition(glm::vec3 position) {
void SpatiallyNestable::setPosition(const glm::vec3& position) {
Transform parentTransform = getParentTransform();
Transform myWorldTransform;
_transformLock.withWriteLock([&] {
@ -207,7 +207,7 @@ glm::quat SpatiallyNestable::getOrientation(int jointIndex) const {
return getTransform(jointIndex).getRotation();
}
void SpatiallyNestable::setOrientation(glm::quat orientation) {
void SpatiallyNestable::setOrientation(const glm::quat& orientation) {
Transform parentTransform = getParentTransform();
Transform myWorldTransform;
_transformLock.withWriteLock([&] {
@ -232,13 +232,13 @@ const Transform SpatiallyNestable::getTransform(int jointIndex) const {
// this returns the world-space transform for this object. It finds its parent's transform (which may
// cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it.
Transform worldTransform = getTransform();
Transform jointInObjectFrame = getJointTransformInObjectFrame(jointIndex);
Transform jointInObjectFrame = getAbsoluteJointTransformInObjectFrame(jointIndex);
Transform jointInWorldFrame;
Transform::mult(jointInWorldFrame, worldTransform, jointInObjectFrame);
return jointInWorldFrame;
}
void SpatiallyNestable::setTransform(const Transform transform) {
void SpatiallyNestable::setTransform(const Transform& transform) {
Transform parentTransform = getParentTransform();
_transformLock.withWriteLock([&] {
Transform::inverseMult(_transform, parentTransform, transform);
@ -259,7 +259,7 @@ glm::vec3 SpatiallyNestable::getScale(int jointIndex) const {
return getScale();
}
void SpatiallyNestable::setScale(glm::vec3 scale) {
void SpatiallyNestable::setScale(const glm::vec3& scale) {
_transformLock.withWriteLock([&] {
_transform.setScale(scale);
});
@ -274,7 +274,7 @@ const Transform SpatiallyNestable::getLocalTransform() const {
return result;
}
void SpatiallyNestable::setLocalTransform(const Transform transform) {
void SpatiallyNestable::setLocalTransform(const Transform& transform) {
_transformLock.withWriteLock([&] {
_transform = transform;
});
@ -289,7 +289,7 @@ glm::vec3 SpatiallyNestable::getLocalPosition() const {
return result;
}
void SpatiallyNestable::setLocalPosition(glm::vec3 position) {
void SpatiallyNestable::setLocalPosition(const glm::vec3& position) {
_transformLock.withWriteLock([&] {
_transform.setTranslation(position);
});
@ -304,7 +304,7 @@ glm::quat SpatiallyNestable::getLocalOrientation() const {
return result;
}
void SpatiallyNestable::setLocalOrientation(glm::quat orientation) {
void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) {
_transformLock.withWriteLock([&] {
_transform.setRotation(orientation);
});
@ -319,7 +319,7 @@ glm::vec3 SpatiallyNestable::getLocalScale() const {
return result;
}
void SpatiallyNestable::setLocalScale(glm::vec3 scale) {
void SpatiallyNestable::setLocalScale(const glm::vec3& scale) {
_transformLock.withWriteLock([&] {
_transform.setScale(scale);
});
@ -339,13 +339,13 @@ QList<SpatiallyNestablePointer> SpatiallyNestable::getChildren() const {
return children;
}
const Transform SpatiallyNestable::getJointTransformInObjectFrame(int jointIndex) const {
Transform jointInObjectFrame;
glm::vec3 position = getJointTranslation(jointIndex);
glm::quat orientation = getJointRotation(jointIndex);
jointInObjectFrame.setRotation(orientation);
jointInObjectFrame.setTranslation(position);
return jointInObjectFrame;
const Transform SpatiallyNestable::getAbsoluteJointTransformInObjectFrame(int jointIndex) const {
Transform jointTransformInObjectFrame;
glm::vec3 position = getAbsoluteJointTranslationInObjectFrame(jointIndex);
glm::quat orientation = getAbsoluteJointRotationInObjectFrame(jointIndex);
jointTransformInObjectFrame.setRotation(orientation);
jointTransformInObjectFrame.setTranslation(position);
return jointTransformInObjectFrame;
}
SpatiallyNestablePointer SpatiallyNestable::getThisPointer() const {

View file

@ -42,32 +42,32 @@ public:
virtual void setID(const QUuid& id) { _id = id; }
virtual const QUuid getParentID() const { return _parentID; }
virtual void setParentID(const QUuid parentID);
virtual void setParentID(const QUuid& parentID);
virtual quint16 getParentJointIndex() const { return _parentJointIndex; }
virtual void setParentJointIndex(quint16 parentJointIndex) { _parentJointIndex = parentJointIndex; }
static glm::vec3 worldToLocal(glm::vec3 position, QUuid parentID, int parentJointIndex);
static glm::quat worldToLocal(glm::quat orientation, QUuid parentID, int parentJointIndex);
static glm::vec3 worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex);
static glm::quat worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex);
static glm::vec3 localToWorld(glm::vec3 position, QUuid parentID, int parentJointIndex);
static glm::quat localToWorld(glm::quat orientation, QUuid parentID, int parentJointIndex);
static glm::vec3 localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex);
static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex);
// world frame
virtual const Transform getTransform() const;
virtual void setTransform(const Transform transform);
virtual void setTransform(const Transform& transform);
virtual Transform getParentTransform() const;
virtual glm::vec3 getPosition() const;
virtual void setPosition(glm::vec3 position);
virtual void setPosition(const glm::vec3& position);
virtual glm::quat getOrientation() const;
virtual glm::quat getOrientation(int jointIndex) const;
virtual void setOrientation(glm::quat orientation);
virtual void setOrientation(const glm::quat& orientation);
virtual glm::vec3 getScale() const;
virtual void setScale(glm::vec3 scale);
virtual void setScale(const glm::vec3& scale);
// get world-frame values for a specific joint
virtual const Transform getTransform(int jointIndex) const;
@ -76,24 +76,24 @@ public:
// object's parent's frame
virtual const Transform getLocalTransform() const;
virtual void setLocalTransform(const Transform transform);
virtual void setLocalTransform(const Transform& transform);
virtual glm::vec3 getLocalPosition() const;
virtual void setLocalPosition(glm::vec3 position);
virtual void setLocalPosition(const glm::vec3& position);
virtual glm::quat getLocalOrientation() const;
virtual void setLocalOrientation(glm::quat orientation);
virtual void setLocalOrientation(const glm::quat& orientation);
virtual glm::vec3 getLocalScale() const;
virtual void setLocalScale(glm::vec3 scale);
virtual void setLocalScale(const glm::vec3& scale);
QList<SpatiallyNestablePointer> getChildren() const;
NestableTypes::NestableType getNestableType() const { return _nestableType; }
// this object's frame
virtual const Transform getJointTransformInObjectFrame(int jointIndex) const;
virtual glm::quat getJointRotation(int index) const { assert(false); return glm::quat(); }
virtual glm::vec3 getJointTranslation(int index) const { assert(false); return glm::vec3(); }
virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { assert(false); return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { assert(false); return glm::vec3(); }
SpatiallyNestablePointer getThisPointer() const;

View file

@ -21,7 +21,6 @@ public:
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
virtual float getTargetFrameRate() override { return TARGET_RATE_Oculus; }
virtual float getTargetFramePeriod() override { return 1.0f / TARGET_RATE_Oculus; }
protected:
virtual void internalPresent() override;

View file

@ -28,7 +28,6 @@ public:
virtual int getHmdScreen() const override;
virtual float getTargetFrameRate() override { return TARGET_RATE_OculusLegacy; }
virtual float getTargetFramePeriod() override { return 1.0f / TARGET_RATE_OculusLegacy; }
// Stereo specific methods
virtual bool isHmd() const override { return true; }

View file

@ -1,16 +1,20 @@
set(TARGET_NAME "stack-manager")
set(BUILD_BUNDLE YES)
setup_hifi_project(Widgets Gui Svg Core Network WebKitWidgets)
add_dependencies(${TARGET_NAME} assignment-client domain-server)
include_application_version()
if (WIN32)
target_zlib()
endif ()
target_quazip()
set_target_properties(
${TARGET_NAME} PROPERTIES
EXCLUDE_FROM_ALL TRUE
)
if (UNIX)
set_target_properties(
${TARGET_NAME} PROPERTIES
EXCLUDE_FROM_ALL TRUE
)
endif (UNIX)
if (DEFINED ENV{JOB_ID})
set(PR_BUILD "false")
@ -45,4 +49,5 @@ if (APPLE)
set(SM_SRCS ${SM_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/assets/icon.icns")
endif ()
package_libraries_for_deployment()
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -81,10 +81,6 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const Q
AppDelegate::AppDelegate(int argc, char* argv[]) :
QApplication(argc, argv),
_qtReady(false),
_dsReady(false),
_dsResourcesReady(false),
_acReady(false),
_domainServerProcess(NULL),
_acMonitorProcess(NULL),
_domainServerName("localhost")
@ -115,14 +111,15 @@ AppDelegate::AppDelegate(int argc, char* argv[]) :
_window = new MainWindow();
createExecutablePath();
downloadLatestExecutablesAndRequirements();
_checkVersionTimer.setInterval(0);
connect(&_checkVersionTimer, SIGNAL(timeout()), this, SLOT(checkVersion()));
_checkVersionTimer.start();
connect(this, &QApplication::aboutToQuit, this, &AppDelegate::stopStack);
_window->setRequirementsLastChecked(QDateTime::currentDateTime().toString());
_window->show();
toggleStack(true);
}
AppDelegate::~AppDelegate() {
@ -427,227 +424,6 @@ void AppDelegate::handleContentSetDownloadFinished() {
emit domainAddressChanged();
}
void AppDelegate::onFileSuccessfullyInstalled(const QUrl& url) {
if (url == GlobalData::getInstance().getRequirementsURL()) {
_qtReady = true;
} else if (url == GlobalData::getInstance().getAssignmentClientURL()) {
_acReady = true;
} else if (url == GlobalData::getInstance().getDomainServerURL()) {
_dsReady = true;
} else if (url == GlobalData::getInstance().getDomainServerResourcesURL()) {
_dsResourcesReady = true;
}
if (_qtReady && _acReady && _dsReady && _dsResourcesReady) {
_window->setRequirementsLastChecked(QDateTime::currentDateTime().toString());
_window->show();
toggleStack(true);
}
}
void AppDelegate::createExecutablePath() {
QDir launchDir(GlobalData::getInstance().getClientsLaunchPath());
QDir resourcesDir(GlobalData::getInstance().getClientsResourcesPath());
QDir logsDir(GlobalData::getInstance().getLogsPath());
if (!launchDir.exists()) {
if (QDir().mkpath(launchDir.absolutePath())) {
qDebug() << "Successfully created directory: "
<< launchDir.absolutePath();
} else {
qCritical() << "Failed to create directory: "
<< launchDir.absolutePath();
}
}
if (!resourcesDir.exists()) {
if (QDir().mkpath(resourcesDir.absolutePath())) {
qDebug() << "Successfully created directory: "
<< resourcesDir.absolutePath();
} else {
qCritical() << "Failed to create directory: "
<< resourcesDir.absolutePath();
}
}
if (!logsDir.exists()) {
if (QDir().mkpath(logsDir.absolutePath())) {
qDebug() << "Successfully created directory: "
<< logsDir.absolutePath();
} else {
qCritical() << "Failed to create directory: "
<< logsDir.absolutePath();
}
}
}
void AppDelegate::downloadLatestExecutablesAndRequirements() {
// Check if Qt is already installed
if (GlobalData::getInstance().getPlatform() == "mac") {
if (QDir(GlobalData::getInstance().getClientsLaunchPath() + "QtCore.framework").exists()) {
_qtReady = true;
}
} else if (GlobalData::getInstance().getPlatform() == "win") {
if (QFileInfo(GlobalData::getInstance().getClientsLaunchPath() + "Qt5Core.dll").exists()) {
_qtReady = true;
}
} else { // linux
if (QFileInfo(GlobalData::getInstance().getClientsLaunchPath() + "libQt5Core.so.5").exists()) {
_qtReady = true;
}
}
QFile reqZipFile(GlobalData::getInstance().getRequirementsZipPath());
QByteArray reqZipData;
if (reqZipFile.open(QIODevice::ReadOnly)) {
reqZipData = reqZipFile.readAll();
reqZipFile.close();
}
QFile resZipFile(GlobalData::getInstance().getDomainServerResourcesZipPath());
QByteArray resZipData;
if (resZipFile.open(QIODevice::ReadOnly)) {
resZipData = resZipFile.readAll();
resZipFile.close();
}
QDir resourcesDir(GlobalData::getInstance().getClientsResourcesPath());
if (!(resourcesDir.entryInfoList(QDir::AllEntries).size() < 3)) {
_dsResourcesReady = true;
}
// if the user has set hifiBuildDirectory, don't attempt to download the domain-server or assignement-client
if (GlobalData::getInstance().isGetHifiBuildDirectorySet()) {
_dsReady = true;
_acReady = true;
} else {
QByteArray dsData;
QFile dsFile(GlobalData::getInstance().getDomainServerExecutablePath());
if (dsFile.open(QIODevice::ReadOnly)) {
dsData = dsFile.readAll();
dsFile.close();
}
QByteArray acData;
QFile acFile(GlobalData::getInstance().getAssignmentClientExecutablePath());
if (acFile.open(QIODevice::ReadOnly)) {
acData = acFile.readAll();
acFile.close();
}
QNetworkRequest acReq(QUrl(GlobalData::getInstance().getAssignmentClientMD5URL()));
QNetworkReply* acReply = _manager->get(acReq);
QEventLoop acLoop;
connect(acReply, SIGNAL(finished()), &acLoop, SLOT(quit()));
acLoop.exec();
QByteArray acMd5Data = acReply->readAll().trimmed();
if (GlobalData::getInstance().getPlatform() == "win") {
// fix for reading the MD5 hash from Windows-generated
// binary data of the MD5 hash
QTextStream stream(acMd5Data);
stream >> acMd5Data;
}
// fix for Mac and Linux network accessibility
if (acMd5Data.size() == 0) {
// network is not accessible
qDebug() << "Could not connect to the internet.";
_window->show();
return;
}
qDebug() << "AC MD5: " << acMd5Data;
if (acMd5Data.toLower() == QCryptographicHash::hash(acData, QCryptographicHash::Md5).toHex()) {
_acReady = true;
}
QNetworkRequest dsReq(QUrl(GlobalData::getInstance().getDomainServerMD5URL()));
QNetworkReply* dsReply = _manager->get(dsReq);
QEventLoop dsLoop;
connect(dsReply, SIGNAL(finished()), &dsLoop, SLOT(quit()));
dsLoop.exec();
QByteArray dsMd5Data = dsReply->readAll().trimmed();
if (GlobalData::getInstance().getPlatform() == "win") {
// fix for reading the MD5 hash from Windows generated
// binary data of the MD5 hash
QTextStream stream(dsMd5Data);
stream >> dsMd5Data;
}
qDebug() << "DS MD5: " << dsMd5Data;
if (dsMd5Data.toLower() == QCryptographicHash::hash(dsData, QCryptographicHash::Md5).toHex()) {
_dsReady = true;
}
}
if (_qtReady) {
// check MD5 of requirements.zip only if Qt is found
QNetworkRequest reqZipReq(QUrl(GlobalData::getInstance().getRequirementsMD5URL()));
QNetworkReply* reqZipReply = _manager->get(reqZipReq);
QEventLoop reqZipLoop;
connect(reqZipReply, SIGNAL(finished()), &reqZipLoop, SLOT(quit()));
reqZipLoop.exec();
QByteArray reqZipMd5Data = reqZipReply->readAll().trimmed();
if (GlobalData::getInstance().getPlatform() == "win") {
// fix for reading the MD5 hash from Windows generated
// binary data of the MD5 hash
QTextStream stream(reqZipMd5Data);
stream >> reqZipMd5Data;
}
qDebug() << "Requirements ZIP MD5: " << reqZipMd5Data;
if (reqZipMd5Data.toLower() != QCryptographicHash::hash(reqZipData, QCryptographicHash::Md5).toHex()) {
_qtReady = false;
}
}
if (_dsResourcesReady) {
// check MD5 of resources.zip only if Domain Server
// resources are installed
QNetworkRequest resZipReq(QUrl(GlobalData::getInstance().getDomainServerResourcesMD5URL()));
QNetworkReply* resZipReply = _manager->get(resZipReq);
QEventLoop resZipLoop;
connect(resZipReply, SIGNAL(finished()), &resZipLoop, SLOT(quit()));
resZipLoop.exec();
QByteArray resZipMd5Data = resZipReply->readAll().trimmed();
if (GlobalData::getInstance().getPlatform() == "win") {
// fix for reading the MD5 hash from Windows generated
// binary data of the MD5 hash
QTextStream stream(resZipMd5Data);
stream >> resZipMd5Data;
}
qDebug() << "Domain Server Resources ZIP MD5: " << resZipMd5Data;
if (resZipMd5Data.toLower() != QCryptographicHash::hash(resZipData, QCryptographicHash::Md5).toHex()) {
_dsResourcesReady = false;
}
}
DownloadManager* downloadManager = 0;
if (!_qtReady || !_acReady || !_dsReady || !_dsResourcesReady) {
// initialise DownloadManager
downloadManager = new DownloadManager(_manager);
downloadManager->setWindowModality(Qt::ApplicationModal);
connect(downloadManager, SIGNAL(fileSuccessfullyInstalled(QUrl)),
SLOT(onFileSuccessfullyInstalled(QUrl)));
downloadManager->show();
} else {
_window->setRequirementsLastChecked(QDateTime::currentDateTime().toString());
_window->show();
toggleStack(true);
}
if (!_qtReady) {
downloadManager->downloadFile(GlobalData::getInstance().getRequirementsURL());
}
if (!_acReady) {
downloadManager->downloadFile(GlobalData::getInstance().getAssignmentClientURL());
}
if (!_dsReady) {
downloadManager->downloadFile(GlobalData::getInstance().getDomainServerURL());
}
if (!_dsResourcesReady) {
downloadManager->downloadFile(GlobalData::getInstance().getDomainServerResourcesURL());
}
}
void AppDelegate::checkVersion() {
QNetworkRequest latestVersionRequest((QUrl(CHECK_BUILDS_URL)));
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);

View file

@ -53,7 +53,6 @@ signals:
void indexPathChangeResponse(bool wasSuccessful);
void stackStateChanged(bool isOn);
private slots:
void onFileSuccessfullyInstalled(const QUrl& url);
void requestDomainServerID();
void handleDomainIDReply();
void handleDomainGetReply();
@ -64,16 +63,10 @@ private slots:
private:
void parseCommandLine();
void createExecutablePath();
void downloadLatestExecutablesAndRequirements();
void changeDomainServerIndexPath(const QString& newPath);
QNetworkAccessManager* _manager;
bool _qtReady;
bool _dsReady;
bool _dsResourcesReady;
bool _acReady;
BackgroundProcess* _domainServerProcess;
BackgroundProcess* _acMonitorProcess;
QHash<QUuid, BackgroundProcess*> _scriptProcesses;

View file

@ -32,11 +32,8 @@ GlobalData::GlobalData() {
_resourcePath = "resources/";
_assignmentClientExecutable = "assignment-client";
_domainServerExecutable = "domain-server";
QString applicationSupportDirectory = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
if (PR_BUILD) {
applicationSupportDirectory += "/pr-binaries";
}
QString applicationSupportDirectory = QCoreApplication::applicationDirPath();
_clientsLaunchPath = QDir::toNativeSeparators(applicationSupportDirectory + "/");
_clientsResourcePath = QDir::toNativeSeparators(applicationSupportDirectory + "/" + _resourcePath);
@ -49,18 +46,6 @@ GlobalData::GlobalData() {
_domainServerExecutablePath.append(".exe");
}
_requirementsURL = urlBase + "/binaries/" + _platform + "/requirements/requirements.zip";
_requirementsZipPath = _clientsLaunchPath + "requirements.zip";
_requirementsMD5URL = urlBase + "/binaries/" + _platform + "/requirements/requirements.md5";
_assignmentClientURL = urlBase + "/binaries/" + _platform + "/assignment-client" + (_platform == "win" ? "/assignment-client.exe" : "/assignment-client");
_domainServerResourcesURL = urlBase + "/binaries/" + _platform + "/domain-server/resources.zip";
_domainServerResourcesZipPath = _clientsLaunchPath + "resources.zip";
_domainServerResourcesMD5URL = urlBase + "/binaries/" + _platform + "/domain-server/resources.md5";
_domainServerURL = urlBase + "/binaries/" + _platform + "/domain-server" + (_platform == "win" ? "/domain-server.exe" : "/domain-server");
_assignmentClientMD5URL = urlBase + "/binaries/" + _platform + "/assignment-client/assignment-client.md5";
_domainServerMD5URL = urlBase + "/binaries/" + _platform + "/domain-server/domain-server.md5";
_defaultDomain = "localhost";
_logsPath = QDir::toNativeSeparators(_clientsLaunchPath + "logs/");
_availableAssignmentTypes.insert("audio-mixer", 0);

View file

@ -9,6 +9,7 @@
#ifndef hifi_GlobalData_h
#define hifi_GlobalData_h
#include <QCoreApplication>
#include <QString>
#include <QHash>