mirror of
https://github.com/lubosz/overte.git
synced 2025-08-28 17:46:19 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into graphicsMaster
This commit is contained in:
commit
5d395ae54f
143 changed files with 2434 additions and 2178 deletions
|
@ -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 ()
|
||||
|
|
|
@ -11,3 +11,4 @@ link_hifi_libraries(
|
|||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
|
@ -102,8 +102,10 @@ void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNo
|
|||
packetType = message->getType();
|
||||
} // fall through to piggyback message
|
||||
|
||||
if (packetType == PacketType::EntityData || packetType == PacketType::EntityErase) {
|
||||
if (packetType == PacketType::EntityData) {
|
||||
_entityViewer.processDatagram(*message, senderNode);
|
||||
} else if (packetType == PacketType::EntityErase) {
|
||||
_entityViewer.processEraseMessage(*message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -416,27 +416,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
return;
|
||||
}
|
||||
|
||||
/* TODO: Looking for a way to prevent locking and encoding a tree that is not
|
||||
// going to result in any packets being sent...
|
||||
//
|
||||
// If our node is root, and the root hasn't changed, and our view hasn't changed,
|
||||
// and we've already seen at least one duplicate packet, then we probably don't need
|
||||
// to lock the tree and encode, because the result should be that no bytes will be
|
||||
// encoded, and this will be a duplicate packet from the last one we sent...
|
||||
OctreeElementPointer root = _myServer->getOctree()->getRoot();
|
||||
bool skipEncode = false;
|
||||
if (
|
||||
(subTree == root)
|
||||
&& (nodeData->getLastRootTimestamp() == root->getLastChanged())
|
||||
&& !viewFrustumChanged
|
||||
&& (nodeData->getDuplicatePacketCount() > 0)
|
||||
) {
|
||||
qDebug() << "is root, root not changed, view not changed, already seen a duplicate!"
|
||||
<< "Can we skip it?";
|
||||
skipEncode = true;
|
||||
}
|
||||
*/
|
||||
|
||||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
|
||||
|
||||
|
@ -471,20 +450,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// the packet and send it
|
||||
completedScene = nodeData->elementBag.isEmpty();
|
||||
|
||||
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
|
||||
if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) {
|
||||
if (_packetData.hasContent() && bytesWritten == 0 &&
|
||||
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
}
|
||||
} else {
|
||||
// in compressed mode and we are trying to pack more... and we don't care if the _packetData has
|
||||
// content or not... because in this case even if we were unable to pack any data, we want to drop
|
||||
// below to our sendNow logic, but we do want to track that we attempted to pack extra
|
||||
if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
extraPackingAttempts++;
|
||||
if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
}
|
||||
}
|
||||
|
||||
nodeData->stats.encodeStopped();
|
||||
|
@ -517,7 +485,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
}
|
||||
|
||||
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
|
||||
extraPackingAttempts = 0;
|
||||
quint64 compressAndWriteEnd = usecTimestampNow();
|
||||
compressAndWriteElapsedUsec = (float)(compressAndWriteEnd - compressAndWriteStart);
|
||||
}
|
||||
|
@ -526,8 +493,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// the packet doesn't have enough space to bother attempting to pack more...
|
||||
bool sendNow = true;
|
||||
|
||||
if (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
|
||||
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
|
||||
if (!completedScene && (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
|
||||
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS)) {
|
||||
sendNow = false; // try to pack more
|
||||
}
|
||||
|
||||
|
@ -539,6 +506,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
|
||||
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
extraPackingAttempts = 0;
|
||||
} else {
|
||||
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
|
||||
// but we've finalized the _packetData, so we want to start a new section, we will do that by
|
||||
|
|
11
cmake/externals/LibOVR/CMakeLists.txt
vendored
11
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -15,17 +15,16 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
# 0.6 public
|
||||
# URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
|
||||
# URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
|
||||
# 0.7 alpha
|
||||
# URL https://s3.amazonaws.com/static.oculus.com/sdk-downloads/0.7.0.0/Public/Alpha/ovr_sdk_win_0.7.0.0_RC1.zip
|
||||
# URL_MD5 a562bb9d117087b2cf9d86653ea70fd8
|
||||
|
||||
# 0.8 public
|
||||
# URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip
|
||||
# URL_MD5 54944b03b95149d6010f84eb701b9647
|
||||
|
||||
if (WIN32)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
|
||||
URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
|
||||
URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip
|
||||
URL_MD5 54944b03b95149d6010f84eb701b9647
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
5
cmake/externals/openvr/CMakeLists.txt
vendored
5
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,9 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
#URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/openvr-0.9.1.zip
|
||||
URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.12.zip
|
||||
URL_MD5 c08dced68ce4e341e1467e6814ae419d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
8
cmake/externals/sdl2/CMakeLists.txt
vendored
8
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -63,7 +63,11 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
|
||||
if (APPLE)
|
||||
|
||||
# NOOP
|
||||
|
||||
elseif (WIN32)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
|
||||
|
||||
|
@ -75,8 +79,12 @@ elseif (WIN32)
|
|||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
|
||||
endif()
|
||||
|
||||
add_paths_to_fixup_libs(${${EXTERNAL_NAME_UPPER}_DLL_PATH})
|
||||
|
||||
else ()
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library")
|
||||
|
||||
endif ()
|
||||
|
|
27
cmake/macros/ConsolidateStackComponents.cmake
Normal file
27
cmake/macros/ConsolidateStackComponents.cmake
Normal 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()
|
|
@ -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 ()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# CopyDllsBesideWindowsExecutable.cmake
|
||||
# PackageLibrariesForDeployment.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
|
@ -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)
|
||||
|
|
|
@ -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@")
|
||||
|
|
|
@ -38,3 +38,4 @@ endif (UNIX)
|
|||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
|
@ -202,31 +202,41 @@ function entityIsGrabbedByOther(entityID) {
|
|||
}
|
||||
|
||||
function getSpatialOffsetPosition(hand, spatialKey) {
|
||||
var position = Vec3.ZERO;
|
||||
|
||||
if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) {
|
||||
return spatialKey.leftRelativePosition;
|
||||
position = spatialKey.leftRelativePosition;
|
||||
}
|
||||
if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) {
|
||||
return spatialKey.rightRelativePosition;
|
||||
position = spatialKey.rightRelativePosition;
|
||||
}
|
||||
if (spatialKey.relativePosition) {
|
||||
return spatialKey.relativePosition;
|
||||
position = spatialKey.relativePosition;
|
||||
}
|
||||
|
||||
return Vec3.ZERO;
|
||||
return position;
|
||||
}
|
||||
|
||||
var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y);
|
||||
function getSpatialOffsetRotation(hand, spatialKey) {
|
||||
var rotation = Quat.IDENTITY;
|
||||
|
||||
if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) {
|
||||
return spatialKey.leftRelativeRotation;
|
||||
rotation = spatialKey.leftRelativeRotation;
|
||||
}
|
||||
if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) {
|
||||
return spatialKey.rightRelativeRotation;
|
||||
rotation = spatialKey.rightRelativeRotation;
|
||||
}
|
||||
if (spatialKey.relativeRotation) {
|
||||
return spatialKey.relativeRotation;
|
||||
rotation = spatialKey.relativeRotation;
|
||||
}
|
||||
|
||||
return Quat.IDENTITY;
|
||||
// Flip left hand
|
||||
if (hand !== RIGHT_HAND) {
|
||||
rotation = Quat.multiply(yFlip, rotation);
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
||||
function MyController(hand) {
|
||||
|
@ -254,6 +264,7 @@ function MyController(hand) {
|
|||
|
||||
this.overlayLine = null;
|
||||
|
||||
this.ignoreIK = false;
|
||||
this.offsetPosition = Vec3.ZERO;
|
||||
this.offsetRotation = Quat.IDENTITY;
|
||||
|
||||
|
@ -853,9 +864,12 @@ function MyController(hand) {
|
|||
|
||||
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
|
||||
// if an object is "equipped" and has a spatialKey, use it.
|
||||
this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
|
||||
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
|
||||
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
|
||||
} else {
|
||||
this.ignoreIK = false;
|
||||
|
||||
var objectRotation = grabbedProperties.rotation;
|
||||
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||
|
||||
|
@ -872,7 +886,8 @@ function MyController(hand) {
|
|||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematicSetVelocity: true
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
|
@ -956,7 +971,8 @@ function MyController(hand) {
|
|||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematicSetVelocity: true
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
}
|
||||
|
@ -982,6 +998,7 @@ function MyController(hand) {
|
|||
// use a spring to pull the object to where it will be when equipped
|
||||
var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
|
||||
var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
|
||||
var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
|
||||
var handRotation = this.getHandRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
var targetRotation = Quat.multiply(handRotation, relativeRotation);
|
||||
|
@ -996,7 +1013,8 @@ function MyController(hand) {
|
|||
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
targetRotation: targetRotation,
|
||||
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
ttl: ACTION_TTL
|
||||
ttl: ACTION_TTL,
|
||||
ignoreIK: ignoreIK
|
||||
});
|
||||
if (this.equipSpringID === NULL_ACTION_ID) {
|
||||
this.equipSpringID = null;
|
||||
|
@ -1009,7 +1027,8 @@ function MyController(hand) {
|
|||
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
targetRotation: targetRotation,
|
||||
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
ttl: ACTION_TTL
|
||||
ttl: ACTION_TTL,
|
||||
ignoreIK: ignoreIK
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
@ -297,7 +298,6 @@
|
|||
var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
|
||||
var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
|
||||
var elModelAnimationHold = document.getElementById("property-model-animation-hold");
|
||||
var elModelAnimationStartAutomatically = document.getElementById("property-model-animation-start-automatically");
|
||||
var elModelTextures = document.getElementById("property-model-textures");
|
||||
var elModelOriginalTextures = document.getElementById("property-model-original-textures");
|
||||
|
||||
|
@ -456,6 +456,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);
|
||||
|
@ -530,7 +531,6 @@
|
|||
elModelAnimationLastFrame.value = properties.animation.lastFrame;
|
||||
elModelAnimationLoop.checked = properties.animation.loop;
|
||||
elModelAnimationHold.checked = properties.animation.hold;
|
||||
elModelAnimationStartAutomatically.checked = properties.animation.startAutomatically;
|
||||
elModelTextures.value = properties.textures;
|
||||
elModelOriginalTextures.value = properties.originalTextures;
|
||||
} else if (properties.type == "Web") {
|
||||
|
@ -671,6 +671,7 @@
|
|||
elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
|
||||
|
||||
elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
|
||||
elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
|
||||
|
||||
var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
|
||||
'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
|
||||
|
@ -782,7 +783,6 @@
|
|||
elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
|
||||
elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
|
||||
elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
|
||||
elModelAnimationStartAutomatically.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'startAutomatically'));
|
||||
|
||||
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
|
||||
|
||||
|
@ -1067,6 +1067,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>
|
||||
|
@ -1339,12 +1345,6 @@
|
|||
<input type='checkbox' id="property-model-animation-hold">
|
||||
</span>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
<span class="label">Animation Start Automatically</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-model-animation-start-automatically">
|
||||
</span>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
<div class="label">Textures</div>
|
||||
<div class="value">
|
||||
|
|
|
@ -74,7 +74,9 @@ var keysToIgnore = [
|
|||
'shapeType',
|
||||
'isEmitting',
|
||||
'sittingPoints',
|
||||
'originalTextures'
|
||||
'originalTextures',
|
||||
'parentJointIndex',
|
||||
'parentID'
|
||||
];
|
||||
|
||||
var individualKeys = [];
|
||||
|
|
|
@ -128,7 +128,6 @@ function setUp() {
|
|||
blue: 255
|
||||
},
|
||||
lifespan: 5.0,
|
||||
visible: false,
|
||||
locked: false,
|
||||
isEmitting: true,
|
||||
lifetime: 3600 // 1 hour; just in case
|
||||
|
|
|
@ -66,21 +66,6 @@
|
|||
max2: 15
|
||||
}
|
||||
|
||||
var BOW_SPATIAL_KEY = {
|
||||
leftRelativePosition: {
|
||||
x: 0.05,
|
||||
y: 0.06,
|
||||
z: -0.05
|
||||
},
|
||||
rightRelativePosition: {
|
||||
x: -0.05,
|
||||
y: 0.06,
|
||||
z: -0.05
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
|
||||
}
|
||||
|
||||
|
||||
var USE_DEBOUNCE = false;
|
||||
|
||||
var TRIGGER_CONTROLS = [
|
||||
|
@ -168,11 +153,9 @@
|
|||
var handToDisable = this.initialHand === 'right' ? 'left' : 'right';
|
||||
Messages.sendMessage('Hifi-Hand-Disabler', handToDisable);
|
||||
|
||||
setEntityCustomData('grabbableKey', this.entityID, {
|
||||
grabbable: false,
|
||||
invertSolidWhileHeld: true,
|
||||
spatialKey: BOW_SPATIAL_KEY
|
||||
});
|
||||
var data = getEntityCustomData('grabbableKey', this.entityID, {});
|
||||
data.grabbable = false;
|
||||
setEntityCustomData('grabbableKey', this.entityID, data);
|
||||
|
||||
},
|
||||
continueNearGrab: function() {
|
||||
|
@ -226,11 +209,10 @@
|
|||
this.isGrabbed = false;
|
||||
this.stringDrawn = false;
|
||||
this.deleteStrings();
|
||||
setEntityCustomData('grabbableKey', this.entityID, {
|
||||
grabbable: true,
|
||||
invertSolidWhileHeld: true,
|
||||
spatialKey: BOW_SPATIAL_KEY
|
||||
});
|
||||
|
||||
var data = getEntityCustomData('grabbableKey', this.entityID, {});
|
||||
data.grabbable = true;
|
||||
setEntityCustomData('grabbableKey', this.entityID, data);
|
||||
Entities.deleteEntity(this.preNotchString);
|
||||
Entities.deleteEntity(this.arrow);
|
||||
this.aiming = false;
|
||||
|
|
|
@ -49,14 +49,14 @@ var bow = Entities.addEntity({
|
|||
invertSolidWhileHeld: true,
|
||||
spatialKey: {
|
||||
leftRelativePosition: {
|
||||
x: 0.05,
|
||||
y: 0.06,
|
||||
z: -0.05
|
||||
x: -0.02,
|
||||
y: 0.08,
|
||||
z: 0.09
|
||||
},
|
||||
rightRelativePosition: {
|
||||
x: -0.05,
|
||||
y: 0.06,
|
||||
z: -0.05
|
||||
relativePosition: {
|
||||
x: 0.02,
|
||||
y: 0.08,
|
||||
z: 0.09
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
|
||||
}
|
||||
|
|
|
@ -53,8 +53,13 @@ var wand = Entities.addEntity({
|
|||
y: 0.1,
|
||||
z: 0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 90)
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, -90)
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function scriptEnding() {
|
||||
Entities.deleteEntity(wand);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
//we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
|
||||
var startTime = Date.now();
|
||||
//if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
|
||||
var LIFETIME = 25;
|
||||
var LIFETIME = 100;
|
||||
var MSEC_PER_SEC = 1000.0;
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
|
@ -85,9 +85,14 @@
|
|||
this.hand = 'LEFT';
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
startNearGrab: function(entityID) {
|
||||
if (!this.hasSpotlight) {
|
||||
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
|
||||
//this light casts the beam
|
||||
this.spotlight = Entities.addEntity({
|
||||
type: "Light",
|
||||
|
@ -97,6 +102,7 @@
|
|||
y: 2,
|
||||
z: 20
|
||||
},
|
||||
parentID: this.entityID,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
|
@ -105,7 +111,9 @@
|
|||
intensity: 2,
|
||||
exponent: 0.3,
|
||||
cutoff: 20,
|
||||
lifetime: LIFETIME
|
||||
lifetime: LIFETIME,
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
});
|
||||
|
||||
//this light creates the effect of a bulb at the end of the flashlight
|
||||
|
@ -116,6 +124,7 @@
|
|||
y: 0.25,
|
||||
z: 0.25
|
||||
},
|
||||
parentID: this.entityID,
|
||||
isSpotlight: false,
|
||||
color: {
|
||||
red: 255,
|
||||
|
@ -123,8 +132,11 @@
|
|||
blue: 255
|
||||
},
|
||||
exponent: 0,
|
||||
lifetime: LIFETIME,
|
||||
cutoff: 90, // in degrees
|
||||
lifetime: LIFETIME
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
|
||||
});
|
||||
|
||||
this.hasSpotlight = true;
|
||||
|
@ -142,7 +154,6 @@
|
|||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
this.setWhichHand();
|
||||
} else {
|
||||
this.updateLightPositions();
|
||||
this.changeLightWithTriggerPressure(this.whichHand);
|
||||
}
|
||||
},
|
||||
|
@ -159,29 +170,7 @@
|
|||
this.lightOn = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateLightPositions: function() {
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
|
||||
//move the two lights along the vectors we set above
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
//move them with the entity model
|
||||
Entities.editEntity(this.spotlight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
|
||||
if (flashLightHand === 'LEFT') {
|
||||
|
|
|
@ -28,10 +28,10 @@ var pistol = Entities.addEntity({
|
|||
spatialKey: {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
y: 0.05,
|
||||
z: -0.08
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(45, 90, 0)
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -100,6 +100,15 @@ else()
|
|||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||
endif()
|
||||
|
||||
# These are external plugins, but we need to do the 'add dependency' here so that their
|
||||
# binary directories get added to the fixup path
|
||||
add_dependency_external_projects(sixense)
|
||||
add_dependency_external_projects(sdl2)
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(OpenVR)
|
||||
endif()
|
||||
|
||||
|
||||
# disable /OPT:REF and /OPT:ICF for the Debug builds
|
||||
# This will prevent the following linker warnings
|
||||
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification
|
||||
|
@ -194,7 +203,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 +210,4 @@ else (APPLE)
|
|||
endif (APPLE)
|
||||
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
#include <HFBackEvent.h>
|
||||
#include <InfoView.h>
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
#include <input-plugins/Joystick.h> // this should probably be removed
|
||||
#include <controllers/UserInputMapper.h>
|
||||
#include <controllers/StateController.h>
|
||||
#include <LogHandler.h>
|
||||
|
@ -195,6 +194,8 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
|
|||
static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
|
||||
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
|
||||
|
||||
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
|
||||
|
||||
#ifndef __APPLE__
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
#else
|
||||
|
@ -350,6 +351,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<UserInputMapper>();
|
||||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
DependencyManager::set<InterfaceParentFinder>();
|
||||
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -369,7 +371,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_frameCount(0),
|
||||
_fps(60.0f),
|
||||
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
|
||||
_entities(true, this, this),
|
||||
_entityClipboardRenderer(false, this, this),
|
||||
_entityClipboard(new EntityTree()),
|
||||
_lastQueriedTime(usecTimestampNow()),
|
||||
|
@ -862,7 +863,7 @@ void Application::cleanupBeforeQuit() {
|
|||
}
|
||||
_keyboardFocusHighlight = nullptr;
|
||||
|
||||
_entities.clear(); // this will allow entity scripts to properly shutdown
|
||||
getEntities()->clear(); // this will allow entity scripts to properly shutdown
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -873,7 +874,7 @@ void Application::cleanupBeforeQuit() {
|
|||
// tell the packet receiver we're shutting down, so it can drop packets
|
||||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||
|
||||
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
|
||||
|
||||
// first stop all timers directly or by invokeMethod
|
||||
|
@ -919,7 +920,7 @@ void Application::emptyLocalCache() {
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
EntityTreePointer tree = _entities.getTree();
|
||||
EntityTreePointer tree = getEntities()->getTree();
|
||||
tree->setSimulation(NULL);
|
||||
|
||||
_octreeProcessor.terminate();
|
||||
|
@ -1122,29 +1123,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();
|
||||
|
@ -1402,7 +1380,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");
|
||||
|
@ -1411,9 +1388,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;
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1424,6 +1398,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.
|
||||
const float paintWaitAndQTTimerAllowance = 0.001f; // 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() {
|
||||
|
@ -1994,7 +1976,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
event->buttons(), event->modifiers());
|
||||
|
||||
|
||||
_entities.mouseMoveEvent(&mappedEvent, deviceID);
|
||||
getEntities()->mouseMoveEvent(&mappedEvent, deviceID);
|
||||
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -2020,7 +2002,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
event->buttons(), event->modifiers());
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
_entities.mousePressEvent(&mappedEvent, deviceID);
|
||||
getEntities()->mousePressEvent(&mappedEvent, deviceID);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
|
||||
|
@ -2065,7 +2047,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
event->buttons(), event->modifiers());
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
_entities.mouseReleaseEvent(&mappedEvent, deviceID);
|
||||
getEntities()->mouseReleaseEvent(&mappedEvent, deviceID);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
|
||||
|
@ -2388,7 +2370,7 @@ void Application::calibrateEyeTracker5Points() {
|
|||
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
|
||||
QVector<EntityItemPointer> entities;
|
||||
|
||||
auto entityTree = _entities.getTree();
|
||||
auto entityTree = getEntities()->getTree();
|
||||
auto exportTree = std::make_shared<EntityTree>();
|
||||
exportTree->createRootElement();
|
||||
|
||||
|
@ -2420,6 +2402,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
|
||||
|
@ -2429,7 +2414,7 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
|
|||
|
||||
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
||||
QVector<EntityItemPointer> entities;
|
||||
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
|
||||
getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
|
||||
|
||||
if (entities.size() > 0) {
|
||||
glm::vec3 root(x, y, z);
|
||||
|
@ -2442,6 +2427,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";
|
||||
|
@ -2486,13 +2475,14 @@ bool Application::importEntities(const QString& urlOrFilename) {
|
|||
|
||||
bool success = _entityClipboard->readFromURL(url.toString());
|
||||
if (success) {
|
||||
_entityClipboard->remapIDs();
|
||||
_entityClipboard->reaverageOctreeElements();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) {
|
||||
return _entityClipboard->sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
|
||||
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z);
|
||||
}
|
||||
|
||||
void Application::initDisplay() {
|
||||
|
@ -2531,13 +2521,13 @@ void Application::init() {
|
|||
// fire off an immediate domain-server check in now that settings are loaded
|
||||
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
|
||||
|
||||
_entities.init();
|
||||
_entities.setViewFrustum(getViewFrustum());
|
||||
getEntities()->init();
|
||||
getEntities()->setViewFrustum(getViewFrustum());
|
||||
|
||||
ObjectMotionState::setShapeManager(&_shapeManager);
|
||||
_physicsEngine->init();
|
||||
|
||||
EntityTreePointer tree = _entities.getTree();
|
||||
EntityTreePointer tree = getEntities()->getTree();
|
||||
_entitySimulation.init(tree, _physicsEngine, &_entityEditSender);
|
||||
tree->setSimulation(&_entitySimulation);
|
||||
|
||||
|
@ -2545,11 +2535,11 @@ void Application::init() {
|
|||
|
||||
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
|
||||
connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity,
|
||||
&_entities, &EntityTreeRenderer::entityCollisionWithEntity);
|
||||
getEntities(), &EntityTreeRenderer::entityCollisionWithEntity);
|
||||
|
||||
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
|
||||
// of events related clicking, hovering over, and entering entities
|
||||
_entities.connectSignalsToSlots(entityScriptingInterface.data());
|
||||
getEntities()->connectSignalsToSlots(entityScriptingInterface.data());
|
||||
|
||||
_entityClipboardRenderer.init();
|
||||
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
|
||||
|
@ -2898,19 +2888,19 @@ void Application::update(float deltaTime) {
|
|||
|
||||
_avatarUpdate->synchronousProcess();
|
||||
|
||||
{
|
||||
if (_physicsEnabled) {
|
||||
PerformanceTimer perfTimer("physics");
|
||||
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation.getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
|
||||
_entities.getTree()->withWriteLock([&] {
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.getObjectsToAdd(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
||||
});
|
||||
_entities.getTree()->withWriteLock([&] {
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.getObjectsToChange(motionStates);
|
||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
||||
_entitySimulation.setObjectsToChange(stillNeedChange);
|
||||
|
@ -2928,12 +2918,12 @@ void Application::update(float deltaTime) {
|
|||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
|
||||
_entities.getTree()->withWriteLock([&] {
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
_entities.getTree()->withWriteLock([&] {
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), _physicsEngine->getSessionID());
|
||||
avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges());
|
||||
});
|
||||
|
@ -2948,12 +2938,13 @@ void Application::update(float deltaTime) {
|
|||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
||||
// deadlock.)
|
||||
_entitySimulation.handleCollisionEvents(collisionEvents);
|
||||
// NOTE: the _entities.update() call below will wait for lock
|
||||
// NOTE: the getEntities()->update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
_entities.update(); // update the models...
|
||||
getEntities()->update(); // update the models...
|
||||
}
|
||||
|
||||
myAvatar->harvestResultsFromPhysicsSimulation();
|
||||
myAvatar->simulateAttachments(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3246,6 +3237,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
bool Application::isHMDMode() const {
|
||||
return getActiveDisplayPlugin()->isHmd();
|
||||
}
|
||||
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
|
||||
|
||||
QRect Application::getDesirableApplicationGeometry() {
|
||||
QRect applicationGeometry = getWindow()->geometry();
|
||||
|
@ -3775,13 +3767,15 @@ void Application::clearDomainOctreeDetails() {
|
|||
});
|
||||
|
||||
// reset the model renderer
|
||||
_entities.clear();
|
||||
getEntities()->clear();
|
||||
}
|
||||
|
||||
void Application::domainChanged(const QString& domainHostname) {
|
||||
updateWindowTitle();
|
||||
clearDomainOctreeDetails();
|
||||
_domainConnectionRefusals.clear();
|
||||
// disable physics until we have enough information about our new location to not cause craziness.
|
||||
_physicsEnabled = false;
|
||||
}
|
||||
|
||||
void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
|
@ -3889,6 +3883,34 @@ void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNode
|
|||
}
|
||||
}
|
||||
|
||||
bool Application::nearbyEntitiesAreReadyForPhysics() {
|
||||
// this is used to avoid the following scenario:
|
||||
// A table has some items sitting on top of it. The items are at rest, meaning they aren't active in bullet.
|
||||
// Someone logs in close to the table. They receive information about the items on the table before they
|
||||
// receive information about the table. The items are very close to the avatar's capsule, so they become
|
||||
// activated in bullet. This causes them to fall to the floor, because the table's shape isn't yet in bullet.
|
||||
EntityTreePointer entityTree = getEntities()->getTree();
|
||||
if (!entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<EntityItemPointer> entities;
|
||||
entityTree->withReadLock([&] {
|
||||
AABox box(getMyAvatar()->getPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));
|
||||
entityTree->findEntities(box, entities);
|
||||
});
|
||||
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
if (!entity->isReadyToComputeShape()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
|
||||
qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) {
|
||||
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
|
||||
|
||||
|
@ -3934,7 +3956,22 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
if (!_physicsEnabled) {
|
||||
if (nearbyEntitiesAreReadyForPhysics()) {
|
||||
// These stats packets are sent in between full sends of a scene.
|
||||
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
|
||||
// scene is ready to compute its collision shape.
|
||||
_physicsEnabled = true;
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
} else {
|
||||
auto characterController = getMyAvatar()->getCharacterController();
|
||||
if (characterController) {
|
||||
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
|
||||
// a non-loading collision hull.
|
||||
characterController->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return statsMessageLength;
|
||||
}
|
||||
|
@ -3983,20 +4020,13 @@ void Application::saveScripts() {
|
|||
settings.endArray();
|
||||
}
|
||||
|
||||
QScriptValue joystickToScriptValue(QScriptEngine *engine, Joystick* const &in) {
|
||||
return engine->newQObject(in);
|
||||
}
|
||||
|
||||
void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) {
|
||||
out = qobject_cast<Joystick*>(object.toQObject());
|
||||
}
|
||||
|
||||
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// we can use the same ones from the application.
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->setPacketSender(&_entityEditSender);
|
||||
entityScriptingInterface->setEntityTree(_entities.getTree());
|
||||
entityScriptingInterface->setEntityTree(getEntities()->getTree());
|
||||
|
||||
// AvatarManager has some custom types
|
||||
AvatarManager::registerMetaTypes(scriptEngine);
|
||||
|
@ -4017,7 +4047,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
||||
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&)));
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection);
|
||||
|
||||
connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool)));
|
||||
connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool)));
|
||||
|
@ -4054,8 +4084,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
|
||||
|
@ -4263,10 +4291,13 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
|
||||
QUrl scriptUrl(scriptFilename);
|
||||
const QString& scriptURLString = scriptUrl.toString();
|
||||
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
||||
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
||||
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
|
||||
|
||||
return _scriptEnginesHash[scriptURLString];
|
||||
return _scriptEnginesHash[scriptURLString];
|
||||
}
|
||||
}
|
||||
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
||||
|
@ -4306,7 +4337,11 @@ void Application::reloadScript(const QString& scriptName, bool isUserLoaded) {
|
|||
void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
|
||||
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
|
||||
|
||||
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
|
||||
}
|
||||
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
UserActivityLogger::getInstance().loadedScript(scriptFilename);
|
||||
|
||||
|
@ -4321,55 +4356,88 @@ void Application::handleScriptLoadError(const QString& scriptFilename) {
|
|||
QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
|
||||
}
|
||||
|
||||
void Application::scriptFinished(const QString& scriptName) {
|
||||
const QString& scriptURLString = QUrl(scriptName).toString();
|
||||
QHash<QString, ScriptEngine*>::iterator it = _scriptEnginesHash.find(scriptURLString);
|
||||
if (it != _scriptEnginesHash.end()) {
|
||||
_scriptEnginesHash.erase(it);
|
||||
_runningScriptsWidget->scriptStopped(scriptName);
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
QStringList Application::getRunningScripts() {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
return _scriptEnginesHash.keys();
|
||||
}
|
||||
|
||||
ScriptEngine* Application::getScriptEngine(const QString& scriptHash) {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
return _scriptEnginesHash.value(scriptHash, nullptr);
|
||||
}
|
||||
|
||||
void Application::scriptFinished(const QString& scriptName, ScriptEngine* engine) {
|
||||
bool removed = false;
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
const QString& scriptURLString = QUrl(scriptName).toString();
|
||||
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
|
||||
if (it.value() == engine) {
|
||||
_scriptEnginesHash.erase(it);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
postLambdaEvent([this, scriptName]() {
|
||||
_runningScriptsWidget->scriptStopped(scriptName);
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Application::stopAllScripts(bool restart) {
|
||||
if (restart) {
|
||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
|
||||
if (restart) {
|
||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (!it.value()->isFinished()) {
|
||||
scriptCache->deleteScript(it.key());
|
||||
if (!it.value()->isFinished()) {
|
||||
scriptCache->deleteScript(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop and possibly restart all currently running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
// Stop and possibly restart all currently running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
}
|
||||
if (restart && it.value()->isUserLoaded()) {
|
||||
connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
QMetaObject::invokeMethod(it.value(), "stop");
|
||||
//it.value()->stop();
|
||||
qCDebug(interfaceapp) << "stopping script..." << it.key();
|
||||
}
|
||||
if (restart && it.value()->isUserLoaded()) {
|
||||
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
|
||||
}
|
||||
it.value()->stop();
|
||||
qCDebug(interfaceapp) << "stopping script..." << it.key();
|
||||
}
|
||||
getMyAvatar()->clearScriptableSettings();
|
||||
}
|
||||
|
||||
bool Application::stopScript(const QString& scriptHash, bool restart) {
|
||||
bool stoppedScript = false;
|
||||
if (_scriptEnginesHash.contains(scriptHash)) {
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
|
||||
if (restart) {
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->deleteScript(QUrl(scriptHash));
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (_scriptEnginesHash.contains(scriptHash)) {
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
|
||||
if (restart) {
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->deleteScript(QUrl(scriptHash));
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
scriptEngine->stop();
|
||||
stoppedScript = true;
|
||||
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
|
||||
}
|
||||
scriptEngine->stop();
|
||||
stoppedScript = true;
|
||||
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
|
||||
}
|
||||
if (_scriptEnginesHash.empty()) {
|
||||
getMyAvatar()->clearScriptableSettings();
|
||||
|
@ -4388,6 +4456,7 @@ void Application::reloadOneScript(const QString& scriptName) {
|
|||
}
|
||||
|
||||
void Application::loadDefaultScripts() {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ public:
|
|||
const ViewFrustum* getDisplayViewFrustum() const;
|
||||
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
|
||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||
EntityTreeRenderer* getEntities() { return &_entities; }
|
||||
EntityTreeRenderer* getEntities() { return DependencyManager::get<EntityTreeRenderer>().data(); }
|
||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||
MainWindow* getWindow() { return _window; }
|
||||
EntityTreePointer getEntityClipboard() { return _entityClipboard; }
|
||||
|
@ -160,14 +160,9 @@ public:
|
|||
|
||||
uint32_t getFrameCount() { return _frameCount; }
|
||||
float getFps() const { return _fps; }
|
||||
float const HMD_TARGET_FRAME_RATE = 75.0f;
|
||||
float const DESKTOP_TARGET_FRAME_RATE = 60.0f;
|
||||
float getTargetFrameRate() { return isHMDMode() ? HMD_TARGET_FRAME_RATE : DESKTOP_TARGET_FRAME_RATE; }
|
||||
float getTargetFramePeriod() { return isHMDMode() ? 1.0f / HMD_TARGET_FRAME_RATE : 1.0f / DESKTOP_TARGET_FRAME_RATE; } // same as 1/getTargetFrameRate, but w/compile-time division
|
||||
float getTargetFrameRate(); // frames/second
|
||||
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);
|
||||
|
@ -202,8 +197,8 @@ public:
|
|||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
|
||||
ScriptEngine* getScriptEngine(const QString& scriptHash) { return _scriptEnginesHash.value(scriptHash, NULL); }
|
||||
QStringList getRunningScripts();
|
||||
ScriptEngine* getScriptEngine(const QString& scriptHash);
|
||||
|
||||
float getRenderResolutionScale() const;
|
||||
|
||||
|
@ -336,7 +331,7 @@ private slots:
|
|||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
void scriptFinished(const QString& scriptName);
|
||||
void scriptFinished(const QString& scriptName, ScriptEngine* engine);
|
||||
void saveScripts();
|
||||
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
|
||||
|
||||
|
@ -395,6 +390,7 @@ private:
|
|||
|
||||
bool importSVOFromURL(const QString& urlString);
|
||||
|
||||
bool nearbyEntitiesAreReadyForPhysics();
|
||||
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
|
||||
void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket);
|
||||
|
||||
|
@ -442,15 +438,12 @@ 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;
|
||||
PhysicsEnginePointer _physicsEngine;
|
||||
|
||||
EntityTreeRenderer _entities;
|
||||
EntityTreeRenderer _entityClipboardRenderer;
|
||||
EntityTreePointer _entityClipboard;
|
||||
|
||||
|
@ -505,6 +498,7 @@ private:
|
|||
|
||||
TouchEvent _lastTouchEvent;
|
||||
|
||||
QReadWriteLock _scriptEnginesHashLock;
|
||||
RunningScriptsWidget* _runningScriptsWidget;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
bool _runningScriptsWidgetWasVisible;
|
||||
|
@ -564,6 +558,7 @@ private:
|
|||
bool _isForeground = true; // starts out assumed to be in foreground
|
||||
bool _inPaint = false;
|
||||
bool _isGLInitialized { false };
|
||||
bool _physicsEnabled { false };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -200,7 +200,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
PerformanceTimer perfTimer("skeleton");
|
||||
_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 +868,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 +1156,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();
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
|
||||
void init();
|
||||
void simulate(float deltaTime);
|
||||
void simulateAttachments(float deltaTime);
|
||||
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||
|
||||
|
@ -87,7 +88,6 @@ public:
|
|||
bool isInitialized() const { return _initialized; }
|
||||
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
||||
const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
|
||||
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||
glm::vec3 getChestPosition() const;
|
||||
float getAvatarScale() const { return getScale().y; }
|
||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||
|
@ -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:
|
||||
|
||||
|
@ -214,8 +217,6 @@ protected:
|
|||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
|
||||
virtual void fixupModelsInScene();
|
||||
|
||||
void simulateAttachments(float deltaTime);
|
||||
|
||||
virtual void updateJointMappings();
|
||||
|
||||
render::ItemID _renderItemID;
|
||||
|
|
|
@ -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) {
|
||||
|
@ -146,13 +145,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
|
||||
_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 distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
|
||||
float distance;
|
||||
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->getLastUnsynchronizedFps();
|
||||
distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
|
||||
} else {
|
||||
// Here we choose to just use the maximum render cutoff distance if throttled.
|
||||
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
|
||||
}
|
||||
_renderDistanceAverage.updateAverage(distance);
|
||||
_renderDistance = _renderDistanceAverage.getAverage();
|
||||
int renderableCount = 0;
|
||||
|
|
|
@ -323,11 +323,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
return;
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
simulateAttachments(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("joints");
|
||||
// copy out the skeleton joints from the model
|
||||
|
@ -1058,7 +1053,7 @@ void MyAvatar::rebuildSkeletonBody() {
|
|||
void MyAvatar::prepareForPhysicsSimulation() {
|
||||
relayDriveKeysToCharacterController();
|
||||
_characterController.setTargetVelocity(getTargetVelocity());
|
||||
_characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation());
|
||||
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
|
||||
if (qApp->isHMDMode()) {
|
||||
updateHMDFollowVelocity();
|
||||
} else if (_followSpeed > 0.0f) {
|
||||
|
@ -1071,7 +1066,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
||||
glm::vec3 position = getPosition();
|
||||
glm::quat orientation = getOrientation();
|
||||
_characterController.getAvatarPositionAndOrientation(position, orientation);
|
||||
_characterController.getPositionAndOrientation(position, orientation);
|
||||
nextAttitude(position, orientation);
|
||||
if (_followSpeed > 0.0f) {
|
||||
adjustSensorTransform();
|
||||
|
|
|
@ -11,255 +11,25 @@
|
|||
|
||||
#include "MyCharacterController.h"
|
||||
|
||||
#include <BulletCollision/CollisionShapes/btMultiSphereShape.h>
|
||||
#include <BulletDynamics/Dynamics/btRigidBody.h>
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||
#include <LinearMath/btDefaultMotionState.h>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <PhysicsLogging.h>
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
#include <BulletUtil.h>
|
||||
|
||||
#include "MyAvatar.h"
|
||||
|
||||
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
||||
const float DEFAULT_GRAVITY = -5.0f;
|
||||
const float JUMP_SPEED = 3.5f;
|
||||
|
||||
const float MAX_FALL_HEIGHT = 20.0f;
|
||||
|
||||
// TODO: improve walking up steps
|
||||
// TODO: make avatars able to walk up and down steps/slopes
|
||||
// TODO: make avatars stand on steep slope
|
||||
// TODO: make avatars not snag on low ceilings
|
||||
|
||||
// helper class for simple ray-traces from character
|
||||
class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback {
|
||||
public:
|
||||
ClosestNotMe(btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) {
|
||||
_me = me;
|
||||
}
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) {
|
||||
if (rayResult.m_collisionObject == _me) {
|
||||
return 1.0f;
|
||||
}
|
||||
return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||
}
|
||||
protected:
|
||||
btRigidBody* _me;
|
||||
};
|
||||
|
||||
MyCharacterController::MyCharacterController(MyAvatar* avatar) {
|
||||
_halfHeight = 1.0f;
|
||||
|
||||
assert(avatar);
|
||||
_avatar = avatar;
|
||||
|
||||
_enabled = false;
|
||||
|
||||
_floorDistance = MAX_FALL_HEIGHT;
|
||||
|
||||
_walkVelocity.setValue(0.0f, 0.0f, 0.0f);
|
||||
_followVelocity.setValue(0.0f, 0.0f, 0.0f);
|
||||
_jumpSpeed = JUMP_SPEED;
|
||||
_isOnGround = false;
|
||||
_isJumping = false;
|
||||
_isFalling = false;
|
||||
_isHovering = true;
|
||||
_isPushingUp = false;
|
||||
_jumpToHoverStart = 0;
|
||||
_followTime = 0.0f;
|
||||
|
||||
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
||||
updateShapeIfNecessary();
|
||||
}
|
||||
|
||||
MyCharacterController::~MyCharacterController() {
|
||||
}
|
||||
|
||||
void MyCharacterController::preStep(btCollisionWorld* collisionWorld) {
|
||||
// trace a ray straight down to see if we're standing on the ground
|
||||
const btTransform& xform = _rigidBody->getWorldTransform();
|
||||
|
||||
// rayStart is at center of bottom sphere
|
||||
btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp;
|
||||
|
||||
// rayEnd is some short distance outside bottom sphere
|
||||
const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius;
|
||||
btScalar rayLength = _radius + FLOOR_PROXIMITY_THRESHOLD;
|
||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||
|
||||
// scan down for nearby floor
|
||||
ClosestNotMe rayCallback(_rigidBody);
|
||||
rayCallback.m_closestHitFraction = 1.0f;
|
||||
collisionWorld->rayTest(rayStart, rayEnd, rayCallback);
|
||||
if (rayCallback.hasHit()) {
|
||||
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
|
||||
btVector3 actualVelocity = _rigidBody->getLinearVelocity();
|
||||
btScalar actualSpeed = actualVelocity.length();
|
||||
|
||||
btVector3 desiredVelocity = _walkVelocity;
|
||||
btScalar desiredSpeed = desiredVelocity.length();
|
||||
|
||||
const btScalar MIN_UP_PUSH = 0.1f;
|
||||
if (desiredVelocity.dot(_currentUp) < MIN_UP_PUSH) {
|
||||
_isPushingUp = false;
|
||||
}
|
||||
|
||||
const btScalar MIN_SPEED = 0.001f;
|
||||
if (_isHovering) {
|
||||
if (desiredSpeed < MIN_SPEED) {
|
||||
if (actualSpeed < MIN_SPEED) {
|
||||
_rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
const btScalar HOVER_BRAKING_TIMESCALE = 0.1f;
|
||||
btScalar tau = glm::max(dt / HOVER_BRAKING_TIMESCALE, 1.0f);
|
||||
_rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity);
|
||||
}
|
||||
} else {
|
||||
const btScalar HOVER_ACCELERATION_TIMESCALE = 0.1f;
|
||||
btScalar tau = dt / HOVER_ACCELERATION_TIMESCALE;
|
||||
_rigidBody->setLinearVelocity(actualVelocity - tau * (actualVelocity - desiredVelocity));
|
||||
}
|
||||
} else {
|
||||
if (onGround()) {
|
||||
// walking on ground
|
||||
if (desiredSpeed < MIN_SPEED) {
|
||||
if (actualSpeed < MIN_SPEED) {
|
||||
_rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
const btScalar HOVER_BRAKING_TIMESCALE = 0.1f;
|
||||
btScalar tau = dt / HOVER_BRAKING_TIMESCALE;
|
||||
_rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity);
|
||||
}
|
||||
} else {
|
||||
// TODO: modify desiredVelocity using floor normal
|
||||
const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f;
|
||||
btScalar tau = dt / WALK_ACCELERATION_TIMESCALE;
|
||||
btVector3 velocityCorrection = tau * (desiredVelocity - actualVelocity);
|
||||
// subtract vertical component
|
||||
velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp;
|
||||
_rigidBody->setLinearVelocity(actualVelocity + velocityCorrection);
|
||||
}
|
||||
} else {
|
||||
// transitioning to flying
|
||||
btVector3 velocityCorrection = desiredVelocity - actualVelocity;
|
||||
const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f;
|
||||
btScalar tau = dt / FLY_ACCELERATION_TIMESCALE;
|
||||
if (!_isPushingUp) {
|
||||
// actually falling --> compute a different velocity attenuation factor
|
||||
const btScalar FALL_ACCELERATION_TIMESCALE = 2.0f;
|
||||
tau = dt / FALL_ACCELERATION_TIMESCALE;
|
||||
// zero vertical component
|
||||
velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp;
|
||||
}
|
||||
_rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection);
|
||||
}
|
||||
}
|
||||
|
||||
// Rather than add _followVelocity to the velocity of the RigidBody, we explicitly teleport
|
||||
// the RigidBody forward according to the formula: distance = rate * time
|
||||
if (_followVelocity.length2() > 0.0f) {
|
||||
btTransform bodyTransform = _rigidBody->getWorldTransform();
|
||||
bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _followVelocity);
|
||||
_rigidBody->setWorldTransform(bodyTransform);
|
||||
}
|
||||
_followTime += dt;
|
||||
}
|
||||
|
||||
void MyCharacterController::jump() {
|
||||
// check for case where user is holding down "jump" key...
|
||||
// we'll eventually tansition to "hover"
|
||||
if (!_isJumping) {
|
||||
if (!_isHovering) {
|
||||
_jumpToHoverStart = usecTimestampNow();
|
||||
_pendingFlags |= PENDING_FLAG_JUMP;
|
||||
}
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100);
|
||||
if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) {
|
||||
_isPushingUp = true;
|
||||
setHovering(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MyCharacterController::onGround() const {
|
||||
const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius;
|
||||
return _floorDistance < FLOOR_PROXIMITY_THRESHOLD;
|
||||
}
|
||||
|
||||
void MyCharacterController::setHovering(bool hover) {
|
||||
if (hover != _isHovering) {
|
||||
_isHovering = hover;
|
||||
_isJumping = false;
|
||||
|
||||
if (_rigidBody) {
|
||||
if (hover) {
|
||||
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
_rigidBody->setGravity(DEFAULT_GRAVITY * _currentUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) {
|
||||
_boxScale = scale;
|
||||
|
||||
float x = _boxScale.x;
|
||||
float z = _boxScale.z;
|
||||
float radius = 0.5f * sqrtf(0.5f * (x * x + z * z));
|
||||
float halfHeight = 0.5f * _boxScale.y - radius;
|
||||
float MIN_HALF_HEIGHT = 0.1f;
|
||||
if (halfHeight < MIN_HALF_HEIGHT) {
|
||||
halfHeight = MIN_HALF_HEIGHT;
|
||||
}
|
||||
|
||||
// compare dimensions
|
||||
float radiusDelta = glm::abs(radius - _radius);
|
||||
float heightDelta = glm::abs(halfHeight - _halfHeight);
|
||||
if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) {
|
||||
// shape hasn't changed --> nothing to do
|
||||
} else {
|
||||
if (_dynamicsWorld) {
|
||||
// must REMOVE from world prior to shape update
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE;
|
||||
// only need to ADD back when we happen to be enabled
|
||||
if (_enabled) {
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
}
|
||||
}
|
||||
|
||||
// it's ok to change offset immediately -- there are no thread safety issues here
|
||||
_shapeLocalOffset = corner + 0.5f * _boxScale;
|
||||
}
|
||||
|
||||
void MyCharacterController::setEnabled(bool enabled) {
|
||||
if (enabled != _enabled) {
|
||||
if (enabled) {
|
||||
// Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit.
|
||||
// Setting the ADD bit here works for all cases so we don't even bother checking other bits.
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
} else {
|
||||
if (_dynamicsWorld) {
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
_isOnGround = false;
|
||||
}
|
||||
setHovering(true);
|
||||
_enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::updateShapeIfNecessary() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {
|
||||
_pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE;
|
||||
|
@ -300,7 +70,7 @@ void MyCharacterController::updateShapeIfNecessary() {
|
|||
if (_isHovering) {
|
||||
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
_rigidBody->setGravity(DEFAULT_GRAVITY * _currentUp);
|
||||
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
|
||||
}
|
||||
//_rigidBody->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
|
||||
} else {
|
||||
|
@ -309,98 +79,3 @@ void MyCharacterController::updateShapeIfNecessary() {
|
|||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::updateUpAxis(const glm::quat& rotation) {
|
||||
btVector3 oldUp = _currentUp;
|
||||
_currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS);
|
||||
if (!_isHovering) {
|
||||
const btScalar MIN_UP_ERROR = 0.01f;
|
||||
if (oldUp.distance(_currentUp) > MIN_UP_ERROR) {
|
||||
_rigidBody->setGravity(DEFAULT_GRAVITY * _currentUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::setAvatarPositionAndOrientation(
|
||||
const glm::vec3& position,
|
||||
const glm::quat& orientation) {
|
||||
// TODO: update gravity if up has changed
|
||||
updateUpAxis(orientation);
|
||||
|
||||
btQuaternion bodyOrientation = glmToBullet(orientation);
|
||||
btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset);
|
||||
_avatarBodyTransform = btTransform(bodyOrientation, bodyPosition);
|
||||
}
|
||||
|
||||
void MyCharacterController::getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const {
|
||||
if (_enabled && _rigidBody) {
|
||||
const btTransform& avatarTransform = _rigidBody->getWorldTransform();
|
||||
rotation = bulletToGLM(avatarTransform.getRotation());
|
||||
position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::setTargetVelocity(const glm::vec3& velocity) {
|
||||
//_walkVelocity = glmToBullet(_avatarData->getTargetVelocity());
|
||||
_walkVelocity = glmToBullet(velocity);
|
||||
}
|
||||
|
||||
void MyCharacterController::setFollowVelocity(const glm::vec3& velocity) {
|
||||
_followVelocity = glmToBullet(velocity);
|
||||
}
|
||||
|
||||
glm::vec3 MyCharacterController::getLinearVelocity() const {
|
||||
glm::vec3 velocity(0.0f);
|
||||
if (_rigidBody) {
|
||||
velocity = bulletToGLM(_rigidBody->getLinearVelocity());
|
||||
}
|
||||
return velocity;
|
||||
}
|
||||
|
||||
void MyCharacterController::preSimulation() {
|
||||
if (_enabled && _dynamicsWorld) {
|
||||
// slam body to where it is supposed to be
|
||||
_rigidBody->setWorldTransform(_avatarBodyTransform);
|
||||
|
||||
// scan for distant floor
|
||||
// rayStart is at center of bottom sphere
|
||||
btVector3 rayStart = _avatarBodyTransform.getOrigin() - _halfHeight * _currentUp;
|
||||
|
||||
// rayEnd is straight down MAX_FALL_HEIGHT
|
||||
btScalar rayLength = _radius + MAX_FALL_HEIGHT;
|
||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||
|
||||
ClosestNotMe rayCallback(_rigidBody);
|
||||
rayCallback.m_closestHitFraction = 1.0f;
|
||||
_dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback);
|
||||
if (rayCallback.hasHit()) {
|
||||
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
|
||||
const btScalar MIN_HOVER_HEIGHT = 3.0f;
|
||||
if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) {
|
||||
setHovering(false);
|
||||
}
|
||||
// TODO: use collision events rather than ray-trace test to disable jumping
|
||||
const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius;
|
||||
if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) {
|
||||
_isJumping = false;
|
||||
}
|
||||
} else {
|
||||
_floorDistance = FLT_MAX;
|
||||
setHovering(true);
|
||||
}
|
||||
|
||||
if (_pendingFlags & PENDING_FLAG_JUMP) {
|
||||
_pendingFlags &= ~ PENDING_FLAG_JUMP;
|
||||
if (onGround()) {
|
||||
_isJumping = true;
|
||||
btVector3 velocity = _rigidBody->getLinearVelocity();
|
||||
velocity += _jumpSpeed * _currentUp;
|
||||
_rigidBody->setLinearVelocity(velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
_followTime = 0.0f;
|
||||
}
|
||||
|
||||
void MyCharacterController::postSimulation() {
|
||||
// postSimulation() exists for symmetry and just in case we need to do something here later
|
||||
}
|
||||
|
|
|
@ -13,12 +13,8 @@
|
|||
#ifndef hifi_MyCharacterController_h
|
||||
#define hifi_MyCharacterController_h
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <BulletUtil.h>
|
||||
#include <CharacterController.h>
|
||||
#include <SharedUtil.h>
|
||||
//#include <SharedUtil.h>
|
||||
|
||||
class btCollisionShape;
|
||||
class MyAvatar;
|
||||
|
@ -28,79 +24,10 @@ public:
|
|||
MyCharacterController(MyAvatar* avatar);
|
||||
~MyCharacterController ();
|
||||
|
||||
// TODO: implement these when needed
|
||||
virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); }
|
||||
virtual void reset(btCollisionWorld* collisionWorld) override { }
|
||||
virtual void warp(const btVector3& origin) override { }
|
||||
virtual void debugDraw(btIDebugDraw* debugDrawer) override { }
|
||||
virtual void setUpInterpolate(bool value) override { }
|
||||
|
||||
// overrides from btCharacterControllerInterface
|
||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override {
|
||||
preStep(collisionWorld);
|
||||
playerStep(collisionWorld, deltaTime);
|
||||
}
|
||||
virtual void preStep(btCollisionWorld* collisionWorld) override;
|
||||
virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt) override;
|
||||
virtual bool canJump() const override { assert(false); return false; } // never call this
|
||||
virtual void jump() override; // call this every frame the jump button is pressed
|
||||
virtual bool onGround() const override;
|
||||
|
||||
// overrides from CharacterController
|
||||
virtual void preSimulation() override;
|
||||
virtual void postSimulation() override;
|
||||
|
||||
bool isHovering() const { return _isHovering; }
|
||||
void setHovering(bool enabled);
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const { return _enabled && _dynamicsWorld; }
|
||||
|
||||
void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale);
|
||||
|
||||
virtual void updateShapeIfNecessary() override;
|
||||
|
||||
void setAvatarPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation);
|
||||
void getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
void setTargetVelocity(const glm::vec3& velocity);
|
||||
void setFollowVelocity(const glm::vec3& velocity);
|
||||
float getFollowTime() const { return _followTime; }
|
||||
|
||||
glm::vec3 getLinearVelocity() const;
|
||||
|
||||
protected:
|
||||
void updateUpAxis(const glm::quat& rotation);
|
||||
|
||||
protected:
|
||||
btVector3 _currentUp;
|
||||
btVector3 _walkVelocity;
|
||||
btVector3 _followVelocity;
|
||||
btTransform _avatarBodyTransform;
|
||||
|
||||
glm::vec3 _shapeLocalOffset;
|
||||
glm::vec3 _boxScale; // used to compute capsule shape
|
||||
|
||||
quint64 _jumpToHoverStart;
|
||||
|
||||
MyAvatar* _avatar { nullptr };
|
||||
|
||||
btScalar _halfHeight;
|
||||
btScalar _radius;
|
||||
|
||||
btScalar _floorDistance;
|
||||
|
||||
btScalar _gravity;
|
||||
|
||||
btScalar _jumpSpeed;
|
||||
btScalar _followTime;
|
||||
|
||||
bool _enabled;
|
||||
bool _isOnGround;
|
||||
bool _isJumping;
|
||||
bool _isFalling;
|
||||
bool _isHovering;
|
||||
bool _isPushingUp;
|
||||
};
|
||||
|
||||
#endif // hifi_MyCharacterController_h
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
#include "Application.h"
|
||||
#include "devices/MotionTracker.h"
|
||||
|
||||
// TODO: this needs to be removed, as well as any related controller-specific information
|
||||
#include <input-plugins/SixenseManager.h>
|
||||
|
||||
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
|
||||
if (event->type() == HFActionEvent::startType()) {
|
||||
emit actionStartEvent(static_cast<HFActionEvent&>(*event));
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "Tooltip.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here
|
||||
#include <controllers/InputDevice.h>
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <avatar/AvatarManager.h>
|
||||
#include <devices/DdeFaceTracker.h>
|
||||
#include <devices/Faceshift.h>
|
||||
#include <input-plugins/SixenseManager.h> // TODO: This should be replaced with InputDevice/InputPlugin, or something similar
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
|
|
@ -374,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()) {
|
||||
|
@ -384,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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,7 +47,7 @@ const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData";
|
|||
static std::once_flag frameTypeRegistration;
|
||||
|
||||
AvatarData::AvatarData() :
|
||||
SpatiallyNestable(NestableTypes::Avatar, QUuid()),
|
||||
SpatiallyNestable(NestableType::Avatar, QUuid()),
|
||||
_handPosition(0.0f),
|
||||
_targetScale(1.0f),
|
||||
_handState(0),
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -83,19 +83,18 @@ void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
|
|||
}
|
||||
WindowOpenGLDisplayPlugin::internalPresent();
|
||||
}
|
||||
|
||||
const uint32_t THROTTLED_FRAMERATE = 15;
|
||||
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
|
||||
static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15;
|
||||
static const int ULIMIITED_PAINT_TIMER_DELAY_MS = 1;
|
||||
int result = ULIMIITED_PAINT_TIMER_DELAY_MS;
|
||||
if (_isThrottled) {
|
||||
result = THROTTLED_PAINT_TIMER_DELAY_MS;
|
||||
}
|
||||
if (0 != _framerateTarget) {
|
||||
result = MSECS_PER_SECOND / _framerateTarget;
|
||||
} else if (_isThrottled) {
|
||||
// This test wouldn't be necessary if we could depend on updateFramerate setting _framerateTarget.
|
||||
// Alas, that gets complicated: isThrottled() is const and other stuff depends on it.
|
||||
result = MSECS_PER_SECOND / THROTTLED_FRAMERATE;
|
||||
}
|
||||
|
||||
qDebug() << "New interval " << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -112,7 +111,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
|
|||
return shouldThrottle;
|
||||
}
|
||||
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
|
||||
QAction* checkedFramerate{ nullptr };
|
||||
foreach(auto action, _framerateActions) {
|
||||
|
@ -134,11 +132,12 @@ void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
|
|||
} else if (FRAMERATE_30 == actionText) {
|
||||
_framerateTarget = 30;
|
||||
}
|
||||
}
|
||||
} else if (_isThrottled) {
|
||||
_framerateTarget = THROTTLED_FRAMERATE;
|
||||
}
|
||||
|
||||
int newInterval = getDesiredInterval();
|
||||
qDebug() << newInterval;
|
||||
_timer.start(getDesiredInterval());
|
||||
_timer.start(newInterval);
|
||||
}
|
||||
|
||||
// FIXME target the screen the window is currently on
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "WindowOpenGLDisplayPlugin.h"
|
||||
|
||||
const float TARGET_FRAMERATE_Basic2DWindowOpenGL = 60.0f;
|
||||
|
||||
class QScreen;
|
||||
class QAction;
|
||||
|
||||
|
@ -18,6 +20,8 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
|||
public:
|
||||
virtual const QString & getName() const override;
|
||||
|
||||
virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; }
|
||||
|
||||
virtual void activate() override;
|
||||
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include "stereo/InterleavedStereoDisplayPlugin.h"
|
||||
#include "Basic2DWindowOpenGLDisplayPlugin.h"
|
||||
|
||||
#include "openvr/OpenVrDisplayPlugin.h"
|
||||
|
||||
const QString& DisplayPlugin::MENU_PATH() {
|
||||
static const QString value = "Display";
|
||||
return value;
|
||||
|
@ -25,22 +23,14 @@ const QString& DisplayPlugin::MENU_PATH() {
|
|||
DisplayPluginList getDisplayPlugins() {
|
||||
DisplayPlugin* PLUGIN_POOL[] = {
|
||||
new Basic2DWindowOpenGLDisplayPlugin(),
|
||||
new NullDisplayPlugin(),
|
||||
#ifdef DEBUG
|
||||
new NullDisplayPlugin(),
|
||||
#endif
|
||||
|
||||
// Stereo modes
|
||||
|
||||
// SBS left/right
|
||||
new SideBySideStereoDisplayPlugin(),
|
||||
// Interleaved left/right
|
||||
new InterleavedStereoDisplayPlugin(),
|
||||
|
||||
// HMDs
|
||||
//#ifdef Q_OS_WIN
|
||||
// // SteamVR SDK
|
||||
// new OpenVrDisplayPlugin(),
|
||||
//#endif
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <QtOpenGL/QGLWidget>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include <gl/GLWidget.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
}
|
||||
|
||||
virtual void run() override {
|
||||
OpenGLDisplayPlugin* currentPlugin{ nullptr };
|
||||
Q_ASSERT(_context);
|
||||
while (!_shutdown) {
|
||||
if (_pendingMainThreadOperation) {
|
||||
|
@ -81,12 +83,13 @@ public:
|
|||
// Check if we have a new plugin to activate
|
||||
if (_newPlugin != nullptr) {
|
||||
// Deactivate the old plugin
|
||||
if (_activePlugin != nullptr) {
|
||||
_activePlugin->uncustomizeContext();
|
||||
if (currentPlugin != nullptr) {
|
||||
currentPlugin->uncustomizeContext();
|
||||
currentPlugin->enableDeactivate();
|
||||
}
|
||||
|
||||
_newPlugin->customizeContext();
|
||||
_activePlugin = _newPlugin;
|
||||
currentPlugin = _newPlugin;
|
||||
_newPlugin = nullptr;
|
||||
}
|
||||
_context->doneCurrent();
|
||||
|
@ -94,20 +97,25 @@ public:
|
|||
}
|
||||
|
||||
// If there's no active plugin, just sleep
|
||||
if (_activePlugin == nullptr) {
|
||||
if (currentPlugin == nullptr) {
|
||||
QThread::usleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
// take the latest texture and present it
|
||||
_context->makeCurrent();
|
||||
_activePlugin->present();
|
||||
_context->doneCurrent();
|
||||
if (QOpenGLContext::currentContext() == _context->contextHandle()) {
|
||||
currentPlugin->present();
|
||||
_context->doneCurrent();
|
||||
} else {
|
||||
qWarning() << "Makecurrent failed";
|
||||
}
|
||||
}
|
||||
|
||||
_context->makeCurrent();
|
||||
if (_activePlugin) {
|
||||
_activePlugin->uncustomizeContext();
|
||||
if (currentPlugin) {
|
||||
currentPlugin->uncustomizeContext();
|
||||
currentPlugin->enableDeactivate();
|
||||
}
|
||||
_context->doneCurrent();
|
||||
_context->moveToThread(qApp->thread());
|
||||
|
@ -147,7 +155,6 @@ private:
|
|||
bool _finishedMainThreadOperation { false };
|
||||
QThread* _mainThread { nullptr };
|
||||
OpenGLDisplayPlugin* _newPlugin { nullptr };
|
||||
OpenGLDisplayPlugin* _activePlugin { nullptr };
|
||||
QGLContext* _context { nullptr };
|
||||
};
|
||||
|
||||
|
@ -208,11 +215,16 @@ void OpenGLDisplayPlugin::stop() {
|
|||
}
|
||||
|
||||
void OpenGLDisplayPlugin::deactivate() {
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
_deactivateWait.wait(lock, [&]{ return _uncustomized; });
|
||||
}
|
||||
_timer.stop();
|
||||
DisplayPlugin::deactivate();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::customizeContext() {
|
||||
_uncustomized = false;
|
||||
auto presentThread = DependencyManager::get<PresentThread>();
|
||||
Q_ASSERT(thread() == presentThread->thread());
|
||||
|
||||
|
@ -233,6 +245,7 @@ void OpenGLDisplayPlugin::uncustomizeContext() {
|
|||
_plane.reset();
|
||||
}
|
||||
|
||||
|
||||
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
|
||||
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to
|
||||
// receive keyPress events for the Alt (and Meta) key in a reliable manner.
|
||||
|
@ -380,3 +393,9 @@ QImage OpenGLDisplayPlugin::getScreenshot() const {
|
|||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::enableDeactivate() {
|
||||
Lock lock(_mutex);
|
||||
_uncustomized = true;
|
||||
_deactivateWait.notify_one();
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "DisplayPlugin.h"
|
||||
|
||||
#include <condition_variable>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
@ -18,8 +20,9 @@
|
|||
|
||||
class OpenGLDisplayPlugin : public DisplayPlugin {
|
||||
protected:
|
||||
using Mutex = std::recursive_mutex;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
using Condition = std::condition_variable;
|
||||
public:
|
||||
OpenGLDisplayPlugin();
|
||||
virtual void activate() override;
|
||||
|
@ -71,7 +74,7 @@ protected:
|
|||
ProgramPtr _program;
|
||||
ShapeWrapperPtr _plane;
|
||||
|
||||
Mutex _mutex;
|
||||
mutable Mutex _mutex;
|
||||
SimpleMovingAverage _usecsPerFrame { 10 };
|
||||
QMap<uint32_t, uint32_t> _sceneTextureToFrameIndexMap;
|
||||
|
||||
|
@ -82,6 +85,12 @@ protected:
|
|||
GLTextureEscrow _sceneTextureEscrow;
|
||||
|
||||
bool _vsyncSupported { false };
|
||||
|
||||
private:
|
||||
void enableDeactivate();
|
||||
Condition _deactivateWait;
|
||||
bool _uncustomized{ false };
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class ZoneEntityItem;
|
|||
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
|
||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <Model.h>
|
||||
#include <PerfStat.h>
|
||||
#include <render/Scene.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
@ -44,7 +46,33 @@ RenderableModelEntityItem::~RenderableModelEntityItem() {
|
|||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setDimensions(const glm::vec3 value) {
|
||||
void RenderableModelEntityItem::setModelURL(const QString& url) {
|
||||
auto& currentURL = getParsedModelURL();
|
||||
ModelEntityItem::setModelURL(url);
|
||||
|
||||
if (currentURL != getParsedModelURL() || !_model) {
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::loader() {
|
||||
_needsModelReload = true;
|
||||
EntityTreeRenderer* renderer = DependencyManager::get<EntityTreeRenderer>().data();
|
||||
assert(renderer);
|
||||
if (!_model || _needsModelReload) {
|
||||
PerformanceTimer perfTimer("getModel");
|
||||
getModel(renderer);
|
||||
}
|
||||
if (_model) {
|
||||
_model->setURL(getParsedModelURL());
|
||||
_model->setCollisionModelURL(QUrl(getCompoundShapeURL()));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setDimensions(const glm::vec3& value) {
|
||||
_dimensionsInitialized = true;
|
||||
ModelEntityItem::setDimensions(value);
|
||||
}
|
||||
|
@ -223,7 +251,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
// check if the URL has changed
|
||||
auto& currentURL = getParsedModelURL();
|
||||
if (currentURL != _model->getURL()) {
|
||||
qDebug().noquote() << "Updating model URL: " << currentURL.toDisplayString();
|
||||
qCDebug(entitiesrenderer).noquote() << "Updating model URL: " << currentURL.toDisplayString();
|
||||
_model->setURL(currentURL);
|
||||
}
|
||||
|
||||
|
@ -318,7 +346,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
|
||||
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
|
||||
Model* result = NULL;
|
||||
|
||||
|
||||
if (!renderer) {
|
||||
return result;
|
||||
}
|
||||
|
@ -340,7 +368,8 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
|
|||
|
||||
// if we have a previously allocated model, but its URL doesn't match
|
||||
// then we need to let our renderer update our model for us.
|
||||
if (_model && QUrl(getModelURL()) != _model->getURL()) {
|
||||
if (_model && (QUrl(getModelURL()) != _model->getURL() ||
|
||||
QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) {
|
||||
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL());
|
||||
_needsInitialSimulation = true;
|
||||
} else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one
|
||||
|
@ -403,23 +432,34 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
}
|
||||
|
||||
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
||||
auto currentCompoundShapeURL = getCompoundShapeURL();
|
||||
ModelEntityItem::setCompoundShapeURL(url);
|
||||
if (_model) {
|
||||
_model->setCollisionModelURL(QUrl(url));
|
||||
|
||||
if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) {
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::isReadyToComputeShape() {
|
||||
ShapeType type = getShapeType();
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
if (!_model) {
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
}
|
||||
return false; // hmm...
|
||||
}
|
||||
|
||||
if (_needsInitialSimulation) {
|
||||
// the _model's offset will be wrong until _needsInitialSimulation is false
|
||||
return false;
|
||||
PerformanceTimer perfTimer("_model->simulate");
|
||||
_model->simulate(0.0f);
|
||||
_needsInitialSimulation = false;
|
||||
}
|
||||
|
||||
assert(!_model->getCollisionURL().isEmpty());
|
||||
|
@ -565,20 +605,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,9 @@ public:
|
|||
|
||||
virtual ~RenderableModelEntityItem();
|
||||
|
||||
virtual void setDimensions(const glm::vec3 value) override;
|
||||
|
||||
virtual void setDimensions(const glm::vec3& value) override;
|
||||
virtual void setModelURL(const QString& url);
|
||||
|
||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
|
@ -67,9 +68,11 @@ 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;
|
||||
|
||||
virtual void loader() override;
|
||||
|
||||
private:
|
||||
void remapTextures();
|
||||
|
|
|
@ -23,46 +23,73 @@
|
|||
#include "untextured_particle_frag.h"
|
||||
#include "textured_particle_vert.h"
|
||||
#include "textured_particle_frag.h"
|
||||
#include "textured_particle_alpha_discard_frag.h"
|
||||
|
||||
class ParticlePayload {
|
||||
|
||||
class ParticlePayloadData {
|
||||
public:
|
||||
typedef render::Payload<ParticlePayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
typedef RenderableParticleEffectEntityItem::Vertex Vertex;
|
||||
static const size_t VERTEX_PER_PARTICLE = 4;
|
||||
|
||||
ParticlePayload(EntityItemPointer entity) :
|
||||
_entity(entity),
|
||||
_vertexFormat(std::make_shared<gpu::Stream::Format>()),
|
||||
_vertexBuffer(std::make_shared<gpu::Buffer>()),
|
||||
_indexBuffer(std::make_shared<gpu::Buffer>()) {
|
||||
|
||||
_vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0);
|
||||
_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv));
|
||||
_vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba));
|
||||
template<typename T>
|
||||
struct InterpolationData {
|
||||
T start;
|
||||
T middle;
|
||||
T finish;
|
||||
T spread;
|
||||
};
|
||||
struct ParticleUniforms {
|
||||
InterpolationData<float> radius;
|
||||
InterpolationData<glm::vec4> color; // rgba
|
||||
float lifespan;
|
||||
};
|
||||
|
||||
struct ParticlePrimitive {
|
||||
ParticlePrimitive(glm::vec3 xyzIn, glm::vec2 uvIn) : xyz(xyzIn), uv(uvIn) {}
|
||||
glm::vec3 xyz; // Position
|
||||
glm::vec2 uv; // Lifetime + seed
|
||||
};
|
||||
|
||||
using Payload = render::Payload<ParticlePayloadData>;
|
||||
using Pointer = Payload::DataPointer;
|
||||
using PipelinePointer = gpu::PipelinePointer;
|
||||
using FormatPointer = gpu::Stream::FormatPointer;
|
||||
using BufferPointer = gpu::BufferPointer;
|
||||
using TexturePointer = gpu::TexturePointer;
|
||||
using Format = gpu::Stream::Format;
|
||||
using Buffer = gpu::Buffer;
|
||||
using BufferView = gpu::BufferView;
|
||||
using ParticlePrimitives = std::vector<ParticlePrimitive>;
|
||||
|
||||
ParticlePayloadData() {
|
||||
ParticleUniforms uniforms;
|
||||
_uniformBuffer = std::make_shared<Buffer>(sizeof(ParticleUniforms), (const gpu::Byte*) &uniforms);
|
||||
|
||||
_vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ,
|
||||
offsetof(ParticlePrimitive, xyz), gpu::Stream::PER_INSTANCE);
|
||||
_vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::VEC2F_UV,
|
||||
offsetof(ParticlePrimitive, uv), gpu::Stream::PER_INSTANCE);
|
||||
}
|
||||
|
||||
void setPipeline(gpu::PipelinePointer pipeline) { _pipeline = pipeline; }
|
||||
const gpu::PipelinePointer& getPipeline() const { return _pipeline; }
|
||||
void setPipeline(PipelinePointer pipeline) { _pipeline = pipeline; }
|
||||
const PipelinePointer& getPipeline() const { return _pipeline; }
|
||||
|
||||
const Transform& getModelTransform() const { return _modelTransform; }
|
||||
void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; }
|
||||
|
||||
const AABox& getBound() const { return _bound; }
|
||||
void setBound(AABox& bound) { _bound = bound; }
|
||||
void setBound(const AABox& bound) { _bound = bound; }
|
||||
|
||||
gpu::BufferPointer getVertexBuffer() { return _vertexBuffer; }
|
||||
const gpu::BufferPointer& getVertexBuffer() const { return _vertexBuffer; }
|
||||
BufferPointer getParticleBuffer() { return _particleBuffer; }
|
||||
const BufferPointer& getParticleBuffer() const { return _particleBuffer; }
|
||||
|
||||
const ParticleUniforms& getParticleUniforms() const { return _uniformBuffer.get<ParticleUniforms>(); }
|
||||
ParticleUniforms& editParticleUniforms() { return _uniformBuffer.edit<ParticleUniforms>(); }
|
||||
|
||||
gpu::BufferPointer getIndexBuffer() { return _indexBuffer; }
|
||||
const gpu::BufferPointer& getIndexBuffer() const { return _indexBuffer; }
|
||||
|
||||
void setTexture(gpu::TexturePointer texture) { _texture = texture; }
|
||||
const gpu::TexturePointer& getTexture() const { return _texture; }
|
||||
void setTexture(TexturePointer texture) { _texture = texture; }
|
||||
const TexturePointer& getTexture() const { return _texture; }
|
||||
|
||||
bool getVisibleFlag() const { return _visibleFlag; }
|
||||
void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; }
|
||||
|
||||
|
||||
void render(RenderArgs* args) const {
|
||||
assert(_pipeline);
|
||||
|
||||
|
@ -74,29 +101,28 @@ public:
|
|||
}
|
||||
|
||||
batch.setModelTransform(_modelTransform);
|
||||
batch.setUniformBuffer(0, _uniformBuffer);
|
||||
batch.setInputFormat(_vertexFormat);
|
||||
batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex));
|
||||
batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0);
|
||||
batch.setInputBuffer(0, _particleBuffer, 0, sizeof(ParticlePrimitive));
|
||||
|
||||
auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t);
|
||||
batch.drawIndexed(gpu::TRIANGLES, numIndices);
|
||||
auto numParticles = _particleBuffer->getSize() / sizeof(ParticlePrimitive);
|
||||
batch.drawInstanced(numParticles, gpu::TRIANGLE_STRIP, VERTEX_PER_PARTICLE);
|
||||
}
|
||||
|
||||
protected:
|
||||
EntityItemPointer _entity;
|
||||
Transform _modelTransform;
|
||||
AABox _bound;
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::Stream::FormatPointer _vertexFormat;
|
||||
gpu::BufferPointer _vertexBuffer;
|
||||
gpu::BufferPointer _indexBuffer;
|
||||
gpu::TexturePointer _texture;
|
||||
PipelinePointer _pipeline;
|
||||
FormatPointer _vertexFormat { std::make_shared<Format>() };
|
||||
BufferPointer _particleBuffer { std::make_shared<Buffer>() };
|
||||
BufferView _uniformBuffer;
|
||||
TexturePointer _texture;
|
||||
bool _visibleFlag = true;
|
||||
};
|
||||
|
||||
namespace render {
|
||||
template <>
|
||||
const ItemKey payloadGetKey(const ParticlePayload::Pointer& payload) {
|
||||
const ItemKey payloadGetKey(const ParticlePayloadData::Pointer& payload) {
|
||||
if (payload->getVisibleFlag()) {
|
||||
return ItemKey::Builder::transparentShape();
|
||||
} else {
|
||||
|
@ -105,13 +131,15 @@ namespace render {
|
|||
}
|
||||
|
||||
template <>
|
||||
const Item::Bound payloadGetBound(const ParticlePayload::Pointer& payload) {
|
||||
const Item::Bound payloadGetBound(const ParticlePayloadData::Pointer& payload) {
|
||||
return payload->getBound();
|
||||
}
|
||||
|
||||
template <>
|
||||
void payloadRender(const ParticlePayload::Pointer& payload, RenderArgs* args) {
|
||||
payload->render(args);
|
||||
void payloadRender(const ParticlePayloadData::Pointer& payload, RenderArgs* args) {
|
||||
if (payload->getVisibleFlag()) {
|
||||
payload->render(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +147,7 @@ namespace render {
|
|||
|
||||
EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID,
|
||||
const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity{ new RenderableParticleEffectEntityItem(entityID) };
|
||||
auto entity = std::make_shared<RenderableParticleEffectEntityItem>(entityID);
|
||||
entity->setProperties(properties);
|
||||
return entity;
|
||||
}
|
||||
|
@ -127,7 +155,7 @@ EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID
|
|||
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID) :
|
||||
ParticleEffectEntityItem(entityItemID) {
|
||||
// lazy creation of particle system pipeline
|
||||
if (!_untexturedPipeline && !_texturedPipeline) {
|
||||
if (!_untexturedPipeline || !_texturedPipeline) {
|
||||
createPipelines();
|
||||
}
|
||||
}
|
||||
|
@ -135,18 +163,15 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
|
|||
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
|
||||
render::ScenePointer scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
|
||||
auto particlePayload =
|
||||
std::shared_ptr<ParticlePayload>(new ParticlePayload(getThisPointer()));
|
||||
particlePayload->setPipeline(_untexturedPipeline);
|
||||
_renderItemId = scene->allocateID();
|
||||
auto renderData = ParticlePayload::Pointer(particlePayload);
|
||||
auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData));
|
||||
_scene = scene;
|
||||
_renderItemId = _scene->allocateID();
|
||||
auto particlePayloadData = std::make_shared<ParticlePayloadData>();
|
||||
particlePayloadData->setPipeline(_untexturedPipeline);
|
||||
auto renderPayload = std::make_shared<ParticlePayloadData::Payload>(particlePayloadData);
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
pendingChanges.resetItem(_renderItemId, renderPayload);
|
||||
_scene = scene;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -174,141 +199,71 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) {
|
|||
updateRenderItem();
|
||||
}
|
||||
|
||||
uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24);
|
||||
}
|
||||
|
||||
class ParticleDetails {
|
||||
public:
|
||||
ParticleDetails(glm::vec3 position, float radius, uint32_t rgba) : position(position), radius(radius), rgba(rgba) { }
|
||||
|
||||
glm::vec3 position;
|
||||
float radius;
|
||||
uint32_t rgba;
|
||||
};
|
||||
|
||||
static glm::vec3 zSortAxis;
|
||||
static bool zSort(const ParticleDetails& rhs, const ParticleDetails& lhs) {
|
||||
return glm::dot(rhs.position, ::zSortAxis) > glm::dot(lhs.position, ::zSortAxis);
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::updateRenderItem() {
|
||||
if (!_scene) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make a copy of each particle's details
|
||||
std::vector<ParticleDetails> particleDetails;
|
||||
particleDetails.reserve(getLivingParticleCount());
|
||||
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
|
||||
auto xcolor = _particleColors[i];
|
||||
auto alpha = (uint8_t)(glm::clamp(_particleAlphas[i] * getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f);
|
||||
auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha);
|
||||
particleDetails.push_back(ParticleDetails(_particlePositions[i], _particleRadiuses[i], rgba));
|
||||
if (!getVisible()) {
|
||||
render::PendingChanges pendingChanges;
|
||||
pendingChanges.updateItem<ParticlePayloadData>(_renderItemId, [](ParticlePayloadData& payload) {
|
||||
payload.setVisibleFlag(false);
|
||||
});
|
||||
|
||||
_scene->enqueuePendingChanges(pendingChanges);
|
||||
return;
|
||||
}
|
||||
|
||||
// sort particles back to front
|
||||
// NOTE: this is view frustum might be one frame out of date.
|
||||
|
||||
auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum();
|
||||
using ParticleUniforms = ParticlePayloadData::ParticleUniforms;
|
||||
using ParticlePrimitive = ParticlePayloadData::ParticlePrimitive;
|
||||
using ParticlePrimitives = ParticlePayloadData::ParticlePrimitives;
|
||||
|
||||
// No need to sort if we're doing additive blending
|
||||
if (_additiveBlending != true) {
|
||||
::zSortAxis = frustum->getDirection();
|
||||
qSort(particleDetails.begin(), particleDetails.end(), zSort);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// allocate vertices
|
||||
_vertices.clear();
|
||||
|
||||
// build vertices from particle positions and radiuses
|
||||
glm::vec3 dir = frustum->getDirection();
|
||||
for (auto&& particle : particleDetails) {
|
||||
glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir));
|
||||
glm::vec3 up = glm::normalize(glm::cross(right, dir));
|
||||
|
||||
glm::vec3 upOffset = up * particle.radius;
|
||||
glm::vec3 rightOffset = right * particle.radius;
|
||||
// generate corners of quad aligned to face the camera.
|
||||
_vertices.emplace_back(particle.position + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), particle.rgba);
|
||||
_vertices.emplace_back(particle.position - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), particle.rgba);
|
||||
_vertices.emplace_back(particle.position - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), particle.rgba);
|
||||
_vertices.emplace_back(particle.position + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), particle.rgba);
|
||||
// Fill in Uniforms structure
|
||||
ParticleUniforms particleUniforms;
|
||||
particleUniforms.radius.start = getRadiusStart();
|
||||
particleUniforms.radius.middle = getParticleRadius();
|
||||
particleUniforms.radius.finish = getRadiusFinish();
|
||||
particleUniforms.radius.spread = getRadiusSpread();
|
||||
particleUniforms.color.start = toGlm(getColorStart(), getAlphaStart());
|
||||
particleUniforms.color.middle = toGlm(getXColor(), getAlpha());
|
||||
particleUniforms.color.finish = toGlm(getColorFinish(), getAlphaFinish());
|
||||
particleUniforms.color.spread = toGlm(getColorSpread(), getAlphaSpread());
|
||||
particleUniforms.lifespan = getLifespan();
|
||||
|
||||
// Build particle primitives
|
||||
auto particlePrimitives = std::make_shared<ParticlePrimitives>();
|
||||
particlePrimitives->reserve(_particles.size()); // Reserve space
|
||||
for (auto& particle : _particles) {
|
||||
particlePrimitives->emplace_back(particle.position, glm::vec2(particle.lifetime, particle.seed));
|
||||
}
|
||||
|
||||
auto bounds = getAABox();
|
||||
auto position = getPosition();
|
||||
auto rotation = getRotation();
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
pendingChanges.updateItem<ParticlePayload>(_renderItemId, [this](ParticlePayload& payload) {
|
||||
// update vertex buffer
|
||||
auto vertexBuffer = payload.getVertexBuffer();
|
||||
size_t numBytes = sizeof(Vertex) * _vertices.size();
|
||||
|
||||
pendingChanges.updateItem<ParticlePayloadData>(_renderItemId, [=](ParticlePayloadData& payload) {
|
||||
payload.setVisibleFlag(true);
|
||||
|
||||
// Update particle uniforms
|
||||
memcpy(&payload.editParticleUniforms(), &particleUniforms, sizeof(ParticleUniforms));
|
||||
|
||||
// Update particle buffer
|
||||
auto particleBuffer = payload.getParticleBuffer();
|
||||
size_t numBytes = sizeof(ParticlePrimitive) * particlePrimitives->size();
|
||||
particleBuffer->resize(numBytes);
|
||||
if (numBytes == 0) {
|
||||
vertexBuffer->resize(0);
|
||||
auto indexBuffer = payload.getIndexBuffer();
|
||||
indexBuffer->resize(0);
|
||||
return;
|
||||
}
|
||||
memcpy(particleBuffer->editData(), particlePrimitives->data(), numBytes);
|
||||
|
||||
vertexBuffer->resize(numBytes);
|
||||
gpu::Byte* data = vertexBuffer->editData();
|
||||
memcpy(data, &(_vertices[0]), numBytes);
|
||||
// Update transform and bounds
|
||||
payload.setModelTransform(transform);
|
||||
payload.setBound(bounds);
|
||||
|
||||
// FIXME, don't update index buffer if num particles has not changed.
|
||||
// update index buffer
|
||||
auto indexBuffer = payload.getIndexBuffer();
|
||||
const size_t NUM_VERTS_PER_PARTICLE = 4;
|
||||
const size_t NUM_INDICES_PER_PARTICLE = 6;
|
||||
auto numQuads = (_vertices.size() / NUM_VERTS_PER_PARTICLE);
|
||||
numBytes = sizeof(uint16_t) * numQuads * NUM_INDICES_PER_PARTICLE;
|
||||
indexBuffer->resize(numBytes);
|
||||
data = indexBuffer->editData();
|
||||
auto indexPtr = reinterpret_cast<uint16_t*>(data);
|
||||
for (size_t i = 0; i < numQuads; ++i) {
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 0] = i * NUM_VERTS_PER_PARTICLE + 0;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 1] = i * NUM_VERTS_PER_PARTICLE + 1;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 2] = i * NUM_VERTS_PER_PARTICLE + 3;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 3] = i * NUM_VERTS_PER_PARTICLE + 1;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 4] = i * NUM_VERTS_PER_PARTICLE + 2;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 5] = i * NUM_VERTS_PER_PARTICLE + 3;
|
||||
}
|
||||
|
||||
// update transform
|
||||
glm::quat rot = getRotation();
|
||||
glm::vec3 pos = getPosition();
|
||||
Transform t;
|
||||
t.setRotation(rot);
|
||||
payload.setModelTransform(t);
|
||||
|
||||
// transform _particleMinBound and _particleMaxBound corners into world coords
|
||||
glm::vec3 d = _particleMaxBound - _particleMinBound;
|
||||
const size_t NUM_BOX_CORNERS = 8;
|
||||
glm::vec3 corners[NUM_BOX_CORNERS] = {
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, d.z)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, d.z)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, d.z)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, d.z))
|
||||
};
|
||||
glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
glm::vec3 max = -min;
|
||||
for (size_t i = 0; i < NUM_BOX_CORNERS; i++) {
|
||||
min.x = std::min(min.x, corners[i].x);
|
||||
min.y = std::min(min.y, corners[i].y);
|
||||
min.z = std::min(min.z, corners[i].z);
|
||||
max.x = std::max(max.x, corners[i].x);
|
||||
max.y = std::max(max.y, corners[i].y);
|
||||
max.z = std::max(max.z, corners[i].z);
|
||||
}
|
||||
AABox bound(min, max - min);
|
||||
payload.setBound(bound);
|
||||
|
||||
bool textured = _texture && _texture->isLoaded();
|
||||
if (textured) {
|
||||
if (_texture && _texture->isLoaded()) {
|
||||
payload.setTexture(_texture->getGPUTexture());
|
||||
payload.setPipeline(_texturedPipeline);
|
||||
} else {
|
||||
|
@ -321,46 +276,29 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
|
|||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::createPipelines() {
|
||||
bool writeToDepthBuffer = false;
|
||||
gpu::State::BlendArg destinationColorBlendArg;
|
||||
if (_additiveBlending) {
|
||||
destinationColorBlendArg = gpu::State::ONE;
|
||||
}
|
||||
else {
|
||||
destinationColorBlendArg = gpu::State::INV_SRC_ALPHA;
|
||||
writeToDepthBuffer = true;
|
||||
}
|
||||
if (!_untexturedPipeline) {
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD,
|
||||
destinationColorBlendArg, gpu::State::FACTOR_ALPHA,
|
||||
gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert));
|
||||
auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag));
|
||||
|
||||
auto program = gpu::Shader::createProgram(vertShader, fragShader);
|
||||
_untexturedPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
if (!_texturedPipeline) {
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
bool writeToDepthBuffer = !_additiveBlending;
|
||||
state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD,
|
||||
destinationColorBlendArg, gpu::State::FACTOR_ALPHA,
|
||||
gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
|
||||
gpu::ShaderPointer fragShader;
|
||||
if (_additiveBlending) {
|
||||
fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
|
||||
}
|
||||
else {
|
||||
//If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts
|
||||
fragShader = gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag));
|
||||
}
|
||||
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
|
||||
|
||||
auto program = gpu::Shader::createProgram(vertShader, fragShader);
|
||||
_texturedPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "RenderableEntityItem.h"
|
||||
|
||||
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
||||
friend class ParticlePayload;
|
||||
friend class ParticlePayloadData;
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID);
|
||||
|
@ -29,23 +29,14 @@ public:
|
|||
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override;
|
||||
|
||||
protected:
|
||||
render::ItemID _renderItemId;
|
||||
|
||||
struct Vertex {
|
||||
Vertex(glm::vec3 xyzIn, glm::vec2 uvIn, uint32_t rgbaIn) : xyz(xyzIn), uv(uvIn), rgba(rgbaIn) {}
|
||||
glm::vec3 xyz;
|
||||
glm::vec2 uv;
|
||||
uint32_t rgba;
|
||||
};
|
||||
|
||||
void createPipelines();
|
||||
|
||||
std::vector<Vertex> _vertices;
|
||||
|
||||
render::ScenePointer _scene;
|
||||
render::ItemID _renderItemId;
|
||||
|
||||
NetworkTexturePointer _texture;
|
||||
gpu::PipelinePointer _untexturedPipeline;
|
||||
gpu::PipelinePointer _texturedPipeline;
|
||||
|
||||
render::ScenePointer _scene;
|
||||
NetworkTexturePointer _texture;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,11 @@
|
|||
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 varColor;
|
||||
in vec2 varTexcoord;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec4 color = texture(colorMap, _texCoord0);
|
||||
outFragColor = color * _color;
|
||||
outFragColor = texture(colorMap, varTexcoord.xy) * varColor;
|
||||
}
|
||||
|
|
|
@ -10,21 +10,84 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Inputs.slh@>
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
out vec4 _color;
|
||||
out vec2 _texCoord0;
|
||||
struct Radii {
|
||||
float start;
|
||||
float middle;
|
||||
float finish;
|
||||
float spread;
|
||||
};
|
||||
struct Colors {
|
||||
vec4 start;
|
||||
vec4 middle;
|
||||
vec4 finish;
|
||||
vec4 spread;
|
||||
};
|
||||
|
||||
struct ParticleUniforms {
|
||||
Radii radius;
|
||||
Colors color;
|
||||
float lifespan;
|
||||
};
|
||||
|
||||
uniform particleBuffer {
|
||||
ParticleUniforms particle;
|
||||
};
|
||||
|
||||
in vec3 inPosition;
|
||||
in vec2 inColor; // This is actual Lifetime + Seed
|
||||
|
||||
out vec4 varColor;
|
||||
out vec2 varTexcoord;
|
||||
|
||||
const int NUM_VERTICES_PER_PARTICLE = 4;
|
||||
const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE](
|
||||
vec4(-1.0, -1.0, 0.0, 0.0),
|
||||
vec4(1.0, -1.0, 0.0, 0.0),
|
||||
vec4(-1.0, 1.0, 0.0, 0.0),
|
||||
vec4(1.0, 1.0, 0.0, 0.0)
|
||||
);
|
||||
|
||||
float bezierInterpolate(float y1, float y2, float y3, float u) {
|
||||
// https://en.wikipedia.org/wiki/Bezier_curve
|
||||
return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3;
|
||||
}
|
||||
|
||||
vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) {
|
||||
return vec4(bezierInterpolate(y1.x, y2.x, y3.x, u),
|
||||
bezierInterpolate(y1.y, y2.y, y3.y, u),
|
||||
bezierInterpolate(y1.z, y2.z, y3.z, u),
|
||||
bezierInterpolate(y1.w, y2.w, y3.w, u));
|
||||
}
|
||||
|
||||
|
||||
void main(void) {
|
||||
// pass along the color & uvs to fragment shader
|
||||
_color = inColor;
|
||||
_texCoord0 = inTexCoord0.xy;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||
|
||||
// Which icon are we dealing with ?
|
||||
int particleID = gl_VertexID / NUM_VERTICES_PER_PARTICLE;
|
||||
// Which quad vertex pos?
|
||||
int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE;
|
||||
|
||||
// Particle properties
|
||||
float age = inColor.x / particle.lifespan;
|
||||
float seed = inColor.y;
|
||||
|
||||
// Pass the texcoord and the z texcoord is representing the texture icon
|
||||
varTexcoord = vec2((UNIT_QUAD[twoTriID].xy + 1.0) * 0.5);
|
||||
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
|
||||
|
||||
// anchor point in eye space
|
||||
float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish , age);
|
||||
vec4 quadPos = radius * UNIT_QUAD[twoTriID];
|
||||
|
||||
vec4 anchorPoint;
|
||||
<$transformModelToEyePos(cam, obj, inPosition, anchorPoint)$>
|
||||
|
||||
vec4 eyePos = anchorPoint + quadPos;
|
||||
<$transformEyeToClipPos(cam, eyePos, gl_Position)$>
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// fragment shader
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec4 color = texture(colorMap, _texCoord0);
|
||||
if (color.a < 0.1) {
|
||||
discard;
|
||||
}
|
||||
outFragColor = color * _color;
|
||||
}
|
|
@ -29,7 +29,6 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire
|
|||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame, _animationLoop->getFirstFrame);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame, _animationLoop->getLastFrame);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold, _animationLoop->getHold);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically, _animationLoop->getStartAutomatically);
|
||||
} else {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
|
||||
|
@ -38,7 +37,6 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire
|
|||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +56,6 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo
|
|||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, _animationLoop->setFirstFrame);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, _animationLoop->setLastFrame);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, _animationLoop->setHold);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, _animationLoop->setStartAutomatically);
|
||||
|
||||
// legacy property support
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, _animationLoop->setFPS, _animationLoop->getFPS);
|
||||
|
@ -73,7 +70,6 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo
|
|||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, setStartAutomatically);
|
||||
|
||||
// legacy property support
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS);
|
||||
|
@ -95,7 +91,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
float lastFrame = _animationLoop ? _animationLoop->getLastFrame() : getLastFrame();
|
||||
bool loop = _animationLoop ? _animationLoop->getLoop() : getLoop();
|
||||
bool hold = _animationLoop ? _animationLoop->getHold() : getHold();
|
||||
bool startAutomatically = _animationLoop ? _animationLoop->getStartAutomatically() : getStartAutomatically();
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
|
@ -130,10 +125,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
running = settingsMap["hold"].toBool();
|
||||
}
|
||||
|
||||
if (settingsMap.contains("startAutomatically")) {
|
||||
running = settingsMap["startAutomatically"].toBool();
|
||||
}
|
||||
|
||||
if (_animationLoop) {
|
||||
_animationLoop->setFPS(fps);
|
||||
_animationLoop->setCurrentFrame(currentFrame);
|
||||
|
@ -142,7 +133,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
_animationLoop->setLastFrame(lastFrame);
|
||||
_animationLoop->setLoop(loop);
|
||||
_animationLoop->setHold(hold);
|
||||
_animationLoop->setStartAutomatically(startAutomatically);
|
||||
} else {
|
||||
setFPS(fps);
|
||||
setCurrentFrame(currentFrame);
|
||||
|
@ -151,7 +141,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
setLastFrame(lastFrame);
|
||||
setLoop(loop);
|
||||
setHold(hold);
|
||||
setStartAutomatically(startAutomatically);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +183,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically());
|
||||
} else {
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
|
||||
|
@ -203,7 +191,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -226,7 +213,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
|
|||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
|
||||
|
@ -235,7 +221,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
|
|||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically);
|
||||
}
|
||||
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL);
|
||||
|
@ -246,7 +231,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
|
|||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically);
|
||||
|
||||
processedBytes += bytesRead;
|
||||
|
||||
|
@ -273,7 +257,6 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_START_AUTOMATICALLY, startAutomatically);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
@ -288,7 +271,6 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con
|
|||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, _animationLoop->getFirstFrame);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, _animationLoop->getLastFrame);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, _animationLoop->getHold);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, _animationLoop->getStartAutomatically);
|
||||
} else {
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame);
|
||||
|
@ -297,7 +279,6 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con
|
|||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, getStartAutomatically);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +294,6 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie
|
|||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, _animationLoop->setFirstFrame);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, _animationLoop->setLastFrame);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, _animationLoop->setHold);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, _animationLoop->setStartAutomatically);
|
||||
} else {
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame);
|
||||
|
@ -322,7 +302,6 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie
|
|||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, setStartAutomatically);
|
||||
}
|
||||
|
||||
return somethingChanged;
|
||||
|
@ -339,7 +318,6 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP
|
|||
requestedProperties += PROP_ANIMATION_FIRST_FRAME;
|
||||
requestedProperties += PROP_ANIMATION_LAST_FRAME;
|
||||
requestedProperties += PROP_ANIMATION_HOLD;
|
||||
requestedProperties += PROP_ANIMATION_START_AUTOMATICALLY;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -363,7 +341,6 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically());
|
||||
} else {
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
|
||||
|
@ -372,7 +349,6 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,7 +371,6 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
|
|||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
|
||||
|
@ -404,7 +379,6 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
|
|||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically);
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
|
|
|
@ -80,7 +80,6 @@ public:
|
|||
DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, AnimationLoop::MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically, startAutomatically, bool, false); // was animationSettings.startAutomatically
|
||||
|
||||
protected:
|
||||
void setFromOldAnimationSettings(const QString& value);
|
||||
|
|
|
@ -36,7 +36,7 @@ int EntityItem::_maxActionsDataSize = 800;
|
|||
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||
SpatiallyNestable(NestableTypes::Entity, entityItemID),
|
||||
SpatiallyNestable(NestableType::Entity, entityItemID),
|
||||
_type(EntityTypes::Unknown),
|
||||
_lastSimulated(0),
|
||||
_lastUpdated(0),
|
||||
|
@ -628,71 +628,50 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
||||
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) {
|
||||
// pack SimulationOwner and terse update properties near each other
|
||||
// pack SimulationOwner and terse update properties near each other
|
||||
|
||||
// NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
|
||||
// even when we would otherwise ignore the rest of the packet.
|
||||
// NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
|
||||
// even when we would otherwise ignore the rest of the packet.
|
||||
|
||||
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
|
||||
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
|
||||
|
||||
QByteArray simOwnerData;
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
|
||||
SimulationOwner newSimOwner;
|
||||
newSimOwner.fromByteArray(simOwnerData);
|
||||
dataAt += bytes;
|
||||
bytesRead += bytes;
|
||||
QByteArray simOwnerData;
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
|
||||
SimulationOwner newSimOwner;
|
||||
newSimOwner.fromByteArray(simOwnerData);
|
||||
dataAt += bytes;
|
||||
bytesRead += bytes;
|
||||
|
||||
if (wantTerseEditLogging() && _simulationOwner != newSimOwner) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner;
|
||||
}
|
||||
if (_simulationOwner.set(newSimOwner)) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
if (wantTerseEditLogging() && _simulationOwner != newSimOwner) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner;
|
||||
}
|
||||
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
|
||||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
overwriteLocalData = oldOverwrite;
|
||||
if (_simulationOwner.set(newSimOwner)) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
} else {
|
||||
// legacy order of packing here
|
||||
// TODO: purge this logic in a few months from now (2015.07)
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
}
|
||||
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
|
||||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
overwriteLocalData = oldOverwrite;
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
|
||||
|
@ -701,17 +680,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
||||
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) {
|
||||
// this code for when there is only simulatorID and no simulation priority
|
||||
|
||||
// we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true
|
||||
// before we try to READ_ENTITY_PROPERTY it
|
||||
bool temp = overwriteLocalData;
|
||||
overwriteLocalData = true;
|
||||
READ_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, QUuid, updateSimulatorID);
|
||||
overwriteLocalData = temp;
|
||||
}
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
}
|
||||
|
@ -1104,7 +1072,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChanged = false;
|
||||
|
||||
// these affect TerseUpdate properties
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, updateSimulationOwner);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity);
|
||||
|
@ -1192,7 +1160,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;
|
||||
}
|
||||
|
@ -1324,6 +1292,13 @@ void EntityItem::updatePosition(const glm::vec3& value) {
|
|||
}
|
||||
if (getLocalPosition() != value) {
|
||||
setLocalPosition(value);
|
||||
_dirtyFlags |= Simulation::DIRTY_POSITION;
|
||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||
if (object->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||
entity->_dirtyFlags |= Simulation::DIRTY_POSITION;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1342,7 +1317,7 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
|
|||
setLocalOrientation(rotation);
|
||||
_dirtyFlags |= Simulation::DIRTY_ROTATION;
|
||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||
if (object->getNestableType() == NestableTypes::Entity) {
|
||||
if (object->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||
entity->_dirtyFlags |= Simulation::DIRTY_ROTATION;
|
||||
entity->_dirtyFlags |= Simulation::DIRTY_POSITION;
|
||||
|
@ -1496,12 +1471,12 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
|
|||
_simulationOwner.set(owner);
|
||||
}
|
||||
|
||||
void EntityItem::updateSimulatorID(const QUuid& value) {
|
||||
if (wantTerseEditLogging() && _simulationOwner.getID() != value) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value;
|
||||
void EntityItem::updateSimulationOwner(const SimulationOwner& owner) {
|
||||
if (wantTerseEditLogging() && _simulationOwner != owner) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner;
|
||||
}
|
||||
|
||||
if (_simulationOwner.setID(value)) {
|
||||
if (_simulationOwner.set(owner)) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
@ -288,7 +288,7 @@ public:
|
|||
|
||||
quint8 getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
||||
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
|
||||
void updateSimulatorID(const QUuid& value);
|
||||
void updateSimulationOwner(const SimulationOwner& owner);
|
||||
void clearSimulationOwnership();
|
||||
|
||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||
|
@ -379,8 +379,10 @@ 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); }
|
||||
|
||||
virtual void loader() {} // called indirectly when urls for geometry are updated
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -747,7 +747,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically);
|
||||
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_CENTER, Atmosphere, atmosphere, Center, center);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_INNER_RADIUS, Atmosphere, atmosphere, InnerRadius, innerRadius);
|
||||
|
@ -1780,6 +1779,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 +1795,6 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
return out;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::parentDependentPropertyChanged() {
|
||||
bool EntityItemProperties::parentDependentPropertyChanged() const {
|
||||
return localPositionChanged() || positionChanged() || localRotationChanged() || rotationChanged();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -801,19 +809,19 @@ glm::vec3 EntityScriptingInterface::localCoordsToVoxelCoords(const QUuid& entity
|
|||
}
|
||||
}
|
||||
|
||||
glm::vec3 EntityScriptingInterface::getJointTranslation(const QUuid& entityID, int jointIndex) {
|
||||
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->getJointTranslation(jointIndex);
|
||||
return modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex);
|
||||
} else {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
glm::quat EntityScriptingInterface::getJointRotation(const QUuid& entityID, int jointIndex) {
|
||||
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->getJointRotation(jointIndex);
|
||||
return modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex);
|
||||
} else {
|
||||
return glm::quat();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -148,9 +148,9 @@ public slots:
|
|||
Q_INVOKABLE glm::vec3 worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords);
|
||||
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 getJointTranslation(const QUuid& entityID, int jointIndex);
|
||||
Q_INVOKABLE glm::quat getJointRotation(const QUuid& entityID, int jointIndex);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -218,7 +219,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
|
|||
// if the entity has children, run UpdateEntityOperator on them. If the children have children, recurse
|
||||
QQueue<SpatiallyNestablePointer> toProcess;
|
||||
foreach (SpatiallyNestablePointer child, entity->getChildren()) {
|
||||
if (child && child->getNestableType() == NestableTypes::Entity) {
|
||||
if (child && child->getNestableType() == NestableType::Entity) {
|
||||
toProcess.enqueue(child);
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +232,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
|
|||
childEntity, newChildBBRelProperties);
|
||||
recurseTreeWithOperator(&theChildOperator);
|
||||
foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) {
|
||||
if (childChild && childChild->getNestableType() == NestableTypes::Entity) {
|
||||
if (childChild && childChild->getNestableType() == NestableType::Entity) {
|
||||
toProcess.enqueue(childChild);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -1252,3 +1266,12 @@ void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytes
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EntityTree::callLoader(EntityItemID entityID) {
|
||||
// this is used to bounce from the networking thread to the main thread
|
||||
EntityItemPointer entity = findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
entity->loader();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -233,6 +235,9 @@ public:
|
|||
return _deletedEntityItemIDs.contains(id);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void callLoader(EntityItemID entityID);
|
||||
|
||||
signals:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -384,11 +384,6 @@ void ModelEntityItem::setAnimationSettings(const QString& value) {
|
|||
setAnimationHold(hold);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("startAutomatically")) {
|
||||
bool startAutomatically = settingsMap["startAutomatically"].toBool();
|
||||
setAnimationStartAutomatically(startAutomatically);
|
||||
}
|
||||
|
||||
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,11 +74,10 @@ public:
|
|||
_color[GREEN_INDEX] = value.green;
|
||||
_color[BLUE_INDEX] = value.blue;
|
||||
}
|
||||
|
||||
// model related properties
|
||||
void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
|
||||
virtual void setCompoundShapeURL(const QString& url);
|
||||
|
||||
// model related properties
|
||||
virtual void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
|
||||
virtual void setCompoundShapeURL(const QString& url);
|
||||
|
||||
// Animation related items...
|
||||
const AnimationPropertyGroup& getAnimationProperties() const { return _animationProperties; }
|
||||
|
@ -97,9 +96,6 @@ public:
|
|||
void setAnimationHold(bool hold) { _animationLoop.setHold(hold); }
|
||||
bool getAnimationHold() const { return _animationLoop.getHold(); }
|
||||
|
||||
void setAnimationStartAutomatically(bool startAutomatically) { _animationLoop.setStartAutomatically(startAutomatically); }
|
||||
bool getAnimationStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
|
||||
|
||||
void setAnimationFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
|
||||
float getAnimationFirstFrame() const { return _animationLoop.getFirstFrame(); }
|
||||
|
||||
|
|
|
@ -40,9 +40,6 @@
|
|||
#include "EntityScriptingInterface.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
const glm::vec3 X_AXIS = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 Z_AXIS = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work
|
||||
|
||||
const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 };
|
||||
|
@ -66,8 +63,8 @@ const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f;
|
|||
const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f;
|
||||
const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3
|
||||
const float ParticleEffectEntityItem::DEFAULT_SPEED_SPREAD = 1.0f;
|
||||
const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, X_AXIS); // Vertical
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = glm::vec3(0.0f, 0.0f, 0.0f); // Emit from point
|
||||
const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point
|
||||
const float ParticleEffectEntityItem::MINIMUM_EMIT_DIMENSION = 0.0f;
|
||||
const float ParticleEffectEntityItem::MAXIMUM_EMIT_DIMENSION = (float)TREE_SCALE;
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0)
|
||||
|
@ -106,36 +103,12 @@ EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID
|
|||
// our non-pure virtual subclass for now...
|
||||
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) :
|
||||
EntityItem(entityItemID),
|
||||
_lastSimulated(usecTimestampNow()),
|
||||
_particleLifetimes(DEFAULT_MAX_PARTICLES, 0.0f),
|
||||
_particlePositions(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_particleVelocities(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_particleAccelerations(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_particleRadiuses(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
|
||||
_radiusStarts(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
|
||||
_radiusMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
|
||||
_radiusFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
|
||||
_particleColors(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
|
||||
_colorStarts(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
|
||||
_colorMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
|
||||
_colorFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
|
||||
_particleAlphas(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
|
||||
_alphaStarts(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
|
||||
_alphaMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
|
||||
_alphaFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
|
||||
_particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)),
|
||||
_particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) ,
|
||||
_additiveBlending(DEFAULT_ADDITIVE_BLENDING)
|
||||
_lastSimulated(usecTimestampNow())
|
||||
{
|
||||
|
||||
_type = EntityTypes::ParticleEffect;
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
|
||||
ParticleEffectEntityItem::~ParticleEffectEntityItem() {
|
||||
}
|
||||
|
||||
|
||||
void ParticleEffectEntityItem::setAlpha(float alpha) {
|
||||
if (MINIMUM_ALPHA <= alpha && alpha <= MAXIMUM_ALPHA) {
|
||||
_alpha = alpha;
|
||||
|
@ -310,8 +283,8 @@ void ParticleEffectEntityItem::setRadiusSpread(float radiusSpread) {
|
|||
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
||||
const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error
|
||||
|
||||
glm::vec3 velocity = _emitSpeed * (_emitOrientation * Z_AXIS);
|
||||
glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Z_AXIS);
|
||||
glm::vec3 velocity = _emitSpeed * (_emitOrientation * Vectors::UNIT_Z);
|
||||
glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Vectors::UNIT_Z);
|
||||
|
||||
glm::vec3 maxVelocity = glm::abs(velocity) + velocitySpread;
|
||||
glm::vec3 maxAccleration = glm::abs(_acceleration) + _accelerationSpread;
|
||||
|
@ -578,7 +551,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
|
|||
|
||||
bool ParticleEffectEntityItem::isEmittingParticles() const {
|
||||
// keep emitting if there are particles still alive.
|
||||
return (getIsEmitting() || getLivingParticleCount() > 0);
|
||||
return (getIsEmitting() || !_particles.empty());
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::needsToCallUpdate() const {
|
||||
|
@ -613,62 +586,25 @@ void ParticleEffectEntityItem::updateShapeType(ShapeType type) {
|
|||
}
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::updateRadius(quint32 index, float age) {
|
||||
_particleRadiuses[index] = Interpolate::interpolate3Points(_radiusStarts[index], _radiusMiddles[index],
|
||||
_radiusFinishes[index], age);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::updateColor(quint32 index, float age) {
|
||||
_particleColors[index].red = (int)Interpolate::interpolate3Points(_colorStarts[index].red, _colorMiddles[index].red,
|
||||
_colorFinishes[index].red, age);
|
||||
_particleColors[index].green = (int)Interpolate::interpolate3Points(_colorStarts[index].green, _colorMiddles[index].green,
|
||||
_colorFinishes[index].green, age);
|
||||
_particleColors[index].blue = (int)Interpolate::interpolate3Points(_colorStarts[index].blue, _colorMiddles[index].blue,
|
||||
_colorFinishes[index].blue, age);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::updateAlpha(quint32 index, float age) {
|
||||
_particleAlphas[index] = Interpolate::interpolate3Points(_alphaStarts[index], _alphaMiddles[index],
|
||||
_alphaFinishes[index], age);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) {
|
||||
_particleMinBound.x = glm::min(_particleMinBound.x, point.x);
|
||||
_particleMinBound.y = glm::min(_particleMinBound.y, point.y);
|
||||
_particleMinBound.z = glm::min(_particleMinBound.z, point.z);
|
||||
_particleMaxBound.x = glm::max(_particleMaxBound.x, point.x);
|
||||
_particleMaxBound.y = glm::max(_particleMaxBound.y, point.y);
|
||||
_particleMaxBound.z = glm::max(_particleMaxBound.z, point.z);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) {
|
||||
glm::vec3 accel = _particleAccelerations[index];
|
||||
glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * accel;
|
||||
glm::vec3 at = accel * deltaTime;
|
||||
_particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared;
|
||||
_particleVelocities[index] += at;
|
||||
void ParticleEffectEntityItem::integrateParticle(Particle& particle, float deltaTime) {
|
||||
glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * particle.acceleration;
|
||||
glm::vec3 at = particle.acceleration * deltaTime;
|
||||
particle.position += particle.velocity * deltaTime + atSquared;
|
||||
particle.velocity += at;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
||||
|
||||
_particleMinBound = glm::vec3(-1.0f, -1.0f, -1.0f);
|
||||
_particleMaxBound = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
// update particles between head and tail
|
||||
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
|
||||
_particleLifetimes[i] += deltaTime;
|
||||
for (Particle& particle : _particles) {
|
||||
particle.lifetime += deltaTime;
|
||||
|
||||
// if particle has died.
|
||||
if (_particleLifetimes[i] >= _lifespan || _lifespan < EPSILON) {
|
||||
if (particle.lifetime >= _lifespan) {
|
||||
// move head forward
|
||||
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
|
||||
_particles.pop_front();
|
||||
} else {
|
||||
float age = _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0
|
||||
updateRadius(i, age);
|
||||
updateColor(i, age);
|
||||
updateAlpha(i, age);
|
||||
integrateParticle(i, deltaTime);
|
||||
extendBounds(_particlePositions[i]);
|
||||
// Otherwise update it
|
||||
integrateParticle(particle, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -677,192 +613,104 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
|||
|
||||
float timeLeftInFrame = deltaTime;
|
||||
while (_timeUntilNextEmit < timeLeftInFrame) {
|
||||
|
||||
timeLeftInFrame -= _timeUntilNextEmit;
|
||||
_timeUntilNextEmit = 1.0f / _emitRate;
|
||||
|
||||
// emit a new particle at tail index.
|
||||
quint32 i = _particleTailIndex;
|
||||
_particleLifetimes[i] = 0.0f;
|
||||
|
||||
// Radius
|
||||
if (_radiusSpread == 0.0f) {
|
||||
_radiusStarts[i] = getRadiusStart();
|
||||
_radiusMiddles[i] =_particleRadius;
|
||||
_radiusFinishes[i] = getRadiusFinish();
|
||||
} else {
|
||||
float spreadMultiplier;
|
||||
if (_particleRadius > 0.0f) {
|
||||
spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _radiusSpread / _particleRadius;
|
||||
} else {
|
||||
spreadMultiplier = 1.0f;
|
||||
}
|
||||
_radiusStarts[i] =
|
||||
glm::clamp(spreadMultiplier * getRadiusStart(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS);
|
||||
_radiusMiddles[i] =
|
||||
glm::clamp(spreadMultiplier * _particleRadius, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS);
|
||||
_radiusFinishes[i] =
|
||||
glm::clamp(spreadMultiplier * getRadiusFinish(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS);
|
||||
}
|
||||
updateRadius(i, 0.0f);
|
||||
|
||||
// Position, velocity, and acceleration
|
||||
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
|
||||
// Emit along z-axis from position
|
||||
_particlePositions[i] = getPosition();
|
||||
_particleVelocities[i] =
|
||||
(_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Z_AXIS);
|
||||
_particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
|
||||
|
||||
} else {
|
||||
// Emit around point or from ellipsoid
|
||||
// - Distribute directions evenly around point
|
||||
// - Distribute points relatively evenly over ellipsoid surface
|
||||
// - Distribute points relatively evenly within ellipsoid volume
|
||||
|
||||
float elevationMinZ = sin(PI_OVER_TWO - _polarFinish);
|
||||
float elevationMaxZ = sin(PI_OVER_TWO - _polarStart);
|
||||
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
|
||||
|
||||
float azimuth;
|
||||
if (_azimuthFinish >= _azimuthStart) {
|
||||
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
|
||||
} else {
|
||||
azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat();
|
||||
}
|
||||
|
||||
glm::vec3 emitDirection;
|
||||
|
||||
if (_emitDimensions == glm::vec3()) {
|
||||
// Point
|
||||
emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Z_AXIS;
|
||||
|
||||
_particlePositions[i] = getPosition();
|
||||
} else {
|
||||
// Ellipsoid
|
||||
float radiusScale = 1.0f;
|
||||
if (_emitRadiusStart < 1.0f) {
|
||||
float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center
|
||||
float randRadius =
|
||||
emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
|
||||
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
|
||||
}
|
||||
|
||||
glm::vec3 radiuses = radiusScale * 0.5f * _emitDimensions;
|
||||
float x = radiuses.x * glm::cos(elevation) * glm::cos(azimuth);
|
||||
float y = radiuses.y * glm::cos(elevation) * glm::sin(azimuth);
|
||||
float z = radiuses.z * glm::sin(elevation);
|
||||
glm::vec3 emitPosition = glm::vec3(x, y, z);
|
||||
emitDirection = glm::normalize(glm::vec3(
|
||||
radiuses.x > 0.0f ? x / (radiuses.x * radiuses.x) : 0.0f,
|
||||
radiuses.y > 0.0f ? y / (radiuses.y * radiuses.y) : 0.0f,
|
||||
radiuses.z > 0.0f ? z / (radiuses.z * radiuses.z) : 0.0f
|
||||
));
|
||||
|
||||
_particlePositions[i] = getPosition() + _emitOrientation * emitPosition;
|
||||
}
|
||||
|
||||
_particleVelocities[i] =
|
||||
(_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection);
|
||||
_particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
|
||||
}
|
||||
integrateParticle(i, timeLeftInFrame);
|
||||
extendBounds(_particlePositions[i]);
|
||||
|
||||
// Color
|
||||
if (_colorSpread == xColor{ 0, 0, 0 }) {
|
||||
_colorStarts[i] = getColorStart();
|
||||
_colorMiddles[i] = getXColor();
|
||||
_colorFinishes[i] = getColorFinish();
|
||||
} else {
|
||||
xColor startColor = getColorStart();
|
||||
xColor middleColor = getXColor();
|
||||
xColor finishColor = getColorFinish();
|
||||
|
||||
float spread = randFloatInRange(-1.0f, 1.0f);
|
||||
float spreadMultiplierRed =
|
||||
middleColor.red > 0 ? 1.0f + spread * (float)_colorSpread.red / (float)middleColor.red : 1.0f;
|
||||
float spreadMultiplierGreen =
|
||||
middleColor.green > 0 ? 1.0f + spread * (float)_colorSpread.green / (float)middleColor.green : 1.0f;
|
||||
float spreadMultiplierBlue =
|
||||
middleColor.blue > 0 ? 1.0f + spread * (float)_colorSpread.blue / (float)middleColor.blue : 1.0f;
|
||||
|
||||
_colorStarts[i].red = (int)glm::clamp(spreadMultiplierRed * (float)startColor.red, 0.0f, 255.0f);
|
||||
_colorStarts[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)startColor.green, 0.0f, 255.0f);
|
||||
_colorStarts[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)startColor.blue, 0.0f, 255.0f);
|
||||
|
||||
_colorMiddles[i].red = (int)glm::clamp(spreadMultiplierRed * (float)middleColor.red, 0.0f, 255.0f);
|
||||
_colorMiddles[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)middleColor.green, 0.0f, 255.0f);
|
||||
_colorMiddles[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)middleColor.blue, 0.0f, 255.0f);
|
||||
|
||||
_colorFinishes[i].red = (int)glm::clamp(spreadMultiplierRed * (float)finishColor.red, 0.0f, 255.0f);
|
||||
_colorFinishes[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)finishColor.green, 0.0f, 255.0f);
|
||||
_colorFinishes[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)finishColor.blue, 0.0f, 255.0f);
|
||||
}
|
||||
updateColor(i, 0.0f);
|
||||
|
||||
// Alpha
|
||||
if (_alphaSpread == 0.0f) {
|
||||
_alphaStarts[i] = getAlphaStart();
|
||||
_alphaMiddles[i] = _alpha;
|
||||
_alphaFinishes[i] = getAlphaFinish();
|
||||
} else {
|
||||
float spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _alphaSpread / _alpha;
|
||||
_alphaStarts[i] = spreadMultiplier * getAlphaStart();
|
||||
_alphaMiddles[i] = spreadMultiplier * _alpha;
|
||||
_alphaFinishes[i] = spreadMultiplier * getAlphaFinish();
|
||||
}
|
||||
updateAlpha(i, 0.0f);
|
||||
|
||||
_particleTailIndex = (_particleTailIndex + 1) % _maxParticles;
|
||||
|
||||
// overflow! move head forward by one.
|
||||
// because the case of head == tail indicates an empty array, not a full one.
|
||||
// This can drop an existing older particle, but this is by design, newer particles are a higher priority.
|
||||
if (_particleTailIndex == _particleHeadIndex) {
|
||||
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
|
||||
if (_particles.size() >= _maxParticles) {
|
||||
_particles.pop_front();
|
||||
}
|
||||
|
||||
// emit a new particle at tail index.
|
||||
_particles.push_back(createParticle());
|
||||
auto particle = _particles.back();
|
||||
particle.lifetime += timeLeftInFrame;
|
||||
|
||||
// Initialize it
|
||||
integrateParticle(particle, deltaTime);
|
||||
|
||||
// Advance in frame
|
||||
timeLeftInFrame -= _timeUntilNextEmit;
|
||||
_timeUntilNextEmit = 1.0f / _emitRate;
|
||||
}
|
||||
|
||||
_timeUntilNextEmit -= timeLeftInFrame;
|
||||
}
|
||||
}
|
||||
|
||||
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
|
||||
Particle particle;
|
||||
|
||||
particle.seed = randFloatInRange(0.0f, 1.0f);
|
||||
|
||||
// Position, velocity, and acceleration
|
||||
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
|
||||
// Emit along z-axis from position
|
||||
particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z);
|
||||
particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
|
||||
|
||||
} else {
|
||||
// Emit around point or from ellipsoid
|
||||
// - Distribute directions evenly around point
|
||||
// - Distribute points relatively evenly over ellipsoid surface
|
||||
// - Distribute points relatively evenly within ellipsoid volume
|
||||
|
||||
float elevationMinZ = sin(PI_OVER_TWO - _polarFinish);
|
||||
float elevationMaxZ = sin(PI_OVER_TWO - _polarStart);
|
||||
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
|
||||
|
||||
float azimuth;
|
||||
if (_azimuthFinish >= _azimuthStart) {
|
||||
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
|
||||
} else {
|
||||
azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat();
|
||||
}
|
||||
|
||||
glm::vec3 emitDirection;
|
||||
|
||||
if (_emitDimensions == Vectors::ZERO) {
|
||||
// Point
|
||||
emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z;
|
||||
} else {
|
||||
// Ellipsoid
|
||||
float radiusScale = 1.0f;
|
||||
if (_emitRadiusStart < 1.0f) {
|
||||
float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center
|
||||
float randRadius =
|
||||
emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
|
||||
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
|
||||
}
|
||||
|
||||
glm::vec3 radii = radiusScale * 0.5f * _emitDimensions;
|
||||
float x = radii.x * glm::cos(elevation) * glm::cos(azimuth);
|
||||
float y = radii.y * glm::cos(elevation) * glm::sin(azimuth);
|
||||
float z = radii.z * glm::sin(elevation);
|
||||
glm::vec3 emitPosition = glm::vec3(x, y, z);
|
||||
emitDirection = glm::normalize(glm::vec3(
|
||||
radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f,
|
||||
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
|
||||
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
|
||||
));
|
||||
|
||||
particle.position = _emitOrientation * emitPosition;
|
||||
}
|
||||
|
||||
particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection);
|
||||
particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
|
||||
}
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) {
|
||||
if (_maxParticles != maxParticles && MINIMUM_MAX_PARTICLES <= maxParticles && maxParticles <= MAXIMUM_MAX_PARTICLES) {
|
||||
_maxParticles = maxParticles;
|
||||
|
||||
// TODO: try to do something smart here and preserve the state of existing particles.
|
||||
|
||||
// resize vectors
|
||||
_particleLifetimes.resize(_maxParticles);
|
||||
_particlePositions.resize(_maxParticles);
|
||||
_particleVelocities.resize(_maxParticles);
|
||||
_particleRadiuses.resize(_maxParticles);
|
||||
_radiusStarts.resize(_maxParticles);
|
||||
_radiusMiddles.resize(_maxParticles);
|
||||
_radiusFinishes.resize(_maxParticles);
|
||||
_particleColors.resize(_maxParticles);
|
||||
_colorStarts.resize(_maxParticles);
|
||||
_colorMiddles.resize(_maxParticles);
|
||||
_colorFinishes.resize(_maxParticles);
|
||||
_particleAlphas.resize(_maxParticles);
|
||||
_alphaStarts.resize(_maxParticles);
|
||||
_alphaMiddles.resize(_maxParticles);
|
||||
_alphaFinishes.resize(_maxParticles);
|
||||
// Pop all the overflowing oldest particles
|
||||
while (_particles.size() > _maxParticles) {
|
||||
_particles.pop_front();
|
||||
}
|
||||
|
||||
// effectively clear all particles and start emitting new ones from scratch.
|
||||
_particleHeadIndex = 0;
|
||||
_particleTailIndex = 0;
|
||||
_timeUntilNextEmit = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// because particles are in a ring buffer, this isn't trivial
|
||||
quint32 ParticleEffectEntityItem::getLivingParticleCount() const {
|
||||
if (_particleTailIndex >= _particleHeadIndex) {
|
||||
return _particleTailIndex - _particleHeadIndex;
|
||||
} else {
|
||||
return (_maxParticles - _particleHeadIndex) + _particleTailIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,19 +11,17 @@
|
|||
#ifndef hifi_ParticleEffectEntityItem_h
|
||||
#define hifi_ParticleEffectEntityItem_h
|
||||
|
||||
#include <AnimationLoop.h>
|
||||
#include <deque>
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
class ParticleEffectEntityItem : public EntityItem {
|
||||
public:
|
||||
ALLOW_INSTANTIATION // This class can be instantiated
|
||||
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
ParticleEffectEntityItem(const EntityItemID& entityItemID);
|
||||
virtual ~ParticleEffectEntityItem();
|
||||
|
||||
ALLOW_INSTANTIATION // This class can be instantiated
|
||||
|
||||
// methods for getting/setting all properties of this entity
|
||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
|
||||
|
@ -218,16 +216,27 @@ public:
|
|||
virtual bool supportsDetailedRayIntersection() const { return false; }
|
||||
|
||||
protected:
|
||||
struct Particle;
|
||||
using Particles = std::deque<Particle>;
|
||||
|
||||
bool isAnimatingSomething() const;
|
||||
|
||||
Particle createParticle();
|
||||
void stepSimulation(float deltaTime);
|
||||
void updateRadius(quint32 index, float age);
|
||||
void updateColor(quint32 index, float age);
|
||||
void updateAlpha(quint32 index, float age);
|
||||
void extendBounds(const glm::vec3& point);
|
||||
void integrateParticle(quint32 index, float deltaTime);
|
||||
quint32 getLivingParticleCount() const;
|
||||
// the properties of this entity
|
||||
void integrateParticle(Particle& particle, float deltaTime);
|
||||
|
||||
struct Particle {
|
||||
float seed { 0.0f };
|
||||
float lifetime { 0.0f };
|
||||
glm::vec3 position { Vectors::ZERO };
|
||||
glm::vec3 velocity { Vectors::ZERO };
|
||||
glm::vec3 acceleration { Vectors::ZERO };
|
||||
};
|
||||
|
||||
// Particles container
|
||||
Particles _particles;
|
||||
|
||||
// Particles properties
|
||||
rgbColor _color;
|
||||
xColor _colorStart = DEFAULT_COLOR;
|
||||
xColor _colorFinish = DEFAULT_COLOR;
|
||||
|
@ -236,63 +245,42 @@ protected:
|
|||
float _alphaStart = DEFAULT_ALPHA_START;
|
||||
float _alphaFinish = DEFAULT_ALPHA_FINISH;
|
||||
float _alphaSpread = DEFAULT_ALPHA_SPREAD;
|
||||
quint32 _maxParticles = DEFAULT_MAX_PARTICLES;
|
||||
float _lifespan = DEFAULT_LIFESPAN;
|
||||
float _emitRate = DEFAULT_EMIT_RATE;
|
||||
float _emitSpeed = DEFAULT_EMIT_SPEED;
|
||||
float _speedSpread = DEFAULT_SPEED_SPREAD;
|
||||
glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION;
|
||||
glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS;
|
||||
float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START;
|
||||
float _polarStart = DEFAULT_POLAR_START;
|
||||
float _polarFinish = DEFAULT_POLAR_FINISH;
|
||||
float _azimuthStart = DEFAULT_AZIMUTH_START;
|
||||
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
|
||||
glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION;
|
||||
glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD;
|
||||
float _particleRadius = DEFAULT_PARTICLE_RADIUS;
|
||||
float _radiusStart = DEFAULT_RADIUS_START;
|
||||
float _radiusFinish = DEFAULT_RADIUS_FINISH;
|
||||
float _radiusSpread = DEFAULT_RADIUS_SPREAD;
|
||||
float _lifespan = DEFAULT_LIFESPAN;
|
||||
|
||||
// Emiter properties
|
||||
quint32 _maxParticles = DEFAULT_MAX_PARTICLES;
|
||||
|
||||
float _emitRate = DEFAULT_EMIT_RATE;
|
||||
float _emitSpeed = DEFAULT_EMIT_SPEED;
|
||||
float _speedSpread = DEFAULT_SPEED_SPREAD;
|
||||
|
||||
glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION;
|
||||
glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS;
|
||||
float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START;
|
||||
glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION;
|
||||
glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD;
|
||||
|
||||
float _polarStart = DEFAULT_POLAR_START;
|
||||
float _polarFinish = DEFAULT_POLAR_FINISH;
|
||||
float _azimuthStart = DEFAULT_AZIMUTH_START;
|
||||
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
|
||||
|
||||
|
||||
quint64 _lastSimulated { 0 };
|
||||
bool _isEmitting { true };
|
||||
|
||||
quint64 _lastSimulated;
|
||||
bool _isEmitting = true;
|
||||
QString _textures { DEFAULT_TEXTURES };
|
||||
bool _texturesChangedFlag { false };
|
||||
ShapeType _shapeType { SHAPE_TYPE_NONE };
|
||||
|
||||
float _timeUntilNextEmit { 0.0f };
|
||||
|
||||
QString _textures = DEFAULT_TEXTURES;
|
||||
bool _texturesChangedFlag = false;
|
||||
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
||||
|
||||
// all the internals of running the particle sim
|
||||
QVector<float> _particleLifetimes;
|
||||
QVector<glm::vec3> _particlePositions;
|
||||
QVector<glm::vec3> _particleVelocities;
|
||||
QVector<glm::vec3> _particleAccelerations;
|
||||
QVector<float> _particleRadiuses;
|
||||
QVector<float> _radiusStarts;
|
||||
QVector<float> _radiusMiddles;
|
||||
QVector<float> _radiusFinishes;
|
||||
QVector<xColor> _particleColors;
|
||||
QVector<xColor> _colorStarts;
|
||||
QVector<xColor> _colorMiddles;
|
||||
QVector<xColor> _colorFinishes;
|
||||
QVector<float> _particleAlphas;
|
||||
QVector<float> _alphaStarts;
|
||||
QVector<float> _alphaMiddles;
|
||||
QVector<float> _alphaFinishes;
|
||||
|
||||
float _timeUntilNextEmit = 0.0f;
|
||||
|
||||
// particle arrays are a ring buffer, use these indices
|
||||
// to keep track of the living particles.
|
||||
quint32 _particleHeadIndex = 0;
|
||||
quint32 _particleTailIndex = 0;
|
||||
|
||||
// bounding volume
|
||||
glm::vec3 _particleMaxBound;
|
||||
glm::vec3 _particleMinBound;
|
||||
|
||||
bool _additiveBlending;
|
||||
|
||||
bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING };
|
||||
};
|
||||
|
||||
#endif // hifi_ParticleEffectEntityItem_h
|
||||
|
|
33
libraries/entities/src/RemapIDOperator.cpp
Normal file
33
libraries/entities/src/RemapIDOperator.cpp
Normal 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;
|
||||
}
|
30
libraries/entities/src/RemapIDOperator.h
Normal file
30
libraries/entities/src/RemapIDOperator.h
Normal 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
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
|
||||
static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
|
||||
|
||||
virtual bool isReadyToComputeShape() { return false; }
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
void updateShapeType(ShapeType type) { _shapeType = type; }
|
||||
virtual ShapeType getShapeType() const;
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ GLBackend::GLShader* compileShader(const Shader& shader) {
|
|||
|
||||
qCWarning(gpulogging) << "GLShader::compileShader - failed to compile the gl shader object:";
|
||||
qCWarning(gpulogging) << temp;
|
||||
|
||||
|
||||
/*
|
||||
filestream.open("debugshader.glsl.info.txt");
|
||||
if (filestream.is_open()) {
|
||||
|
|
|
@ -77,16 +77,6 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
BufferStream::BufferStream() :
|
||||
_buffers(),
|
||||
_offsets(),
|
||||
_strides()
|
||||
{}
|
||||
|
||||
BufferStream::~BufferStream() {
|
||||
}
|
||||
|
||||
void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) {
|
||||
_buffers.push_back(buffer);
|
||||
_offsets.push_back(offset);
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
// Frequency describer
|
||||
enum Frequency {
|
||||
PER_VERTEX = 0,
|
||||
PER_INSTANCE,
|
||||
PER_INSTANCE = 1,
|
||||
};
|
||||
|
||||
// The attribute description
|
||||
|
@ -124,10 +124,7 @@ typedef std::vector< Offset > Offsets;
|
|||
// A Buffer Stream can be assigned to the Batch to set several stream channels in one call
|
||||
class BufferStream {
|
||||
public:
|
||||
typedef Offsets Strides;
|
||||
|
||||
BufferStream();
|
||||
~BufferStream();
|
||||
using Strides = Offsets;
|
||||
|
||||
void clear() { _buffers.clear(); _offsets.clear(); _strides.clear(); }
|
||||
void addBuffer(const BufferPointer& buffer, Offset offset, Offset stride);
|
||||
|
|
|
@ -86,7 +86,6 @@ TransformCamera getTransformCamera() {
|
|||
}
|
||||
<@endfunc@>
|
||||
|
||||
|
||||
<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@>
|
||||
{ // transformModelToWorldPos
|
||||
<$worldPos$> = (<$objectTransform$>._model * <$modelPos$>);
|
||||
|
@ -136,7 +135,22 @@ TransformCamera getTransformCamera() {
|
|||
|
||||
<@func transformClipToEyeDir(cameraTransform, clipPos, eyeDir)@>
|
||||
{ // transformClipToEyeDir
|
||||
<$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0));
|
||||
<$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 0.0));
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
<@func $transformModelToEyePos(cameraTransform, objectTransform, modelPos, eyePos)@>
|
||||
<!// Equivalent to the following but hoppefully a tad more accurate
|
||||
//return camera._view * object._model * pos; !>
|
||||
{ // transformModelToEyePos
|
||||
vec4 _worldpos = (<$objectTransform$>._model * vec4(<$modelPos$>.xyz, 1.0));
|
||||
<$eyePos$> = (<$cameraTransform$>._view * _worldpos);
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
<@func transformEyeToClipPos(cameraTransform, eyePos, clipPos)@>
|
||||
{ // transformEyeToClipPos
|
||||
<$clipPos$> = <$cameraTransform$>._projection * vec4(<$eyePos$>.xyz, 1.0);
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
|
|
|
@ -1,15 +1,5 @@
|
|||
set(TARGET_NAME input-plugins)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared plugins controllers script-engine render-utils)
|
||||
link_hifi_libraries(shared plugins controllers)
|
||||
|
||||
GroupSources("src/input-plugins")
|
||||
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(OpenVR)
|
||||
find_package(OpenVR REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_sdl2()
|
||||
target_sixense()
|
||||
|
|
|
@ -13,17 +13,11 @@
|
|||
#include <plugins/PluginManager.h>
|
||||
|
||||
#include "KeyboardMouseDevice.h"
|
||||
#include "SDL2Manager.h"
|
||||
#include "SixenseManager.h"
|
||||
#include "ViveControllerManager.h"
|
||||
|
||||
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
|
||||
InputPluginList getInputPlugins() {
|
||||
InputPlugin* PLUGIN_POOL[] = {
|
||||
new KeyboardMouseDevice(),
|
||||
new SDL2Manager(),
|
||||
new SixenseManager(),
|
||||
new ViveControllerManager(),
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ Light::Light() :
|
|||
_transform() {
|
||||
// only if created from nothing shall we create the Buffer to store the properties
|
||||
Schema schema;
|
||||
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
}
|
||||
|
||||
Light::Light(const Light& light) :
|
||||
|
|
|
@ -112,8 +112,6 @@ public:
|
|||
Vec4 _shadow{0.0f};
|
||||
|
||||
Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
Schema() {}
|
||||
};
|
||||
|
||||
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -319,10 +319,10 @@ void Resource::attemptRequest() {
|
|||
|
||||
void Resource::finishedLoading(bool success) {
|
||||
if (success) {
|
||||
qDebug().noquote() << "Finished loading:" << _url.toDisplayString();
|
||||
qCDebug(networking).noquote() << "Finished loading:" << _url.toDisplayString();
|
||||
_loaded = true;
|
||||
} else {
|
||||
qDebug().noquote() << "Failed to load:" << _url.toDisplayString();
|
||||
qCDebug(networking).noquote() << "Failed to load:" << _url.toDisplayString();
|
||||
_failedToLoad = true;
|
||||
}
|
||||
_loadPriorities.clear();
|
||||
|
@ -339,13 +339,13 @@ void Resource::makeRequest() {
|
|||
_request = ResourceManager::createResourceRequest(this, _activeUrl);
|
||||
|
||||
if (!_request) {
|
||||
qDebug().noquote() << "Failed to get request for" << _url.toDisplayString();
|
||||
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
|
||||
ResourceCache::requestCompleted(this);
|
||||
finishedLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug().noquote() << "Starting request for:" << _url.toDisplayString();
|
||||
qCDebug(networking).noquote() << "Starting request for:" << _url.toDisplayString();
|
||||
|
||||
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
|
||||
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
|
||||
|
@ -369,7 +369,7 @@ void Resource::handleReplyFinished() {
|
|||
if (result == ResourceRequest::Success) {
|
||||
_data = _request->getData();
|
||||
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
|
||||
qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
|
||||
qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
|
||||
|
||||
finishedLoading(true);
|
||||
emit loaded(_data);
|
||||
|
@ -377,7 +377,7 @@ void Resource::handleReplyFinished() {
|
|||
} else {
|
||||
switch (result) {
|
||||
case ResourceRequest::Result::Timeout: {
|
||||
qDebug() << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
|
||||
qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
|
||||
// Fall through to other cases
|
||||
}
|
||||
case ResourceRequest::Result::ServerUnavailable: {
|
||||
|
@ -386,7 +386,7 @@ void Resource::handleReplyFinished() {
|
|||
const int BASE_DELAY_MS = 1000;
|
||||
if (_attempts++ < MAX_ATTEMPTS) {
|
||||
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
|
||||
qDebug().nospace() << "Retrying to load the asset in " << waitTime
|
||||
qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime
|
||||
<< "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS;
|
||||
QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
|
||||
break;
|
||||
|
@ -394,7 +394,7 @@ void Resource::handleReplyFinished() {
|
|||
// fall through to final failure
|
||||
}
|
||||
default: {
|
||||
qDebug() << "Error loading " << _url;
|
||||
qCDebug(networking) << "Error loading " << _url;
|
||||
auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError
|
||||
: QNetworkReply::UnknownNetworkError;
|
||||
emit failed(error);
|
||||
|
|
|
@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityAdd:
|
||||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
return VERSION_ENTITIES_HAVE_PARENTS;
|
||||
return VERSION_ENTITITES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP;
|
||||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
return 17;
|
||||
|
|
|
@ -161,5 +161,6 @@ const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48;
|
|||
const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49;
|
||||
const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50;
|
||||
const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51;
|
||||
const PacketVersion VERSION_ENTITITES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -11,8 +11,55 @@
|
|||
|
||||
#include "CharacterController.h"
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "BulletUtil.h"
|
||||
#include "PhysicsCollisionGroups.h"
|
||||
|
||||
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
||||
const float JUMP_SPEED = 3.5f;
|
||||
const float MAX_FALL_HEIGHT = 20.0f;
|
||||
|
||||
|
||||
// helper class for simple ray-traces from character
|
||||
class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback {
|
||||
public:
|
||||
ClosestNotMe(btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) {
|
||||
_me = me;
|
||||
}
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) {
|
||||
if (rayResult.m_collisionObject == _me) {
|
||||
return 1.0f;
|
||||
}
|
||||
return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||
}
|
||||
protected:
|
||||
btRigidBody* _me;
|
||||
};
|
||||
|
||||
|
||||
CharacterController::CharacterController() {
|
||||
_halfHeight = 1.0f;
|
||||
|
||||
_enabled = false;
|
||||
|
||||
_floorDistance = MAX_FALL_HEIGHT;
|
||||
|
||||
_walkVelocity.setValue(0.0f, 0.0f, 0.0f);
|
||||
_followVelocity.setValue(0.0f, 0.0f, 0.0f);
|
||||
_jumpSpeed = JUMP_SPEED;
|
||||
_isOnGround = false;
|
||||
_isJumping = false;
|
||||
_isFalling = false;
|
||||
_isHovering = true;
|
||||
_isPushingUp = false;
|
||||
_jumpToHoverStart = 0;
|
||||
_followTime = 0.0f;
|
||||
|
||||
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
||||
|
||||
}
|
||||
|
||||
bool CharacterController::needsRemoval() const {
|
||||
return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
|
||||
}
|
||||
|
@ -23,7 +70,7 @@ bool CharacterController::needsAddition() const {
|
|||
|
||||
void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
||||
if (_dynamicsWorld != world) {
|
||||
if (_dynamicsWorld) {
|
||||
if (_dynamicsWorld) {
|
||||
if (_rigidBody) {
|
||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
||||
_dynamicsWorld->removeAction(this);
|
||||
|
@ -33,9 +80,13 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
if (world && _rigidBody) {
|
||||
_dynamicsWorld = world;
|
||||
_pendingFlags &= ~PENDING_FLAG_JUMP;
|
||||
// Before adding the RigidBody to the world we must save its oldGravity to the side
|
||||
// because adding an object to the world will overwrite it with the default gravity.
|
||||
btVector3 oldGravity = _rigidBody->getGravity();
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR);
|
||||
_dynamicsWorld->addAction(this);
|
||||
//reset(_dynamicsWorld);
|
||||
// restore gravity settings
|
||||
_rigidBody->setGravity(oldGravity);
|
||||
}
|
||||
}
|
||||
if (_dynamicsWorld) {
|
||||
|
@ -48,5 +99,283 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
} else {
|
||||
_pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::preStep(btCollisionWorld* collisionWorld) {
|
||||
// trace a ray straight down to see if we're standing on the ground
|
||||
const btTransform& xform = _rigidBody->getWorldTransform();
|
||||
|
||||
// rayStart is at center of bottom sphere
|
||||
btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp;
|
||||
|
||||
// rayEnd is some short distance outside bottom sphere
|
||||
const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius;
|
||||
btScalar rayLength = _radius + FLOOR_PROXIMITY_THRESHOLD;
|
||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||
|
||||
// scan down for nearby floor
|
||||
ClosestNotMe rayCallback(_rigidBody);
|
||||
rayCallback.m_closestHitFraction = 1.0f;
|
||||
collisionWorld->rayTest(rayStart, rayEnd, rayCallback);
|
||||
if (rayCallback.hasHit()) {
|
||||
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
|
||||
btVector3 actualVelocity = _rigidBody->getLinearVelocity();
|
||||
btScalar actualSpeed = actualVelocity.length();
|
||||
|
||||
btVector3 desiredVelocity = _walkVelocity;
|
||||
btScalar desiredSpeed = desiredVelocity.length();
|
||||
|
||||
const btScalar MIN_UP_PUSH = 0.1f;
|
||||
if (desiredVelocity.dot(_currentUp) < MIN_UP_PUSH) {
|
||||
_isPushingUp = false;
|
||||
}
|
||||
|
||||
const btScalar MIN_SPEED = 0.001f;
|
||||
if (_isHovering) {
|
||||
if (desiredSpeed < MIN_SPEED) {
|
||||
if (actualSpeed < MIN_SPEED) {
|
||||
_rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
const btScalar HOVER_BRAKING_TIMESCALE = 0.1f;
|
||||
btScalar tau = glm::max(dt / HOVER_BRAKING_TIMESCALE, 1.0f);
|
||||
_rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity);
|
||||
}
|
||||
} else {
|
||||
const btScalar HOVER_ACCELERATION_TIMESCALE = 0.1f;
|
||||
btScalar tau = dt / HOVER_ACCELERATION_TIMESCALE;
|
||||
_rigidBody->setLinearVelocity(actualVelocity - tau * (actualVelocity - desiredVelocity));
|
||||
}
|
||||
} else {
|
||||
if (onGround()) {
|
||||
// walking on ground
|
||||
if (desiredSpeed < MIN_SPEED) {
|
||||
if (actualSpeed < MIN_SPEED) {
|
||||
_rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
const btScalar HOVER_BRAKING_TIMESCALE = 0.1f;
|
||||
btScalar tau = dt / HOVER_BRAKING_TIMESCALE;
|
||||
_rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity);
|
||||
}
|
||||
} else {
|
||||
// TODO: modify desiredVelocity using floor normal
|
||||
const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f;
|
||||
btScalar tau = dt / WALK_ACCELERATION_TIMESCALE;
|
||||
btVector3 velocityCorrection = tau * (desiredVelocity - actualVelocity);
|
||||
// subtract vertical component
|
||||
velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp;
|
||||
_rigidBody->setLinearVelocity(actualVelocity + velocityCorrection);
|
||||
}
|
||||
} else {
|
||||
// transitioning to flying
|
||||
btVector3 velocityCorrection = desiredVelocity - actualVelocity;
|
||||
const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f;
|
||||
btScalar tau = dt / FLY_ACCELERATION_TIMESCALE;
|
||||
if (!_isPushingUp) {
|
||||
// actually falling --> compute a different velocity attenuation factor
|
||||
const btScalar FALL_ACCELERATION_TIMESCALE = 2.0f;
|
||||
tau = dt / FALL_ACCELERATION_TIMESCALE;
|
||||
// zero vertical component
|
||||
velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp;
|
||||
}
|
||||
_rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection);
|
||||
}
|
||||
}
|
||||
|
||||
// Rather than add _followVelocity to the velocity of the RigidBody, we explicitly teleport
|
||||
// the RigidBody forward according to the formula: distance = rate * time
|
||||
if (_followVelocity.length2() > 0.0f) {
|
||||
btTransform bodyTransform = _rigidBody->getWorldTransform();
|
||||
bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _followVelocity);
|
||||
_rigidBody->setWorldTransform(bodyTransform);
|
||||
}
|
||||
_followTime += dt;
|
||||
}
|
||||
|
||||
void CharacterController::jump() {
|
||||
// check for case where user is holding down "jump" key...
|
||||
// we'll eventually tansition to "hover"
|
||||
if (!_isJumping) {
|
||||
if (!_isHovering) {
|
||||
_jumpToHoverStart = usecTimestampNow();
|
||||
_pendingFlags |= PENDING_FLAG_JUMP;
|
||||
}
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100);
|
||||
if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) {
|
||||
_isPushingUp = true;
|
||||
setHovering(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CharacterController::onGround() const {
|
||||
const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius;
|
||||
return _floorDistance < FLOOR_PROXIMITY_THRESHOLD;
|
||||
}
|
||||
|
||||
void CharacterController::setHovering(bool hover) {
|
||||
if (hover != _isHovering) {
|
||||
_isHovering = hover;
|
||||
_isJumping = false;
|
||||
|
||||
if (_rigidBody) {
|
||||
if (hover) {
|
||||
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
|
||||
} else {
|
||||
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) {
|
||||
_boxScale = scale;
|
||||
|
||||
float x = _boxScale.x;
|
||||
float z = _boxScale.z;
|
||||
float radius = 0.5f * sqrtf(0.5f * (x * x + z * z));
|
||||
float halfHeight = 0.5f * _boxScale.y - radius;
|
||||
float MIN_HALF_HEIGHT = 0.1f;
|
||||
if (halfHeight < MIN_HALF_HEIGHT) {
|
||||
halfHeight = MIN_HALF_HEIGHT;
|
||||
}
|
||||
|
||||
// compare dimensions
|
||||
float radiusDelta = glm::abs(radius - _radius);
|
||||
float heightDelta = glm::abs(halfHeight - _halfHeight);
|
||||
if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) {
|
||||
// shape hasn't changed --> nothing to do
|
||||
} else {
|
||||
if (_dynamicsWorld) {
|
||||
// must REMOVE from world prior to shape update
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE;
|
||||
// only need to ADD back when we happen to be enabled
|
||||
if (_enabled) {
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
}
|
||||
}
|
||||
|
||||
// it's ok to change offset immediately -- there are no thread safety issues here
|
||||
_shapeLocalOffset = corner + 0.5f * _boxScale;
|
||||
}
|
||||
|
||||
void CharacterController::setEnabled(bool enabled) {
|
||||
if (enabled != _enabled) {
|
||||
if (enabled) {
|
||||
// Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit.
|
||||
// Setting the ADD bit here works for all cases so we don't even bother checking other bits.
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
} else {
|
||||
if (_dynamicsWorld) {
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
_isOnGround = false;
|
||||
}
|
||||
setHovering(true);
|
||||
_enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::updateUpAxis(const glm::quat& rotation) {
|
||||
btVector3 oldUp = _currentUp;
|
||||
_currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS);
|
||||
if (!_isHovering) {
|
||||
const btScalar MIN_UP_ERROR = 0.01f;
|
||||
if (oldUp.distance(_currentUp) > MIN_UP_ERROR) {
|
||||
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::setPositionAndOrientation(
|
||||
const glm::vec3& position,
|
||||
const glm::quat& orientation) {
|
||||
// TODO: update gravity if up has changed
|
||||
updateUpAxis(orientation);
|
||||
|
||||
btQuaternion bodyOrientation = glmToBullet(orientation);
|
||||
btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset);
|
||||
_characterBodyTransform = btTransform(bodyOrientation, bodyPosition);
|
||||
}
|
||||
|
||||
void CharacterController::getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const {
|
||||
if (_enabled && _rigidBody) {
|
||||
const btTransform& avatarTransform = _rigidBody->getWorldTransform();
|
||||
rotation = bulletToGLM(avatarTransform.getRotation());
|
||||
position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::setTargetVelocity(const glm::vec3& velocity) {
|
||||
//_walkVelocity = glmToBullet(_avatarData->getTargetVelocity());
|
||||
_walkVelocity = glmToBullet(velocity);
|
||||
}
|
||||
|
||||
void CharacterController::setFollowVelocity(const glm::vec3& velocity) {
|
||||
_followVelocity = glmToBullet(velocity);
|
||||
}
|
||||
|
||||
glm::vec3 CharacterController::getLinearVelocity() const {
|
||||
glm::vec3 velocity(0.0f);
|
||||
if (_rigidBody) {
|
||||
velocity = bulletToGLM(_rigidBody->getLinearVelocity());
|
||||
}
|
||||
return velocity;
|
||||
}
|
||||
|
||||
void CharacterController::preSimulation() {
|
||||
if (_enabled && _dynamicsWorld) {
|
||||
// slam body to where it is supposed to be
|
||||
_rigidBody->setWorldTransform(_characterBodyTransform);
|
||||
|
||||
// scan for distant floor
|
||||
// rayStart is at center of bottom sphere
|
||||
btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp;
|
||||
|
||||
// rayEnd is straight down MAX_FALL_HEIGHT
|
||||
btScalar rayLength = _radius + MAX_FALL_HEIGHT;
|
||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||
|
||||
ClosestNotMe rayCallback(_rigidBody);
|
||||
rayCallback.m_closestHitFraction = 1.0f;
|
||||
_dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback);
|
||||
if (rayCallback.hasHit()) {
|
||||
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
|
||||
const btScalar MIN_HOVER_HEIGHT = 3.0f;
|
||||
if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) {
|
||||
setHovering(false);
|
||||
}
|
||||
// TODO: use collision events rather than ray-trace test to disable jumping
|
||||
const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius;
|
||||
if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) {
|
||||
_isJumping = false;
|
||||
}
|
||||
} else {
|
||||
_floorDistance = FLT_MAX;
|
||||
setHovering(true);
|
||||
}
|
||||
|
||||
if (_pendingFlags & PENDING_FLAG_JUMP) {
|
||||
_pendingFlags &= ~ PENDING_FLAG_JUMP;
|
||||
if (onGround()) {
|
||||
_isJumping = true;
|
||||
btVector3 velocity = _rigidBody->getLinearVelocity();
|
||||
velocity += _jumpSpeed * _currentUp;
|
||||
_rigidBody->setLinearVelocity(velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
_followTime = 0.0f;
|
||||
}
|
||||
|
||||
void CharacterController::postSimulation() {
|
||||
// postSimulation() exists for symmetry and just in case we need to do something here later
|
||||
}
|
||||
|
|
|
@ -17,11 +17,14 @@
|
|||
#include <btBulletDynamicsCommon.h>
|
||||
#include <BulletDynamics/Character/btCharacterControllerInterface.h>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
||||
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
||||
const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2;
|
||||
const uint32_t PENDING_FLAG_JUMP = 1U << 3;
|
||||
|
||||
const float DEFAULT_CHARACTER_GRAVITY = -5.0f;
|
||||
|
||||
class btRigidBody;
|
||||
class btCollisionWorld;
|
||||
|
@ -29,31 +32,89 @@ class btDynamicsWorld;
|
|||
|
||||
class CharacterController : public btCharacterControllerInterface {
|
||||
public:
|
||||
CharacterController();
|
||||
virtual ~CharacterController() {}
|
||||
|
||||
bool needsRemoval() const;
|
||||
bool needsAddition() const;
|
||||
void setDynamicsWorld(btDynamicsWorld* world);
|
||||
btCollisionObject* getCollisionObject() { return _rigidBody; }
|
||||
|
||||
virtual void updateShapeIfNecessary() = 0;
|
||||
virtual void preSimulation() = 0;
|
||||
virtual void postSimulation() = 0;
|
||||
|
||||
// overrides from btCharacterControllerInterface
|
||||
virtual void setWalkDirection(const btVector3 &walkDirection) { assert(false); }
|
||||
virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); }
|
||||
virtual void reset(btCollisionWorld* collisionWorld) override { }
|
||||
virtual void warp(const btVector3& origin) override { }
|
||||
virtual void debugDraw(btIDebugDraw* debugDrawer) override { }
|
||||
virtual void setUpInterpolate(bool value) override { }
|
||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override {
|
||||
preStep(collisionWorld);
|
||||
playerStep(collisionWorld, deltaTime);
|
||||
}
|
||||
virtual void preStep(btCollisionWorld *collisionWorld) override;
|
||||
virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override;
|
||||
virtual bool canJump() const override { assert(false); return false; } // never call this
|
||||
virtual void jump() override;
|
||||
virtual bool onGround() const;
|
||||
|
||||
void preSimulation();
|
||||
void postSimulation();
|
||||
|
||||
void setPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation);
|
||||
void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
void setTargetVelocity(const glm::vec3& velocity);
|
||||
void setFollowVelocity(const glm::vec3& velocity);
|
||||
float getFollowTime() const { return _followTime; }
|
||||
|
||||
glm::vec3 getLinearVelocity() const;
|
||||
|
||||
bool isHovering() const { return _isHovering; }
|
||||
void setHovering(bool enabled);
|
||||
|
||||
void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale);
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const { return _enabled && _dynamicsWorld; }
|
||||
|
||||
/* these from btCharacterControllerInterface remain to be overridden
|
||||
virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void warp(const btVector3 &origin) = 0;
|
||||
virtual void preStep(btCollisionWorld *collisionWorld) = 0;
|
||||
virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) = 0;
|
||||
virtual bool canJump() const = 0;
|
||||
virtual void jump() = 0;
|
||||
virtual bool onGround() const = 0;
|
||||
*/
|
||||
protected:
|
||||
void updateUpAxis(const glm::quat& rotation);
|
||||
|
||||
protected:
|
||||
btVector3 _currentUp;
|
||||
btVector3 _walkVelocity;
|
||||
btVector3 _followVelocity;
|
||||
btTransform _characterBodyTransform;
|
||||
|
||||
glm::vec3 _shapeLocalOffset;
|
||||
|
||||
glm::vec3 _boxScale; // used to compute capsule shape
|
||||
|
||||
quint64 _jumpToHoverStart;
|
||||
|
||||
btScalar _halfHeight;
|
||||
btScalar _radius;
|
||||
|
||||
btScalar _floorDistance;
|
||||
|
||||
btScalar _gravity;
|
||||
|
||||
btScalar _jumpSpeed;
|
||||
btScalar _followTime;
|
||||
|
||||
bool _enabled;
|
||||
bool _isOnGround;
|
||||
bool _isJumping;
|
||||
bool _isFalling;
|
||||
bool _isHovering;
|
||||
bool _isPushingUp;
|
||||
|
||||
btDynamicsWorld* _dynamicsWorld { nullptr };
|
||||
btRigidBody* _rigidBody { nullptr };
|
||||
uint32_t _pendingFlags { 0 };
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_CharacterControllerInterface_h
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
/// By default, all HMDs are stereo
|
||||
virtual bool isStereo() const { return isHmd(); }
|
||||
virtual bool isThrottled() const { return false; }
|
||||
virtual float getTargetFrameRate() { return 0.0f; }
|
||||
|
||||
// Rendering support
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -766,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);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue