diff --git a/CMakeGraphvizOptions.cmake b/CMakeGraphvizOptions.cmake new file mode 100644 index 0000000000..f1b73f1cae --- /dev/null +++ b/CMakeGraphvizOptions.cmake @@ -0,0 +1 @@ +set(GRAPHVIZ_EXTERNAL_LIBS FALSE) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 13d5b2bc79..0da8dc7309 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,10 @@ if (WIN32) add_paths_to_fixup_libs("${QT_DIR}/bin") endif () +if (NOT DEFINED SERVER_ONLY) + set(SERVER_ONLY 0) +endif() + # add subdirectories for all targets if (NOT ANDROID) add_subdirectory(assignment-client) @@ -205,14 +209,16 @@ if (NOT ANDROID) set_target_properties(domain-server PROPERTIES FOLDER "Apps") add_subdirectory(ice-server) set_target_properties(ice-server PROPERTIES FOLDER "Apps") - add_subdirectory(interface) - set_target_properties(interface PROPERTIES FOLDER "Apps") add_subdirectory(stack-manager) set_target_properties(stack-manager PROPERTIES FOLDER "Apps") - add_subdirectory(tests) - add_subdirectory(plugins) + if (NOT SERVER_ONLY) + add_subdirectory(interface) + set_target_properties(interface PROPERTIES FOLDER "Apps") + add_subdirectory(tests) + add_subdirectory(plugins) + endif() add_subdirectory(tools) -endif () +endif() if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index bbee597797..0d62b8dcc7 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "AssignmentFactory.h" #include "AssignmentActionFactory.h" @@ -61,6 +62,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri DependencyManager::registerInheritance(); auto actionFactory = DependencyManager::set(); + DependencyManager::set(); // setup a thread for the NodeList and its PacketReceiver QThread* nodeThread = new QThread(this); diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 044b44a165..78a049edd6 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -33,7 +33,7 @@ bool OctreeQueryNode::packetIsDuplicate() const { // of the entire packet, we need to compare only the packet content... if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) { - if (memcmp(&_lastOctreePayload + OCTREE_PACKET_EXTRA_HEADERS_SIZE, + if (memcmp(_lastOctreePayload.data() + OCTREE_PACKET_EXTRA_HEADERS_SIZE, _octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE, _octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) { return true; @@ -101,7 +101,7 @@ void OctreeQueryNode::resetOctreePacket() { // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing // packet send rate. _lastOctreePacketLength = _octreePacket->getPayloadSize(); - memcpy(&_lastOctreePayload, _octreePacket->getPayload(), _lastOctreePacketLength); + memcpy(_lastOctreePayload.data(), _octreePacket->getPayload(), _lastOctreePacketLength); // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. diff --git a/cmake/externals/neuron/CMakeLists.txt b/cmake/externals/neuron/CMakeLists.txt new file mode 100644 index 0000000000..6936725571 --- /dev/null +++ b/cmake/externals/neuron/CMakeLists.txt @@ -0,0 +1,56 @@ +include(ExternalProject) + +set(EXTERNAL_NAME neuron) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.zip") +set(NEURON_URL_MD5 "0ab54ca04c9cc8094e0fa046c226e574") + +ExternalProject_Add(${EXTERNAL_NAME} + URL ${NEURON_URL} + URL_MD5 ${NEURON_URL_MD5} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1) + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +# set include dir +if(WIN32) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Windows/include" CACHE TYPE INTERNAL) +elseif(APPLE) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Mac/include" CACHE TYPE INTERNAL) +else() + # Unsupported +endif() + +if(WIN32) + + if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(ARCH_DIR "x64") + else() + set(ARCH_DIR "x86") + endif() + + set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Windows/lib/${ARCH_DIR}") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL) + + add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}") + +elseif(APPLE) + + set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Mac/dylib") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL) + + add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}") + +else() + # UNSUPPORTED +endif() + diff --git a/cmake/externals/qxmpp/CMakeLists.txt b/cmake/externals/qxmpp/CMakeLists.txt deleted file mode 100644 index 600aa7b2ff..0000000000 --- a/cmake/externals/qxmpp/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -set(EXTERNAL_NAME qxmpp) - -# we need to find qmake inside QT_DIR -find_program(QMAKE_COMMAND NAME qmake PATHS ${QT_DIR}/bin $ENV{QTTOOLDIR} NO_DEFAULT_PATH) - -if (NOT QMAKE_COMMAND) - message(FATAL_ERROR "Could not find qmake. Qxmpp cannot be compiled without qmake.") -endif () - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () - -if (WIN32) - find_program(PLATFORM_BUILD_COMMAND nmake PATHS "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin") - - if (NOT PLATFORM_BUILD_COMMAND) - message(FATAL_ERROR "You asked CMake to grap QXmpp and build it, but nmake was not found. Please make sure the folder containing nmake.exe is in your PATH.") - endif () -else () - find_program(PLATFORM_BUILD_COMMAND make) -endif () - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://qxmpp.googlecode.com/files/qxmpp-0.7.6.tar.gz - URL_MD5 ee45a97313306ded2ff0f6618a3ed1e1 - BUILD_IN_SOURCE 1 - PATCH_COMMAND patch -p2 -t -N --verbose < ${CMAKE_CURRENT_SOURCE_DIR}/qxmpp.patch - CONFIGURE_COMMAND ${QMAKE_COMMAND} PREFIX= - BUILD_COMMAND ${PLATFORM_BUILD_COMMAND} - INSTALL_COMMAND ${PLATFORM_BUILD_COMMAND} install - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -if (CMAKE_GENERATOR STREQUAL Xcode) - find_program(DITTO_COMMAND ditto) - - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - copy-from-xcode-install - COMMENT "Copying from /tmp/hifi.dst${INSTALL_DIR} to move install to proper location" - COMMAND ${DITTO_COMMAND} /tmp/hifi.dst${INSTALL_DIR} ${INSTALL_DIR} - DEPENDEES install - LOG 1 - ) -endif () - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Qxmpp include directory") - -set(_LIB_DIR ${INSTALL_DIR}/lib) - -if (WIN32) - set(_LIB_EXT "0.lib") - - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of QXmpp DLL") -else () - if (APPLE) - set(_LIB_EXT ".dylib") - else () - set(_LIB_EXT ".so") - endif () - - set(_LIB_PREFIX "lib") -endif () - -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_LIB_DIR}/${_LIB_PREFIX}qxmpp${_LIB_EXT} CACHE FILEPATH "Path to QXmpp release library") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to QXmpp debug library") diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index bb0b268dd4..17b5d5f49d 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -40,7 +40,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<$,$,$>:--release> $" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" ) elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE) find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH) diff --git a/cmake/macros/TargetNeuron.cmake b/cmake/macros/TargetNeuron.cmake new file mode 100644 index 0000000000..11a92e68ad --- /dev/null +++ b/cmake/macros/TargetNeuron.cmake @@ -0,0 +1,17 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Anthony J. Thibault on 2015/12/21 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_NEURON) + # Neuron data reader is only available on these platforms + if (WIN32 OR APPLE) + add_dependency_external_projects(neuron) + find_package(Neuron REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${NEURON_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${NEURON_LIBRARIES}) + add_definitions(-DHAVE_NEURON) + endif(WIN32 OR APPLE) +endmacro() diff --git a/cmake/modules/FindNeuron.cmake b/cmake/modules/FindNeuron.cmake new file mode 100644 index 0000000000..6a93b3a016 --- /dev/null +++ b/cmake/modules/FindNeuron.cmake @@ -0,0 +1,28 @@ +# +# FindNeuron.cmake +# +# Try to find the Perception Neuron SDK +# +# You must provide a NEURON_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# NEURON_FOUND - system found Neuron SDK +# NEURON_INCLUDE_DIRS - the Neuron SDK include directory +# NEURON_LIBRARIES - Link this to use Neuron +# +# Created on 12/21/2015 by Anthony J. Thibault +# 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(SelectLibraryConfigurations) +select_library_configurations(NEURON) + +set(NEURON_REQUIREMENTS NEURON_INCLUDE_DIRS NEURON_LIBRARIES) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Neuron DEFAULT_MSG NEURON_INCLUDE_DIRS NEURON_LIBRARIES) +mark_as_advanced(NEURON_LIBRARIES NEURON_INCLUDE_DIRS NEURON_SEARCH_DIRS) + diff --git a/examples/acScripts/triggeredRecordingOnAC.js b/examples/acScripts/triggeredRecordingOnAC.js new file mode 100644 index 0000000000..9de5173615 --- /dev/null +++ b/examples/acScripts/triggeredRecordingOnAC.js @@ -0,0 +1,72 @@ +// +// triggeredRecordingOnAC.js +// examples/acScripts +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is the triggered rocording script used in the winterSmashUp target practice game. +// Change the CLIP_URL to your asset, +// the RECORDING_CHANNEL and RECORDING_CHANNEL_MESSAGE are used to trigger it i.e.: +// Messages.sendMessage("PlayBackOnAssignment", "BowShootingGameWelcome"); +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +const CLIP_URL = "atp:3fbe82f2153c443f12f9a2b14ce2d7fa2fff81977263746d9e0885ea5aabed62.hfr"; + +const RECORDING_CHANNEL = 'PlayBackOnAssignment'; +const RECORDING_CHANNEL_MESSAGE = 'BowShootingGameWelcome'; // For each different assignment add a different message here. +const PLAY_FROM_CURRENT_LOCATION = true; +const USE_DISPLAY_NAME = true; +const USE_ATTACHMENTS = true; +const USE_AVATAR_MODEL = true; +const AUDIO_OFFSET = 0.0; +const STARTING_TIME = 0.0; +const COOLDOWN_PERIOD = 0; // The period in ms that no animations can be played after one has been played already + +var isPlaying = false; +var isPlayable = true; + +var playRecording = function() { + if (!isPlayable || isPlaying) { + return; + } + Agent.isAvatar = true; + Recording.setPlayFromCurrentLocation(PLAY_FROM_CURRENT_LOCATION); + Recording.setPlayerUseDisplayName(USE_DISPLAY_NAME); + Recording.setPlayerUseAttachments(USE_ATTACHMENTS); + Recording.setPlayerUseHeadModel(false); + Recording.setPlayerUseSkeletonModel(USE_AVATAR_MODEL); + Recording.setPlayerLoop(false); + Recording.setPlayerTime(STARTING_TIME); + Recording.setPlayerAudioOffset(AUDIO_OFFSET); + Recording.loadRecording(CLIP_URL); + Recording.startPlaying(); + isPlaying = true; + isPlayable = false; // Set this true again after the cooldown period +}; + +Script.update.connect(function(deltaTime) { + if (isPlaying && !Recording.isPlaying()) { + print('Reached the end of the recording. Resetting.'); + isPlaying = false; + Agent.isAvatar = false; + if (COOLDOWN_PERIOD === 0) { + isPlayable = true; + return; + } + Script.setTimeout(function () { + isPlayable; + }, COOLDOWN_PERIOD); + } +}); + +Messages.subscribe(RECORDING_CHANNEL); + +Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel === RECORDING_CHANNEL && message === RECORDING_CHANNEL_MESSAGE) { + playRecording(); + } +}); diff --git a/examples/controllers/getHUDLookAtPositionTest.js b/examples/controllers/getHUDLookAtPositionTest.js new file mode 100644 index 0000000000..348b6757b8 --- /dev/null +++ b/examples/controllers/getHUDLookAtPositionTest.js @@ -0,0 +1,54 @@ +// +// getHUDLookAtPositionTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +// This script demonstrates the testing of the HMD.getHUDLookAtPosition--() functions. +// If these functions are working correctly, we'd expect to see a 3D cube and a 2D square +// follow around the center of the HMD view. + +var cubePosition = { x: 0, y: 0, z: 0 }; +var cubeSize = 0.03; +var cube = Overlays.addOverlay("cube", { + position: cubePosition, + size: cubeSize, + color: { red: 255, green: 0, blue: 0}, + alpha: 1, + solid: false + }); + +var square = Overlays.addOverlay("text", { + x: 0, + y: 0, + width: 20, + height: 20, + color: { red: 255, green: 255, blue: 0}, + backgroundColor: { red: 255, green: 255, blue: 0}, + alpha: 1 + }); + + +Script.update.connect(function(deltaTime) { + if (!HMD.active) { + return; + } + var lookAt3D = HMD.getHUDLookAtPosition3D(); + Overlays.editOverlay(cube, { position: lookAt3D }); + + var lookAt2D = HMD.getHUDLookAtPosition2D(); + Overlays.editOverlay(square, { x: lookAt2D.x, y: lookAt2D.y }); +}); + +Script.scriptEnding.connect(function(){ + Overlays.deleteOverlay(cube); + Overlays.deleteOverlay(square); +}); + + diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 07894b46d1..ed02bd3709 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1,5 +1,4 @@ // handControllerGrab.js -// examples // // Created by Eric Levin on 9/2/15 // Additions by James B. Pollack @imgntn on 9/24/2015 @@ -7,6 +6,7 @@ // Copyright 2015 High Fidelity, Inc. // // Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -14,7 +14,6 @@ Script.include("../libraries/utils.js"); - // // add lines where the hand ray picking is happening // @@ -24,12 +23,15 @@ var WANT_DEBUG = false; // these tune time-averaging and "on" value for analog trigger // -var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value -var TRIGGER_ON_VALUE = 0.4; +var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing +var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab +var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab var TRIGGER_OFF_VALUE = 0.15; var BUMPER_ON_VALUE = 0.5; +var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move. + // // distant manipulation // @@ -54,6 +56,7 @@ var LINE_ENTITY_DIMENSIONS = { y: 1000, z: 1000 }; + var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray @@ -96,7 +99,7 @@ var MSEC_PER_SEC = 1000.0; var LIFETIME = 10; var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; -var PICKS_PER_SECOND_PER_HAND = 5; +var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; var GRABBABLE_PROPERTIES = [ "position", @@ -117,6 +120,22 @@ var DEFAULT_GRABBABLE_DATA = { }; +// sometimes we want to exclude objects from being picked +var USE_BLACKLIST = true; +var blacklist = []; + +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_PARTICLE_BEAM_FOR_SEARCHING = false; + +var USE_ENTITY_LINES_FOR_MOVING = false; +var USE_OVERLAY_LINES_FOR_MOVING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = true; + +var USE_SPOTLIGHT = false; +var USE_POINTLIGHT = false; + // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; @@ -137,6 +156,7 @@ var STATE_WAITING_FOR_BUMPER_RELEASE = 15; var STATE_EQUIP_SPRING = 16; + function stateToName(state) { switch (state) { case STATE_OFF: @@ -214,10 +234,14 @@ function getSpatialOffsetPosition(hand, spatialKey) { position = spatialKey.relativePosition; } + // add the relative hand center offset + var handSizeRatio = calculateHandSizeRatio(); + position = Vec3.multiply(position, handSizeRatio); return position; } var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + function getSpatialOffsetRotation(hand, spatialKey) { var rotation = Quat.IDENTITY; @@ -261,9 +285,20 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; - + //for visualizations this.overlayLine = null; - + this.particleBeam = null; + + //for lights + this.spotlight = null; + this.pointlight = null; + this.overlayLine = null; + this.searchSphere = null; + + // how far from camera to search intersection? + this.intersectionDistance = 0.0; + this.searchSphereDistance = 0.0; + this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -329,7 +364,7 @@ function MyController(hand) { print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); } this.state = newState; - } + }; this.debugLine = function(closePoint, farPoint, color) { Entities.addEntity({ @@ -349,34 +384,7 @@ function MyController(hand) { } }) }); - } - - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - } + }; this.lineOn = function(closePoint, farPoint, color) { // draw a line @@ -409,6 +417,254 @@ function MyController(hand) { } }; + var SEARCH_SPHERE_ALPHA = 0.5; + this.searchSphereOn = function(location, size, color) { + if (this.searchSphere === null) { + var sphereProperties = { + position: location, + size: size, + color: color, + alpha: SEARCH_SPHERE_ALPHA, + solid: true, + visible: true + } + this.searchSphere = Overlays.addOverlay("sphere", sphereProperties); + } else { + Overlays.editOverlay(this.searchSphere, { position: location, size: size, color: color, visible: true }); + } + } + + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + }; + + this.handleParticleBeam = function(position, orientation, color) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; + if (this.particleBeam === null) { + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); + } else { + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); + } + }; + + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + + var distance = Vec3.distance(handPosition, objectPosition); + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; + + + if (this.particleBeam === null) { + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); + } else { + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); + } + }; + + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + visible: false, + "name": "Particle Beam", + "color": color, + "maxParticles": 2000, + "lifespan": lifespan, + "emitRate": 50, + "emitSpeed": speed, + "speedSpread": spread, + "emitOrientation": { + "x": -1, + "y": 0, + "z": 0, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.015, + "radiusSpread": 0.005, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 0, + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + }; + + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + Entities.editEntity(this.particleBeam, { + rotation: orientation, + position: position, + visible: true, + color: color, + emitSpeed: speed, + speedSpread: spread, + lifespan: lifespan + }) + + }; + + this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + }; + + this.handleSpotlight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + }) + } + }; + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + + var lightProperties = { + type: "Light", + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.pointlight === null) { + this.pointlight = Entities.addEntity(lightProperties); + } else { + + } + }; + this.lineOff = function() { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); @@ -423,6 +679,53 @@ function MyController(hand) { this.overlayLine = null; }; + this.searchSphereOff = function() { + if (this.searchSphere !== null) { + //Overlays.editOverlay(this.searchSphere, { visible: false }); + Overlays.deleteOverlay(this.searchSphere); + this.searchSphere = null; + this.searchSphereDistance = 0.0; + this.intersectionDistance = 0.0; + } + + }; + + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.editEntity(this.particleBeam, { + visible: false + }) + } + } + + this.turnLightsOff = function() { + if (this.spotlight !== null) { + Entities.deleteEntity(this.spotlight); + this.spotlight = null; + } + + if (this.pointlight !== null) { + Entities.deleteEntity(this.pointlight); + this.pointlight = null; + } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + this.searchSphereOff(); + }; + this.triggerPress = function(value) { _this.rawTriggerValue = value; }; @@ -431,7 +734,6 @@ function MyController(hand) { _this.rawBumperValue = value; }; - this.updateSmoothedTrigger = function() { var triggerValue = this.rawTriggerValue; // smooth out trigger value @@ -439,6 +741,10 @@ function MyController(hand) { (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); }; + this.triggerSmoothedGrab = function() { + return this.triggerValue > TRIGGER_GRAB_VALUE; + }; + this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; }; @@ -447,32 +753,26 @@ function MyController(hand) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { - var triggerValue = this.rawTriggerValue; - return triggerValue > TRIGGER_ON_VALUE; - }; - this.bumperSqueezed = function() { return _this.rawBumperValue > BUMPER_ON_VALUE; - } + }; this.bumperReleased = function() { return _this.rawBumperValue < BUMPER_ON_VALUE; - } - + }; this.off = function() { - if (this.triggerSmoothedSqueezed()) { + if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) { this.lastPickTime = 0; - this.setState(STATE_SEARCHING); - return; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation; + if (this.triggerSmoothedSqueezed()) { + this.setState(STATE_SEARCHING); + } else { + this.setState(STATE_EQUIP_SEARCHING); + } } - if (this.bumperSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_EQUIP_SEARCHING); - return; - } - } + }; this.search = function() { this.grabbedEntity = null; @@ -482,15 +782,20 @@ function MyController(hand) { return; } - // the trigger is being pressed, do a ray test + // the trigger is being pressed, so do a ray test to see what we are hitting var handPosition = this.getHandPosition(); + + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; + var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); + var distantPickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()), + origin: Camera.position, + direction: Vec3.mix(Quat.getUp(this.getHandRotation()), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE }; - // don't pick 60x per second. + // Pick at some maximum rate, not always var pickRays = []; var now = Date.now(); if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { @@ -515,13 +820,28 @@ function MyController(hand) { }) } - var intersection = Entities.findRayIntersection(pickRayBacked, true); + Messages.sendMessage('Hifi-Light-Overlay-Ray-Check', JSON.stringify(pickRayBacked)); + + var intersection; + + if (USE_BLACKLIST === true && blacklist.length !== 0) { + intersection = Entities.findRayIntersection(pickRayBacked, true, [], blacklist); + } else { + intersection = Entities.findRayIntersection(pickRayBacked, true); + } + if (intersection.intersects) { + // the ray is intersecting something we can move. - var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + var defaultDisableNearGrabData = { + disableNearGrab: false + }; + //sometimes we want things to stay right where they are when we let go. + var disableNearGrabData = getEntityCustomData('handControllerKey', intersection.entityID, defaultDisableNearGrabData); if (intersection.properties.name == "Grab Debug Entity") { continue; @@ -530,11 +850,11 @@ function MyController(hand) { if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { continue; } - if (intersectionDistance > pickRay.length) { + if (this.intersectionDistance > pickRay.length) { // too far away for this ray. continue; } - if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + if (this.intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. if (grabbableData.wantsTrigger) { this.grabbedEntity = intersection.entityID; @@ -543,7 +863,11 @@ function MyController(hand) { } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); + if (disableNearGrabData.disableNearGrab !== true) { + this.setState(STATE_NEAR_GRABBING); + } else { + //disable near grab on this thing + } } else { // equipping if (typeof grabbableData.spatialKey !== 'undefined') { // TODO @@ -568,11 +892,11 @@ function MyController(hand) { // this.setState(STATE_EQUIP_SPRING); this.setState(STATE_EQUIP); return; - } else if (this.state == STATE_SEARCHING) { + } else if ((this.state == STATE_SEARCHING) && this.triggerSmoothedGrab()) { this.setState(STATE_DISTANCE_HOLDING); return; } - } else if (grabbableData.wantsTrigger) { + } else if (grabbableData.wantsTrigger && this.triggerSmoothedGrab()) { this.grabbedEntity = intersection.entityID; this.setState(STATE_FAR_TRIGGER); return; @@ -581,6 +905,7 @@ function MyController(hand) { } } + // forward ray test failed, try sphere test. if (WANT_DEBUG) { Entities.addEntity({ @@ -666,13 +991,42 @@ function MyController(hand) { this.setState(STATE_NEAR_TRIGGER); return; } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + var defaultDisableNearGrabData = { + disableNearGrab: false + }; + //sometimes we want things to stay right where they are when we let go. + var disableNearGrabData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultDisableNearGrabData); + if (disableNearGrabData.disableNearGrab === true) { + //do nothing because near grab is disabled for this object + } else { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + + } + return; } } - //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + //search line visualizations + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } + if (this.intersectionDistance > 0) { + var SPHERE_INTERSECTION_SIZE = 0.011; + var SEARCH_SPHERE_FOLLOW_RATE = 0.50; + var SEARCH_SPHERE_CHASE_DROP = 0.2; + this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE + this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE); + var searchSphereLocation = Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); + searchSphereLocation.y -= ((this.intersectionDistance - this.searchSphereDistance) / this.intersectionDistance) * SEARCH_SPHERE_CHASE_DROP; + this.searchSphereOn(searchSphereLocation, SPHERE_INTERSECTION_SIZE * this.intersectionDistance, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR); + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(handPosition, searchSphereLocation, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR); + } + } }; this.distanceHolding = function() { @@ -725,7 +1079,7 @@ function MyController(hand) { this.currentAvatarPosition = MyAvatar.position; this.currentAvatarOrientation = MyAvatar.orientation; - this.overlayLineOff(); + this.turnOffVisualizations(); }; this.continueDistanceHolding = function() { @@ -751,7 +1105,6 @@ function MyController(hand) { return; } - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * @@ -816,110 +1169,196 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance }); - var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + var defaultMoveWithHeadData = { + disableMoveWithHead: false + }; + + var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); + + if (handControllerData.disableMoveWithHead !== true) { + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.multiply(Vec3.subtract(before, after), HAND_HEAD_MIX_RATIO); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + } + + var defaultConstraintData = { + axisStart: false, + axisEnd: false, + } + + var constraintData = getEntityCustomData('lightModifierKey', this.grabbedEntity, defaultConstraintData); + var clampedVector; + var targetPosition; + if (constraintData.axisStart !== false) { + clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisStart, constraintData.axisEnd); + targetPosition = clampedVector; + } else { + targetPosition = { + x: this.currentObjectPosition.x, + y: this.currentObjectPosition.y, + z: this.currentObjectPosition.z + } + + } + + + //visualizations + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) + } + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); } Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, + targetPosition: targetPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, ttl: ACTION_TTL }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + this.projectVectorAlongAxis = function(position, axisStart, axisEnd) { - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } + var aPrime = Vec3.subtract(position, axisStart); - this.lineOff(); - this.overlayLineOff(); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false + var bPrime = Vec3.subtract(axisEnd, axisStart); + + + var bPrimeMagnitude = Vec3.length(bPrime); + + var dotProduct = Vec3.dot(aPrime, bPrime); + + + var scalar = dotProduct / bPrimeMagnitude; + + if (scalar < 0) { + scalar = 0; + } + + if (scalar > 1) { + scalar = 1; + } + + var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime))); + + return projection + + }, + + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + this.lineOff(); + this.overlayLineOff(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + 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); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - 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); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - this.setState(STATE_CONTINUE_EQUIP_BD); } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; + this.currentObjectTime = Date.now(); + }; this.continueNearGrabbing = function() { if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { @@ -984,13 +1423,12 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); Entities.callEntityMethod(this.grabbedEntity, "unequip"); this.endHandGrasp(); - } }; this.pullTowardEquipPosition = function() { - this.lineOff(); - this.overlayLineOff(); + + this.turnOffVisualizations(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); @@ -1108,11 +1546,15 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; _this.allTouchedIDs = {}; + this.touchTest = function() { var maxDistance = 0.05; var leftHandPosition = MyAvatar.getLeftPalmPosition(); @@ -1181,11 +1623,39 @@ function MyController(hand) { this.release = function() { - this.lineOff(); - this.overlayLineOff(); + this.turnLightsOff(); + this.turnOffVisualizations(); + if (this.grabbedEntity !== null) { if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); + //add velocity whatnot + var defaultReleaseVelocityData = { + disableReleaseVelocity: false + }; + //sometimes we want things to stay right where they are when we let go. + var releaseVelocityData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultReleaseVelocityData); + if (releaseVelocityData.disableReleaseVelocity === true) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + + Entities.editEntity(this.grabbedEntity, { + velocity: { + x: 0, + y: 0, + z: 0 + }, + angularVelocity: { + x: 0, + y: 0, + z: 0 + } + }) + Entities.deleteAction(this.grabbedEntity, this.actionID); + + } else { + //don't make adjustments + Entities.deleteAction(this.grabbedEntity, this.actionID); + + } } } @@ -1199,6 +1669,9 @@ function MyController(hand) { this.cleanup = function() { this.release(); this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); }; this.activateEntity = function(entityID, grabbedProperties) { @@ -1269,27 +1742,34 @@ function MyController(hand) { } //return an object with our updated settings return result; - } + }; this.graspHandler = null + this.startHandGrasp = function() { if (this.hand === RIGHT_HAND) { this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); } else if (this.hand === LEFT_HAND) { this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); } - } + }; this.endHandGrasp = function() { // Tell the animation system we don't need any more callbacks. MyAvatar.removeAnimationStateHandler(this.graspHandler); - } + }; -} +}; var rightController = new MyController(RIGHT_HAND); var leftController = new MyController(LEFT_HAND); +//preload the particle beams so that they are full length when you start searching +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} + var MAPPING_NAME = "com.highfidelity.handControllerGrab"; var mapping = Controller.newMapping(MAPPING_NAME); @@ -1301,39 +1781,66 @@ mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); Controller.enableMapping(MAPPING_NAME); +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items var handToDisable = 'none'; function update() { - if (handToDisable !== LEFT_HAND && handToDisable!=='both') { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { leftController.update(); } - if (handToDisable !== RIGHT_HAND && handToDisable!=='both') { + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { rightController.update(); } } Messages.subscribe('Hifi-Hand-Disabler'); +Messages.subscribe('Hifi-Hand-Grab'); +Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); -handleHandDisablerMessages = function(channel, message, sender) { - +handleHandMessages = function(channel, message, sender) { if (sender === MyAvatar.sessionUUID) { - if (message === 'left') { - handToDisable = LEFT_HAND; - } - if (message === 'right') { - handToDisable = RIGHT_HAND; - } - if(message==='both'){ - handToDisable='both'; - } - if(message==='none'){ - handToDisable='none'; + if (channel === 'Hifi-Hand-Disabler') { + if (message === 'left') { + handToDisable = LEFT_HAND; + } + if (message === 'right') { + handToDisable = RIGHT_HAND; + } + if (message === 'both' || message === 'none') { + handToDisable = message; + } + } else if (channel === 'Hifi-Hand-Grab') { + try { + var data = JSON.parse(message); + var selectedController = (data.hand === 'left') ? leftController : rightController; + selectedController.release(); + selectedController.setState(STATE_EQUIP); + selectedController.grabbedEntity = data.entityID; + + } catch (e) {} + + } else if (channel === 'Hifi-Hand-RayPick-Blacklist') { + try { + var data = JSON.parse(message); + var action = data.action; + var id = data.id; + var index = blacklist.indexOf(id); + + if (action === 'add' && index === -1) { + blacklist.push(id); + } + if (action === 'remove') { + if (index > -1) { + blacklist.splice(index, 1); + } + } + + } catch (e) {} } } - } -Messages.messageReceived.connect(handleHandDisablerMessages); +Messages.messageReceived.connect(handleHandMessages); function cleanup() { rightController.cleanup(); @@ -1342,4 +1849,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js new file mode 100644 index 0000000000..7cebc13feb --- /dev/null +++ b/examples/controllers/neuron/neuronAvatar.js @@ -0,0 +1,239 @@ +var JOINT_PARENT_MAP = { + Hips: "", + RightUpLeg: "Hips", + RightLeg: "RightUpLeg", + RightFoot: "RightLeg", + LeftUpLeg: "Hips", + LeftLeg: "LeftUpLeg", + LeftFoot: "LeftLeg", + Spine: "Hips", + Spine1: "Spine", + Spine2: "Spine1", + Spine3: "Spine2", + Neck: "Spine3", + Head: "Neck", + RightShoulder: "Spine3", + RightArm: "RightShoulder", + RightForeArm: "RightArm", + RightHand: "RightForeArm", + RightHandThumb1: "RightHand", + RightHandThumb2: "RightHandThumb1", + RightHandThumb3: "RightHandThumb2", + RightHandThumb4: "RightHandThumb3", + RightHandIndex1: "RightHand", + RightHandIndex2: "RightHandIndex1", + RightHandIndex3: "RightHandIndex2", + RightHandIndex4: "RightHandIndex3", + RightHandMiddle1: "RightHand", + RightHandMiddle2: "RightHandMiddle1", + RightHandMiddle3: "RightHandMiddle2", + RightHandMiddle4: "RightHandMiddle3", + RightHandRing1: "RightHand", + RightHandRing2: "RightHandRing1", + RightHandRing3: "RightHandRing2", + RightHandRing4: "RightHandRing3", + RightHandPinky1: "RightHand", + RightHandPinky2: "RightHandPinky1", + RightHandPinky3: "RightHandPinky2", + RightHandPinky4: "RightHandPinky3", + LeftShoulder: "Spine3", + LeftArm: "LeftShoulder", + LeftForeArm: "LeftArm", + LeftHand: "LeftForeArm", + LeftHandThumb1: "LeftHand", + LeftHandThumb2: "LeftHandThumb1", + LeftHandThumb3: "LeftHandThumb2", + LeftHandThumb4: "LeftHandThumb3", + LeftHandIndex1: "LeftHand", + LeftHandIndex2: "LeftHandIndex1", + LeftHandIndex3: "LeftHandIndex2", + LeftHandIndex4: "LeftHandIndex3", + LeftHandMiddle1: "LeftHand", + LeftHandMiddle2: "LeftHandMiddle1", + LeftHandMiddle3: "LeftHandMiddle2", + LeftHandMiddle4: "LeftHandMiddle3", + LeftHandRing1: "LeftHand", + LeftHandRing2: "LeftHandRing1", + LeftHandRing3: "LeftHandRing2", + LeftHandRing4: "LeftHandRing3", + LeftHandPinky1: "LeftHand", + LeftHandPinky2: "LeftHandPinky1", + LeftHandPinky3: "LeftHandPinky2", + LeftHandPinky: "LeftHandPinky3", +}; + +var USE_TRANSLATIONS = false; + +// ctor +function Xform(rot, pos) { + this.rot = rot; + this.pos = pos; +}; +Xform.mul = function (lhs, rhs) { + var rot = Quat.multiply(lhs.rot, rhs.rot); + var pos = Vec3.sum(lhs.pos, Vec3.multiplyQbyV(lhs.rot, rhs.pos)); + return new Xform(rot, pos); +}; +Xform.prototype.inv = function () { + var invRot = Quat.inverse(this.rot); + var invPos = Vec3.multiply(-1, this.pos); + return new Xform(invRot, Vec3.multiplyQbyV(invRot, invPos)); +}; +Xform.prototype.toString = function () { + var rot = this.rot; + var pos = this.pos; + return "Xform rot = (" + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "), pos = (" + pos.x + ", " + pos.y + ", " + pos.z + ")"; +}; + +function dumpHardwareMapping() { + Object.keys(Controller.Hardware).forEach(function (deviceName) { + Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { + print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); + }); + }); +} + +// ctor +function NeuronAvatar() { + var self = this; + Script.scriptEnding.connect(function () { + self.shutdown(); + }); + Controller.hardwareChanged.connect(function () { + self.hardwareChanged(); + }); + + if (Controller.Hardware.Neuron) { + this.activate(); + } else { + this.deactivate(); + } +} + +NeuronAvatar.prototype.shutdown = function () { + this.deactivate(); +}; + +NeuronAvatar.prototype.hardwareChanged = function () { + if (Controller.Hardware.Neuron) { + this.activate(); + } else { + this.deactivate(); + } +}; + +NeuronAvatar.prototype.activate = function () { + if (!this._active) { + Script.update.connect(updateCallback); + } + this._active = true; + + // build absDefaultPoseMap + this._defaultAbsRotMap = {}; + this._defaultAbsPosMap = {}; + this._defaultAbsRotMap[""] = {x: 0, y: 0, z: 0, w: 1}; + this._defaultAbsPosMap[""] = {x: 0, y: 0, z: 0}; + var keys = Object.keys(JOINT_PARENT_MAP); + var i, l = keys.length; + for (i = 0; i < l; i++) { + var jointName = keys[i]; + var j = MyAvatar.getJointIndex(jointName); + var parentJointName = JOINT_PARENT_MAP[jointName]; + this._defaultAbsRotMap[jointName] = Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointRotation(j)); + this._defaultAbsPosMap[jointName] = Vec3.sum(this._defaultAbsPosMap[parentJointName], + Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointTranslation(j))); + } +}; + +NeuronAvatar.prototype.deactivate = function () { + if (this._active) { + var self = this; + Script.update.disconnect(updateCallback); + } + this._active = false; + MyAvatar.clearJointsData(); +}; + +NeuronAvatar.prototype.update = function (deltaTime) { + + var hmdActive = HMD.active; + var keys = Object.keys(JOINT_PARENT_MAP); + var i, l = keys.length; + var absDefaultRot = {}; + var jointName, channel, pose, parentJointName, j, parentDefaultAbsRot; + var localRotations = {}; + var localTranslations = {}; + for (i = 0; i < l; i++) { + var jointName = keys[i]; + var channel = Controller.Hardware.Neuron[jointName]; + if (channel) { + pose = Controller.getPoseValue(channel); + parentJointName = JOINT_PARENT_MAP[jointName]; + j = MyAvatar.getJointIndex(jointName); + defaultAbsRot = this._defaultAbsRotMap[jointName]; + parentDefaultAbsRot = this._defaultAbsRotMap[parentJointName]; + + // Rotations from the neuron controller are in world orientation but are delta's from the default pose. + // So first we build the absolute rotation of the default pose (local into world). + // Then apply the rotation from the controller, in world space. + // Then we transform back into joint local by multiplying by the inverse of the parents absolute rotation. + var localRotation = Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot)); + if (!hmdActive || jointName !== "Hips") { + MyAvatar.setJointRotation(j, localRotation); + } + localRotations[jointName] = localRotation; + + // translation proportions might be different from the neuron avatar and the user avatar skeleton. + // so this is disabled by default + if (USE_TRANSLATIONS) { + var localTranslation = Vec3.multiplyQbyV(Quat.inverse(parentDefaultAbsRot), pose.translation); + MyAvatar.setJointTranslation(j, localTranslation); + localTranslations[jointName] = localTranslation; + } else { + localTranslations[jointName] = MyAvatar.getDefaultJointTranslation(j); + } + } + } + + // it attempts to adjust the hips so that the avatar's head is at the same location & oreintation as the HMD. + // however it's fighting with the internal c++ code that also attempts to adjust the hips. + if (hmdActive) { + var UNIT_SCALE = 1 / 100; + var hmdXform = new Xform(HMD.orientation, Vec3.multiply(1 / UNIT_SCALE, HMD.position)); // convert to cm + var y180Xform = new Xform({x: 0, y: 1, z: 0, w: 0}, {x: 0, y: 0, z: 0}); + var avatarXform = new Xform(MyAvatar.orientation, Vec3.multiply(1 / UNIT_SCALE, MyAvatar.position)); // convert to cm + var hipsJointIndex = MyAvatar.getJointIndex("Hips"); + var modelOffsetInvXform = new Xform({x: 0, y: 0, z: 0, w: 1}, MyAvatar.getDefaultJointTranslation(hipsJointIndex)); + var defaultHipsXform = new Xform(MyAvatar.getDefaultJointRotation(hipsJointIndex), MyAvatar.getDefaultJointTranslation(hipsJointIndex)); + + var headXform = new Xform(localRotations["Head"], localTranslations["Head"]); + + // transform eyes down the heirarchy chain into avatar space. + var hierarchy = ["Neck", "Spine3", "Spine2", "Spine1", "Spine"]; + var i, l = hierarchy.length; + for (i = 0; i < l; i++) { + var xform = new Xform(localRotations[hierarchy[i]], localTranslations[hierarchy[i]]); + headXform = Xform.mul(xform, headXform); + } + headXform = Xform.mul(defaultHipsXform, headXform); + + var preXform = Xform.mul(headXform, y180Xform); + var postXform = Xform.mul(avatarXform, Xform.mul(y180Xform, modelOffsetInvXform.inv())); + + // solve for the offset that will put the eyes at the hmd position & orientation. + var hipsOffsetXform = Xform.mul(postXform.inv(), Xform.mul(hmdXform, preXform.inv())); + + // now combine it with the default hips transform + var hipsXform = Xform.mul(hipsOffsetXform, defaultHipsXform); + + MyAvatar.setJointRotation("Hips", hipsXform.rot); + MyAvatar.setJointTranslation("Hips", hipsXform.pos); + } +}; + +var neuronAvatar = new NeuronAvatar(); + +function updateCallback(deltaTime) { + neuronAvatar.update(deltaTime); +} + diff --git a/examples/controllers/philipsVersion.js b/examples/controllers/philipsVersion.js new file mode 100644 index 0000000000..4ae617cf0b --- /dev/null +++ b/examples/controllers/philipsVersion.js @@ -0,0 +1,87 @@ +// +// reticleTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var PITCH_DEADZONE = 1.0; +var PITCH_MAX = 20.0; +var YAW_DEADZONE = 1.0; +var YAW_MAX = 20.0; +var PITCH_SCALING = 10.0; +var YAW_SCALING = 10.0; + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticle(dY, dX) { + var globalPos = Controller.getReticlePosition(); + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + print("UNEXPECTED dX:" + dX + "----------------------------"); + dX = 0; + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + print("UNEXPECTED dY:" + dY + "----------------------------"); + dY = 0; + } + + globalPos.x += dX; + globalPos.y += dY; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping = Controller.newMapping(MAPPING_NAME); + +var lastHandPitch = 0; +var lastHandYaw = 0; + +mapping.from(Controller.Standard.LeftHand).peek().to(function(pose) { + var handEulers = Quat.safeEulerAngles(pose.rotation); + //Vec3.print("handEulers:", handEulers); + + var handPitch = handEulers.y; + var handYaw = handEulers.x; + var changePitch = (handPitch - lastHandPitch) * PITCH_SCALING; + var changeYaw = (handYaw - lastHandYaw) * YAW_SCALING; + if (Math.abs(changePitch) > PITCH_MAX) { + print("Pitch: " + changePitch); + changePitch = 0; + } + if (Math.abs(changeYaw) > YAW_MAX) { + print("Yaw: " + changeYaw); + changeYaw = 0; + } + changePitch = Math.abs(changePitch) < PITCH_DEADZONE ? 0 : changePitch; + changeYaw = Math.abs(changeYaw) < YAW_DEADZONE ? 0 : changeYaw; + moveReticle(changePitch, changeYaw); + lastHandPitch = handPitch; + lastHandYaw = handYaw; + +}); +mapping.enable(); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); diff --git a/examples/controllers/proceduralHandPoseExample.js b/examples/controllers/proceduralHandPoseExample.js new file mode 100644 index 0000000000..8b735f579f --- /dev/null +++ b/examples/controllers/proceduralHandPoseExample.js @@ -0,0 +1,76 @@ +// +// proceduralHandPoseExample.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var MAPPING_NAME = "com.highfidelity.examples.proceduralHandPose"; +var mapping = Controller.newMapping(MAPPING_NAME); +var translation = { x: 0, y: 0.1, z: 0 }; +var translationDx = 0.01; +var translationDy = 0.01; +var translationDz = -0.01; +var TRANSLATION_LIMIT = 0.5; + +var pitch = 45; +var yaw = 0; +var roll = 45; +var pitchDelta = 1; +var yawDelta = -1; +var rollDelta = 1; +var ROTATION_MIN = -90; +var ROTATION_MAX = 90; + +mapping.from(function() { + + // adjust the hand translation in a periodic back and forth motion for each of the 3 axes + translation.x = translation.x + translationDx; + translation.y = translation.y + translationDy; + translation.z = translation.z + translationDz; + if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 * TRANSLATION_LIMIT))) { + translationDx = translationDx * -1; + } + if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 * TRANSLATION_LIMIT))) { + translationDy = translationDy * -1; + } + if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 * TRANSLATION_LIMIT))) { + translationDz = translationDz * -1; + } + + // adjust the hand rotation in a periodic back and forth motion for each of pitch/yaw/roll + pitch = pitch + pitchDelta; + yaw = yaw + yawDelta; + roll = roll + rollDelta; + if ((pitch > ROTATION_MAX) || (pitch < ROTATION_MIN)) { + pitchDelta = pitchDelta * -1; + } + if ((yaw > ROTATION_MAX) || (yaw < ROTATION_MIN)) { + yawDelta = yawDelta * -1; + } + if ((roll > ROTATION_MAX) || (roll < ROTATION_MIN)) { + rollDelta = rollDelta * -1; + } + + var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll); + + var pose = { + translation: translation, + rotation: rotation, + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 } + }; + return pose; +}).debug(true).to(Controller.Standard.LeftHand); + +Controller.enableMapping(MAPPING_NAME); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); diff --git a/examples/controllers/reticleHandAngularVelocityTest.js b/examples/controllers/reticleHandAngularVelocityTest.js new file mode 100644 index 0000000000..94288b7bfb --- /dev/null +++ b/examples/controllers/reticleHandAngularVelocityTest.js @@ -0,0 +1,121 @@ +// +// reticleHandAngularVelocityTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// If you set this to true, you will get the raw instantaneous angular velocity. +// note: there is a LOT of noise in the hydra rotation, you will probably be very +// frustrated with the level of jitter. +var USE_INSTANTANEOUS_ANGULAR_VELOCITY = false; +var whichHand = Controller.Standard.RightHand; +var whichTrigger = Controller.Standard.RT; + + + +function msecTimestampNow() { + var d = new Date(); + return d.getTime(); +} + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticle(dX, dY) { + var globalPos = Controller.getReticlePosition(); + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + print("surpressing unexpectedly large change dX:" + dX + "----------------------------"); + dX = 0; + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + print("surpressing unexpectedly large change dY:" + dY + "----------------------------"); + dY = 0; + } + + globalPos.x += dX; + globalPos.y += dY; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + +var firstTime = true; +var lastTime = msecTimestampNow(); +var previousRotation; + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.from(whichHand).peek().to(function(pose) { + + var MSECS_PER_SECOND = 1000; + var now = msecTimestampNow(); + var deltaMsecs = (now - lastTime); + var deltaTime = deltaMsecs / MSECS_PER_SECOND; + + if (firstTime) { + previousRotation = pose.rotation; + lastTime = msecTimestampNow(); + firstTime = false; + } + + // pose.angularVelocity - is the angularVelocity in a "physics" sense, that + // means the direction of the vector is the axis of symetry of rotation + // and the scale of the vector is the speed in radians/second of rotation + // around that axis. + // + // we want to deconstruct that in the portion of the rotation on the Y axis + // and make that portion move our reticle in the horizontal/X direction + // and the portion of the rotation on the X axis and make that portion + // move our reticle in the veritcle/Y direction + var xPart = -pose.angularVelocity.y; + var yPart = -pose.angularVelocity.x; + + // pose.angularVelocity is "smoothed", we can calculate our own instantaneous + // angular velocity as such: + if (USE_INSTANTANEOUS_ANGULAR_VELOCITY) { + var previousConjugate = Quat.conjugate(previousRotation); + var deltaRotation = Quat.multiply(pose.rotation, previousConjugate); + var normalizedDeltaRotation = Quat.normalize(deltaRotation); + var axis = Quat.axis(normalizedDeltaRotation); + var speed = Quat.angle(normalizedDeltaRotation) / deltaTime; + var instantaneousAngularVelocity = Vec3.multiply(speed, axis); + + xPart = -instantaneousAngularVelocity.y; + yPart = -instantaneousAngularVelocity.x; + + previousRotation = pose.rotation; + } + + var MOVE_SCALE = 1; + lastTime = now; + + var dX = (xPart * MOVE_SCALE) / deltaTime; + var dY = (yPart * MOVE_SCALE) / deltaTime; + + moveReticle(dX, dY); +}); +mapping.enable(); + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); + + diff --git a/examples/controllers/reticleHandRotationTest.js b/examples/controllers/reticleHandRotationTest.js new file mode 100644 index 0000000000..a303e5e7b4 --- /dev/null +++ b/examples/controllers/reticleHandRotationTest.js @@ -0,0 +1,110 @@ +// +// reticleHandRotationTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var DEBUGGING = false; + +Math.clamp=function(a,b,c) { + return Math.max(b,Math.min(c,a)); +} + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +function moveReticleAbsolute(x, y) { + var globalPos = Controller.getReticlePosition(); + globalPos.x = x; + globalPos.y = y; + Controller.setReticlePosition(globalPos); +} + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.enable(); + + + +function debugPrint(message) { + if (DEBUGGING) { + print(message); + } +} + +var leftRightBias = 0.0; +var filteredRotatedLeft = Vec3.UNIT_NEG_Y; +var filteredRotatedRight = Vec3.UNIT_NEG_Y; + +Script.update.connect(function(deltaTime) { + + var poseRight = Controller.getPoseValue(Controller.Standard.RightHand); + var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand); + + // NOTE: hack for now + var screenSizeX = 1920; + var screenSizeY = 1080; + + var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y); + var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y); + + lastRotatedRight = rotatedRight; + + + // Decide which hand should be controlling the pointer + // by comparing which one is moving more, and by + // tending to stay with the one moving more. + var BIAS_ADJUST_RATE = 0.5; + var BIAS_ADJUST_DEADZONE = 0.05; + leftRightBias += (Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity)) * BIAS_ADJUST_RATE; + if (leftRightBias < BIAS_ADJUST_DEADZONE) { + leftRightBias = 0.0; + } else if (leftRightBias > (1.0 - BIAS_ADJUST_DEADZONE)) { + leftRightBias = 1.0; + } + + // Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers + var VELOCITY_FILTER_GAIN = 1.0; + filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0)); + filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0)); + var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, leftRightBias); + + var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again... + var absoluteYaw = -rotated.x; // from -1 left to 1 right + + var ROTATION_BOUND = 0.6; + var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND); + var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND); + + // using only from -ROTATION_BOUND to ROTATION_BOUND + var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND); + var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND); + + var x = screenSizeX * xRatio; + var y = screenSizeY * yRatio; + + // don't move the reticle with the hand controllers unless the controllers are actually being moved + var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.0001; + var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - leftRightBias) + Vec3.length(poseRight.angularVelocity) * leftRightBias; + + if (!(xRatio == 0.5 && yRatio == 0) && (angularVelocityMagnitude > MINIMUM_CONTROLLER_ANGULAR_VELOCITY)) { + moveReticleAbsolute(x, y); + } +}); + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); + + diff --git a/examples/controllers/reticleTests.js b/examples/controllers/reticleTests.js index 4c805c8b1c..56b2ba413a 100644 --- a/examples/controllers/reticleTests.js +++ b/examples/controllers/reticleTests.js @@ -33,7 +33,6 @@ var mappingJSON = { mapping = Controller.parseMapping(JSON.stringify(mappingJSON)); mapping.enable(); - Script.scriptEnding.connect(function(){ mapping.disable(); }); diff --git a/examples/directory.js b/examples/directory.js index b1fac19e8b..8d9993ffda 100644 --- a/examples/directory.js +++ b/examples/directory.js @@ -62,8 +62,13 @@ var directory = (function () { function setUp() { viewport = Controller.getViewportDimensions(); - directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false); - directoryWindow.setVisible(false); + directoryWindow = new OverlayWebWindow({ + title: 'Directory', + source: DIRECTORY_URL, + width: 900, + height: 700, + visible: false + }); directoryButton = Overlays.addOverlay("image", { imageURL: DIRECTORY_BUTTON_URL, diff --git a/examples/edit.js b/examples/edit.js index 59b6ae3e7d..99219fcaa2 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -140,8 +140,37 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", { }); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); -marketplaceWindow.setVisible(false); +var marketplaceWindow = new OverlayWebWindow({ + title: 'Marketplace', + source: "about:blank", + width: 900, + height: 700, + visible: false +}); + +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting marketplace URL to " + url); + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + marketplaceWindow.raise(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} var toolBar = (function() { var that = {}, @@ -413,12 +442,9 @@ var toolBar = (function() { newModelButtonDown = true; return true; } + if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { - if (marketplaceWindow.url != MARKETPLACE_URL) { - marketplaceWindow.setURL(MARKETPLACE_URL); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + toggleMarketplace(); return true; } @@ -1336,6 +1362,7 @@ function getPositionToCreateEntity() { } function importSVO(importURL) { + print("Import URL requested: " + importURL) if (!Entities.canAdjustLocks()) { Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -1574,11 +1601,7 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } else if (data.type == "showMarketplace") { - if (marketplaceWindow.url != data.url) { - marketplaceWindow.setURL(data.url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(); } else if (data.type == "action") { if (data.action == "moveSelectionToGrid") { if (selectionManager.hasSelection()) { @@ -1859,12 +1882,7 @@ var propertyMenu = PopupMenu(); propertyMenu.onSelectMenuItem = function(name) { if (propertyMenu.marketplaceID) { - var url = MARKETPLACE_URL + "/items/" + propertyMenu.marketplaceID; - if (marketplaceWindow.url != url) { - marketplaceWindow.setURL(url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(propertyMenu.marketplaceID); } }; diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js new file mode 100644 index 0000000000..12ef2df48a --- /dev/null +++ b/examples/flowArts/arcBall/arcBall.js @@ -0,0 +1,145 @@ +// +// arcBall.js +// examples/arcBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); + + +var scriptURL = Script.resolvePath("arcBallEntityScript.js?v1" + Math.random()); +ArcBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + name: "Arc Ball", + script: scriptURL, + position: Vec3.sum(spawnPosition, { + x: 0, + y: .7, + z: 0 + }), + dimensions: { + x: .05, + y: .05, + z: .05 + }, + color: { + red: 100, + green: 10, + blue: 150 + }, + ignoreForCollisions: true, + damping: 0.8, + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + // relativePosition: { + // x: 0, + // y: -0.5, + // z: 0.0 + // }, + }, + // invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + name: "ballLight", + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var arcBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + name: "Arc Ball Particle Effect", + colorStart: { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: { + red: 25, + green: 20, + blue: 255 + }, + maxParticles: 100000, + lifespan: 2, + emitRate: 400, + emitSpeed: .1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: .5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(arcBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js new file mode 100644 index 0000000000..987ddc7a31 --- /dev/null +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -0,0 +1,155 @@ +// arcBallEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script handles the logic for the arcBall rave toy +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + var ArcBall = function() { + _this = this; + this.colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }, { + red: 200, + green: 10, + blue: 10 + }]; + + this.searchRadius = 10; + }; + + ArcBall.prototype = { + isGrabbed: false, + startDistantGrab: function() { + this.searchForNearbyArcBalls(); + }, + + startNearGrab: function() { + this.searchForNearbyArcBalls(); + }, + + searchForNearbyArcBalls: function() { + //Search for nearby balls and create an arc to it if one is found + var position = Entities.getEntityProperties(this.entityID, "position").position + var entities = Entities.findEntities(position, this.searchRadius); + entities.forEach(function(entity) { + var props = Entities.getEntityProperties(entity, ["position", "name"]); + if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { + _this.target = entity; + _this.createBeam(position, props.position); + + } + }); + }, + + createBeam: function(startPosition, endPosition) { + + // Creates particle arc from start position to end position + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); + + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + name: "Particle Arc", + parentID: this.entityID, + parentJointIndex: -1, + // position: startPosition, + isEmitting: true, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, + emitOrientation: emitOrientation, + emitSpeed: 1, + speedSpread: 0.02, + emitDimensions: { + x: .01, + y: .01, + z: .01 + }, + polarStart: 0, + polarFinish: 0, + azimuthStart: 0.02, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: 0, + y: 0, + z: 0 + }, + radiusStart: 0.01, + radiusFinish: 0.005, + radiusSpread: 0.005, + alpha: 0.5, + alphaSpread: 0.1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: true + } + this.particleArc = Entities.addEntity(props); + }, + + updateBeam: function() { + if(!this.target) { + return; + } + var startPosition = Entities.getEntityProperties(this.entityID, "position").position; + var targetPosition = Entities.getEntityProperties(this.target, "position").position; + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + Entities.editEntity(this.particleArc, { + emitOrientation: emitOrientation + }); + }, + + continueNearGrab: function() { + this.updateBeam(); + }, + + continueDistantGrab: function() { + this.updateBeam(); + }, + + releaseGrab: function() { + Entities.editEntity(this.particleArc, { + isEmitting: false + }); + this.target = null; + }, + + unload: function() { + if (this.particleArc) { + Entities.deleteEntity(this.particleArc); + } + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new ArcBall(); +}); \ No newline at end of file diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js new file mode 100644 index 0000000000..faa07c186d --- /dev/null +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -0,0 +1,91 @@ +// +// flowArtsHutSpawner.js +// examples/flowArts +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a special flow arts hut with a bunch of flow art toys people can go in and play with +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +Script.include("../../libraries/utils.js"); +Script.include("lightBall/lightBall.js"); +Script.include("raveStick/raveStick.js"); +Script.include("lightSaber/lightSaber.js"); +Script.include("arcBall/arcBall.js"); + + + +var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); +basePosition.y = MyAvatar.position.y + 1; + +// RAVE ITEMS +// var lightBall = new LightBall(basePosition); + +var arcBall = new ArcBall(basePosition); +var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); +var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); +var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); + + +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/RaveRoom.fbx"; + +var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; + +var raveRoom = Entities.addEntity({ + type: "Model", + name: "Rave Hut Room", + modelURL: modelURL, + position: basePosition, + dimensions:roomDimensions, + visible: true +}); + +var floor = Entities.addEntity({ + type: "Box", + name: "Rave Floor", + position: Vec3.sum(basePosition, {x: 0, y: -1.2, z: 0}), + dimensions: {x: roomDimensions.x, y: 0.6, z: roomDimensions.z}, + color: {red: 50, green: 10, blue: 100}, + shapeType: 'box' +}); + + + +var lightZone = Entities.addEntity({ + type: "Zone", + name: "Rave Hut Zone", + shapeType: 'box', + keyLightIntensity: 0.4, + keyLightColor: { + red: 50, + green: 0, + blue: 50 + }, + keyLightAmbientIntensity: .2, + position: MyAvatar.position, + dimensions: { + x: 100, + y: 100, + z: 100 + } +}); + +function cleanup() { + + Entities.deleteEntity(raveRoom); + Entities.deleteEntity(lightZone); + Entities.deleteEntity(floor); + // lightBall.cleanup(); + arcBall.cleanup(); + arcBall2.cleanup(); + raveStick.cleanup(); + lightSaber.cleanup(); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js new file mode 100644 index 0000000000..b2ed00f326 --- /dev/null +++ b/examples/flowArts/lightBall/lightBall.js @@ -0,0 +1,145 @@ +// +// LightBall.js +// examples/lightBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); + +LightBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + name: "containerBall", + position: Vec3.sum(spawnPosition, { + x: 0, + y: 0.5, + z: 0 + }), + dimensions: { + x: 0.1, + y: 0.1, + z: 0.1 + }, + color: { + red: 15, + green: 10, + blue: 150 + }, + collisionsWillMove: true, + // gravity: { + // x: 0, + // y: -0.5, + // z: 0 + // }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0.1, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + name: "ballLight", + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var lightBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + name: "particleBall", + colorStart: { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: { + red: 25, + green: 20, + blue: 255 + }, + maxParticles: 100000, + lifespan: 2, + emitRate: 10000, + emitSpeed: 0.1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: 0.00, + y: 0.00, + z: 0.00 + }, + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: 0.5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(lightBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} diff --git a/examples/flowArts/lightSaber/lightSaber.js b/examples/flowArts/lightSaber/lightSaber.js new file mode 100644 index 0000000000..c4ab49e8e6 --- /dev/null +++ b/examples/flowArts/lightSaber/lightSaber.js @@ -0,0 +1,67 @@ +// +// LightSaber.js +// examples +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a lightsaber which activates on grab +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/lightSaber.fbx"; +var scriptURL = Script.resolvePath("lightSaberEntityScript.js"); +LightSaber = function(spawnPosition) { + + var saberHandle = Entities.addEntity({ + type: "Model", + name: "LightSaber Handle", + modelURL: modelURL, + position: spawnPosition, + shapeType: 'box', + collisionsWillMove: true, + script: scriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.1 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(180, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + + var light = Entities.addEntity({ + type: 'Light', + name: "raveLight", + parentID: saberHandle, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: {red: 200, green: 10, blue: 200}, + intensity: 5 + }); + + + function cleanup() { + Entities.deleteEntity(saberHandle); + } + + this.cleanup = cleanup; +} diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js new file mode 100644 index 0000000000..a86f471449 --- /dev/null +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -0,0 +1,116 @@ +// lightSaberEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script creates the logic for displaying the lightsaber beam. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + // this is the "constructor" for the entity as a JS object we don't do much here + var LightSaber = function() { + _this = this; + this.colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + }; + + LightSaber.prototype = { + isGrabbed: false, + + startNearGrab: function() { + Entities.editEntity(this.beam, { + isEmitting: true, + visible: true + }); + }, + + releaseGrab: function() { + Entities.editEntity(this.beam, { + visible: false, + isEmitting: false + }); + }, + + preload: function(entityID) { + this.entityID = entityID; + this.createBeam(); + }, + + unload: function() { + Entities.deleteEntity(this.beam); + }, + + createBeam: function() { + + this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec); + var position = this.props.position; + + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + name: "LightSaber Beam", + position: position, + parentID: this.entityID, + isEmitting: false, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 2, + emitRate: 1000, + emitOrientation: forwardQuat, + emitSpeed: 0.7, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: 0, + azimuthStart: 0.1, + azimuthFinish: 0.01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + adiusFinish: 0.025, + alpha: 0.7, + alphaSpread: 0.1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: false + } + this.beam = Entities.addEntity(props); + + } + }; + // entity scripts always need to return a newly constructed object of our type + return new LightSaber(); +}); diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js new file mode 100644 index 0000000000..bb936d55c2 --- /dev/null +++ b/examples/flowArts/lightTrails.js @@ -0,0 +1,191 @@ +// +// lightTrails.js +// examples +// +// Created by Eric Levin on 5/14/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates light trails as you move your hydra hands +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../libraries/utils.js"); + +var eraseTrail = true; +var ugLSD = 25; +// var eraseTrail = false; +var LEFT = 0; +var RIGHT = 1; +var MAX_POINTS_PER_LINE = 50; + +var LIFETIME = 6000; +var DRAWING_DEPTH = 0.8; +var LINE_DIMENSIONS = 100; + + +var MIN_POINT_DISTANCE = 0.02; + + +var colorPalette = [{ + red: 250, + green: 137, + blue: 162 +}, { + red: 204, + green: 244, + blue: 249 +}, { + red: 146, + green: 206, + blue: 116 +}, { + red: 240, + green: 87, + blue: 129 +}]; + +var STROKE_WIDTH = 0.04; + +function controller(side, triggerAction) { + this.triggerHeld = false; + this.triggerThreshold = 0.9; + this.side = side; + this.triggerAction = triggerAction; + var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; + + this.light = Entities.addEntity({ + type: 'Light', + position: MyAvatar.position, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + this.trail = Entities.addEntity({ + type: "PolyLine", + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + color: {red: 255, green: 255, blue: 255}, + textures: texture, + lifetime: LIFETIME + }); + this.points = []; + this.normals = []; + this.strokeWidths = []; + var self = this; + + this.trailEraseInterval = Script.setInterval(function() { + if (self.points.length > 0 && eraseTrail) { + self.points.shift(); + self.normals.shift(); + self.strokeWidths.shift(); + Entities.editEntity(self.trail, { + linePoints: self.points, + strokeWidths: self.strokeWidths, + normals: self.normals + }); + } + }, ugLSD); + + + this.setTrailPosition = function(position) { + this.trailPosition = position; + Entities.editEntity(this.trail, { + position: this.trailPosition + }); + } + + + this.update = function(deltaTime) { + this.updateControllerState(); + var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); + var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset); + Entities.editEntity(this.light, { + position: newTrailPos + }); + + + if (!this.drawing) { + this.setTrailPosition(newTrailPos); + this.drawing = true; + } + + if (this.drawing) { + var localPoint = Vec3.subtract(newTrailPos, this.trailPosition); + if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + + if (this.points.length === MAX_POINTS_PER_LINE) { + this.points.shift(); + this.normals.shift(); + this.strokeWidths.shift(); + } + + this.points.push(localPoint); + var normal = computeNormal(newTrailPos, Camera.getPosition()); + + this.normals.push(normal); + this.strokeWidths.push(STROKE_WIDTH + Math.random() * 0.01); + Entities.editEntity(this.trail, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + }); + + } + } + + + this.updateControllerState = function() { + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; + this.triggerValue = Controller.getActionValue(this.triggerAction); + + } + + this.cleanup = function() { + Entities.deleteEntity(this.trail); + Entities.deleteEntity(this.light); + Script.clearInterval(this.trailEraseInterval); + } +} + +function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); +} + +function update(deltaTime) { + leftController.update(deltaTime); + rightController.update(deltaTime); +} + +function scriptEnding() { + leftController.cleanup(); + rightController.cleanup(); +} + +function vectorIsZero(v) { + return v.x === 0 && v.y === 0 && v.z === 0; +} + + +var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK")); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + +function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} \ No newline at end of file diff --git a/examples/flowArts/raveStick/raveStick.js b/examples/flowArts/raveStick/raveStick.js new file mode 100644 index 0000000000..5fb019bf97 --- /dev/null +++ b/examples/flowArts/raveStick/raveStick.js @@ -0,0 +1,95 @@ +// +// RaveStick.js +// examples/flowArats/raveStick +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a rave stick which makes pretty light trails as you paint +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/raveStick.fbx"; +var scriptURL = Script.resolvePath("raveStickEntityScript.js"); +RaveStick = function(spawnPosition) { + var colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + var stick = Entities.addEntity({ + type: "Model", + name: "raveStick", + modelURL: modelURL, + position: spawnPosition, + shapeType: 'box', + collisionsWillMove: true, + script: scriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + rightRelativePosition: { + x: 0.02, + y: 0, + z: 0 + }, + leftRelativePosition: { + x: -0.02, + y: 0, + z: 0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + + var light = Entities.addEntity({ + type: 'Light', + name: "raveLight", + parentID: stick, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0) + var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var color = { + red: 0, + green: 200, + blue: 40 + }; + + + + + function cleanup() { + Entities.deleteEntity(stick); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js new file mode 100644 index 0000000000..3f571817d2 --- /dev/null +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -0,0 +1,141 @@ +// raveStickEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script create light trails on a given object as it moves. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + var LIFETIME = 6000; + var DRAWING_DEPTH = 0.8; + var LINE_DIMENSIONS = 100; + var MAX_POINTS_PER_LINE = 50; + var MIN_POINT_DISTANCE = 0.02; + var STROKE_WIDTH = 0.05 + var ugLSD = 35; + var RaveStick = function() { + _this = this; + this.colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; + this.trail = Entities.addEntity({ + type: "PolyLine", + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + textures: texture, + lifetime: LIFETIME + }); + + this.points = []; + this.normals = []; + this.strokeWidths = []; + }; + + RaveStick.prototype = { + isGrabbed: false, + + startNearGrab: function() { + this.trailBasePosition = Entities.getEntityProperties(this.entityID, "position").position; + Entities.editEntity(this.trail, { + position: this.trailBasePosition + }); + this.points = []; + this.normals = []; + this.strokeWidths = []; + this.setupEraseInterval(); + }, + + continueNearGrab: function() { + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.04)); + var localPoint = Vec3.subtract(position, this.trailBasePosition); + if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + if (this.points.length === MAX_POINTS_PER_LINE) { + this.points.shift(); + this.normals.shift(); + this.strokeWidths.shift(); + } + + this.points.push(localPoint); + var normal = Quat.getUp(props.rotation); + this.normals.push(normal); + this.strokeWidths.push(STROKE_WIDTH); + Entities.editEntity(this.trail, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths + }); + + + }, + + setupEraseInterval: function() { + this.trailEraseInterval = Script.setInterval(function() { + if (_this.points.length > 0) { + _this.points.shift(); + _this.normals.shift(); + _this.strokeWidths.shift(); + Entities.editEntity(_this.trail, { + linePoints: _this.points, + strokeWidths: _this.strokeWidths, + normals: _this.normals + }); + } + }, ugLSD); + }, + + releaseGrab: function() { + if(!this.trailEraseInterval) { + return; + } + Script.setTimeout(function() { + Script.clearInterval(_this.trailEraseInterval); + _this.trailEraseInterval = null; + }, 3000); + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + + unload: function() { + Entities.deleteEntity(this.beam); + Entities.deleteEntity(this.trail); + if (this.trailEraseInterval) { + Script.clearInterval(this.trailEraseInterval); + } + } + }; + return new RaveStick(); + + function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); + } +}); \ No newline at end of file diff --git a/examples/html/eventBridgeLoader.js b/examples/html/eventBridgeLoader.js new file mode 100644 index 0000000000..b62e7d9384 --- /dev/null +++ b/examples/html/eventBridgeLoader.js @@ -0,0 +1,59 @@ + +//public slots: +// void emitWebEvent(const QString& data); +// void emitScriptEvent(const QString& data); +// +//signals: +// void webEventReceived(const QString& data); +// void scriptEventReceived(const QString& data); +// + +EventBridgeConnectionProxy = function(parent) { + this.parent = parent; + this.realSignal = this.parent.realBridge.scriptEventReceived + this.webWindowId = this.parent.webWindow.windowId; +} + +EventBridgeConnectionProxy.prototype.connect = function(callback) { + var that = this; + this.realSignal.connect(function(id, message) { + if (id === that.webWindowId) { callback(message); } + }); +} + +EventBridgeProxy = function(webWindow) { + this.webWindow = webWindow; + this.realBridge = this.webWindow.eventBridge; + this.scriptEventReceived = new EventBridgeConnectionProxy(this); +} + +EventBridgeProxy.prototype.emitWebEvent = function(data) { + this.realBridge.emitWebEvent(data); +} + +openEventBridge = function(callback) { + EVENT_BRIDGE_URI = "ws://localhost:51016"; + socket = new WebSocket(this.EVENT_BRIDGE_URI); + + socket.onclose = function() { + console.error("web channel closed"); + }; + + socket.onerror = function(error) { + console.error("web channel error: " + error); + }; + + socket.onopen = function() { + channel = new QWebChannel(socket, function(channel) { + console.log("Document url is " + document.URL); + for(var key in channel.objects){ + console.log("registered object: " + key); + } + var webWindow = channel.objects[document.URL.toLowerCase()]; + console.log("WebWindow is " + webWindow) + eventBridgeProxy = new EventBridgeProxy(webWindow); + if (callback) { callback(eventBridgeProxy); } + }); + } +} + diff --git a/examples/html/qmlWebTest.html b/examples/html/qmlWebTest.html new file mode 100644 index 0000000000..e59535701d --- /dev/null +++ b/examples/html/qmlWebTest.html @@ -0,0 +1,31 @@ + + +Properties + + + + + + + + + + + + + diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 6edbe6844b..94ffb48a71 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -16,23 +16,72 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; SPACE_LOCAL = "local"; SPACE_WORLD = "world"; + SelectionManager = (function() { var that = {}; + function subscribeToUpdateMessages() { + Messages.subscribe('entityToolUpdates'); + Messages.messageReceived.connect(handleEntitySelectionToolUpdates); + } + + function handleEntitySelectionToolUpdates(channel, message, sender) { + if (channel !== 'entityToolUpdates') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + + if (message === 'callUpdate') { + that._update(); + } + } + + subscribeToUpdateMessages(); + that.savedProperties = {}; that.selections = []; var listeners = []; that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - that.localPosition = { x: 0, y: 0, z: 0 }; - that.localDimensions = { x: 0, y: 0, z: 0 }; - that.localRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; + that.localPosition = { + x: 0, + y: 0, + z: 0 + }; + that.localDimensions = { + x: 0, + y: 0, + z: 0 + }; + that.localRegistrationPoint = { + x: 0.5, + y: 0.5, + z: 0.5 + }; that.worldRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - that.worldPosition = { x: 0, y: 0, z: 0 }; - that.worldDimensions = { x: 0, y: 0, z: 0 }; - that.worldRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; - that.centerPosition = { x: 0, y: 0, z: 0 }; + that.worldPosition = { + x: 0, + y: 0, + z: 0 + }; + that.worldDimensions = { + x: 0, + y: 0, + z: 0 + }; + that.worldRegistrationPoint = { + x: 0.5, + y: 0.5, + z: 0.5 + }; + that.centerPosition = { + x: 0, + y: 0, + z: 0 + }; that.saveProperties = function() { that.savedProperties = {}; @@ -177,9 +226,9 @@ function getRelativeCenterPosition(dimensions, registrationPoint) { } } -SelectionDisplay = (function () { +SelectionDisplay = (function() { var that = {}; - + var MINIMUM_DIMENSION = 0.001; var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; @@ -194,14 +243,18 @@ SelectionDisplay = (function () { var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.svg"; var showExtendedStretchHandles = false; - + var spaceMode = SPACE_LOCAL; var mode = "UNKNOWN"; var overlayNames = new Array(); var lastCameraPosition = Camera.getPosition(); var lastCameraOrientation = Camera.getOrientation(); - var handleHoverColor = { red: 224, green: 67, blue: 36 }; + var handleHoverColor = { + red: 224, + green: 67, + blue: 36 + }; var handleHoverAlpha = 1.0; var rotateOverlayTargetSize = 10000; // really big target @@ -221,19 +274,27 @@ SelectionDisplay = (function () { var pitchNormal; var rollNormal; var rotationNormal; - + var originalRotation; var originalPitch; var originalYaw; var originalRoll; - - var handleColor = { red: 255, green: 255, blue: 255 }; + + var handleColor = { + red: 255, + green: 255, + blue: 255 + }; var handleAlpha = 0.7; - var highlightedHandleColor = { red: 183, green: 64, blue: 44 }; + var highlightedHandleColor = { + red: 183, + green: 64, + blue: 44 + }; var highlightedHandleAlpha = 0.9; - + var previousHandle = false; var previousHandleColor; var previousHandleAlpha; @@ -242,115 +303,182 @@ SelectionDisplay = (function () { var grabberSizeEdge = 0.015; var grabberSizeFace = 0.025; var grabberAlpha = 1; - var grabberColorCorner = { red: 120, green: 120, blue: 120 }; - var grabberColorEdge = { red: 0, green: 0, blue: 0 }; - var grabberColorFace = { red: 120, green: 120, blue: 120 }; + var grabberColorCorner = { + red: 120, + green: 120, + blue: 120 + }; + var grabberColorEdge = { + red: 0, + green: 0, + blue: 0 + }; + var grabberColorFace = { + red: 120, + green: 120, + blue: 120 + }; var grabberLineWidth = 0.5; var grabberSolid = true; - var grabberMoveUpPosition = { x: 0, y: 0, z: 0 }; + var grabberMoveUpPosition = { + x: 0, + y: 0, + z: 0 + }; - var lightOverlayColor = { red: 255, green: 153, blue: 0 }; + var lightOverlayColor = { + red: 255, + green: 153, + blue: 0 + }; var grabberPropertiesCorner = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeCorner, - color: grabberColorCorner, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeCorner, + color: grabberColorCorner, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; var grabberPropertiesEdge = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeEdge, - color: grabberColorEdge, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeEdge, + color: grabberColorEdge, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; var grabberPropertiesFace = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeFace, - color: grabberColorFace, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; - + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeFace, + color: grabberColorFace, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; + var spotLightLineProperties = { color: lightOverlayColor, lineWidth: 1.5, }; - + var highlightBox = Overlays.addOverlay("cube", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 90, green: 90, blue: 90}, - alpha: 1, - solid: false, - visible: false, - dashed: true, - lineWidth: 2.0, - ignoreRayIntersection: true, // this never ray intersects - drawInFront: true - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 90, + green: 90, + blue: 90 + }, + alpha: 1, + solid: false, + visible: false, + dashed: true, + lineWidth: 2.0, + ignoreRayIntersection: true, // this never ray intersects + drawInFront: true + }); var selectionBox = Overlays.addOverlay("cube", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 255, green: 0, blue: 0}, - alpha: 1, - solid: false, - visible: false, - dashed: false, - lineWidth: 1.0, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 255, + green: 0, + blue: 0 + }, + alpha: 1, + solid: false, + visible: false, + dashed: false, + lineWidth: 1.0, + }); var selectionBoxes = []; var rotationDegreesDisplay = Overlays.addOverlay("text3d", { - position: { x:0, y: 0, z: 0}, - text: "", - color: { red: 0, green: 0, blue: 0}, - backgroundColor: { red: 255, green: 255, blue: 255 }, - alpha: 0.7, - backgroundAlpha: 0.7, - visible: false, - isFacingAvatar: true, - drawInFront: true, - ignoreRayIntersection: true, - dimensions: { x: 0, y: 0 }, - lineHeight: 0.0, - topMargin: 0, - rightMargin: 0, - bottomMargin: 0, - leftMargin: 0, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + text: "", + color: { + red: 0, + green: 0, + blue: 0 + }, + backgroundColor: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 0.7, + backgroundAlpha: 0.7, + visible: false, + isFacingAvatar: true, + drawInFront: true, + ignoreRayIntersection: true, + dimensions: { + x: 0, + y: 0 + }, + lineHeight: 0.0, + topMargin: 0, + rightMargin: 0, + bottomMargin: 0, + leftMargin: 0, + }); var grabberMoveUp = Overlays.addOverlay("image3d", { - url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: true, - drawInFront: true, - }); + url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: true, + drawInFront: true, + }); // var normalLine = Overlays.addOverlay("line3d", { // visible: true, @@ -360,7 +488,7 @@ SelectionDisplay = (function () { // color: { red: 255, green: 255, blue: 0 }, // ignoreRayIntersection: true, // }); - + var grabberLBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberRBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberLBF = Overlays.addOverlay("cube", grabberPropertiesCorner); @@ -481,171 +609,324 @@ SelectionDisplay = (function () { ]; - var baseOverlayAngles = { x: 0, y: 0, z: 0 }; + var baseOverlayAngles = { + x: 0, + y: 0, + z: 0 + }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { - position: { x: 1, y: 0, z: 0}, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.5, - solid: true, - visible: false, - width: 300, height: 200, - rotation: baseOverlayRotation, - ignoreRayIntersection: true, // always ignore this - }); + position: { + x: 1, + y: 0, + z: 0 + }, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.5, + solid: true, + visible: false, + width: 300, + height: 200, + rotation: baseOverlayRotation, + ignoreRayIntersection: true, // always ignore this + }); - var yawOverlayAngles = { x: 90, y: 0, z: 0 }; + var yawOverlayAngles = { + x: 90, + y: 0, + z: 0 + }; var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); - var pitchOverlayAngles = { x: 0, y: 90, z: 0 }; + var pitchOverlayAngles = { + x: 0, + y: 90, + z: 0 + }; var pitchOverlayRotation = Quat.fromVec3Degrees(pitchOverlayAngles); - var rollOverlayAngles = { x: 0, y: 180, z: 0 }; + var rollOverlayAngles = { + x: 0, + y: 180, + z: 0 + }; var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); var xRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var yRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 255, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var zRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 0, blue: 255 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var rotateZeroOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 2.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + visible: false, + lineWidth: 2.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateCurrentOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 2.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 0, blue: 255 }, - ignoreRayIntersection: true, // always ignore this - }); + visible: false, + lineWidth: 2.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayTarget = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: rotateOverlayTargetSize, - color: { red: 0, green: 0, blue: 0 }, - alpha: 0.0, - solid: true, - visible: false, - rotation: yawOverlayRotation, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: rotateOverlayTargetSize, + color: { + red: 0, + green: 0, + blue: 0 + }, + alpha: 0.0, + solid: true, + visible: false, + rotation: yawOverlayRotation, + }); var rotateOverlayInner = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.2, + solid: true, + visible: false, + rotation: yawOverlayRotation, + hasTickMarks: true, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayOuter = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.2, + solid: true, + visible: false, + rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + hasTickMarks: true, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayCurrent = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 224, green: 67, blue: 36}, - alpha: 0.8, - solid: true, - visible: false, - rotation: yawOverlayRotation, - ignoreRayIntersection: true, // always ignore this - hasTickMarks: true, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 224, + green: 67, + blue: 36 + }, + alpha: 0.8, + solid: true, + visible: false, + rotation: yawOverlayRotation, + ignoreRayIntersection: true, // always ignore this + hasTickMarks: true, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + }); var yawHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var pitchHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var rollHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var allOverlays = [ highlightBox, @@ -703,7 +984,7 @@ SelectionDisplay = (function () { overlayNames[grabberEdgeNL] = "grabberEdgeNL"; overlayNames[grabberEdgeFR] = "grabberEdgeFR"; overlayNames[grabberEdgeFL] = "grabberEdgeFL"; - + overlayNames[yawHandle] = "yawHandle"; overlayNames[pitchHandle] = "pitchHandle"; overlayNames[rollHandle] = "rollHandle"; @@ -718,6 +999,7 @@ SelectionDisplay = (function () { var activeTool = null; var grabberTools = {}; + function addGrabberTool(overlay, tool) { grabberTools[overlay] = { mode: tool.mode, @@ -727,8 +1009,8 @@ SelectionDisplay = (function () { } } - - that.cleanup = function () { + + that.cleanup = function() { for (var i = 0; i < allOverlays.length; i++) { Overlays.deleteOverlay(allOverlays[i]); } @@ -741,19 +1023,21 @@ SelectionDisplay = (function () { var properties = Entities.getEntityProperties(entityID); Overlays.editOverlay(highlightBox, { visible: true, - position: properties.boundingBox.center, + position: properties.boundingBox.center, dimensions: properties.dimensions, rotation: properties.rotation }); }; that.unhighlightSelectable = function(entityID) { - Overlays.editOverlay(highlightBox,{ visible: false}); + Overlays.editOverlay(highlightBox, { + visible: false + }); }; that.select = function(entityID, event) { var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - + lastCameraPosition = Camera.getPosition(); lastCameraOrientation = Camera.getOrientation(); @@ -766,12 +1050,16 @@ SelectionDisplay = (function () { print(" event.y:" + event.y); Vec3.print(" current position:", properties.position); } - - + + } - Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); - Overlays.editOverlay(highlightBox, { visible: false }); + Entities.editEntity(entityID, { + localRenderAlpha: 0.1 + }); + Overlays.editOverlay(highlightBox, { + visible: false + }); that.updateHandles(); } @@ -789,7 +1077,7 @@ SelectionDisplay = (function () { } var rotateHandleOffset = 0.05; - + var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; var dimensions, rotation; @@ -828,136 +1116,320 @@ SelectionDisplay = (function () { * ------------------------------*/ - + var cameraPosition = Camera.getPosition(); if (cameraPosition.x > objectCenter.x) { // must be BRF or BRN if (cameraPosition.z < objectCenter.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 90, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 90, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 0 + }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - yawCorner = { x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: left + rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: near - rotateHandleOffset + }; - pitchCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset}; + pitchCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - rollCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; + rollCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: right, y: boundsCenter.y, z: boundsCenter.z}; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: far }; - + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: right, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: far + }; + + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_SOUTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_SOUTH_URL + }); - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_SOUTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_SOUTH_URL }); - } else { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 0, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 0, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 180, + y: 270, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 90 + }); + + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - yawCorner = { x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset }; + yawCorner = { + x: left + rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: far + rotateHandleOffset + }; - pitchCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; + pitchCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - rollCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset}; + rollCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: right, y: boundsCenter.y, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: near}; + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: right, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: near + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } } else { - + // must be BLF or BLN if (cameraPosition.z < objectCenter.z) { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 180, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 180, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 90, + y: 0, + z: 90 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 180 + }); - yawCorner = { x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - pitchCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: right - rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: near - rotateHandleOffset + }; - rollCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset}; + pitchCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: left, y: boundsCenter.y, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: far}; + rollCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: left, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: far + }; + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } else { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawNormal = { x: 0, y: 1, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; - pitchNormal = { x: 1, y: 0, z: 0 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 270, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 180, + y: 270, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 180 + }); - yawCorner = { x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; - rollCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: right - rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: far + rotateHandleOffset + }; - pitchCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset}; + rollCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: near }; - pitchCenter = { x: left, y: boundsCenter.y, z: boundsCenter.z}; + pitchCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: near + }; + pitchCenter = { + x: left, + y: boundsCenter.y, + z: boundsCenter.z + }; + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } } - + var rotateHandlesVisible = true; var rotationOverlaysVisible = false; var translateHandlesVisible = true; @@ -984,20 +1456,38 @@ SelectionDisplay = (function () { rotateHandlesVisible = false; translateHandlesVisible = false; } - + var rotation = selectionManager.worldRotation; var dimensions = selectionManager.worldDimensions; var position = selectionManager.worldPosition; - - Overlays.editOverlay(rotateOverlayTarget, { visible: rotationOverlaysVisible }); - Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible }); - Overlays.editOverlay(rotateCurrentOverlay, { visible: rotationOverlaysVisible }); + + Overlays.editOverlay(rotateOverlayTarget, { + visible: rotationOverlaysVisible + }); + Overlays.editOverlay(rotateZeroOverlay, { + visible: rotationOverlaysVisible + }); + Overlays.editOverlay(rotateCurrentOverlay, { + visible: rotationOverlaysVisible + }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden - Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, rotation: yawHandleRotation}); - Overlays.editOverlay(pitchHandle, { visible: rotateHandlesVisible, position: pitchCorner, rotation: pitchHandleRotation}); - Overlays.editOverlay(rollHandle, { visible: rotateHandlesVisible, position: rollCorner, rotation: rollHandleRotation}); + Overlays.editOverlay(yawHandle, { + visible: rotateHandlesVisible, + position: yawCorner, + rotation: yawHandleRotation + }); + Overlays.editOverlay(pitchHandle, { + visible: rotateHandlesVisible, + position: pitchCorner, + rotation: pitchHandleRotation + }); + Overlays.editOverlay(rollHandle, { + visible: rotateHandlesVisible, + position: rollCorner, + rotation: rollHandleRotation + }); }; that.setSpaceMode = function(newSpaceMode) { @@ -1016,8 +1506,7 @@ SelectionDisplay = (function () { that.updateHandles(); }; - that.unselectAll = function () { - }; + that.unselectAll = function() {}; that.updateHandles = function() { if (SelectionManager.selections.length == 0) { @@ -1061,34 +1550,138 @@ SelectionDisplay = (function () { var worldTop = SelectionManager.worldDimensions.y / 2; - var LBN = { x: left, y: bottom, z: near }; - var RBN = { x: right, y: bottom, z: near }; - var LBF = { x: left, y: bottom, z: far }; - var RBF = { x: right, y: bottom, z: far }; - var LTN = { x: left, y: top, z: near }; - var RTN = { x: right, y: top, z: near }; - var LTF = { x: left, y: top, z: far }; - var RTF = { x: right, y: top, z: far }; + var LBN = { + x: left, + y: bottom, + z: near + }; + var RBN = { + x: right, + y: bottom, + z: near + }; + var LBF = { + x: left, + y: bottom, + z: far + }; + var RBF = { + x: right, + y: bottom, + z: far + }; + var LTN = { + x: left, + y: top, + z: near + }; + var RTN = { + x: right, + y: top, + z: near + }; + var LTF = { + x: left, + y: top, + z: far + }; + var RTF = { + x: right, + y: top, + z: far + }; - var TOP = { x: center.x, y: top, z: center.z }; - var BOTTOM = { x: center.x, y: bottom, z: center.z }; - var LEFT = { x: left, y: center.y, z: center.z }; - var RIGHT = { x: right, y: center.y, z: center.z }; - var NEAR = { x: center.x, y: center.y, z: near }; - var FAR = { x: center.x, y: center.y, z: far }; + var TOP = { + x: center.x, + y: top, + z: center.z + }; + var BOTTOM = { + x: center.x, + y: bottom, + z: center.z + }; + var LEFT = { + x: left, + y: center.y, + z: center.z + }; + var RIGHT = { + x: right, + y: center.y, + z: center.z + }; + var NEAR = { + x: center.x, + y: center.y, + z: near + }; + var FAR = { + x: center.x, + y: center.y, + z: far + }; - var EdgeTR = { x: right, y: top, z: center.z }; - var EdgeTL = { x: left, y: top, z: center.z }; - var EdgeTF = { x: center.x, y: top, z: front }; - var EdgeTN = { x: center.x, y: top, z: near }; - var EdgeBR = { x: right, y: bottom, z: center.z }; - var EdgeBL = { x: left, y: bottom, z: center.z }; - var EdgeBF = { x: center.x, y: bottom, z: front }; - var EdgeBN = { x: center.x, y: bottom, z: near }; - var EdgeNR = { x: right, y: center.y, z: near }; - var EdgeNL = { x: left, y: center.y, z: near }; - var EdgeFR = { x: right, y: center.y, z: front }; - var EdgeFL = { x: left, y: center.y, z: front }; + var EdgeTR = { + x: right, + y: top, + z: center.z + }; + var EdgeTL = { + x: left, + y: top, + z: center.z + }; + var EdgeTF = { + x: center.x, + y: top, + z: front + }; + var EdgeTN = { + x: center.x, + y: top, + z: near + }; + var EdgeBR = { + x: right, + y: bottom, + z: center.z + }; + var EdgeBL = { + x: left, + y: bottom, + z: center.z + }; + var EdgeBF = { + x: center.x, + y: bottom, + z: front + }; + var EdgeBN = { + x: center.x, + y: bottom, + z: near + }; + var EdgeNR = { + x: right, + y: center.y, + z: near + }; + var EdgeNL = { + x: left, + y: center.y, + z: near + }; + var EdgeFR = { + x: right, + y: center.y, + z: front + }; + var EdgeFL = { + x: left, + y: center.y, + z: front + }; LBN = Vec3.multiplyQbyV(rotation, LBN); RBN = Vec3.multiplyQbyV(rotation, RBN); @@ -1151,7 +1744,7 @@ SelectionDisplay = (function () { var stretchHandlesVisible = spaceMode == SPACE_LOCAL; var extendedStretchHandlesVisible = stretchHandlesVisible && showExtendedStretchHandles; - if (selectionManager.selections.length == 1 ) { + if (selectionManager.selections.length == 1) { var properties = Entities.getEntityProperties(selectionManager.selections[0]); if (properties.type == "Light" && properties.isSpotlight == true) { var stretchHandlesVisible = false; @@ -1190,7 +1783,11 @@ SelectionDisplay = (function () { }); Overlays.editOverlay(grabberSpotLightCircle, { position: NEAR, - dimensions: { x: distance, y: distance, z: 1 }, + dimensions: { + x: distance, + y: distance, + z: 1 + }, lineWidth: 1.5, rotation: rotation, visible: true, @@ -1217,15 +1814,33 @@ SelectionDisplay = (function () { visible: true, }); - Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); - Overlays.editOverlay(grabberPointLightT, { visible: false }); - Overlays.editOverlay(grabberPointLightB, { visible: false }); - Overlays.editOverlay(grabberPointLightL, { visible: false }); - Overlays.editOverlay(grabberPointLightR, { visible: false }); - Overlays.editOverlay(grabberPointLightF, { visible: false }); - Overlays.editOverlay(grabberPointLightN, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleX, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleY, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + visible: false + }); + Overlays.editOverlay(grabberPointLightT, { + visible: false + }); + Overlays.editOverlay(grabberPointLightB, { + visible: false + }); + Overlays.editOverlay(grabberPointLightL, { + visible: false + }); + Overlays.editOverlay(grabberPointLightR, { + visible: false + }); + Overlays.editOverlay(grabberPointLightF, { + visible: false + }); + Overlays.editOverlay(grabberPointLightN, { + visible: false + }); } else if (properties.type == "Light" && properties.isSpotlight == false) { var stretchHandlesVisible = false; var extendedStretchHandlesVisible = false; @@ -1262,75 +1877,203 @@ SelectionDisplay = (function () { Overlays.editOverlay(grabberPointLightCircleX, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleY, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleZ, { position: position, rotation: rotation, - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); - Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); - Overlays.editOverlay(grabberSpotLightL, { visible: false }); - Overlays.editOverlay(grabberSpotLightR, { visible: false }); - Overlays.editOverlay(grabberSpotLightT, { visible: false }); - Overlays.editOverlay(grabberSpotLightB, { visible: false }); - Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + Overlays.editOverlay(grabberSpotLightRadius, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightB, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightCircle, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineB, { + visible: false + }); } else { - Overlays.editOverlay(grabberSpotLightCenter, { visible: false }); - Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); - Overlays.editOverlay(grabberSpotLightL, { visible: false }); - Overlays.editOverlay(grabberSpotLightR, { visible: false }); - Overlays.editOverlay(grabberSpotLightT, { visible: false }); - Overlays.editOverlay(grabberSpotLightB, { visible: false }); - Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + Overlays.editOverlay(grabberSpotLightCenter, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightRadius, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightB, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightCircle, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineB, { + visible: false + }); - Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); - Overlays.editOverlay(grabberPointLightT, { visible: false }); - Overlays.editOverlay(grabberPointLightB, { visible: false }); - Overlays.editOverlay(grabberPointLightL, { visible: false }); - Overlays.editOverlay(grabberPointLightR, { visible: false }); - Overlays.editOverlay(grabberPointLightF, { visible: false }); - Overlays.editOverlay(grabberPointLightN, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleX, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleY, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + visible: false + }); + Overlays.editOverlay(grabberPointLightT, { + visible: false + }); + Overlays.editOverlay(grabberPointLightB, { + visible: false + }); + Overlays.editOverlay(grabberPointLightL, { + visible: false + }); + Overlays.editOverlay(grabberPointLightR, { + visible: false + }); + Overlays.editOverlay(grabberPointLightF, { + visible: false + }); + Overlays.editOverlay(grabberPointLightN, { + visible: false + }); } } - Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, rotation: rotation, position: LBN }); - Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, rotation: rotation, position: RBN }); - Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, rotation: rotation, position: LBF }); - Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, rotation: rotation, position: RBF }); + Overlays.editOverlay(grabberLBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: LBN + }); + Overlays.editOverlay(grabberRBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: RBN + }); + Overlays.editOverlay(grabberLBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: LBF + }); + Overlays.editOverlay(grabberRBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: RBF + }); - Overlays.editOverlay(grabberLTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LTN }); - Overlays.editOverlay(grabberRTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RTN }); - Overlays.editOverlay(grabberLTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LTF }); - Overlays.editOverlay(grabberRTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RTF }); + Overlays.editOverlay(grabberLTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LTN + }); + Overlays.editOverlay(grabberRTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RTN + }); + Overlays.editOverlay(grabberLTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LTF + }); + Overlays.editOverlay(grabberRTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RTF + }); - Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, rotation: rotation, position: TOP }); - Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, rotation: rotation, position: BOTTOM }); - Overlays.editOverlay(grabberLEFT, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LEFT }); - Overlays.editOverlay(grabberRIGHT, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RIGHT }); - Overlays.editOverlay(grabberNEAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: NEAR }); - Overlays.editOverlay(grabberFAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: FAR }); + Overlays.editOverlay(grabberTOP, { + visible: stretchHandlesVisible, + rotation: rotation, + position: TOP + }); + Overlays.editOverlay(grabberBOTTOM, { + visible: stretchHandlesVisible, + rotation: rotation, + position: BOTTOM + }); + Overlays.editOverlay(grabberLEFT, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LEFT + }); + Overlays.editOverlay(grabberRIGHT, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RIGHT + }); + Overlays.editOverlay(grabberNEAR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: NEAR + }); + Overlays.editOverlay(grabberFAR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: FAR + }); var boxPosition = Vec3.multiplyQbyV(rotation, center); boxPosition = Vec3.sum(position, boxPosition); @@ -1346,9 +2089,17 @@ SelectionDisplay = (function () { for (var i = 0; i < overlaysNeeded; i++) { selectionBoxes.push( Overlays.addOverlay("cube", { - position: { x: 0, y: 0, z: 0 }, + position: { + x: 0, + y: 0, + z: 0 + }, size: 1, - color: { red: 255, green: 153, blue: 0 }, + color: { + red: 255, + green: 153, + blue: 0 + }, alpha: 1, solid: false, visible: false, @@ -1366,7 +2117,11 @@ SelectionDisplay = (function () { // Adjust overlay position to take registrationPoint into account // centeredRP = registrationPoint with range [-0.5, 0.5] - var centeredRP = Vec3.subtract(properties.registrationPoint, { x: 0.5, y: 0.5, z: 0.5 }); + var centeredRP = Vec3.subtract(properties.registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); var offset = vec3Mult(properties.dimensions, centeredRP); offset = Vec3.multiply(-1, offset); offset = Vec3.multiplyQbyV(properties.rotation, offset); @@ -1382,25 +2137,81 @@ SelectionDisplay = (function () { } // Hide any remaining selection boxes for (; i < selectionBoxes.length; i++) { - Overlays.editOverlay(selectionBoxes[i], { visible: false }); + Overlays.editOverlay(selectionBoxes[i], { + visible: false + }); } - Overlays.editOverlay(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR }); - Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL }); - Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF }); - Overlays.editOverlay(grabberEdgeTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTN }); - Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBR }); - Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBL }); - Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBF }); - Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBN }); - Overlays.editOverlay(grabberEdgeNR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeNR }); - Overlays.editOverlay(grabberEdgeNL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeNL }); - Overlays.editOverlay(grabberEdgeFR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeFR }); - Overlays.editOverlay(grabberEdgeFL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeFL }); + Overlays.editOverlay(grabberEdgeTR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTR + }); + Overlays.editOverlay(grabberEdgeTL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTL + }); + Overlays.editOverlay(grabberEdgeTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTF + }); + Overlays.editOverlay(grabberEdgeTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTN + }); + Overlays.editOverlay(grabberEdgeBR, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBR + }); + Overlays.editOverlay(grabberEdgeBL, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBL + }); + Overlays.editOverlay(grabberEdgeBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBF + }); + Overlays.editOverlay(grabberEdgeBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBN + }); + Overlays.editOverlay(grabberEdgeNR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeNR + }); + Overlays.editOverlay(grabberEdgeNL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeNL + }); + Overlays.editOverlay(grabberEdgeFR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeFR + }); + Overlays.editOverlay(grabberEdgeFL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeFL + }); var grabberMoveUpOffset = 0.1; - grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z } - Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" }); + grabberMoveUpPosition = { + x: position.x, + y: position.y + worldTop + grabberMoveUpOffset, + z: position.z + } + Overlays.editOverlay(grabberMoveUp, { + visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" + }); Overlays.editOverlay(baseOfEntityProjectionOverlay, { visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL", @@ -1423,16 +2234,19 @@ SelectionDisplay = (function () { that.setOverlaysVisible = function(isVisible) { var length = allOverlays.length; for (var i = 0; i < length; i++) { - Overlays.editOverlay(allOverlays[i], { visible: isVisible }); + Overlays.editOverlay(allOverlays[i], { + visible: isVisible + }); } length = selectionBoxes.length; for (var i = 0; i < length; i++) { - Overlays.editOverlay(selectionBoxes[i], { visible: isVisible }); + Overlays.editOverlay(selectionBoxes[i], { + visible: isVisible + }); } }; - that.unselect = function (entityID) { - }; + that.unselect = function(entityID) {}; var initialXZPick = null; var isConstrained = false; @@ -1447,7 +2261,11 @@ SelectionDisplay = (function () { var dimensions = SelectionManager.worldDimensions; var pickRay = Camera.computePickRay(event.x, event.y); - initialXZPick = rayPlaneIntersection(pickRay, startPosition, { x: 0, y: 1, z: 0 }); + initialXZPick = rayPlaneIntersection(pickRay, startPosition, { + x: 0, + y: 1, + z: 0 + }); // Duplicate entities if alt is pressed. This will make a // copy of the selected entities and move the _original_ entities, not @@ -1473,13 +2291,21 @@ SelectionDisplay = (function () { onEnd: function(event, reason) { pushCommandForSelections(duplicatedEntityIDs); - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); }, onMove: function(event) { pickRay = Camera.computePickRay(event.x, event.y); - var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { x: 0, y: 1, z: 0 }); + var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { + x: 0, + y: 1, + z: 0 + }); var vector = Vec3.subtract(pick, initialXZPick); // If shifted, constrain to one axis @@ -1490,19 +2316,49 @@ SelectionDisplay = (function () { vector.x = 0; } if (!isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: true }); - var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 }); - var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 }); - var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 }); - var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 }); - Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true }); - Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true }); + Overlays.editOverlay(xRailOverlay, { + visible: true + }); + var xStart = Vec3.sum(startPosition, { + x: -10000, + y: 0, + z: 0 + }); + var xEnd = Vec3.sum(startPosition, { + x: 10000, + y: 0, + z: 0 + }); + var zStart = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: -10000 + }); + var zEnd = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: 10000 + }); + Overlays.editOverlay(xRailOverlay, { + start: xStart, + end: xEnd, + visible: true + }); + Overlays.editOverlay(zRailOverlay, { + start: zStart, + end: zEnd, + visible: true + }); isConstrained = true; } } else { if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); isConstrained = false; } } @@ -1510,14 +2366,18 @@ SelectionDisplay = (function () { constrainMajorOnly = event.isControl; var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, selectionManager.worldDimensions)); vector = Vec3.subtract( - grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), - cornerPosition); + grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), + cornerPosition); var wantDebug = false; for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; - var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, z: vector.z }); + var newPosition = Vec3.sum(properties.position, { + x: vector.x, + y: 0, + z: vector.z + }); Entities.editEntity(SelectionManager.selections[i], { position: newPosition, }); @@ -1533,7 +2393,7 @@ SelectionDisplay = (function () { SelectionManager._update(); } }; - + var lastXYPick = null var upDownPickNormal = null; addGrabberTool(grabberMoveUp, { @@ -1579,11 +2439,11 @@ SelectionDisplay = (function () { var vector = Vec3.subtract(newIntersection, lastXYPick); vector = grid.snapToGrid(vector); - + // we only care about the Y axis vector.x = 0; vector.z = 0; - + var wantDebug = false; if (wantDebug) { print("translateUpDown... "); @@ -1609,12 +2469,16 @@ SelectionDisplay = (function () { }); var vec3Mult = function(v1, v2) { - return { x: v1.x * v2.x, y: v1.y * v2.y, z: v1.z * v2.z }; - } - // stretchMode - name of mode - // direction - direction to stretch in - // pivot - point to use as a pivot - // offset - the position of the overlay tool relative to the selections center position + return { + x: v1.x * v2.x, + y: v1.y * v2.y, + z: v1.z * v2.z + }; + } + // stretchMode - name of mode + // direction - direction to stretch in + // pivot - point to use as a pivot + // offset - the position of the overlay tool relative to the selections center position var makeStretchTool = function(stretchMode, direction, pivot, offset, customOnMove) { var signs = { x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), @@ -1659,7 +2523,11 @@ SelectionDisplay = (function () { } // Modify range of registrationPoint to be [-0.5, 0.5] - var centeredRP = Vec3.subtract(registrationPoint, { x: 0.5, y: 0.5, z: 0.5 }); + var centeredRP = Vec3.subtract(registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); // Scale pivot to be in the same range as registrationPoint var scaledPivot = Vec3.multiply(0.5, pivot) @@ -1675,9 +2543,17 @@ SelectionDisplay = (function () { pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); if (numDimensions == 1 && mask.x) { - var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 }); + var start = Vec3.multiplyQbyV(rotation, { + x: -10000, + y: 0, + z: 0 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 10000, y: 0, z: 0 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 10000, + y: 0, + z: 0 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(xRailOverlay, { start: start, @@ -1686,9 +2562,17 @@ SelectionDisplay = (function () { }); } if (numDimensions == 1 && mask.y) { - var start = Vec3.multiplyQbyV(rotation, { x: 0, y: -10000, z: 0 }); + var start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: -10000, + z: 0 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 10000, z: 0 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 10000, + z: 0 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(yRailOverlay, { start: start, @@ -1697,9 +2581,17 @@ SelectionDisplay = (function () { }); } if (numDimensions == 1 && mask.z) { - var start = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: -10000 }); + var start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: -10000 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: 10000 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: 10000 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(zRailOverlay, { start: start, @@ -1709,26 +2601,50 @@ SelectionDisplay = (function () { } if (numDimensions == 1) { if (mask.x == 1) { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } else if (mask.y == 1) { - planeNormal = { x: 1, y: 0, z: 0 }; + planeNormal = { + x: 1, + y: 0, + z: 0 + }; } else { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } } else if (numDimensions == 2) { if (mask.x == 0) { - planeNormal = { x: 1, y: 0, z: 0 }; + planeNormal = { + x: 1, + y: 0, + z: 0 + }; } else if (mask.y == 0) { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } else { - planeNormal = { x: 0, y: 0, z: z }; + planeNormal = { + x: 0, + y: 0, + z: z + }; } } planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); var pickRay = Camera.computePickRay(event.x, event.y); lastPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + pickRayPosition, + planeNormal); // Overlays.editOverlay(normalLine, { // start: initialPosition, @@ -1739,9 +2655,15 @@ SelectionDisplay = (function () { }; var onEnd = function(event, reason) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(yRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(yRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); pushCommandForSelections(); }; @@ -1762,8 +2684,8 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); newPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + pickRayPosition, + planeNormal); var vector = Vec3.subtract(newPick, lastPick); vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); @@ -1798,14 +2720,14 @@ SelectionDisplay = (function () { } else { newDimensions = Vec3.sum(initialDimensions, changeInDimensions); } - + newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); - + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions)); var newPosition = Vec3.sum(initialPosition, changeInPosition); - + for (var i = 0; i < SelectionManager.selections.length; i++) { Entities.editEntity(SelectionManager.selections[i], { position: newPosition, @@ -1882,7 +2804,11 @@ SelectionDisplay = (function () { size = props.dimensions.z + change.z; } - var newDimensions = { x: size, y: size, z: size }; + var newDimensions = { + x: size, + y: size, + z: size + }; Entities.editEntity(selectionManager.selections[0], { dimensions: newDimensions, @@ -1891,47 +2817,411 @@ SelectionDisplay = (function () { SelectionManager._update(); } - addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); - addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); - addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }); - addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); - addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); + addStretchTool(grabberNEAR, "STRETCH_NEAR", { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }); + addStretchTool(grabberFAR, "STRETCH_FAR", { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: 1 + }); + addStretchTool(grabberTOP, "STRETCH_TOP", { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }); + addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }); + addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { + x: -1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }); + addStretchTool(grabberLEFT, "STRETCH_LEFT", { + x: 1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }); - addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, cutoffStretchFunc); + addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }); + addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { + x: 0, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { + x: 0, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, cutoffStretchFunc); - addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }, radiusStretchFunc); + addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { + x: 0, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { + x: 0, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }, radiusStretchFunc); - addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}, { x: -1, y: -1, z: -1 }); - addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}, { x: 1, y: -1, z: -1 }); - addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}, { x: -1, y: -1, z: 1 }); - addStretchTool(grabberRBF, "STRETCH_RBF", null, {x: -1, y: 0, z: -1}, { x: 1, y: -1, z: 1 }); - addStretchTool(grabberLTN, "STRETCH_LTN", null, {x: 1, y: 0, z: 1}, { x: -1, y: 1, z: -1 }); - addStretchTool(grabberRTN, "STRETCH_RTN", null, {x: -1, y: 0, z: 1}, { x: 1, y: 1, z: -1 }); - addStretchTool(grabberLTF, "STRETCH_LTF", null, {x: 1, y: 0, z: -1}, { x: -1, y: 1, z: 1 }); - addStretchTool(grabberRTF, "STRETCH_RTF", null, {x: -1, y: 0, z: -1}, { x: 1, y: 1, z: 1 }); + addStretchTool(grabberLBN, "STRETCH_LBN", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: -1, + z: -1 + }); + addStretchTool(grabberRBN, "STRETCH_RBN", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: -1, + z: -1 + }); + addStretchTool(grabberLBF, "STRETCH_LBF", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: -1, + z: 1 + }); + addStretchTool(grabberRBF, "STRETCH_RBF", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: -1, + z: 1 + }); + addStretchTool(grabberLTN, "STRETCH_LTN", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: 1, + z: -1 + }); + addStretchTool(grabberRTN, "STRETCH_RTN", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: 1, + z: -1 + }); + addStretchTool(grabberLTF, "STRETCH_LTF", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: 1, + z: 1 + }); + addStretchTool(grabberRTF, "STRETCH_RTF", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: 1, + z: 1 + }); - addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, {x: 1, y: 1, z: 0}, { x: 1, y: 1, z: 0 }); - addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, {x: -1, y: 1, z: 0}, { x: -1, y: 1, z: 0 }); - addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, {x: 0, y: 1, z: -1}, { x: 0, y: 1, z: -1 }); - addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, {x: 0, y: 1, z: 1}, { x: 0, y: 1, z: 1 }); - addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, {x: -1, y: 0, z: 0}, { x: 1, y: -1, z: 0 }); - addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, {x: 1, y: 0, z: 0}, { x: -1, y: -1, z: 0 }); - addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, {x: 0, y: 0, z: -1}, { x: 0, y: -1, z: -1 }); - addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, {x: 0, y: 0, z: 1}, { x: 0, y: -1, z: 1 }); - addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, {x: -1, y: 0, z: 1}, { x: 1, y: 0, z: -1 }); - addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, {x: 1, y: 0, z: 1}, { x: -1, y: 0, z: -1 }); - addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}, { x: 1, y: 0, z: 1 }); - addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}, { x: -1, y: 0, z: 1 }); + addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, { + x: 1, + y: 1, + z: 0 + }, { + x: 1, + y: 1, + z: 0 + }); + addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, { + x: -1, + y: 1, + z: 0 + }, { + x: -1, + y: 1, + z: 0 + }); + addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, { + x: 0, + y: 1, + z: -1 + }, { + x: 0, + y: 1, + z: -1 + }); + addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, { + x: 0, + y: 1, + z: 1 + }, { + x: 0, + y: 1, + z: 1 + }); + addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: -1, + z: 0 + }); + addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: -1, + z: 0 + }); + addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: -1, + z: -1 + }); + addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: -1, + z: 1 + }); + addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: 0, + z: -1 + }); + addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: 0, + z: -1 + }); + addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: 0, + z: 1 + }); + addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: 0, + z: 1 + }); function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { var angle = angleFromZero * (Math.PI / 180); @@ -1967,34 +3257,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2003,19 +3290,35 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, yawHandleRotation, yawCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); + var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2028,7 +3331,11 @@ SelectionDisplay = (function () { var snapToInner = distanceFromCenter < innerRadius; var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + var yawChange = Quat.fromVec3Degrees({ + x: 0, + y: angleFromZero, + z: 0 + }); // Entities should only reposition if we are rotating multiple selections around // the selections center point. Otherwise, the rotation will be around the entities @@ -2066,19 +3373,43 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } - + } } }); @@ -2096,34 +3427,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2132,18 +3460,34 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, pitchHandleRotation, pitchCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2158,8 +3502,12 @@ SelectionDisplay = (function () { var snapToInner = distanceFromCenter < innerRadius; var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - - var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); + + var pitchChange = Quat.fromVec3Degrees({ + x: angleFromZero, + y: 0, + z: 0 + }); for (var i = 0; i < SelectionManager.selections.length; i++) { var entityID = SelectionManager.selections[i]; @@ -2188,17 +3536,41 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } } } @@ -2217,34 +3589,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2253,18 +3622,34 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, rollHandleRotation, rollCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2280,7 +3665,11 @@ SelectionDisplay = (function () { var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); + var rollChange = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: angleFromZero + }); for (var i = 0; i < SelectionManager.selections.length; i++) { var entityID = SelectionManager.selections[i]; var properties = Entities.getEntityProperties(entityID); @@ -2308,17 +3697,41 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } } } @@ -2329,10 +3742,10 @@ SelectionDisplay = (function () { // FIXME - this cause problems with editing in the entity properties window //SelectionManager._update(); - + if (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation)) { - + that.updateRotationHandles(); } } @@ -2347,12 +3760,20 @@ SelectionDisplay = (function () { var somethingClicked = false; var pickRay = Camera.computePickRay(event.x, event.y); - + // before we do a ray test for grabbers, disable the ray intersection for our selection box - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: true }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: true }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: true + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2377,14 +3798,16 @@ SelectionDisplay = (function () { activeTool.onBegin(event); } } else { - switch(result.overlayID) { + switch (result.overlayID) { case grabberMoveUp: mode = "TRANSLATE_UP_DOWN"; somethingClicked = true; // in translate mode, we hide our stretch handles... for (var i = 0; i < stretchHandles.length; i++) { - Overlays.editOverlay(stretchHandles[i], { visible: false }); + Overlays.editOverlay(stretchHandles[i], { + visible: false + }); } break; @@ -2397,8 +3820,8 @@ SelectionDisplay = (function () { break; case grabberFAR: - case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? - case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? + case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? + case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? mode = "STRETCH_FAR"; somethingClicked = true; break; @@ -2411,14 +3834,14 @@ SelectionDisplay = (function () { somethingClicked = true; break; case grabberRIGHT: - case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? - case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? + case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? + case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? mode = "STRETCH_RIGHT"; somethingClicked = true; break; case grabberLEFT: - case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? - case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? + case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? + case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? mode = "STRETCH_LEFT"; somethingClicked = true; break; @@ -2429,29 +3852,43 @@ SelectionDisplay = (function () { } } } - + // if one of the items above was clicked, then we know we are in translate or stretch mode, and we // should hide our rotate handles... if (somethingClicked) { - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); + if (mode != "TRANSLATE_UP_DOWN") { - Overlays.editOverlay(grabberMoveUp, { visible: false }); + Overlays.editOverlay(grabberMoveUp, { + visible: false + }); } } - + if (!somethingClicked) { - + print("rotate handle case..."); - + // After testing our stretch handles, then check out rotate handles - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); - + var overlayOrientation; var overlayCenter; @@ -2460,12 +3897,12 @@ SelectionDisplay = (function () { var pitch = angles.x; var yaw = angles.y; var roll = angles.z; - + originalRotation = properties.rotation; originalPitch = pitch; originalYaw = yaw; originalRoll = roll; - + if (result.intersects) { var tool = grabberTools[result.overlayID]; if (tool) { @@ -2476,7 +3913,7 @@ SelectionDisplay = (function () { activeTool.onBegin(event); } } - switch(result.overlayID) { + switch (result.overlayID) { case yawHandle: mode = "ROTATE_YAW"; somethingClicked = true; @@ -2514,59 +3951,147 @@ SelectionDisplay = (function () { print(" somethingClicked:" + somethingClicked); print(" mode:" + mode); - + if (somethingClicked) { - - Overlays.editOverlay(rotateOverlayTarget, { visible: true, rotation: overlayOrientation, position: overlayCenter }); - Overlays.editOverlay(rotateOverlayInner, { visible: true, rotation: overlayOrientation, position: overlayCenter }); - Overlays.editOverlay(rotateOverlayOuter, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 0 }); - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); + + Overlays.editOverlay(rotateOverlayTarget, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter, + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter, + startAt: 0, + endAt: 0 + }); + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - Overlays.editOverlay(grabberMoveUp, { visible: false }); - Overlays.editOverlay(grabberLBN, { visible: false }); - Overlays.editOverlay(grabberLBF, { visible: false }); - Overlays.editOverlay(grabberRBN, { visible: false }); - Overlays.editOverlay(grabberRBF, { visible: false }); - Overlays.editOverlay(grabberLTN, { visible: false }); - Overlays.editOverlay(grabberLTF, { visible: false }); - Overlays.editOverlay(grabberRTN, { visible: false }); - Overlays.editOverlay(grabberRTF, { visible: false }); + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); + Overlays.editOverlay(grabberMoveUp, { + visible: false + }); + Overlays.editOverlay(grabberLBN, { + visible: false + }); + Overlays.editOverlay(grabberLBF, { + visible: false + }); + Overlays.editOverlay(grabberRBN, { + visible: false + }); + Overlays.editOverlay(grabberRBF, { + visible: false + }); + Overlays.editOverlay(grabberLTN, { + visible: false + }); + Overlays.editOverlay(grabberLTF, { + visible: false + }); + Overlays.editOverlay(grabberRTN, { + visible: false + }); + Overlays.editOverlay(grabberRTF, { + visible: false + }); - Overlays.editOverlay(grabberTOP, { visible: false }); - Overlays.editOverlay(grabberBOTTOM, { visible: false }); - Overlays.editOverlay(grabberLEFT, { visible: false }); - Overlays.editOverlay(grabberRIGHT, { visible: false }); - Overlays.editOverlay(grabberNEAR, { visible: false }); - Overlays.editOverlay(grabberFAR, { visible: false }); + Overlays.editOverlay(grabberTOP, { + visible: false + }); + Overlays.editOverlay(grabberBOTTOM, { + visible: false + }); + Overlays.editOverlay(grabberLEFT, { + visible: false + }); + Overlays.editOverlay(grabberRIGHT, { + visible: false + }); + Overlays.editOverlay(grabberNEAR, { + visible: false + }); + Overlays.editOverlay(grabberFAR, { + visible: false + }); - Overlays.editOverlay(grabberEdgeTR, { visible: false }); - Overlays.editOverlay(grabberEdgeTL, { visible: false }); - Overlays.editOverlay(grabberEdgeTF, { visible: false }); - Overlays.editOverlay(grabberEdgeTN, { visible: false }); - Overlays.editOverlay(grabberEdgeBR, { visible: false }); - Overlays.editOverlay(grabberEdgeBL, { visible: false }); - Overlays.editOverlay(grabberEdgeBF, { visible: false }); - Overlays.editOverlay(grabberEdgeBN, { visible: false }); - Overlays.editOverlay(grabberEdgeNR, { visible: false }); - Overlays.editOverlay(grabberEdgeNL, { visible: false }); - Overlays.editOverlay(grabberEdgeFR, { visible: false }); - Overlays.editOverlay(grabberEdgeFL, { visible: false }); + Overlays.editOverlay(grabberEdgeTR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTF, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTN, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBF, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBN, { + visible: false + }); + Overlays.editOverlay(grabberEdgeNR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeNL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeFR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeFL, { + visible: false + }); } } if (!somethingClicked) { - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - switch(result.overlayID) { + switch (result.overlayID) { case selectionBox: activeTool = translateXZTool; mode = translateXZTool.mode; @@ -2584,17 +4109,25 @@ SelectionDisplay = (function () { if (somethingClicked) { pickRay = Camera.computePickRay(event.x, event.y); if (wantDebug) { - print("mousePressEvent()...... " + overlayNames[result.overlayID]); + print("mousePressEvent()...... " + overlayNames[result.overlayID]); } } // reset everything as intersectable... // TODO: we could optimize this since some of these were already flipped back - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: false }); - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); - + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: false + }); + return somethingClicked; }; @@ -2613,7 +4146,7 @@ SelectionDisplay = (function () { var highlightNeeded = false; if (result.intersects) { - switch(result.overlayID) { + switch (result.overlayID) { case yawHandle: case pitchHandle: case rollHandle: @@ -2621,7 +4154,7 @@ SelectionDisplay = (function () { pickedAlpha = handleAlpha; highlightNeeded = true; break; - + case grabberMoveUp: pickedColor = handleColor; pickedAlpha = handleAlpha; @@ -2682,30 +4215,42 @@ SelectionDisplay = (function () { default: if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } break; } - + if (highlightNeeded) { if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } - Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha }); + Overlays.editOverlay(result.overlayID, { + color: highlightedHandleColor, + alpha: highlightedHandleAlpha + }); previousHandle = result.overlayID; previousHandleColor = pickedColor; previousHandleAlpha = pickedAlpha; } - + } else { if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } } - + return false; }; @@ -2728,7 +4273,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(rollHandle, { scale: handleSize, }); - var pos = Vec3.sum(grabberMoveUpPosition, { x: 0, y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, z: 0 }); + var pos = Vec3.sum(grabberMoveUpPosition, { + x: 0, + y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, + z: 0 + }); Overlays.editOverlay(grabberMoveUp, { position: pos, scale: handleSize / 2, @@ -2745,34 +4294,43 @@ SelectionDisplay = (function () { activeTool = null; // hide our rotation overlays..., and show our handles if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { - Overlays.editOverlay(rotateOverlayTarget, { visible: false }); - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { + visible: false + }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); showHandles = true; } if (mode != "UNKNOWN") { showHandles = true; } - + mode = "UNKNOWN"; - + // if something is selected, then reset the "original" properties for any potential next click+move operation if (SelectionManager.hasSelection()) { if (showHandles) { that.select(SelectionManager.selections[0], event); } } - + }; // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: // Controller.mousePressEvent.connect(that.mousePressEvent); // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); - + + + return that; -}()); - +}()); \ No newline at end of file diff --git a/examples/libraries/lightOverlayManager.js b/examples/libraries/lightOverlayManager.js index 0942fae723..2d3618096b 100644 --- a/examples/libraries/lightOverlayManager.js +++ b/examples/libraries/lightOverlayManager.js @@ -53,7 +53,9 @@ LightOverlayManager = function() { if (visible != isVisible) { visible = isVisible; for (var id in entityOverlays) { - Overlays.editOverlay(entityOverlays[id], { visible: visible }); + Overlays.editOverlay(entityOverlays[id], { + visible: visible + }); } } }; @@ -61,8 +63,7 @@ LightOverlayManager = function() { // Allocate or get an unused overlay function getOverlay() { if (unusedOverlays.length == 0) { - var overlay = Overlays.addOverlay("image3d", { - }); + var overlay = Overlays.addOverlay("image3d", {}); allOverlays.push(overlay); } else { var overlay = unusedOverlays.pop(); @@ -72,7 +73,9 @@ LightOverlayManager = function() { function releaseOverlay(overlay) { unusedOverlays.push(overlay); - Overlays.editOverlay(overlay, { visible: false }); + Overlays.editOverlay(overlay, { + visible: false + }); } function addEntity(entityID) { @@ -88,7 +91,11 @@ LightOverlayManager = function() { visible: visible, alpha: 0.9, scale: 0.5, - color: { red: 255, green: 255, blue: 255 } + color: { + red: 255, + green: 255, + blue: 255 + } }); } } @@ -123,4 +130,4 @@ LightOverlayManager = function() { Overlays.deleteOverlay(allOverlays[i]); } }); -}; +}; \ No newline at end of file diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 5d14bfb7dd..115c1bcb65 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -271,3 +271,29 @@ hexToRgb = function(hex) { } : null; } +calculateHandSizeRatio = function() { + // Get the ratio of the current avatar's hand to Owen's hand + + var standardCenterHandPoint = 0.11288; + var jointNames = MyAvatar.getJointNames(); + //get distance from handJoint up to leftHandIndex3 as a proxy for center of hand + var wristToFingertipDistance = 0;; + for (var i = 0; i < jointNames.length; i++) { + var jointName = jointNames[i]; + print(jointName) + if (jointName.indexOf("LeftHandIndex") !== -1) { + // translations are relative to parent joint, so simply add them together + // joints face down the y-axis + var translation = MyAvatar.getDefaultJointTranslation(i).y; + wristToFingertipDistance += translation; + } + } + // Right now units are in cm, so convert to meters + wristToFingertipDistance /= 100; + + var centerHandPoint = wristToFingertipDistance/2; + + // Compare against standard hand (Owen) + var handSizeRatio = centerHandPoint/standardCenterHandPoint; + return handSizeRatio; +} diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md new file mode 100644 index 0000000000..f23f22148a --- /dev/null +++ b/examples/light_modifier/README.md @@ -0,0 +1,29 @@ +This PR demonstrates one way in-world editing of objects might work. + +Running this script will show light overlay icons in-world. Enter edit mode by running your distance beam through a light overlay. Exit using the red X. + +When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached. + +To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js +To reset, I recommend stopping all scripts then re-loading lightLoader.js + +When you run the lightLoader.js script, several scripts will be loaded: +- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end, will support blacklisting of entities for raypicking during search for objects) +- lightModifier.js (listens for message to create sliders for a given light. will start with slider set to the light's initial properties) +- lightModifierTestScene.js (creates a light) +- slider.js (attached to each slider entity) +- lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent) +- visiblePanel.js (the transparent panel) +- closeButton.js (for closing the ui) +- ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world) +- ../libraries/entitySelectionTool.js (visualizes volume of the lights) + +Current sliders are (top to bottom): +red +green +blue +intensity +cutoff +exponent + +![capture](https://cloud.githubusercontent.com/assets/843228/11910139/afaaf1ae-a5a5-11e5-8b66-0eb3fc6976df.PNG) diff --git a/examples/light_modifier/closeButton.js b/examples/light_modifier/closeButton.js new file mode 100644 index 0000000000..72fcfbc382 --- /dev/null +++ b/examples/light_modifier/closeButton.js @@ -0,0 +1,36 @@ +// +// closeButton.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that closes sliders when interacted with. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + function CloseButton() { + return this; + } + + CloseButton.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + this.initialProperties = entityProperties + this.userData = JSON.parse(entityProperties.userData); + }, + startNearGrab: function() { + + }, + startFarTrigger: function() { + Messages.sendMessage('Hifi-Light-Modifier-Cleanup', 'callCleanup') + } + + }; + + return new CloseButton(); +}); \ No newline at end of file diff --git a/examples/light_modifier/lightLoader.js b/examples/light_modifier/lightLoader.js new file mode 100644 index 0000000000..83618f85c2 --- /dev/null +++ b/examples/light_modifier/lightLoader.js @@ -0,0 +1,20 @@ +// +// lightLoader.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Loads a test scene showing sliders that you can grab and move to change entity properties. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var grabScript = Script.resolvePath('../controllers/handControllerGrab.js?' + Math.random(0 - 100)); +Script.load(grabScript); +var lightModifier = Script.resolvePath('lightModifier.js?' + Math.random(0 - 100)); +Script.load(lightModifier); +Script.setTimeout(function() { + var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js?' + Math.random(0 - 100)); + Script.load(lightModifierTestScene); +}, 750) \ No newline at end of file diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js new file mode 100644 index 0000000000..b50bbe9478 --- /dev/null +++ b/examples/light_modifier/lightModifier.js @@ -0,0 +1,876 @@ +// +// lightModifier.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +//some experimental options +var ONLY_I_CAN_EDIT = false; +var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; +var VERTICAL_SLIDERS = false; +var SHOW_OVERLAYS = true; +var SHOW_LIGHT_VOLUME = true; +var USE_PARENTED_PANEL = true; +var VISIBLE_PANEL = true; +var USE_LABELS = true; +var LEFT_LABELS = false; +var RIGHT_LABELS = true; +var ROTATE_CLOSE_BUTTON = false; + +//variables for managing overlays +var selectionDisplay; +var selectionManager; +var lightOverlayManager; + +//for when we make a 3d model of a light a parent for the light +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); + +if (SHOW_OVERLAYS === true) { + + Script.include('../libraries/gridTool.js'); + Script.include('../libraries/entitySelectionTool.js?' + Math.random(0 - 100)); + Script.include('../libraries/lightOverlayManager.js'); + + var grid = Grid(); + gridTool = GridTool({ + horizontalGrid: grid + }); + gridTool.setVisible(false); + + selectionDisplay = SelectionDisplay; + selectionManager = SelectionManager; + lightOverlayManager = new LightOverlayManager(); + selectionManager.addEventListener(function() { + selectionDisplay.updateHandles(); + lightOverlayManager.updatePositions(); + }); + lightOverlayManager.setVisible(true); +} + +var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}' + +var AXIS_SCALE = 1; +var COLOR_MAX = 255; +var INTENSITY_MAX = 0.05; +var CUTOFF_MAX = 360; +var EXPONENT_MAX = 1; + +var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); +var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_very_good.fbx'; +var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; +var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); +var TRANSPARENT_PANEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/transparent_box_alpha_15.fbx'; +var VISIBLE_PANEL_SCRIPT_URL = Script.resolvePath('visiblePanel.js?' + Math.random(0, 100)); + +var RED = { + red: 255, + green: 0, + blue: 0 +}; + +var GREEN = { + red: 0, + green: 255, + blue: 0 +}; + +var BLUE = { + red: 0, + green: 0, + blue: 255 +}; + +var PURPLE = { + red: 255, + green: 0, + blue: 255 +}; + +var WHITE = { + red: 255, + green: 255, + blue: 255 +}; + +var ORANGE = { + red: 255, + green: 165, + blue: 0 +} + +var SLIDER_DIMENSIONS = { + x: 0.075, + y: 0.075, + z: 0.075 +}; + +var CLOSE_BUTTON_DIMENSIONS = { + x: 0.1, + y: 0.025, + z: 0.1 +} + +var LIGHT_MODEL_DIMENSIONS = { + x: 0.58, + y: 1.21, + z: 0.57 +} + +var PER_ROW_OFFSET = { + x: 0, + y: -0.2, + z: 0 +}; +var sliders = []; +var slidersRef = { + 'color_red': null, + 'color_green': null, + 'color_blue': null, + intensity: null, + cutoff: null, + exponent: null +}; +var light = null; + +var basePosition; +var avatarRotation; + +function entitySlider(light, color, sliderType, displayText, row) { + this.light = light; + this.lightID = light.id.replace(/[{}]/g, ""); + this.initialProperties = light.initialProperties; + this.color = color; + this.sliderType = sliderType; + this.displayText = displayText; + this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); + this.basePosition.y += 1; + basePosition = this.basePosition; + avatarRot = this.avatarRot; + + var message = { + lightID: this.lightID, + sliderType: this.sliderType, + sliderValue: null + }; + + if (this.sliderType === 'color_red') { + message.sliderValue = this.initialProperties.color.red + this.setValueFromMessage(message); + } + if (this.sliderType === 'color_green') { + message.sliderValue = this.initialProperties.color.green + this.setValueFromMessage(message); + } + if (this.sliderType === 'color_blue') { + message.sliderValue = this.initialProperties.color.blue + this.setValueFromMessage(message); + } + + if (this.sliderType === 'intensity') { + message.sliderValue = this.initialProperties.intensity + this.setValueFromMessage(message); + } + + if (this.sliderType === 'exponent') { + message.sliderValue = this.initialProperties.exponent + this.setValueFromMessage(message); + } + + if (this.sliderType === 'cutoff') { + message.sliderValue = this.initialProperties.cutoff + this.setValueFromMessage(message); + } + + this.setInitialSliderPositions(); + this.createAxis(); + this.createSliderIndicator(); + if (USE_LABELS === true) { + this.createLabel() + } + return this; +} + +//what's the ux for adjusting values? start with simple entities, try image overlays etc +entitySlider.prototype = { + createAxis: function() { + //start of line + var position; + var extension; + + if (VERTICAL_SLIDERS == true) { + position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot))))); + //line starts on bottom and goes up + var upVector = Quat.getUp(this.avatarRot); + extension = Vec3.multiply(AXIS_SCALE, upVector); + } else { + position = Vec3.sum(this.basePosition, this.verticalOffset); + //line starts on left and goes to right + //set the end of the line to the right + var rightVector = Quat.getRight(this.avatarRot); + extension = Vec3.multiply(AXIS_SCALE, rightVector); + } + + + this.axisStart = position; + this.endOfAxis = Vec3.sum(position, extension); + this.createEndOfAxisEntity(); + + var properties = { + type: 'Line', + name: 'Hifi-Slider-Axis::' + this.sliderType, + color: this.color, + collisionsWillMove: false, + ignoreForCollisions: true, + dimensions: { + x: 3, + y: 3, + z: 3 + }, + position: position, + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, extension], + lineWidth: 5, + }; + + this.axis = Entities.addEntity(properties); + }, + createEndOfAxisEntity: function() { + //we use this to track the end of the axis while parented to a panel + var properties = { + name: 'Hifi-End-Of-Axis', + type: 'Box', + collisionsWillMove: false, + ignoreForCollisions: true, + dimensions: { + x: 0.01, + y: 0.01, + z: 0.01 + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + position: this.endOfAxis, + parentID: this.axis, + visible: false + } + + this.endOfAxisEntity = Entities.addEntity(this.endOfAxis); + }, + createLabel: function() { + + var LABEL_WIDTH = 0.25 + var PER_LETTER_SPACING = 0.1; + var textWidth = this.displayText.length * PER_LETTER_SPACING; + + var position; + if (LEFT_LABELS === true) { + var leftVector = Vec3.multiply(-1, Quat.getRight(this.avatarRot)); + + var extension = Vec3.multiply(textWidth, leftVector); + + position = Vec3.sum(this.axisStart, extension); + } + + if (RIGHT_LABELS === true) { + var rightVector = Quat.getRight(this.avatarRot); + + var extension = Vec3.multiply(textWidth / 1.75, rightVector); + + position = Vec3.sum(this.endOfAxis, extension); + } + + + var labelProperties = { + name: 'Hifi-Slider-Label-' + this.sliderType, + type: 'Text', + dimensions: { + x: textWidth, + y: 0.2, + z: 0.1 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + text: this.displayText, + lineHeight: 0.14, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + position: position, + rotation: this.avatarRot, + } + print('BEFORE CREATE LABEL' + JSON.stringify(labelProperties)) + this.label = Entities.addEntity(labelProperties); + print('AFTER CREATE LABEL') + }, + createSliderIndicator: function() { + var extensionVector; + var position; + if (VERTICAL_SLIDERS == true) { + position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot))))); + extensionVector = Quat.getUp(this.avatarRot); + + } else { + position = Vec3.sum(this.basePosition, this.verticalOffset); + extensionVector = Quat.getRight(this.avatarRot); + + } + + var initialDistance; + if (this.sliderType === 'color_red') { + initialDistance = this.distanceRed; + } + if (this.sliderType === 'color_green') { + initialDistance = this.distanceGreen; + } + if (this.sliderType === 'color_blue') { + initialDistance = this.distanceBlue; + } + if (this.sliderType === 'intensity') { + initialDistance = this.distanceIntensity; + } + if (this.sliderType === 'cutoff') { + initialDistance = this.distanceCutoff; + } + if (this.sliderType === 'exponent') { + initialDistance = this.distanceExponent; + } + + var extension = Vec3.multiply(initialDistance, extensionVector); + var sliderPosition = Vec3.sum(position, extension); + + var properties = { + type: 'Sphere', + name: 'Hifi-Slider-' + this.sliderType, + dimensions: SLIDER_DIMENSIONS, + collisionsWillMove: true, + color: this.color, + position: sliderPosition, + script: SLIDER_SCRIPT_URL, + ignoreForCollisions: true, + userData: JSON.stringify({ + lightModifierKey: { + lightID: this.lightID, + sliderType: this.sliderType, + axisStart: position, + axisEnd: this.endOfAxis, + }, + handControllerKey: { + disableReleaseVelocity: true, + disableMoveWithHead: true, + disableNearGrab:true + } + }), + }; + + this.sliderIndicator = Entities.addEntity(properties); + }, + setValueFromMessage: function(message) { + + //message is not for our light + if (message.lightID !== this.lightID) { + // print('not our light') + return; + } + + //message is not our type + if (message.sliderType !== this.sliderType) { + // print('not our slider type') + return + } + + var lightProperties = Entities.getEntityProperties(this.lightID); + + if (this.sliderType === 'color_red') { + Entities.editEntity(this.lightID, { + color: { + red: message.sliderValue, + green: lightProperties.color.green, + blue: lightProperties.color.blue + } + }); + } + + if (this.sliderType === 'color_green') { + Entities.editEntity(this.lightID, { + color: { + red: lightProperties.color.red, + green: message.sliderValue, + blue: lightProperties.color.blue + } + }); + } + + if (this.sliderType === 'color_blue') { + Entities.editEntity(this.lightID, { + color: { + red: lightProperties.color.red, + green: lightProperties.color.green, + blue: message.sliderValue, + } + }); + } + + if (this.sliderType === 'intensity') { + Entities.editEntity(this.lightID, { + intensity: message.sliderValue + }); + } + + if (this.sliderType === 'cutoff') { + Entities.editEntity(this.lightID, { + cutoff: message.sliderValue + }); + } + + if (this.sliderType === 'exponent') { + Entities.editEntity(this.lightID, { + exponent: message.sliderValue + }); + } + }, + setInitialSliderPositions: function() { + this.distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE; + this.distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE; + this.distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE; + this.distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE; + this.distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE; + this.distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE; + } + +}; + + +var panel; +var visiblePanel; + +function makeSliders(light) { + + if (USE_PARENTED_PANEL === true) { + panel = createPanelEntity(MyAvatar.position); + } + + if (light.type === 'spotlight') { + var USE_COLOR_SLIDER = true; + var USE_INTENSITY_SLIDER = true; + var USE_CUTOFF_SLIDER = true; + var USE_EXPONENT_SLIDER = true; + } + if (light.type === 'pointlight') { + var USE_COLOR_SLIDER = true; + var USE_INTENSITY_SLIDER = true; + var USE_CUTOFF_SLIDER = false; + var USE_EXPONENT_SLIDER = false; + } + if (USE_COLOR_SLIDER === true) { + slidersRef.color_red = new entitySlider(light, RED, 'color_red', 'Red', 1); + slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 'Green', 2); + slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 'Blue', 3); + + sliders.push(slidersRef.color_red); + sliders.push(slidersRef.color_green); + sliders.push(slidersRef.color_blue); + + } + if (USE_INTENSITY_SLIDER === true) { + slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 'Intensity', 4); + sliders.push(slidersRef.intensity); + } + if (USE_CUTOFF_SLIDER === true) { + slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 'Cutoff', 5); + sliders.push(slidersRef.cutoff); + } + if (USE_EXPONENT_SLIDER === true) { + slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 'Exponent', 6); + sliders.push(slidersRef.exponent); + } + + createCloseButton(slidersRef.color_red.axisStart); + + subscribeToSliderMessages(); + + if (USE_PARENTED_PANEL === true) { + parentEntitiesToPanel(panel); + } + + if (SLIDERS_SHOULD_STAY_WITH_AVATAR === true) { + parentPanelToAvatar(panel); + } + + if (VISIBLE_PANEL === true) { + visiblePanel = createVisiblePanel(); + } +}; + +function parentPanelToAvatar(panel) { + //this is going to need some more work re: the sliders actually being grabbable. probably something to do with updating axis movement + Entities.editEntity(panel, { + parentID: MyAvatar.sessionUUID, + //actually figure out which one to parent it to -- probably a spine or something. + parentJointIndex: 1, + }) +} + + +function parentEntitiesToPanel(panel) { + + sliders.forEach(function(slider) { + Entities.editEntity(slider.axis, { + parentID: panel + }) + Entities.editEntity(slider.sliderIndicator, { + parentID: panel + }) + }) + + closeButtons.forEach(function(button) { + Entities.editEntity(button, { + parentID: panel + }) + }) +} + +function createPanelEntity(position) { + print('CREATING PANEL at ' + JSON.stringify(position)); + var panelProperties = { + name: 'Hifi-Slider-Panel', + type: 'Box', + dimensions: { + x: 0.1, + y: 0.1, + z: 0.1 + }, + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true + } + + var panel = Entities.addEntity(panelProperties); + return panel +} + +function createVisiblePanel() { + var totalOffset = -PER_ROW_OFFSET.y * sliders.length; + + var moveRight = Vec3.sum(basePosition, Vec3.multiply(AXIS_SCALE / 2, Quat.getRight(avatarRot))); + + var moveDown = Vec3.sum(moveRight, Vec3.multiply((sliders.length + 1) / 2, PER_ROW_OFFSET)) + var panelProperties = { + name: 'Hifi-Visible-Transparent-Panel', + type: 'Model', + modelURL: TRANSPARENT_PANEL_URL, + dimensions: { + x: AXIS_SCALE + 0.1, + y: totalOffset, + z: SLIDER_DIMENSIONS.z / 4 + }, + visible: true, + collisionsWillMove: false, + ignoreForCollisions: true, + position: moveDown, + rotation: avatarRot, + script: VISIBLE_PANEL_SCRIPT_URL + } + + var panel = Entities.addEntity(panelProperties); + + return panel +} + + +function createLightModel(position, rotation) { + var blockProperties = { + name: 'Hifi-Spotlight-Model', + type: 'Model', + shapeType: 'box', + modelURL: LIGHT_MODEL_URL, + dimensions: LIGHT_MODEL_DIMENSIONS, + collisionsWillMove: true, + position: position, + rotation: rotation, + script: PARENT_SCRIPT_URL, + userData: JSON.stringify({ + handControllerKey: { + disableReleaseVelocity: true + } + }) + }; + + var block = Entities.addEntity(blockProperties); + + return block +} + +var closeButtons = []; + +function createCloseButton(axisStart) { + var MARGIN = 0.10; + var VERTICAL_OFFFSET = { + x: 0, + y: 0.15, + z: 0 + }; + var leftVector = Vec3.multiply(-1, Quat.getRight(avatarRot)); + var extension = Vec3.multiply(MARGIN, leftVector); + var position = Vec3.sum(axisStart, extension); + + var buttonProperties = { + name: 'Hifi-Close-Button', + type: 'Model', + modelURL: CLOSE_BUTTON_MODEL_URL, + dimensions: CLOSE_BUTTON_DIMENSIONS, + position: Vec3.sum(position, VERTICAL_OFFFSET), + rotation: Quat.multiply(avatarRot, Quat.fromPitchYawRollDegrees(90, 0, 45)), + //rotation: Quat.fromPitchYawRollDegrees(0, 0, 90), + collisionsWillMove: false, + ignoreForCollisions: true, + script: CLOSE_BUTTON_SCRIPT_URL, + userData: JSON.stringify({ + grabbableKey: { + wantsTrigger: true + } + }) + } + + var button = Entities.addEntity(buttonProperties); + + closeButtons.push(button); + + if (ROTATE_CLOSE_BUTTON === true) { + Script.update.connect(rotateCloseButtons); + } +} + +function rotateCloseButtons() { + closeButtons.forEach(function(button) { + Entities.editEntity(button, { + angularVelocity: { + x: 0, + y: 0.5, + z: 0 + } + }) + + }) +} + +function subScribeToNewLights() { + Messages.subscribe('Hifi-Light-Mod-Receiver'); + Messages.messageReceived.connect(handleLightModMessages); +} + +function subscribeToSliderMessages() { + Messages.subscribe('Hifi-Slider-Value-Reciever'); + Messages.messageReceived.connect(handleValueMessages); +} + +function subscribeToLightOverlayRayCheckMessages() { + Messages.subscribe('Hifi-Light-Overlay-Ray-Check'); + Messages.messageReceived.connect(handleLightOverlayRayCheckMessages); +} + +function subscribeToCleanupMessages() { + Messages.subscribe('Hifi-Light-Modifier-Cleanup'); + Messages.messageReceived.connect(handleCleanupMessages); +} + + +function handleLightModMessages(channel, message, sender) { + if (channel !== 'Hifi-Light-Mod-Receiver') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + var parsedMessage = JSON.parse(message); + + makeSliders(parsedMessage.light); + light = parsedMessage.light.id + if (SHOW_LIGHT_VOLUME === true) { + selectionManager.setSelections([parsedMessage.light.id]); + } +} + +function handleValueMessages(channel, message, sender) { + + if (channel !== 'Hifi-Slider-Value-Reciever') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + var parsedMessage = JSON.parse(message); + + slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage); +} + +var currentLight; +var block; +var oldParent = null; +var hasParent = false; + +function handleLightOverlayRayCheckMessages(channel, message, sender) { + if (channel !== 'Hifi-Light-Overlay-Ray-Check') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + + var pickRay = JSON.parse(message); + + var doesIntersect = lightOverlayManager.findRayIntersection(pickRay); + // print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects); + if (doesIntersect.intersects === true) { + // print('FULL MESSAGE:::' + JSON.stringify(doesIntersect)) + + var lightID = doesIntersect.entityID; + if (currentLight === lightID) { + // print('ALREADY HAVE A BLOCK, EXIT') + return; + } + + currentLight = lightID; + var lightProperties = Entities.getEntityProperties(lightID); + if (lightProperties.parentID !== DEFAULT_PARENT_ID) { + //this light has a parent already. so lets call our block the parent and then make sure not to delete it at the end; + oldParent = lightProperties.parentID; + hasParent = true; + block = lightProperties.parentID; + if (lightProperties.parentJointIndex !== -1) { + //should make sure to retain the parent too. but i don't actually know what the + } + } else { + block = createLightModel(lightProperties.position, lightProperties.rotation); + } + + var light = { + id: lightID, + type: 'spotlight', + initialProperties: lightProperties + } + + makeSliders(light); + + if (SHOW_LIGHT_VOLUME === true) { + selectionManager.setSelections([lightID]); + } + + Entities.editEntity(lightID, { + parentID: block, + parentJointIndex: -1 + }); + + } +} + +function handleCleanupMessages(channel, message, sender) { + + if (channel !== 'Hifi-Light-Modifier-Cleanup') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + if (message === 'callCleanup') { + cleanup(true); + } +} + +function updateSliderAxis() { + sliders.forEach(function(slider) { + + }) +} + +function cleanup(fromMessage) { + var i; + for (i = 0; i < sliders.length; i++) { + Entities.deleteEntity(sliders[i].axis); + Entities.deleteEntity(sliders[i].sliderIndicator); + Entities.deleteEntity(sliders[i].label); + } + + while (closeButtons.length > 0) { + Entities.deleteEntity(closeButtons.pop()); + } + + //if the light was already parented to something we will want to restore that. or come up with groups or something clever. + if (oldParent !== null) { + Entities.editEntity(currentLight, { + parentID: oldParent, + }); + } else { + Entities.editEntity(currentLight, { + parentID: null, + }); + } + + + if (fromMessage !== true) { + Messages.messageReceived.disconnect(handleLightModMessages); + Messages.messageReceived.disconnect(handleValueMessages); + Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages); + lightOverlayManager.setVisible(false); + } + + + Entities.deleteEntity(panel); + Entities.deleteEntity(visiblePanel); + + selectionManager.clearSelections(); + + if (ROTATE_CLOSE_BUTTON === true) { + Script.update.disconnect(rotateCloseButtons); + } + + if (hasParent === false) { + Entities.deleteEntity(block); + } + + oldParent = null; + hasParent = false; + currentLight = null; + sliders = []; + +} + +Script.scriptEnding.connect(cleanup); + +Script.scriptEnding.connect(function() { + lightOverlayManager.setVisible(false); +}) + + +subscribeToLightOverlayRayCheckMessages(); +subScribeToNewLights(); +subscribeToCleanupMessages(); + + + +//other light properties +// diffuseColor: { red: 255, green: 255, blue: 255 }, +// ambientColor: { red: 255, green: 255, blue: 255 }, +// specularColor: { red: 255, green: 255, blue: 255 }, +// constantAttenuation: 1, +// linearAttenuation: 0, +// quadraticAttenuation: 0, +// exponent: 0, +// cutoff: 180, // in degrees \ No newline at end of file diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js new file mode 100644 index 0000000000..58956850f2 --- /dev/null +++ b/examples/light_modifier/lightModifierTestScene.js @@ -0,0 +1,73 @@ +// +// lightModifierTestScene.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Given a selected light, instantiate some entities that represent various values you can dynamically adjust. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); +var basePosition, avatarRot; +avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); +basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot))); + +var light; + +function createLight() { + var position = basePosition; + position.y += 2; + var lightTransform = evalLightWorldTransform(position, avatarRot); + var lightProperties = { + name: 'Hifi-Spotlight', + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 8 + }, + color: { + red: 255, + green: 0, + blue: 255 + }, + intensity: 0.035, + exponent: 1, + cutoff: 30, + lifetime: -1, + position: lightTransform.p, + rotation: lightTransform.q + }; + + light = Entities.addEntity(lightProperties); + +} + +function evalLightWorldTransform(modelPos, modelRot) { + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; +} + +function cleanup() { + Entities.deleteEntity(light); +} + +Script.scriptEnding.connect(cleanup); + +createLight(); \ No newline at end of file diff --git a/examples/light_modifier/lightParent.js b/examples/light_modifier/lightParent.js new file mode 100644 index 0000000000..2b53c05d0a --- /dev/null +++ b/examples/light_modifier/lightParent.js @@ -0,0 +1,40 @@ +// +// lightParent.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that tells the light parent to update the selection tool when we move it. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + (function() { + + function LightParent() { + return this; + } + + LightParent.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + this.initialProperties = entityProperties + this.userData = JSON.parse(entityProperties.userData); + }, + startNearGrab: function() {}, + startDistantGrab: function() { + + }, + continueNearGrab: function() { + this.continueDistantGrab(); + }, + continueDistantGrab: function() { + Messages.sendMessage('entityToolUpdates', 'callUpdate'); + }, + + }; + + return new LightParent(); + }); \ No newline at end of file diff --git a/examples/light_modifier/slider.js b/examples/light_modifier/slider.js new file mode 100644 index 0000000000..e1dfea4e87 --- /dev/null +++ b/examples/light_modifier/slider.js @@ -0,0 +1,105 @@ +// +// slider.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + var AXIS_SCALE = 1; + var COLOR_MAX = 255; + var INTENSITY_MAX = 0.05; + var CUTOFF_MAX = 360; + var EXPONENT_MAX = 1; + + function Slider() { + return this; + } + + Slider.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + var parsedUserData = JSON.parse(entityProperties.userData); + this.userData = parsedUserData.lightModifierKey; + }, + startNearGrab: function() { + this.setInitialProperties(); + }, + startDistantGrab: function() { + this.setInitialProperties(); + }, + setInitialProperties: function() { + this.initialProperties = Entities.getEntityProperties(this.entityID); + }, + continueNearGrab: function() { + // this.continueDistantGrab(); + }, + continueDistantGrab: function() { + this.setSliderValueBasedOnDistance(); + }, + setSliderValueBasedOnDistance: function() { + var currentPosition = Entities.getEntityProperties(this.entityID, "position").position; + + var distance = Vec3.distance(this.userData.axisStart, currentPosition); + + if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, COLOR_MAX); + } + if (this.userData.sliderType === 'intensity') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, INTENSITY_MAX); + } + if (this.userData.sliderType === 'cutoff') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, CUTOFF_MAX); + } + if (this.userData.sliderType === 'exponent') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, EXPONENT_MAX); + }; + + this.sendValueToSlider(); + }, + releaseGrab: function() { + Entities.editEntity(this.entityID, { + velocity: { + x: 0, + y: 0, + z: 0 + }, + angularVelocity: { + x: 0, + y: 0, + z: 0 + } + }) + + this.sendValueToSlider(); + }, + scaleValueBasedOnDistanceFromStart: function(value, min2, max2) { + var min1 = 0; + var max1 = AXIS_SCALE; + var min2 = min2; + var max2 = max2; + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + }, + sendValueToSlider: function() { + var _t = this; + var message = { + lightID: _t.userData.lightID, + sliderType: _t.userData.sliderType, + sliderValue: _t.sliderValue + } + Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); + if (_t.userData.sliderType === 'cutoff') { + Messages.sendMessage('entityToolUpdates', 'callUpdate'); + } + } + }; + + return new Slider(); +}); \ No newline at end of file diff --git a/examples/light_modifier/visiblePanel.js b/examples/light_modifier/visiblePanel.js new file mode 100644 index 0000000000..cf6875fc59 --- /dev/null +++ b/examples/light_modifier/visiblePanel.js @@ -0,0 +1,40 @@ +// +// visiblePanel.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that disables picking on this panel. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + function VisiblePanel() { + return this; + } + + VisiblePanel.prototype = { + preload: function(entityID) { + this.entityID = entityID; + + var data = { + action: 'add', + id: this.entityID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)) + }, + unload: function() { + var data = { + action: 'remove', + id: this.entityID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)) + } + + }; + + return new VisiblePanel(); +}); \ No newline at end of file diff --git a/examples/painting/closePaint.js b/examples/painting/closePaint.js index 60b4ac2e25..bea4d9c9aa 100644 --- a/examples/painting/closePaint.js +++ b/examples/painting/closePaint.js @@ -43,6 +43,8 @@ var MAX_STROKE_WIDTH = 0.04; var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); +var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png"; + function MyController(hand, triggerAction) { @@ -148,7 +150,8 @@ function MyController(hand, triggerAction) { y: 50, z: 50 }, - lifetime: 200 + lifetime: 200, + textures: textureURL }); this.strokePoints = []; this.strokeNormals = []; diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 61d7291e11..374ec8b873 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -29,6 +29,8 @@ var MIN_STROKE_WIDTH = 0.0005; var MAX_STROKE_WIDTH = 0.03; + var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png"; + var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT, @@ -168,6 +170,7 @@ type: "PolyLine", name: "paintStroke", color: this.strokeColor, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png", dimensions: { x: 50, y: 50, diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index f3005495ec..f6c7aa77ca 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -247,4 +247,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -//Script.scriptEnding.connect(cleanup); +// Script.scriptEnding.connect(cleanup); diff --git a/examples/rayPickingFilterExample.js b/examples/rayPickingFilterExample.js new file mode 100644 index 0000000000..73d151847b --- /dev/null +++ b/examples/rayPickingFilterExample.js @@ -0,0 +1,73 @@ + // + // rayPickingFilterExample.js + // examples + // + // Created by Eric Levin on 12/24/2015 + // Copyright 2015 High Fidelity, Inc. + // + // This is an example script that demonstrates the use of filtering entities for ray picking + // + // Distributed under the Apache License, Version 2.0. + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + // + + + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + + var whiteListBox = Entities.addEntity({ + type: "Box", + color: { + red: 10, + green: 200, + blue: 10 + }, + dimensions: { + x: 0.2, + y: 0.2, + z: 0.2 + }, + position: center + }); + + var blackListBox = Entities.addEntity({ + type: "Box", + color: { + red: 100, + green: 10, + blue: 10 + }, + dimensions: { + x: 0.2, + y: 0.2, + z: 0.2 + }, + position: Vec3.sum(center, { + x: 0, + y: 0.3, + z: 0 + }) + }); + + + function castRay(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + // In this example every entity will be pickable except the entities in the blacklist array + // the third argument is the whitelist array,and the fourth and final is the blacklist array + var pickResults = Entities.findRayIntersection(pickRay, true, [], [blackListBox]); + + // With below example, only entities added to whitelist will be pickable + // var pickResults = Entities.findRayIntersection(pickRay, true, [whiteListBox], []); + + if (pickResults.intersects) { + print("INTERSECTION!"); + } + + } + + function cleanup() { + Entities.deleteEntity(whiteListBox); + Entities.deleteEntity(blackListBox); + } + + Script.scriptEnding.connect(cleanup); + Controller.mousePressEvent.connect(castRay); \ No newline at end of file diff --git a/examples/tests/qmlTest.js b/examples/tests/qmlTest.js new file mode 100644 index 0000000000..f1aa59cc09 --- /dev/null +++ b/examples/tests/qmlTest.js @@ -0,0 +1,40 @@ +print("Launching web window"); +qmlWindow = new OverlayWindow({ + title: 'Test Qml', + source: "https://s3.amazonaws.com/DreamingContent/qml/content.qml", + height: 240, + width: 320, + toolWindow: false, + visible: true +}); + +//qmlWindow.eventBridge.webEventReceived.connect(function(data) { +// print("JS Side event received: " + data); +//}); +// +//var titles = ["A", "B", "C"]; +//var titleIndex = 0; +// +//Script.setInterval(function() { +// qmlWindow.eventBridge.emitScriptEvent("JS Event sent"); +// var size = qmlWindow.size; +// var position = qmlWindow.position; +// print("Window visible: " + qmlWindow.visible) +// if (qmlWindow.visible) { +// print("Window size: " + size.x + "x" + size.y) +// print("Window pos: " + position.x + "x" + position.y) +// qmlWindow.setVisible(false); +// } else { +// qmlWindow.setVisible(true); +// qmlWindow.setTitle(titles[titleIndex]); +// qmlWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100); +// titleIndex += 1; +// titleIndex %= titles.length; +// } +//}, 2 * 1000); +// +//Script.setTimeout(function() { +// print("Closing script"); +// qmlWindow.close(); +// Script.stop(); +//}, 15 * 1000) diff --git a/examples/tests/qmlWebTest.js b/examples/tests/qmlWebTest.js new file mode 100644 index 0000000000..5faa68668d --- /dev/null +++ b/examples/tests/qmlWebTest.js @@ -0,0 +1,33 @@ +print("Launching web window"); + +var htmlUrl = Script.resolvePath("..//html/qmlWebTest.html") +webWindow = new OverlayWebWindow('Test Event Bridge', htmlUrl, 320, 240, false); +print("JS Side window: " + webWindow); +print("JS Side bridge: " + webWindow.eventBridge); +webWindow.eventBridge.webEventReceived.connect(function(data) { + print("JS Side event received: " + data); +}); + +var titles = ["A", "B", "C"]; +var titleIndex = 0; + +Script.setInterval(function() { + webWindow.eventBridge.emitScriptEvent("JS Event sent"); + var size = webWindow.size; + var position = webWindow.position; + print("Window url: " + webWindow.url) + print("Window visible: " + webWindow.visible) + print("Window size: " + size.x + "x" + size.y) + print("Window pos: " + position.x + "x" + position.y) + webWindow.setVisible(!webWindow.visible); + webWindow.setTitle(titles[titleIndex]); + webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100); + titleIndex += 1; + titleIndex %= titles.length; +}, 2 * 1000); + +Script.setTimeout(function() { + print("Closing script"); + webWindow.close(); + Script.stop(); +}, 15 * 1000) diff --git a/examples/toybox/bow/bow.js b/examples/toybox/bow/bow.js index f0128acc77..44fe33bb31 100644 --- a/examples/toybox/bow/bow.js +++ b/examples/toybox/bow/bow.js @@ -241,9 +241,9 @@ userData: JSON.stringify({ grabbableKey: { grabbable: false - } + }, + creatorSessionUUID: MyAvatar.sessionUUID }) - }); var makeArrowStick = function(entityA, entityB, collision) { diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index 4b93842896..7835dbf7f9 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -38,6 +38,14 @@ var pingPongGun = Entities.addEntity({ collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ grabbableKey: { + spatialKey: { + relativePosition: { + x: -0.05, + y: 0, + z: 0.0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90) + }, invertSolidWhileHeld: true } }) diff --git a/examples/toybox/pistol/pistol.js b/examples/toybox/pistol/pistol.js index 8ef26b94c1..7fb05d992f 100644 --- a/examples/toybox/pistol/pistol.js +++ b/examples/toybox/pistol/pistol.js @@ -29,21 +29,14 @@ this.equipped = false; this.forceMultiplier = 1; this.laserLength = 100; - this.laserOffsets = { - y: .095 - }; - this.firingOffsets = { - z: 0.16 - } + this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw"); this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav"); this.playRichochetSoundChance = 0.1; this.fireVolume = 0.2; this.bulletForce = 10; - - - this.showLaser = false; + }; Pistol.prototype = { @@ -58,20 +51,36 @@ if (!this.equipped) { return; } - this.toggleWithTriggerPressure(); + this.updateProps(); if (this.showLaser) { this.updateLaser(); } + this.toggleWithTriggerPressure(); + + + }, + + updateProps: function() { + var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); + this.position = gunProps.position; + this.rotation = gunProps.rotation; + this.firingDirection = Quat.getFront(this.rotation); + var upVec = Quat.getUp(this.rotation); + this.barrelPoint = Vec3.sum(this.position, Vec3.multiply(upVec, this.laserOffsets.y)); + this.laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength)); + this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) + var pickRay = { + origin: this.barrelPoint, + direction: this.firingDirection + }; }, toggleWithTriggerPressure: function() { this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]); if (this.triggerValue < RELOAD_THRESHOLD) { - // print('RELOAD'); this.canShoot = true; } if (this.canShoot === true && this.triggerValue === 1) { - // print('SHOOT'); this.fire(); this.canShoot = false; } @@ -91,17 +100,10 @@ }, updateLaser: function() { - var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); - var position = gunProps.position; - var rotation = gunProps.rotation; - this.firingDirection = Quat.getFront(rotation); - var upVec = Quat.getUp(rotation); - this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y)); - var laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength)); - this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) + Overlays.editOverlay(this.laser, { start: this.barrelPoint, - end: laserTip, + end: this.laserTip, alpha: 1 }); }, @@ -114,19 +116,6 @@ }); }, - preload: function(entityID) { - this.entityID = entityID; - // this.initControllerMapping(); - this.laser = Overlays.addOverlay("line3d", { - start: ZERO_VECTOR, - end: ZERO_VECTOR, - color: COLORS.RED, - alpha: 1, - visible: true, - lineWidth: 2 - }); - }, - triggerPress: function(hand, value) { if (this.hand === hand && value === 1) { //We are pulling trigger on the hand we have the gun in, so fire @@ -135,15 +124,16 @@ }, fire: function() { - var pickRay = { - origin: this.barrelPoint, - direction: this.firingDirection - }; + Audio.playSound(this.fireSound, { position: this.barrelPoint, volume: this.fireVolume }); + var pickRay = { + origin: this.barrelPoint, + direction: this.firingDirection + }; this.createGunFireEffect(this.barrelPoint) var intersection = Entities.findRayIntersectionBlocking(pickRay, true); if (intersection.intersects) { @@ -170,11 +160,11 @@ }, createEntityHitEffect: function(position) { - var flash = Entities.addEntity({ + var sparks = Entities.addEntity({ type: "ParticleEffect", position: position, lifetime: 4, - "name": "Flash Emitter", + "name": "Sparks Emitter", "color": { red: 228, green: 128, @@ -228,7 +218,7 @@ }); Script.setTimeout(function() { - Entities.editEntity(flash, { + Entities.editEntity(sparks, { isEmitting: false }); }, 100); @@ -261,11 +251,11 @@ "z": 0 }, "accelerationSpread": { - "x": .2, + "x": 0.2, "y": 0, - "z": .2 + "z": 0.2 }, - "radiusSpread": .04, + "radiusSpread": 0.04, "particleRadius": 0.07, "radiusStart": 0.07, "radiusFinish": 0.07, @@ -282,11 +272,46 @@ }); }, 100); - var flash = Entities.addEntity({ + Entities.editEntity(this.flash, { + isEmitting: true + }); + Script.setTimeout(function() { + Entities.editEntity(_this.flash, { + isEmitting: false + }); + }, 100) + + }, + + preload: function(entityID) { + this.entityID = entityID; + this.laser = Overlays.addOverlay("line3d", { + start: ZERO_VECTOR, + end: ZERO_VECTOR, + color: COLORS.RED, + alpha: 1, + visible: true, + lineWidth: 2 + }); + this.laserOffsets = { + y: 0.095 + }; + this.firingOffsets = { + z: 0.16 + } + var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); + var position = gunProps.position; + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + this.firingDirection = Quat.getFront(rotation); + var upVec = Quat.getUp(rotation); + this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y)); + this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) + + this.flash = Entities.addEntity({ type: "ParticleEffect", - position: position, - lifetime: 4, + position: this.barrelPoint, "name": "Muzzle Flash", + isEmitting: false, "color": { red: 228, green: 128, @@ -339,16 +364,13 @@ "textures": "http://ericrius1.github.io/PartiArt/assets/star.png" }); - Script.setTimeout(function() { - Entities.editEntity(flash, { - isEmitting: false - }); - }, 100) - - } + Script.setTimeout(function() { + Entities.editEntity(_this.flash, {parentID: _this.entityID}); + }, 500) + }, }; // entity scripts always need to return a newly constructed object of our type return new Pistol(); -}); \ No newline at end of file +}); diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js index d9fa999a13..0e08f80d0e 100644 --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -384,10 +384,10 @@ var CHECK_MARK_COLOR = { y: newY }); Overlays.editOverlay(this.checkMark, { - y: newY + y: newY + (0.25 * this.thumbSize) }); Overlays.editOverlay(this.unCheckMark, { - y: newY + y: newY + (0.25 * this.thumbSize) }); }; @@ -399,10 +399,10 @@ var CHECK_MARK_COLOR = { y: this.y }); Overlays.editOverlay(this.checkMark, { - y: this.y + y: this.y + (0.25 * this.thumbSize) }); Overlays.editOverlay(this.unCheckMark, { - y: this.y + y: this.y+ (0.25 * this.thumbSize) }); }; @@ -814,12 +814,14 @@ var CHECK_MARK_COLOR = { }); }; - CollapsablePanelItem.prototype.destroy = function() { Overlays.deleteOverlay(this.title); Overlays.deleteOverlay(this.thumb); }; + CollapsablePanelItem.prototype.editTitle = function(opts) { + Overlays.editOverlay(this.title, opts); + }; CollapsablePanelItem.prototype.hide = function() { Overlays.editOverlay(this.title, { @@ -1531,4 +1533,4 @@ Controller.captureKeyEvents({ }); Controller.captureKeyEvents({ text: "right" -}); \ No newline at end of file +}); diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index cca97b7184..3e619dbd5a 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -1,6 +1,7 @@ // -// SunLightExample.js -// examples +// renderEngineDebug.js +// examples/utilities/tools +// // Sam Gateau // Copyright 2015 High Fidelity, Inc. // @@ -10,78 +11,93 @@ Script.include("cookies.js"); +var MENU = "Developer>Render>Debug Deferred Buffer"; +var ACTIONS = ["Off", "Diffuse", "Alpha", "Specular", "Roughness", "Normal", "Depth", "Lighting", "Custom"]; +var SETTINGS_KEY = "EngineDebugScript.DebugMode"; + +Number.prototype.clamp = function(min, max) { + return Math.min(Math.max(this, min), max); +}; + var panel = new Panel(10, 100); -function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) { - this.subPanel = panel.newSubPanel(name); +function CounterWidget(parentPanel, name, counter) { + var subPanel = parentPanel.newSubPanel(name); + var widget = parentPanel.items[name]; + widget.editTitle({ width: 270 }); - this.subPanel.newSlider("Num Feed", 0, 1, - function(value) { }, - feedGetter, - function(value) { return (value); }); - this.subPanel.newSlider("Num Drawn", 0, 1, - function(value) { }, - drawGetter, - function(value) { return (value); }); - this.subPanel.newSlider("Max Drawn", -1, 1, - capSetter, - capGetter, - function(value) { return (value); }); + subPanel.newSlider('Max Drawn', -1, 1, + function(value) { counter.maxDrawn = value; }, // setter + function() { return counter.maxDrawn; }, // getter + function(value) { return value; }); - this.update = function () { - var numFeed = this.subPanel.get("Num Feed"); - this.subPanel.set("Num Feed", numFeed); - this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn")); + var slider = subPanel.getWidget('Max Drawn'); - var numMax = Math.max(numFeed, 1); - this.subPanel.getWidget("Num Feed").setMaxValue(numMax); - this.subPanel.getWidget("Num Drawn").setMaxValue(numMax); - this.subPanel.getWidget("Max Drawn").setMaxValue(numMax); + this.update = function () { + var numDrawn = counter.numDrawn; // avoid double polling + var numMax = Math.max(numDrawn, 1); + var title = [ + ' ' + name, + numDrawn + ' / ' + counter.numFeed + ].join('\t'); + + widget.editTitle({ text: title }); + slider.setMaxValue(numMax); }; }; -var opaquesCounter = new CounterWidget(panel, "Opaques", - function () { return Scene.getEngineNumFeedOpaqueItems(); }, - function () { return Scene.getEngineNumDrawnOpaqueItems(); }, - function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, - function () { return Scene.getEngineMaxDrawnOpaqueItems(); } -); +var opaquesCounter = new CounterWidget(panel, "Opaques", Render.opaque); +var transparentsCounter = new CounterWidget(panel, "Transparents", Render.transparent); +var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D); -var transparentsCounter = new CounterWidget(panel, "Transparents", - function () { return Scene.getEngineNumFeedTransparentItems(); }, - function () { return Scene.getEngineNumDrawnTransparentItems(); }, - function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, - function () { return Scene.getEngineMaxDrawnTransparentItems(); } -); +var resizing = false; +var previousMode = Settings.getValue(SETTINGS_KEY, -1); +Menu.addActionGroup(MENU, ACTIONS, ACTIONS[previousMode + 1]); +Render.deferredDebugMode = previousMode; +Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size -var overlaysCounter = new CounterWidget(panel, "Overlays", - function () { return Scene.getEngineNumFeedOverlay3DItems(); }, - function () { return Scene.getEngineNumDrawnOverlay3DItems(); }, - function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, - function () { return Scene.getEngineMaxDrawnOverlay3DItems(); } -); +function setEngineDeferredDebugSize(eventX) { + var scaledX = (2.0 * (eventX / Window.innerWidth) - 1.0).clamp(-1.0, 1.0); + Render.deferredDebugSize = { x: scaledX, y: -1.0, z: 1.0, w: 1.0 }; +} +function shouldStartResizing(eventX) { + var x = Math.abs(eventX - Window.innerWidth * (1.0 + Render.deferredDebugSize.x) / 2.0); + var mode = Render.deferredDebugMode; + return mode !== -1 && x < 20; +} +function menuItemEvent(menuItem) { + var index = ACTIONS.indexOf(menuItem); + if (index >= 0) { + Render.deferredDebugMode = (index - 1); + } +} // see libraries/render/src/render/Engine.h var showDisplayStatusFlag = 1; var showNetworkStatusFlag = 2; panel.newCheckbox("Display status", - function(value) { Scene.setEngineDisplayItemStatus(value ? - Scene.doEngineDisplayItemStatus() | showDisplayStatusFlag : - Scene.doEngineDisplayItemStatus() & ~showDisplayStatusFlag); }, - function() { return (Scene.doEngineDisplayItemStatus() & showDisplayStatusFlag) > 0; }, + function(value) { Render.displayItemStatus = (value ? + Render.displayItemStatus | showDisplayStatusFlag : + Render.displayItemStatus & ~showDisplayStatusFlag); }, + function() { return (Render.displayItemStatus & showDisplayStatusFlag) > 0; }, function(value) { return (value & showDisplayStatusFlag) > 0; } ); panel.newCheckbox("Network/Physics status", - function(value) { Scene.setEngineDisplayItemStatus(value ? - Scene.doEngineDisplayItemStatus() | showNetworkStatusFlag : - Scene.doEngineDisplayItemStatus() & ~showNetworkStatusFlag); }, - function() { return (Scene.doEngineDisplayItemStatus() & showNetworkStatusFlag) > 0; }, + function(value) { Render.displayItemStatus = (value ? + Render.displayItemStatus | showNetworkStatusFlag : + Render.displayItemStatus & ~showNetworkStatusFlag); }, + function() { return (Render.displayItemStatus & showNetworkStatusFlag) > 0; }, function(value) { return (value & showNetworkStatusFlag) > 0; } ); +panel.newSlider("Tone Mapping Exposure", -10, 10, + function (value) { Render.tone.exposure = value; }, + function() { return Render.tone.exposure; }, + function (value) { return (value); }); + var tickTackPeriod = 500; function updateCounters() { @@ -91,11 +107,51 @@ function updateCounters() { } Script.setInterval(updateCounters, tickTackPeriod); -Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); -Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); -Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); +function mouseMoveEvent(event) { + if (resizing) { + setEngineDeferredDebugSize(event.x); + } else { + panel.mouseMoveEvent(event); + } +} + +function mousePressEvent(event) { + if (shouldStartResizing(event.x)) { + resizing = true; + } else { + panel.mousePressEvent(event); + } +} + +function mouseReleaseEvent(event) { + if (resizing) { + resizing = false; + } else { + panel.mouseReleaseEvent(event); + } +} + +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + +Menu.menuItemEvent.connect(menuItemEvent); function scriptEnding() { panel.destroy(); + Menu.removeActionGroup(MENU); + // Reset + Settings.setValue(SETTINGS_KEY, Render.deferredDebugMode); + Render.deferredDebugMode = -1; + Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; + Render.opaque.maxDrawn = -1; + Render.transparent.maxDrawn = -1; + Render.overlay3D.maxDrawn = -1; } Script.scriptEnding.connect(scriptEnding); + + +// Collapse items +panel.mousePressEvent({ x: panel.x, y: panel.items["Overlays"].y}); +panel.mousePressEvent({ x: panel.x, y: panel.items["Transparents"].y}); +panel.mousePressEvent({ x: panel.x, y: panel.items["Opaques"].y}); diff --git a/examples/winterSmashUp/targetPractice/shooterPlatform.js b/examples/winterSmashUp/targetPractice/shooterPlatform.js new file mode 100644 index 0000000000..4fae47a415 --- /dev/null +++ b/examples/winterSmashUp/targetPractice/shooterPlatform.js @@ -0,0 +1,73 @@ +// +// shooterPlatform.js +// examples/winterSmashUp/targetPractice +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// The Winter Smash Up Target Practice Game using a bow. +// This is the platform that spawns the bow and attaches it to the avatars hand, +// then de-rez the bow on leaving to prevent walking up to the targets with the bow. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this = this; + + const GAME_CHANNEL = 'winterSmashUpGame'; + const SCRIPT_URL = Script.resolvePath('../../toybox/bow/bow.js'); + const MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow-deadly.fbx"; + const COLLISION_HULL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow_collision_hull.obj"; + const RIGHT_HAND = 1; + const LEFT_HAND = 0; + + var bowEntity = undefined; + + _this.enterEntity = function(entityID) { + print('entered bow game entity'); + + // Triggers a recording on an assignment client: + Messages.sendMessage('PlayBackOnAssignment', 'BowShootingGameExplaination'); + + bowEntity = Entities.addEntity({ + name: 'Hifi-Bow-For-Game', + type: 'Model', + modelURL: MODEL_URL, + position: MyAvatar.position, + dimensions: {x: 0.04, y: 1.3, z: 0.21}, + collisionsWillMove: true, + gravity: {x: 0, y: 0, z: 0}, + shapeType: 'compound', + compoundShapeURL: COLLISION_HULL_URL, + script: SCRIPT_URL, + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true, + spatialKey: { + 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) + } + } + }) + }); + Messages.sendMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'left', entityID: bowEntity})); + }; + + _this.leaveEntity = function(entityID) { + if (bowEntity !== undefined) { + Entities.deleteEntity(bowEntity); + bowEntity = undefined; + } + }; +}); diff --git a/examples/winterSmashUp/targetPractice/startTargetPractice.js b/examples/winterSmashUp/targetPractice/startTargetPractice.js new file mode 100644 index 0000000000..2e65d09fb1 --- /dev/null +++ b/examples/winterSmashUp/targetPractice/startTargetPractice.js @@ -0,0 +1,93 @@ +// +// startTargetPractice.js +// examples/winterSmashUp/targetPractice +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// The Winter Smash Up Target Practice Game using a bow. +// This script starts the game, when the entity that contains the script gets shot. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this = this; + var waitForScriptLoad = false; + + const MAX_GAME_TIME = 60; //seconds + const SCRIPT_URL = 'http://s3.amazonaws.com/hifi-public/scripts/winterSmashUp/targetPractice/targetPracticeGame.js'; + const GAME_CHANNEL = 'winterSmashUpGame'; + + var isScriptRunning = function(script) { + script = script.toLowerCase().trim(); + var runningScripts = ScriptDiscoveryService.getRunning(); + for (i in runningScripts) { + if (runningScripts[i].url.toLowerCase().trim() == script) { + return true; + } + } + return false; + }; + + var sendStartSignal = function() { + Messages.sendMessage(GAME_CHANNEL, JSON.stringify({ + action: 'start', + gameEntityID: _this.entityID, + playerSessionUUID: MyAvatar.sessionUUID + })); + } + + var startGame = function() { + //TODO: check here if someone is already playing this game instance by verifying the userData for playerSessionID + // and startTime with a maximum timeout of X seconds (30?) + + + if (!isScriptRunning(SCRIPT_URL)) { + // Loads the script for the player if this isn't yet loaded + Script.load(SCRIPT_URL); + waitForScriptLoad = true; + return; + } + sendStartSignal(); + }; + + Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel == GAME_CHANNEL) { + var data = JSON.parse(message); + switch (data.action) { + case 'scriptLoaded': + if (waitForPing) { + sendStartSignal(); + waitForPing = false; + } + break; + } + } + }); + + _this.preload = function(entityID) { + _this.entityID = entityID; + }; + + _this.collisionWithEntity = function(entityA, entityB, collisionInfo) { + if (entityA == _this.entityID) { + try { + var data = JSON.parse(Entities.getEntityProperties(entityB).userData); + if (data.creatorSessionUUID === MyAvatar.sessionUUID) { + print('attempting to startGame by collisionWithEntity.'); + startGame(); + } + } catch(e) { + } + } + }; + + _this.onShot = function(forceDirection) { + print('attempting to startGame by onShot.'); + startGame(); + }; + + Messages.subscribe(GAME_CHANNEL); +}); diff --git a/examples/winterSmashUp/targetPractice/targetPracticeGame.js b/examples/winterSmashUp/targetPractice/targetPracticeGame.js new file mode 100644 index 0000000000..d58258af5a --- /dev/null +++ b/examples/winterSmashUp/targetPractice/targetPracticeGame.js @@ -0,0 +1,227 @@ +// +// targetPracticeGame.js +// examples/winterSmashUp/targetPractice +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// The Winter Smash Up Target Practice Game using a bow. +// This script runs on the client side (it is loaded through the platform trigger entity) +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +const GAME_CHANNEL = 'winterSmashUpGame'; +const SCORE_POST_URL = 'https://script.google.com/macros/s/AKfycbwZAMx6cMBx6-8NGEhR8ELUA-dldtpa_4P55z38Q4vYHW6kneg/exec'; +const MODEL_URL = 'http://cdn.highfidelity.com/chris/production/winter/game/'; +const MAX_GAME_TIME = 120; //seconds +const TARGET_CLOSE_OFFSET = 0.5; +const MILLISECS_IN_SEC = 1000; +const HIT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Clay_Pigeon_02.L.wav'; +const GRAVITY = -9.8; +const MESSAGE_WIDTH = 100; +const MESSAGE_HEIGHT = 50; + +const TARGETS = [ + {pitch: -1, yaw: -20, maxDistance: 17}, + {pitch: -1, yaw: -15, maxDistance: 17}, + {pitch: -1, yaw: -10, maxDistance: 17}, + {pitch: -2, yaw: -5, maxDistance: 17}, + {pitch: -1, yaw: 0, maxDistance: 17}, + {pitch: 3, yaw: 10, maxDistance: 17}, + {pitch: -1, yaw: 15, maxDistance: 17}, + {pitch: -1, yaw: 20, maxDistance: 17}, + {pitch: 2, yaw: 25, maxDistance: 17}, + {pitch: 0, yaw: 30, maxDistance: 17} +]; + +var gameRunning = false; +var gameStartTime; +var gameEntityID; +var gameTimeoutTimer; +var targetEntities = []; +var hitSound = SoundCache.getSound(HIT_SOUND_URL); +var messageOverlay = undefined; +var messageExpire = 0; + +var clearMessage = function() { + if (messageOverlay !== undefined) { + Overlays.deleteOverlay(messageOverlay); + messageOverlay = undefined; + } +}; + +var displayMessage = function(message, timeout) { + clearMessage(); + messageExpire = Date.now() + timeout; + messageOverlay = Overlays.addOverlay('text', { + text: message, + x: (Window.innerWidth / 2) - (MESSAGE_WIDTH / 2), + y: (Window.innerHeight / 2) - (MESSAGE_HEIGHT / 2), + width: MESSAGE_WIDTH, + height: MESSAGE_HEIGHT, + alpha: 1, + backgroundAlpha: 0.0, + font: {size: 20} + }); +}; + +var getStatsText = function() { + var timeLeft = MAX_GAME_TIME - ((Date.now() - gameStartTime) / MILLISECS_IN_SEC); + return 'Time remaining: ' + timeLeft.toFixed(1) + 's\nTargets hit: ' + (TARGETS.length - targetEntities.length) + '/' + TARGETS.length; +}; + +const timerOverlayWidth = 50; +var timerOverlay = Overlays.addOverlay('text', { + text: '', + x: Window.innerWidth / 2 - (timerOverlayWidth / 2), + y: 100, + width: timerOverlayWidth, + alpha: 1, + backgroundAlpha: 0.0, + visible: false, + font: {size: 20} +}); + +var cleanRemainingTargets = function() { + while (targetEntities.length > 0) { + Entities.deleteEntity(targetEntities.pop()); + } +}; + +var createTarget = function(position, rotation, scale) { + var initialDimensions = {x: 1.8437, y: 0.1614, z: 1.8438}; + var dimensions = Vec3.multiply(initialDimensions, scale); + return Entities.addEntity({ + type: 'Model', + rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), + lifetime: MAX_GAME_TIME, + modelURL: MODEL_URL + 'target.fbx', + shapeType: 'compound', + compoundShapeURL: MODEL_URL + 'targetCollision.obj', + dimensions: dimensions, + position: position + }); +}; + +var createTargetInDirection = function(startPosition, startRotation, pitch, yaw, maxDistance, scale) { + var directionQuat = Quat.multiply(startRotation, Quat.fromPitchYawRollDegrees(pitch, yaw, 0.0)); + var directionVec = Vec3.multiplyQbyV(directionQuat, Vec3.FRONT); + var intersection = Entities.findRayIntersection({direction: directionVec, origin: startPosition}, true); + var distance = maxDistance; + if (intersection.intersects && intersection.distance <= maxDistance) { + distance = intersection.distance - TARGET_CLOSE_OFFSET; + } + return createTarget(Vec3.sum(startPosition, Vec3.multiplyQbyV(directionQuat, Vec3.multiply(Vec3.FRONT, distance))), startRotation, scale); +}; + +var killTimer = function() { + if (gameTimeoutTimer !== undefined) { + Script.clearTimeout(gameTimeoutTimer); + gameTimeoutTimer = undefined; + } +}; + +var submitScore = function() { + gameRunning = false; + killTimer(); + Overlays.editOverlay(timerOverlay, {visible: false}); + if (!GlobalServices.username) { + displayMessage('Could not submit score, you are not logged in.', 5000); + return; + } + var timeItTook = Date.now() - gameStartTime; + var req = new XMLHttpRequest(); + req.open('POST', SCORE_POST_URL, false); + req.send(JSON.stringify({ + username: GlobalServices.username, + time: timeItTook / MILLISECS_IN_SEC + })); + displayMessage('Your score has been submitted!', 5000); +}; + +var onTargetHit = function(targetEntity, projectileEntity, collision) { + var targetIndex = targetEntities.indexOf(targetEntity); + if (targetIndex !== -1) { + try { + var data = JSON.parse(Entities.getEntityProperties(projectileEntity).userData); + if (data.creatorSessionUUID === MyAvatar.sessionUUID) { + this.audioInjector = Audio.playSound(hitSound, { + position: collision.contactPoint, + volume: 0.5 + }); + // Attach arrow to target for the nice effect + Entities.editEntity(projectileEntity, { + ignoreForCollisions: true, + parentID: targetEntity + }); + Entities.editEntity(targetEntity, { + collisionsWillMove: true, + gravity: {x: 0, y: GRAVITY, z: 0}, + velocity: {x: 0, y: -0.01, z: 0} + }); + targetEntities.splice(targetIndex, 1); + if (targetEntities.length === 0) { + submitScore(); + } + } + } catch(e) { + } + } +}; + +var startGame = function(entityID) { + cleanRemainingTargets(); + killTimer(); + gameEntityID = entityID; + targetEntities = []; + var parentEntity = Entities.getEntityProperties(gameEntityID); + for (var i in TARGETS) { + var target = TARGETS[i]; + var targetEntity = createTargetInDirection(parentEntity.position, parentEntity.rotation, target.pitch, target.yaw, target.maxDistance, 0.67); + Script.addEventHandler(targetEntity, 'collisionWithEntity', onTargetHit); + targetEntities.push(targetEntity); + } + gameStartTime = Date.now(); + gameTimeoutTimer = Script.setTimeout(function() { + cleanRemainingTargets(); + Overlays.editOverlay(timerOverlay, {visible: false}); + gameRunning = false; + displayMessage('Game timed out.', 5000); + }, MAX_GAME_TIME * MILLISECS_IN_SEC); + gameRunning = true; + Overlays.editOverlay(timerOverlay, {visible: true, text: getStatsText()}); + displayMessage('Game started! GO GO GO!', 3000); +}; + +Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel == GAME_CHANNEL) { + var data = JSON.parse(message); + switch (data.action) { + case 'start': + if (data.playerSessionUUID === MyAvatar.sessionUUID) { + startGame(data.gameEntityID); + } + break; + } + } +}); + +Messages.subscribe(GAME_CHANNEL); +Messages.sendMessage(GAME_CHANNEL, JSON.stringify({action: 'scriptLoaded'})); + +Script.update.connect(function(deltaTime) { + if (gameRunning) { + Overlays.editOverlay(timerOverlay, {text: getStatsText()}); + } + if (messageOverlay !== undefined && Date.now() > messageExpire) { + clearMessage(); + } +}); + +Script.scriptEnding.connect(function() { + Overlays.deleteOverlay(timerOverlay); + cleanRemainingTargets(); + clearMessage(); +}); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 87cf1a384c..5d96b95624 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -45,7 +45,9 @@ else () list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) endif () -find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets) +find_package(Qt5 COMPONENTS + Gui Multimedia Network OpenGL Qml Quick Script Svg + WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets) # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) @@ -107,7 +109,9 @@ add_dependency_external_projects(sdl2) if (WIN32) add_dependency_external_projects(OpenVR) endif() - +if(WIN32 OR APPLE) + add_dependency_external_projects(neuron) +endif() # disable /OPT:REF and /OPT:ICF for the Debug builds # This will prevent the following linker warnings @@ -175,9 +179,17 @@ include_directories("${PROJECT_SOURCE_DIR}/src") target_link_libraries( ${TARGET_NAME} - Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets + Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL + Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg + Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets ) +# Issue causes build failure unless we add this directory. +# See https://bugreports.qt.io/browse/QTBUG-43351 +if (WIN32) + add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine) +endif() + # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) @@ -209,5 +221,9 @@ else (APPLE) endif() endif (APPLE) +if (WIN32) + set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml") +endif() + package_libraries_for_deployment() consolidate_stack_components() diff --git a/interface/resources/controllers/neuron.json b/interface/resources/controllers/neuron.json new file mode 100644 index 0000000000..2d61f80c35 --- /dev/null +++ b/interface/resources/controllers/neuron.json @@ -0,0 +1,7 @@ +{ + "name": "Neuron to Standard", + "channels": [ + { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Hydra.RightHand", "to": "Standard.RightHand" } + ] +} diff --git a/interface/resources/controllers/standard_navigation.json b/interface/resources/controllers/standard_navigation.json new file mode 100644 index 0000000000..c3b30e8607 --- /dev/null +++ b/interface/resources/controllers/standard_navigation.json @@ -0,0 +1,61 @@ +{ + "name": "Standard to Action", + "when": "Application.NavigationFocused", + "channels": [ + { "disabled_from": { "makeAxis" : [ "Standard.DD", "Standard.DU" ] }, "to": "Actions.UiNavVertical" }, + { "disabled_from": { "makeAxis" : [ "Standard.DL", "Standard.DR" ] }, "to": "Actions.UiNavLateral" }, + { "disabled_from": { "makeAxis" : [ "Standard.LB", "Standard.RB" ] }, "to": "Actions.UiNavGroup" }, + { "from": "Standard.DU", "to": "Actions.UiNavVertical" }, + { "from": "Standard.DD", "to": "Actions.UiNavVertical", "filters": "invert" }, + { "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" }, + { "from": "Standard.DR", "to": "Actions.UiNavLateral" }, + { "from": "Standard.LB", "to": "Actions.UiNavGroup","filters": "invert" }, + { "from": "Standard.RB", "to": "Actions.UiNavGroup" }, + { "from": [ "Standard.A", "Standard.X", "Standard.RT", "Standard.LT" ], "to": "Actions.UiNavSelect" }, + { "from": [ "Standard.B", "Standard.Y", "Standard.RightPrimaryThumb", "Standard.LeftPrimaryThumb" ], "to": "Actions.UiNavBack" }, + { + "from": [ "Standard.RT", "Standard.LT" ], + "to": "Actions.UiNavSelect", + "filters": [ + { "type": "deadZone", "min": 0.5 }, + "constrainToInteger" + ] + }, + { + "from": "Standard.LX", "to": "Actions.UiNavLateral", + "filters": [ + { "type": "deadZone", "min": 0.95 }, + "constrainToInteger", + { "type": "pulse", "interval": 0.4 } + ] + }, + { + "from": "Standard.LY", "to": "Actions.UiNavVertical", + "filters": [ + "invert", + { "type": "deadZone", "min": 0.95 }, + "constrainToInteger", + { "type": "pulse", "interval": 0.4 } + ] + }, + { + "from": "Standard.RX", "to": "Actions.UiNavLateral", + "filters": [ + { "type": "deadZone", "min": 0.95 }, + "constrainToInteger", + { "type": "pulse", "interval": 0.4 } + ] + }, + { + "from": "Standard.RY", "to": "Actions.UiNavVertical", + "filters": [ + "invert", + { "type": "deadZone", "min": 0.95 }, + "constrainToInteger", + { "type": "pulse", "interval": 0.4 } + ] + } + ] +} + + diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 947bf739fc..8f3cd0e1e7 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,6 +1,6 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtWebKit 3.0 +import QtWebEngine 1.1 import "controls" import "styles" @@ -39,9 +39,10 @@ VrDialog { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - anchors.bottom: scrollView.top + anchors.bottom: webview.top color: "white" } + Row { id: buttons spacing: 4 @@ -112,26 +113,22 @@ VrDialog { } } - ScrollView { - id: scrollView + WebEngineView { + id: webview + url: "http://highfidelity.com" anchors.top: buttons.bottom anchors.topMargin: 8 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right - WebView { - id: webview - url: "http://highfidelity.com" - anchors.fill: parent - onLoadingChanged: { - if (loadRequest.status == WebView.LoadSucceededStarted) { - addressBar.text = loadRequest.url - } - } - onIconChanged: { - barIcon.source = icon + onLoadingChanged: { + if (loadRequest.status == WebEngineView.LoadSucceededStatus) { + addressBar.text = loadRequest.url } } + onIconChanged: { + console.log("New icon: " + icon) + } } } // item @@ -146,5 +143,4 @@ VrDialog { break; } } - } // dialog diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index 012f04f1fd..75b82342ca 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -2,7 +2,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 -import QtWebKit 3.0 +import QtWebEngine 1.1 import "controls" VrDialog { @@ -18,15 +18,11 @@ VrDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - ScrollView { + WebEngineView { + id: webview + objectName: "WebView" anchors.fill: parent - WebView { - objectName: "WebView" - id: webview - url: infoView.url - anchors.fill: parent - } - } - + url: infoView.url + } } } diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml index 946f32e84a..3a66c5340f 100644 --- a/interface/resources/qml/MarketplaceDialog.qml +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -2,7 +2,7 @@ import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 -import QtWebKit 3.0 +import QtWebEngine 1.1 import "controls" VrDialog { @@ -24,27 +24,22 @@ VrDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - - ScrollView { + WebEngineView { + objectName: "WebView" + id: webview + url: "https://metaverse.highfidelity.com/marketplace" anchors.fill: parent - WebView { - objectName: "WebView" - id: webview - url: "https://metaverse.highfidelity.com/marketplace" - anchors.fill: parent - onNavigationRequested: { - console.log(request.url) - if (!marketplaceDialog.navigationRequested(request.url)) { - console.log("Application absorbed the request") - request.action = WebView.IgnoreRequest; - return; - } - console.log("Application passed on the request") - request.action = WebView.AcceptRequest; + onNavigationRequested: { + console.log(request.url) + if (!marketplaceDialog.navigationRequested(request.url)) { + console.log("Application absorbed the request") + request.action = WebView.IgnoreRequest; return; - } + } + console.log("Application passed on the request") + request.action = WebView.AcceptRequest; + return; } - } - - } + } + } } diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml new file mode 100644 index 0000000000..c2076c93e3 --- /dev/null +++ b/interface/resources/qml/QmlWebWindow.qml @@ -0,0 +1,77 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtWebEngine 1.1 + +import "controls" +import "styles" + +VrDialog { + id: root + HifiConstants { id: hifi } + title: "WebWindow" + resizable: true + enabled: false + visible: false + // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer + destroyOnCloseButton: false + contentImplicitWidth: clientArea.implicitWidth + contentImplicitHeight: clientArea.implicitHeight + backgroundColor: "#7f000000" + property url source: "about:blank" + + signal navigating(string url) + function stop() { + webview.stop(); + } + + Component.onCompleted: { + // Ensure the JS from the web-engine makes it to our logging + webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); + }); + + } + + Item { + id: clientArea + implicitHeight: 600 + implicitWidth: 800 + x: root.clientX + y: root.clientY + width: root.clientWidth + height: root.clientHeight + + WebEngineView { + id: webview + url: root.source + anchors.fill: parent + focus: true + + onUrlChanged: { + var currentUrl = url.toString(); + var newUrl = urlHandler.fixupUrl(currentUrl); + if (newUrl != currentUrl) { + url = newUrl; + } + } + + onLoadingChanged: { + // Required to support clicking on "hifi://" links + if (WebEngineView.LoadStartedStatus == loadRequest.status) { + var url = loadRequest.url.toString(); + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + webview.stop(); + } + } + } + } + + profile: WebEngineProfile { + id: webviewProfile + httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)" + storageName: "qmlWebEngine" + } + } + } // item +} // dialog diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml new file mode 100644 index 0000000000..951aa24471 --- /dev/null +++ b/interface/resources/qml/QmlWindow.qml @@ -0,0 +1,55 @@ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtWebChannel 1.0 +import QtWebSockets 1.0 +import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel + +import "controls" +import "styles" + +VrDialog { + id: root + objectName: "topLevelWindow" + HifiConstants { id: hifi } + title: "QmlWindow" + resizable: true + enabled: false + visible: false + focus: true + property var channel; + + // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer + destroyOnCloseButton: false + contentImplicitWidth: clientArea.implicitWidth + contentImplicitHeight: clientArea.implicitHeight + property alias source: pageLoader.source + + Item { + id: clientArea + implicitHeight: 600 + implicitWidth: 800 + x: root.clientX + y: root.clientY + width: root.clientWidth + height: root.clientHeight + focus: true + clip: true + + Loader { + id: pageLoader + objectName: "Loader" + anchors.fill: parent + focus: true + property var dialog: root + + onLoaded: { + forceActiveFocus() + } + + Keys.onPressed: { + console.log("QmlWindow pageLoader keypress") + } + } + } // item +} // dialog diff --git a/interface/resources/qml/TestMenu.qml b/interface/resources/qml/TestMenu.qml index 4d109e6298..fe8a26e234 100644 --- a/interface/resources/qml/TestMenu.qml +++ b/interface/resources/qml/TestMenu.qml @@ -4,116 +4,7 @@ import Hifi 1.0 // Currently for testing a pure QML replacement menu Item { - Item { - objectName: "AllActions" - Action { - id: aboutApp - objectName: "HifiAction_" + MenuConstants.AboutApp - text: qsTr("About Interface") - } - - // - // File Menu - // - Action { - id: login - objectName: "HifiAction_" + MenuConstants.Login - text: qsTr("Login") - } - Action { - id: quit - objectName: "HifiAction_" + MenuConstants.Quit - text: qsTr("Quit") - //shortcut: StandardKey.Quit - shortcut: "Ctrl+Q" - } - - - // - // Edit menu - // - Action { - id: undo - text: "Undo" - shortcut: StandardKey.Undo - } - - Action { - id: redo - text: "Redo" - shortcut: StandardKey.Redo - } - - Action { - id: animations - objectName: "HifiAction_" + MenuConstants.Animations - text: qsTr("Animations...") - } - Action { - id: attachments - text: qsTr("Attachments...") - } - Action { - id: explode - text: qsTr("Explode on quit") - checkable: true - checked: true - } - Action { - id: freeze - text: qsTr("Freeze on quit") - checkable: true - checked: false - } - ExclusiveGroup { - Action { - id: visibleToEveryone - objectName: "HifiAction_" + MenuConstants.VisibleToEveryone - text: qsTr("Everyone") - checkable: true - checked: true - } - Action { - id: visibleToFriends - objectName: "HifiAction_" + MenuConstants.VisibleToFriends - text: qsTr("Friends") - checkable: true - } - Action { - id: visibleToNoOne - objectName: "HifiAction_" + MenuConstants.VisibleToNoOne - text: qsTr("No one") - checkable: true - } - } - } - Menu { objectName: "rootMenu"; - Menu { - title: "File" - MenuItem { action: login } - MenuItem { action: explode } - MenuItem { action: freeze } - MenuItem { action: quit } - } - Menu { - title: "Tools" - Menu { - title: "I Am Visible To" - MenuItem { action: visibleToEveryone } - MenuItem { action: visibleToFriends } - MenuItem { action: visibleToNoOne } - } - MenuItem { action: animations } - } - Menu { - title: "Long menu name top menu" - MenuItem { action: aboutApp } - } - Menu { - title: "Help" - MenuItem { action: aboutApp } - } } } diff --git a/interface/resources/qml/WebEntity.qml b/interface/resources/qml/WebEntity.qml index 0eb943cac7..ae94105672 100644 --- a/interface/resources/qml/WebEntity.qml +++ b/interface/resources/qml/WebEntity.qml @@ -1,10 +1,10 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtWebKit 3.0 +import QtWebEngine 1.1 -WebView { +WebEngineView { id: root - objectName: "webview" anchors.fill: parent + objectName: "webview" url: "about:blank" } diff --git a/interface/resources/qml/controls/VrDialog.qml b/interface/resources/qml/controls/VrDialog.qml index aa14e2fcba..411fdbbb0b 100644 --- a/interface/resources/qml/controls/VrDialog.qml +++ b/interface/resources/qml/controls/VrDialog.qml @@ -41,6 +41,11 @@ DialogBase { // modify the visibility onEnabledChanged: { opacity = enabled ? 1.0 : 0.0 + // If the dialog is initially invisible, setting opacity doesn't + // trigger making it visible. + if (enabled) { + visible = true; + } } // The actual animator diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e3aa8ad4e1..f6350cb8d5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -11,6 +11,8 @@ #include "Application.h" +#include + #include #include #include @@ -28,11 +30,14 @@ #include #include #include -#include #include #include #include +#include +#include +#include + #include #include #include @@ -47,6 +52,7 @@ #include #include +#include #include #include #include @@ -89,6 +95,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +108,7 @@ #include #include #include +#include #include "AnimDebugDraw.h" #include "AudioClient.h" @@ -335,12 +343,15 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + #if defined(Q_OS_MAC) || defined(Q_OS_WIN) DependencyManager::set(); #endif DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -362,6 +373,17 @@ Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; + +// FIXME hack access to the internal share context for the Chromium helper +// Normally we'd want to use QWebEngine::initialize(), but we can't because +// our primary context is a QGLWidget, which can't easily be initialized to share +// from a QOpenGLContext. +// +// So instead we create a new offscreen context to share with the QGLWidget, +// and manually set THAT to be the shared context for the Chromium helper +OffscreenGLCanvas* _chromiumShareContext { nullptr }; +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); + Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv), _dependencyManagerIsSetup(setupEssentials(argc, argv)), @@ -623,6 +645,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _glWidget->makeCurrent(); _glWidget->initializeGL(); + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->create(_glWidget->context()->contextHandle()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->create(_glWidget->context()->contextHandle()); _offscreenContext->makeCurrent(); @@ -663,6 +690,74 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Setup the userInputMapper with the actions auto userInputMapper = DependencyManager::get(); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { + using namespace controller; + static auto offscreenUi = DependencyManager::get(); + if (offscreenUi->navigationFocused()) { + auto actionEnum = static_cast(action); + int key = Qt::Key_unknown; + static int lastKey = Qt::Key_unknown; + bool navAxis = false; + switch (actionEnum) { + case Action::UI_NAV_VERTICAL: + navAxis = true; + if (state > 0.0f) { + key = Qt::Key_Up; + } else if (state < 0.0f) { + key = Qt::Key_Down; + } + break; + + case Action::UI_NAV_LATERAL: + navAxis = true; + if (state > 0.0f) { + key = Qt::Key_Right; + } else if (state < 0.0f) { + key = Qt::Key_Left; + } + break; + + case Action::UI_NAV_GROUP: + navAxis = true; + if (state > 0.0f) { + key = Qt::Key_Tab; + } else if (state < 0.0f) { + key = Qt::Key_Backtab; + } + break; + + case Action::UI_NAV_BACK: + key = Qt::Key_Escape; + break; + + case Action::UI_NAV_SELECT: + key = Qt::Key_Return; + break; + } + + if (navAxis) { + if (lastKey != Qt::Key_unknown) { + QKeyEvent event(QEvent::KeyRelease, lastKey, Qt::NoModifier); + sendEvent(offscreenUi->getWindow(), &event); + lastKey = Qt::Key_unknown; + } + + if (key != Qt::Key_unknown) { + QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier); + sendEvent(offscreenUi->getWindow(), &event); + lastKey = key; + } + } else if (key != Qt::Key_unknown) { + if (state) { + QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier); + sendEvent(offscreenUi->getWindow(), &event); + } else { + QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier); + sendEvent(offscreenUi->getWindow(), &event); + } + return; + } + } + if (action == controller::toInt(controller::Action::RETICLE_CLICK)) { auto globalPos = QCursor::pos(); auto localPos = _glWidget->mapFromGlobal(globalPos); @@ -686,13 +781,37 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { VrMenu::toggle(); // show context menu even on non-stereo displays } else if (action == controller::toInt(controller::Action::RETICLE_X)) { - auto globalPos = QCursor::pos(); - globalPos.setX(globalPos.x() + state); - QCursor::setPos(globalPos); + auto oldPos = QCursor::pos(); + auto newPos = oldPos; + newPos.setX(oldPos.x() + state); + QCursor::setPos(newPos); + + + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPosG = { oldPos.x(), oldPos.y() }; + glm::vec2 newPosG = { newPos.x(), newPos.y() }; + auto distance = glm::distance(oldPosG, newPosG); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Action::RETICLE_X... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG; + } + } else if (action == controller::toInt(controller::Action::RETICLE_Y)) { - auto globalPos = QCursor::pos(); - globalPos.setY(globalPos.y() + state); - QCursor::setPos(globalPos); + auto oldPos = QCursor::pos(); + auto newPos = oldPos; + newPos.setY(oldPos.y() + state); + QCursor::setPos(newPos); + + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPosG = { oldPos.x(), oldPos.y() }; + glm::vec2 newPosG = { newPos.x(), newPos.y() }; + auto distance = glm::distance(oldPosG, newPosG); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Action::RETICLE_Y... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG; + } } } }); @@ -706,9 +825,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); })); - _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { - return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); - })); + _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { + return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); + })); + _applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float { + static auto offscreenUi = DependencyManager::get(); + return offscreenUi->navigationFocused() ? 1.0 : 0.0; + })); userInputMapper->registerDevice(_applicationStateDevice); @@ -1051,9 +1174,59 @@ void Application::initializeUi() { offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->load("Root.qml"); offscreenUi->load("RootMenu.qml"); - auto scriptingInterface = DependencyManager::get(); - offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data()); - offscreenUi->getRootContext()->setContextProperty("MyAvatar", getMyAvatar()); + // FIXME either expose so that dialogs can set this themselves or + // do better detection in the offscreen UI of what has focus + offscreenUi->setNavigationFocused(false); + + auto rootContext = offscreenUi->getRootContext(); + auto engine = rootContext->engine(); + connect(engine, &QQmlEngine::quit, [] { + qApp->quit(); + }); + rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); + rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); + rootContext->setContextProperty("Controller", DependencyManager::get().data()); + rootContext->setContextProperty("Entities", DependencyManager::get().data()); + rootContext->setContextProperty("MyAvatar", getMyAvatar()); + rootContext->setContextProperty("Messages", DependencyManager::get().data()); + rootContext->setContextProperty("Recording", DependencyManager::get().data()); + + rootContext->setContextProperty("TREE_SCALE", TREE_SCALE); + rootContext->setContextProperty("Quat", new Quat()); + rootContext->setContextProperty("Vec3", new Vec3()); + rootContext->setContextProperty("Uuid", new ScriptUUID()); + + rootContext->setContextProperty("AvatarList", DependencyManager::get().data()); + + rootContext->setContextProperty("Camera", &_myCamera); + +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) + rootContext->setContextProperty("SpeechRecognizer", DependencyManager::get().data()); +#endif + + rootContext->setContextProperty("Overlays", &_overlays); + rootContext->setContextProperty("Desktop", DependencyManager::get().data()); + + rootContext->setContextProperty("Window", DependencyManager::get().data()); + rootContext->setContextProperty("Menu", MenuScriptingInterface::getInstance()); + rootContext->setContextProperty("Stats", Stats::getInstance()); + rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); + rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); + rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); + rootContext->setContextProperty("SoundCache", DependencyManager::get().data()); + rootContext->setContextProperty("Account", AccountScriptingInterface::getInstance()); + rootContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface); + rootContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); + rootContext->setContextProperty("FaceTracker", DependencyManager::get().data()); + rootContext->setContextProperty("AvatarManager", DependencyManager::get().data()); + rootContext->setContextProperty("UndoStack", &_undoStackScriptingInterface); + rootContext->setContextProperty("LODManager", DependencyManager::get().data()); + rootContext->setContextProperty("Paths", DependencyManager::get().data()); + rootContext->setContextProperty("HMD", DependencyManager::get().data()); + rootContext->setContextProperty("Scene", DependencyManager::get().data()); + rootContext->setContextProperty("Render", DependencyManager::get().data()); + rootContext->setContextProperty("ScriptDiscoveryService", this->getRunningScriptsWidget()); + _glWidget->installEventFilter(offscreenUi.data()); VrMenu::load(); VrMenu::executeQueuedLambdas(); @@ -1161,26 +1334,15 @@ void Application::paintGL() { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { PerformanceTimer perfTimer("Mirror"); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + renderArgs._blitFramebuffer = DependencyManager::get()->getSelfieFramebuffer(); + renderRearViewMirror(&renderArgs, _mirrorViewRect); + + renderArgs._blitFramebuffer.reset(); renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - { - float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale()); - // Flip the src and destination rect horizontally to do the mirror - auto mirrorRect = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio); - auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w); - - auto selfieFbo = DependencyManager::get()->getSelfieFramebuffer(); - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - batch.setFramebuffer(selfieFbo); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); - batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest); - batch.setFramebuffer(nullptr); - }); - } } { @@ -1351,65 +1513,11 @@ void Application::paintGL() { renderArgs._context->setStereoProjections(eyeProjections); renderArgs._context->setStereoViews(eyeOffsets); } + renderArgs._blitFramebuffer = finalFramebuffer; displaySide(&renderArgs, _myCamera); + + renderArgs._blitFramebuffer.reset(); renderArgs._context->enableStereo(false); - - // Blit primary to final FBO - auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); - - if (renderArgs._renderMode == RenderArgs::MIRROR_RENDER_MODE) { - if (displayPlugin->isStereo()) { - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - gpu::Vec4i srcRectLeft; - srcRectLeft.z = size.width() / 2; - srcRectLeft.w = size.height(); - - gpu::Vec4i srcRectRight; - srcRectRight.x = size.width() / 2; - srcRectRight.z = size.width(); - srcRectRight.w = size.height(); - - gpu::Vec4i destRectLeft; - destRectLeft.x = srcRectLeft.z; - destRectLeft.z = srcRectLeft.x; - destRectLeft.y = srcRectLeft.y; - destRectLeft.w = srcRectLeft.w; - - gpu::Vec4i destRectRight; - destRectRight.x = srcRectRight.z; - destRectRight.z = srcRectRight.x; - destRectRight.y = srcRectRight.y; - destRectRight.w = srcRectRight.w; - - batch.setFramebuffer(finalFramebuffer); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); - // BLit left to right and right to left in stereo - batch.blit(primaryFbo, srcRectRight, finalFramebuffer, destRectLeft); - batch.blit(primaryFbo, srcRectLeft, finalFramebuffer, destRectRight); - }); - } else { - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - gpu::Vec4i srcRect; - srcRect.z = size.width(); - srcRect.w = size.height(); - gpu::Vec4i destRect; - destRect.x = size.width(); - destRect.y = 0; - destRect.z = 0; - destRect.w = size.height(); - batch.setFramebuffer(finalFramebuffer); - batch.blit(primaryFbo, srcRect, finalFramebuffer, destRect); - }); - } - } else { - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - gpu::Vec4i rect; - rect.z = size.width(); - rect.w = size.height(); - batch.setFramebuffer(finalFramebuffer); - batch.blit(primaryFbo, rect, finalFramebuffer, rect); - }); - } } // Overlay Composition, needs to occur after screen space effects have completed @@ -1417,7 +1525,9 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/compositor"); PerformanceTimer perfTimer("compositor"); + auto primaryFbo = finalFramebuffer; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo)); if (displayPlugin->isStereo()) { QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); @@ -1442,7 +1552,9 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PerformanceTimer perfTimer("pluginOutput"); + auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0); + GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer); Q_ASSERT(0 != finalTexture); @@ -2570,7 +2682,7 @@ void Application::init() { _environment.init(); - DependencyManager::get()->init(this); + DependencyManager::get()->init(); DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); @@ -3021,8 +3133,7 @@ void Application::update(float deltaTime) { getEntities()->update(); // update the models... } - myAvatar->harvestResultsFromPhysicsSimulation(); - myAvatar->simulateAttachments(deltaTime); + myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); } } @@ -3407,7 +3518,7 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { renderArgs->_renderMode = RenderArgs::DEFAULT_RENDER_MODE; renderRearViewMirror(renderArgs, QRect(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), true); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); renderArgs->_context->downloadFramebuffer(primaryFbo, glm::ivec4(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), image); @@ -3508,78 +3619,86 @@ namespace render { // Background rendering decision auto skyStage = DependencyManager::get()->getSkyStage(); - if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) { + auto backgroundMode = skyStage->getBackgroundMode(); + + if (backgroundMode == model::SunSkyStage::NO_BACKGROUND) { // this line intentionally left blank - } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) { - if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceTimer perfTimer("stars"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::payloadRender() ... stars..."); - // should be the first rendering pass - w/o depth buffer / lighting - - // compute starfield alpha based on distance from atmosphere - float alpha = 1.0f; - bool hasStars = true; - - if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - // TODO: handle this correctly for zones - const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum - - if (closestData.getHasStars()) { - const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; - const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; - - glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation()) - / closestData.getAtmosphereOuterRadius(); - float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter()); - if (height < closestData.getAtmosphereInnerRadius()) { - // If we're inside the atmosphere, then determine if our keyLight is below the horizon - alpha = 0.0f; - - if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) - + APPROXIMATE_DISTANCE_FROM_HORIZON; - alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); - } - - - } else if (height < closestData.getAtmosphereOuterRadius()) { - alpha = (height - closestData.getAtmosphereInnerRadius()) / - (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); - - if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) - + APPROXIMATE_DISTANCE_FROM_HORIZON; - alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); - } - } - } else { - hasStars = false; - } + } else { + if (backgroundMode == model::SunSkyStage::SKY_BOX) { + auto skybox = skyStage->getSkybox(); + if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) { + PerformanceTimer perfTimer("skybox"); + skybox->render(batch, *(args->_viewFrustum)); + } else { + // If no skybox texture is available, render the SKY_DOME while it loads + backgroundMode = model::SunSkyStage::SKY_DOME; } - - // finally render the starfield - if (hasStars) { - background->_stars.render(args, alpha); - } - - // draw the sky dome - if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - PerformanceTimer perfTimer("atmosphere"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... atmosphere..."); - - background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); - } - } - } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { - PerformanceTimer perfTimer("skybox"); - auto skybox = skyStage->getSkybox(); - if (skybox) { - skybox->render(batch, *(args->_viewFrustum)); + if (backgroundMode == model::SunSkyStage::SKY_DOME) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { + PerformanceTimer perfTimer("stars"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::payloadRender() ... stars..."); + // should be the first rendering pass - w/o depth buffer / lighting + + // compute starfield alpha based on distance from atmosphere + float alpha = 1.0f; + bool hasStars = true; + + if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { + // TODO: handle this correctly for zones + const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum + + if (closestData.getHasStars()) { + const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; + const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; + + glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation()) + / closestData.getAtmosphereOuterRadius(); + float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter()); + if (height < closestData.getAtmosphereInnerRadius()) { + // If we're inside the atmosphere, then determine if our keyLight is below the horizon + alpha = 0.0f; + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + + + } else if (height < closestData.getAtmosphereOuterRadius()) { + alpha = (height - closestData.getAtmosphereInnerRadius()) / + (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + } + } else { + hasStars = false; + } + } + + // finally render the starfield + if (hasStars) { + background->_stars.render(args, alpha); + } + + // draw the sky dome + if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { + PerformanceTimer perfTimer("atmosphere"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::displaySide() ... atmosphere..."); + + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); + } + + } } } } @@ -3677,34 +3796,19 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // For now every frame pass the renderContext { PerformanceTimer perfTimer("EngineRun"); - render::RenderContext renderContext; - auto sceneInterface = DependencyManager::get(); - - renderContext._cullOpaque = sceneInterface->doEngineCullOpaque(); - renderContext._sortOpaque = sceneInterface->doEngineSortOpaque(); - renderContext._renderOpaque = sceneInterface->doEngineRenderOpaque(); - renderContext._cullTransparent = sceneInterface->doEngineCullTransparent(); - renderContext._sortTransparent = sceneInterface->doEngineSortTransparent(); - renderContext._renderTransparent = sceneInterface->doEngineRenderTransparent(); - - renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems(); - renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); - renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems(); - - renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus(); - if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) { - renderContext._drawItemStatus |= render::showNetworkStatusFlag; - } - renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect(); - - renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); - renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing); + auto renderInterface = DependencyManager::get(); + auto renderContext = renderInterface->getRenderContext(); renderArgs->_shouldRender = LODManager::shouldRender; - - renderContext.args = renderArgs; renderArgs->_viewFrustum = getDisplayViewFrustum(); + renderContext.setArgs(renderArgs); + + bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); + bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing); + bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned); + renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus); + _renderEngine->setRenderContext(renderContext); // Before the deferred pass, let's try to use the render engine @@ -3712,15 +3816,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se _renderEngine->run(); myAvatar->endRenderRun(); - auto engineRC = _renderEngine->getRenderContext(); - sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems); - sceneInterface->setEngineDrawnOpaqueItems(engineRC->_numDrawnOpaqueItems); - - sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems); - sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems); - - sceneInterface->setEngineFeedOverlay3DItems(engineRC->_numFeedOverlay3DItems); - sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems); + auto engineContext = _renderEngine->getRenderContext(); + renderInterface->setItemCounts(engineContext->getItemsConfig()); } activeRenderingThread = nullptr; @@ -4146,6 +4243,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri LocationScriptingInterface::locationSetter); scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); + scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); + scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); @@ -4174,11 +4273,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Render", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); } -bool Application::canAcceptURL(const QString& urlString) { +bool Application::canAcceptURL(const QString& urlString) const { QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; diff --git a/interface/src/Application.h b/interface/src/Application.h index a665e925a9..0953aedd8c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "avatar/AvatarUpdate.h" #include "avatar/MyAvatar.h" @@ -88,7 +89,7 @@ class Application; #endif #define qApp (static_cast(QCoreApplication::instance())) -class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { +class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, public AbstractUriHandler { Q_OBJECT // TODO? Get rid of those @@ -219,8 +220,8 @@ public: QString getScriptsLocation(); void setScriptsLocation(const QString& scriptsLocation); - bool canAcceptURL(const QString& url); - bool acceptURL(const QString& url, bool defaultUpload = false); + virtual bool canAcceptURL(const QString& url) const override; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae22869273..85af912892 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1085,6 +1085,26 @@ void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) { QMenuBar::repaint(); } +void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected) { + auto menu = addMenu(groupName); + + QActionGroup* actionGroup = new QActionGroup(menu); + actionGroup->setExclusive(true); + + auto menuScriptingInterface = MenuScriptingInterface::getInstance(); + for (auto action : actionList) { + auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected, + menuScriptingInterface, + SLOT(menuItemTriggered())); + actionGroup->addAction(item); + } + + QMenuBar::repaint(); +} + +void Menu::removeActionGroup(const QString& groupName) { + removeMenu(groupName); +} MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { VrMenu::executeOrQueue([=](VrMenu* vrMenu) { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 22da7d9127..4d898caefc 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -106,6 +106,8 @@ public slots: void addMenuItem(const MenuItemProperties& properties); void removeMenuItem(const QString& menuName, const QString& menuitem); bool menuItemExists(const QString& menuName, const QString& menuitem); + void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString()); + void removeActionGroup(const QString& groupName); bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d0b6588380..d9e2159df7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -42,6 +42,7 @@ #include "Util.h" #include "world.h" #include "InterfaceLogging.h" +#include "SoftAttachmentModel.h" #include using namespace std; @@ -108,9 +109,6 @@ Avatar::Avatar(RigPointer rig) : Avatar::~Avatar() { assert(_motionState == nullptr); - for(auto attachment : _unusedAttachments) { - delete attachment; - } } const float BILLBOARD_LOD_DISTANCE = 40.0f; @@ -237,6 +235,8 @@ void Avatar::simulate(float deltaTime) { // until velocity is included in AvatarData update message. //_position += _velocity * deltaTime; measureMotionDerivatives(deltaTime); + + simulateAttachments(deltaTime); } bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { @@ -304,7 +304,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr _skeletonModel.addToScene(scene, pendingChanges); getHead()->getFaceModel().addToScene(scene, pendingChanges); - for (auto attachmentModel : _attachmentModels) { + for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, pendingChanges); } @@ -315,7 +315,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptrgetFaceModel().removeFromScene(scene, pendingChanges); - for (auto attachmentModel : _attachmentModels) { + for (auto& attachmentModel : _attachmentModels) { attachmentModel->removeFromScene(scene, pendingChanges); } } @@ -545,15 +545,14 @@ void Avatar::fixupModelsInScene() { faceModel.removeFromScene(scene, pendingChanges); faceModel.addToScene(scene, pendingChanges); } - for (auto attachmentModel : _attachmentModels) { + for (auto& attachmentModel : _attachmentModels) { if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { attachmentModel->removeFromScene(scene, pendingChanges); attachmentModel->addToScene(scene, pendingChanges); } } - for (auto attachmentModelToRemove : _attachmentsToRemove) { + for (auto& attachmentModelToRemove : _attachmentsToRemove) { attachmentModelToRemove->removeFromScene(scene, pendingChanges); - _unusedAttachments << attachmentModelToRemove; } _attachmentsToRemove.clear(); scene->enqueuePendingChanges(pendingChanges); @@ -583,21 +582,29 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { return true; } +// virtual void Avatar::simulateAttachments(float deltaTime) { for (int i = 0; i < _attachmentModels.size(); i++) { const AttachmentData& attachment = _attachmentData.at(i); - Model* model = _attachmentModels.at(i); + auto& model = _attachmentModels.at(i); int jointIndex = getJointIndex(attachment.jointName); glm::vec3 jointPosition; glm::quat jointRotation; - if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) { - model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale()); - model->setRotation(jointRotation * attachment.rotation); - model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale - model->setSnapModelToCenter(false); // hack to force resnap - model->setSnapModelToCenter(true); + if (attachment.isSoft) { + // soft attachments do not have transform offsets + model->setTranslation(getPosition()); + model->setRotation(getOrientation() * Quaternions::Y_180); model->simulate(deltaTime); + } else { + if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && + _skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) { + model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale()); + model->setRotation(jointRotation * attachment.rotation); + model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale + model->setSnapModelToCenter(false); // hack to force resnap + model->setSnapModelToCenter(true); + model->simulate(deltaTime); + } } } } @@ -920,13 +927,48 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModel.setURL(_skeletonModelURL); } +// create new model, can return an instance of a SoftAttachmentModel rather then Model +static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) { + if (isSoft) { + // cast to std::shared_ptr + return std::dynamic_pointer_cast(std::make_shared(std::make_shared(), nullptr, rigOverride)); + } else { + return std::make_shared(std::make_shared()); + } +} + void Avatar::setAttachmentData(const QVector& attachmentData) { - AvatarData::setAttachmentData(attachmentData); if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection, Q_ARG(const QVector, attachmentData)); return; } + + auto oldAttachmentData = _attachmentData; + AvatarData::setAttachmentData(attachmentData); + + // if number of attachments has been reduced, remove excess models. + while (_attachmentModels.size() > attachmentData.size()) { + auto attachmentModel = _attachmentModels.back(); + _attachmentModels.pop_back(); + _attachmentsToRemove.push_back(attachmentModel); + } + + for (int i = 0; i < attachmentData.size(); i++) { + if (i == _attachmentModels.size()) { + // if number of attachments has been increased, we need to allocate a new model + _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig())); + } + else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { + // if the attachment has changed type, we need to re-allocate a new one. + _attachmentsToRemove.push_back(_attachmentModels[i]); + _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig()); + } + _attachmentModels[i]->setURL(attachmentData[i].modelURL); + } + + // AJT: TODO REMOVE + /* // make sure we have as many models as attachments while (_attachmentModels.size() < attachmentData.size()) { Model* model = nullptr; @@ -939,16 +981,20 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { _attachmentModels.append(model); } while (_attachmentModels.size() > attachmentData.size()) { - auto attachmentModel = _attachmentModels.takeLast(); - _attachmentsToRemove << attachmentModel; + auto attachmentModel = _attachmentModels.back(); + _attachmentModels.pop_back(); + _attachmentsToRemove.push_back(attachmentModel); } + */ + /* // update the urls for (int i = 0; i < attachmentData.size(); i++) { _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); _attachmentModels[i]->setSnapModelToCenter(true); _attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale); } + */ } void Avatar::setBillboard(const QByteArray& billboard) { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 4926212b4d..6d4477e93f 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -68,7 +68,7 @@ public: void init(); void simulate(float deltaTime); - void simulateAttachments(float deltaTime); + virtual void simulateAttachments(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition); @@ -177,9 +177,9 @@ protected: SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; - QVector _attachmentModels; - QVector _attachmentsToRemove; - QVector _unusedAttachments; + std::vector> _attachmentModels; + std::vector> _attachmentsToRemove; + float _bodyYawDelta; // degrees/sec // These position histories and derivatives are in the world-frame. diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index f672600db9..7d8d5e4c9b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -269,8 +269,8 @@ QVector AvatarManager::getAvatarIdentifiers() { } AvatarData* AvatarManager::getAvatar(QUuid avatarID) { - QReadLocker locker(&_hashLock); - return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar. + // Null/Default-constructed QUuids will return MyAvatar + return getAvatarBySessionID(avatarID).get(); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c989f1dc71..000cd75784 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -201,6 +201,11 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } +// virtual +void MyAvatar::simulateAttachments(float deltaTime) { + // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() +} + QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { CameraMode mode = qApp->getCamera()->getMode(); _globalPosition = getPosition(); @@ -621,6 +626,7 @@ void MyAvatar::saveData() { settings.setValue("rotation_y", eulers.y); settings.setValue("rotation_z", eulers.z); settings.setValue("scale", attachment.scale); + settings.setValue("isSoft", attachment.isSoft); } settings.endArray(); @@ -702,6 +708,7 @@ void MyAvatar::loadData() { eulers.z = loadSetting(settings, "rotation_z", 0.0f); attachment.rotation = glm::quat(eulers); attachment.scale = loadSetting(settings, "scale", 1.0f); + attachment.isSoft = settings.value("isSoft").toBool(); attachmentData.append(attachment); } settings.endArray(); @@ -1057,7 +1064,7 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setFollowVelocity(_followVelocity); } -void MyAvatar::harvestResultsFromPhysicsSimulation() { +void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaType) { glm::vec3 position = getPosition(); glm::quat orientation = getOrientation(); _characterController.getPositionAndOrientation(position, orientation); @@ -1068,6 +1075,9 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() { } else { setVelocity(_characterController.getLinearVelocity()); } + + // now that physics has adjusted our position, we can update attachements. + Avatar::simulateAttachments(deltaType); } void MyAvatar::adjustSensorTransform() { @@ -1599,7 +1609,7 @@ void MyAvatar::maybeUpdateBillboard() { if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) { return; } - foreach (Model* model, _attachmentModels) { + for (auto& model : _attachmentModels) { if (!model->isLoadedWithTextures()) { return; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 019ba0f992..1a5753d0a5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -83,6 +83,8 @@ public: MyAvatar(RigPointer rig); ~MyAvatar(); + virtual void simulateAttachments(float deltaTime) override; + AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; } AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; } AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; } @@ -204,7 +206,7 @@ public: MyCharacterController* getCharacterController() { return &_characterController; } void prepareForPhysicsSimulation(); - void harvestResultsFromPhysicsSimulation(); + void harvestResultsFromPhysicsSimulation(float deltaTime); void adjustSensorTransform(); const QString& getCollisionSoundURL() { return _collisionSoundURL; } diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp new file mode 100644 index 0000000000..92534f01ea --- /dev/null +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -0,0 +1,84 @@ +// +// SoftAttachmentModel.cpp +// interface/src/avatar +// +// Created by Anthony J. Thibault on 12/17/15. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "SoftAttachmentModel.h" +#include "InterfaceLogging.h" + +SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : + Model(rig, parent), + _rigOverride(rigOverride) { + assert(_rig); + assert(_rigOverride); +} + +SoftAttachmentModel::~SoftAttachmentModel() { +} + +// virtual +void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + _needsUpdateClusterMatrices = true; +} + +int SoftAttachmentModel::getJointIndexOverride(int i) const { + QString name = _rig->nameOfJoint(i); + if (name.isEmpty()) { + return -1; + } + return _rigOverride->indexOfJoint(name); +} + +// virtual +// use the _rigOverride matrices instead of the Model::_rig +void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { + if (!_needsUpdateClusterMatrices) { + return; + } + _needsUpdateClusterMatrices = false; + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + + glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); + for (int i = 0; i < _meshStates.size(); i++) { + MeshState& state = _meshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + + // TODO: cache these look ups as an optimization + int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); + glm::mat4 jointMatrix(glm::mat4::_null); + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) { + jointMatrix = _rigOverride->getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig->getJointTransform(cluster.jointIndex); + } + state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + } + + // Once computed the cluster matrices, update the buffer(s) + if (mesh.clusters.size() > 1) { + if (!state.clusterBuffer) { + state.clusterBuffer = std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } else { + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } + } + } + + // post the blender if we're not currently waiting for one to finish + if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + _blendedBlendshapeCoefficients = _blendshapeCoefficients; + DependencyManager::get()->noteRequiresBlend(this); + } +} diff --git a/interface/src/avatar/SoftAttachmentModel.h b/interface/src/avatar/SoftAttachmentModel.h new file mode 100644 index 0000000000..84a8b4cc66 --- /dev/null +++ b/interface/src/avatar/SoftAttachmentModel.h @@ -0,0 +1,42 @@ +// +// SoftAttachmentModel.h +// interface/src/avatar +// +// Created by Anthony J. Thibault on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_SoftAttachmentModel_h +#define hifi_SoftAttachmentModel_h + +#include + +// A model that allows the creator to specify a secondary rig instance. +// When the cluster matrices are created for rendering, the +// cluster matrices will use the secondary rig for the joint poses +// instead of the primary rig. +// +// This is used by Avatar instances to wear clothing that follows the same +// animated pose as the SkeletonModel. + +class SoftAttachmentModel : public Model { + Q_OBJECT + +public: + + SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride); + ~SoftAttachmentModel(); + + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) override; + +protected: + int getJointIndexOverride(int i) const; + + RigPointer _rigOverride; +}; + +#endif // hifi_SoftAttachmentModel_h diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index 87ea3220a4..126cd53003 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -17,7 +17,6 @@ AccountScriptingInterface::AccountScriptingInterface() { AccountManager& accountManager = AccountManager::getInstance(); connect(&accountManager, &AccountManager::balanceChanged, this, &AccountScriptingInterface::updateBalance); - } AccountScriptingInterface* AccountScriptingInterface::getInstance() { @@ -39,3 +38,12 @@ void AccountScriptingInterface::updateBalance() { AccountManager& accountManager = AccountManager::getInstance(); emit balanceChanged(accountManager.getAccountInfo().getBalanceInSatoshis()); } + +QString AccountScriptingInterface::getUsername() { + AccountManager& accountManager = AccountManager::getInstance(); + if (accountManager.isLoggedIn()) { + return accountManager.getAccountInfo().getUsername(); + } else { + return "Unknown user"; + } +} diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index e9cf0ede5f..578a9d6728 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -24,6 +24,7 @@ signals: public slots: static AccountScriptingInterface* getInstance(); float getBalance(); + QString getUsername(); bool isLoggedIn(); void updateBalance(); }; diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index ff7784b9ae..087d391daa 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -84,6 +84,19 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& return result; } +void MenuScriptingInterface::addActionGroup(const QString& groupName, const QStringList& actionList, + const QString& selected) { + QMetaObject::invokeMethod(Menu::getInstance(), "addActionGroup", + Q_ARG(const QString&, groupName), + Q_ARG(const QStringList&, actionList), + Q_ARG(const QString&, selected)); +} + +void MenuScriptingInterface::removeActionGroup(const QString& groupName) { + QMetaObject::invokeMethod(Menu::getInstance(), "removeActionGroup", + Q_ARG(const QString&, groupName)); +} + bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { bool result; QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 5c01318a38..51399c2fa5 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -42,6 +42,10 @@ public slots: void removeMenuItem(const QString& menuName, const QString& menuitem); bool menuItemExists(const QString& menuName, const QString& menuitem); + void addActionGroup(const QString& groupName, const QStringList& actionList, + const QString& selected = QString()); + void removeActionGroup(const QString& groupName); + bool isOptionChecked(const QString& menuOption); void setIsOptionChecked(const QString& menuOption, bool isChecked); diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index 1d53eb3871..d718b52d6d 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -27,13 +28,13 @@ AttachmentsDialog::AttachmentsDialog(QWidget* parent) : QDialog(parent) { - + setWindowTitle("Edit Attachments"); setAttribute(Qt::WA_DeleteOnClose); - + QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); - + QScrollArea* area = new QScrollArea(); layout->addWidget(area); area->setWidgetResizable(true); @@ -42,26 +43,26 @@ AttachmentsDialog::AttachmentsDialog(QWidget* parent) : container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); area->setWidget(container); _attachments->addStretch(1); - + foreach (const AttachmentData& data, DependencyManager::get()->getMyAvatar()->getAttachmentData()) { addAttachment(data); } - + QPushButton* newAttachment = new QPushButton("New Attachment"); connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment())); layout->addWidget(newAttachment); - + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok); layout->addWidget(buttons); connect(buttons, SIGNAL(accepted()), SLOT(deleteLater())); _ok = buttons->button(QDialogButtonBox::Ok); - + setMinimumSize(600, 600); } void AttachmentsDialog::setVisible(bool visible) { QDialog::setVisible(visible); - + // un-default the OK button if (visible) { _ok->setDefault(false); @@ -104,11 +105,11 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData _dialog(dialog), _applying(false) { setFrameStyle(QFrame::StyledPanel); - + QFormLayout* layout = new QFormLayout(); layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); setLayout(layout); - + QHBoxLayout* urlBox = new QHBoxLayout(); layout->addRow("Model URL:", urlBox); urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1); @@ -117,7 +118,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData QPushButton* chooseURL = new QPushButton("Choose"); urlBox->addWidget(chooseURL); connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL())); - + layout->addRow("Joint:", _jointName = new QComboBox()); QSharedPointer geometry = DependencyManager::get()->getMyAvatar()->getSkeletonModel().getGeometry(); if (geometry && geometry->isLoaded()) { @@ -127,26 +128,30 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData } _jointName->setCurrentText(data.jointName); connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged())); - + QHBoxLayout* translationBox = new QHBoxLayout(); translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x)); translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y)); translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z)); layout->addRow("Translation:", translationBox); - + QHBoxLayout* rotationBox = new QHBoxLayout(); glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation)); rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x)); rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y)); rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z)); layout->addRow("Rotation:", rotationBox); - + layout->addRow("Scale:", _scale = new QDoubleSpinBox()); _scale->setSingleStep(0.01); _scale->setMaximum(FLT_MAX); _scale->setValue(data.scale); connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); - + + layout->addRow("Is Soft:", _isSoft = new QCheckBox()); + _isSoft->setChecked(data.isSoft); + connect(_isSoft, SIGNAL(stateChanged(int)), SLOT(updateAttachmentData())); + QPushButton* remove = new QPushButton("Delete"); layout->addRow(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater())); @@ -160,6 +165,7 @@ AttachmentData AttachmentPanel::getAttachmentData() const { data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value()); data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value()))); data.scale = _scale->value(); + data.isSoft = _isSoft->isChecked(); return data; } @@ -227,6 +233,7 @@ void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) { _rotationY->setValue(eulers.y); _rotationZ->setValue(eulers.z); _scale->setValue(attachment.scale); + _isSoft->setChecked(attachment.isSoft); _applying = false; _dialog->updateAttachmentData(); } diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h index d3b9219a16..43ba5f8f3e 100644 --- a/interface/src/ui/AttachmentsDialog.h +++ b/interface/src/ui/AttachmentsDialog.h @@ -36,11 +36,11 @@ public slots: void updateAttachmentData(); private slots: - + void addAttachment(const AttachmentData& data = AttachmentData()); private: - + QVBoxLayout* _attachments; QPushButton* _ok; }; @@ -50,7 +50,7 @@ class AttachmentPanel : public QFrame { Q_OBJECT public: - + AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData()); AttachmentData getAttachmentData() const; @@ -64,9 +64,9 @@ private slots: void updateAttachmentData(); private: - + void applyAttachmentData(const AttachmentData& attachment); - + AttachmentsDialog* _dialog; QLineEdit* _modelURL; QComboBox* _jointName; @@ -77,6 +77,7 @@ private: QDoubleSpinBox* _rotationY; QDoubleSpinBox* _rotationZ; QDoubleSpinBox* _scale; + QCheckBox* _isSoft; bool _applying; }; diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp new file mode 100644 index 0000000000..79004a72a6 --- /dev/null +++ b/libraries/animation/src/AnimExpression.cpp @@ -0,0 +1,676 @@ +// +// AnimExpression.cpp +// +// Created by Anthony J. Thibault on 11/1/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include "AnimExpression.h" +#include "AnimationLogging.h" + +AnimExpression::AnimExpression(const QString& str) : + _expression(str) { + auto iter = str.begin(); + parseExpr(_expression, iter); + while(!_tokenStack.empty()) { + _tokenStack.pop(); + } +} + +// +// Tokenizer +// + +void AnimExpression::unconsumeToken(const Token& token) { + _tokenStack.push(token); +} + +AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const { + if (!_tokenStack.empty()) { + Token top = _tokenStack.top(); + _tokenStack.pop(); + return top; + } else { + while (iter != str.end()) { + if (iter->isSpace()) { + ++iter; + } else if (iter->isLetter()) { + return consumeIdentifier(str, iter); + } else if (iter->isDigit()) { + return consumeNumber(str, iter); + } else { + switch (iter->unicode()) { + case '&': return consumeAnd(str, iter); + case '|': return consumeOr(str, iter); + case '>': return consumeGreaterThan(str, iter); + case '<': return consumeLessThan(str, iter); + case '(': ++iter; return Token(Token::LeftParen); + case ')': ++iter; return Token(Token::RightParen); + case '!': return consumeNot(str, iter); + case '-': ++iter; return Token(Token::Minus); + case '+': ++iter; return Token(Token::Plus); + case '*': ++iter; return Token(Token::Multiply); + case '/': ++iter; return Token(Token::Divide); + case '%': ++iter; return Token(Token::Modulus); + case ',': ++iter; return Token(Token::Comma); + default: + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } + } + } + return Token(Token::End); + } +} + +AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->isLetter()); + auto begin = iter; + while ((iter->isLetter() || iter->isDigit()) && iter != str.end()) { + ++iter; + } + int pos = (int)(begin - str.begin()); + int len = (int)(iter - begin); + + QStringRef stringRef(const_cast(&str), pos, len); + if (stringRef == "true") { + return Token(true); + } else if (stringRef == "false") { + return Token(false); + } else { + return Token(stringRef); + } +} + +// TODO: not very efficient or accruate, but it's close enough for now. +static float computeFractionalPart(int fractionalPart) +{ + float frac = (float)fractionalPart; + while (fractionalPart) { + fractionalPart /= 10; + frac /= 10.0f; + } + return frac; +} + +static float computeFloat(int whole, int fraction) { + return (float)whole + computeFractionalPart(fraction); +} + +AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->isDigit()); + auto begin = iter; + while (iter->isDigit() && iter != str.end()) { + ++iter; + } + + // parse whole integer part + int pos = (int)(begin - str.begin()); + int len = (int)(iter - begin); + QString sub = QStringRef(const_cast(&str), pos, len).toString(); + int whole = sub.toInt(); + + // parse optional fractional part + if (iter->unicode() == '.') { + iter++; + auto begin = iter; + while (iter->isDigit() && iter != str.end()) { + ++iter; + } + + int pos = (int)(begin - str.begin()); + int len = (int)(iter - begin); + QString sub = QStringRef(const_cast(&str), pos, len).toString(); + int fraction = sub.toInt(); + + return Token(computeFloat(whole, fraction)); + + } else { + return Token(whole); + } +} + +AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '&'); + iter++; + if (iter->unicode() == '&') { + iter++; + return Token(Token::And); + } else { + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } +} + +AnimExpression::Token AnimExpression::consumeOr(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '|'); + iter++; + if (iter->unicode() == '|') { + iter++; + return Token(Token::Or); + } else { + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } +} + +AnimExpression::Token AnimExpression::consumeGreaterThan(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '>'); + iter++; + if (iter->unicode() == '=') { + iter++; + return Token(Token::GreaterThanEqual); + } else { + return Token(Token::GreaterThan); + } +} + +AnimExpression::Token AnimExpression::consumeLessThan(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '<'); + iter++; + if (iter->unicode() == '=') { + iter++; + return Token(Token::LessThanEqual); + } else { + return Token(Token::LessThan); + } +} + +AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '!'); + iter++; + if (iter->unicode() == '=') { + iter++; + return Token(Token::NotEqual); + } else { + return Token(Token::Not); + } +} + +// +// Parser +// + +/* +Expr → Term Expr' +Expr' → '||' Term Expr' + | ε +Term → Unary Term' +Term' → '&&' Unary Term' + | ε +Unary → '!' Unary + | Factor +Factor → INT + | BOOL + | FLOAT + | IDENTIFIER + | '(' Expr ')' +*/ + +// Expr → Term Expr' +bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter) { + if (!parseTerm(str, iter)) { + return false; + } + if (!parseExprPrime(str, iter)) { + return false; + } + return true; +} + +// Expr' → '||' Term Expr' | ε +bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::Or) { + if (!parseTerm(str, iter)) { + unconsumeToken(token); + return false; + } + if (!parseExprPrime(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Or}); + return true; + } else { + unconsumeToken(token); + return true; + } +} + +// Term → Unary Term' +bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) { + if (!parseUnary(str, iter)) { + return false; + } + if (!parseTermPrime(str, iter)) { + return false; + } + return true; +} + +// Term' → '&&' Unary Term' | ε +bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::And) { + if (!parseUnary(str, iter)) { + unconsumeToken(token); + return false; + } + if (!parseTermPrime(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::And}); + return true; + } else { + unconsumeToken(token); + return true; + } +} + +// Unary → '!' Unary | Factor +bool AnimExpression::parseUnary(const QString& str, QString::const_iterator& iter) { + + auto token = consumeToken(str, iter); + if (token.type == Token::Not) { + if (!parseUnary(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Not}); + return true; + } + unconsumeToken(token); + + return parseFactor(str, iter); +} + + +// Factor → INT | BOOL | FLOAT | IDENTIFIER | '(' Expr ')' +bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::Int) { + _opCodes.push_back(OpCode {token.intVal}); + return true; + } else if (token.type == Token::Bool) { + _opCodes.push_back(OpCode {(bool)token.intVal}); + return true; + } else if (token.type == Token::Float) { + _opCodes.push_back(OpCode {token.floatVal}); + return true; + } else if (token.type == Token::Identifier) { + _opCodes.push_back(OpCode {token.strVal}); + return true; + } else if (token.type == Token::LeftParen) { + if (!parseExpr(str, iter)) { + unconsumeToken(token); + return false; + } + auto nextToken = consumeToken(str, iter); + if (nextToken.type != Token::RightParen) { + unconsumeToken(nextToken); + unconsumeToken(token); + return false; + } + return true; + } else { + unconsumeToken(token); + return false; + } +} + +// +// Evaluator +// + +AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const { + std::stack stack; + for (auto& opCode : _opCodes) { + switch (opCode.type) { + case OpCode::Identifier: + case OpCode::Int: + case OpCode::Float: + case OpCode::Bool: + stack.push(opCode); + break; + case OpCode::And: evalAnd(map, stack); break; + case OpCode::Or: evalOr(map, stack); break; + case OpCode::GreaterThan: evalGreaterThan(map, stack); break; + case OpCode::GreaterThanEqual: evalGreaterThanEqual(map, stack); break; + case OpCode::LessThan: evalLessThan(map, stack); break; + case OpCode::LessThanEqual: evalLessThanEqual(map, stack); break; + case OpCode::Equal: evalEqual(map, stack); break; + case OpCode::NotEqual: evalNotEqual(map, stack); break; + case OpCode::Not: evalNot(map, stack); break; + case OpCode::Subtract: evalSubtract(map, stack); break; + case OpCode::Add: evalAdd(map, stack); break; + case OpCode::Multiply: evalMultiply(map, stack); break; + case OpCode::Divide: evalDivide(map, stack); break; + case OpCode::Modulus: evalModulus(map, stack); break; + case OpCode::UnaryMinus: evalUnaryMinus(map, stack); break; + } + } + return coerseToValue(map, stack.top()); +} + +#define POP_BOOL(NAME) \ + const OpCode& NAME##_temp = stack.top(); \ + bool NAME = NAME##_temp.coerceBool(map); \ + stack.pop() + +#define PUSH(EXPR) \ + stack.push(OpCode {(EXPR)}) + +void AnimExpression::evalAnd(const AnimVariantMap& map, std::stack& stack) const { + POP_BOOL(lhs); + POP_BOOL(rhs); + PUSH(lhs && rhs); +} + +void AnimExpression::evalOr(const AnimVariantMap& map, std::stack& stack) const { + POP_BOOL(lhs); + POP_BOOL(rhs); + PUSH(lhs || rhs); +} + +void AnimExpression::evalGreaterThan(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalGreaterThanEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalLessThan(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalLessThanEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalNotEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalNot(const AnimVariantMap& map, std::stack& stack) const { + POP_BOOL(rhs); + PUSH(!rhs); +} + +void AnimExpression::evalSubtract(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(0.0f); +} + +void AnimExpression::add(int lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs + rhs.intVal); + break; + case OpCode::Float: + PUSH((float)lhs + rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::add(float lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs + (float)rhs.intVal); + break; + case OpCode::Float: + PUSH(lhs + rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = coerseToValue(map, stack.top()); + stack.pop(); + OpCode rhs = coerseToValue(map, stack.top()); + stack.pop(); + + switch (lhs.type) { + case OpCode::Bool: + add(lhs.intVal, rhs, stack); + break; + case OpCode::Int: + add(lhs.intVal, rhs, stack); + break; + case OpCode::Float: + add(lhs.floatVal, rhs, stack); + break; + default: + add(0, rhs, stack); + break; + } +} + +void AnimExpression::evalMultiply(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = coerseToValue(map, stack.top()); + stack.pop(); + OpCode rhs = coerseToValue(map, stack.top()); + stack.pop(); + + switch(lhs.type) { + case OpCode::Bool: + mul(lhs.intVal, rhs, stack); + break; + case OpCode::Int: + mul(lhs.intVal, rhs, stack); + break; + case OpCode::Float: + mul(lhs.floatVal, rhs, stack); + break; + default: + mul(0, rhs, stack); + break; + } +} + +void AnimExpression::mul(int lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs * rhs.intVal); + break; + case OpCode::Float: + PUSH((float)lhs * rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::mul(float lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs * (float)rhs.intVal); + break; + case OpCode::Float: + PUSH(lhs * rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::evalDivide(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(0.0f); +} + +void AnimExpression::evalModulus(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH((int)0); +} + +void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack& stack) const { + OpCode rhs = stack.top(); stack.pop(); + + switch (rhs.type) { + case OpCode::Identifier: { + const AnimVariant& var = map.get(rhs.strVal); + switch (var.getType()) { + case AnimVariant::Type::Bool: + qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool"; + // interpret this as boolean not. + PUSH(!var.getBool()); + break; + case AnimVariant::Type::Int: + PUSH(-var.getInt()); + break; + case AnimVariant::Type::Float: + PUSH(-var.getFloat()); + break; + default: + // TODO: Vec3, Quat are unsupported + assert(false); + PUSH(false); + break; + } + } + case OpCode::Int: + PUSH(-rhs.intVal); + break; + case OpCode::Float: + PUSH(-rhs.floatVal); + break; + case OpCode::Bool: + qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool"; + // interpret this as boolean not. + PUSH(!rhs.coerceBool(map)); + break; + default: + qCCritical(animation) << "AnimExpression: ERRROR for unary minus, expected a number, type = " << rhs.type; + assert(false); + PUSH(false); + break; + } +} + +AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const { + switch (opCode.type) { + case OpCode::Identifier: + { + const AnimVariant& var = map.get(opCode.strVal); + switch (var.getType()) { + case AnimVariant::Type::Bool: + return OpCode((bool)var.getBool()); + break; + case AnimVariant::Type::Int: + return OpCode(var.getInt()); + break; + case AnimVariant::Type::Float: + return OpCode(var.getFloat()); + break; + default: + // TODO: Vec3, Quat are unsupported + assert(false); + return OpCode(0); + break; + } + } + break; + case OpCode::Bool: + case OpCode::Int: + case OpCode::Float: + return opCode; + default: + qCCritical(animation) << "AnimExpression: ERROR expected a number, type = " << opCode.type; + assert(false); + return OpCode(0); + break; + } +} + +#ifndef NDEBUG +void AnimExpression::dumpOpCodes() const { + QString tmp; + for (auto& op : _opCodes) { + switch (op.type) { + case OpCode::Identifier: tmp += QString(" %1").arg(op.strVal); break; + case OpCode::Bool: tmp += QString(" %1").arg(op.intVal ? "true" : "false"); break; + case OpCode::Int: tmp += QString(" %1").arg(op.intVal); break; + case OpCode::Float: tmp += QString(" %1").arg(op.floatVal); break; + case OpCode::And: tmp += " &&"; break; + case OpCode::Or: tmp += " ||"; break; + case OpCode::GreaterThan: tmp += " >"; break; + case OpCode::GreaterThanEqual: tmp += " >="; break; + case OpCode::LessThan: tmp += " <"; break; + case OpCode::LessThanEqual: tmp += " <="; break; + case OpCode::Equal: tmp += " =="; break; + case OpCode::NotEqual: tmp += " !="; break; + case OpCode::Not: tmp += " !"; break; + case OpCode::Subtract: tmp += " -"; break; + case OpCode::Add: tmp += " +"; break; + case OpCode::Multiply: tmp += " *"; break; + case OpCode::Divide: tmp += " /"; break; + case OpCode::Modulus: tmp += " %"; break; + case OpCode::UnaryMinus: tmp += " unary-"; break; + default: tmp += " ???"; break; + } + } + qCDebug(animation).nospace().noquote() << "opCodes =" << tmp; + qCDebug(animation).resetFormat(); +} +#endif diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h new file mode 100644 index 0000000000..468217f5b3 --- /dev/null +++ b/libraries/animation/src/AnimExpression.h @@ -0,0 +1,158 @@ +// +// AnimExpression.h +// +// Created by Anthony J. Thibault on 11/1/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// 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_AnimExpression +#define hifi_AnimExpression + +#include +#include +#include +#include +#include +#include "AnimVariant.h" + +class AnimExpression { +public: + friend class AnimTests; + AnimExpression(const QString& str); +protected: + struct Token { + enum Type { + End = 0, + Identifier, + Bool, + Int, + Float, + And, + Or, + GreaterThan, + GreaterThanEqual, + LessThan, + LessThanEqual, + Equal, + NotEqual, + LeftParen, + RightParen, + Not, + Minus, + Plus, + Multiply, + Divide, + Modulus, + Comma, + Error + }; + Token(Type type) : type {type} {} + Token(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} + explicit Token(int val) : type {Type::Int}, intVal {val} {} + explicit Token(bool val) : type {Type::Bool}, intVal {val} {} + explicit Token(float val) : type {Type::Float}, floatVal {val} {} + Type type {End}; + QString strVal; + int intVal {0}; + float floatVal {0.0f}; + }; + + struct OpCode { + enum Type { + Identifier, + Bool, + Int, + Float, + And, + Or, + GreaterThan, + GreaterThanEqual, + LessThan, + LessThanEqual, + Equal, + NotEqual, + Not, + Subtract, + Add, + Multiply, + Divide, + Modulus, + UnaryMinus + }; + OpCode(Type type) : type {type} {} + explicit OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} + explicit OpCode(const QString& str) : type {Type::Identifier}, strVal {str} {} + explicit OpCode(int val) : type {Type::Int}, intVal {val} {} + explicit OpCode(bool val) : type {Type::Bool}, intVal {(int)val} {} + explicit OpCode(float val) : type {Type::Float}, floatVal {val} {} + + bool coerceBool(const AnimVariantMap& map) const { + if (type == Int || type == Bool) { + return intVal != 0; + } else if (type == Identifier) { + return map.lookup(strVal, false); + } else { + return true; + } + } + + Type type {Int}; + QString strVal; + int intVal {0}; + float floatVal {0.0f}; + }; + + void unconsumeToken(const Token& token); + Token consumeToken(const QString& str, QString::const_iterator& iter) const; + Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const; + Token consumeNumber(const QString& str, QString::const_iterator& iter) const; + Token consumeAnd(const QString& str, QString::const_iterator& iter) const; + Token consumeOr(const QString& str, QString::const_iterator& iter) const; + Token consumeGreaterThan(const QString& str, QString::const_iterator& iter) const; + Token consumeLessThan(const QString& str, QString::const_iterator& iter) const; + Token consumeNot(const QString& str, QString::const_iterator& iter) const; + + bool parseExpr(const QString& str, QString::const_iterator& iter); + bool parseExprPrime(const QString& str, QString::const_iterator& iter); + bool parseTerm(const QString& str, QString::const_iterator& iter); + bool parseTermPrime(const QString& str, QString::const_iterator& iter); + bool parseUnary(const QString& str, QString::const_iterator& iter); + bool parseFactor(const QString& str, QString::const_iterator& iter); + + OpCode evaluate(const AnimVariantMap& map) const; + void evalAnd(const AnimVariantMap& map, std::stack& stack) const; + void evalOr(const AnimVariantMap& map, std::stack& stack) const; + void evalGreaterThan(const AnimVariantMap& map, std::stack& stack) const; + void evalGreaterThanEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalLessThan(const AnimVariantMap& map, std::stack& stack) const; + void evalLessThanEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalNotEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalNot(const AnimVariantMap& map, std::stack& stack) const; + void evalSubtract(const AnimVariantMap& map, std::stack& stack) const; + void evalAdd(const AnimVariantMap& map, std::stack& stack) const; + void add(int lhs, const OpCode& rhs, std::stack& stack) const; + void add(float lhs, const OpCode& rhs, std::stack& stack) const; + void evalMultiply(const AnimVariantMap& map, std::stack& stack) const; + void mul(int lhs, const OpCode& rhs, std::stack& stack) const; + void mul(float lhs, const OpCode& rhs, std::stack& stack) const; + void evalDivide(const AnimVariantMap& map, std::stack& stack) const; + void evalModulus(const AnimVariantMap& map, std::stack& stack) const; + void evalUnaryMinus(const AnimVariantMap& map, std::stack& stack) const; + + OpCode coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const; + + QString _expression; + mutable std::stack _tokenStack; // TODO: remove, only needed during parsing + std::vector _opCodes; + +#ifndef NDEBUG + void dumpOpCodes() const; +#endif +}; + +#endif + diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index d57d1b3c34..911899f5a9 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -15,6 +15,8 @@ #include #include "AnimVariant.h" // which has AnimVariant/AnimVariantMap +const AnimVariant AnimVariant::False = AnimVariant(); + QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { if (QThread::currentThread() != engine->thread()) { qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 0cd7fb29ac..531e2c4a2d 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -34,6 +34,8 @@ public: NumTypes }; + static const AnimVariant False; + AnimVariant() : _type(Type::Bool) { memset(&_val, 0, sizeof(_val)); } AnimVariant(bool value) : _type(Type::Bool) { _val.boolVal = value; } AnimVariant(int value) : _type(Type::Int) { _val.intVal = value; } @@ -57,13 +59,50 @@ public: void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast(&_val) = value; } void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } - bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } - int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; } - float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; } - - const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast(&_val); } - const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast(&_val); } - const QString& getString() const { assert(_type == Type::String); return _stringVal; } + bool getBool() const { + if (_type == Type::Bool) { + return _val.boolVal; + } else if (_type == Type::Int) { + return _val.intVal != 0; + } else { + return false; + } + } + int getInt() const { + if (_type == Type::Int) { + return _val.intVal; + } else if (_type == Type::Float) { + return (int)_val.floats[0]; + } else { + return 0; + } + } + float getFloat() const { + if (_type == Type::Float) { + return _val.floats[0]; + } else if (_type == Type::Int) { + return (float)_val.intVal; + } else { + return 0.0f; + } + } + const glm::vec3& getVec3() const { + if (_type == Type::Vec3) { + return *reinterpret_cast(&_val); + } else { + return Vectors::ZERO; + } + } + const glm::quat& getQuat() const { + if (_type == Type::Quat) { + return *reinterpret_cast(&_val); + } else { + return Quaternions::IDENTITY; + } + } + const QString& getString() const { + return _stringVal; + } protected: Type _type; @@ -71,7 +110,7 @@ protected: union { bool boolVal; int intVal; - float floats[16]; + float floats[4]; } _val; }; @@ -172,6 +211,15 @@ public: void clearMap() { _map.clear(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } + const AnimVariant& get(const QString& key) const { + auto iter = _map.find(key); + if (iter != _map.end()) { + return iter->second; + } else { + return AnimVariant::False; + } + } + // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const; // Side-effect us with the value of object's own properties. (No inherited properties.) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 68f382d2d9..c6060d791a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -245,6 +245,14 @@ int Rig::indexOfJoint(const QString& jointName) const { } } +QString Rig::nameOfJoint(int jointIndex) const { + if (_animSkeleton) { + return _animSkeleton->getJointName(jointIndex); + } else { + return ""; + } +} + void Rig::setModelOffset(const glm::mat4& modelOffsetMat) { AnimPose newModelOffset = AnimPose(modelOffsetMat); if (!isEqual(_modelOffset.trans, newModelOffset.trans) || @@ -289,8 +297,10 @@ void Rig::clearJointState(int index) { void Rig::clearJointStates() { _internalPoseSet._overrideFlags.clear(); - _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints()); - _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + if (_animSkeleton) { + _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints()); + _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + } } void Rig::clearJointAnimationPriority(int index) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9faf93e40b..669af2ea64 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -91,6 +91,7 @@ public: bool jointStatesEmpty(); int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; + QString nameOfJoint(int jointIndex) const; void setModelOffset(const glm::mat4& modelOffsetMat); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index a0d78da7f8..1f15007339 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -289,7 +289,6 @@ uint64_t AudioInjector::injectNextFrame() { _currentSendOffset = 0; } else { // we weren't to loop, say that we're done now - qDebug() << "AudioInjector::injectNextFrame has sent all data and was not asked to loop - calling finish()."; finish(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2ed9a47e02..d5e756d6db 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1260,6 +1260,7 @@ void AvatarData::updateJointMappings() { static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl"); static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName"); static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform"); +static const QString JSON_ATTACHMENT_IS_SOFT = QStringLiteral("isSoft"); QJsonObject AttachmentData::toJson() const { QJsonObject result; @@ -1278,6 +1279,7 @@ QJsonObject AttachmentData::toJson() const { if (!transform.isIdentity()) { result[JSON_ATTACHMENT_TRANSFORM] = Transform::toJson(transform); } + result[JSON_ATTACHMENT_IS_SOFT] = isSoft; return result; } @@ -1302,21 +1304,25 @@ void AttachmentData::fromJson(const QJsonObject& json) { rotation = transform.getRotation(); scale = transform.getScale().x; } + + if (json.contains(JSON_ATTACHMENT_IS_SOFT)) { + isSoft = json[JSON_ATTACHMENT_IS_SOFT].toBool(); + } } bool AttachmentData::operator==(const AttachmentData& other) const { return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation && - rotation == other.rotation && scale == other.scale; + rotation == other.rotation && scale == other.scale && isSoft == other.isSoft; } QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) { return out << attachment.modelURL << attachment.jointName << - attachment.translation << attachment.rotation << attachment.scale; + attachment.translation << attachment.rotation << attachment.scale << attachment.isSoft; } QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { return in >> attachment.modelURL >> attachment.jointName >> - attachment.translation >> attachment.rotation >> attachment.scale; + attachment.translation >> attachment.rotation >> attachment.scale >> attachment.isSoft; } void AttachmentDataObject::setModelURL(const QString& modelURL) const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 64c215cee7..bb35a6ea29 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -437,9 +437,10 @@ public: glm::vec3 translation; glm::quat rotation; float scale { 1.0f }; - + bool isSoft { false }; + bool isValid() const { return modelURL.isValid(); } - + bool operator==(const AttachmentData& other) const; QJsonObject toJson() const; diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 0bda52b237..7bee5101b1 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -70,6 +70,12 @@ namespace controller { makeAxisPair(Action::RETICLE_UP, "ReticleUp"), makeAxisPair(Action::RETICLE_DOWN, "ReticleDown"), + makeAxisPair(Action::UI_NAV_LATERAL, "UiNavLateral"), + makeAxisPair(Action::UI_NAV_VERTICAL, "UiNavVertical"), + makeAxisPair(Action::UI_NAV_GROUP, "UiNavGroup"), + makeAxisPair(Action::UI_NAV_SELECT, "UiNavSelect"), + makeAxisPair(Action::UI_NAV_BACK, "UiNavBack"), + // Aliases and bisected versions makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 56dd9660d9..812d3b8df3 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -55,6 +55,12 @@ enum class Action { SHIFT, + UI_NAV_LATERAL, + UI_NAV_VERTICAL, + UI_NAV_GROUP, + UI_NAV_SELECT, + UI_NAV_BACK, + // Pointer/Reticle control RETICLE_CLICK, RETICLE_X, @@ -90,6 +96,7 @@ enum class Action { BOOM_IN, BOOM_OUT, + NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index fc3477b41a..3add7d236f 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -83,6 +83,7 @@ protected: friend class UserInputMapper; virtual Input::NamedVector getAvailableInputs() const = 0; + virtual QStringList getDefaultMappingConfigs() const { return QStringList() << getDefaultMappingConfig(); } virtual QString getDefaultMappingConfig() const { return QString(); } virtual EndpointPointer createEndpoint(const Input& input) const; diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index f07472e9e0..d9641618c1 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -42,7 +42,22 @@ namespace controller { } void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { - // nothing for now... + auto translation = object.property("translation"); + auto rotation = object.property("rotation"); + auto velocity = object.property("velocity"); + auto angularVelocity = object.property("angularVelocity"); + if (translation.isValid() && + rotation.isValid() && + velocity.isValid() && + angularVelocity.isValid()) { + vec3FromScriptValue(translation, pose.translation); + quatFromScriptValue(rotation, pose.rotation); + vec3FromScriptValue(velocity, pose.velocity); + vec3FromScriptValue(angularVelocity, pose.angularVelocity); + pose.valid = true; + } else { + pose.valid = false; + } } } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 9af478e709..52ec52e852 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include +#include #include "UserInputMapper.h" #include "StandardControls.h" @@ -87,6 +89,21 @@ namespace controller { Q_INVOKABLE QObject* parseMapping(const QString& json); Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); + Q_INVOKABLE glm::vec2 getReticlePosition() { + return toGlm(QCursor::pos()); + } + Q_INVOKABLE void setReticlePosition(glm::vec2 position) { + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPos = toGlm(QCursor::pos()); + auto distance = glm::distance(oldPos, position); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position; + } + + QCursor::setPos(position.x, position.y); + } //Q_INVOKABLE bool isPrimaryButtonPressed() const; //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index fadbeee326..e101c5f4ff 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -131,10 +131,10 @@ EndpointPointer StandardController::createEndpoint(const Input& input) const { return std::make_shared(input); } -QString StandardController::getDefaultMappingConfig() const { +QStringList StandardController::getDefaultMappingConfigs() const { static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json"; - return DEFAULT_MAPPING_JSON; + static const QString DEFAULT_NAV_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_navigation.json"; + return QStringList() << DEFAULT_NAV_MAPPING_JSON << DEFAULT_MAPPING_JSON; } - } diff --git a/libraries/controllers/src/controllers/StandardController.h b/libraries/controllers/src/controllers/StandardController.h index 6c18c76371..57bd0faba5 100644 --- a/libraries/controllers/src/controllers/StandardController.h +++ b/libraries/controllers/src/controllers/StandardController.h @@ -27,7 +27,7 @@ class StandardController : public QObject, public InputDevice { public: virtual EndpointPointer createEndpoint(const Input& input) const override; virtual Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; + virtual QStringList getDefaultMappingConfigs() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index bbd33c5cb3..2b0613321e 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -88,15 +88,73 @@ namespace controller { // No correlation to SDL enum StandardPoseChannel { - LEFT_HAND = 0, - RIGHT_HAND, + HIPS = 0, + RIGHT_UP_LEG, + RIGHT_LEG, + RIGHT_FOOT, + LEFT_UP_LEG, + LEFT_LEG, + LEFT_FOOT, + SPINE, + SPINE1, + SPINE2, + SPINE3, + NECK, HEAD, + RIGHT_SHOULDER, + RIGHT_ARM, + RIGHT_FORE_ARM, + RIGHT_HAND, + RIGHT_HAND_THUMB1, + RIGHT_HAND_THUMB2, + RIGHT_HAND_THUMB3, + RIGHT_HAND_THUMB4, + RIGHT_HAND_INDEX1, + RIGHT_HAND_INDEX2, + RIGHT_HAND_INDEX3, + RIGHT_HAND_INDEX4, + RIGHT_HAND_MIDDLE1, + RIGHT_HAND_MIDDLE2, + RIGHT_HAND_MIDDLE3, + RIGHT_HAND_MIDDLE4, + RIGHT_HAND_RING1, + RIGHT_HAND_RING2, + RIGHT_HAND_RING3, + RIGHT_HAND_RING4, + RIGHT_HAND_PINKY1, + RIGHT_HAND_PINKY2, + RIGHT_HAND_PINKY3, + RIGHT_HAND_PINKY4, + LEFT_SHOULDER, + LEFT_ARM, + LEFT_FORE_ARM, + LEFT_HAND, + LEFT_HAND_THUMB1, + LEFT_HAND_THUMB2, + LEFT_HAND_THUMB3, + LEFT_HAND_THUMB4, + LEFT_HAND_INDEX1, + LEFT_HAND_INDEX2, + LEFT_HAND_INDEX3, + LEFT_HAND_INDEX4, + LEFT_HAND_MIDDLE1, + LEFT_HAND_MIDDLE2, + LEFT_HAND_MIDDLE3, + LEFT_HAND_MIDDLE4, + LEFT_HAND_RING1, + LEFT_HAND_RING2, + LEFT_HAND_RING3, + LEFT_HAND_RING4, + LEFT_HAND_PINKY1, + LEFT_HAND_PINKY2, + LEFT_HAND_PINKY3, + LEFT_HAND_PINKY4, NUM_STANDARD_POSES }; enum StandardCounts { TRIGGERS = 2, ANALOG_STICKS = 2, - POSES = 2, // FIXME 3? if we want to expose the head? + POSES = NUM_STANDARD_POSES }; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 9251a663ba..fe64566b29 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -100,7 +100,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) { } _registeredDevices[deviceID] = device; - auto mapping = loadMapping(device->getDefaultMappingConfig()); + auto mapping = loadMappings(device->getDefaultMappingConfigs()); if (mapping) { _mappingsByDevice[deviceID] = mapping; enableMapping(mapping); @@ -139,7 +139,7 @@ void UserInputMapper::loadDefaultMapping(uint16 deviceID) { } - auto mapping = loadMapping(proxyEntry->second->getDefaultMappingConfig()); + auto mapping = loadMappings(proxyEntry->second->getDefaultMappingConfigs()); if (mapping) { auto prevMapping = _mappingsByDevice[deviceID]; disableMapping(prevMapping); @@ -710,6 +710,21 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { return parseMapping(json); } +MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) { + Mapping::Pointer result; + for (const QString& jsonFile : jsonFiles) { + auto subMapping = loadMapping(jsonFile); + if (subMapping) { + if (!result) { + result = subMapping; + } else { + auto& routes = result->routes; + routes.insert(routes.end(), subMapping->routes.begin(), subMapping->routes.end()); + } + } + } + return result; +} static const QString JSON_NAME = QStringLiteral("name"); @@ -888,7 +903,7 @@ Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { if (value.isObject()) { - auto object = value.toObject(); + auto object = value.toObject(); if (object.contains("makeAxis")) { auto axisValue = object.value("makeAxis"); if (axisValue.isArray()) { @@ -985,6 +1000,20 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { return result; } +void injectConditional(Route::Pointer& route, Conditional::Pointer& conditional) { + if (!conditional) { + return; + } + + if (!route->conditional) { + route->conditional = conditional; + return; + } + + route->conditional = std::make_shared(conditional, route->conditional); +} + + Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { if (!json.isObject()) { return Mapping::Pointer(); @@ -994,12 +1023,24 @@ Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { auto mapping = std::make_shared("default"); mapping->name = obj[JSON_NAME].toString(); const auto& jsonChannels = obj[JSON_CHANNELS].toArray(); + Conditional::Pointer globalConditional; + if (obj.contains(JSON_CHANNEL_WHEN)) { + auto conditionalsValue = obj[JSON_CHANNEL_WHEN]; + globalConditional = parseConditional(conditionalsValue); + } + for (const auto& channelIt : jsonChannels) { Route::Pointer route = parseRoute(channelIt); + if (!route) { qWarning() << "Couldn't parse route"; continue; } + + if (globalConditional) { + injectConditional(route, globalConditional); + } + mapping->routes.push_back(route); } _mappingsByName[mapping->name] = mapping; diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index d93a93016c..98a85a2a44 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -107,6 +107,7 @@ namespace controller { MappingPointer newMapping(const QString& mappingName); MappingPointer parseMapping(const QString& json); MappingPointer loadMapping(const QString& jsonFile); + MappingPointer loadMappings(const QStringList& jsonFiles); void loadDefaultMapping(uint16 deviceID); void enableMapping(const QString& mappingName, bool enable = true); diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h index c60e4b15df..2299843a24 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h @@ -18,7 +18,11 @@ class AndConditional : public Conditional { public: using Pointer = std::shared_ptr; - AndConditional(Conditional::List children) : _children(children) { } + AndConditional(Conditional::List children) + : _children(children) {} + + AndConditional(Conditional::Pointer& first, Conditional::Pointer& second) + : _children({ first, second }) {} virtual bool satisfied() override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index bb9517b136..3e7fde347e 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -10,6 +10,8 @@ #include +#include + using namespace controller; float ScriptEndpoint::peek() const { @@ -23,7 +25,16 @@ void ScriptEndpoint::updateValue() { return; } - _lastValueRead = (float)_callable.call().toNumber(); + QScriptValue result = _callable.call(); + + // If the callable ever returns a non-number, we assume it's a pose + // and start reporting ourselves as a pose. + if (result.isNumber()) { + _lastValueRead = (float)_callable.call().toNumber(); + } else { + Pose::fromScriptValue(result, _lastPoseRead); + _returnPose = true; + } } void ScriptEndpoint::apply(float value, const Pointer& source) { @@ -44,3 +55,36 @@ void ScriptEndpoint::internalApply(float value, int sourceID) { _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); } + +Pose ScriptEndpoint::peekPose() const { + const_cast(this)->updatePose(); + return _lastPoseRead; +} + +void ScriptEndpoint::updatePose() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updatePose", Qt::QueuedConnection); + return; + } + QScriptValue result = _callable.call(); + Pose::fromScriptValue(result, _lastPoseRead); +} + +void ScriptEndpoint::apply(const Pose& newPose, const Pointer& source) { + if (newPose == _lastPoseWritten) { + return; + } + internalApply(newPose, source->getInput().getID()); +} + +void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) { + _lastPoseWritten = newPose; + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(const Pose&, newPose), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) })); +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index 836af721f6..00439f5560 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -27,13 +27,26 @@ public: virtual float peek() const override; virtual void apply(float newValue, const Pointer& source) override; + + virtual Pose peekPose() const override; + virtual void apply(const Pose& newValue, const Pointer& source) override; + + virtual bool isPose() const override { return _returnPose; } + protected: Q_INVOKABLE void updateValue(); Q_INVOKABLE virtual void internalApply(float newValue, int sourceID); + + Q_INVOKABLE void updatePose(); + Q_INVOKABLE virtual void internalApply(const Pose& newValue, int sourceID); private: QScriptValue _callable; float _lastValueRead { 0.0f }; float _lastValueWritten { 0.0f }; + + bool _returnPose { false }; + Pose _lastPoseRead; + Pose _lastPoseWritten; }; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e3618d0e2a..f517c49b00 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -256,33 +256,38 @@ void EntityTreeRenderer::forceRecheckEntities() { void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr zone) { QSharedPointer scene = DependencyManager::get(); + auto sceneStage = scene->getStage(); + auto sceneKeyLight = sceneStage->getKeyLight(); + auto sceneLocation = sceneStage->getLocation(); + auto sceneTime = sceneStage->getTime(); + if (zone) { if (!_hasPreviousZone) { - _previousKeyLightColor = scene->getKeyLightColor(); - _previousKeyLightIntensity = scene->getKeyLightIntensity(); - _previousKeyLightAmbientIntensity = scene->getKeyLightAmbientIntensity(); - _previousKeyLightDirection = scene->getKeyLightDirection(); - _previousStageSunModelEnabled = scene->isStageSunModelEnabled(); - _previousStageLongitude = scene->getStageLocationLongitude(); - _previousStageLatitude = scene->getStageLocationLatitude(); - _previousStageAltitude = scene->getStageLocationAltitude(); - _previousStageHour = scene->getStageDayTime(); - _previousStageDay = scene->getStageYearTime(); + _previousKeyLightColor = sceneKeyLight->getColor(); + _previousKeyLightIntensity = sceneKeyLight->getIntensity(); + _previousKeyLightAmbientIntensity = sceneKeyLight->getAmbientIntensity(); + _previousKeyLightDirection = sceneKeyLight->getDirection(); + _previousStageSunModelEnabled = sceneStage->isSunModelEnabled(); + _previousStageLongitude = sceneLocation->getLongitude(); + _previousStageLatitude = sceneLocation->getLatitude(); + _previousStageAltitude = sceneLocation->getAltitude(); + _previousStageHour = sceneTime->getHour(); + _previousStageDay = sceneTime->getDay(); _hasPreviousZone = true; } - scene->setKeyLightColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor())); - scene->setKeyLightIntensity(zone->getKeyLightProperties().getIntensity()); - scene->setKeyLightAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity()); - scene->setKeyLightDirection(zone->getKeyLightProperties().getDirection()); - scene->setStageSunModelEnable(zone->getStageProperties().getSunModelEnabled()); - scene->setStageLocation(zone->getStageProperties().getLongitude(), zone->getStageProperties().getLatitude(), + sceneKeyLight->setColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor())); + sceneKeyLight->setIntensity(zone->getKeyLightProperties().getIntensity()); + sceneKeyLight->setAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity()); + sceneKeyLight->setDirection(zone->getKeyLightProperties().getDirection()); + sceneStage->setSunModelEnable(zone->getStageProperties().getSunModelEnabled()); + sceneStage->setLocation(zone->getStageProperties().getLongitude(), zone->getStageProperties().getLatitude(), zone->getStageProperties().getAltitude()); - scene->setStageDayTime(zone->getStageProperties().calculateHour()); - scene->setStageYearTime(zone->getStageProperties().calculateDay()); + sceneTime->setHour(zone->getStageProperties().calculateHour()); + sceneTime->setDay(zone->getStageProperties().calculateDay()); if (zone->getBackgroundMode() == BACKGROUND_MODE_ATMOSPHERE) { EnvironmentData data = zone->getEnvironmentData(); - glm::vec3 keyLightDirection = scene->getKeyLightDirection(); + glm::vec3 keyLightDirection = sceneKeyLight->getDirection(); glm::vec3 inverseKeyLightDirection = keyLightDirection * -1.0f; // NOTE: is this right? It seems like the "sun" should be based on the center of the @@ -293,7 +298,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetKeyLightIntensity() * KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO; + float sunBrightness = sceneKeyLight->getIntensity() * KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO; data.setSunBrightness(sunBrightness); _viewState->overrideEnvironmentData(data); @@ -339,15 +344,15 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrsetKeyLightColor(_previousKeyLightColor); - scene->setKeyLightIntensity(_previousKeyLightIntensity); - scene->setKeyLightAmbientIntensity(_previousKeyLightAmbientIntensity); - scene->setKeyLightDirection(_previousKeyLightDirection); - scene->setStageSunModelEnable(_previousStageSunModelEnabled); - scene->setStageLocation(_previousStageLongitude, _previousStageLatitude, + sceneKeyLight->setColor(_previousKeyLightColor); + sceneKeyLight->setIntensity(_previousKeyLightIntensity); + sceneKeyLight->setAmbientIntensity(_previousKeyLightAmbientIntensity); + sceneKeyLight->setDirection(_previousKeyLightDirection); + sceneStage->setSunModelEnable(_previousStageSunModelEnabled); + sceneStage->setLocation(_previousStageLongitude, _previousStageLatitude, _previousStageAltitude); - scene->setStageDayTime(_previousStageHour); - scene->setStageYearTime(_previousStageDay); + sceneTime->setHour(_previousStageHour); + sceneTime->setDay(_previousStageDay); _hasPreviousZone = false; } _viewState->endOverrideEnvironmentData(); @@ -482,7 +487,8 @@ void EntityTreeRenderer::deleteReleasedModels() { } RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; if (_tree) { EntityTreePointer entityTree = std::static_pointer_cast(_tree); @@ -490,7 +496,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, - result.face, result.surfaceNormal, entityIdsToInclude, + result.face, result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 076fe26d6f..2c205336c0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -130,7 +130,8 @@ private: QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude = QVector()); + bool precisionPicking, const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToDiscard = QVector()); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 14e89b59fd..9127e4ca22 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -385,7 +385,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { _needsInitialSimulation = true; } } - + return result; } @@ -398,14 +398,14 @@ void RenderableModelEntityItem::update(const quint64& now) { EntityItemProperties properties; auto extents = _model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - + qCDebug(entitiesrenderer) << "Autoresizing:" << (!getName().isEmpty() ? getName() : getModelURL()); QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", Qt::QueuedConnection, Q_ARG(QUuid, getEntityItemID()), Q_ARG(EntityItemProperties, properties)); } - + ModelEntityItem::update(now); } @@ -427,7 +427,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori // << precisionPicking; QString extraInfo; - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); } @@ -447,24 +447,22 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { ShapeType type = getShapeType(); if (type == SHAPE_TYPE_COMPOUND) { - if (!_model) { + if (!_model || _model->getCollisionURL().isEmpty()) { EntityTreePointer tree = getTree(); if (tree) { QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); } - return false; // hmm... + return false; } - assert(!_model->getCollisionURL().isEmpty()); - if (_model->getURL().isEmpty()) { // we need a render geometry with a scale to proceed, so give up. return false; } - + const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); const QSharedPointer renderNetworkGeometry = _model->getGeometry(); - + if ((collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) && (renderNetworkGeometry && renderNetworkGeometry->isLoaded())) { // we have both URLs AND both geometries AND they are both fully loaded. diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 4abd8dbafd..567d2bc24a 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -223,10 +223,10 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { 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.color.start = glm::vec4(getColorStartRGB(), getAlphaStart()); + particleUniforms.color.middle = glm::vec4(getColorRGB(), getAlpha()); + particleUniforms.color.finish = glm::vec4(getColorFinishRGB(), getAlphaFinish()); + particleUniforms.color.spread = glm::vec4(getColorSpreadRGB(), getAlphaSpread()); particleUniforms.lifespan = getLifespan(); // Build particle primitives @@ -240,8 +240,11 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { auto position = getPosition(); auto rotation = getRotation(); Transform transform; - transform.setTranslation(position); - transform.setRotation(rotation); + if (!getEmitterShouldTrail()) { + transform.setTranslation(position); + transform.setRotation(rotation); + } + render::PendingChanges pendingChanges; pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 256e36ebc3..7b3bbc4c02 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -23,6 +23,13 @@ #include "paintStroke_frag.h" + +struct PolyLineUniforms { + glm::vec3 color; +}; + + + EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderablePolyLineEntityItem(entityID) }; entity->setProperties(properties); @@ -30,9 +37,12 @@ EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& enti } RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID) : - PolyLineEntityItem(entityItemID) { - _numVertices = 0; +PolyLineEntityItem(entityItemID), +_numVertices(0) +{ _vertices = QVector(0.0f); + PolyLineUniforms uniforms; + _uniformBuffer = std::make_shared(sizeof(PolyLineUniforms), (const gpu::Byte*) &uniforms); } gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline; @@ -41,13 +51,11 @@ int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; void RenderablePolyLineEntityItem::createPipeline() { static const int NORMAL_OFFSET = 12; - static const int COLOR_OFFSET = 24; - static const int TEXTURE_OFFSET = 28; + static const int TEXTURE_OFFSET = 24; _format.reset(new gpu::Stream::Format()); _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); - _format->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, COLOR_OFFSET); _format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), TEXTURE_OFFSET); auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert)); @@ -72,47 +80,28 @@ void RenderablePolyLineEntityItem::updateGeometry() { _verticesBuffer.reset(new gpu::Buffer()); int vertexIndex = 0; vec2 uv; - float tailStart = 0.0f; - float tailEnd = 0.25f; - float tailLength = tailEnd - tailStart; - - float headStart = 0.76f; - float headEnd = 1.0f; - float headLength = headEnd - headStart; float uCoord, vCoord; - - int numTailStrips = 5; - int numHeadStrips = 10; - int startHeadIndex = _vertices.size() / 2 - numHeadStrips; + uCoord = 0.0f; + float uCoordInc = 1.0 / (_vertices.size() / 2); for (int i = 0; i < _vertices.size() / 2; i++) { - uCoord = 0.26f; vCoord = 0.0f; - //tail - if (i < numTailStrips) { - uCoord = float(i) / numTailStrips * tailLength + tailStart; - } - - //head - if (i > startHeadIndex) { - uCoord = float((i + 1) - startHeadIndex) / numHeadStrips * headLength + headStart; - } + uv = vec2(uCoord, vCoord); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)&_color); _verticesBuffer->append(sizeof(glm::vec2), (gpu::Byte*)&uv); vertexIndex++; uv.y = 1.0f; _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)_color); _verticesBuffer->append(sizeof(glm::vec2), (const gpu::Byte*)&uv); vertexIndex++; _numVertices += 2; + uCoord += uCoordInc; } _pointsChanged = false; _normalsChanged = false; @@ -152,6 +141,11 @@ void RenderablePolyLineEntityItem::updateVertices() { _vertices << v1 << v2; } + // Guard against an empty polyline + if (finalIndex < 0) { + return; + } + // For last point we can assume binormals are the same since it represents the last two vertices of quad point = _points.at(finalIndex); v1 = point + binormal; @@ -161,6 +155,16 @@ void RenderablePolyLineEntityItem::updateVertices() { } +void RenderablePolyLineEntityItem::update(const quint64& now) { + PolyLineUniforms uniforms; + uniforms.color = toGlm(getXColor()); + memcpy(&_uniformBuffer.edit(), &uniforms, sizeof(PolyLineUniforms)); + if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { + updateVertices(); + updateGeometry(); + } + +} void RenderablePolyLineEntityItem::render(RenderArgs* args) { QWriteLocker lock(&_quadReadWriteLock); @@ -181,17 +185,13 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(getType() == EntityTypes::PolyLine); - Q_ASSERT(args->_batch); - if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { - updateVertices(); - updateGeometry(); - } gpu::Batch& batch = *args->_batch; Transform transform = Transform(); transform.setTranslation(getPosition()); transform.setRotation(getRotation()); + batch.setUniformBuffer(0, _uniformBuffer); batch.setModelTransform(transform); batch.setPipeline(_pipeline); @@ -200,7 +200,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { } else { batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, args->_whiteTexture); } - + batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index eca7c59fef..d320610d83 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -29,6 +29,8 @@ public: RenderablePolyLineEntityItem(const EntityItemID& entityItemID); virtual void render(RenderArgs* args); + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const { return true; }; SIMPLE_RENDERABLE(); @@ -42,6 +44,7 @@ protected: void updateGeometry(); void updateVertices(); gpu::BufferPointer _verticesBuffer; + gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; diff --git a/libraries/entities-renderer/src/RenderableProceduralItemShader.h b/libraries/entities-renderer/src/RenderableProceduralItemShader.h index 8c9e7c77dd..a01a6b8571 100644 --- a/libraries/entities-renderer/src/RenderableProceduralItemShader.h +++ b/libraries/entities-renderer/src/RenderableProceduralItemShader.h @@ -23,8 +23,6 @@ layout(location = 0) out vec4 _fragColor0; layout(location = 1) out vec4 _fragColor1; layout(location = 2) out vec4 _fragColor2; -// the glow intensity -uniform float glowIntensity; // the alpha threshold uniform float alphaThreshold; uniform sampler2D normalFittingMap; @@ -318,8 +316,8 @@ const QString SHADER_TEMPLATE_V1 = SHADER_COMMON + R"SCRIBE( void main(void) { vec4 emissive = getProceduralColor(); - float alpha = glowIntensity * emissive.a; - if (alpha != glowIntensity) { + float alpha = emissive.a; + if (alpha != 1.0) { discard; } diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 4e2bc8d097..fc659d5928 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -23,23 +23,24 @@ in vec3 interpolatedNormal; in vec2 varTexcoord; in vec4 varColor; -float rand(vec2 point){ - return fract(sin(dot(point.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - +struct PolyLineUniforms { + vec3 color; +}; +uniform polyLineBuffer { + PolyLineUniforms polyline; +}; void main(void) { vec4 texel = texture(originalTexture, varTexcoord); int frontCondition = 1 -int(gl_FrontFacing) * 2; - vec3 color = varColor.rgb; - //vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess + vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, texel.a, - color *texel.rgb, + polyline.color * texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index 7d7523deb9..769b87f2a9 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -13,8 +13,8 @@ // <@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> // the interpolated normal @@ -30,7 +30,8 @@ void main(void) { varTexcoord = inTexCoord0.st; // pass along the diffuse color - varColor = inColor; + varColor = colorToLinearRGBA(inColor); + // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/entities-renderer/src/untextured_particle.slv b/libraries/entities-renderer/src/untextured_particle.slv index 3d1d99c0dc..ab0ea15219 100644 --- a/libraries/entities-renderer/src/untextured_particle.slv +++ b/libraries/entities-renderer/src/untextured_particle.slv @@ -9,16 +9,15 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec4 _color; void main(void) { // pass along the diffuse color - _color = inColor; + _color = colorToLinearRGBA(inColor); TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 98a099fcaa..3a2fdf55d4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -195,7 +195,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); - CHECK_PROPERTY_CHANGE(PROP_ADDITIVE_BLENDING, additiveBlending); + CHECK_PROPERTY_CHANGE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); @@ -354,7 +354,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ADDITIVE_BLENDING, additiveBlending); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); } @@ -515,7 +515,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(additiveBlending, bool, setAdditiveBlending); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail , bool, setEmitterShouldTrail); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel); @@ -670,7 +670,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float); - ADD_PROPERTY_TO_MAP(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool); + ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); @@ -980,7 +980,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); - APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, properties.getAdditiveBlending()); + APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail()); } if (properties.getType() == EntityTypes::Zone) { @@ -1265,7 +1265,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); } if (properties.getType() == EntityTypes::Zone) { @@ -1608,8 +1608,8 @@ QList EntityItemProperties::listChangedProperties() { if (alphaFinishChanged()) { out += "alphaFinish"; } - if (additiveBlendingChanged()) { - out += "additiveBlending"; + if (emitterShouldTrailChanged()) { + out += "emitterShouldTrail"; } if (modelURLChanged()) { out += "modelURL"; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c13519996a..5420e75aed 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -161,7 +161,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD); DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); - DEFINE_PROPERTY(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool, ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING); + DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 3bca911a56..2ba86a491e 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -149,7 +149,7 @@ enum EntityPropertyList { PROP_ANIMATION_HOLD, PROP_ANIMATION_START_AUTOMATICALLY, - PROP_ADDITIVE_BLENDING, + PROP_EMITTER_SHOULD_TRAIL, PROP_PARENT_ID, PROP_PARENT_JOINT_INDEX, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a0a6719521..0746c2a824 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -357,19 +357,21 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - QVector entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entitiesToInclude, entitiesToDiscard); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - const QVector& entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + const QVector& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + const QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; @@ -377,7 +379,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, - result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate, + result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { result.entityID = intersectedEntity->getEntityItemID(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index f745b6b644..d08a1b7e36 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -112,11 +112,11 @@ public slots: /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; @@ -189,7 +189,7 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude); + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ba6294f8a8..f0a03623c2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -498,6 +498,7 @@ public: BoxFace& face; glm::vec3& surfaceNormal; const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; void** intersectedObject; bool found; bool precisionPicking; @@ -510,7 +511,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); if (entityTreeElementPointer ->findRayIntersection(args->origin, args->direction, keepSearching, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->intersectedObject, args->precisionPicking)) { + args->entityIdsToDiscard, args->intersectedObject, args->precisionPicking)) { args->found = true; } return keepSearching; @@ -518,9 +519,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking }; + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, false, precisionPicking }; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5e54e562a0..f68e2d59e9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -84,6 +84,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToDiscard = QVector(), void** intersectedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL, diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index ff2e97e8fe..8944c95cbc 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -475,8 +475,8 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject, bool precisionPicking) { + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking) { keepSearching = true; // assume that we will continue searching after this. @@ -501,7 +501,7 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: if (_cube.contains(origin) || distanceToElementCube < distance) { if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails, - face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) { + face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, precisionPicking, distanceToElementCube)) { if (distanceToElementDetails < distance) { distance = distanceToElementDetails; @@ -516,13 +516,13 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { + const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... int entityNumber = 0; bool somethingIntersected = false; forEachEntity([&](EntityItemPointer entity) { - if (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) { + if ( (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { return; } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d8a182156d..aa05438bde 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -144,11 +144,13 @@ public: virtual bool canRayIntersect() const { return hasEntities(); } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index a1f16ee251..9215374653 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -252,9 +252,11 @@ void ModelEntityItem::getAnimationFrame(bool& newFrame, if (index < translations.size()) { translationMat = glm::translate(translations[index]); } - glm::mat4 rotationMat; + glm::mat4 rotationMat(glm::mat4::_null); if (index < rotations.size()) { - rotationMat = glm::mat4_cast(rotations[index]); + rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + } else { + rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); } glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * rotationMat * fbxJoints[index].postTransform); diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 165afd1536..16196aa129 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -52,13 +52,13 @@ const float ParticleEffectEntityItem::MINIMUM_ALPHA = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_ALPHA = 1.0f; const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000; const quint32 ParticleEffectEntityItem::MINIMUM_MAX_PARTICLES = 1; -const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 10000; +const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 100000; const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f; const float ParticleEffectEntityItem::MINIMUM_LIFESPAN = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_LIFESPAN = 86400.0f; // 1 day const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_RATE = 0.0f; -const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 1000.0f; +const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 100000.0f; 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 @@ -91,7 +91,7 @@ const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f; const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS; const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; -const bool ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING = false; +const bool ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL = false; EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -331,7 +331,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(additiveBlending, getAdditiveBlending); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail); return properties; @@ -370,7 +370,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(additiveBlending, setAdditiveBlending); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail); if (somethingChanged) { bool wantDebug = false; @@ -463,7 +463,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING) { - READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); + READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); } return bytesRead; @@ -503,7 +503,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_POLAR_FINISH; requestedProperties += PROP_AZIMUTH_START; requestedProperties += PROP_AZIMUTH_FINISH; - requestedProperties += PROP_ADDITIVE_BLENDING; + requestedProperties += PROP_EMITTER_SHOULD_TRAIL; return requestedProperties; } @@ -546,7 +546,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); - APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, getAdditiveBlending()); + APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail()); } bool ParticleEffectEntityItem::isEmittingParticles() const { @@ -641,13 +641,17 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { Particle particle; - - particle.seed = randFloatInRange(0.0f, 1.0f); - + + + particle.seed = randFloatInRange(-1.0f, 1.0f); + if (getEmitterShouldTrail()) { + particle.position = getPosition(); + } // 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.velocity = (_emitSpeed + 0.2f * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; } else { @@ -658,11 +662,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); - float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + // float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) *randFloat()); float azimuth; if (_azimuthFinish >= _azimuthStart) { - azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); + azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); } else { azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); } @@ -693,7 +698,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f )); - particle.position = _emitOrientation * emitPosition; + if (getEmitterShouldTrail()) { + particle.position += _emitOrientation * emitPosition; + } + else { + particle.position = _emitOrientation * emitPosition; + } } particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index c35a45baeb..5afcbe2ae1 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -15,6 +15,8 @@ #include "EntityItem.h" +#include "ColorUtils.h" + class ParticleEffectEntityItem : public EntityItem { public: ALLOW_INSTANTIATION // This class can be instantiated @@ -47,6 +49,7 @@ public: const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } + glm::vec3 getColorRGB() const { return ColorUtils::toLinearVec3(toGlm(getXColor())); } static const xColor DEFAULT_COLOR; void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } @@ -59,14 +62,17 @@ public: bool _isColorStartInitialized = false; void setColorStart(const xColor& colorStart) { _colorStart = colorStart; _isColorStartInitialized = true; } xColor getColorStart() const { return _isColorStartInitialized ? _colorStart : getXColor(); } + glm::vec3 getColorStartRGB() const { return _isColorStartInitialized ? ColorUtils::toLinearVec3(toGlm(_colorStart)) : getColorRGB(); } bool _isColorFinishInitialized = false; void setColorFinish(const xColor& colorFinish) { _colorFinish = colorFinish; _isColorFinishInitialized = true; } xColor getColorFinish() const { return _isColorFinishInitialized ? _colorFinish : getXColor(); } + glm::vec3 getColorFinishRGB() const { return _isColorStartInitialized ? ColorUtils::toLinearVec3(toGlm(_colorFinish)) : getColorRGB(); } static const xColor DEFAULT_COLOR_SPREAD; void setColorSpread(const xColor& colorSpread) { _colorSpread = colorSpread; } xColor getColorSpread() const { return _colorSpread; } + glm::vec3 getColorSpreadRGB() const { return ColorUtils::toLinearVec3(toGlm(_colorSpread)); } static const float MAXIMUM_ALPHA; static const float MINIMUM_ALPHA; @@ -207,10 +213,10 @@ public: } } - static const bool DEFAULT_ADDITIVE_BLENDING; - bool getAdditiveBlending() const { return _additiveBlending; } - void setAdditiveBlending(bool additiveBlending) { - _additiveBlending = additiveBlending; + static const bool DEFAULT_EMITTER_SHOULD_TRAIL; + bool getEmitterShouldTrail() const { return _emitterShouldTrail; } + void setEmitterShouldTrail(bool emitterShouldTrail) { + _emitterShouldTrail = emitterShouldTrail; } virtual bool supportsDetailedRayIntersection() const { return false; } @@ -280,7 +286,7 @@ protected: float _timeUntilNextEmit { 0.0f }; - bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING }; + bool _emitterShouldTrail { DEFAULT_EMITTER_SHOULD_TRAIL }; }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index e7281d8157..b0b78bc9c5 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -89,6 +89,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } + bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { if (_points.size() > MAX_POINTS_PER_LINE - 1) { qDebug() << "MAX POINTS REACHED!"; @@ -104,6 +105,7 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { return true; } + bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { _strokeWidths = strokeWidths; _strokeWidthsChanged = true; diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index dfe062bbdb..cf7b14dbf1 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -49,7 +49,7 @@ class PolyLineEntityItem : public EntityItem { memcpy(_color, value, sizeof(_color)); } void setColor(const xColor& value) { - + _color[RED_INDEX] = value.red; _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; @@ -75,7 +75,9 @@ class PolyLineEntityItem : public EntityItem { _texturesChangedFlag = true; } } - + + virtual bool needsToCallUpdate() const { return true; } + virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } // never have a ray intersection pick a PolyLineEntityItem. diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index bddc7d19ae..f89f1f5b72 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -254,10 +254,33 @@ private: _quit = true; } + static const uint64_t MAX_SHUTDOWN_WAIT_SECS = 5; void stop() { - QMutexLocker lock(&_mutex); - post(STOP); - _cond.wait(&_mutex); + if (_thread.isRunning()) { + qDebug() << "Stopping QML render thread " << _thread.currentThreadId(); + { + QMutexLocker lock(&_mutex); + post(STOP); + } + auto start = usecTimestampNow(); + auto now = usecTimestampNow(); + bool shutdownClean = false; + while (now - start < (MAX_SHUTDOWN_WAIT_SECS * USECS_PER_SECOND)) { + QMutexLocker lock(&_mutex); + if (_cond.wait(&_mutex, MSECS_PER_SECOND)) { + shutdownClean = true; + break; + } + now = usecTimestampNow(); + } + + if (!shutdownClean) { + qWarning() << "Failed to shut down the QML render thread"; + } + + } else { + qDebug() << "QML render thread already completed"; + } } bool allowNewFrame(uint8_t fps) { @@ -320,10 +343,13 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { void OffscreenQmlSurface::resize(const QSize& newSize) { if (!_renderer || !_renderer->_quickWindow) { - QSize currentSize = _renderer->_quickWindow->geometry().size(); - if (newSize == currentSize) { - return; - } + return; + } + + + QSize currentSize = _renderer->_quickWindow->geometry().size(); + if (newSize == currentSize) { + return; } _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize); @@ -437,7 +463,9 @@ void OffscreenQmlSurface::updateQuick() { } if (_render) { + QMutexLocker lock(&(_renderer->_mutex)); _renderer->post(RENDER); + _renderer->_cond.wait(&(_renderer->_mutex)); _render = false; } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index 67315b0783..608e811b4b 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -37,11 +37,11 @@ public: using MouseTranslator = std::function; - void create(QOpenGLContext* context); + virtual void create(QOpenGLContext* context); void resize(const QSize& size); QSize size() const; - QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { + Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { return load(QUrl(qmlSourceFile), f); } diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 3e879df7af..6397d30e13 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -39,6 +39,11 @@ void QOpenGLContextWrapper::doneCurrent() { _context->doneCurrent(); } +void QOpenGLContextWrapper::setShareContext(QOpenGLContext* otherContext) { + _context->setShareContext(otherContext); +} + bool isCurrentContext(QOpenGLContext* context) { return QOpenGLContext::currentContext() == context; -} \ No newline at end of file +} + diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index 832119162c..b736253213 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -25,6 +25,12 @@ public: void swapBuffers(QSurface* surface); bool makeCurrent(QSurface* surface); void doneCurrent(); + void setShareContext(QOpenGLContext* otherContext); + + QOpenGLContext* getContext() { + return _context; + } + private: QOpenGLContext* _context { nullptr }; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 14871aafd1..f9c4131a1b 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -311,6 +311,12 @@ void Batch::blit(const FramebufferPointer& src, const Vec4i& srcViewport, _params.push_back(dstViewport.w); } +void Batch::generateTextureMips(const TexturePointer& texture) { + ADD_COMMAND(generateTextureMips); + + _params.push_back(_textures.cache(texture)); +} + void Batch::beginQuery(const QueryPointer& query) { ADD_COMMAND(beginQuery); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 2afcc7caa9..88d28f9d10 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -211,6 +211,9 @@ public: // with xy and zw the bounding corners of the rect region. void blit(const FramebufferPointer& src, const Vec4i& srcRect, const FramebufferPointer& dst, const Vec4i& dstRect); + // Generate the mips for a texture + void generateTextureMips(const TexturePointer& texture); + // Query Section void beginQuery(const QueryPointer& query); void endQuery(const QueryPointer& query); @@ -292,6 +295,7 @@ public: COMMAND_setFramebuffer, COMMAND_clearFramebuffer, COMMAND_blit, + COMMAND_generateTextureMips, COMMAND_beginQuery, COMMAND_endQuery, diff --git a/libraries/gpu/src/gpu/Color.slh b/libraries/gpu/src/gpu/Color.slh new file mode 100644 index 0000000000..f633e6e2d4 --- /dev/null +++ b/libraries/gpu/src/gpu/Color.slh @@ -0,0 +1,52 @@ + +<@if not GPU_COLOR_SLH@> +<@def GPU_COLOR_SLH@> + 0.04045 + // constants: + // T = 0.04045 + // A = 1 / 1.055 = 0.94786729857 + // B = 0.055 * A = 0.05213270142 + // C = 1 / 12.92 = 0.0773993808 + // G = 2.4 + const float T = 0.04045; + const float A = 0.947867; + const float B = 0.052132; + const float C = 0.077399; + const float G = 2.4; + + if (cs > T) { + return pow((cs * A + B), G); + } else { + return cs * C; + } +} + +vec3 colorToLinear(vec3 srgb) { + return vec3(colorComponentToLinear(srgb.x), colorComponentToLinear(srgb.y), colorComponentToLinear(srgb.z)); +} +!> + +vec3 colorToLinearRGB(vec3 srgb) { + const float GAMMA_22 = 2.2; + return pow(srgb, vec3(GAMMA_22)); +} + +vec4 colorToLinearRGBA(vec4 srgba) { + return vec4(colorToLinearRGB(srgba.xyz), srgba.w); +} + +<@endif@> \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 3022f47b51..41a95e2578 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -160,6 +160,7 @@ enum Semantic { RGB, RGBA, BGRA, + XY, XYZ, XYZW, @@ -176,6 +177,8 @@ enum Semantic { SRGBA, SBGRA, + R11G11B10, + UNIFORM, UNIFORM_BUFFER, SAMPLER, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index d4f3c5c4b3..a730c06bd9 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -52,6 +52,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setFramebuffer), (&::gpu::GLBackend::do_clearFramebuffer), (&::gpu::GLBackend::do_blit), + (&::gpu::GLBackend::do_generateTextureMips), (&::gpu::GLBackend::do_beginQuery), (&::gpu::GLBackend::do_endQuery), diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f44fbe6c0d..400c9f08f8 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -376,12 +376,16 @@ protected: // Resource Stage void do_setResourceTexture(Batch& batch, size_t paramOffset); - + + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s void releaseResourceTexture(uint32_t slot); + void resetResourceStage(); struct ResourceStageState { Textures _textures; + int findEmptyTextureSlot() const; + ResourceStageState(): _textures(MAX_NUM_RESOURCE_TEXTURES, nullptr) {} @@ -432,6 +436,7 @@ protected: void do_setFramebuffer(Batch& batch, size_t paramOffset); void do_clearFramebuffer(Batch& batch, size_t paramOffset); void do_blit(Batch& batch, size_t paramOffset); + void do_generateTextureMips(Batch& batch, size_t paramOffset); // Synchronize the state cache of this Backend with the actual real state of the GL Context void syncOutputStateCache(); diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 6d8ce7b2c6..5f226141a8 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -361,4 +361,4 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); (void) CHECK_GL_ERROR(); -} \ No newline at end of file +} diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 8601c7512b..8c9647e0f2 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -231,6 +231,7 @@ void GLBackend::releaseResourceTexture(uint32_t slot) { } } + void GLBackend::resetResourceStage() { for (uint32_t i = 0; i < _resource._textures.size(); i++) { releaseResourceTexture(i); @@ -268,3 +269,13 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) { } } +int GLBackend::ResourceStageState::findEmptyTextureSlot() const { + // start from the end of the slots, try to find an empty one that can be used + for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { + if (!_textures[i]) { + return i; + } + } + return -1; +} + diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 65f2e5ca8a..17802ae6ed 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -224,6 +224,11 @@ public: case gpu::SRGBA: texel.internalFormat = GL_SRGB; // standard 2.2 gamma correction color break; + case gpu::R11G11B10: { + // the type should be float + texel.internalFormat = GL_R11F_G11F_B10F; + break; + } default: qCDebug(gpulogging) << "Unknown combination of texel format"; } @@ -240,6 +245,59 @@ public: break; case gpu::RGBA: texel.internalFormat = GL_RGBA; + switch (dstFormat.getType()) { + case gpu::UINT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32UI; + break; + case gpu::INT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32I; + break; + case gpu::FLOAT: + texel.internalFormat = GL_RGBA32F; + break; + case gpu::UINT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16UI; + break; + case gpu::INT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16I; + break; + case gpu::NUINT16: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16; + break; + case gpu::NINT16: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16_SNORM; + break; + case gpu::HALF: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16F; + break; + case gpu::UINT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8UI; + break; + case gpu::INT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8I; + break; + case gpu::NUINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; + case gpu::NINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } break; case gpu::SRGB: texel.internalFormat = GL_SRGB; @@ -347,7 +405,6 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } - object->_target = GL_TEXTURE_2D; syncSampler(texture.getSampler(), texture.getType(), object); @@ -530,3 +587,37 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); } + + + +void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + GLTexture* object = GLBackend::syncGPUObject(*resourceTexture); + if (!object) { + return; + } + + // IN 4.1 we still need to find an available slot + auto freeSlot = _resource.findEmptyTextureSlot(); + auto bindingSlot = (freeSlot < 0 ? 0 : freeSlot); + glActiveTexture(GL_TEXTURE0 + bindingSlot); + glBindTexture(object->_target, object->_texture); + + glGenerateMipmap(object->_target); + + if (freeSlot < 0) { + // If had to use slot 0 then restore state + GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]); + if (boundObject) { + glBindTexture(boundObject->_target, boundObject->_texture); + } + } else { + // clean up + glBindTexture(object->_target, 0); + } + (void)CHECK_GL_ERROR(); +} diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index ddb3a0d755..74b7734618 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -23,7 +23,7 @@ Shader::Shader(Type type, const Source& source): { } -Shader::Shader(Type type, Pointer& vertex, Pointer& pixel): +Shader::Shader(Type type, const Pointer& vertex, const Pointer& pixel): _type(type) { _shaders.resize(2); @@ -44,7 +44,7 @@ Shader::Pointer Shader::createPixel(const Source& source) { return Pointer(new Shader(PIXEL, source)); } -Shader::Pointer Shader::createProgram(Pointer& vertexShader, Pointer& pixelShader) { +Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { if (vertexShader && vertexShader->getType() == VERTEX && pixelShader && pixelShader->getType() == PIXEL) { return Pointer(new Shader(PROGRAM, vertexShader, pixelShader)); diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index bceb00c71e..b737a42e12 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -111,7 +111,7 @@ public: static Pointer createVertex(const Source& source); static Pointer createPixel(const Source& source); - static Pointer createProgram(Pointer& vertexShader, Pointer& pixelShader); + static Pointer createProgram(const Pointer& vertexShader, const Pointer& pixelShader); ~Shader(); @@ -157,7 +157,7 @@ public: protected: Shader(Type type, const Source& source); - Shader(Type type, Pointer& vertex, Pointer& pixel); + Shader(Type type, const Pointer& vertex, const Pointer& pixel); Shader(const Shader& shader); // deep copy of the sysmem shader Shader& operator=(const Shader& shader); // deep copy of the sysmem texture diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index c3e2809c4b..6e8eb10380 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -407,7 +407,7 @@ public: TexturePointer _texture = TexturePointer(NULL); uint16 _subresource = 0; - Element _element = Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); + Element _element = Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); TextureView() {}; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e1aeb92e41..4fd47affc2 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -308,10 +308,9 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap); } if (!material.emissiveTexture.filename.isEmpty()) { - networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content); + networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), LIGHTMAP_TEXTURE, material.emissiveTexture.content); networkMaterial->emissiveTextureName = material.emissiveTexture.name; - //checkForTexcoordLightmap = true; auto lightmapMap = model::TextureMapPointer(new model::TextureMap()); lightmapMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3e80b6c7aa..ae705faf86 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -85,7 +85,7 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; } - _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB), 256, 2)); + _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2)); _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); } return _permutationNormalTexture; @@ -98,7 +98,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF }; const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { - _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; @@ -106,7 +106,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { - _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_GRAY); } return _grayTexture; @@ -114,7 +114,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { - _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; @@ -122,7 +122,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { - _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } return _blackTexture; @@ -151,11 +151,11 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path) { QImage image = QImage(path).mirrored(false, true); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); } gpu::TexturePointer texture = gpu::TexturePointer( gpu::Texture::create2D(formatGPU, image.width(), image.height(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index cc22509631..4171b6c1cb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -29,7 +29,7 @@ class NetworkTexture; typedef QSharedPointer NetworkTexturePointer; -enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, CUSTOM_TEXTURE }; +enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, LIGHTMAP_TEXTURE, CUSTOM_TEXTURE }; /// Stores cached textures, including render-to-texture targets. class TextureCache : public ResourceCache, public Dependency { diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index c2ff828af3..1d0f6ee5d9 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -44,9 +44,9 @@ Material& Material::operator= (const Material& material) { Material::~Material() { } -void Material::setDiffuse(const Color& diffuse) { +void Material::setDiffuse(const Color& diffuse, bool isSRGB) { _key.setDiffuse(glm::any(glm::greaterThan(diffuse, Color(0.0f)))); - _schemaBuffer.edit()._diffuse = diffuse; + _schemaBuffer.edit()._diffuse = (isSRGB ? ColorUtils::toLinearVec3(diffuse) : diffuse); } void Material::setMetallic(float metallic) { @@ -54,9 +54,9 @@ void Material::setMetallic(float metallic) { _schemaBuffer.edit()._metallic = glm::vec3(metallic); } -void Material::setEmissive(const Color& emissive) { +void Material::setEmissive(const Color& emissive, bool isSRGB) { _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); - _schemaBuffer.edit()._emissive = emissive; + _schemaBuffer.edit()._emissive = (isSRGB ? ColorUtils::toLinearVec3(emissive) : emissive); } void Material::setGloss(float gloss) { diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index b0725d9908..cb5a285dba 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -219,28 +219,31 @@ public: virtual ~Material(); const MaterialKey& getKey() const { return _key; } - - const Color& getEmissive() const { return _schemaBuffer.get()._emissive; } - const Color& getDiffuse() const { return _schemaBuffer.get()._diffuse; } - float getMetallic() const { return _schemaBuffer.get()._metallic.x; } - float getGloss() const { return _schemaBuffer.get()._gloss; } - float getOpacity() const { return _schemaBuffer.get()._opacity; } - void setEmissive(const Color& emissive); - void setDiffuse(const Color& diffuse); + void setEmissive(const Color& emissive, bool isSRGB = true); + Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._emissive) : _schemaBuffer.get()._emissive); } + + void setDiffuse(const Color& diffuse, bool isSRGB = true); + Color getDiffuse(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._diffuse) : _schemaBuffer.get()._diffuse); } + void setMetallic(float metallic); + float getMetallic() const { return _schemaBuffer.get()._metallic.x; } + void setGloss(float gloss); + float getGloss() const { return _schemaBuffer.get()._gloss; } + void setOpacity(float opacity); + float getOpacity() const { return _schemaBuffer.get()._opacity; } // Schema to access the attribute values of the material class Schema { public: - Color _diffuse{0.5f}; + glm::vec3 _diffuse{ 0.5f }; float _opacity{1.f}; - Color _metallic{0.03f}; + glm::vec3 _metallic{ 0.03f }; float _gloss{0.1f}; - Color _emissive{0.0f}; + glm::vec3 _emissive{ 0.0f }; float _spare0{0.0f}; glm::vec4 _spareVec4{0.0f}; // for alignment beauty, Material size == Mat4x4 diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 8c37359638..476ac2fa08 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -96,7 +96,12 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky } }); + // Render + gpu::TexturePointer skymap = skybox.getCubemap(); + // FIXME: skymap->isDefined may not be threadsafe + assert(skymap && skymap->isDefined()); + glm::mat4 projMat; viewFrustum.evalProjectionMatrix(projMat); @@ -106,11 +111,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac - gpu::TexturePointer skymap; - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - skymap = skybox.getCubemap(); - } - batch.setPipeline(thePipeline); batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer); batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap); @@ -118,6 +118,5 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.draw(gpu::TRIANGLE_STRIP, 4); batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr); - } diff --git a/libraries/model/src/model/Skybox.slf b/libraries/model/src/model/Skybox.slf index 6246bbd9d3..f8a568bcf9 100755 --- a/libraries/model/src/model/Skybox.slf +++ b/libraries/model/src/model/Skybox.slf @@ -52,8 +52,7 @@ void main(void) { } } - vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction - _fragColor = vec4(pixel, 0.0); + _fragColor = vec4(color, 0.0); #endif diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 67f0262b61..47d3ba3063 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -225,6 +225,21 @@ void SunSkyStage::setOriginOrientation(const Quat& orientation) { invalidate(); } +void SunSkyStage::setOriginLongitude(float longitude) { + _earthSunModel.setLongitude(longitude); + invalidate(); +} + +void SunSkyStage::setOriginLatitude(float latitude) { + _earthSunModel.setLatitude(latitude); + invalidate(); +} + +void SunSkyStage::setOriginSurfaceAltitude(float altitude) { + _earthSunModel.setAltitude(altitude); + invalidate(); +} + void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { _earthSunModel.setLongitude(longitude); _earthSunModel.setLatitude(latitude); diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 978c308ac6..bf586b6b55 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -184,6 +184,9 @@ public: const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] + void setOriginLatitude(float latitude); + void setOriginLongitude(float longitude); + void setOriginSurfaceAltitude(float surfaceAltitude); void setOriginLocation(float longitude, float latitude, float surfaceAltitude); float getOriginLatitude() const { return _earthSunModel.getLatitude(); } float getOriginLongitude() const { return _earthSunModel.getLongitude(); } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d443522a6f..a86c7cdbec 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -119,13 +119,13 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con if ((image.width() > 0) && (image.height() > 0)) { // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } @@ -156,11 +156,11 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src bool isLinearRGB = true; - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -246,11 +246,11 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } @@ -368,11 +368,11 @@ gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, c // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } @@ -520,3 +520,89 @@ gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, c return theTexture; } + + +gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + QImage image = srcImage; + + int imageArea = image.width() * image.height(); + + int opaquePixels = 0; + int translucentPixels = 0; + //bool isTransparent = false; + int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; + const int EIGHT_BIT_MAXIMUM = 255; + QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); + + if (!image.hasAlphaChannel()) { + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } + // int redTotal = 0, greenTotal = 0, blueTotal = 0; + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + QRgb rgb = image.pixel(x, y); + redTotal += qRed(rgb); + greenTotal += qGreen(rgb); + blueTotal += qBlue(rgb); + } + } + if (imageArea > 0) { + averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); + } + } else { + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + + // check for translucency/false transparency + // int opaquePixels = 0; + // int translucentPixels = 0; + // int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + QRgb rgb = image.pixel(x, y); + redTotal += qRed(rgb); + greenTotal += qGreen(rgb); + blueTotal += qBlue(rgb); + int alpha = qAlpha(rgb); + alphaTotal += alpha; + if (alpha == EIGHT_BIT_MAXIMUM) { + opaquePixels++; + } else if (alpha != 0) { + translucentPixels++; + } + } + } + if (opaquePixels == imageArea) { + qCDebug(modelLog) << "Image with alpha channel is completely opaque:" << QString(srcImageName.c_str()); + image = image.convertToFormat(QImage::Format_RGB888); + } + + averageColor = QColor(redTotal / imageArea, + greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea); + + //isTransparent = (translucentPixels >= imageArea / 2); + } + + gpu::Texture* theTexture = nullptr; + if ((image.width() > 0) && (image.height() > 0)) { + + // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + } + + + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + theTexture->autoGenerateMips(-1); + } + + return theTexture; +} diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index 107ca2e879..6bc5b8228c 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -35,6 +35,8 @@ public: static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); + }; diff --git a/libraries/networking/src/AbstractUriHandler.h b/libraries/networking/src/AbstractUriHandler.h new file mode 100644 index 0000000000..3d6ed472ec --- /dev/null +++ b/libraries/networking/src/AbstractUriHandler.h @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis on 2015/12/17 +// Copyright 2013-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 +// + +#pragma once +#ifndef hifi_network_AbstractUriHandler_h +#define hifi_network_AbstractUriHandler_h + +class AbstractUriHandler { +public: + virtual bool canAcceptURL(const QString& url) const = 0; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) = 0; +}; + +#endif diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e591ee8a31..5af2bbe7c7 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -94,7 +94,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); - connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::stopKeepalivePingTimer); + connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // we definitely want STUN to update our public socket, so call the LNL to kick that off startSTUNPublicSocketUpdate(); @@ -234,6 +234,7 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) void NodeList::sendDomainServerCheckIn() { if (_isShuttingDown) { qCDebug(networking) << "Refusing to send a domain-server check in while shutting down."; + return; } if (_publicSockAddr.isNull()) { diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index fd465a0aed..f195011290 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -23,16 +23,27 @@ QMutex ResourceManager::_prefixMapLock; void ResourceManager::setUrlPrefixOverride(const QString& prefix, const QString& replacement) { QMutexLocker locker(&_prefixMapLock); - _prefixMap[prefix] = replacement; + if (replacement.isEmpty()) { + _prefixMap.erase(prefix); + } else { + _prefixMap[prefix] = replacement; + } } QString ResourceManager::normalizeURL(const QString& urlString) { QString result = urlString; - QMutexLocker locker(&_prefixMapLock); - foreach(const auto& entry, _prefixMap) { + PrefixMap copy; + + { + QMutexLocker locker(&_prefixMapLock); + copy = _prefixMap; + } + + foreach(const auto& entry, copy) { const auto& prefix = entry.first; const auto& replacement = entry.second; if (result.startsWith(prefix)) { + qDebug() << "Replacing " << prefix << " with " << replacement; result.replace(0, prefix.size(), replacement); } } diff --git a/libraries/networking/src/ResourceScriptingInterface.cpp b/libraries/networking/src/ResourceScriptingInterface.cpp new file mode 100644 index 0000000000..38be49049c --- /dev/null +++ b/libraries/networking/src/ResourceScriptingInterface.cpp @@ -0,0 +1,15 @@ +// +// Created by Bradley Austin Davis on 2015/12/29 +// 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 "ResourceScriptingInterface.h" + +#include "ResourceManager.h" + +void ResourceScriptingInterface::overrideUrlPrefix(const QString& prefix, const QString& replacement) { + ResourceManager::setUrlPrefixOverride(prefix, replacement); +} diff --git a/libraries/networking/src/ResourceScriptingInterface.h b/libraries/networking/src/ResourceScriptingInterface.h new file mode 100644 index 0000000000..d9777e7514 --- /dev/null +++ b/libraries/networking/src/ResourceScriptingInterface.h @@ -0,0 +1,31 @@ +// +// AssetClient.h +// libraries/networking/src +// +// Created by Ryan Huffman on 2015/07/21 +// 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_networking_ResourceScriptingInterface_h +#define hifi_networking_ResourceScriptingInterface_h + +#include + +#include + +class ResourceScriptingInterface : public QObject, public Dependency { + Q_OBJECT +public: + Q_INVOKABLE void overrideUrlPrefix(const QString& prefix, const QString& replacement); + + Q_INVOKABLE void restoreUrlPrefix(const QString& prefix) { + overrideUrlPrefix(prefix, ""); + } +}; + + +#endif diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 9aeddbc99c..2f5ab7a015 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -122,8 +122,10 @@ void ThreadedAssignment::startSendingStats() { } void ThreadedAssignment::stopSendingStats() { - // stop sending stats, we just disconnected from domain - _statsTimer->stop(); + if (_statsTimer) { + // stop sending stats, we just disconnected from domain + _statsTimer->stop(); + } } void ThreadedAssignment::checkInWithDomainServerOrExit() { diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 4daf7f83b6..ca869ec29f 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -44,7 +44,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP; case PacketType::AvatarData: case PacketType::BulkAvatarData: - return 17; + return static_cast(AvatarMixerPacketVersion::SoftAttachmentSupport); default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 5daa185af4..3b4b358daf 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -163,4 +163,9 @@ const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50; const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51; const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52; +enum class AvatarMixerPacketVersion : PacketVersion { + TranslationSupport = 17, + SoftAttachmentSupport +}; + #endif // hifi_PacketHeaders_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 636d1a9a1a..652089ebb1 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1867,7 +1867,7 @@ bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputSt QByteArray jsonBuffer; char* rawData = new char[READ_JSON_BUFFER_SIZE]; - while (true) { + while (!inputStream.atEnd()) { int got = inputStream.readRawData(rawData, READ_JSON_BUFFER_SIZE - 1); if (got < 0) { qCritical() << "error while reading from json stream"; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 40366257df..5a12627abd 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -44,7 +44,7 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) { void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { assert(entity); - if (entity->shouldBePhysical()) { + if (entity->shouldBePhysical()) { EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); if (!motionState) { _pendingAdds.insert(entity); @@ -117,6 +117,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { _pendingRemoves.clear(); _pendingAdds.clear(); _pendingChanges.clear(); + _outgoingChanges.clear(); } // end EntitySimulation overrides diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 27e326fcba..4429f49346 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -79,6 +79,7 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() { auto& container = PluginContainer::getInstance(); for (auto plugin : displayPlugins) { plugin->setContainer(&container); + plugin->init(); } }); @@ -104,6 +105,7 @@ const InputPluginList& PluginManager::getInputPlugins() { auto& container = PluginContainer::getInstance(); for (auto plugin : inputPlugins) { plugin->setContainer(&container); + plugin->init(); } }); return inputPlugins; diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index ce6f29c3d5..167d49cbaf 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -48,6 +48,10 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, } if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { + gpu::TexturePointer skymap = skybox.getCubemap(); + // FIXME: skymap->isDefined may not be threadsafe + assert(skymap && skymap->isDefined()); + glm::mat4 projMat; viewFrustum.evalProjectionMatrix(projMat); @@ -56,10 +60,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, batch.setProjectionTransform(projMat); batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac - - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - batch.setResourceTexture(0, skybox.getCubemap()); - } + batch.setResourceTexture(0, skybox.getCubemap()); skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1)); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.slf b/libraries/procedural/src/procedural/ProceduralSkybox.slf index 382801f52d..7ad6f6b5a1 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.slf +++ b/libraries/procedural/src/procedural/ProceduralSkybox.slf @@ -35,6 +35,8 @@ void main(void) { #ifdef PROCEDURAL vec3 color = getSkyboxColor(); + // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline + color = pow(color, vec3(2.2)); _fragColor = vec4(color, 0.0); #else @@ -42,8 +44,7 @@ void main(void) { vec3 coord = normalize(_normal); vec3 texel = texture(cubeMap, coord).rgb; vec3 color = texel * _skybox._color.rgb; - vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction - _fragColor = vec4(pixel, 0.0); + _fragColor = vec4(color, 0.0); #endif diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index e54f55fc94..c32bf2654d 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -175,10 +175,10 @@ const gpu::PipelinePointer& AmbientOcclusion::getBlendPipeline() { } void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto framebufferCache = DependencyManager::get(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); @@ -201,7 +201,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Occlusion step getOcclusionPipeline(); batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture()); - batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); + batch.setResourceTexture(1, framebufferCache->getDeferredNormalTexture()); _occlusionBuffer->setRenderBuffer(0, _occlusionTexture); batch.setFramebuffer(_occlusionBuffer); @@ -276,7 +276,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Blend step getBlendPipeline(); batch.setResourceTexture(0, _hBlurTexture); - batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); + batch.setFramebuffer(framebufferCache->getDeferredFramebuffer()); // Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture batch.setPipeline(getBlendPipeline()); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index d4707c172d..3acc88f953 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -93,14 +93,14 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { } void Antialiasing::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - if (renderContext->args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + if (renderContext->getArgs()->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { return; } - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); @@ -123,7 +123,7 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re // FXAA step getAntialiasingPipeline(); - batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); + batch.setResourceTexture(0, framebufferCache->getDeferredColorTexture()); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); batch.setFramebuffer(_antialiasingBuffer); batch.setPipeline(getAntialiasingPipeline()); @@ -153,7 +153,7 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re // Blend step getBlendPipeline(); batch.setResourceTexture(0, _antialiasingTexture); - batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); + batch.setFramebuffer(framebufferCache->getDeferredFramebuffer()); batch.setPipeline(getBlendPipeline()); DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp new file mode 100644 index 0000000000..ca678770fb --- /dev/null +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -0,0 +1,214 @@ +// +// DebugDeferredBuffer.cpp +// libraries/render-utils/src +// +// Created by Clement on 12/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DebugDeferredBuffer.h" + +#include + +#include +#include +#include +#include + +#include "GeometryCache.h" +#include "FramebufferCache.h" + +#include "debug_deferred_buffer_vert.h" +#include "debug_deferred_buffer_frag.h" + +using namespace render; + +enum Slots { + Diffuse = 0, + Normal, + Specular, + Depth, + Lighting +}; + +static const std::string DEEFAULT_DIFFUSE_SHADER { + "vec4 getFragmentColor() {" + " return vec4(pow(texture(diffuseMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" + " }" +}; +static const std::string DEEFAULT_ALPHA_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(diffuseMap, uv).a), 1.0);" + " }" +}; +static const std::string DEEFAULT_SPECULAR_SHADER { + "vec4 getFragmentColor() {" + " return vec4(texture(specularMap, uv).xyz, 1.0);" + " }" +}; +static const std::string DEEFAULT_ROUGHNESS_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(specularMap, uv).a), 1.0);" + " }" +}; +static const std::string DEEFAULT_NORMAL_SHADER { + "vec4 getFragmentColor() {" + " return vec4(normalize(texture(normalMap, uv).xyz), 1.0);" + " }" +}; +static const std::string DEEFAULT_DEPTH_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(depthMap, uv).x), 1.0);" + " }" +}; +static const std::string DEEFAULT_LIGHTING_SHADER { + "vec4 getFragmentColor() {" + " return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" + " }" +}; +static const std::string DEEFAULT_CUSTOM_SHADER { + "vec4 getFragmentColor() {" + " return vec4(1.0, 0.0, 0.0, 1.0);" + " }" +}; + +static std::string getFileContent(std::string fileName, std::string defaultContent = std::string()) { + QFile customFile(QString::fromStdString(fileName)); + if (customFile.open(QIODevice::ReadOnly)) { + return customFile.readAll().toStdString(); + } + qWarning() << "DebugDeferredBuffer::getFileContent(): Could not open" + << QString::fromStdString(fileName); + return defaultContent; +} + +#include // TODO REMOVE: Temporary until UI +DebugDeferredBuffer::DebugDeferredBuffer() { + // TODO REMOVE: Temporary until UI + static const auto DESKTOP_PATH = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + static const auto CUSTOM_FILE = DESKTOP_PATH.toStdString() + "/custom.slh"; + CustomPipeline pipeline; + pipeline.info = QFileInfo(QString::fromStdString(CUSTOM_FILE)); + _customPipelines.emplace(CUSTOM_FILE, pipeline); +} + +std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string customFile) { + switch (mode) { + case DiffuseMode: + return DEEFAULT_DIFFUSE_SHADER; + case AlphaMode: + return DEEFAULT_ALPHA_SHADER; + case SpecularMode: + return DEEFAULT_SPECULAR_SHADER; + case RoughnessMode: + return DEEFAULT_ROUGHNESS_SHADER; + case NormalMode: + return DEEFAULT_NORMAL_SHADER; + case DepthMode: + return DEEFAULT_DEPTH_SHADER; + case LightingMode: + return DEEFAULT_LIGHTING_SHADER; + case CustomMode: + return getFileContent(customFile, DEEFAULT_CUSTOM_SHADER); + } + Q_UNREACHABLE(); + return std::string(); +} + +bool DebugDeferredBuffer::pipelineNeedsUpdate(Modes mode, std::string customFile) const { + if (mode != CustomMode) { + return !_pipelines[mode]; + } + + auto it = _customPipelines.find(customFile); + if (it != _customPipelines.end() && it->second.pipeline) { + auto& info = it->second.info; + + auto lastModified = info.lastModified(); + info.refresh(); + return lastModified != info.lastModified(); + } + + return true; +} + +const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::string customFile) { + if (pipelineNeedsUpdate(mode, customFile)) { + static const std::string VERTEX_SHADER { debug_deferred_buffer_vert }; + static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag }; + static const std::string SOURCE_PLACEHOLDER { "//SOURCE_PLACEHOLDER" }; + static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); + Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, + "Could not find source placeholder"); + + auto bakedFragmentShader = FRAGMENT_SHADER; + bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), + getShaderSourceCode(mode, customFile)); + + static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto ps = gpu::Shader::createPixel(bakedFragmentShader); + const auto program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("diffuseMap", Diffuse)); + slotBindings.insert(gpu::Shader::Binding("normalMap", Normal)); + slotBindings.insert(gpu::Shader::Binding("specularMap", Specular)); + slotBindings.insert(gpu::Shader::Binding("depthMap", Depth)); + slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting)); + gpu::Shader::makeProgram(*program, slotBindings); + + auto pipeline = gpu::Pipeline::create(program, std::make_shared()); + + // Good to go add the brand new pipeline + if (mode != CustomMode) { + _pipelines[mode] = pipeline; + } else { + _customPipelines[customFile].pipeline = pipeline; + } + } + + if (mode != CustomMode) { + return _pipelines[mode]; + } else { + return _customPipelines[customFile].pipeline; + } +} + + +void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + const auto geometryBuffer = DependencyManager::get(); + const auto framebufferCache = DependencyManager::get(); + + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // TODO REMOVE: Temporary until UI + auto first = _customPipelines.begin()->first; + + batch.setPipeline(getPipeline(Modes(renderContext->_deferredDebugMode), first)); + + batch.setResourceTexture(Diffuse, framebufferCache->getDeferredColorTexture()); + batch.setResourceTexture(Normal, framebufferCache->getDeferredNormalTexture()); + batch.setResourceTexture(Specular, framebufferCache->getDeferredSpecularTexture()); + batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture()); + batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture()); + + const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); + const glm::vec2 bottomLeft(renderContext->_deferredDebugSize.x, renderContext->_deferredDebugSize.y); + const glm::vec2 topRight(renderContext->_deferredDebugSize.z, renderContext->_deferredDebugSize.w); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color); + }); +} diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h new file mode 100644 index 0000000000..682888b2af --- /dev/null +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -0,0 +1,54 @@ +// +// DebugDeferredBuffer.h +// libraries/render-utils/src +// +// Created by Clement on 12/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DebugDeferredBuffer_h +#define hifi_DebugDeferredBuffer_h + +#include + +#include + +class DebugDeferredBuffer { +public: + using JobModel = render::Job::Model; + + DebugDeferredBuffer(); + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + +private: + enum Modes : uint8_t { + DiffuseMode = 0, + AlphaMode, + SpecularMode, + RoughnessMode, + NormalMode, + DepthMode, + LightingMode, + + CustomMode // Needs to stay last + }; + struct CustomPipeline { + gpu::PipelinePointer pipeline; + mutable QFileInfo info; + }; + using StandardPipelines = std::array; + using CustomPipelines = std::unordered_map; + + bool pipelineNeedsUpdate(Modes mode, std::string customFile = std::string()) const; + const gpu::PipelinePointer& getPipeline(Modes mode, std::string customFile = std::string()); + std::string getShaderSourceCode(Modes mode, std::string customFile = std::string()); + + StandardPipelines _pipelines; + CustomPipelines _customPipelines; +}; + +#endif // hifi_DebugDeferredBuffer_h \ No newline at end of file diff --git a/libraries/render-utils/src/DeferredBuffer.slh b/libraries/render-utils/src/DeferredBuffer.slh index 275966534a..18606f2525 100755 --- a/libraries/render-utils/src/DeferredBuffer.slh +++ b/libraries/render-utils/src/DeferredBuffer.slh @@ -24,6 +24,9 @@ uniform sampler2D specularMap; // the depth texture uniform sampler2D depthMap; +// the lighting texture +uniform sampler2D lightingMap; + struct DeferredTransform { mat4 projection; diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 1c1330f0c0..1045c4afc7 100755 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -15,12 +15,6 @@ layout(location = 0) out vec4 _fragColor0; layout(location = 1) out vec4 _fragColor1; layout(location = 2) out vec4 _fragColor2; -// the glow intensity -uniform float glowIntensity; - -// the alpha threshold -uniform float alphaThreshold; - uniform sampler2D normalFittingMap; vec3 bestFitNormal(vec3 normal) { @@ -39,36 +33,38 @@ vec3 bestFitNormal(vec3 normal) { return (cN * 0.5 + 0.5); } + +// the alpha threshold +const float alphaThreshold = 0.5; float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { - return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold)); + return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); } const vec3 DEFAULT_SPECULAR = vec3(0.1); const float DEFAULT_SHININESS = 10; void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) { - if (alpha != glowIntensity) { + if (alpha != 1.0) { discard; } - _fragColor0 = vec4(diffuse.rgb, alpha); + _fragColor0 = vec4(diffuse.rgb, 1.0); // Opaque _fragColor1 = vec4(bestFitNormal(normal), 1.0); _fragColor2 = vec4(specular, shininess / 128.0); } void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) { - if (alpha != glowIntensity) { + if (alpha != 1.0) { discard; } - _fragColor0 = vec4(diffuse.rgb, alpha); - //_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); + _fragColor0 = vec4(diffuse.rgb, 0.5); _fragColor1 = vec4(bestFitNormal(normal), 0.5); _fragColor2 = vec4(emissive, shininess / 128.0); } void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) { - if (alpha <= alphaThreshold) { + if (alpha <= 0.0) { discard; } diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 983b8002f7..11e157a50c 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -83,7 +83,7 @@ vec3 evalAmbienGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); - color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + color += vec3(diffuse * shading.w + shading.rgb) * shadowAttenuation * getLightColor(light) * getLightIntensity(light); return color; } @@ -106,7 +106,7 @@ vec3 evalAmbienSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); - color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + color += vec3(diffuse * shading.w + shading.rgb) * shadowAttenuation * getLightColor(light) * getLightIntensity(light); return color; } @@ -129,7 +129,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); - color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + color += vec3(diffuse * shading.w + shading.rgb) * shadowAttenuation * getLightColor(light) * getLightIntensity(light); return color; } diff --git a/libraries/render-utils/src/DeferredLighting.slh b/libraries/render-utils/src/DeferredLighting.slh index 888742bb18..cf03861c2f 100755 --- a/libraries/render-utils/src/DeferredLighting.slh +++ b/libraries/render-utils/src/DeferredLighting.slh @@ -23,17 +23,16 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 sp // Specular Lighting depends on the half vector and the gloss vec3 halfDir = normalize(fragEyeDir + fragLightDir); - // float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); float specularPower = pow(max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); specularPower *= (gloss * 128.0 * 0.125 + 0.25); float shlickPower = (1.0 - dot(fragLightDir,halfDir)); float shlickPower2 = shlickPower * shlickPower; float shlickPower5 = shlickPower2 * shlickPower2 * shlickPower; - vec3 schlick = specular * (1.0 - shlickPower5) + vec3(shlickPower5); - vec3 reflect = specularPower * schlick; + vec3 fresnel = specular * (1.0 - shlickPower5) + vec3(shlickPower5); + vec3 reflect = specularPower * fresnel * diffuse; - return vec4(reflect, diffuse); + return vec4(reflect, diffuse * (1 - fresnel.x)); } <@endfunc@> @@ -49,7 +48,7 @@ vec4 evalBlinnShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 vec3 halfDir = normalize(fragEyeDir + fragLightDir); float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); - vec3 reflect = specularPower * specular; + vec3 reflect = specularPower * specular * diffuse; return vec4(reflect, diffuse); } @@ -59,14 +58,9 @@ vec4 evalBlinnShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 <$declareEvalPBRShading()$> - +// Return xyz the specular/reflection component and w the diffuse component vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 specular, float gloss) { - - /*if (gl_FragCoord.x > 1000) { - return evalBlinnShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); - } else {*/ - return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); - //} + return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); } <@endif@> diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index dcadaa5177..db0e47de5e 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -34,25 +34,13 @@ #include "deferred_light_spot_vert.h" #include "directional_light_frag.h" -#include "directional_light_shadow_map_frag.h" -#include "directional_light_cascaded_shadow_map_frag.h" - #include "directional_ambient_light_frag.h" -#include "directional_ambient_light_shadow_map_frag.h" -#include "directional_ambient_light_cascaded_shadow_map_frag.h" - #include "directional_skybox_light_frag.h" -#include "directional_skybox_light_shadow_map_frag.h" -#include "directional_skybox_light_cascaded_shadow_map_frag.h" #include "point_light_frag.h" #include "spot_light_frag.h" -static const std::string glowIntensityShaderHandle = "glowIntensity"; - struct LightLocations { - int shadowDistances; - int shadowScale; int radius; int ambientSphere; int lightBufferUnit; @@ -92,7 +80,7 @@ gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config return pipeline; } -void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { +void DeferredLightingEffect::init() { auto VS = gpu::Shader::createVertex(std::string(simple_vert)); auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); auto PSEmissive = gpu::Shader::createPixel(std::string(simple_textured_emisive_frag)); @@ -105,54 +93,23 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { gpu::Shader::makeProgram(*_simpleShader, slotBindings); gpu::Shader::makeProgram(*_emissiveShader, slotBindings); - _viewState = viewState; + _directionalLightLocations = std::make_shared(); - _directionalLightShadowMapLocations = std::make_shared(); - _directionalLightCascadedShadowMapLocations = std::make_shared(); _directionalAmbientSphereLightLocations = std::make_shared(); - _directionalAmbientSphereLightShadowMapLocations = std::make_shared(); - _directionalAmbientSphereLightCascadedShadowMapLocations = std::make_shared(); _directionalSkyboxLightLocations = std::make_shared(); - _directionalSkyboxLightShadowMapLocations = std::make_shared(); - _directionalSkyboxLightCascadedShadowMapLocations = std::make_shared(); _pointLightLocations = std::make_shared(); _spotLightLocations = std::make_shared(); loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations); - loadLightProgram(deferred_light_vert, directional_light_shadow_map_frag, false, _directionalLightShadowMap, - _directionalLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap, - _directionalLightCascadedShadowMapLocations); loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap, - _directionalAmbientSphereLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap, - _directionalAmbientSphereLightCascadedShadowMapLocations); loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap, - _directionalSkyboxLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap, - _directionalSkyboxLightCascadedShadowMapLocations); loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations); loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations); - { - //auto VSFS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - //auto PSBlit = gpu::StandardShaderLib::getDrawTexturePS(); - auto blitProgram = gpu::StandardShaderLib::getProgram(gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS, gpu::StandardShaderLib::getDrawTexturePS); - gpu::Shader::makeProgram(*blitProgram); - auto blitState = std::make_shared(); - blitState->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - blitState->setColorWriteMask(true, true, true, false); - _blitLightBuffer = gpu::Pipeline::create(blitProgram, blitState); - } - // Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light _globalLights.push_back(0); _allocatedLights.push_back(std::make_shared()); @@ -175,8 +132,6 @@ gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch batch.setPipeline(pipeline); gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; - int glowIntensity = program->getUniforms().findLocation("glowIntensity"); - batch._glUniform1f(glowIntensity, 1.0f); if (!config.isTextured()) { // If it is not textured, bind white texture and keep using textured pipeline @@ -342,11 +297,27 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu void DeferredLightingEffect::prepare(RenderArgs* args) { gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); + batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); + // Clear Lighting buffer + auto lightingFbo = DependencyManager::get()->getLightingFramebuffer(); + + batch.setFramebuffer(lightingFbo); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(vec3(0), 0), true); + + // Clear deferred + auto deferredFbo = DependencyManager::get()->getDeferredFramebuffer(); + + batch.setFramebuffer(deferredFbo); + + // Clear Color, Depth and Stencil + batch.clearFramebuffer( + gpu::Framebuffer::BUFFER_COLOR0 | + gpu::Framebuffer::BUFFER_DEPTH | + gpu::Framebuffer::BUFFER_STENCIL, + vec4(vec3(0), 1), 1.0, 0.0, true); - batch.setFramebuffer(primaryFbo); // clear the normal and specular buffers batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); const float MAX_SPECULAR_EXPONENT = 128.0f; @@ -354,8 +325,6 @@ void DeferredLightingEffect::prepare(RenderArgs* args) { }); } -gpu::FramebufferPointer _copyFBO; - void DeferredLightingEffect::render(RenderArgs* args) { gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { @@ -375,18 +344,16 @@ void DeferredLightingEffect::render(RenderArgs* args) { QSize framebufferSize = framebufferCache->getFrameBufferSize(); // binding the first framebuffer - _copyFBO = framebufferCache->getFramebuffer(); - batch.setFramebuffer(_copyFBO); + auto lightingFBO = framebufferCache->getLightingFramebuffer(); + batch.setFramebuffer(lightingFBO); - // Clearing it batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); // BInd the G-Buffer surfaces - batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); - batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); - batch.setResourceTexture(2, framebufferCache->getPrimarySpecularTexture()); + batch.setResourceTexture(0, framebufferCache->getDeferredColorTexture()); + batch.setResourceTexture(1, framebufferCache->getDeferredNormalTexture()); + batch.setResourceTexture(2, framebufferCache->getDeferredSpecularTexture()); batch.setResourceTexture(3, framebufferCache->getPrimaryDepthTexture()); // THe main viewport is assumed to be the mono viewport (or the 2 stereo faces side by side within that viewport) @@ -679,42 +646,6 @@ void DeferredLightingEffect::render(RenderArgs* args) { } } - -void DeferredLightingEffect::copyBack(RenderArgs* args) { - auto framebufferCache = DependencyManager::get(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - batch.enableStereo(false); - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - - // TODO why doesn't this blit work? It only seems to affect a small area below the rear view mirror. - // auto destFbo = framebufferCache->getPrimaryFramebuffer(); - auto destFbo = framebufferCache->getPrimaryFramebufferDepthColor(); - // gpu::Vec4i vp = args->_viewport; - // batch.blit(_copyFBO, vp, framebufferCache->getPrimaryFramebuffer(), vp); - batch.setFramebuffer(destFbo); - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); - { - float sMin = args->_viewport.x / (float)framebufferSize.width(); - float sWidth = args->_viewport.z / (float)framebufferSize.width(); - float tMin = args->_viewport.y / (float)framebufferSize.height(); - float tHeight = args->_viewport.w / (float)framebufferSize.height(); - Transform model; - batch.setPipeline(_blitLightBuffer); - model.setTranslation(glm::vec3(sMin, tMin, 0.0)); - model.setScale(glm::vec3(sWidth, tHeight, 1.0)); - batch.setModelTransform(model); - } - - batch.setResourceTexture(0, _copyFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - args->_context->render(batch); - }); - framebufferCache->releaseFramebuffer(_copyFBO); -} - void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferUnit) { auto globalLight = _allocatedLights[_globalLights.front()]; args->_batch->setUniformBuffer(lightBufferUnit, globalLight->getSchemaBuffer()); @@ -731,7 +662,6 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1)); slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); - slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); const int LIGHT_GPU_SLOT = 3; slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); @@ -742,8 +672,6 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo gpu::Shader::makeProgram(*program, slotBindings); - locations->shadowDistances = program->getUniforms().findLocation("shadowDistances"); - locations->shadowScale = program->getUniforms().findLocation("shadowScale"); locations->radius = program->getUniforms().findLocation("radius"); locations->ambientSphere = program->getUniforms().findLocation("ambientSphere.L00"); @@ -756,6 +684,11 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo locations->deferredTransformBuffer = program->getBuffers().findLocation("deferredTransformBuffer"); auto state = std::make_shared(); + state->setColorWriteMask(true, true, true, false); + + // Stencil test all the light passes for objects pixels only, not the background + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + if (lightVolume) { state->setCullMode(gpu::State::CULL_BACK); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index eee5993c29..dfb0ecbffb 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -21,7 +21,6 @@ #include "model/Stage.h" #include "model/Geometry.h" -class AbstractViewStateInterface; class RenderArgs; class SimpleProgramKey; struct LightLocations; @@ -34,7 +33,7 @@ public: static const int NORMAL_FITTING_MAP_SLOT = 10; static const int DEFERRED_TRANSFORM_BUFFER_SLOT = 2; - void init(AbstractViewStateInterface* viewState); + void init(); /// Sets up the state necessary to render static untextured geometry with the simple program. gpu::PipelinePointer bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, @@ -78,7 +77,6 @@ public: void prepare(RenderArgs* args); void render(RenderArgs* args); - void copyBack(RenderArgs* args); void setupTransparent(RenderArgs* args, int lightBufferUnit); @@ -101,29 +99,15 @@ private: gpu::ShaderPointer _simpleShader; gpu::ShaderPointer _emissiveShader; QHash _simplePrograms; - - gpu::PipelinePointer _blitLightBuffer; - + gpu::PipelinePointer _directionalSkyboxLight; LightLocationsPtr _directionalSkyboxLightLocations; - gpu::PipelinePointer _directionalSkyboxLightShadowMap; - LightLocationsPtr _directionalSkyboxLightShadowMapLocations; - gpu::PipelinePointer _directionalSkyboxLightCascadedShadowMap; - LightLocationsPtr _directionalSkyboxLightCascadedShadowMapLocations; gpu::PipelinePointer _directionalAmbientSphereLight; LightLocationsPtr _directionalAmbientSphereLightLocations; - gpu::PipelinePointer _directionalAmbientSphereLightShadowMap; - LightLocationsPtr _directionalAmbientSphereLightShadowMapLocations; - gpu::PipelinePointer _directionalAmbientSphereLightCascadedShadowMap; - LightLocationsPtr _directionalAmbientSphereLightCascadedShadowMapLocations; gpu::PipelinePointer _directionalLight; LightLocationsPtr _directionalLightLocations; - gpu::PipelinePointer _directionalLightShadowMap; - LightLocationsPtr _directionalLightShadowMapLocations; - gpu::PipelinePointer _directionalLightCascadedShadowMap; - LightLocationsPtr _directionalLightCascadedShadowMapLocations; gpu::PipelinePointer _pointLight; LightLocationsPtr _pointLightLocations; @@ -155,8 +139,6 @@ private: std::vector _globalLights; std::vector _pointLights; std::vector _spotLights; - - AbstractViewStateInterface* _viewState; int _ambientLightMode = 0; model::AtmospherePointer _atmosphere; diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 5907d3fa27..9f3d4ba0fe 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -33,61 +33,76 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; - _primaryFramebufferFull.reset(); - _primaryFramebufferDepthColor.reset(); + _primaryFramebuffer.reset(); _primaryDepthTexture.reset(); _primaryColorTexture.reset(); - _primaryNormalTexture.reset(); - _primarySpecularTexture.reset(); + _deferredFramebuffer.reset(); + _deferredFramebufferDepthColor.reset(); + _deferredColorTexture.reset(); + _deferredNormalTexture.reset(); + _deferredSpecularTexture.reset(); _selfieFramebuffer.reset(); _cachedFramebuffers.clear(); + _lightingTexture.reset(); + _lightingFramebuffer.reset(); } } void FramebufferCache::createPrimaryFramebuffer() { - _primaryFramebufferFull = gpu::FramebufferPointer(gpu::Framebuffer::create()); - _primaryFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _deferredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _deferredFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create()); - auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + auto colorFormat = gpu::Element::COLOR_RGBA_32; auto width = _frameBufferSize.width(); auto height = _frameBufferSize.height(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); _primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primaryNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primarySpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primaryFramebufferFull->setRenderBuffer(0, _primaryColorTexture); - _primaryFramebufferFull->setRenderBuffer(1, _primaryNormalTexture); - _primaryFramebufferFull->setRenderBuffer(2, _primarySpecularTexture); + _primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture); - _primaryFramebufferDepthColor->setRenderBuffer(0, _primaryColorTexture); + _deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + + _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); + _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); + _deferredFramebuffer->setRenderBuffer(2, _deferredSpecularTexture); + + _deferredFramebufferDepthColor->setRenderBuffer(0, _deferredColorTexture); // auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); - - _primaryFramebufferFull->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); - _primaryFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); - + _primaryFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _deferredFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler)); _selfieFramebuffer->setRenderBuffer(0, tex); + + auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); + + // FIXME: Decide on the proper one, let s stick to R11G11B10 for now + //_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, defaultSampler)); + _lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::R11G11B10), width, height, defaultSampler)); + //_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::HALF, gpu::RGBA), width, height, defaultSampler)); + _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); + _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); } gpu::FramebufferPointer FramebufferCache::getPrimaryFramebuffer() { - if (!_primaryFramebufferFull) { + if (!_primaryFramebuffer) { createPrimaryFramebuffer(); } - return _primaryFramebufferFull; -} - -gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferDepthColor() { - if (!_primaryFramebufferDepthColor) { - createPrimaryFramebuffer(); - } - return _primaryFramebufferDepthColor; + return _primaryFramebuffer; } gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() { @@ -104,18 +119,53 @@ gpu::TexturePointer FramebufferCache::getPrimaryColorTexture() { return _primaryColorTexture; } -gpu::TexturePointer FramebufferCache::getPrimaryNormalTexture() { - if (!_primaryNormalTexture) { +gpu::FramebufferPointer FramebufferCache::getDeferredFramebuffer() { + if (!_deferredFramebuffer) { createPrimaryFramebuffer(); } - return _primaryNormalTexture; + return _deferredFramebuffer; } -gpu::TexturePointer FramebufferCache::getPrimarySpecularTexture() { - if (!_primarySpecularTexture) { +gpu::FramebufferPointer FramebufferCache::getDeferredFramebufferDepthColor() { + if (!_deferredFramebufferDepthColor) { createPrimaryFramebuffer(); } - return _primarySpecularTexture; + return _deferredFramebufferDepthColor; +} + +gpu::TexturePointer FramebufferCache::getDeferredColorTexture() { + if (!_deferredColorTexture) { + createPrimaryFramebuffer(); + } + return _deferredColorTexture; +} + +gpu::TexturePointer FramebufferCache::getDeferredNormalTexture() { + if (!_deferredNormalTexture) { + createPrimaryFramebuffer(); + } + return _deferredNormalTexture; +} + +gpu::TexturePointer FramebufferCache::getDeferredSpecularTexture() { + if (!_deferredSpecularTexture) { + createPrimaryFramebuffer(); + } + return _deferredSpecularTexture; +} + +gpu::FramebufferPointer FramebufferCache::getLightingFramebuffer() { + if (!_lightingFramebuffer) { + createPrimaryFramebuffer(); + } + return _lightingFramebuffer; +} + +gpu::TexturePointer FramebufferCache::getLightingTexture() { + if (!_lightingTexture) { + createPrimaryFramebuffer(); + } + return _lightingTexture; } gpu::FramebufferPointer FramebufferCache::getFramebuffer() { diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index e9a1bbf8e8..7ac5e4af63 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -30,12 +30,20 @@ public: /// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is /// used for scene rendering. gpu::FramebufferPointer getPrimaryFramebuffer(); - gpu::FramebufferPointer getPrimaryFramebufferDepthColor(); gpu::TexturePointer getPrimaryDepthTexture(); gpu::TexturePointer getPrimaryColorTexture(); - gpu::TexturePointer getPrimaryNormalTexture(); - gpu::TexturePointer getPrimarySpecularTexture(); + + gpu::FramebufferPointer getDeferredFramebuffer(); + gpu::FramebufferPointer getDeferredFramebufferDepthColor(); + + gpu::TexturePointer getDeferredColorTexture(); + gpu::TexturePointer getDeferredNormalTexture(); + gpu::TexturePointer getDeferredSpecularTexture(); + + + gpu::TexturePointer getLightingTexture(); + gpu::FramebufferPointer getLightingFramebuffer(); /// Returns the framebuffer object used to render shadow maps; gpu::FramebufferPointer getShadowFramebuffer(); @@ -56,14 +64,21 @@ private: void createPrimaryFramebuffer(); - gpu::FramebufferPointer _primaryFramebufferFull; - gpu::FramebufferPointer _primaryFramebufferDepthColor; + gpu::FramebufferPointer _primaryFramebuffer; gpu::TexturePointer _primaryDepthTexture; gpu::TexturePointer _primaryColorTexture; - gpu::TexturePointer _primaryNormalTexture; - gpu::TexturePointer _primarySpecularTexture; - + + gpu::FramebufferPointer _deferredFramebuffer; + gpu::FramebufferPointer _deferredFramebufferDepthColor; + + gpu::TexturePointer _deferredColorTexture; + gpu::TexturePointer _deferredNormalTexture; + gpu::TexturePointer _deferredSpecularTexture; + + gpu::TexturePointer _lightingTexture; + gpu::FramebufferPointer _lightingFramebuffer; + gpu::FramebufferPointer _shadowFramebuffer; gpu::FramebufferPointer _selfieFramebuffer; diff --git a/libraries/render-utils/src/HitEffect.cpp b/libraries/render-utils/src/HitEffect.cpp index 7e8b5f5fa0..bf65eaf059 100644 --- a/libraries/render-utils/src/HitEffect.cpp +++ b/libraries/render-utils/src/HitEffect.cpp @@ -14,6 +14,7 @@ #include +#include #include #include @@ -60,9 +61,9 @@ const gpu::PipelinePointer& HitEffect::getHitEffectPipeline() { } void HitEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { glm::mat4 projMat; diff --git a/libraries/render-utils/src/HitEffect.h b/libraries/render-utils/src/HitEffect.h index b560cf5550..0a96a5300d 100644 --- a/libraries/render-utils/src/HitEffect.h +++ b/libraries/render-utils/src/HitEffect.h @@ -9,11 +9,7 @@ #ifndef hifi_hitEffect_h #define hifi_hitEffect_h -#include -#include "render/DrawTask.h" - -class AbstractViewStateInterface; -class ProgramObject; +#include class HitEffect { public: @@ -23,7 +19,7 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); typedef render::Job::Model JobModel; - const gpu::PipelinePointer& getHitEffectPipeline(); + const gpu::PipelinePointer& getHitEffectPipeline(); private: gpu::PipelinePointer _hitEffectPipeline; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2d8d7b598b..414ad9cf97 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -199,8 +199,6 @@ void MeshPartPayload::render(RenderArgs* args) const { gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - model::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); @@ -217,7 +215,7 @@ void MeshPartPayload::render(RenderArgs* args) const { } ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, args, locations); @@ -395,9 +393,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); const std::vector>& networkMeshes = _model->_geometry->getMeshes(); @@ -467,9 +463,12 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, args, locations); + if (!locations) { // the pipeline could not be found + return; + } // Bind the model transform and the skinCLusterMatrices if needed bindTransform(batch, locations); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index dbd3a6289f..efaa79ddbb 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -971,11 +971,14 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { _needsUpdateClusterMatrices = true; _rig->updateAnimations(deltaTime, parentTransform); } + void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); updateRig(deltaTime, parentTransform); } + +// virtual void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { PerformanceTimer perfTimer("Model::updateClusterMatrices"); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index a93338e41a..3416a9b71e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -112,7 +112,7 @@ public: bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } virtual void simulate(float deltaTime, bool fullUpdate = true); - void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation); + virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation); /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -312,7 +312,7 @@ protected: // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; -private: +protected: void deleteGeometry(); void initJointTransforms(); @@ -370,7 +370,6 @@ private: bool _showCollisionHull = false; friend class ModelMeshPartPayload; -protected: RigPointer _rig; }; diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp index 10c9d738d2..312d34e41b 100644 --- a/libraries/render-utils/src/ModelRender.cpp +++ b/libraries/render-utils/src/ModelRender.cpp @@ -228,10 +228,8 @@ void ModelRender::RenderPipelineLib::addRenderPipeline(ModelRender::RenderKey ke void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, ModelRender::Locations& locations) { - locations.alphaThreshold = program->getUniforms().findLocation("alphaThreshold"); locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); - locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); locations.normalTextureUnit = program->getTextures().findLocation("normalMap"); @@ -244,14 +242,14 @@ void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, } -void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, +void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations) { PerformanceTimer perfTimer("Model::pickPrograms"); getRenderPipelineLib(); - RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + RenderKey key(mode, translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); auto pipeline = _renderPipelineLib.find(key.getRaw()); if (pipeline == _renderPipelineLib.end()) { qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); @@ -266,15 +264,6 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b // Setup the One pipeline batch.setPipeline((*pipeline).second._pipeline); - if ((locations->alphaThreshold > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - batch._glUniform1f(locations->alphaThreshold, alphaThreshold); - } - - if ((locations->glowIntensity > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed - batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); - } - if ((locations->normalFittingMapUnit > -1)) { batch.setResourceTexture(locations->normalFittingMapUnit, DependencyManager::get()->getNormalFittingTexture()); diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h index 39fe05378d..8331440fb0 100644 --- a/libraries/render-utils/src/ModelRender.h +++ b/libraries/render-utils/src/ModelRender.h @@ -29,21 +29,19 @@ public: class Locations { public: - int alphaThreshold; int texcoordMatrices; int diffuseTextureUnit; int normalTextureUnit; int specularTextureUnit; int emissiveTextureUnit; int emissiveParams; - int glowIntensity; int normalFittingMapUnit; int skinClusterBufferUnit; int materialBufferUnit; int lightBufferUnit; }; - static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, + static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations); @@ -111,9 +109,9 @@ public: ) {} RenderKey(RenderArgs::RenderMode mode, - bool translucent, float alphaThreshold, bool hasLightmap, + bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey(((translucent && (alphaThreshold == 0.0f) && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) + RenderKey(((translucent && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) | (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly | (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0) | (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 275e8af29c..1b1d08f353 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -18,10 +18,11 @@ #include #include -#include "FramebufferCache.h" +#include "DebugDeferredBuffer.h" #include "DeferredLightingEffect.h" -#include "TextureCache.h" +#include "FramebufferCache.h" #include "HitEffect.h" +#include "TextureCache.h" #include "render/DrawStatus.h" #include "AmbientOcclusionEffect.h" @@ -34,92 +35,91 @@ using namespace render; -void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - - auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); - - batch.enableStereo(false); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - batch.setFramebuffer(primaryFbo); - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_DEPTH | - gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 1), 1.0, 0.0, true); - }); -} void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - DependencyManager::get()->prepare(renderContext->args); + DependencyManager::get()->prepare(renderContext->getArgs()); } void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - DependencyManager::get()->render(renderContext->args); + DependencyManager::get()->render(renderContext->getArgs()); } -void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - PerformanceTimer perfTimer("ResolveDeferred"); - DependencyManager::get()->copyBack(renderContext->args); +void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("ToneMappingDeferred"); + _toneMappingEffect.render(renderContext->getArgs()); } RenderDeferredTask::RenderDeferredTask() : Task() { - _jobs.push_back(Job(new SetupDeferred::JobModel("SetupFramebuffer"))); - - _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred"))); + // CPU only, create the list of renderedOpaques items _jobs.push_back(Job(new FetchItems::JobModel("FetchOpaque", - FetchItems( - [] (const RenderContextPointer& context, int count) { - context->_numFeedOpaqueItems = count; - } - ) + FetchItems([](const RenderContextPointer& context, int count) { + context->getItemsConfig().opaque.numFeed = count; + }) ))); _jobs.push_back(Job(new CullItemsOpaque::JobModel("CullOpaque", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput()))); auto& renderedOpaques = _jobs.back().getOutput(); - _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput()))); + // CPU only, create the list of renderedTransparents items + _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", + FetchItems(ItemFilter::Builder::transparentShape().withoutLayered(), + [](const RenderContextPointer& context, int count) { + context->getItemsConfig().transparent.numFeed = count; + }) + ))); + _jobs.push_back(Job(new CullItemsTransparent::JobModel("CullTransparent", _jobs.back().getOutput()))); + _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); + auto& renderedTransparents = _jobs.back().getOutput(); + + // GPU Jobs: Start preparing the deferred and lighting buffer + _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred"))); + + // Render opaque objects in DeferredBuffer + _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", renderedOpaques))); + + // Once opaque is all rendered create stencil background _jobs.push_back(Job(new DrawStencilDeferred::JobModel("DrawOpaqueStencil"))); + + // Use Stencil and start drawing background in Lighting buffer _jobs.push_back(Job(new DrawBackgroundDeferred::JobModel("DrawBackgroundDeferred"))); + // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. _jobs.push_back(Job(new DrawLight::JobModel("DrawLight"))); - _jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred"))); - _jobs.push_back(Job(new ResolveDeferred::JobModel("ResolveDeferred"))); - _jobs.push_back(Job(new AmbientOcclusion::JobModel("AmbientOcclusion"))); + // DeferredBuffer is complete, now let's shade it into the LightingBuffer + _jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred"))); + + // AO job, to be revisited + _jobs.push_back(Job(new AmbientOcclusion::JobModel("AmbientOcclusion"))); _jobs.back().setEnabled(false); _occlusionJobIndex = (int)_jobs.size() - 1; + // AA job to be revisited _jobs.push_back(Job(new Antialiasing::JobModel("Antialiasing"))); - _jobs.back().setEnabled(false); _antialiasingJobIndex = (int)_jobs.size() - 1; - _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", - FetchItems( - ItemFilter::Builder::transparentShape().withoutLayered(), - [] (const RenderContextPointer& context, int count) { - context->_numFeedTransparentItems = count; - } - ) - ))); - _jobs.push_back(Job(new CullItemsTransparent::JobModel("CullTransparent", _jobs.back().getOutput()))); - - - _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); - _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); + // Render transparent objects forward in LigthingBuffer + _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", renderedTransparents))); - // Grab a texture map representing the different status icons and assign that to the drawStatsuJob - auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); - _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); + // Lighting Buffer ready for tone mapping + _jobs.push_back(Job(new ToneMappingDeferred::JobModel("ToneMapping"))); + _toneMappingJobIndex = (int)_jobs.size() - 1; + // Debugging Deferred buffer job + _jobs.push_back(Job(new DebugDeferredBuffer::JobModel("DebugDeferredBuffer"))); _jobs.back().setEnabled(false); - _drawStatusJobIndex = (int)_jobs.size() - 1; + _drawDebugDeferredBufferIndex = (int)_jobs.size() - 1; + + // Status icon rendering job + { + // Grab a texture map representing the different status icons and assign that to the drawStatsuJob + auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); + _jobs.back().setEnabled(false); + _drawStatusJobIndex = (int)_jobs.size() - 1; + } _jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D"))); @@ -127,12 +127,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.back().setEnabled(false); _drawHitEffectJobIndex = (int)_jobs.size() -1; - - // Give ourselves 3 frmaes of timer queries - _timerQueries.push_back(std::make_shared()); - _timerQueries.push_back(std::make_shared()); - _timerQueries.push_back(std::make_shared()); - _currentTimerQueryIndex = 0; + _jobs.push_back(Job(new Blit::JobModel("Blit"))); } RenderDeferredTask::~RenderDeferredTask() { @@ -147,23 +142,29 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend // Is it possible that we render without a viewFrustum ? - if (!(renderContext->args && renderContext->args->_viewFrustum)) { + if (!(renderContext->getArgs() && renderContext->getArgs()->_viewFrustum)) { return; } - // Make sure we turn the displayItemStatus on/off - setDrawItemStatus(renderContext->_drawItemStatus); + // Make sure we turn the deferred buffer debug on/off + setDrawDebugDeferredBuffer(renderContext->_deferredDebugMode); - //Make sure we display hit effect on screen, as desired from a script - setDrawHitEffect(renderContext->_drawHitEffect); + // Make sure we turn the displayItemStatus on/off + setDrawItemStatus(renderContext->getDrawStatus()); + + // Make sure we display hit effect on screen, as desired from a script + setDrawHitEffect(renderContext->getDrawHitEffect()); // TODO: turn on/off AO through menu item - setOcclusionStatus(renderContext->_occlusionStatus); + setOcclusionStatus(renderContext->getOcclusionStatus()); - setAntialiasingStatus(renderContext->_fxaaStatus); + setAntialiasingStatus(renderContext->getFxaaStatus()); - renderContext->args->_context->syncCache(); + setToneMappingExposure(renderContext->getTone().exposure); + setToneMappingToneCurve(renderContext->getTone().toneCurve); + + renderContext->getArgs()->_context->syncCache(); for (auto job : _jobs) { job.run(sceneContext, renderContext); @@ -172,16 +173,17 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend }; void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; - renderContext->_numDrawnOpaqueItems = (int)inItems.size(); + auto& opaque = renderContext->getItemsConfig().opaque; + opaque.numDrawn = (int)inItems.size(); glm::mat4 projMat; Transform viewMat; @@ -191,26 +193,23 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - { - const float OPAQUE_ALPHA_THRESHOLD = 0.5f; - args->_alphaThreshold = OPAQUE_ALPHA_THRESHOLD; - } - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOpaqueItems); + renderItems(sceneContext, renderContext, inItems, opaque.maxDrawn); args->_batch = nullptr; }); } void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; - renderContext->_numDrawnTransparentItems = (int)inItems.size(); + auto& transparent = renderContext->getItemsConfig().transparent; + transparent.numDrawn = (int)inItems.size(); glm::mat4 projMat; Transform viewMat; @@ -219,11 +218,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; - args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD; - - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnTransparentItems); + + renderItems(sceneContext, renderContext, inItems, transparent.maxDrawn); args->_batch = nullptr; }); } @@ -246,8 +242,8 @@ const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() { } void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -262,11 +258,12 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon inItems.emplace_back(id); } } - renderContext->_numFeedOverlay3DItems = (int)inItems.size(); - renderContext->_numDrawnOverlay3DItems = (int)inItems.size(); + auto& overlay3D = renderContext->getItemsConfig().overlay3D; + overlay3D.numFeed = (int)inItems.size(); + overlay3D.numDrawn = (int)inItems.size(); if (!inItems.empty()) { - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call @@ -295,7 +292,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon batch.setPipeline(getOpaquePipeline()); batch.setResourceTexture(0, args->_whiteTexture); - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems); + renderItems(sceneContext, renderContext, inItems, renderContext->getItemsConfig().overlay3D.maxDrawn); }); args->_batch = nullptr; args->_whiteTexture.reset(); @@ -324,19 +321,19 @@ const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() { } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // from the touched pixel generate the stencil buffer - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; - auto primaryFboColorDepthStencil = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + auto deferredFboColorDepthStencil = DependencyManager::get()->getDeferredFramebufferDepthColor(); batch.enableStereo(false); - batch.setFramebuffer(primaryFboColorDepthStencil); + batch.setFramebuffer(deferredFboColorDepthStencil); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); @@ -350,8 +347,8 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren } void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -363,16 +360,15 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const for (auto id : items) { inItems.emplace_back(id); } - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; - auto primaryFboColorDepthStencil = DependencyManager::get()->getPrimaryFramebufferDepthColor(); - auto primaryFboFull = DependencyManager::get()->getPrimaryFramebuffer(); + auto lightingFBO = DependencyManager::get()->getLightingFramebuffer(); batch.enableSkybox(true); - batch.setFramebuffer(primaryFboColorDepthStencil); + batch.setFramebuffer(lightingFBO); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); @@ -387,8 +383,106 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const renderItems(sceneContext, renderContext, inItems); - batch.setFramebuffer(primaryFboFull); - }); args->_batch = nullptr; } + +void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_context); + + RenderArgs* renderArgs = renderContext->getArgs(); + auto blitFbo = renderArgs->_blitFramebuffer; + + if (!blitFbo) { + return; + } + + // Determine size from viewport + int width = renderArgs->_viewport.z; + int height = renderArgs->_viewport.w; + + // Blit primary to blit FBO + auto framebufferCache = DependencyManager::get(); + auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); + + gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { + batch.setFramebuffer(blitFbo); + + if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + if (renderArgs->_context->isStereo()) { + gpu::Vec4i srcRectLeft; + srcRectLeft.z = width / 2; + srcRectLeft.w = height; + + gpu::Vec4i srcRectRight; + srcRectRight.x = width / 2; + srcRectRight.z = width; + srcRectRight.w = height; + + gpu::Vec4i destRectLeft; + destRectLeft.x = srcRectLeft.z; + destRectLeft.z = srcRectLeft.x; + destRectLeft.y = srcRectLeft.y; + destRectLeft.w = srcRectLeft.w; + + gpu::Vec4i destRectRight; + destRectRight.x = srcRectRight.z; + destRectRight.z = srcRectRight.x; + destRectRight.y = srcRectRight.y; + destRectRight.w = srcRectRight.w; + + // Blit left to right and right to left in stereo + batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); + batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); + } else { + gpu::Vec4i srcRect; + srcRect.z = width; + srcRect.w = height; + + gpu::Vec4i destRect; + destRect.x = width; + destRect.y = 0; + destRect.z = 0; + destRect.w = height; + + batch.blit(primaryFbo, srcRect, blitFbo, destRect); + } + } else { + gpu::Vec4i rect; + rect.z = width; + rect.w = height; + + batch.blit(primaryFbo, rect, blitFbo, rect); + } + }); +} + +void RenderDeferredTask::setToneMappingExposure(float exposure) { + if (_toneMappingJobIndex >= 0) { + _jobs[_toneMappingJobIndex].edit()._toneMappingEffect.setExposure(exposure); + } +} + +float RenderDeferredTask::getToneMappingExposure() const { + if (_toneMappingJobIndex >= 0) { + return _jobs[_toneMappingJobIndex].get()._toneMappingEffect.getExposure(); + } else { + return 0.0f; + } +} + +void RenderDeferredTask::setToneMappingToneCurve(int toneCurve) { + if (_toneMappingJobIndex >= 0) { + _jobs[_toneMappingJobIndex].edit()._toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)toneCurve); + } +} + +int RenderDeferredTask::getToneMappingToneCurve() const { + if (_toneMappingJobIndex >= 0) { + return _jobs[_toneMappingJobIndex].get()._toneMappingEffect.getToneCurve(); + } else { + return 0.0f; + } +} + diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 6daa90b1ed..051faa3238 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -16,6 +16,8 @@ #include "gpu/Pipeline.h" +#include "ToneMappingEffect.h" + class SetupDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); @@ -38,11 +40,13 @@ public: typedef render::Job::Model JobModel; }; -class ResolveDeferred { +class ToneMappingDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + ToneMappingEffect _toneMappingEffect; + + typedef render::Job::Model JobModel; }; class DrawOpaqueDeferred { @@ -80,10 +84,17 @@ class DrawOverlay3D { static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable public: static const gpu::PipelinePointer& getOpaquePipeline(); + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + + typedef render::Job::Model JobModel; +}; +class Blit { +public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + typedef render::Job::Model JobModel; }; class RenderDeferredTask : public render::Task { @@ -93,16 +104,23 @@ public: ~RenderDeferredTask(); render::Jobs _jobs; - + + int _drawDebugDeferredBufferIndex = -1; int _drawStatusJobIndex = -1; int _drawHitEffectJobIndex = -1; - + + void setDrawDebugDeferredBuffer(int draw) { + if (_drawDebugDeferredBufferIndex >= 0) { + _jobs[_drawDebugDeferredBufferIndex].setEnabled(draw >= 0); + } + } + bool doDrawDebugDeferredBuffer() const { if (_drawDebugDeferredBufferIndex >= 0) { return _jobs[_drawDebugDeferredBufferIndex].isEnabled(); } else { return false; } } + void setDrawItemStatus(int draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw > 0); } } - bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } } void setDrawHitEffect(bool draw) { if (_drawHitEffectJobIndex >= 0) { _jobs[_drawHitEffectJobIndex].setEnabled(draw); } } @@ -118,6 +136,14 @@ public: void setAntialiasingStatus(bool draw) { if (_antialiasingJobIndex >= 0) { _jobs[_antialiasingJobIndex].setEnabled(draw); } } bool doAntialiasingStatus() const { if (_antialiasingJobIndex >= 0) { return _jobs[_antialiasingJobIndex].isEnabled(); } else { return false; } } + int _toneMappingJobIndex = -1; + + void setToneMappingExposure(float exposure); + float getToneMappingExposure() const; + + void setToneMappingToneCurve(int toneCurve); + int getToneMappingToneCurve() const; + virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); diff --git a/libraries/render-utils/src/RenderScriptingInterface.cpp b/libraries/render-utils/src/RenderScriptingInterface.cpp new file mode 100644 index 0000000000..a99dc814d5 --- /dev/null +++ b/libraries/render-utils/src/RenderScriptingInterface.cpp @@ -0,0 +1,52 @@ +// +// RenderScriptingInterface.cpp +// libraries/render-utils +// +// Created by Zach Pomerantz on 12/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderScriptingInterface.h" + +RenderScriptingInterface::RenderScriptingInterface() {}; + +void RenderScripting::Tone::setCurve(const QString& curve) { + if (curve == QString("None")) { + toneCurve = 0; + } else if (curve == QString("Gamma22")) { + toneCurve = 1; + } else if (curve == QString("Reinhard")) { + toneCurve = 2; + } else if (curve == QString("Filmic")) { + toneCurve = 3; + } +} + +QString RenderScripting::Tone::getCurve() const { + switch (toneCurve) { + case 0: + return QString("None"); + case 1: + return QString("Gamma22"); + case 2: + return QString("Reinhard"); + case 3: + return QString("Filmic"); + default: + return QString("Filmic"); + }; +} + +render::RenderContext RenderScriptingInterface::getRenderContext() { + render::RenderContext::ItemsConfig items{ *_opaque, *_transparent, *_overlay3D }; + return render::RenderContext{ items, *_tone, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode }; +} + +void RenderScriptingInterface::setItemCounts(const render::RenderContext::ItemsConfig& items) { + _opaque->setCounts(items.opaque); + _transparent->setCounts(items.transparent); + _overlay3D->setCounts(items.overlay3D); +} \ No newline at end of file diff --git a/libraries/render-utils/src/RenderScriptingInterface.h b/libraries/render-utils/src/RenderScriptingInterface.h new file mode 100644 index 0000000000..08226ef6df --- /dev/null +++ b/libraries/render-utils/src/RenderScriptingInterface.h @@ -0,0 +1,115 @@ +// +// RenderScriptingInterface.h +// libraries/render-utils +// +// Created by Zach Pomerantz on 12/16/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderScriptingInterface_h +#define hifi_RenderScriptingInterface_h + +#include // QObject +#include // Dependency + +#include "render/Engine.h" + +namespace RenderScripting { + using State = render::RenderContext::ItemsConfig::State; + using Counter = render::RenderContext::ItemsConfig::Counter; + + class ItemCounter : public QObject, public Counter { + Q_OBJECT + + public: + Q_PROPERTY(int numFeed READ getNumFeed) + Q_PROPERTY(int numDrawn READ getNumDrawn) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn) + + protected: + int getNumFeed() const { return numFeed; } + int getNumDrawn() const { return numDrawn; } + }; + using ItemCounterPointer = std::unique_ptr; + + class ItemState : public QObject, public State { + Q_OBJECT + + public: + Q_PROPERTY(bool render MEMBER render) + Q_PROPERTY(bool cull MEMBER cull) + Q_PROPERTY(bool sort MEMBER sort) + + Q_PROPERTY(int numFeed READ getNumFeed) + Q_PROPERTY(int numDrawn READ getNumDrawn) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn) + + protected: + int getNumFeed() const { return numFeed; } + int getNumDrawn() const { return numDrawn; } + }; + using ItemStatePointer = std::unique_ptr; + + class Tone : public QObject, public render::RenderContext::Tone { + Q_OBJECT + + public: + Q_PROPERTY(float exposure MEMBER exposure) + Q_PROPERTY(QString curve READ getCurve WRITE setCurve) + + QString getCurve() const; + int getCurveValue() const { return toneCurve; } + void setCurve(const QString& curve); + }; + using TonePointer = std::unique_ptr; +}; + +class RenderScriptingInterface : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + + public: + Q_PROPERTY(RenderScripting::ItemState* opaque READ getOpaque) + Q_PROPERTY(RenderScripting::ItemState* transparent READ getTransparent) + Q_PROPERTY(RenderScripting::ItemCounter* overlay3D READ getOverlay3D) + + Q_PROPERTY(RenderScripting::Tone* tone READ getTone) + + Q_PROPERTY(int displayItemStatus MEMBER _drawStatus) + Q_PROPERTY(bool displayHitEffect MEMBER _drawHitEffect) + + Q_PROPERTY(int deferredDebugMode MEMBER _deferredDebugMode) + Q_PROPERTY(glm::vec4 deferredDebugSize MEMBER _deferredDebugSize) + + render::RenderContext getRenderContext(); + void setItemCounts(const render::RenderContext::ItemsConfig& items); + +protected: + RenderScriptingInterface(); + ~RenderScriptingInterface() {}; + + RenderScripting::ItemState* getOpaque() const { return _opaque.get(); } + RenderScripting::ItemState* getTransparent() const { return _transparent.get(); } + RenderScripting::ItemCounter* getOverlay3D() const { return _overlay3D.get(); } + + RenderScripting::Tone* getTone() const { return _tone.get(); } + + RenderScripting::ItemStatePointer _opaque = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; + RenderScripting::ItemStatePointer _transparent = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; + RenderScripting::ItemCounterPointer _overlay3D = RenderScripting::ItemCounterPointer{new RenderScripting::ItemCounter{}}; + + RenderScripting::TonePointer _tone = RenderScripting::TonePointer{ new RenderScripting::Tone{} }; + + // Options + int _drawStatus = 0; + bool _drawHitEffect = false; + + // Debugging + int _deferredDebugMode = -1; + glm::vec4 _deferredDebugSize { 0.0f, -1.0f, 1.0f, 1.0f }; +}; + +#endif // hifi_RenderScriptingInterface_h diff --git a/libraries/render-utils/src/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf index c2a3635f07..10b39dc210 100755 --- a/libraries/render-utils/src/SkyFromAtmosphere.slf +++ b/libraries/render-utils/src/SkyFromAtmosphere.slf @@ -108,5 +108,6 @@ void main (void) vec3 finalColor = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; outFragColor.a = finalColor.b; - outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + // outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + outFragColor.rgb = finalColor.rgb; } diff --git a/libraries/render-utils/src/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf index e3569a2914..42282fe08b 100755 --- a/libraries/render-utils/src/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -114,5 +114,6 @@ void main (void) vec3 finalColor = color + fMiePhase * secondaryColor; outFragColor.a = finalColor.b; - outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + // outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + outFragColor.rgb = finalColor.rgb; } diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp new file mode 100644 index 0000000000..2583719424 --- /dev/null +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -0,0 +1,145 @@ +// +// ToneMappingEffect.cpp +// libraries/render-utils/src +// +// Created by Sam Gateau on 12/7/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ToneMappingEffect.h" + +#include +#include + +#include + +#include "FramebufferCache.h" + + +ToneMappingEffect::ToneMappingEffect() { + Parameters parameters; + _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); +} + +void ToneMappingEffect::init() { + const char BlitTextureGamma_frag[] = R"SCRIBE(#version 410 core + // Generated on Sat Oct 24 09:34:37 2015 + // + // Draw texture 0 fetched at texcoord.xy + // + // Created by Sam Gateau on 6/22/2015 + // Copyright 2015 High Fidelity, Inc. + // + // Distributed under the Apache License, Version 2.0. + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + // + + struct ToneMappingParams { + vec4 _exp_2powExp_s0_s1; + ivec4 _toneCurve_s0_s1_s2; + }; + + const float INV_GAMMA_22 = 1.0 / 2.2; + const int ToneCurveNone = 0; + const int ToneCurveGamma22 = 1; + const int ToneCurveReinhard = 2; + const int ToneCurveFilmic = 3; + + uniform toneMappingParamsBuffer { + ToneMappingParams params; + }; + float getTwoPowExposure() { + return params._exp_2powExp_s0_s1.y; + } + int getToneCurve() { + return params._toneCurve_s0_s1_s2.x; + } + + uniform sampler2D colorMap; + + in vec2 varTexCoord0; + out vec4 outFragColor; + + void main(void) { + vec4 fragColorRaw = texture(colorMap, varTexCoord0); + vec3 fragColor = fragColorRaw.xyz; + + vec3 srcColor = fragColor * getTwoPowExposure(); + + int toneCurve = getToneCurve(); + vec3 tonedColor = srcColor; + if (toneCurve == ToneCurveFilmic) { + vec3 x = max(vec3(0.0), srcColor-0.004); + tonedColor = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); + } else if (toneCurve == ToneCurveReinhard) { + tonedColor = srcColor/(1.0 + srcColor); + tonedColor = pow(tonedColor, vec3(INV_GAMMA_22)); + } else if (toneCurve == ToneCurveGamma22) { + tonedColor = pow(srcColor, vec3(INV_GAMMA_22)); + } // else None toned = src + + outFragColor = vec4(tonedColor, 1.0); + } + + )SCRIBE"; + auto blitPS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(BlitTextureGamma_frag))); + + auto blitVS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(blitVS, blitPS)); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("toneMappingParamsBuffer"), 3)); + gpu::Shader::makeProgram(*blitProgram, slotBindings); + auto blitState = std::make_shared(); + blitState->setColorWriteMask(true, true, true, true); + _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState)); +} + +void ToneMappingEffect::setExposure(float exposure) { + _parametersBuffer.edit()._exposure = exposure; + _parametersBuffer.edit()._twoPowExposure = pow(2.0, exposure); +} + +void ToneMappingEffect::setToneCurve(ToneCurve curve) { + _parametersBuffer.edit()._toneCurve = curve; +} + +void ToneMappingEffect::render(RenderArgs* args) { + if (!_blitLightBuffer) { + init(); + } + auto framebufferCache = DependencyManager::get(); + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + batch.enableStereo(false); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + + auto lightingBuffer = framebufferCache->getLightingTexture(); + auto destFbo = framebufferCache->getPrimaryFramebuffer(); + batch.setFramebuffer(destFbo); + + // FIXME: Generate the Luminosity map + //batch.generateTextureMips(lightingBuffer); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.setViewTransform(Transform()); + { + float sMin = args->_viewport.x / (float)framebufferSize.width(); + float sWidth = args->_viewport.z / (float)framebufferSize.width(); + float tMin = args->_viewport.y / (float)framebufferSize.height(); + float tHeight = args->_viewport.w / (float)framebufferSize.height(); + Transform model; + batch.setPipeline(_blitLightBuffer); + model.setTranslation(glm::vec3(sMin, tMin, 0.0)); + model.setScale(glm::vec3(sWidth, tHeight, 1.0)); + batch.setModelTransform(model); + } + + batch.setUniformBuffer(3, _parametersBuffer); + batch.setResourceTexture(0, lightingBuffer); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} \ No newline at end of file diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h new file mode 100644 index 0000000000..20ee9024cf --- /dev/null +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -0,0 +1,64 @@ +// +// ToneMappingEffect.h +// libraries/render-utils/src +// +// Created by Sam Gateau on 12/7/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ToneMappingEffect_h +#define hifi_ToneMappingEffect_h + +#include +#include + +#include +#include + +class RenderArgs; + +class ToneMappingEffect { +public: + ToneMappingEffect(); + virtual ~ToneMappingEffect() {} + + void render(RenderArgs* args); + + void setExposure(float exposure); + float getExposure() const { return _parametersBuffer.get()._exposure; } + + // Different tone curve available + enum ToneCurve { + None = 0, + Gamma22, + Reinhard, + Filmic, + }; + void setToneCurve(ToneCurve curve); + ToneCurve getToneCurve() const { return (ToneCurve)_parametersBuffer.get()._toneCurve; } + +private: + + gpu::PipelinePointer _blitLightBuffer; + + // Class describing the uniform buffer with all the parameters common to the tone mapping shaders + class Parameters { + public: + float _exposure = 0.0f; + float _twoPowExposure = 1.0f; + glm::vec2 spareA; + int _toneCurve = Filmic; + glm::vec3 spareB; + + Parameters() {} + }; + typedef gpu::BufferView UniformBufferView; + gpu::BufferView _parametersBuffer; + + void init(); +}; + +#endif // hifi_ToneMappingEffect_h diff --git a/libraries/render-utils/src/animdebugdraw.slv b/libraries/render-utils/src/animdebugdraw.slv index f3117714b0..3cb356c055 100644 --- a/libraries/render-utils/src/animdebugdraw.slv +++ b/libraries/render-utils/src/animdebugdraw.slv @@ -9,16 +9,15 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec4 _color; void main(void) { // pass along the diffuse color - _color = inColor.rgba; + _color = colorToLinearRGBA(inColor.rgba); TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf new file mode 100644 index 0000000000..375972cdc3 --- /dev/null +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// debug_deferred_buffer.slf +// fragment shader +// +// Created by Clement on 12/3 +// 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 DeferredBuffer.slh@> + +in vec2 uv; +out vec4 outFragColor; + +//SOURCE_PLACEHOLDER + +void main(void) { + outFragColor = getFragmentColor(); +} \ No newline at end of file diff --git a/libraries/render-utils/src/debug_deferred_buffer.slv b/libraries/render-utils/src/debug_deferred_buffer.slv new file mode 100644 index 0000000000..85ff2b651d --- /dev/null +++ b/libraries/render-utils/src/debug_deferred_buffer.slv @@ -0,0 +1,22 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// debug_deferred_buffer.slv +// vertex shader +// +// Created by Clement on 12/3 +// 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 gpu/Inputs.slh@> + +out vec2 uv; + +void main(void) { + uv = (inPosition.xy + 1.0) * 0.5; + gl_Position = inPosition; +} \ No newline at end of file diff --git a/libraries/render-utils/src/directional_ambient_light.slf b/libraries/render-utils/src/directional_ambient_light.slf index 52ecc71a14..ae3b05862e 100755 --- a/libraries/render-utils/src/directional_ambient_light.slf +++ b/libraries/render-utils/src/directional_ambient_light.slf @@ -27,7 +27,6 @@ void main(void) { DeferredTransform deferredTransform = getDeferredTransform(); DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - // Light mapped or not ? if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { vec3 color = evalLightmappedColor( deferredTransform.viewInverse, diff --git a/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf deleted file mode 100755 index 8b0212636e..0000000000 --- a/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf +++ /dev/null @@ -1,57 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientSphereGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienSphereGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_shadow_map.slf deleted file mode 100755 index 97d69f2e63..0000000000 --- a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf +++ /dev/null @@ -1,56 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientSphereGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienSphereGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf deleted file mode 100644 index 4abe8e2e9d..0000000000 --- a/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf +++ /dev/null @@ -1,59 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_light_shadow_map.slf b/libraries/render-utils/src/directional_light_shadow_map.slf deleted file mode 100644 index 4249b2787c..0000000000 --- a/libraries/render-utils/src/directional_light_shadow_map.slf +++ /dev/null @@ -1,58 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf deleted file mode 100755 index 3c09bf62b6..0000000000 --- a/libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf +++ /dev/null @@ -1,59 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Sam Gateau on 5/8/2015. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalSkyboxGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalSkyboxGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_skybox_light_shadow_map.slf b/libraries/render-utils/src/directional_skybox_light_shadow_map.slf deleted file mode 100755 index 6f709f31fa..0000000000 --- a/libraries/render-utils/src/directional_skybox_light_shadow_map.slf +++ /dev/null @@ -1,58 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Sam Gateau on 5/8/2015. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalSkyboxGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalSkyboxGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/hit_effect.slf b/libraries/render-utils/src/hit_effect.slf index c059488ba1..cc4484442f 100644 --- a/libraries/render-utils/src/hit_effect.slf +++ b/libraries/render-utils/src/hit_effect.slf @@ -20,8 +20,8 @@ in vec2 varQuadPosition; out vec4 outFragColor; void main(void) { - vec2 center = vec2(0.0, 0.0); - float distFromCenter = distance( vec2(0.0, 0.0), varQuadPosition); - float alpha = mix(0.0, 0.5, pow(distFromCenter,5.)); - outFragColor = vec4(1.0, 0.0, 0.0, alpha); + vec2 center = vec2(0.0, 0.0); + float distFromCenter = distance( vec2(0.0, 0.0), varQuadPosition); + float alpha = mix(0.0, 0.5, pow(distFromCenter,5.)); + outFragColor = vec4(1.0, 0.0, 0.0, alpha); } \ No newline at end of file diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index 6d56833de1..47de20a5d9 100755 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -12,9 +12,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -27,9 +26,8 @@ out vec3 _color; out vec2 _texCoord0; void main(void) { - - // pass along the diffuse color - _color = inColor.xyz; + // pass along the diffuse color in linear space + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/model_lightmap.slv b/libraries/render-utils/src/model_lightmap.slv index 7742896228..eacc91245c 100755 --- a/libraries/render-utils/src/model_lightmap.slv +++ b/libraries/render-utils/src/model_lightmap.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -29,7 +28,8 @@ out vec3 _normal; out vec3 _color; void main(void) { - _color = inColor.xyz; + // pass along the diffuse color in linear space + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slv b/libraries/render-utils/src/model_lightmap_normal_map.slv index f63030301f..6046a67e10 100755 --- a/libraries/render-utils/src/model_lightmap_normal_map.slv +++ b/libraries/render-utils/src/model_lightmap_normal_map.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -30,7 +29,8 @@ out vec3 _tangent; out vec3 _color; void main(void) { - _color = inColor.xyz; + // pass along the diffuse color in linear space + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/model_normal_map.slv b/libraries/render-utils/src/model_normal_map.slv index e989c1c294..5ed4e37278 100755 --- a/libraries/render-utils/src/model_normal_map.slv +++ b/libraries/render-utils/src/model_normal_map.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -30,7 +29,7 @@ out vec3 _color; void main(void) { // pass along the diffuse color - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.xy, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv index a57f02d854..74416f0c1f 100644 --- a/libraries/render-utils/src/overlay3D.slv +++ b/libraries/render-utils/src/overlay3D.slv @@ -11,9 +11,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec2 varTexcoord; @@ -30,7 +29,7 @@ void main(void) { varTexcoord = inTexCoord0.xy; // pass along the color - varColor = inColor; + varColor = colorToLinearRGBA(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/point_light.slf b/libraries/render-utils/src/point_light.slf index e9045e18c5..716a39aee9 100644 --- a/libraries/render-utils/src/point_light.slf +++ b/libraries/render-utils/src/point_light.slf @@ -66,7 +66,7 @@ void main(void) { float radialAttenuation = evalLightAttenuation(light, fragLightDistance); // Final Lighting color - vec3 fragColor = shading.w * (frag.diffuse + shading.xyz); + vec3 fragColor = (shading.w * frag.diffuse + shading.xyz); _fragColor = vec4(fragColor * radialAttenuation * getLightColor(light) * getLightIntensity(light), 0.0); if (getLightShowContour(light) > 0.0) { diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 576acf9340..b24e4f92ff 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -40,6 +40,8 @@ void main(void) { #ifdef PROCEDURAL_V1 specular = getProceduralColor().rgb; + // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline + specular = pow(specular, vec3(2.2)); emissiveAmount = 1.0; #else emissiveAmount = getProceduralColors(diffuse, specular, shininess); @@ -49,9 +51,9 @@ void main(void) { if (emissiveAmount > 0.0) { packDeferredFragmentLightmap( - normal, glowIntensity, diffuse, specular, shininess, specular); + normal, 1.0, diffuse, specular, shininess, specular); } else { packDeferredFragment( - normal, glowIntensity, diffuse, specular, shininess); + normal, 1.0, diffuse, specular, shininess); } } diff --git a/libraries/render-utils/src/simple.slv b/libraries/render-utils/src/simple.slv index 823654ec27..59b16cf0e5 100644 --- a/libraries/render-utils/src/simple.slv +++ b/libraries/render-utils/src/simple.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> uniform bool Instanced = false; @@ -28,7 +27,7 @@ out vec2 _texCoord0; out vec4 _position; void main(void) { - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.rgb); _texCoord0 = inTexCoord0.st; _position = inPosition; _modelNormal = inNormal.xyz; diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index d567b043f4..727c029bbe 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -29,7 +29,7 @@ void main(void) { packDeferredFragment( normalize(_normal.xyz), - glowIntensity * texel.a, + texel.a, _color.rgb * texel.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple_textured_emisive.slf b/libraries/render-utils/src/simple_textured_emisive.slf index 96119e98f0..1dd3d667a6 100644 --- a/libraries/render-utils/src/simple_textured_emisive.slf +++ b/libraries/render-utils/src/simple_textured_emisive.slf @@ -27,7 +27,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(_normal), - glowIntensity * texel.a, + texel.a, _color.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS, texel.rgb); diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index cba419dd4d..8ab475e1fd 100755 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> <@include Skinning.slh@> @@ -34,7 +33,7 @@ void main(void) { skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the diffuse color - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.rgb); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index 5dfe5ba957..cec93a024e 100755 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> <@include Skinning.slh@> @@ -36,7 +35,7 @@ void main(void) { skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the diffuse color - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.rgb); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/spot_light.slf b/libraries/render-utils/src/spot_light.slf index 73b081260e..d7a20fc5e5 100644 --- a/libraries/render-utils/src/spot_light.slf +++ b/libraries/render-utils/src/spot_light.slf @@ -73,7 +73,7 @@ void main(void) { float angularAttenuation = evalLightSpotAttenuation(light, cosSpotAngle); // Final Lighting color - vec3 fragColor = shading.w * (frag.diffuse + shading.xyz); + vec3 fragColor = (shading.w * frag.diffuse + shading.xyz); _fragColor = vec4(fragColor * angularAttenuation * radialAttenuation * getLightColor(light) * getLightIntensity(light), 0.0); if (getLightShowContour(light) > 0.0) { diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv index 340dfd9c8e..f7e72b6997 100644 --- a/libraries/render-utils/src/standardTransformPNTC.slv +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec3 varPosition; @@ -25,7 +24,7 @@ out vec4 varColor; void main(void) { varTexCoord0 = inTexCoord0.st; - varColor = inColor; + varColor = colorToLinearRGBA(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/stars.slv b/libraries/render-utils/src/stars.slv index b9ad736d5e..d00bb8b7dd 100644 --- a/libraries/render-utils/src/stars.slv +++ b/libraries/render-utils/src/stars.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> // TODO we need to get the viewport resolution and FOV passed to us so we can modify the point size @@ -26,7 +25,7 @@ out vec4 varColor; out float varSize; void main(void) { - varColor = inColor.rgba; + varColor = colorToLinearRGBA(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 5587185ead..43202c29c2 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -198,11 +198,11 @@ void Font::read(QIODevice& in) { image = image.convertToFormat(QImage::Format_RGBA8888); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); } _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 3f37ce378b..fa177bd0d4 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -97,9 +97,9 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const { void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); auto& scene = sceneContext->_scene; const int NUM_STATUS_VEC4_PER_ITEM = 2; const int VEC4_LENGTH = 4; @@ -179,7 +179,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const unsigned int VEC3_ADRESS_OFFSET = 3; - if ((renderContext->_drawItemStatus & showDisplayStatusFlag) > 0) { + if ((renderContext->getDrawStatus() & showDisplayStatusFlag) > 0) { for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); @@ -192,7 +192,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, batch.setPipeline(getDrawItemStatusPipeline()); - if ((renderContext->_drawItemStatus & showNetworkStatusFlag) > 0) { + if ((renderContext->getDrawStatus() & showNetworkStatusFlag) > 0) { for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index c0e50037e0..44ba57143f 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -37,7 +37,7 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon // Is it possible that we render without a viewFrustum ? - if (!(renderContext->args && renderContext->args->_viewFrustum)) { + if (!(renderContext->getArgs() && renderContext->getArgs()->_viewFrustum)) { return; } @@ -54,11 +54,11 @@ Job::~Job() { void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; - auto renderDetails = renderContext->args->_details._item; + RenderArgs* args = renderContext->getArgs(); + auto renderDetails = renderContext->getArgs()->_details._item; renderDetails->_considered += inItems.size(); @@ -115,7 +115,7 @@ void CullItems::run(const SceneContextPointer& sceneContext, const RenderContext outItems.clear(); outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OTHER_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -124,7 +124,7 @@ void CullItemsOpaque::run(const SceneContextPointer& sceneContext, const RenderC outItems.clear(); outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OPAQUE_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -133,7 +133,7 @@ void CullItemsTransparent::run(const SceneContextPointer& sceneContext, const Re outItems.clear(); outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::TRANSLUCENT_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -163,11 +163,11 @@ struct BackToFrontSort { }; void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); // Allocate and simply copy @@ -211,7 +211,7 @@ void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderCo void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems) { auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); // render if ((maxDrawnItems < 0) || (maxDrawnItems > (int) inItems.size())) { for (auto itemDetails : inItems) { @@ -236,8 +236,8 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render lights auto& scene = sceneContext->_scene; @@ -253,7 +253,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext ItemIDsBounds culledItems; culledItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OTHER_ITEM); cullItems(sceneContext, renderContext, inItems, culledItems); @@ -265,8 +265,8 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext } void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -278,7 +278,7 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo for (auto id : items) { inItems.emplace_back(id); } - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; batch.enableSkybox(true); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 3f628c3a02..ab102e32a7 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -84,6 +84,17 @@ public: const Varying getInput() const { return _concept->getInput(); } const Varying getOutput() const { return _concept->getOutput(); } + template T& edit() { + auto theConcept = std::dynamic_pointer_cast(_concept); + assert(theConcept); + return theConcept->_data; + } + template const T& get() const { + auto theConcept = std::dynamic_pointer_cast(_concept); + assert(theConcept); + return theConcept->_data; + } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { PerformanceTimer perfTimer(getName().c_str()); PROFILE_RANGE(getName().c_str()); diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 86f35eb831..907c836347 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -13,6 +13,20 @@ #include "DrawTask.h" using namespace render; +RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) + : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, + _args{ nullptr }, + _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, + _items{ items }, _tone{ tone } {} + +void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) { + _occlusionStatus = occlusion; + _fxaaStatus = fxaa; + + if (showOwned) { + _drawStatus |= render::showNetworkStatusFlag; + } +}; Engine::Engine() : _sceneContext(std::make_shared()), diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 7c11246cff..4192dd3ed9 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -23,7 +23,7 @@ public: SceneContext() {} }; -typedef std::shared_ptr SceneContextPointer; +using SceneContextPointer = std::shared_ptr; // see examples/utilities/tools/renderEngineDebug.js const int showDisplayStatusFlag = 1; @@ -32,38 +32,83 @@ const int showNetworkStatusFlag = 2; class RenderContext { public: - RenderArgs* args; + class ItemsConfig { + public: + class Counter { + public: + Counter() {}; + Counter(const Counter& counter) { + numFeed = numDrawn = 0; + maxDrawn = counter.maxDrawn; + }; - bool _cullOpaque = true; - bool _sortOpaque = true; - bool _renderOpaque = true; - bool _cullTransparent = true; - bool _sortTransparent = true; - bool _renderTransparent = true; + void setCounts(const Counter& counter) { + numFeed = counter.numFeed; + numDrawn = counter.numDrawn; + }; - int _numFeedOpaqueItems = 0; - int _numDrawnOpaqueItems = 0; - int _maxDrawnOpaqueItems = -1; + int numFeed = 0; + int numDrawn = 0; + int maxDrawn = -1; + }; + + class State : public Counter { + public: + bool render = true; + bool cull = true; + bool sort = true; + + Counter counter{}; + }; + + ItemsConfig(State opaqueState, State transparentState, Counter overlay3DCounter) + : opaque{ opaqueState }, transparent{ transparentState }, overlay3D{ overlay3DCounter } {} + ItemsConfig() : ItemsConfig{ {}, {}, {} } {} + + // TODO: If member count increases, store counters in a map instead of multiple members + State opaque{}; + State transparent{}; + Counter overlay3D{}; + }; + + class Tone { + public: + int toneCurve = 1; // Means just Gamma 2.2 correction + float exposure = 0.0; + }; - int _numFeedTransparentItems = 0; - int _numDrawnTransparentItems = 0; - int _maxDrawnTransparentItems = -1; + RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); + RenderContext() : RenderContext({}, {}, {}, {}, {}, {}) {}; - int _numFeedOverlay3DItems = 0; - int _numDrawnOverlay3DItems = 0; - int _maxDrawnOverlay3DItems = -1; + void setArgs(RenderArgs* args) { _args = args; } + inline RenderArgs* getArgs() { return _args; } + inline ItemsConfig& getItemsConfig() { return _items; } + inline Tone& getTone() { return _tone; } + inline int getDrawStatus() { return _drawStatus; } + inline bool getDrawHitEffect() { return _drawHitEffect; } + inline bool getOcclusionStatus() { return _occlusionStatus; } + inline bool getFxaaStatus() { return _fxaaStatus; } + void setOptions(bool occlusion, bool fxaa, bool showOwned); - int _drawItemStatus = 0; - bool _drawHitEffect = false; + // Debugging + int _deferredDebugMode; + glm::vec4 _deferredDebugSize; +protected: + RenderArgs* _args; + + // Options + int _drawStatus; // bitflag + bool _drawHitEffect; bool _occlusionStatus = false; bool _fxaaStatus = false; - RenderContext() {} + ItemsConfig _items; + Tone _tone; }; typedef std::shared_ptr RenderContextPointer; -// THe base class for a task that runs on the SceneContext +// The base class for a task that runs on the SceneContext class Task { public: Task() {} @@ -76,7 +121,7 @@ protected: typedef std::shared_ptr TaskPointer; typedef std::vector Tasks; -// The root of the takss, the Engine, should not be known from the Tasks, +// The root of the tasks, the Engine, should not be known from the Tasks, // The SceneContext is what navigates from the engine down to the Tasks class Engine { public: diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index bb74b20be0..f6b1726770 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -22,6 +22,10 @@ quat Quat::normalize(const glm::quat& q) { return glm::normalize(q); } +quat Quat::conjugate(const glm::quat& q) { + return glm::conjugate(q); +} + glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return ::rotationBetween(v1, v2); } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 543c93401f..bc7f11ab01 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -26,6 +26,7 @@ class Quat : public QObject { public slots: glm::quat multiply(const glm::quat& q1, const glm::quat& q2); glm::quat normalize(const glm::quat& q); + glm::quat conjugate(const glm::quat& q); glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp index 59d2765659..062e502d6e 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.cpp +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -1,6 +1,6 @@ // // SceneScriptingInterface.cpp -// interface/src/scripting +// libraries/script-engine // // Created by Sam Gateau on 2/24/15. // Copyright 2014 High Fidelity, Inc. @@ -11,90 +11,97 @@ #include "SceneScriptingInterface.h" -#include - - #include -SceneScriptingInterface::SceneScriptingInterface() { - // Let's make sure the sunSkyStage is using a proceduralSKybox - _skyStage->setSkybox(model::SkyboxPointer(new ProceduralSkybox())); -} - -void SceneScriptingInterface::setStageOrientation(const glm::quat& orientation) { - _skyStage->setOriginOrientation(orientation); -} -void SceneScriptingInterface::setStageLocation(float longitude, float latitude, float altitude) { - _skyStage->setOriginLocation(longitude, latitude, altitude); -} - -float SceneScriptingInterface::getStageLocationLongitude() const { +float SceneScripting::Location::getLongitude() const { return _skyStage->getOriginLongitude(); } -float SceneScriptingInterface::getStageLocationLatitude() const { + +float SceneScripting::Location::getLatitude() const { return _skyStage->getOriginLatitude(); } -float SceneScriptingInterface::getStageLocationAltitude() const { + +float SceneScripting::Location::getAltitude() const { return _skyStage->getOriginSurfaceAltitude(); } -void SceneScriptingInterface::setStageDayTime(float hour) { +void SceneScripting::Location::setLongitude(float longitude) { + _skyStage->setOriginLongitude(longitude); +} + +void SceneScripting::Location::setLatitude(float latitude) { + _skyStage->setOriginLatitude(latitude); +} + +void SceneScripting::Location::setAltitude(float altitude) { + _skyStage->setOriginSurfaceAltitude(altitude); +} + +void SceneScripting::Time::setHour(float hour) { _skyStage->setDayTime(hour); } -float SceneScriptingInterface::getStageDayTime() const { +float SceneScripting::Time::getHour() const { return _skyStage->getDayTime(); } -void SceneScriptingInterface::setStageYearTime(int day) { +void SceneScripting::Time::setDay(int day) { _skyStage->setYearTime(day); } -int SceneScriptingInterface::getStageYearTime() const { +int SceneScripting::Time::getDay() const { return _skyStage->getYearTime(); } -void SceneScriptingInterface::setKeyLightColor(const glm::vec3& color) { - _skyStage->setSunColor(color); -} - -glm::vec3 SceneScriptingInterface::getKeyLightColor() const { +glm::vec3 SceneScripting::KeyLight::getColor() const { return _skyStage->getSunColor(); } -void SceneScriptingInterface::setKeyLightIntensity(float intensity) { - _skyStage->setSunIntensity(intensity); +void SceneScripting::KeyLight::setColor(const glm::vec3& color) { + _skyStage->setSunColor(color); } -float SceneScriptingInterface::getKeyLightIntensity() const { +float SceneScripting::KeyLight::getIntensity() const { return _skyStage->getSunIntensity(); } -void SceneScriptingInterface::setKeyLightAmbientIntensity(float intensity) { - _skyStage->setSunAmbientIntensity(intensity); +void SceneScripting::KeyLight::setIntensity(float intensity) { + _skyStage->setSunIntensity(intensity); } -float SceneScriptingInterface::getKeyLightAmbientIntensity() const { +float SceneScripting::KeyLight::getAmbientIntensity() const { return _skyStage->getSunAmbientIntensity(); } -void SceneScriptingInterface::setKeyLightDirection(const glm::vec3& direction) { - _skyStage->setSunDirection(direction); +void SceneScripting::KeyLight::setAmbientIntensity(float intensity) { + _skyStage->setSunAmbientIntensity(intensity); } -glm::vec3 SceneScriptingInterface::getKeyLightDirection() const { +glm::vec3 SceneScripting::KeyLight::getDirection() const { return _skyStage->getSunDirection(); } -void SceneScriptingInterface::setStageSunModelEnable(bool isEnabled) { +void SceneScripting::KeyLight::setDirection(const glm::vec3& direction) { + _skyStage->setSunDirection(direction); +} + +void SceneScripting::Stage::setOrientation(const glm::quat& orientation) const { + _skyStage->setOriginOrientation(orientation); +} + +void SceneScripting::Stage::setLocation(float longitude, float latitude, float altitude) { + _skyStage->setOriginLocation(longitude, latitude, altitude); +} + +void SceneScripting::Stage::setSunModelEnable(bool isEnabled) { _skyStage->setSunModelEnable(isEnabled); } -bool SceneScriptingInterface::isStageSunModelEnabled() const { +bool SceneScripting::Stage::isSunModelEnabled() const { return _skyStage->isSunModelEnabled(); } -void SceneScriptingInterface::setBackgroundMode(const QString& mode) { +void SceneScripting::Stage::setBackgroundMode(const QString& mode) { if (mode == QString("inherit")) { _skyStage->setBackgroundMode(model::SunSkyStage::NO_BACKGROUND); } else if (mode == QString("atmosphere")) { @@ -104,7 +111,7 @@ void SceneScriptingInterface::setBackgroundMode(const QString& mode) { } } -QString SceneScriptingInterface::getBackgroundMode() const { +QString SceneScripting::Stage::getBackgroundMode() const { switch (_skyStage->getBackgroundMode()) { case model::SunSkyStage::NO_BACKGROUND: return QString("inherit"); @@ -117,8 +124,9 @@ QString SceneScriptingInterface::getBackgroundMode() const { }; } -model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { - return _skyStage; +SceneScriptingInterface::SceneScriptingInterface() : _stage{ new SceneScripting::Stage{ _skyStage } } { + // Let's make sure the sunSkyStage is using a proceduralSkybox + _skyStage->setSkybox(model::SkyboxPointer(new ProceduralSkybox())); } void SceneScriptingInterface::setShouldRenderAvatars(bool shouldRenderAvatars) { @@ -135,35 +143,6 @@ void SceneScriptingInterface::setShouldRenderEntities(bool shouldRenderEntities) } } -void SceneScriptingInterface::setEngineRenderOpaque(bool renderOpaque) { - _engineRenderOpaque = renderOpaque; -} - -void SceneScriptingInterface::setEngineRenderTransparent(bool renderTransparent) { - _engineRenderTransparent = renderTransparent; -} - -void SceneScriptingInterface::setEngineCullOpaque(bool cullOpaque) { - _engineCullOpaque = cullOpaque; -} - -void SceneScriptingInterface::setEngineCullTransparent(bool cullTransparent) { - _engineCullTransparent = cullTransparent; -} - -void SceneScriptingInterface::setEngineSortOpaque(bool sortOpaque) { - _engineSortOpaque = sortOpaque; -} - -void SceneScriptingInterface::setEngineSortTransparent(bool sortTransparent) { - _engineSortOpaque = sortTransparent; -} - -void SceneScriptingInterface::clearEngineCounters() { - _numFeedOpaqueItems = 0; - _numDrawnOpaqueItems = 0; - _numFeedTransparentItems = 0; - _numDrawnTransparentItems = 0; - _numFeedOverlay3DItems = 0; - _numDrawnOverlay3DItems = 0; +model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { + return _skyStage; } diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 8cd48d9ddd..0be8b066aa 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -1,6 +1,6 @@ // // SceneScriptingInterface.h -// interface/src/scripting +// libraries/script-engine // // Created by Sam Gateau on 2/24/15. // Copyright 2014 High Fidelity, Inc. @@ -12,141 +12,149 @@ #ifndef hifi_SceneScriptingInterface_h #define hifi_SceneScriptingInterface_h -#include - -#include +#include // QObject +#include // Dependency #include "model/Stage.h" +// TODO: if QT moc ever supports nested classes, subclass these to the interface instead of namespacing +namespace SceneScripting { + class Location : public QObject { + Q_OBJECT + + public: + Location(model::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} + + Q_PROPERTY(float longitude READ getLongitude WRITE setLongitude) + Q_PROPERTY(float latitude READ getLatitude WRITE setLatitude) + Q_PROPERTY(float altitude READ getAltitude WRITE setAltitude) + + float getLongitude() const; + float getLatitude() const; + float getAltitude() const; + void setLongitude(float longitude); + void setLatitude(float latitude); + void setAltitude(float altitude); + + protected: + model::SunSkyStagePointer _skyStage; + }; + using LocationPointer = std::unique_ptr; + + class Time : public QObject { + Q_OBJECT + + public: + Time(model::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} + + Q_PROPERTY(float hour READ getHour WRITE setHour) + Q_PROPERTY(int day READ getDay WRITE setDay) + + float getHour() const; + void setHour(float hour); + int getDay() const; + void setDay(int day); + + protected: + model::SunSkyStagePointer _skyStage; + }; + using TimePointer = std::unique_ptr