History

Merge branch 'master' of github.com:highfidelity/hifi into audio/parity

Zach Pomerantz committed 9 years ago

Commit a8f69bb2 parents e7da007 ae81738385 files · +9641 −4012Browse files

Unified · Split

385 files
  1. BUILD.md +15 −10
  2. BUILD_ANDROID.md +9 −9
  3. BUILD_LINUX.md +2 −1
  4. BUILD_OSX.md +7 −4
  5. BUILD_WIN.md +33 −29
  6. INSTALL.md +4 −4
  7. assignment-client/src/Agent.cpp +2 −5
  8. assignment-client/src/AssignmentClient.cpp +1 −17
  9. assignment-client/src/audio/AudioMixerSlavePool.h +2 −2
  10. assignment-client/src/avatars/AvatarMixerSlavePool.h +1 −2
  11. assignment-client/src/scripts/EntityScriptServer.cpp +1 −5
  12. cmake/externals/nvtt/CMakeLists.txt +4 −4
  13. domain-server/resources/web/content/index.shtml +1 −1
  14. gvr-interface/src/RenderingClient.cpp +2 −10
  15. interface/resources/avatar/avatar-animation.json +3 −3
  16. interface/resources/config/render.json +9 −7
  17. interface/resources/controllers/oculus_touch.json +3 −1
  18. interface/resources/controllers/vive.json +8 −8
  19. interface/resources/fonts/hifi-glyphs.ttf +0 −0
  20. interface/resources/html/createGlobalEventBridge.js +1 −1
  21. interface/resources/meshes/Jointy3/Jointy3.fbx +0 −0
  22. interface/resources/meshes/defaultAvatar_full.fst +73 −69
  23. interface/resources/meshes/mannequin/Eyes.ktx +0 −0
  24. interface/resources/meshes/mannequin/lambert1_Base_Color.ktx +0 −0
  25. interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx +0 −0
  26. interface/resources/meshes/mannequin/lambert1_Roughness.ktx +0 −0
  27. interface/resources/meshes/mannequin/mannequin.baked.fbx +0 −0
  28. interface/resources/qml/AssetServer.qml +1 −1
  29. interface/resources/qml/Browser.qml +3 −18
  30. interface/resources/qml/QmlWebWindow.qml +2 −8
  31. interface/resources/qml/QmlWindow.qml +0 −9
  32. interface/resources/qml/TabletBrowser.qml +4 −14
  33. interface/resources/qml/ToolWindow.qml +3 −10
  34. interface/resources/qml/controls-uit/BaseWebView.qml +0 −3
  35. interface/resources/qml/controls/TabletWebScreen.qml +4 −14
  36. interface/resources/qml/controls/TabletWebView.qml +3 −14
  37. interface/resources/qml/controls/WebEntityView.qml +1 −5
  38. interface/resources/qml/controls/WebView.qml +3 −14
  39. interface/resources/qml/dialogs/TabletLoginDialog.qml +0 −1
  40. interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +4 −12
  41. interface/resources/qml/dialogs/preferences/AvatarPreference.qml +1 −3
  42. interface/resources/qml/hifi/Desktop.qml +0 −6
  43. interface/resources/qml/hifi/Pal.qml +7 −4
  44. interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +1 −2
  45. interface/resources/qml/hifi/dialogs/TabletDCDialog.qml +0 −1
  46. interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml +0 −1
  47. interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +0 −1
  48. interface/resources/qml/hifi/dialogs/TabletLODTools.qml +0 −1
  49. interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +0 −1
  50. interface/resources/qml/hifi/tablet/Edit.qml +0 −2
  51. interface/resources/qml/hifi/tablet/EditTabView.qml +1 −6
  52. interface/resources/qml/hifi/tablet/InputRecorder.qml +0 −1
  53. interface/resources/qml/hifi/tablet/NewModelDialog.qml +1 −2
  54. interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +0 −2
  55. interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml +0 −1
  56. interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml +1 −0
  57. interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml +0 −1
  58. interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +0 −1
  59. interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml +0 −1
  60. interface/resources/qml/hifi/tablet/TabletLodPreferences.qml +0 −1
  61. interface/resources/qml/hifi/tablet/TabletMenu.qml +0 −2
  62. interface/resources/qml/hifi/tablet/TabletMenuStack.qml +0 −1
  63. interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml +0 −1
  64. interface/resources/qml/hifi/tablet/TabletRoot.qml +11 −14
  65. interface/resources/qml/hifi/tablet/WindowRoot.qml +11 −11
  66. interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +1 −1
  67. interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml +5 −10
  68. interface/resources/snapshot/img/no-image.jpg +0 −0
  69. interface/src/Application.cpp +283 −241
  70. interface/src/Application.h +7 −5
  71. interface/src/AvatarBookmarks.cpp +22 −2
  72. interface/src/AvatarBookmarks.h +1 −0
  73. interface/src/CrashHandler.cpp +20 −8
  74. interface/src/CrashHandler.h +1 −1
  75. interface/src/DiscoverabilityManager.cpp +1 −1
  76. interface/src/Menu.cpp +6 −0
  77. interface/src/Menu.h +1 −0
  78. interface/src/avatar/MyAvatar.cpp +276 −57
  79. interface/src/avatar/MyAvatar.h +24 −10
  80. interface/src/avatar/MyHead.cpp +9 −8
  81. interface/src/avatar/MyHead.h +1 −1
  82. interface/src/avatar/MySkeletonModel.cpp +8 −16
  83. interface/src/main.cpp +2 −6
  84. interface/src/networking/CloseEventSender.cpp +95 −0
  85. interface/src/networking/CloseEventSender.h +42 −0
  86. interface/src/scripting/HMDScriptingInterface.cpp +4 −0
  87. interface/src/scripting/HMDScriptingInterface.h +1 −0
  88. interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp +5 −4
  89. interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.h +0 −1
  90. interface/src/ui/ModelsBrowser.cpp +2 −6
  91. interface/src/ui/overlays/Text3DOverlay.cpp +13 −3
  92. interface/src/ui/overlays/Text3DOverlay.h +7 −6
  93. interface/src/ui/overlays/Web3DOverlay.cpp +35 −33
  94. libraries/animation/src/AnimInverseKinematics.cpp +294 −23
  95. libraries/animation/src/AnimInverseKinematics.h +29 −3
  96. libraries/animation/src/IKTarget.cpp +3 −0
  97. libraries/animation/src/IKTarget.h +1 −0
  98. libraries/animation/src/Rig.cpp +66 −32
  99. libraries/animation/src/Rig.h +7 −4
  100. libraries/animation/src/RotationAccumulator.cpp +2 −2
  101. libraries/animation/src/TranslationAccumulator.cpp +34 −0
  102. libraries/animation/src/TranslationAccumulator.h +42 −0
  103. libraries/audio-client/src/AudioClient.cpp +37 −78
  104. libraries/audio-client/src/AudioClient.h +2 −2
  105. libraries/audio/src/Sound.cpp +45 −16
  106. libraries/audio/src/Sound.h +30 −3
  107. libraries/avatars-renderer/src/avatars-renderer/Head.cpp +0 −12
  108. libraries/avatars-renderer/src/avatars-renderer/Head.h +0 −3
  109. libraries/avatars/src/AvatarData.cpp +4 −1
  110. libraries/avatars/src/HeadData.h +0 −3
  111. libraries/controllers/src/controllers/Actions.h +2 −2
  112. libraries/controllers/src/controllers/InputRecorder.cpp +77 −46
  113. libraries/controllers/src/controllers/InputRecorder.h +11 −8
  114. libraries/controllers/src/controllers/Pose.h +1 −0
  115. libraries/controllers/src/controllers/UserInputMapper.cpp +10 −0
  116. libraries/controllers/src/controllers/UserInputMapper.h +1 −0
  117. libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +7 −8
  118. libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h +11 −0
  119. libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h +3 −3
  120. libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +12 −0
  121. libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +6 −0
  122. libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +6 −0
  123. libraries/entities-renderer/src/EntityTreeRenderer.cpp +1 −221
  124. libraries/entities-renderer/src/EntityTreeRenderer.h +0 −3
  125. libraries/entities-renderer/src/RenderableModelEntityItem.cpp +1 −1
  126. libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +3 −0
  127. libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +2 −0
  128. libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +5 −0
  129. libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +2 −0
  130. libraries/entities-renderer/src/RenderableWebEntityItem.cpp +4 −4
  131. libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +408 −38
  132. libraries/entities-renderer/src/RenderableZoneEntityItem.h +14 −3
  133. libraries/entities/src/EntityItem.cpp +7 −17
  134. libraries/entities/src/EntityItem.h +2 −2
  135. libraries/entities/src/EntityScriptingInterface.cpp +6 −4
  136. libraries/entities/src/SimulationFlags.h +1 −3
  137. libraries/entities/src/SimulationOwner.cpp +11 −11
  138. libraries/entities/src/SimulationOwner.h +3 −2
  139. libraries/entities/src/ZoneEntityItem.cpp +39 −20
  140. libraries/entities/src/ZoneEntityItem.h +15 −1
  141. libraries/fbx/src/FBXReader.cpp +4 −10
  142. libraries/gl/CMakeLists.txt +1 −1
  143. libraries/gl/src/gl/OffscreenQmlSurface.cpp +152 −75
  144. libraries/gl/src/gl/OffscreenQmlSurface.h +5 −5
  145. libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp +0 −1
  146. libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +13 −2
  147. libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +4 −0
  148. libraries/gpu/src/gpu/DrawTransformVertexPosition.slv +28 −0
  149. libraries/gpu/src/gpu/DrawVertexPosition.slv +6 −13
  150. libraries/gpu/src/gpu/DrawWhite.slf +6 −4
  151. libraries/gpu/src/gpu/StandardShaderLib.cpp +38 −2
  152. libraries/gpu/src/gpu/StandardShaderLib.h +15 −0
  153. libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +0 −2
  154. libraries/input-plugins/src/input-plugins/TouchscreenDevice.h +0 −2
  155. libraries/ktx/src/khronos/KHR.h +2 −0
  156. libraries/ktx/src/ktx/Writer.cpp +1 −1
  157. libraries/model-networking/src/model-networking/KTXCache.cpp +14 −0
  158. libraries/model-networking/src/model-networking/KTXCache.h +6 −0
  159. libraries/model/src/model/Geometry.cpp +38 −0
  160. libraries/model/src/model/Geometry.h +6 −0
  161. libraries/model/src/model/Light.cpp +1 −1
  162. libraries/model/src/model/Light.h +3 −3
  163. libraries/model/src/model/Skybox.cpp +1 −1
  164. libraries/model/src/model/Skybox.h +4 −0
  165. libraries/networking/CMakeLists.txt +1 −1
  166. libraries/networking/src/AccountManager.cpp +7 −1
  167. libraries/networking/src/AccountManager.h +2 −1
  168. libraries/networking/src/AssetRequest.cpp +7 −2
  169. libraries/networking/src/AssetRequest.h +3 −0
  170. libraries/networking/src/AssetResourceRequest.cpp +33 −3
  171. libraries/networking/src/FileCache.cpp +146 −64
  172. libraries/networking/src/FileCache.h +42 −24
  173. libraries/networking/src/FileResourceRequest.cpp +11 −0
  174. libraries/networking/src/FileTypeProfile.cpp +0 −0
  175. libraries/networking/src/FileTypeProfile.h +0 −0
  176. libraries/networking/src/FileTypeRequestInterceptor.cpp +0 −0
  177. libraries/networking/src/FileTypeRequestInterceptor.h +0 −0
  178. libraries/networking/src/HFTabletWebEngineProfile.cpp +1 −0
  179. libraries/networking/src/HFTabletWebEngineProfile.h +0 −0
  180. libraries/networking/src/HFTabletWebEngineRequestInterceptor.cpp +1 −1
  181. libraries/networking/src/HFTabletWebEngineRequestInterceptor.h +0 −0
  182. libraries/networking/src/HFWebEngineProfile.cpp +1 −0
  183. libraries/networking/src/HFWebEngineProfile.h +0 −0
  184. libraries/networking/src/HFWebEngineRequestInterceptor.cpp +1 −1
  185. libraries/networking/src/HFWebEngineRequestInterceptor.h +0 −0
  186. libraries/networking/src/HTTPResourceRequest.cpp +16 −0
  187. libraries/networking/src/LimitedNodeList.cpp +2 −4
  188. libraries/networking/src/LimitedNodeList.h +4 −3
  189. libraries/networking/src/MessagesClient.cpp +6 −6
  190. libraries/networking/src/MessagesClient.h +1 −1
  191. libraries/networking/src/Node.h +1 −1
  192. libraries/networking/src/NodeList.cpp +6 −0
  193. libraries/networking/src/NodeList.h +2 −1
  194. libraries/networking/src/RequestFilters.cpp +1 −1
  195. libraries/networking/src/RequestFilters.h +0 −0
  196. libraries/networking/src/ResourceRequest.h +15 −0
  197. libraries/networking/src/SandboxUtils.cpp +3 −4
  198. libraries/networking/src/SandboxUtils.h +1 −1
  199. libraries/networking/src/UserActivityLogger.cpp +9 −1
  200. libraries/networking/src/UserActivityLogger.h +6 −1
  201. libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +9 −1
  202. libraries/networking/src/UserActivityLoggerScriptingInterface.h +2 −0
  203. libraries/networking/src/udt/PacketHeaders.cpp +1 −1
  204. libraries/networking/src/udt/PacketHeaders.h +2 −1
  205. libraries/networking/src/udt/SendQueue.cpp +3 −0
  206. libraries/physics/src/CharacterController.cpp +13 −3
  207. libraries/physics/src/CharacterController.h +2 −0
  208. libraries/physics/src/EntityMotionState.cpp +22 −15
  209. libraries/physics/src/EntityMotionState.h +1 −0
  210. libraries/physics/src/ObjectMotionState.cpp +2 −0
  211. libraries/physics/src/ObjectMotionState.h +2 −1
  212. libraries/physics/src/PhysicalEntitySimulation.cpp +1 −1
  213. libraries/physics/src/PhysicsEngine.cpp +25 −20
  214. libraries/physics/src/PhysicsEngine.h +3 −2
  215. libraries/physics/src/ThreadSafeDynamicsWorld.h +2 −0
  216. libraries/plugins/src/plugins/DisplayPlugin.h +1 −0
  217. libraries/plugins/src/plugins/InputPlugin.h +2 −1
  218. libraries/plugins/src/plugins/Plugin.h +13 −0
  219. libraries/plugins/src/plugins/PluginUtils.cpp +9 −0
  220. libraries/plugins/src/plugins/PluginUtils.h +1 −0
  221. libraries/procedural/src/procedural/ProceduralSkybox.cpp +1 −1
  222. libraries/render-utils/CMakeLists.txt +1 −1
  223. libraries/render-utils/src/AnimDebugDraw.cpp +1 −1
  224. libraries/render-utils/src/AntialiasingEffect.cpp +4 −4
  225. libraries/render-utils/src/BackgroundStage.cpp +140 −0
  226. libraries/render-utils/src/BackgroundStage.h +80 −0
  227. libraries/render-utils/src/DeferredLightingEffect.cpp +51 −126
  228. libraries/render-utils/src/DeferredLightingEffect.h +17 −26
  229. libraries/render-utils/src/GeometryCache.cpp +94 −30
  230. libraries/render-utils/src/GeometryCache.h +2 −2
  231. libraries/render-utils/src/HitEffect.cpp +0 −94
  232. libraries/render-utils/src/HitEffect.h +0 −38
  233. libraries/render-utils/src/LightClusters.cpp +2 −0
  234. libraries/render-utils/src/LightClusters.h +3 −3
  235. libraries/render-utils/src/LightPayload.cpp +68 −0
  236. libraries/render-utils/src/LightPayload.h +38 −0
  237. libraries/render-utils/src/LightStage.h +8 −3
  238. libraries/render-utils/src/Model.cpp +2 −2
  239. libraries/render-utils/src/Model.h +1 −1
  240. libraries/render-utils/src/RenderDeferredTask.cpp +15 −95
  241. libraries/render-utils/src/RenderDeferredTask.h +0 −29
  242. libraries/render-utils/src/RenderPipelines.cpp +2 −0
  243. libraries/render-utils/src/RenderShadowTask.cpp +3 −2
  244. libraries/render-utils/src/RenderViewTask.cpp +33 −0
  245. libraries/render-utils/src/RenderViewTask.h +31 −0
  246. libraries/render-utils/src/StencilMaskPass.cpp +126 −0
  247. libraries/render-utils/src/StencilMaskPass.h +71 −0
  248. libraries/render-utils/src/SurfaceGeometryPass.cpp +4 −3
  249. libraries/render-utils/src/ToneMappingEffect.cpp +1 −1
  250. libraries/render-utils/src/ZoneRenderer.cpp +188 −3
  251. libraries/render-utils/src/ZoneRenderer.h +40 −1
  252. libraries/render-utils/src/directional_light.slf +0 −47
  253. libraries/render-utils/src/directional_light_shadow.slf +0 −49
  254. libraries/render-utils/src/hit_effect.slf +0 −27
  255. libraries/render-utils/src/point_light.slf +0 −85
  256. libraries/render-utils/src/spot_light.slf +0 −115
  257. libraries/render-utils/src/stencil_drawMask.slf +23 −0
  258. libraries/render-utils/src/text/Font.cpp +3 −0
  259. libraries/render-utils/src/text/Font.h +1 −1
  260. libraries/render-utils/src/zone_draw.slh +37 −0
  261. libraries/render-utils/src/zone_drawAmbient.slf +54 −0
  262. libraries/render-utils/src/zone_drawKeyLight.slf +57 −0
  263. libraries/render-utils/src/zone_drawSkybox.slf +48 −0
  264. libraries/render/src/render/EngineStats.cpp +0 −2
  265. libraries/render/src/task/Config.cpp +2 −0
  266. libraries/render/src/task/Config.h +3 −2
  267. libraries/render/src/task/Task.h +1 −0
  268. libraries/script-engine/src/TabletScriptingInterface.cpp +27 −19
  269. libraries/shared/src/CubicHermiteSpline.h +113 −0
  270. libraries/shared/src/Extents.h +1 −1
  271. libraries/shared/src/GLMHelpers.cpp +24 −25
  272. libraries/shared/src/GeometryUtil.cpp +2 −2
  273. libraries/shared/src/GeometryUtil.h +3 −3
  274. libraries/shared/src/NumericalConstants.h +3 −0
  275. libraries/shared/src/RenderArgs.h +4 −4
  276. libraries/shared/src/RunningMarker.cpp +1 −34
  277. libraries/shared/src/RunningMarker.h +1 −11
  278. libraries/shared/src/TBBHelpers.h +28 −0
  279. libraries/shared/src/ThreadHelpers.cpp +39 −0
  280. libraries/shared/src/ThreadHelpers.h +10 −2
  281. libraries/shared/src/TriangleSet.cpp +6 −6
  282. libraries/shared/src/TriangleSet.h +3 −3
  283. libraries/ui/src/OffscreenUi.cpp +8 −8
  284. libraries/ui/src/QmlWindowClass.cpp +2 −2
  285. libraries/ui/src/VrMenu.cpp +1 −1
  286. plugins/hifiKinect/src/KinectPlugin.cpp +6 −2
  287. plugins/hifiKinect/src/KinectPlugin.h +1 −0
  288. plugins/hifiNeuron/src/NeuronPlugin.h +0 −2
  289. plugins/hifiSdl2/src/SDL2Manager.h +0 −1
  290. plugins/hifiSixense/src/SixenseManager.h +1 −1
  291. plugins/oculus/src/OculusBaseDisplayPlugin.cpp +10 −1
  292. plugins/oculus/src/OculusBaseDisplayPlugin.h +3 −0
  293. plugins/oculus/src/OculusControllerManager.cpp +48 −11
  294. plugins/oculus/src/OculusControllerManager.h +9 −2
  295. plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +1 −1
  296. plugins/openvr/CMakeLists.txt +3 −2
  297. plugins/openvr/src/OpenVrDisplayPlugin.cpp +30 −0
  298. plugins/openvr/src/OpenVrDisplayPlugin.h +4 −1
  299. plugins/openvr/src/OpenVrHelpers.cpp +16 −0
  300. plugins/openvr/src/OpenVrHelpers.h +1 −0
  301. plugins/openvr/src/ViveControllerManager.cpp +174 −8
  302. plugins/openvr/src/ViveControllerManager.h +11 −1
  303. scripts/defaultScripts.js +2 −1
  304. scripts/developer/tests/avatarToWorldTests.js +127 −0
  305. scripts/developer/tests/worldToAvatarTests.js +134 −0
  306. scripts/developer/utilities/render/deferredLighting.qml +16 −6
  307. scripts/developer/utilities/render/stats.qml +6 −7
  308. scripts/system/assets/sounds/snap.wav +0 −0
  309. scripts/system/assets/sounds/sound-print-photo.wav +0 −0
  310. scripts/system/bubble.js +11 −6
  311. scripts/system/chat.js +1 −1
  312. scripts/system/controllers/handControllerGrab.js +2 −1
  313. scripts/system/controllers/teleport.js +17 −37
  314. scripts/system/edit.js +173 −63
  315. scripts/system/html/ChatPage.html +0 −0
  316. scripts/system/html/SnapshotReview.html +5 −1
  317. scripts/system/html/css/SnapshotReview.css +37 −0
  318. scripts/system/html/css/edit-style.css +3 −1
  319. scripts/system/html/css/hifi-style.css +1 −1
  320. scripts/system/html/css/jsoneditor.css +1 −1
  321. scripts/system/html/entityProperties.html +3 −0
  322. scripts/system/html/img/button-snap-print.svg +25 −0
  323. scripts/system/html/js/SnapshotReview.js +101 −20
  324. scripts/system/html/js/entityProperties.js +62 −127
  325. scripts/system/html/js/eventBridgeLoader.js +1 −1
  326. scripts/system/html/js/marketplacesInject.js +2 −9
  327. scripts/system/libraries/WebTablet.js +38 −31
  328. scripts/system/libraries/entityList.js +7 −1
  329. scripts/system/libraries/gridTool.js +22 −8
  330. scripts/system/makeUserConnection.js +2 −3
  331. scripts/system/particle_explorer/dat.gui.min.js +0 −95
  332. scripts/system/particle_explorer/hifi-entity-ui.js +680 −0
  333. scripts/system/particle_explorer/particle-style.css +124 −0
  334. scripts/system/particle_explorer/particleExplorer.html +28 −81
  335. scripts/system/particle_explorer/particleExplorer.js +397 −537
  336. scripts/system/particle_explorer/particleExplorerTool.js +6 −4
  337. scripts/system/progress.js +2 −2
  338. scripts/system/snapshot.js +172 −18
  339. scripts/system/tablet-ui/tabletUI.js +2 −6
  340. server-console/src/main.js +23 −25
  341. tests/controllers/src/main.cpp +8 −0
  342. tests/ktx/CMakeLists.txt +7 −13
  343. tests/ktx/src/KtxTests.cpp +195 −0
  344. tests/ktx/src/KtxTests.h +21 −0
  345. tests/ktx/src/main.cpp +0 −223
  346. tests/networking/src/FileCacheTests.cpp +187 −0
  347. tests/networking/src/FileCacheTests.h +31 −0
  348. tests/qt59/CMakeLists.txt +15 −0
  349. tests/qt59/src/main.cpp +78 −0
  350. tests/render-perf/CMakeLists.txt +1 −1
  351. tests/shaders/src/main.cpp +0 −11
  352. tests/shared/src/CubicHermiteSplineTests.cpp +77 −0
  353. tests/shared/src/CubicHermiteSplineTests.h +23 −0
  354. tests/shared/src/GLMHelpersTests.cpp +74 −0
  355. tests/shared/src/GLMHelpersTests.h +1 −0
  356. tests/shared/src/StorageTests.cpp +9 −7
  357. tools/ac-client/src/ACClientApp.cpp +1 −14
  358. tools/atp-get/src/ATPGetApp.cpp +1 −15
  359. tools/oven/src/BakerCLI.cpp +64 −0
  360. tools/oven/src/BakerCLI.h +34 −0
  361. tools/oven/src/Oven.cpp +30 −7
  362. tools/oven/src/Oven.h +1 −1
  363. unpublishedScripts/marketplace/record/assets/sounds/countdown-tick.wav +0 −0
  364. unpublishedScripts/marketplace/record/assets/sounds/finish-recording.wav +0 −0
  365. unpublishedScripts/marketplace/record/assets/sounds/start-recording.wav +0 −0
  366. unpublishedScripts/marketplace/record/html/css/edit-style.css +1262 −0
  367. unpublishedScripts/marketplace/record/html/css/record.css +0 −0
  368. unpublishedScripts/marketplace/record/html/fonts/FiraSans-SemiBold.ttf +0 −0
  369. unpublishedScripts/marketplace/record/html/fonts/FiraSans.license +94 −0
  370. unpublishedScripts/marketplace/record/html/fonts/Raleway-Bold.ttf +0 −0
  371. unpublishedScripts/marketplace/record/html/fonts/Raleway-Light.ttf +0 −0
  372. unpublishedScripts/marketplace/record/html/fonts/Raleway-Regular.ttf +0 −0
  373. unpublishedScripts/marketplace/record/html/fonts/Raleway-SemiBold.ttf +0 −0
  374. unpublishedScripts/marketplace/record/html/fonts/Raleway.license +94 −0
  375. unpublishedScripts/marketplace/record/html/fonts/hifi-glyphs.ttf +0 −0
  376. unpublishedScripts/marketplace/record/html/img/loader-red-countdown-ring.gif +0 −0
  377. unpublishedScripts/marketplace/record/html/js/record.js +0 −0
  378. unpublishedScripts/marketplace/record/html/record.html +0 −0
  379. unpublishedScripts/marketplace/record/playRecordingAC.js +0 −0
  380. unpublishedScripts/marketplace/record/record.js +0 −0
  381. unpublishedScripts/marketplace/stopwatch/models/transparent-box.fbx +0 −0
  382. unpublishedScripts/marketplace/stopwatch/spawnStopwatch.js +36 −2
  383. unpublishedScripts/marketplace/stopwatch/stopwatchReset.js +22 −0
  384. unpublishedScripts/marketplace/stopwatch/stopwatchServer.js +31 −19
  385. unpublishedScripts/marketplace/stopwatch/stopwatchStartStop.js +7 −6
BUILD.md+15−10
@@ -1,4 +1,4 @@
1###Dependencies1### Dependencies
2 2
3* [cmake](https://cmake.org/download/) ~> 3.3.23* [cmake](https://cmake.org/download/) ~> 3.3.2
4* [Qt](https://www.qt.io/download-open-source) ~> 5.6.24* [Qt](https://www.qt.io/download-open-source) ~> 5.6.2
@@ -6,7 +6,7 @@ · 1 unmodified line
6 * IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities.6 * IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities.
7* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)7* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
8 8
9####CMake External Project Dependencies9#### CMake External Project Dependencies
10 10
11* [boostconfig](https://github.com/boostorg/config) ~> 1.5811* [boostconfig](https://github.com/boostorg/config) ~> 1.58
12* [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases) ~> 2.8312* [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases) ~> 2.83
@@ -30,16 +30,19 @@ These are not placed in your normal build tree when doing an out of source build · 17 unmodified lines
30 30
31If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.31If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
32 32
33###OS Specific Build Guides33### OS Specific Build Guides
34
34* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.35* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
35* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.36* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
36* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows.37* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows.
37* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android38* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
38 39
39###CMake40### CMake
41
40Hifi uses CMake to generate build files and project files for your platform.42Hifi uses CMake to generate build files and project files for your platform.
41 43
42####Qt44#### Qt
45
43In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation.46In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation.
44 47
45This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).48This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
@@ -50,7 +53,8 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e · 4 unmodified lines
50 export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake53 export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake
51 export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake54 export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
52 55
53####Generating build files56#### Generating build files
57
54Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.58Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.
55 59
56 mkdir build60 mkdir build
@@ -59,14 +63,15 @@ Create a build directory in the root of your checkout and then run the CMake bui · 2 unmodified lines
59 63
60If cmake gives you the same error message repeatedly after the build fails (e.g. you had a typo in the QT_CMAKE_PREFIX_PATH that you fixed but the `.cmake` files still cannot be found), try removing `CMakeCache.txt`.64If cmake gives you the same error message repeatedly after the build fails (e.g. you had a typo in the QT_CMAKE_PREFIX_PATH that you fixed but the `.cmake` files still cannot be found), try removing `CMakeCache.txt`.
61 65
62####Variables66#### Variables
67
63Any variables that need to be set for CMake to find dependencies can be set as ENV variables in your shell profile, or passed directly to CMake with a `-D` flag appended to the `cmake ..` command.68Any variables that need to be set for CMake to find dependencies can be set as ENV variables in your shell profile, or passed directly to CMake with a `-D` flag appended to the `cmake ..` command.
64 69
65For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:70For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
66 71
67 cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/lib/cmake72 cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/lib/cmake
68 73
69####Finding Dependencies74#### Finding Dependencies
70 75
71The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).76The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).
72 77
@@ -78,8 +83,8 @@ In the examples below the variable $NAME would be replaced by the name of the de · 5 unmodified lines
78* $NAME_ROOT_DIR - set this variable in your ENV83* $NAME_ROOT_DIR - set this variable in your ENV
79* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name'84* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name'
80 85
81###Optional Components86### Optional Components
82 87
83####Devices88#### Devices
84 89
85You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.90You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.
BUILD_ANDROID.md+9−9
@@ -1,6 +1,6 @@
1Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file.1Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file.
2 2
3###Android Dependencies3### Android Dependencies
4 4
5You will need the following tools to build our Android targets.5You will need the following tools to build our Android targets.
6 6
@@ -17,23 +17,23 @@ You will need the following tools to build our Android targets. · 10 unmodified lines
17 17
18You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine.18You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine.
19 19
20####Scribe20#### Scribe
21 21
22High Fidelity has a shader pre-processing tool called `scribe` that various libraries will call on during the build process. You must compile scribe using your native toolchain (following the build instructions for your platform) and then pass a CMake variable or set an ENV variable `SCRIBE_PATH` that is a path to the scribe executable.22High Fidelity has a shader pre-processing tool called `scribe` that various libraries will call on during the build process. You must compile scribe using your native toolchain (following the build instructions for your platform) and then pass a CMake variable or set an ENV variable `SCRIBE_PATH` that is a path to the scribe executable.
23 23
24CMake will fatally error if it does not find the scribe executable while using the android toolchain.24CMake will fatally error if it does not find the scribe executable while using the android toolchain.
25 25
26####Optional Components26#### Optional Components
27 27
28* [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) ~> 0.4.228* [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) ~> 0.4.2
29 29
30####ANDROID_LIB_DIR30#### ANDROID_LIB_DIR
31 31
32Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies.32Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies.
33 33
34This is most easily accomplished by installing all Android dependencies in the same folder. You can place this folder wherever you like on your machine. In this build guide and across our CMakeLists files this folder is referred to as `ANDROID_LIB_DIR`. You can set `ANDROID_LIB_DIR` in your environment or by passing when you run CMake.34This is most easily accomplished by installing all Android dependencies in the same folder. You can place this folder wherever you like on your machine. In this build guide and across our CMakeLists files this folder is referred to as `ANDROID_LIB_DIR`. You can set `ANDROID_LIB_DIR` in your environment or by passing when you run CMake.
35 35
36####Qt36#### Qt
37 37
38Install Qt 5.5.1 for Android for your host environment from the [Qt downloads page](http://www.qt.io/download/). Install Qt to ``$ANDROID_LIB_DIR/Qt``. This is required so that our root CMakeLists file can help CMake find your Android Qt installation.38Install Qt 5.5.1 for Android for your host environment from the [Qt downloads page](http://www.qt.io/download/). Install Qt to ``$ANDROID_LIB_DIR/Qt``. This is required so that our root CMakeLists file can help CMake find your Android Qt installation.
39 39
@@ -41,7 +41,7 @@ The component required for the Android build is the `Android armv7` component. · 1 unmodified line
41 41
42If you would like to install Qt to a different location, or attempt to build with a different Qt version, you can pass `ANDROID_QT_CMAKE_PREFIX_PATH` to CMake. Point to the `cmake` folder inside `$VERSION_NUMBER/android_armv7/lib`. Otherwise, our root CMakeLists will set it to `$ANDROID_LIB_DIR/Qt/5.5/android_armv7/lib/cmake`.42If you would like to install Qt to a different location, or attempt to build with a different Qt version, you can pass `ANDROID_QT_CMAKE_PREFIX_PATH` to CMake. Point to the `cmake` folder inside `$VERSION_NUMBER/android_armv7/lib`. Otherwise, our root CMakeLists will set it to `$ANDROID_LIB_DIR/Qt/5.5/android_armv7/lib/cmake`.
43 43
44####OpenSSL44#### OpenSSL
45 45
46Cross-compilation of OpenSSL has been tested from an OS X machine running 10.10 compiling OpenSSL 1.0.2. It is likely that the steps below will work for other OpenSSL versions than 1.0.2.46Cross-compilation of OpenSSL has been tested from an OS X machine running 10.10 compiling OpenSSL 1.0.2. It is likely that the steps below will work for other OpenSSL versions than 1.0.2.
47 47
@@ -76,7 +76,7 @@ This should generate libcrypto and libssl in the root of the OpenSSL directory. · 28 unmodified lines
76 76
77If you have been building other components it is possible that the OpenSSL compile will fail based on the values other cross-compilations (tbb, bullet) have set. Ensure that you are in a new terminal window to avoid compilation errors from previously set environment variables.77If you have been building other components it is possible that the OpenSSL compile will fail based on the values other cross-compilations (tbb, bullet) have set. Ensure that you are in a new terminal window to avoid compilation errors from previously set environment variables.
78 78
79####Oculus Mobile SDK79#### Oculus Mobile SDK
80 80
81The Oculus Mobile SDK is optional, for Gear VR support. It is not required to compile gvr-interface.81The Oculus Mobile SDK is optional, for Gear VR support. It is not required to compile gvr-interface.
82 82
@@ -91,7 +91,7 @@ ndk-build · 8 unmodified lines
91 91
92This will create the liboculus.a archive that our FindLibOVR module will look for when cmake is run.92This will create the liboculus.a archive that our FindLibOVR module will look for when cmake is run.
93 93
94#####Hybrid testing94##### Hybrid testing
95 95
96Currently the 'vr_dual' mode that would allow us to run a hybrid app has limited support in the Oculus Mobile SDK. The best way to have an application we can launch without having to connect to the GearVR is to put the Gear VR Service into developer mode. This stops Oculus Home from taking over the device when it is plugged into the Gear VR headset, and allows the application to be launched from the Applications page.96Currently the 'vr_dual' mode that would allow us to run a hybrid app has limited support in the Oculus Mobile SDK. The best way to have an application we can launch without having to connect to the GearVR is to put the Gear VR Service into developer mode. This stops Oculus Home from taking over the device when it is plugged into the Gear VR headset, and allows the application to be launched from the Applications page.
97 97
@@ -99,7 +99,7 @@ To put the Gear VR Service into developer mode you need an application with an O · 1 unmodified line
99 99
100Once the application is on your device, go to `Settings->Application Manager->Gear VR Service->Manage Storage`. Tap on `VR Service Version` six times. It will scan your device to verify that you have an osig file in an application on your device, and then it will let you enable Developer mode.100Once the application is on your device, go to `Settings->Application Manager->Gear VR Service->Manage Storage`. Tap on `VR Service Version` six times. It will scan your device to verify that you have an osig file in an application on your device, and then it will let you enable Developer mode.
101 101
102###CMake102### CMake
103 103
104We use CMake to generate the makefiles that compile and deploy the Android APKs to your device. In order to create Makefiles for the Android targets, CMake requires that some environment variables are set, and that other variables are passed to it when it is run.104We use CMake to generate the makefiles that compile and deploy the Android APKs to your device. In order to create Makefiles for the Android targets, CMake requires that some environment variables are set, and that other variables are passed to it when it is run.
105 105
BUILD_LINUX.md+2−1
@@ -1,6 +1,7 @@
1Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.1Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
2 2
3###Qt5 Dependencies3### Qt5 Dependencies
4
4Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:5Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
5 6
6 libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev7 libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
BUILD_OSX.md+7−4
@@ -1,12 +1,13 @@
1Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.1Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.
2 2
3###Homebrew3### Homebrew
4
4[Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple.5[Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple.
5 6
6 brew tap homebrew/versions7 brew tap homebrew/versions
7 brew install cmake openssl8 brew install cmake openssl
8 9
9###OpenSSL10### OpenSSL
10 11
11Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.12Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
12For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:13For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
@@ -15,7 +16,8 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: · 2 unmodified lines
15 16
16Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.17Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
17 18
18###Qt19### Qt
20
19Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg). 21Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg).
20 22
21Keep the default components checked when going through the installer.23Keep the default components checked when going through the installer.
@@ -23,7 +25,8 @@ Keep the default components checked when going through the installer. · 1 unmodified line
23Once Qt is installed, you need to manually configure the following:25Once Qt is installed, you need to manually configure the following:
24* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory.26* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory.
25 27
26###Xcode28### Xcode
29
27If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.30If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
28 31
29 cmake .. -GXcode32 cmake .. -GXcode
BUILD_WIN.md+33−29
@@ -1,62 +1,66 @@
1This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.1This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.
2 2
3###Step 1. Installing Visual Studio 20133## Building High Fidelity
4
5### Step 1. Installing Visual Studio 2013
4 6
5If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer.7If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer.
6 8
7Note: Newer versions of Visual Studio are not yet compatible. 9Note: Newer versions of Visual Studio are not yet compatible.
8 10
9###Step 2. Installing CMake11### Step 2. Installing CMake
10 12
11Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.13Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
12 14
13###Step 3. Installing Qt15### Step 3. Installing Qt
14 16
15Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe). 17Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe).
16 18
17Keep the default components checked when going through the installer.19Keep the default components checked when going through the installer.
18 20
19###Step 4. Setting Qt Environment Variable21### Step 4. Setting Qt Environment Variable
20 22
21Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search).23Go to `Control Panel > System > Advanced System Settings > Environment Variables > New...` (or search “Environment Variables” in Start Search).
22* Set "Variable name": QT_CMAKE_PREFIX_PATH24* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
23* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake`25* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake`
24 26
25###Step 5. Installing OpenSSL27### Step 5. Installing OpenSSL
26 28
27Download and install the [Win64 OpenSSL v1.0.2k Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2k.exe).29Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe).
28 30
29###Step 6. Running CMake to Generate Build Files31### Step 6. Running CMake to Generate Build Files
30 32
31Run Command Prompt from Start and run the following commands:33Run Command Prompt from Start and run the following commands:
32 cd "%HIFI_DIR%"34````
33 mkdir build35cd "%HIFI_DIR%"
34 cd build36mkdir build
35 cmake .. -G "Visual Studio 12 Win64"37cd build
38cmake .. -G "Visual Studio 12 Win64"
39````
36 40
37Where %HIFI_DIR% is the directory for the highfidelity repository. 41Where `%HIFI_DIR%` is the directory for the highfidelity repository.
38 42
39###Step 7. Making a Build43### Step 7. Making a Build
40 44
41Open '%HIFI_DIR%\build\hifi.sln' using Visual Studio.45Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
42 46
43Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance.47Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance.
44 48
45Run Build > Build Solution.49Run `Build > Build Solution`.
46 50
47###Step 8. Testing Interface51### Step 8. Testing Interface
48 52
49Create another environment variable (see Step #4)53Create another environment variable (see Step #4)
50* Set "Variable name": _NO_DEBUG_HEAP54* Set "Variable name": `_NO_DEBUG_HEAP`
51* Set "Variable value": 155* Set "Variable value": `1`
52 56
53In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run Debug > Start Debugging.57In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run `Debug > Start Debugging`.
54 58
55Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow.59Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow.
56 60
57Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Release\interface.exe61Note: You can also run Interface by launching it from command line or File Explorer from `%HIFI_DIR%\build\interface\Release\interface.exe`
58 62
59###Troubleshooting63## Troubleshooting
60 64
61For any problems after Step #6, first try this: 65For any problems after Step #6, first try this:
62* Delete your locally cloned copy of the highfidelity repository66* Delete your locally cloned copy of the highfidelity repository
@@ -64,18 +68,18 @@ For any problems after Step #6, first try this: · 1 unmodified line
64* Redownload the [repository](https://github.com/highfidelity/hifi) 68* Redownload the [repository](https://github.com/highfidelity/hifi)
65* Restart directions from Step #669* Restart directions from Step #6
66 70
67####CMake gives you the same error message repeatedly after the build fails71#### CMake gives you the same error message repeatedly after the build fails
68 72
69Remove `CMakeCache.txt` found in the '%HIFI_DIR%\build' directory73Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory.
70 74
71####nmake cannot be found75#### nmake cannot be found
72 76
73Make sure nmake.exe is located at the following path:77Make sure nmake.exe is located at the following path:
78
74 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin79 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin
75 80
76If not, add the directory where nmake is located to the PATH environment variable.81If not, add the directory where nmake is located to the PATH environment variable.
77 82
78####Qt is throwing an error83#### Qt is throwing an error
79
80Make sure you have the correct version (5.6.2) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly.
81 84
85Make sure you have the correct version (5.6.2) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.
INSTALL.md+4−4
@@ -2,15 +2,15 @@ Follow the [build guide](BUILD.md) to figure out how to build High Fidelity for · 1 unmodified line
2 2
3During generation, CMake should produce an `install` target and a `package` target.3During generation, CMake should produce an `install` target and a `package` target.
4 4
5###Install5### Install
6 6
7The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.7The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
8 8
9###Packaging9### Packaging
10 10
11To produce an installer, run the `package` target.11To produce an installer, run the `package` target.
12 12
13####Windows13#### Windows
14 14
15To produce an executable installer on Windows, the following are required:15To produce an executable installer on Windows, the following are required:
16 16
@@ -20,6 +20,6 @@ To produce an executable installer on Windows, the following are required: · 3 unmodified lines
20 20
21Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.21Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.
22 22
23####OS X23#### OS X
24 24
25Run the `package` target to create an Apple Disk Image (.dmg).25Run the `package` target to create an Apple Disk Image (.dmg).
assignment-client/src/Agent.cpp+2−5
@@ -166,11 +166,7 @@ void Agent::run() { · 165 unmodified lines
166 166
167 // Setup MessagesClient167 // Setup MessagesClient
168 auto messagesClient = DependencyManager::set<MessagesClient>();168 auto messagesClient = DependencyManager::set<MessagesClient>();
169 QThread* messagesThread = new QThread;169 messagesClient->startThread();
170 messagesThread->setObjectName("Messages Client Thread");
171 messagesClient->moveToThread(messagesThread);
172 connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
173 messagesThread->start();
174 170
175 // make sure we hear about connected nodes so we can grab an ATP script if a request is pending171 // make sure we hear about connected nodes so we can grab an ATP script if a request is pending
176 connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated);172 connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated);
@@ -410,6 +406,7 @@ void Agent::executeScript() { · 233 unmodified lines
410 bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened406 bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
411 bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed407 bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed
412 _audioGateOpen = audioGateOpen;408 _audioGateOpen = audioGateOpen;
409 Q_UNUSED(openedInLastBlock);
413 410
414 // the codec must be flushed to silence before sending silent packets,411 // the codec must be flushed to silence before sending silent packets,
415 // so delay the transition to silent packets by one packet after becoming silent.412 // so delay the transition to silent packets by one packet after becoming silent.
assignment-client/src/AssignmentClient.cpp+1−17
@@ -69,17 +69,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri · 68 unmodified lines
69 DependencyManager::set<ResourceScriptingInterface>();69 DependencyManager::set<ResourceScriptingInterface>();
70 DependencyManager::set<UserActivityLoggerScriptingInterface>();70 DependencyManager::set<UserActivityLoggerScriptingInterface>();
71 71
72 // setup a thread for the NodeList and its PacketReceiver72 nodeList->startThread();
73 QThread* nodeThread = new QThread(this);
74 nodeThread->setObjectName("NodeList Thread");
75 nodeThread->start();
76
77 // make sure the node thread is given highest priority
78 nodeThread->setPriority(QThread::TimeCriticalPriority);
79
80 // put the NodeList on the node thread
81 nodeList->moveToThread(nodeThread);
82
83 // set the logging target to the the CHILD_TARGET_NAME73 // set the logging target to the the CHILD_TARGET_NAME
84 LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);74 LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
85 75
@@ -166,14 +156,8 @@ void AssignmentClient::stopAssignmentClient() { · 80 unmodified lines
166}156}
167 157
168AssignmentClient::~AssignmentClient() {158AssignmentClient::~AssignmentClient() {
169 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
170
171 // remove the NodeList from the DependencyManager159 // remove the NodeList from the DependencyManager
172 DependencyManager::destroy<NodeList>();160 DependencyManager::destroy<NodeList>();
173
174 // ask the node thread to quit and wait until it is done
175 nodeThread->quit();
176 nodeThread->wait();
177}161}
178 162
179void AssignmentClient::aboutToQuit() {163void AssignmentClient::aboutToQuit() {
assignment-client/src/audio/AudioMixerSlavePool.h+2−2
@@ -16,10 +16,10 @@ · 15 unmodified lines
16#include <mutex>16#include <mutex>
17#include <vector>17#include <vector>
18 18
19#include <tbb/concurrent_queue.h>
20
21#include <QThread>19#include <QThread>
22 20
21#include <TBBHelpers.h>
22
23#include "AudioMixerSlave.h"23#include "AudioMixerSlave.h"
24 24
25class AudioMixerSlavePool;25class AudioMixerSlavePool;
assignment-client/src/avatars/AvatarMixerSlavePool.h+1−2
@@ -16,10 +16,9 @@ · 15 unmodified lines
16#include <mutex>16#include <mutex>
17#include <vector>17#include <vector>
18 18
19#include <tbb/concurrent_queue.h>
20
21#include <QThread>19#include <QThread>
22 20
21#include <TBBHelpers.h>
23#include <NodeList.h>22#include <NodeList.h>
24 23
25#include "AvatarMixerSlave.h"24#include "AvatarMixerSlave.h"
assignment-client/src/scripts/EntityScriptServer.cpp+1−5
@@ -236,11 +236,7 @@ void EntityScriptServer::run() { · 235 unmodified lines
236 236
237 // Setup MessagesClient237 // Setup MessagesClient
238 auto messagesClient = DependencyManager::set<MessagesClient>();238 auto messagesClient = DependencyManager::set<MessagesClient>();
239 QThread* messagesThread = new QThread;239 messagesClient->startThread();
240 messagesThread->setObjectName("Messages Client Thread");
241 messagesClient->moveToThread(messagesThread);
242 connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
243 messagesThread->start();
244 240
245 DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();241 DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
246 connect(&domainHandler, &DomainHandler::settingsReceived, this, &EntityScriptServer::handleSettings);242 connect(&domainHandler, &DomainHandler::settingsReceived, this, &EntityScriptServer::handleSettings);
cmake/externals/nvtt/CMakeLists.txt+4−4
@@ -8,8 +8,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) · 7 unmodified lines
8if (WIN32)8if (WIN32)
9 ExternalProject_Add(9 ExternalProject_Add(
10 ${EXTERNAL_NAME}10 ${EXTERNAL_NAME}
11 URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.zip11 URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.hifi.zip
12 URL_MD5 3ea6eeadbcc69071acf9c49ba565760e12 URL_MD5 10da01cf601f88f6dc12a6bc13c89136
13 CONFIGURE_COMMAND ""13 CONFIGURE_COMMAND ""
14 BUILD_COMMAND ""14 BUILD_COMMAND ""
15 INSTALL_COMMAND ""15 INSTALL_COMMAND ""
@@ -29,8 +29,8 @@ else () · 13 unmodified lines
29 29
30 ExternalProject_Add(30 ExternalProject_Add(
31 ${EXTERNAL_NAME}31 ${EXTERNAL_NAME}
32 URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.zip32 URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi.zip
33 URL_MD5 81b8fa6a9ee3f986088eb6e2215d6a5733 URL_MD5 5794b950f8b265a9a41b2839b3bf7ebb
34 CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>34 CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
35 LOG_DOWNLOAD 135 LOG_DOWNLOAD 1
36 LOG_CONFIGURE 136 LOG_CONFIGURE 1
domain-server/resources/web/content/index.shtml+1−1
@@ -21,7 +21,7 @@ · 20 unmodified lines
21 </p>21 </p>
22 <p>22 <p>
23 If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:<br>23 If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:<br>
24 <pre>C:\Users\[username]\AppData\Roaming\High Fidelity\assignment-client/entities/models.json.gz</pre>24 <pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
25 <pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>25 <pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
26 <pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>26 <pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
27 </p>27 </p>
gvr-interface/src/RenderingClient.cpp+2−10
@@ -37,20 +37,12 @@ RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString · 36 unmodified lines
37 DependencyManager::set<AvatarHashMap>();37 DependencyManager::set<AvatarHashMap>();
38 38
39 // get our audio client setup on its own thread39 // get our audio client setup on its own thread
40 QThread* audioThread = new QThread();
41 auto audioClient = DependencyManager::set<AudioClient>();40 auto audioClient = DependencyManager::set<AudioClient>();
42
43 audioClient->setPositionGetter(getPositionForAudio);41 audioClient->setPositionGetter(getPositionForAudio);
44 audioClient->setOrientationGetter(getOrientationForAudio);42 audioClient->setOrientationGetter(getOrientationForAudio);
43 audioClient->startThread();
45 44
46 audioClient->moveToThread(audioThread);45
47 connect(audioThread, &QThread::started, audioClient.data(), &AudioClient::start);
48 connect(audioClient.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
49 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
50
51 audioThread->start();
52
53
54 connect(&_avatarTimer, &QTimer::timeout, this, &RenderingClient::sendAvatarPacket);46 connect(&_avatarTimer, &QTimer::timeout, this, &RenderingClient::sendAvatarPacket);
55 _avatarTimer.setInterval(16); // 60 FPS47 _avatarTimer.setInterval(16); // 60 FPS
56 _avatarTimer.start();48 _avatarTimer.start();
interface/resources/avatar/avatar-animation.json+3−3
@@ -103,8 +103,8 @@ · 102 unmodified lines
103 "rotationVar": "spine2Rotation",103 "rotationVar": "spine2Rotation",
104 "typeVar": "spine2Type",104 "typeVar": "spine2Type",
105 "weightVar": "spine2Weight",105 "weightVar": "spine2Weight",
106 "weight": 1.0,106 "weight": 2.0,
107 "flexCoefficients": [1.0, 0.5, 0.5]107 "flexCoefficients": [1.0, 0.5, 0.25]
108 },108 },
109 {109 {
110 "jointName": "Head",110 "jointName": "Head",
@@ -113,7 +113,7 @@ · 2 unmodified lines
113 "typeVar": "headType",113 "typeVar": "headType",
114 "weightVar": "headWeight",114 "weightVar": "headWeight",
115 "weight": 4.0,115 "weight": 4.0,
116 "flexCoefficients": [1, 0.05, 0.25, 0.25, 0.25]116 "flexCoefficients": [1, 0.5, 0.25, 0.2, 0.1]
117 },117 },
118 {118 {
119 "jointName": "LeftArm",119 "jointName": "LeftArm",
interface/resources/config/render.json+9−7
@@ -1,14 +1,16 @@
1{1{
2 "RenderShadowTask": {2 "RenderMainView": {
3 "Enabled": {3 "RenderShadowTask": {
4 "enabled": true
5 }
6 },
7 "RenderDeferredTask": {
8 "AmbientOcclusion": {
9 "Enabled": {4 "Enabled": {
10 "enabled": true5 "enabled": true
11 }6 }
7 },
8 "RenderDeferredTask": {
9 "AmbientOcclusion": {
10 "Enabled": {
11 "enabled": true
12 }
13 }
12 }14 }
13 }15 }
14}16}
interface/resources/controllers/oculus_touch.json+3−1
@@ -57,6 +57,8 @@ · 56 unmodified lines
57 { "from": "OculusTouch.LeftThumbUp", "to": "Standard.LeftThumbUp" },57 { "from": "OculusTouch.LeftThumbUp", "to": "Standard.LeftThumbUp" },
58 { "from": "OculusTouch.RightThumbUp", "to": "Standard.RightThumbUp" },58 { "from": "OculusTouch.RightThumbUp", "to": "Standard.RightThumbUp" },
59 { "from": "OculusTouch.LeftIndexPoint", "to": "Standard.LeftIndexPoint" },59 { "from": "OculusTouch.LeftIndexPoint", "to": "Standard.LeftIndexPoint" },
60 { "from": "OculusTouch.RightIndexPoint", "to": "Standard.RightIndexPoint" }60 { "from": "OculusTouch.RightIndexPoint", "to": "Standard.RightIndexPoint" },
61
62 { "from": "OculusTouch.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] }
61 ]63 ]
62}64}
interface/resources/controllers/vive.json+8−8
@@ -35,35 +35,35 @@ · 34 unmodified lines
35 { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },35 { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },
36 36
37 { "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] },37 { "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] },
38 { "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] },38 { "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] },
39 39
40 {40 {
41 "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",41 "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
42 "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],42 "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
43 "when": [ "Application.InHMD"]43 "when": [ "Application.InHMD" ]
44 },44 },
45 45
46 {46 {
47 "from": "Vive.RightFoot", "to" : "Standard.RightFoot",47 "from": "Vive.RightFoot", "to" : "Standard.RightFoot",
48 "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],48 "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
49 "when": [ "Application.InHMD"]49 "when": [ "Application.InHMD" ]
50 },50 },
51 51
52 {52 {
53 "from": "Vive.Hips", "to" : "Standard.Hips",53 "from": "Vive.Hips", "to" : "Standard.Hips",
54 "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],54 "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
55 "when": [ "Application.InHMD"]55 "when": [ "Application.InHMD" ]
56 },56 },
57 57
58 {58 {
59 "from": "Vive.Spine2", "to" : "Standard.Spine2",59 "from": "Vive.Spine2", "to" : "Standard.Spine2",
60 "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],60 "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
61 "when": [ "Application.InHMD"]61 "when": [ "Application.InHMD" ]
62 },62 },
63 63
64 { "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] },64 { "from": "Vive.Head", "to" : "Standard.Head", "when": [ "Application.InHMD" ] },
65 65
66 { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when" : [ "Application.InHMD"] },66 { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when": [ "Application.InHMD" ] },
67 { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when" : [ "Application.InHMD"] }67 { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] }
68 ]68 ]
69}69}
interface/resources/fonts/hifi-glyphs.ttf+0−0

Binary file not shown.

interface/resources/html/createGlobalEventBridge.js+1−1
@@ -32,7 +32,7 @@ var EventBridge; · 31 unmodified lines
32 var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) {32 var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) {
33 // replace the TempEventBridge with the real one.33 // replace the TempEventBridge with the real one.
34 var tempEventBridge = EventBridge;34 var tempEventBridge = EventBridge;
35 EventBridge = channel.objects.eventBridgeWrapper.eventBridge;35 EventBridge = channel.objects.eventBridge;
36 tempEventBridge._callbacks.forEach(function (callback) {36 tempEventBridge._callbacks.forEach(function (callback) {
37 EventBridge.scriptEventReceived.connect(callback);37 EventBridge.scriptEventReceived.connect(callback);
38 });38 });
interface/resources/meshes/Jointy3/Jointy3.fbx+0−0

Binary file not shown.

interface/resources/meshes/defaultAvatar_full.fst+73−69
@@ -1,85 +1,89 @@
1name = Jointy31name = mannequin
2type = body+head2type = body+head
3scale = 13scale = 1
4filename = Jointy3/Jointy3.fbx4filename = mannequin/mannequin.baked.fbx
5texdir = Jointy3/textures5joint = jointEyeLeft = LeftEye
6joint = jointRightHand = RightHand
7joint = jointHead = Head
8joint = jointEyeRight = RightEye
9joint = jointLean = Spine
6joint = jointNeck = Neck10joint = jointNeck = Neck
7joint = jointLeftHand = LeftHand11joint = jointLeftHand = LeftHand
8joint = jointEyeRight = RightEye
9joint = jointHead = Head
10joint = jointRightHand = RightHand
11joint = jointRoot = Hips12joint = jointRoot = Hips
12joint = jointLean = Spine
13joint = jointEyeLeft = LeftEye
14freeJoint = LeftArm13freeJoint = LeftArm
15freeJoint = LeftForeArm14freeJoint = LeftForeArm
16freeJoint = RightArm15freeJoint = RightArm
17freeJoint = RightForeArm16freeJoint = RightForeArm
18jointIndex = RightHand = 1717bs = EyeBlink_L = blink = 1
19jointIndex = LeftHandIndex3 = 5618bs = JawOpen = mouth_Open = 1
20jointIndex = Hips = 019bs = LipsFunnel = Oo = 1
21jointIndex = LeftHandRing2 = 4720bs = BrowsU_L = brow_Up = 1
22jointIndex = LeftHandThumb3 = 6021jointIndex = RightHandIndex2 = 27
23jointIndex = RightShoulder = 1422jointIndex = LeftHandIndex2 = 51
24jointIndex = RightHandRing1 = 3023jointIndex = RightUpLeg = 6
25jointIndex = RightHandRing3 = 3224jointIndex = RightToe_End = 10
26jointIndex = LeftHandPinky4 = 4525jointIndex = RightEye = 65
27jointIndex = LeftHandRing1 = 4626jointIndex = LeftHandPinky1 = 42
28jointIndex = LeftFoot = 827jointIndex = RightHandRing1 = 22
29jointIndex = RightHandIndex2 = 2328jointIndex = face = 67
30jointIndex = RightToeBase = 429jointIndex = LeftUpLeg = 1
31jointIndex = RightHandMiddle4 = 29
32jointIndex = RightHandPinky4 = 37
33jointIndex = LeftToe_End = 10
34jointIndex = RightEye = 66
35jointIndex = RightHandPinky2 = 35
36jointIndex = RightHandRing2 = 31
37jointIndex = LeftHand = 4130jointIndex = LeftHand = 41
38jointIndex = RightToe_End = 531jointIndex = LeftHandMiddle1 = 58
39jointIndex = LeftEye = 6532jointIndex = LeftHandIndex1 = 50
40jointIndex = LeftHandThumb2 = 5933jointIndex = LeftEye = 64
41jointIndex = pCylinder73Shape1 = 6734jointIndex = RightHandIndex1 = 26
35jointIndex = LeftHandPinky4 = 45
36jointIndex = RightArm = 15
42jointIndex = LeftShoulder = 3837jointIndex = LeftShoulder = 38
43jointIndex = LeftHandIndex2 = 5538jointIndex = RightHandPinky2 = 19
39jointIndex = RightHandThumb1 = 30
44jointIndex = RightForeArm = 1640jointIndex = RightForeArm = 16
45jointIndex = LeftHandMiddle2 = 5141jointIndex = LeftHandMiddle3 = 60
46jointIndex = RightHandRing4 = 33
47jointIndex = LeftLeg = 7
48jointIndex = LeftHandThumb4 = 61
49jointIndex = LeftForeArm = 40
50jointIndex = HeadTop_End = 64
51jointIndex = RightHandPinky1 = 34
52jointIndex = RightHandIndex1 = 22
53jointIndex = LeftHandIndex1 = 54
54jointIndex = RightLeg = 2
55jointIndex = RightHandIndex4 = 25
56jointIndex = Neck = 6242jointIndex = Neck = 62
57jointIndex = LeftHandMiddle1 = 5043jointIndex = LeftHandThumb1 = 54
58jointIndex = RightHandPinky3 = 3644jointIndex = RightHandMiddle2 = 35
59jointIndex = LeftHandPinky2 = 4345jointIndex = LeftHandMiddle4 = 61
60jointIndex = RightHandMiddle3 = 2846jointIndex = mannequin = 68
61jointIndex = RightHandThumb4 = 2147jointIndex = Spine1 = 12
62jointIndex = LeftUpLeg = 648jointIndex = RightFoot = 8
63jointIndex = RightFoot = 349jointIndex = RightHand = 17
64jointIndex = LeftHandThumb1 = 5850jointIndex = LeftHandIndex3 = 52
65jointIndex = LeftArm = 3951jointIndex = RightHandIndex3 = 28
66jointIndex = RightHandMiddle1 = 2652jointIndex = RightHandMiddle4 = 37
67jointIndex = LeftHandRing3 = 4853jointIndex = LeftLeg = 2
68jointIndex = LeftHandMiddle4 = 5354jointIndex = RightHandMiddle1 = 34
69jointIndex = RightUpLeg = 1
70jointIndex = RightHandMiddle2 = 27
71jointIndex = LeftToeBase = 9
72jointIndex = RightHandThumb2 = 19
73jointIndex = Spine2 = 1355jointIndex = Spine2 = 13
74jointIndex = Spine = 1156jointIndex = LeftHandMiddle2 = 59
57jointIndex = LeftHandPinky3 = 44
58jointIndex = LeftHandThumb3 = 56
75jointIndex = LeftHandRing4 = 4959jointIndex = LeftHandRing4 = 49
60jointIndex = RightHandThumb2 = 31
61jointIndex = LeftHandRing3 = 48
62jointIndex = HeadTop_End = 66
63jointIndex = LeftHandThumb4 = 57
64jointIndex = RightHandThumb3 = 32
65jointIndex = RightHandPinky1 = 18
66jointIndex = RightLeg = 7
67jointIndex = RightHandMiddle3 = 36
68jointIndex = RightHandPinky3 = 20
69jointIndex = LeftToeBase = 4
70jointIndex = LeftForeArm = 40
71jointIndex = RightShoulder = 14
72jointIndex = LeftHandRing2 = 47
73jointIndex = LeftHandThumb2 = 55
76jointIndex = Head = 6374jointIndex = Head = 63
77jointIndex = LeftHandPinky3 = 4475jointIndex = RightHandRing4 = 25
78jointIndex = LeftHandPinky1 = 4276jointIndex = LeftHandRing1 = 46
79jointIndex = RightHandThumb1 = 1877jointIndex = LeftFoot = 3
80jointIndex = LeftHandIndex4 = 5778jointIndex = RightHandRing3 = 24
81jointIndex = LeftHandMiddle3 = 5279jointIndex = RightHandThumb4 = 33
82jointIndex = RightHandIndex3 = 2480jointIndex = LeftArm = 39
83jointIndex = Spine1 = 1281jointIndex = LeftToe_End = 5
84jointIndex = RightArm = 1582jointIndex = RightToeBase = 9
85jointIndex = RightHandThumb3 = 2083jointIndex = RightHandPinky4 = 21
84jointIndex = Spine = 11
85jointIndex = LeftHandIndex4 = 53
86jointIndex = LeftHandPinky2 = 43
87jointIndex = RightHandIndex4 = 29
88jointIndex = Hips = 0
89jointIndex = RightHandRing2 = 23
interface/resources/meshes/mannequin/Eyes.ktx+0−0

Binary file not shown.

interface/resources/meshes/mannequin/lambert1_Base_Color.ktx+0−0

Binary file not shown.

interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx+0−0

Binary file not shown.

interface/resources/meshes/mannequin/lambert1_Roughness.ktx+0−0

Binary file not shown.

interface/resources/meshes/mannequin/mannequin.baked.fbx+0−0

Binary file not shown.

interface/resources/qml/AssetServer.qml+1−1
@@ -177,7 +177,7 @@ ScrollingWindow { · 176 unmodified lines
177 SHAPE_TYPE_STATIC_MESH177 SHAPE_TYPE_STATIC_MESH
178 ],178 ],
179 checkStateOnDisable: false,179 checkStateOnDisable: false,
180 warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic"180 warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors"
181 }181 }
182 });182 });
183 183
interface/resources/qml/Browser.qml+3−18
@@ -2,7 +2,6 @@ import QtQuick 2.5 · 1 unmodified line
2import QtQuick.Controls 1.22import QtQuick.Controls 1.2
3import QtWebChannel 1.03import QtWebChannel 1.0
4import QtWebEngine 1.24import QtWebEngine 1.2
5import FileTypeProfile 1.0
6 5
7import "controls-uit"6import "controls-uit"
8import "styles" as HifiStyles7import "styles" as HifiStyles
@@ -22,8 +21,6 @@ ScrollingWindow { · 13 unmodified lines
22 property alias url: webview.url21 property alias url: webview.url
23 property alias webView: webview22 property alias webView: webview
24 23
25 property alias eventBridge: eventBridgeWrapper.eventBridge
26
27 signal loadingChanged(int status)24 signal loadingChanged(int status)
28 25
29 x: 10026 x: 100
@@ -209,21 +206,7 @@ ScrollingWindow { · 179 unmodified lines
209 WebView {206 WebView {
210 id: webview207 id: webview
211 url: "https://highfidelity.com/"208 url: "https://highfidelity.com/"
212 209 profile: FileTypeProfile;
213 property alias eventBridgeWrapper: eventBridgeWrapper
214
215 QtObject {
216 id: eventBridgeWrapper
217 WebChannel.id: "eventBridgeWrapper"
218 property var eventBridge;
219 }
220
221 profile: FileTypeProfile {
222 id: webviewProfile
223 storageName: "qmlWebEngine"
224 }
225
226 webChannel.registeredObjects: [eventBridgeWrapper]
227 210
228 // Create a global EventBridge object for raiseAndLowerKeyboard.211 // Create a global EventBridge object for raiseAndLowerKeyboard.
229 WebEngineScript {212 WebEngineScript {
@@ -271,6 +254,8 @@ ScrollingWindow { · 41 unmodified lines
271 }254 }
272 255
273 Component.onCompleted: {256 Component.onCompleted: {
257 webChannel.registerObject("eventBridge", eventBridge);
258 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
274 desktop.initWebviewProfileHandlers(webview.profile);259 desktop.initWebviewProfileHandlers(webview.profile);
275 }260 }
276 }261 }
interface/resources/qml/QmlWebWindow.qml+2−8
@@ -26,15 +26,8 @@ Windows.ScrollingWindow { · 25 unmodified lines
26 // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer26 // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
27 destroyOnCloseButton: false27 destroyOnCloseButton: false
28 property alias source: webview.url28 property alias source: webview.url
29 property alias eventBridge: eventBridgeWrapper.eventBridge;
30 property alias scriptUrl: webview.userScriptUrl29 property alias scriptUrl: webview.userScriptUrl
31 30
32 QtObject {
33 id: eventBridgeWrapper
34 WebChannel.id: "eventBridgeWrapper"
35 property var eventBridge;
36 }
37
38 // This is for JS/QML communication, which is unused in a WebWindow,31 // This is for JS/QML communication, which is unused in a WebWindow,
39 // but not having this here results in spurious warnings about a 32 // but not having this here results in spurious warnings about a
40 // missing signal33 // missing signal
@@ -70,7 +63,6 @@ Windows.ScrollingWindow { · 29 unmodified lines
70 url: "about:blank"63 url: "about:blank"
71 anchors.fill: parent64 anchors.fill: parent
72 focus: true65 focus: true
73 webChannel.registeredObjects: [eventBridgeWrapper]
74 66
75 property string userScriptUrl: ""67 property string userScriptUrl: ""
76 68
@@ -107,6 +99,8 @@ Windows.ScrollingWindow { · 30 unmodified lines
107 }99 }
108 100
109 Component.onCompleted: {101 Component.onCompleted: {
102 webChannel.registerObject("eventBridge", eventBridge);
103 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
110 eventBridge.webEventReceived.connect(onWebEventReceived);104 eventBridge.webEventReceived.connect(onWebEventReceived);
111 }105 }
112 }106 }
interface/resources/qml/QmlWindow.qml+0−9
@@ -30,15 +30,6 @@ Windows.Window { · 29 unmodified lines
30 property bool keyboardRaised: false30 property bool keyboardRaised: false
31 property bool punctuationMode: false31 property bool punctuationMode: false
32 32
33 // JavaScript event bridge object in case QML content includes Web content.
34 property alias eventBridge: eventBridgeWrapper.eventBridge;
35
36 QtObject {
37 id: eventBridgeWrapper
38 WebChannel.id: "eventBridgeWrapper"
39 property var eventBridge;
40 }
41
42 onSourceChanged: {33 onSourceChanged: {
43 if (dynamicContent) {34 if (dynamicContent) {
44 dynamicContent.destroy();35 dynamicContent.destroy();
interface/resources/qml/TabletBrowser.qml+4−14
@@ -8,7 +8,6 @@ import "controls-uit" as HifiControls · 7 unmodified lines
8import "styles" as HifiStyles8import "styles" as HifiStyles
9import "styles-uit"9import "styles-uit"
10import "windows"10import "windows"
11import HFTabletWebEngineProfile 1.0
12 11
13Item {12Item {
14 id: root13 id: root
@@ -19,7 +18,6 @@ Item { · 4 unmodified lines
19 property variant permissionsBar: {'securityOrigin':'none','feature':'none'}18 property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
20 property alias url: webview.url19 property alias url: webview.url
21 property WebEngineView webView: webview20 property WebEngineView webView: webview
22 property alias eventBridge: eventBridgeWrapper.eventBridge
23 property bool canGoBack: webview.canGoBack21 property bool canGoBack: webview.canGoBack
24 property bool canGoForward: webview.canGoForward22 property bool canGoForward: webview.canGoForward
25 23
@@ -33,12 +31,6 @@ Item { · 7 unmodified lines
33 webview.profile = profile;31 webview.profile = profile;
34 }32 }
35 33
36 QtObject {
37 id: eventBridgeWrapper
38 WebChannel.id: "eventBridgeWrapper"
39 property var eventBridge;
40 }
41
42 WebEngineView {34 WebEngineView {
43 id: webview35 id: webview
44 objectName: "webEngineView"36 objectName: "webEngineView"
@@ -47,10 +39,7 @@ Item { · 2 unmodified lines
47 width: parent.width39 width: parent.width
48 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height40 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
49 41
50 profile: HFTabletWebEngineProfile {42 profile: HFTabletWebEngineProfile;
51 id: webviewTabletProfile
52 storageName: "qmlTabletWebEngine"
53 }
54 43
55 property string userScriptUrl: ""44 property string userScriptUrl: ""
56 45
@@ -82,9 +71,10 @@ Item { · 25 unmodified lines
82 71
83 property string newUrl: ""72 property string newUrl: ""
84 73
85 webChannel.registeredObjects: [eventBridgeWrapper]
86
87 Component.onCompleted: {74 Component.onCompleted: {
75 webChannel.registerObject("eventBridge", eventBridge);
76 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
77
88 // Ensure the JS from the web-engine makes it to our logging78 // Ensure the JS from the web-engine makes it to our logging
89 webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {79 webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
90 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);80 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
interface/resources/qml/ToolWindow.qml+3−10
@@ -79,15 +79,11 @@ ScrollingWindow { · 78 unmodified lines
79 id: webView79 id: webView
80 anchors.fill: parent80 anchors.fill: parent
81 enabled: false81 enabled: false
82 property alias eventBridgeWrapper: eventBridgeWrapper82 Component.onCompleted: {
83 83 webChannel.registerObject("eventBridge", eventBridge);
84 QtObject {84 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
85 id: eventBridgeWrapper
86 WebChannel.id: "eventBridgeWrapper"
87 property var eventBridge
88 }85 }
89 86
90 webChannel.registeredObjects: [eventBridgeWrapper]
91 onEnabledChanged: toolWindow.updateVisiblity()87 onEnabledChanged: toolWindow.updateVisiblity()
92 }88 }
93 }89 }
@@ -251,12 +247,9 @@ ScrollingWindow { · 157 unmodified lines
251 tab.enabled = true;247 tab.enabled = true;
252 tab.originalUrl = properties.source;248 tab.originalUrl = properties.source;
253 249
254 var eventBridge = properties.eventBridge;
255
256 var result = tab.item;250 var result = tab.item;
257 result.enabled = true;251 result.enabled = true;
258 tabView.tabCount++;252 tabView.tabCount++;
259 result.eventBridgeWrapper.eventBridge = eventBridge;
260 result.url = properties.source;253 result.url = properties.source;
261 return result;254 return result;
262 }255 }
interface/resources/qml/controls-uit/BaseWebView.qml+0−3
@@ -10,13 +10,10 @@ · 9 unmodified lines
10 10
11import QtQuick 2.511import QtQuick 2.5
12import QtWebEngine 1.212import QtWebEngine 1.2
13import HFWebEngineProfile 1.0
14 13
15WebEngineView {14WebEngineView {
16 id: root15 id: root
17 16
18 // profile: desktop.browserProfile
19
20 Component.onCompleted: {17 Component.onCompleted: {
21 console.log("Connecting JS messaging to Hifi Logging")18 console.log("Connecting JS messaging to Hifi Logging")
22 // Ensure the JS from the web-engine makes it to our logging19 // Ensure the JS from the web-engine makes it to our logging
interface/resources/qml/controls/TabletWebScreen.qml+4−14
@@ -2,12 +2,10 @@ import QtQuick 2.5 · 1 unmodified line
2import QtWebEngine 1.12import QtWebEngine 1.1
3import QtWebChannel 1.03import QtWebChannel 1.0
4import "../controls-uit" as HiFiControls4import "../controls-uit" as HiFiControls
5import HFTabletWebEngineProfile 1.0
6 5
7Item {6Item {
8 property alias url: root.url7 property alias url: root.url
9 property alias scriptURL: root.userScriptUrl8 property alias scriptURL: root.userScriptUrl
10 property alias eventBridge: eventBridgeWrapper.eventBridge
11 property alias canGoBack: root.canGoBack;9 property alias canGoBack: root.canGoBack;
12 property var goBack: root.goBack;10 property var goBack: root.goBack;
13 property alias urlTag: root.urlTag11 property alias urlTag: root.urlTag
@@ -23,12 +21,6 @@ Item { · 9 unmodified lines
23 }21 }
24 */22 */
25 23
26 QtObject {
27 id: eventBridgeWrapper
28 WebChannel.id: "eventBridgeWrapper"
29 property var eventBridge;
30 }
31
32 property alias viewProfile: root.profile24 property alias viewProfile: root.profile
33 25
34 WebEngineView {26 WebEngineView {
@@ -39,10 +31,7 @@ Item { · 4 unmodified lines
39 width: parent.width31 width: parent.width
40 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height32 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
41 33
42 profile: HFTabletWebEngineProfile {34 profile: HFTabletWebEngineProfile;
43 id: webviewProfile
44 storageName: "qmlTabletWebEngine"
45 }
46 35
47 property string userScriptUrl: ""36 property string userScriptUrl: ""
48 37
@@ -75,10 +64,11 @@ Item { · 26 unmodified lines
75 userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]64 userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
76 65
77 property string newUrl: ""66 property string newUrl: ""
78 67
79 webChannel.registeredObjects: [eventBridgeWrapper]
80 68
81 Component.onCompleted: {69 Component.onCompleted: {
70 webChannel.registerObject("eventBridge", eventBridge);
71 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
82 // Ensure the JS from the web-engine makes it to our logging72 // Ensure the JS from the web-engine makes it to our logging
83 root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {73 root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
84 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);74 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
interface/resources/qml/controls/TabletWebView.qml+3−14
@@ -2,7 +2,6 @@ import QtQuick 2.5 · 1 unmodified line
2import QtQuick.Controls 1.42import QtQuick.Controls 1.4
3import QtWebEngine 1.23import QtWebEngine 1.2
4import QtWebChannel 1.04import QtWebChannel 1.0
5import HFTabletWebEngineProfile 1.0
6import "../controls-uit" as HiFiControls5import "../controls-uit" as HiFiControls
7import "../styles" as HifiStyles6import "../styles" as HifiStyles
8import "../styles-uit"7import "../styles-uit"
@@ -18,7 +17,6 @@ Item { · 9 unmodified lines
18 property int headerHeight: 7017 property int headerHeight: 70
19 property string url18 property string url
20 property string scriptURL19 property string scriptURL
21 property alias eventBridge: eventBridgeWrapper.eventBridge
22 property bool keyboardEnabled: false20 property bool keyboardEnabled: false
23 property bool keyboardRaised: false21 property bool keyboardRaised: false
24 property bool punctuationMode: false22 property bool punctuationMode: false
@@ -136,12 +134,6 @@ Item { · 111 unmodified lines
136 loadUrl(url);134 loadUrl(url);
137 }135 }
138 136
139 QtObject {
140 id: eventBridgeWrapper
141 WebChannel.id: "eventBridgeWrapper"
142 property var eventBridge;
143 }
144
145 WebEngineView {137 WebEngineView {
146 id: webview138 id: webview
147 objectName: "webEngineView"139 objectName: "webEngineView"
@@ -150,10 +142,7 @@ Item { · 2 unmodified lines
150 width: parent.width142 width: parent.width
151 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight143 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
152 anchors.top: buttons.bottom144 anchors.top: buttons.bottom
153 profile: HFTabletWebEngineProfile {145 profile: HFTabletWebEngineProfile;
154 id: webviewTabletProfile
155 storageName: "qmlTabletWebEngine"
156 }
157 146
158 property string userScriptUrl: ""147 property string userScriptUrl: ""
159 148
@@ -186,9 +175,9 @@ Item { · 26 unmodified lines
186 175
187 property string newUrl: ""176 property string newUrl: ""
188 177
189 webChannel.registeredObjects: [eventBridgeWrapper]
190
191 Component.onCompleted: {178 Component.onCompleted: {
179 webChannel.registerObject("eventBridge", eventBridge);
180 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
192 // Ensure the JS from the web-engine makes it to our logging181 // Ensure the JS from the web-engine makes it to our logging
193 webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {182 webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
194 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);183 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
interface/resources/qml/controls/WebEntityView.qml+1−5
@@ -10,13 +10,9 @@ · 9 unmodified lines
10 10
11import QtQuick 2.511import QtQuick 2.5
12import "."12import "."
13import FileTypeProfile 1.0
14 13
15WebView {14WebView {
16 viewProfile: FileTypeProfile {15 viewProfile: FileTypeProfile;
17 id: webviewProfile
18 storageName: "qmlWebEngine"
19 }
20 16
21 urlTag: "noDownload=true";17 urlTag: "noDownload=true";
22}18}
interface/resources/qml/controls/WebView.qml+3−14
@@ -2,12 +2,10 @@ import QtQuick 2.5 · 1 unmodified line
2import QtWebEngine 1.12import QtWebEngine 1.1
3import QtWebChannel 1.03import QtWebChannel 1.0
4import "../controls-uit" as HiFiControls4import "../controls-uit" as HiFiControls
5import HFWebEngineProfile 1.0
6 5
7Item {6Item {
8 property alias url: root.url7 property alias url: root.url
9 property alias scriptURL: root.userScriptUrl8 property alias scriptURL: root.userScriptUrl
10 property alias eventBridge: eventBridgeWrapper.eventBridge
11 property alias canGoBack: root.canGoBack;9 property alias canGoBack: root.canGoBack;
12 property var goBack: root.goBack;10 property var goBack: root.goBack;
13 property alias urlTag: root.urlTag11 property alias urlTag: root.urlTag
@@ -23,12 +21,6 @@ Item { · 9 unmodified lines
23 }21 }
24 */22 */
25 23
26 QtObject {
27 id: eventBridgeWrapper
28 WebChannel.id: "eventBridgeWrapper"
29 property var eventBridge;
30 }
31
32 property alias viewProfile: root.profile24 property alias viewProfile: root.profile
33 25
34 WebEngineView {26 WebEngineView {
@@ -39,10 +31,7 @@ Item { · 4 unmodified lines
39 width: parent.width31 width: parent.width
40 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height32 height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
41 33
42 profile: HFWebEngineProfile {34 profile: HFWebEngineProfile;
43 id: webviewProfile
44 storageName: "qmlWebEngine"
45 }
46 35
47 property string userScriptUrl: ""36 property string userScriptUrl: ""
48 37
@@ -76,9 +65,9 @@ Item { · 27 unmodified lines
76 65
77 property string newUrl: ""66 property string newUrl: ""
78 67
79 webChannel.registeredObjects: [eventBridgeWrapper]
80
81 Component.onCompleted: {68 Component.onCompleted: {
69 webChannel.registerObject("eventBridge", eventBridge);
70 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
82 // Ensure the JS from the web-engine makes it to our logging71 // Ensure the JS from the web-engine makes it to our logging
83 root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {72 root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
84 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);73 console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
interface/resources/qml/dialogs/TabletLoginDialog.qml+0−1
@@ -20,7 +20,6 @@ TabletModalWindow { · 19 unmodified lines
20 id: loginDialogRoot20 id: loginDialogRoot
21 objectName: "LoginDialog"21 objectName: "LoginDialog"
22 22
23 property var eventBridge;
24 signal sendToScript(var message);23 signal sendToScript(var message);
25 property bool isHMD: false24 property bool isHMD: false
26 property bool gotoPreviousApp: false;25 property bool gotoPreviousApp: false;
interface/resources/qml/dialogs/preferences/AvatarBrowser.qml+4−12
@@ -24,8 +24,6 @@ Window { · 23 unmodified lines
24 resizable: true24 resizable: true
25 modality: Qt.ApplicationModal25 modality: Qt.ApplicationModal
26 26
27 property alias eventBridge: eventBridgeWrapper.eventBridge
28
29 Item {27 Item {
30 anchors.fill: parent28 anchors.fill: parent
31 29
@@ -45,16 +43,6 @@ Window { · 13 unmodified lines
45 bottom: keyboard.top43 bottom: keyboard.top
46 }44 }
47 45
48 property alias eventBridgeWrapper: eventBridgeWrapper
49
50 QtObject {
51 id: eventBridgeWrapper
52 WebChannel.id: "eventBridgeWrapper"
53 property var eventBridge;
54 }
55
56 webChannel.registeredObjects: [eventBridgeWrapper]
57
58 // Create a global EventBridge object for raiseAndLowerKeyboard.46 // Create a global EventBridge object for raiseAndLowerKeyboard.
59 WebEngineScript {47 WebEngineScript {
60 id: createGlobalEventBridge48 id: createGlobalEventBridge
@@ -73,6 +61,10 @@ Window { · 12 unmodified lines
73 61
74 userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]62 userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
75 63
64 Component.onCompleted: {
65 webChannel.registerObject("eventBridge", eventBridge);
66 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
67 }
76 }68 }
77 69
78 Keyboard {70 Keyboard {
interface/resources/qml/dialogs/preferences/AvatarPreference.qml+1−3
@@ -116,9 +116,7 @@ Preference { · 115 unmodified lines
116 116
117 Component {117 Component {
118 id: tabletAvatarBrowserBuilder;118 id: tabletAvatarBrowserBuilder;
119 TabletAvatarBrowser {119 TabletAvatarBrowser { }
120 eventBridge: tabletRoot.eventBridge
121 }
122 }120 }
123 121
124 }122 }
interface/resources/qml/hifi/Desktop.qml+0−6
@@ -2,7 +2,6 @@ import QtQuick 2.5 · 1 unmodified line
2import QtQuick.Controls 1.42import QtQuick.Controls 1.4
3import QtWebEngine 1.1;3import QtWebEngine 1.1;
4import Qt.labs.settings 1.04import Qt.labs.settings 1.0
5import HFWebEngineProfile 1.0
6 5
7import "../desktop" as OriginalDesktop6import "../desktop" as OriginalDesktop
8import ".."7import ".."
@@ -27,11 +26,6 @@ OriginalDesktop.Desktop { · 18 unmodified lines
27 property alias toolWindow: toolWindow26 property alias toolWindow: toolWindow
28 ToolWindow { id: toolWindow }27 ToolWindow { id: toolWindow }
29 28
30 property var browserProfile: HFWebEngineProfile {
31 id: webviewProfile
32 storageName: "qmlWebEngine"
33 }
34
35 Action {29 Action {
36 text: "Open Browser"30 text: "Open Browser"
37 shortcut: "Ctrl+B"31 shortcut: "Ctrl+B"
interface/resources/qml/hifi/Pal.qml+7−4
@@ -44,7 +44,6 @@ Rectangle { · 43 unmodified lines
44 property var activeTab: "nearbyTab";44 property var activeTab: "nearbyTab";
45 property bool currentlyEditingDisplayName: false45 property bool currentlyEditingDisplayName: false
46 property bool punctuationMode: false;46 property bool punctuationMode: false;
47 property var eventBridge;
48 47
49 HifiConstants { id: hifi; }48 HifiConstants { id: hifi; }
50 49
@@ -388,8 +387,13 @@ Rectangle { · 337 unmodified lines
388 sortIndicatorColumn: settings.nearbySortIndicatorColumn;387 sortIndicatorColumn: settings.nearbySortIndicatorColumn;
389 sortIndicatorOrder: settings.nearbySortIndicatorOrder;388 sortIndicatorOrder: settings.nearbySortIndicatorOrder;
390 onSortIndicatorColumnChanged: {389 onSortIndicatorColumnChanged: {
391 settings.nearbySortIndicatorColumn = sortIndicatorColumn;390 if (sortIndicatorColumn > 2) {
392 sortModel();391 // these are not sortable, switch back to last column
392 sortIndicatorColumn = settings.nearbySortIndicatorColumn;
393 } else {
394 settings.nearbySortIndicatorColumn = sortIndicatorColumn;
395 sortModel();
396 }
393 }397 }
394 onSortIndicatorOrderChanged: {398 onSortIndicatorOrderChanged: {
395 settings.nearbySortIndicatorOrder = sortIndicatorOrder;399 settings.nearbySortIndicatorOrder = sortIndicatorOrder;
@@ -1038,7 +1042,6 @@ Rectangle { · 642 unmodified lines
1038 } // Keyboard1042 } // Keyboard
1039 1043
1040 HifiControls.TabletWebView {1044 HifiControls.TabletWebView {
1041 eventBridge: pal.eventBridge;
1042 id: userInfoViewer;1045 id: userInfoViewer;
1043 anchors {1046 anchors {
1044 top: parent.top;1047 top: parent.top;
interface/resources/qml/hifi/dialogs/TabletAssetServer.qml+1−2
@@ -24,7 +24,6 @@ Rectangle { · 23 unmodified lines
24 property string title: "Asset Browser"24 property string title: "Asset Browser"
25 property bool keyboardRaised: false25 property bool keyboardRaised: false
26 26
27 property var eventBridge;
28 signal sendToScript(var message);27 signal sendToScript(var message);
29 property bool isHMD: false28 property bool isHMD: false
30 29
@@ -179,7 +178,7 @@ Rectangle { · 148 unmodified lines
179 SHAPE_TYPE_STATIC_MESH178 SHAPE_TYPE_STATIC_MESH
180 ],179 ],
181 checkStateOnDisable: false,180 checkStateOnDisable: false,
182 warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic"181 warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors"
183 }182 }
184 });183 });
185 184
interface/resources/qml/hifi/dialogs/TabletDCDialog.qml+0−1
@@ -20,7 +20,6 @@ Rectangle { · 19 unmodified lines
20 id: root20 id: root
21 objectName: "DCConectionTiming"21 objectName: "DCConectionTiming"
22 22
23 property var eventBridge;
24 signal sendToScript(var message);23 signal sendToScript(var message);
25 property bool isHMD: false24 property bool isHMD: false
26 25
interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml+0−1
@@ -19,7 +19,6 @@ Rectangle { · 18 unmodified lines
19 id: root19 id: root
20 20
21 objectName: "DebugWindow"21 objectName: "DebugWindow"
22 property var eventBridge;
23 22
24 property var title: "Debug Window"23 property var title: "Debug Window"
25 property bool isHMD: false24 property bool isHMD: false
interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml+0−1
@@ -20,7 +20,6 @@ Rectangle { · 19 unmodified lines
20 id: root20 id: root
21 objectName: "EntityStatistics"21 objectName: "EntityStatistics"
22 22
23 property var eventBridge;
24 signal sendToScript(var message);23 signal sendToScript(var message);
25 property bool isHMD: false24 property bool isHMD: false
26 25
interface/resources/qml/hifi/dialogs/TabletLODTools.qml+0−1
@@ -20,7 +20,6 @@ Rectangle { · 19 unmodified lines
20 id: root20 id: root
21 objectName: "LODTools"21 objectName: "LODTools"
22 22
23 property var eventBridge;
24 signal sendToScript(var message);23 signal sendToScript(var message);
25 property bool isHMD: false24 property bool isHMD: false
26 25
interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml+0−1
@@ -23,7 +23,6 @@ Rectangle { · 22 unmodified lines
23 property string title: "Running Scripts"23 property string title: "Running Scripts"
24 HifiConstants { id: hifi }24 HifiConstants { id: hifi }
25 signal sendToScript(var message);25 signal sendToScript(var message);
26 property var eventBridge;
27 property var scripts: ScriptDiscoveryService;26 property var scripts: ScriptDiscoveryService;
28 property var scriptsModel: scripts.scriptsModelFilter27 property var scriptsModel: scripts.scriptsModelFilter
29 property var runningScriptsModel: ListModel { }28 property var runningScriptsModel: ListModel { }
interface/resources/qml/hifi/tablet/Edit.qml+0−2
@@ -7,14 +7,12 @@ StackView { · 6 unmodified lines
7 objectName: "stack"7 objectName: "stack"
8 initialItem: Qt.resolvedUrl('EditTabView.qml')8 initialItem: Qt.resolvedUrl('EditTabView.qml')
9 9
10 property var eventBridge;
11 signal sendToScript(var message);10 signal sendToScript(var message);
12 11
13 HifiConstants { id: hifi }12 HifiConstants { id: hifi }
14 13
15 function pushSource(path) {14 function pushSource(path) {
16 editRoot.push(Qt.resolvedUrl(path));15 editRoot.push(Qt.resolvedUrl(path));
17 editRoot.currentItem.eventBridge = editRoot.eventBridge;
18 editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);16 editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
19 }17 }
20 18
interface/resources/qml/hifi/tablet/EditTabView.qml+1−6
@@ -5,7 +5,6 @@ import QtWebChannel 1.0 · 4 unmodified lines
5import QtQuick.Controls.Styles 1.45import QtQuick.Controls.Styles 1.4
6import "../../controls"6import "../../controls"
7import "../toolbars"7import "../toolbars"
8import HFWebEngineProfile 1.0
9import QtGraphicalEffects 1.08import QtGraphicalEffects 1.0
10import "../../controls-uit" as HifiControls9import "../../controls-uit" as HifiControls
11import "../../styles-uit"10import "../../styles-uit"
@@ -182,7 +181,6 @@ TabView { · 170 unmodified lines
182 WebView {181 WebView {
183 id: entityListToolWebView182 id: entityListToolWebView
184 url: "../../../../../scripts/system/html/entityList.html"183 url: "../../../../../scripts/system/html/entityList.html"
185 eventBridge: editRoot.eventBridge
186 anchors.fill: parent184 anchors.fill: parent
187 enabled: true185 enabled: true
188 }186 }
@@ -197,7 +195,6 @@ TabView { · 8 unmodified lines
197 WebView {195 WebView {
198 id: entityPropertiesWebView196 id: entityPropertiesWebView
199 url: "../../../../../scripts/system/html/entityProperties.html"197 url: "../../../../../scripts/system/html/entityProperties.html"
200 eventBridge: editRoot.eventBridge
201 anchors.fill: parent198 anchors.fill: parent
202 enabled: true199 enabled: true
203 }200 }
@@ -212,7 +209,6 @@ TabView { · 8 unmodified lines
212 WebView {209 WebView {
213 id: gridControlsWebView210 id: gridControlsWebView
214 url: "../../../../../scripts/system/html/gridControls.html"211 url: "../../../../../scripts/system/html/gridControls.html"
215 eventBridge: editRoot.eventBridge
216 anchors.fill: parent212 anchors.fill: parent
217 enabled: true213 enabled: true
218 }214 }
@@ -227,7 +223,6 @@ TabView { · 8 unmodified lines
227 WebView {223 WebView {
228 id: particleExplorerWebView224 id: particleExplorerWebView
229 url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"225 url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"
230 eventBridge: editRoot.eventBridge
231 anchors.fill: parent226 anchors.fill: parent
232 enabled: true227 enabled: true
233 }228 }
@@ -290,7 +285,7 @@ TabView { · 56 unmodified lines
290 editTabView.currentIndex = id;285 editTabView.currentIndex = id;
291 } else {286 } else {
292 console.warn('Attempt to switch to invalid tab:', id);287 console.warn('Attempt to switch to invalid tab:', id);
293 } 288 }
294 } else if (typeof id === 'string'){289 } else if (typeof id === 'string'){
295 switch (id.toLowerCase()) {290 switch (id.toLowerCase()) {
296 case 'create':291 case 'create':
interface/resources/qml/hifi/tablet/InputRecorder.qml+0−1
@@ -18,7 +18,6 @@ import "../../dialogs" · 17 unmodified lines
18 18
19Rectangle {19Rectangle {
20 id: inputRecorder20 id: inputRecorder
21 property var eventBridge;
22 HifiConstants { id: hifi }21 HifiConstants { id: hifi }
23 signal sendToScript(var message);22 signal sendToScript(var message);
24 color: hifi.colors.baseGray;23 color: hifi.colors.baseGray;
interface/resources/qml/hifi/tablet/NewModelDialog.qml+1−2
@@ -20,7 +20,6 @@ Rectangle { · 19 unmodified lines
20 // height: parent.height20 // height: parent.height
21 HifiConstants { id: hifi }21 HifiConstants { id: hifi }
22 color: hifi.colors.baseGray;22 color: hifi.colors.baseGray;
23 property var eventBridge;
24 signal sendToScript(var message);23 signal sendToScript(var message);
25 property bool keyboardEnabled: false24 property bool keyboardEnabled: false
26 property bool punctuationMode: false25 property bool punctuationMode: false
@@ -118,7 +117,7 @@ Rectangle { · 91 unmodified lines
118 id: text2117 id: text2
119 width: 160118 width: 160
120 color: "#ffffff"119 color: "#ffffff"
121 text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic")120 text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic, and should not be used as floors")
122 wrapMode: Text.WordWrap121 wrapMode: Text.WordWrap
123 font.pixelSize: 12122 font.pixelSize: 12
124 }123 }
interface/resources/qml/hifi/tablet/TabletAddressDialog.qml+0−2
@@ -29,7 +29,6 @@ StackView { · 28 unmodified lines
29 initialItem: addressBarDialog29 initialItem: addressBarDialog
30 width: parent !== null ? parent.width : undefined30 width: parent !== null ? parent.width : undefined
31 height: parent !== null ? parent.height : undefined31 height: parent !== null ? parent.height : undefined
32 property var eventBridge;
33 property int cardWidth: 212;32 property int cardWidth: 212;
34 property int cardHeight: 152;33 property int cardHeight: 152;
35 property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";34 property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
@@ -80,7 +79,6 @@ StackView { · 44 unmodified lines
80 var card = tabletWebView.createObject();79 var card = tabletWebView.createObject();
81 card.url = addressBarDialog.metaverseServerUrl + targetString;80 card.url = addressBarDialog.metaverseServerUrl + targetString;
82 card.parentStackItem = root;81 card.parentStackItem = root;
83 card.eventBridge = root.eventBridge;
84 root.push(card);82 root.push(card);
85 return;83 return;
86 }84 }
interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml+0−1
@@ -25,7 +25,6 @@ Item { · 24 unmodified lines
25 property bool keyboardRaised: false25 property bool keyboardRaised: false
26 property bool punctuationMode: false26 property bool punctuationMode: false
27 27
28 property var eventBridge;
29 signal sendToScript(var message);28 signal sendToScript(var message);
30 29
31 anchors.fill: parent30 anchors.fill: parent
interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml+1−0
@@ -20,6 +20,7 @@ StackView { · 19 unmodified lines
20 property string title: "Audio Buffers"20 property string title: "Audio Buffers"
21 property alias gotoPreviousApp: root.gotoPreviousApp;21 property alias gotoPreviousApp: root.gotoPreviousApp;
22 property var eventBridge;22 property var eventBridge;
23
23 signal sendToScript(var message);24 signal sendToScript(var message);
24 25
25 function pushSource(path) {26 function pushSource(path) {
interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml+0−1
@@ -19,7 +19,6 @@ StackView { · 18 unmodified lines
19 objectName: "stack"19 objectName: "stack"
20 property string title: "Avatar Settings"20 property string title: "Avatar Settings"
21 21
22 property var eventBridge;
23 signal sendToScript(var message);22 signal sendToScript(var message);
24 23
25 function pushSource(path) {24 function pushSource(path) {
interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml+0−1
@@ -19,7 +19,6 @@ StackView { · 18 unmodified lines
19 objectName: "stack"19 objectName: "stack"
20 property string title: "General Settings"20 property string title: "General Settings"
21 property alias gotoPreviousApp: root.gotoPreviousApp;21 property alias gotoPreviousApp: root.gotoPreviousApp;
22 property var eventBridge;
23 signal sendToScript(var message);22 signal sendToScript(var message);
24 23
25 function pushSource(path) {24 function pushSource(path) {
interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml+0−1
@@ -19,7 +19,6 @@ StackView { · 18 unmodified lines
19 objectName: "stack"19 objectName: "stack"
20 property string title: "Graphics Settings"20 property string title: "Graphics Settings"
21 21
22 property var eventBridge;
23 signal sendToScript(var message);22 signal sendToScript(var message);
24 23
25 function pushSource(path) {24 function pushSource(path) {
interface/resources/qml/hifi/tablet/TabletLodPreferences.qml+0−1
@@ -19,7 +19,6 @@ StackView { · 18 unmodified lines
19 objectName: "stack"19 objectName: "stack"
20 property string title: "LOD Settings"20 property string title: "LOD Settings"
21 21
22 property var eventBridge;
23 signal sendToScript(var message);22 signal sendToScript(var message);
24 23
25 function pushSource(path) {24 function pushSource(path) {
interface/resources/qml/hifi/tablet/TabletMenu.qml+0−2
@@ -4,7 +4,6 @@ import QtQuick.Controls 1.4 · 3 unmodified lines
4import QtQml 2.24import QtQml 2.2
5import QtWebChannel 1.05import QtWebChannel 1.0
6import QtWebEngine 1.16import QtWebEngine 1.1
7import HFWebEngineProfile 1.0
8 7
9 8
10import "."9import "."
@@ -22,7 +21,6 @@ FocusScope { · 11 unmodified lines
22 property var point: Qt.point(50, 50);21 property var point: Qt.point(50, 50);
23 TabletMenuStack { id: menuPopperUpper }22 TabletMenuStack { id: menuPopperUpper }
24 property string subMenu: ""23 property string subMenu: ""
25 property var eventBridge;
26 signal sendToScript(var message);24 signal sendToScript(var message);
27 25
28 Rectangle {26 Rectangle {
interface/resources/qml/hifi/tablet/TabletMenuStack.qml+0−1
@@ -49,7 +49,6 @@ Item { · 48 unmodified lines
49 49
50 function pushSource(path) {50 function pushSource(path) {
51 d.push(Qt.resolvedUrl(path));51 d.push(Qt.resolvedUrl(path));
52 d.currentItem.eventBridge = tabletMenu.eventBridge
53 d.currentItem.sendToScript.connect(tabletMenu.sendToScript);52 d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
54 d.currentItem.focus = true;53 d.currentItem.focus = true;
55 d.currentItem.forceActiveFocus();54 d.currentItem.forceActiveFocus();
interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml+0−1
@@ -19,7 +19,6 @@ StackView { · 18 unmodified lines
19 objectName: "stack"19 objectName: "stack"
20 property var title: "Networking Settings"20 property var title: "Networking Settings"
21 21
22 property var eventBridge;
23 signal sendToScript(var message);22 signal sendToScript(var message);
24 23
25 function pushSource(path) {24 function pushSource(path) {
interface/resources/qml/hifi/tablet/TabletRoot.qml+11−14
@@ -1,7 +1,6 @@
1import QtQuick 2.01import QtQuick 2.0
2import Hifi 1.02import Hifi 1.0
3import QtQuick.Controls 1.43import QtQuick.Controls 1.4
4import HFTabletWebEngineProfile 1.0
5import "../../dialogs"4import "../../dialogs"
6import "../../controls"5import "../../controls"
7 6
@@ -9,7 +8,6 @@ Item { · 1 unmodified line
9 id: tabletRoot8 id: tabletRoot
10 objectName: "tabletRoot"9 objectName: "tabletRoot"
11 property string username: "Unknown user"10 property string username: "Unknown user"
12 property var eventBridge;
13 property var rootMenu;11 property var rootMenu;
14 property var openModal: null;12 property var openModal: null;
15 property var openMessage: null;13 property var openMessage: null;
@@ -112,7 +110,6 @@ Item { · 96 unmodified lines
112 function openBrowserWindow(request, profile) {110 function openBrowserWindow(request, profile) {
113 var component = Qt.createComponent("../../controls/TabletWebView.qml");111 var component = Qt.createComponent("../../controls/TabletWebView.qml");
114 var newWindow = component.createObject(tabletRoot);112 var newWindow = component.createObject(tabletRoot);
115 newWindow.eventBridge = tabletRoot.eventBridge;
116 newWindow.remove = true;113 newWindow.remove = true;
117 newWindow.profile = profile;114 newWindow.profile = profile;
118 request.openIn(newWindow.webView);115 request.openIn(newWindow.webView);
@@ -166,21 +163,21 @@ Item { · 47 unmodified lines
166 objectName: "loader"163 objectName: "loader"
167 asynchronous: false164 asynchronous: false
168 165
169
170 width: parent.width166 width: parent.width
171 height: parent.height167 height: parent.height
172 168
173 onLoaded: {169 // Hook up callback for clara.io download from the marketplace.
174 if (loader.item.hasOwnProperty("eventBridge")) {170 Connections {
175 loader.item.eventBridge = eventBridge;171 id: eventBridgeConnection
176 172 target: eventBridge
177 // Hook up callback for clara.io download from the marketplace.173 onWebEventReceived: {
178 eventBridge.webEventReceived.connect(function (event) {174 if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
179 if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {175 ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
180 ApplicationInterface.addAssetToWorldFromURL(event.slice(18));176 }
181 }
182 });
183 }177 }
178 }
179
180 onLoaded: {
184 if (loader.item.hasOwnProperty("sendToScript")) {181 if (loader.item.hasOwnProperty("sendToScript")) {
185 loader.item.sendToScript.connect(tabletRoot.sendToScript);182 loader.item.sendToScript.connect(tabletRoot.sendToScript);
186 }183 }
interface/resources/qml/hifi/tablet/WindowRoot.qml+11−11
@@ -18,7 +18,6 @@ Windows.ScrollingWindow { · 17 unmodified lines
18 id: tabletRoot18 id: tabletRoot
19 objectName: "tabletRoot"19 objectName: "tabletRoot"
20 property string username: "Unknown user"20 property string username: "Unknown user"
21 property var eventBridge;
22 21
23 property var rootMenu;22 property var rootMenu;
24 property string subMenu: ""23 property string subMenu: ""
@@ -86,17 +85,18 @@ Windows.ScrollingWindow { · 61 unmodified lines
86 anchors.left: parent.left85 anchors.left: parent.left
87 anchors.top: parent.top86 anchors.top: parent.top
88 87
89 onLoaded: {88 // Hook up callback for clara.io download from the marketplace.
90 if (loader.item.hasOwnProperty("eventBridge")) {89 Connections {
91 loader.item.eventBridge = eventBridge;90 id: eventBridgeConnection
92 91 target: eventBridge
93 // Hook up callback for clara.io download from the marketplace.92 onWebEventReceived: {
94 eventBridge.webEventReceived.connect(function (event) {93 if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
95 if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {94 ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
96 ApplicationInterface.addAssetToWorldFromURL(event.slice(18));95 }
97 }
98 });
99 }96 }
97 }
98
99 onLoaded: {
100 if (loader.item.hasOwnProperty("sendToScript")) {100 if (loader.item.hasOwnProperty("sendToScript")) {
101 loader.item.sendToScript.connect(tabletRoot.sendToScript);101 loader.item.sendToScript.connect(tabletRoot.sendToScript);
102 }102 }
interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml+1−1
@@ -136,7 +136,7 @@ Item { · 135 unmodified lines
136 for (var i = 0; i < sections.length; i++) {136 for (var i = 0; i < sections.length; i++) {
137 totalHeight += sections[i].height + sections[i].getPreferencesHeight();137 totalHeight += sections[i].height + sections[i].getPreferencesHeight();
138 }138 }
139 var bottomPadding = 100;139 var bottomPadding = 170;
140 return (totalHeight + bottomPadding);140 return (totalHeight + bottomPadding);
141 }141 }
142 }142 }
interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml+5−10
@@ -27,8 +27,6 @@ Item { · 26 unmodified lines
27 property bool keyboardRaised: false27 property bool keyboardRaised: false
28 property bool punctuationMode: false28 property bool punctuationMode: false
29 29
30 property alias eventBridge: eventBridgeWrapper.eventBridge
31
32 anchors.fill: parent30 anchors.fill: parent
33 31
34 BaseWebView {32 BaseWebView {
@@ -43,14 +41,6 @@ Item { · 8 unmodified lines
43 bottom: footer.top41 bottom: footer.top
44 }42 }
45 43
46 QtObject {
47 id: eventBridgeWrapper
48 WebChannel.id: "eventBridgeWrapper"
49 property var eventBridge;
50 }
51
52 webChannel.registeredObjects: [eventBridgeWrapper]
53
54 // Create a global EventBridge object for raiseAndLowerKeyboard.44 // Create a global EventBridge object for raiseAndLowerKeyboard.
55 WebEngineScript {45 WebEngineScript {
56 id: createGlobalEventBridge46 id: createGlobalEventBridge
@@ -68,6 +58,11 @@ Item { · 11 unmodified lines
68 }58 }
69 59
70 userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]60 userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
61
62 Component.onCompleted: {
63 webChannel.registerObject("eventBridge", eventBridge);
64 webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
65 }
71 }66 }
72 67
73 Rectangle {68 Rectangle {
interface/resources/snapshot/img/no-image.jpg+0−0

Binary file not shown.

interface/src/Application.cpp+283−241
@@ -11,6 +11,9 @@ · 10 unmodified lines
11 11
12#include "Application.h"12#include "Application.h"
13 13
14#include <chrono>
15#include <thread>
16
14#include <gl/Config.h>17#include <gl/Config.h>
15#include <glm/glm.hpp>18#include <glm/glm.hpp>
16#include <glm/gtx/component_wise.hpp>19#include <glm/gtx/component_wise.hpp>
@@ -69,6 +72,7 @@ · 52 unmodified lines
69#include <ErrorDialog.h>72#include <ErrorDialog.h>
70#include <FileScriptingInterface.h>73#include <FileScriptingInterface.h>
71#include <Finally.h>74#include <Finally.h>
75#include <FingerprintUtils.h>
72#include <FramebufferCache.h>76#include <FramebufferCache.h>
73#include <gpu/Batch.h>77#include <gpu/Batch.h>
74#include <gpu/Context.h>78#include <gpu/Context.h>
@@ -110,7 +114,9 @@ · 35 unmodified lines
110#include <render/RenderFetchCullSortTask.h>114#include <render/RenderFetchCullSortTask.h>
111#include <RenderDeferredTask.h>115#include <RenderDeferredTask.h>
112#include <RenderForwardTask.h>116#include <RenderForwardTask.h>
117#include <RenderViewTask.h>
113#include <ResourceCache.h>118#include <ResourceCache.h>
119#include <ResourceRequest.h>
114#include <SandboxUtils.h>120#include <SandboxUtils.h>
115#include <SceneScriptingInterface.h>121#include <SceneScriptingInterface.h>
116#include <ScriptEngines.h>122#include <ScriptEngines.h>
@@ -144,10 +150,8 @@ · 27 unmodified lines
144#include "InterfaceLogging.h"150#include "InterfaceLogging.h"
145#include "LODManager.h"151#include "LODManager.h"
146#include "ModelPackager.h"152#include "ModelPackager.h"
147#include "networking/HFWebEngineProfile.h"
148#include "networking/HFTabletWebEngineProfile.h"
149#include "networking/FileTypeProfile.h"
150#include "scripting/Audio.h"153#include "scripting/Audio.h"
154#include "networking/CloseEventSender.h"
151#include "scripting/TestScriptingInterface.h"155#include "scripting/TestScriptingInterface.h"
152#include "scripting/AccountScriptingInterface.h"156#include "scripting/AccountScriptingInterface.h"
153#include "scripting/AssetMappingsScriptingInterface.h"157#include "scripting/AssetMappingsScriptingInterface.h"
@@ -244,6 +248,8 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda · 90 unmodified lines
244Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);248Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
245 249
246static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";250static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";
251static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds
252static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
247 253
248static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";254static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
249 255
@@ -438,6 +444,10 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 188 unmodified lines
438 const char* portStr = getCmdOption(argc, constArgv, "--listenPort");444 const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
439 const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;445 const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
440 446
447 static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
448 bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
449 bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
450
441 Setting::init();451 Setting::init();
442 452
443 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {453 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
@@ -458,10 +468,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 14 unmodified lines
458 QCoreApplication::addLibraryPath(audioDLLPath);468 QCoreApplication::addLibraryPath(audioDLLPath);
459#endif469#endif
460 470
461 static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
462 bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
463 bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
464
465 DependencyManager::registerInheritance<LimitedNodeList, NodeList>();471 DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
466 DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();472 DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
467 DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();473 DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();
@@ -536,6 +542,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 68 unmodified lines
536 DependencyManager::set<AvatarBookmarks>();542 DependencyManager::set<AvatarBookmarks>();
537 DependencyManager::set<LocationBookmarks>();543 DependencyManager::set<LocationBookmarks>();
538 DependencyManager::set<Snapshot>();544 DependencyManager::set<Snapshot>();
545 DependencyManager::set<CloseEventSender>();
539 546
540 return previousSessionCrashed;547 return previousSessionCrashed;
541}548}
@@ -649,6 +656,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 107 unmodified lines
649 Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us656 Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
650 657
651 auto nodeList = DependencyManager::get<NodeList>();658 auto nodeList = DependencyManager::get<NodeList>();
659 nodeList->startThread();
652 660
653 // Set up a watchdog thread to intentionally crash the application on deadlocks661 // Set up a watchdog thread to intentionally crash the application on deadlocks
654 _deadlockWatchdogThread = new DeadlockWatchdogThread();662 _deadlockWatchdogThread = new DeadlockWatchdogThread();
@@ -674,25 +682,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 19 unmodified lines
674 682
675 updateHeartbeat();683 updateHeartbeat();
676 684
677 // start the nodeThread so its event loop is running
678 QThread* nodeThread = new QThread(this);
679 nodeThread->setObjectName("NodeList Thread");
680 nodeThread->start();
681
682 // make sure the node thread is given highest priority
683 nodeThread->setPriority(QThread::TimeCriticalPriority);
684
685 // setup a timer for domain-server check ins685 // setup a timer for domain-server check ins
686 QTimer* domainCheckInTimer = new QTimer(nodeList.data());686 QTimer* domainCheckInTimer = new QTimer(nodeList.data());
687 connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);687 connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
688 domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);688 domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
689 689
690 // put the NodeList and datagram processing on the node thread
691 nodeList->moveToThread(nodeThread);
692
693 // put the audio processing on a separate thread
694 QThread* audioThread = new QThread();
695 audioThread->setObjectName("Audio Thread");
696 690
697 auto audioIO = DependencyManager::get<AudioClient>();691 auto audioIO = DependencyManager::get<AudioClient>();
698 audioIO->setPositionGetter([]{692 audioIO->setPositionGetter([]{
@@ -708,7 +702,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 9 unmodified lines
708 return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;702 return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
709 });703 });
710 704
711 audioIO->moveToThread(audioThread);
712 recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {705 recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
713 audioIO->handleRecordedAudioInput(frame->data);706 audioIO->handleRecordedAudioInput(frame->data);
714 });707 });
@@ -720,11 +713,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 5 unmodified lines
720 recorder->recordFrame(AUDIO_FRAME_TYPE, audio);713 recorder->recordFrame(AUDIO_FRAME_TYPE, audio);
721 }714 }
722 });715 });
723 716 audioIO->startThread();
724 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
725 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
726 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
727 audioThread->start();
728 717
729 auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface, scripting::Audio>();718 auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface, scripting::Audio>();
730 connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);719 connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
@@ -750,12 +739,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 19 unmodified lines
750 updateHeartbeat();739 updateHeartbeat();
751 740
752 // Setup MessagesClient741 // Setup MessagesClient
753 auto messagesClient = DependencyManager::get<MessagesClient>();742 DependencyManager::get<MessagesClient>()->startThread();
754 QThread* messagesThread = new QThread;
755 messagesThread->setObjectName("Messages Client Thread");
756 messagesClient->moveToThread(messagesThread);
757 connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
758 messagesThread->start();
759 743
760 const DomainHandler& domainHandler = nodeList->getDomainHandler();744 const DomainHandler& domainHandler = nodeList->getDomainHandler();
761 745
@@ -950,6 +934,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 188 unmodified lines
950 properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;934 properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
951 }935 }
952 936
937 // add firstRun flag from settings to launch event
938 Setting::Handle<bool> firstRun { Settings::firstRun, true };
939 properties["first_run"] = firstRun.get();
940
941 // add the user's machine ID to the launch event
942 properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
943
953 UserActivityLogger::getInstance().logAction("launch", properties);944 UserActivityLogger::getInstance().logAction("launch", properties);
954 945
955 // Tell our entity edit sender about our known jurisdictions946 // Tell our entity edit sender about our known jurisdictions
@@ -1309,6 +1300,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 353 unmodified lines
1309 properties["kbps_in"] = bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond();1300 properties["kbps_in"] = bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond();
1310 properties["kbps_out"] = bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond();1301 properties["kbps_out"] = bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond();
1311 1302
1303 properties["atp_in_kbps"] = bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AssetServer);
1304
1312 auto nodeList = DependencyManager::get<NodeList>();1305 auto nodeList = DependencyManager::get<NodeList>();
1313 SharedNodePointer entityServerNode = nodeList->soloNodeOfType(NodeType::EntityServer);1306 SharedNodePointer entityServerNode = nodeList->soloNodeOfType(NodeType::EntityServer);
1314 SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);1307 SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
@@ -1322,8 +1315,61 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 7 unmodified lines
1322 properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1;1315 properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1;
1323 1316
1324 auto loadingRequests = ResourceCache::getLoadingRequests();1317 auto loadingRequests = ResourceCache::getLoadingRequests();
1318
1319 QJsonArray loadingRequestsStats;
1320 for (const auto& request : loadingRequests) {
1321 QJsonObject requestStats;
1322 requestStats["filename"] = request->getURL().fileName();
1323 requestStats["received"] = request->getBytesReceived();
1324 requestStats["total"] = request->getBytesTotal();
1325 requestStats["attempts"] = (int)request->getDownloadAttempts();
1326 loadingRequestsStats.append(requestStats);
1327 }
1328
1325 properties["active_downloads"] = loadingRequests.size();1329 properties["active_downloads"] = loadingRequests.size();
1326 properties["pending_downloads"] = ResourceCache::getPendingRequestCount();1330 properties["pending_downloads"] = ResourceCache::getPendingRequestCount();
1331 properties["active_downloads_details"] = loadingRequestsStats;
1332
1333 auto statTracker = DependencyManager::get<StatTracker>();
1334
1335 properties["processing_resources"] = statTracker->getStat("Processing").toInt();
1336 properties["pending_processing_resources"] = statTracker->getStat("PendingProcessing").toInt();
1337
1338 QJsonObject startedRequests;
1339 startedRequests["atp"] = statTracker->getStat(STAT_ATP_REQUEST_STARTED).toInt();
1340 startedRequests["http"] = statTracker->getStat(STAT_HTTP_REQUEST_STARTED).toInt();
1341 startedRequests["file"] = statTracker->getStat(STAT_FILE_REQUEST_STARTED).toInt();
1342 startedRequests["total"] = startedRequests["atp"].toInt() + startedRequests["http"].toInt()
1343 + startedRequests["file"].toInt();
1344 properties["started_requests"] = startedRequests;
1345
1346 QJsonObject successfulRequests;
1347 successfulRequests["atp"] = statTracker->getStat(STAT_ATP_REQUEST_SUCCESS).toInt();
1348 successfulRequests["http"] = statTracker->getStat(STAT_HTTP_REQUEST_SUCCESS).toInt();
1349 successfulRequests["file"] = statTracker->getStat(STAT_FILE_REQUEST_SUCCESS).toInt();
1350 successfulRequests["total"] = successfulRequests["atp"].toInt() + successfulRequests["http"].toInt()
1351 + successfulRequests["file"].toInt();
1352 properties["successful_requests"] = successfulRequests;
1353
1354 QJsonObject failedRequests;
1355 failedRequests["atp"] = statTracker->getStat(STAT_ATP_REQUEST_FAILED).toInt();
1356 failedRequests["http"] = statTracker->getStat(STAT_HTTP_REQUEST_FAILED).toInt();
1357 failedRequests["file"] = statTracker->getStat(STAT_FILE_REQUEST_FAILED).toInt();
1358 failedRequests["total"] = failedRequests["atp"].toInt() + failedRequests["http"].toInt()
1359 + failedRequests["file"].toInt();
1360 properties["failed_requests"] = failedRequests;
1361
1362 QJsonObject cacheRequests;
1363 cacheRequests["atp"] = statTracker->getStat(STAT_ATP_REQUEST_CACHE).toInt();
1364 cacheRequests["http"] = statTracker->getStat(STAT_HTTP_REQUEST_CACHE).toInt();
1365 cacheRequests["total"] = cacheRequests["atp"].toInt() + cacheRequests["http"].toInt();
1366 properties["cache_requests"] = cacheRequests;
1367
1368 QJsonObject atpMappingRequests;
1369 atpMappingRequests["started"] = statTracker->getStat(STAT_ATP_MAPPING_REQUEST_STARTED).toInt();
1370 atpMappingRequests["failed"] = statTracker->getStat(STAT_ATP_MAPPING_REQUEST_FAILED).toInt();
1371 atpMappingRequests["successful"] = statTracker->getStat(STAT_ATP_MAPPING_REQUEST_SUCCESS).toInt();
1372 properties["atp_mapping_requests"] = atpMappingRequests;
1327 1373
1328 properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;1374 properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;
1329 1375
@@ -1339,9 +1385,43 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 9 unmodified lines
1339 properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount;1385 properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount;
1340 properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount;1386 properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount;
1341 1387
1388 NodeToOctreeSceneStats* octreeServerSceneStats = getOcteeSceneStats();
1389 unsigned long totalServerOctreeElements = 0;
1390 for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) {
1391 totalServerOctreeElements += i->second.getTotalElements();
1392 }
1393
1394 properties["local_octree_elements"] = (qint64) OctreeElement::getInternalNodeCount();
1395 properties["server_octree_elements"] = (qint64) totalServerOctreeElements;
1396
1342 properties["active_display_plugin"] = getActiveDisplayPlugin()->getName();1397 properties["active_display_plugin"] = getActiveDisplayPlugin()->getName();
1343 properties["using_hmd"] = isHMDMode();1398 properties["using_hmd"] = isHMDMode();
1344 1399
1400 _autoSwitchDisplayModeSupportedHMDPlugin = nullptr;
1401 foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
1402 if (displayPlugin->isHmd() &&
1403 displayPlugin->getSupportsAutoSwitch()) {
1404 _autoSwitchDisplayModeSupportedHMDPlugin = displayPlugin;
1405 _autoSwitchDisplayModeSupportedHMDPluginName =
1406 _autoSwitchDisplayModeSupportedHMDPlugin->getName();
1407 _previousHMDWornStatus =
1408 _autoSwitchDisplayModeSupportedHMDPlugin->isDisplayVisible();
1409 break;
1410 }
1411 }
1412
1413 if (_autoSwitchDisplayModeSupportedHMDPlugin) {
1414 if (getActiveDisplayPlugin() != _autoSwitchDisplayModeSupportedHMDPlugin &&
1415 !_autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {
1416 startHMDStandBySession();
1417 }
1418 // Poll periodically to check whether the user has worn HMD or not. Switch Display mode accordingly.
1419 // If the user wears HMD then switch to VR mode. If the user removes HMD then switch to Desktop mode.
1420 QTimer* autoSwitchDisplayModeTimer = new QTimer(this);
1421 connect(autoSwitchDisplayModeTimer, SIGNAL(timeout()), this, SLOT(switchDisplayMode()));
1422 autoSwitchDisplayModeTimer->start(INTERVAL_TO_CHECK_HMD_WORN_STATUS);
1423 }
1424
1345 auto glInfo = getGLContextData();1425 auto glInfo = getGLContextData();
1346 properties["gl_info"] = glInfo;1426 properties["gl_info"] = glInfo;
1347 properties["gpu_used_memory"] = (int)BYTES_TO_MB(gpu::Context::getUsedGPUMemSize());1427 properties["gpu_used_memory"] = (int)BYTES_TO_MB(gpu::Context::getUsedGPUMemSize());
@@ -1364,6 +1444,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 16 unmodified lines
1364 lastLeftHandPose = leftHandPose;1444 lastLeftHandPose = leftHandPose;
1365 lastRightHandPose = rightHandPose;1445 lastRightHandPose = rightHandPose;
1366 1446
1447 properties["local_socket_changes"] = DependencyManager::get<StatTracker>()->getStat(LOCAL_SOCKET_CHANGE_STAT).toInt();
1448
1367 UserActivityLogger::getInstance().logAction("stats", properties);1449 UserActivityLogger::getInstance().logAction("stats", properties);
1368 });1450 });
1369 sendStatsTimer->start();1451 sendStatsTimer->start();
@@ -1463,6 +1545,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 93 unmodified lines
1463 updateSystemTabletMode();1545 updateSystemTabletMode();
1464 1546
1465 connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);1547 connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
1548
1549 qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
1466}1550}
1467 1551
1468void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {1552void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
@@ -1571,6 +1655,12 @@ void Application::aboutToQuit() { · 102 unmodified lines
1571 }1655 }
1572 1656
1573 getActiveDisplayPlugin()->deactivate();1657 getActiveDisplayPlugin()->deactivate();
1658 if (_autoSwitchDisplayModeSupportedHMDPlugin
1659 && _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {
1660 _autoSwitchDisplayModeSupportedHMDPlugin->endSession();
1661 }
1662 // use the CloseEventSender via a QThread to send an event that says the user asked for the app to close
1663 DependencyManager::get<CloseEventSender>()->startThread();
1574 1664
1575 // Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.1665 // Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.
1576 DependencyManager::get<OffscreenUi>()->hide("RunningScripts");1666 DependencyManager::get<OffscreenUi>()->hide("RunningScripts");
@@ -1658,6 +1748,8 @@ void Application::cleanupBeforeQuit() { · 81 unmodified lines
1658 // stop QML1748 // stop QML
1659 DependencyManager::destroy<OffscreenUi>();1749 DependencyManager::destroy<OffscreenUi>();
1660 1750
1751 DependencyManager::destroy<OffscreenQmlSurfaceCache>();
1752
1661 if (_snapshotSoundInjector != nullptr) {1753 if (_snapshotSoundInjector != nullptr) {
1662 _snapshotSoundInjector->stop();1754 _snapshotSoundInjector->stop();
1663 }1755 }
@@ -1685,6 +1777,10 @@ Application::~Application() { · 21 unmodified lines
1685 1777
1686 _physicsEngine->setCharacterController(nullptr);1778 _physicsEngine->setCharacterController(nullptr);
1687 1779
1780 // the _shapeManager should have zero references
1781 _shapeManager.collectGarbage();
1782 assert(_shapeManager.getNumShapes() == 0);
1783
1688 // shutdown render engine1784 // shutdown render engine
1689 _main3DScene = nullptr;1785 _main3DScene = nullptr;
1690 _renderEngine = nullptr;1786 _renderEngine = nullptr;
@@ -1713,15 +1809,9 @@ Application::~Application() { · 22 unmodified lines
1713 1809
1714 ResourceManager::cleanup();1810 ResourceManager::cleanup();
1715 1811
1716 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
1717
1718 // remove the NodeList from the DependencyManager1812 // remove the NodeList from the DependencyManager
1719 DependencyManager::destroy<NodeList>();1813 DependencyManager::destroy<NodeList>();
1720 1814
1721 // ask the node thread to quit and wait until it is done
1722 nodeThread->quit();
1723 nodeThread->wait();
1724
1725 Leapmotion::destroy();1815 Leapmotion::destroy();
1726 1816
1727 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {1817 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
@@ -1736,6 +1826,15 @@ Application::~Application() { · 8 unmodified lines
1736 1826
1737 _window->deleteLater();1827 _window->deleteLater();
1738 1828
1829 // make sure that the quit event has finished sending before we take the application down
1830 auto closeEventSender = DependencyManager::get<CloseEventSender>();
1831 while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) {
1832 // sleep a little so we're not spinning at 100%
1833 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1834 }
1835 // quit the thread used by the closure event sender
1836 closeEventSender->thread()->quit();
1837
1739 // Can't log to file passed this point, FileLogger about to be deleted1838 // Can't log to file passed this point, FileLogger about to be deleted
1740 qInstallMessageHandler(LogHandler::verboseMessageHandler);1839 qInstallMessageHandler(LogHandler::verboseMessageHandler);
1741}1840}
@@ -1771,15 +1870,9 @@ void Application::initializeGL() { · 29 unmodified lines
1771 1870
1772 // Set up the render engine1871 // Set up the render engine
1773 render::CullFunctor cullFunctor = LODManager::shouldRender;1872 render::CullFunctor cullFunctor = LODManager::shouldRender;
1774 _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
1775 const auto items = _renderEngine->addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
1776 assert(items.canCast<RenderFetchCullSortTask::Output>());
1777 static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";1873 static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
1778 if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) {1874 bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
1779 _renderEngine->addJob<RenderForwardTask>("Forward", items);1875 _renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, isDeferred);
1780 } else {
1781 _renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items);
1782 }
1783 _renderEngine->load();1876 _renderEngine->load();
1784 _renderEngine->registerScene(_main3DScene);1877 _renderEngine->registerScene(_main3DScene);
1785 1878
@@ -1828,14 +1921,10 @@ void Application::initializeUi() { · 42 unmodified lines
1828 UpdateDialog::registerType();1921 UpdateDialog::registerType();
1829 qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");1922 qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
1830 1923
1831 qmlRegisterType<HFWebEngineProfile>("HFWebEngineProfile", 1, 0, "HFWebEngineProfile");
1832 qmlRegisterType<HFTabletWebEngineProfile>("HFTabletWebEngineProfile", 1, 0, "HFTabletWebEngineProfile");
1833 qmlRegisterType<FileTypeProfile>("FileTypeProfile", 1, 0, "FileTypeProfile");
1834
1835 auto offscreenUi = DependencyManager::get<OffscreenUi>();1924 auto offscreenUi = DependencyManager::get<OffscreenUi>();
1836 offscreenUi->create(_glWidget->qglContext());1925 offscreenUi->create(_glWidget->qglContext());
1837 1926
1838 auto rootContext = offscreenUi->getRootContext();1927 auto surfaceContext = offscreenUi->getSurfaceContext();
1839 1928
1840 offscreenUi->setProxyWindow(_window->windowHandle());1929 offscreenUi->setProxyWindow(_window->windowHandle());
1841 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));1930 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
@@ -1847,7 +1936,7 @@ void Application::initializeUi() { · 5 unmodified lines
1847 // do better detection in the offscreen UI of what has focus1936 // do better detection in the offscreen UI of what has focus
1848 offscreenUi->setNavigationFocused(false);1937 offscreenUi->setNavigationFocused(false);
1849 1938
1850 auto engine = rootContext->engine();1939 auto engine = surfaceContext->engine();
1851 connect(engine, &QQmlEngine::quit, [] {1940 connect(engine, &QQmlEngine::quit, [] {
1852 qApp->quit();1941 qApp->quit();
1853 });1942 });
@@ -1856,78 +1945,77 @@ void Application::initializeUi() { · 2 unmodified lines
1856 1945
1857 // For some reason there is already an "Application" object in the QML context,1946 // For some reason there is already an "Application" object in the QML context,
1858 // though I can't find it. Hence, "ApplicationInterface"1947 // though I can't find it. Hence, "ApplicationInterface"
1859 rootContext->setContextProperty("ApplicationInterface", this);1948 surfaceContext->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
1860 rootContext->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());1949 surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
1861 rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());1950 surfaceContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
1862 rootContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
1863 1951
1864 rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());1952 surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
1865 rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());1953 surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
1866 _fileDownload = new FileScriptingInterface(engine);1954 _fileDownload = new FileScriptingInterface(engine);
1867 rootContext->setContextProperty("File", _fileDownload);1955 surfaceContext->setContextProperty("File", _fileDownload);
1868 connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip);1956 connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip);
1869 rootContext->setContextProperty("MyAvatar", getMyAvatar().get());1957 surfaceContext->setContextProperty("MyAvatar", getMyAvatar().get());
1870 rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data());1958 surfaceContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data());
1871 rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());1959 surfaceContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
1872 rootContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());1960 surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
1873 rootContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());1961 surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
1874 rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface);1962 surfaceContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface);
1875 rootContext->setContextProperty("Rates", new RatesScriptingInterface(this));1963 surfaceContext->setContextProperty("Rates", new RatesScriptingInterface(this));
1876 rootContext->setContextProperty("pathToFonts", "../../");
1877 1964
1878 rootContext->setContextProperty("TREE_SCALE", TREE_SCALE);1965 surfaceContext->setContextProperty("TREE_SCALE", TREE_SCALE);
1879 rootContext->setContextProperty("Quat", new Quat());1966 // FIXME Quat and Vec3 won't work with QJSEngine used by QML
1880 rootContext->setContextProperty("Vec3", new Vec3());1967 surfaceContext->setContextProperty("Quat", new Quat());
1881 rootContext->setContextProperty("Uuid", new ScriptUUID());1968 surfaceContext->setContextProperty("Vec3", new Vec3());
1882 rootContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());1969 surfaceContext->setContextProperty("Uuid", new ScriptUUID());
1970 surfaceContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
1883 1971
1884 rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());1972 surfaceContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
1885 rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());1973 surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
1886 1974
1887 rootContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());1975 surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
1888 1976
1889 rootContext->setContextProperty("Camera", &_myCamera);1977 surfaceContext->setContextProperty("Camera", &_myCamera);
1890 1978
1891#if defined(Q_OS_MAC) || defined(Q_OS_WIN)1979#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
1892 rootContext->setContextProperty("SpeechRecognizer", DependencyManager::get<SpeechRecognizer>().data());1980 surfaceContext->setContextProperty("SpeechRecognizer", DependencyManager::get<SpeechRecognizer>().data());
1893#endif1981#endif
1894 1982
1895 rootContext->setContextProperty("Overlays", &_overlays);1983 surfaceContext->setContextProperty("Overlays", &_overlays);
1896 rootContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());1984 surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
1897 rootContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());1985 surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
1898 rootContext->setContextProperty("Stats", Stats::getInstance());1986 surfaceContext->setContextProperty("Stats", Stats::getInstance());
1899 rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());1987 surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
1900 rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());1988 surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
1901 rootContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());1989 surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
1902 rootContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());1990 surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
1903 1991
1904 // Caches1992 // Caches
1905 rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());1993 surfaceContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
1906 rootContext->setContextProperty("TextureCache", DependencyManager::get<TextureCache>().data());1994 surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCache>().data());
1907 rootContext->setContextProperty("ModelCache", DependencyManager::get<ModelCache>().data());1995 surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCache>().data());
1908 rootContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());1996 surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
1909 1997
1910 rootContext->setContextProperty("Account", AccountScriptingInterface::getInstance());1998 surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
1911 rootContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data()); 1999 surfaceContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
1912 rootContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);2000 surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
1913 rootContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());2001 surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
1914 rootContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());2002 surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
1915 rootContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());2003 surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
1916 rootContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);2004 surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
1917 rootContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());2005 surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
1918 rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());2006 surfaceContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
1919 rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());2007 surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
1920 rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());2008 surfaceContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
1921 rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());2009 surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
1922 rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());2010 surfaceContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
1923 rootContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());2011 surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
1924 2012
1925 rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());2013 surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
1926 2014
1927 rootContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());2015 surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
1928 2016
1929 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {2017 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
1930 rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));2018 surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
1931 }2019 }
1932 2020
1933 2021
@@ -2066,7 +2154,7 @@ void Application::paintGL() { · 132 unmodified lines
2066 _myCamera.setOrientation(glm::quat_cast(camMat));2154 _myCamera.setOrientation(glm::quat_cast(camMat));
2067 } else {2155 } else {
2068 _myCamera.setPosition(myAvatar->getDefaultEyePosition());2156 _myCamera.setPosition(myAvatar->getDefaultEyePosition());
2069 _myCamera.setOrientation(myAvatar->getMyHead()->getCameraOrientation());2157 _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation());
2070 }2158 }
2071 } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {2159 } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
2072 if (isHMDMode()) {2160 if (isHMDMode()) {
@@ -2186,6 +2274,9 @@ void Application::paintGL() { · 113 unmodified lines
2186 });2274 });
2187 renderArgs._context->setStereoProjections(eyeProjections);2275 renderArgs._context->setStereoProjections(eyeProjections);
2188 renderArgs._context->setStereoViews(eyeOffsets);2276 renderArgs._context->setStereoViews(eyeOffsets);
2277
2278 // Configure the type of display / stereo
2279 renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR);
2189 }2280 }
2190 renderArgs._blitFramebuffer = finalFramebuffer;2281 renderArgs._blitFramebuffer = finalFramebuffer;
2191 displaySide(&renderArgs, _myCamera);2282 displaySide(&renderArgs, _myCamera);
@@ -2379,15 +2470,16 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 187 unmodified lines
2379 2470
2380 // Check HMD use (may be technically available without being in use)2471 // Check HMD use (may be technically available without being in use)
2381 bool hasHMD = PluginUtils::isHMDAvailable();2472 bool hasHMD = PluginUtils::isHMDAvailable();
2382 bool isUsingHMD = hasHMD && hasHandControllers && _displayPlugin->isHmd();2473 bool isUsingHMD = _displayPlugin->isHmd();
2474 bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD;
2383 2475
2384 Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false };2476 Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false };
2385 Setting::Handle<bool> firstRun{ Settings::firstRun, true };2477 Setting::Handle<bool> firstRun{ Settings::firstRun, true };
2386 2478
2387 bool isTutorialComplete = tutorialComplete.get();2479 bool isTutorialComplete = tutorialComplete.get();
2388 bool shouldGoToTutorial = isUsingHMD && hasTutorialContent && !isTutorialComplete;2480 bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete;
2389 2481
2390 qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMD;2482 qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers;
2391 qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent <<2483 qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent <<
2392 ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial;2484 ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial;
2393 2485
@@ -2401,10 +2493,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 7 unmodified lines
2401 2493
2402 const QString TUTORIAL_PATH = "/tutorial_begin";2494 const QString TUTORIAL_PATH = "/tutorial_begin";
2403 2495
2496 static const QString SENT_TO_TUTORIAL = "tutorial";
2497 static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
2498 static const QString SENT_TO_ENTRY = "entry";
2499 static const QString SENT_TO_SANDBOX = "sandbox";
2500
2501 QString sentTo;
2502
2404 if (shouldGoToTutorial) {2503 if (shouldGoToTutorial) {
2405 if (sandboxIsRunning) {2504 if (sandboxIsRunning) {
2406 qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";2505 qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
2407 DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);2506 DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);
2507 sentTo = SENT_TO_TUTORIAL;
2408 } else {2508 } else {
2409 qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";2509 qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
2410 if (firstRun.get()) {2510 if (firstRun.get()) {
@@ -2412,8 +2512,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 1 unmodified line
2412 }2512 }
2413 if (addressLookupString.isEmpty()) {2513 if (addressLookupString.isEmpty()) {
2414 DependencyManager::get<AddressManager>()->goToEntry();2514 DependencyManager::get<AddressManager>()->goToEntry();
2515 sentTo = SENT_TO_ENTRY;
2415 } else {2516 } else {
2416 DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);2517 DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
2518 sentTo = SENT_TO_PREVIOUS_LOCATION;
2417 }2519 }
2418 }2520 }
2419 } else {2521 } else {
@@ -2426,23 +2528,40 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 6 unmodified lines
2426 2528
2427 // If this is a first run we short-circuit the address passed in2529 // If this is a first run we short-circuit the address passed in
2428 if (isFirstRun) {2530 if (isFirstRun) {
2429 if (isUsingHMD) {2531 if (isUsingHMDAndHandControllers) {
2430 if (sandboxIsRunning) {2532 if (sandboxIsRunning) {
2431 qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";2533 qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
2432 DependencyManager::get<AddressManager>()->goToLocalSandbox();2534 DependencyManager::get<AddressManager>()->goToLocalSandbox();
2535 sentTo = SENT_TO_SANDBOX;
2433 } else {2536 } else {
2434 qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";2537 qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
2435 DependencyManager::get<AddressManager>()->goToEntry();2538 DependencyManager::get<AddressManager>()->goToEntry();
2539 sentTo = SENT_TO_ENTRY;
2436 }2540 }
2437 } else {2541 } else {
2438 DependencyManager::get<AddressManager>()->goToEntry();2542 DependencyManager::get<AddressManager>()->goToEntry();
2543 sentTo = SENT_TO_ENTRY;
2439 }2544 }
2440 } else {2545 } else {
2441 qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);2546 qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
2442 DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);2547 DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
2548 sentTo = SENT_TO_PREVIOUS_LOCATION;
2443 }2549 }
2444 }2550 }
2445 2551
2552 UserActivityLogger::getInstance().logAction("startup_sent_to", {
2553 { "sent_to", sentTo },
2554 { "sandbox_is_running", sandboxIsRunning },
2555 { "has_hmd", hasHMD },
2556 { "has_hand_controllers", hasHandControllers },
2557 { "is_using_hmd", isUsingHMD },
2558 { "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers },
2559 { "content_version", contentVersion },
2560 { "is_tutorial_complete", isTutorialComplete },
2561 { "has_tutorial_content", hasTutorialContent },
2562 { "should_go_to_tutorial", shouldGoToTutorial }
2563 });
2564
2446 _connectionMonitor.init();2565 _connectionMonitor.init();
2447 2566
2448 // After all of the constructor is completed, then set firstRun to false.2567 // After all of the constructor is completed, then set firstRun to false.
@@ -2777,6 +2896,17 @@ void Application::keyPressEvent(QKeyEvent* event) { · 328 unmodified lines
2777 if (isShifted && isMeta && !isOption) {2896 if (isShifted && isMeta && !isOption) {
2778 Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);2897 Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
2779 } else if (!isOption && !isShifted && isMeta) {2898 } else if (!isOption && !isShifted && isMeta) {
2899 AudioInjectorOptions options;
2900 options.localOnly = true;
2901 options.stereo = true;
2902
2903 if (_snapshotSoundInjector) {
2904 _snapshotSoundInjector->setOptions(options);
2905 _snapshotSoundInjector->restart();
2906 } else {
2907 QByteArray samples = _snapshotSound->getByteArray();
2908 _snapshotSoundInjector = AudioInjector::playSound(samples, options);
2909 }
2780 takeSnapshot(true);2910 takeSnapshot(true);
2781 }2911 }
2782 break;2912 break;
@@ -3951,6 +4081,7 @@ void Application::updateMyAvatarLookAtPosition() { · 1168 unmodified lines
3951 lookAtPosition.x = -lookAtPosition.x;4081 lookAtPosition.x = -lookAtPosition.x;
3952 }4082 }
3953 if (isHMD) {4083 if (isHMD) {
4084 // TODO -- this code is probably wrong, getHeadPose() returns something in sensor frame, not avatar
3954 glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();4085 glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
3955 glm::quat hmdRotation = glm::quat_cast(headPose);4086 glm::quat hmdRotation = glm::quat_cast(headPose);
3956 lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);4087 lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
@@ -3992,9 +4123,10 @@ void Application::updateMyAvatarLookAtPosition() { · 35 unmodified lines
3992 }4123 }
3993 } else {4124 } else {
3994 // I am not looking at anyone else, so just look forward4125 // I am not looking at anyone else, so just look forward
3995 if (isHMD) {4126 auto headPose = myAvatar->getHeadControllerPoseInSensorFrame();
3996 glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();4127 if (headPose.isValid()) {
3997 lookAtSpot = transformPoint(worldHMDMat, glm::vec3(0.0f, 0.0f, -TREE_SCALE));4128 glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix();
4129 lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE));
3998 } else {4130 } else {
3999 lookAtSpot = myAvatar->getHead()->getEyePosition() +4131 lookAtSpot = myAvatar->getHead()->getEyePosition() +
4000 (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));4132 (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
@@ -4492,12 +4624,13 @@ void Application::update(float deltaTime) { · 491 unmodified lines
4492 4624
4493 getEntities()->getTree()->withWriteLock([&] {4625 getEntities()->getTree()->withWriteLock([&] {
4494 PerformanceTimer perfTimer("handleOutgoingChanges");4626 PerformanceTimer perfTimer("handleOutgoingChanges");
4495 const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
4496 _entitySimulation->handleDeactivatedMotionStates(deactivations);
4497 4627
4498 const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();4628 const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
4499 _entitySimulation->handleChangedMotionStates(outgoingChanges);4629 _entitySimulation->handleChangedMotionStates(outgoingChanges);
4500 avatarManager->handleChangedMotionStates(outgoingChanges);4630 avatarManager->handleChangedMotionStates(outgoingChanges);
4631
4632 const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
4633 _entitySimulation->handleDeactivatedMotionStates(deactivations);
4501 });4634 });
4502 4635
4503 if (!_aboutToQuit) {4636 if (!_aboutToQuit) {
@@ -4885,12 +5018,6 @@ QRect Application::getDesirableApplicationGeometry() const { · 381 unmodified lines
4885 return applicationGeometry;5018 return applicationGeometry;
4886}5019}
4887 5020
4888glm::vec3 Application::getSunDirection() const {
4889 // Sun direction is in fact just the location of the sun relative to the origin
4890 auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
4891 return skyStage->getSunLight()->getDirection();
4892}
4893
4894// FIXME, preprocessor guard this check to occur only in DEBUG builds5021// FIXME, preprocessor guard this check to occur only in DEBUG builds
4895static QThread * activeRenderingThread = nullptr;5022static QThread * activeRenderingThread = nullptr;
4896 5023
@@ -4947,7 +5074,7 @@ namespace render { · 50 unmodified lines
4947 template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); }5074 template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); }
4948 template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }5075 template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }
4949 template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {5076 template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {
4950 if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {5077 if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
4951 PerformanceTimer perfTimer("worldBox");5078 PerformanceTimer perfTimer("worldBox");
4952 5079
4953 auto& batch = *args->_batch;5080 auto& batch = *args->_batch;
@@ -4957,90 +5084,6 @@ namespace render { · 3 unmodified lines
4957 }5084 }
4958}5085}
4959 5086
4960// Background Render Data & rendering functions
4961class BackgroundRenderData {
4962public:
4963 typedef render::Payload<BackgroundRenderData> Payload;
4964 typedef Payload::DataPointer Pointer;
4965
4966 static render::ItemID _item; // unique WorldBoxRenderData
4967};
4968
4969render::ItemID BackgroundRenderData::_item = 0;
4970
4971namespace render {
4972 template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) {
4973 return ItemKey::Builder::background();
4974 }
4975
4976 template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) {
4977 return Item::Bound();
4978 }
4979
4980 template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) {
4981 Q_ASSERT(args->_batch);
4982 gpu::Batch& batch = *args->_batch;
4983
4984 // Background rendering decision
4985 auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
4986 auto backgroundMode = skyStage->getBackgroundMode();
4987
4988 switch (backgroundMode) {
4989 case model::SunSkyStage::SKY_DEFAULT: {
4990 auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
4991 auto sceneKeyLight = scene->getKeyLight();
4992
4993 scene->setSunModelEnable(false);
4994 sceneKeyLight->setColor(ColorUtils::toVec3(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR));
4995 sceneKeyLight->setIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY);
4996 sceneKeyLight->setAmbientIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY);
4997 sceneKeyLight->setDirection(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION);
4998 // fall through: render a skybox (if available), or the defaults (if requested)
4999 }
5000
5001 case model::SunSkyStage::SKY_BOX: {
5002 auto skybox = skyStage->getSkybox();
5003 if (!skybox->empty()) {
5004 PerformanceTimer perfTimer("skybox");
5005 skybox->render(batch, args->getViewFrustum());
5006 break;
5007 }
5008 // fall through: render defaults (if requested)
5009 }
5010
5011 case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: {
5012 if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
5013 auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
5014 auto sceneKeyLight = scene->getKeyLight();
5015 auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
5016 if (defaultSkyboxAmbientTexture) {
5017 sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
5018 sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
5019 } else {
5020 static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(
5021 "Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step");
5022 }
5023 // fall through: render defaults skybox
5024 } else {
5025 break;
5026 }
5027 }
5028
5029 case model::SunSkyStage::SKY_DEFAULT_TEXTURE:
5030 if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
5031 qApp->getDefaultSkybox()->render(batch, args->getViewFrustum());
5032 }
5033 break;
5034
5035 // Any other cases require no extra rendering
5036 case model::SunSkyStage::NO_BACKGROUND:
5037 default:
5038 break;
5039 }
5040 }
5041}
5042
5043
5044void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) {5087void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) {
5045 5088
5046 // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.5089 // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
@@ -5064,15 +5107,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 17 unmodified lines
5064 // The pending changes collecting the changes here5107 // The pending changes collecting the changes here
5065 render::Transaction transaction;5108 render::Transaction transaction;
5066 5109
5067 // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities
5068 // Background rendering decision
5069 if (!render::Item::isValidID(BackgroundRenderData::_item)) {
5070 auto backgroundRenderData = make_shared<BackgroundRenderData>();
5071 auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
5072 BackgroundRenderData::_item = _main3DScene->allocateID();
5073 transaction.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
5074 }
5075
5076 // Assuming nothing gets rendered through that5110 // Assuming nothing gets rendered through that
5077 if (!selfAvatarOnly) {5111 if (!selfAvatarOnly) {
5078 if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {5112 if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
@@ -5108,12 +5142,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 29 unmodified lines
5108 });5142 });
5109 }5143 }
5110 5144
5111 // Setup the current Zone Entity lighting
5112 {
5113 auto stage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
5114 DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(stage->getSunLight());
5115 }
5116
5117 {5145 {
5118 PerformanceTimer perfTimer("SceneProcessTransaction");5146 PerformanceTimer perfTimer("SceneProcessTransaction");
5119 _main3DScene->enqueueTransaction(transaction);5147 _main3DScene->enqueueTransaction(transaction);
@@ -6384,21 +6412,6 @@ void Application::loadAddAvatarBookmarkDialog() const { · 1264 unmodified lines
6384}6412}
6385 6413
6386void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {6414void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
6387
6388 //keep sound thread out of event loop scope
6389
6390 AudioInjectorOptions options;
6391 options.localOnly = true;
6392 options.stereo = true;
6393
6394 if (_snapshotSoundInjector) {
6395 _snapshotSoundInjector->setOptions(options);
6396 _snapshotSoundInjector->restart();
6397 } else {
6398 QByteArray samples = _snapshotSound->getByteArray();
6399 _snapshotSoundInjector = AudioInjector::playSound(samples, options);
6400 }
6401
6402 postLambdaEvent([notify, includeAnimated, aspectRatio, this] {6415 postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
6403 // Get a screenshot and save it6416 // Get a screenshot and save it
6404 QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));6417 QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
@@ -6811,6 +6824,35 @@ void Application::updateDisplayMode() { · 406 unmodified lines
6811 Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");6824 Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
6812}6825}
6813 6826
6827void Application::switchDisplayMode() {
6828 if (!_autoSwitchDisplayModeSupportedHMDPlugin) {
6829 return;
6830 }
6831 bool currentHMDWornStatus = _autoSwitchDisplayModeSupportedHMDPlugin->isDisplayVisible();
6832 if (currentHMDWornStatus != _previousHMDWornStatus) {
6833 // Switch to respective mode as soon as currentHMDWornStatus changes
6834 if (currentHMDWornStatus) {
6835 qCDebug(interfaceapp) << "Switching from Desktop to HMD mode";
6836 endHMDSession();
6837 setActiveDisplayPlugin(_autoSwitchDisplayModeSupportedHMDPluginName);
6838 } else {
6839 qCDebug(interfaceapp) << "Switching from HMD to desktop mode";
6840 setActiveDisplayPlugin(DESKTOP_DISPLAY_PLUGIN_NAME);
6841 startHMDStandBySession();
6842 }
6843 emit activeDisplayPluginChanged();
6844 }
6845 _previousHMDWornStatus = currentHMDWornStatus;
6846}
6847
6848void Application::startHMDStandBySession() {
6849 _autoSwitchDisplayModeSupportedHMDPlugin->startStandBySession();
6850}
6851
6852void Application::endHMDSession() {
6853 _autoSwitchDisplayModeSupportedHMDPlugin->endSession();
6854}
6855
6814mat4 Application::getEyeProjection(int eye) const {6856mat4 Application::getEyeProjection(int eye) const {
6815 QMutexLocker viewLocker(&_viewMutex);6857 QMutexLocker viewLocker(&_viewMutex);
6816 if (isHMDMode()) {6858 if (isHMDMode()) {
interface/src/Application.h+7−5
@@ -439,7 +439,7 @@ private slots: · 438 unmodified lines
439 void addAssetToWorldErrorTimeout();439 void addAssetToWorldErrorTimeout();
440 440
441 void handleSandboxStatus(QNetworkReply* reply);441 void handleSandboxStatus(QNetworkReply* reply);
442 442 void switchDisplayMode();
443private:443private:
444 static void initDisplay();444 static void initDisplay();
445 void init();445 void init();
@@ -457,8 +457,6 @@ private: · 11 unmodified lines
457 457
458 void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false);458 void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false);
459 459
460 glm::vec3 getSunDirection() const;
461
462 void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed);460 void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed);
463 461
464 int sendNackPackets();462 int sendNackPackets();
@@ -679,7 +677,11 @@ private: · 214 unmodified lines
679 FileScriptingInterface* _fileDownload;677 FileScriptingInterface* _fileDownload;
680 AudioInjector* _snapshotSoundInjector { nullptr };678 AudioInjector* _snapshotSoundInjector { nullptr };
681 SharedSoundPointer _snapshotSound;679 SharedSoundPointer _snapshotSound;
680
681 DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin;
682 QString _autoSwitchDisplayModeSupportedHMDPluginName;
683 bool _previousHMDWornStatus;
684 void startHMDStandBySession();
685 void endHMDSession();
682};686};
683
684
685#endif // hifi_Application_h687#endif // hifi_Application_h
interface/src/AvatarBookmarks.cpp+22−2
@@ -21,15 +21,35 @@ · 20 unmodified lines
21 21
22#include "MainWindow.h"22#include "MainWindow.h"
23#include "Menu.h"23#include "Menu.h"
24
25#include "AvatarBookmarks.h"24#include "AvatarBookmarks.h"
25#include "InterfaceLogging.h"
26
26#include <QtQuick/QQuickWindow>27#include <QtQuick/QQuickWindow>
27 28
28AvatarBookmarks::AvatarBookmarks() {29AvatarBookmarks::AvatarBookmarks() {
29 _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;30 _bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME;
30 readFromFile();31 readFromFile();
31}32}
32 33
34void AvatarBookmarks::readFromFile() {
35 // migrate old avatarbookmarks.json, used to be in 'local' folder on windows
36 QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
37 QFile oldConfig(oldConfigPath);
38
39 // I imagine that in a year from now, this code for migrating (as well as the two lines above)
40 // may be removed since all bookmarks should have been migrated by then
41 // - Robbie Uvanni (6.8.2017)
42 if (oldConfig.exists()) {
43 if (QDir().rename(oldConfigPath, _bookmarksFilename)) {
44 qCDebug(interfaceapp) << "Successfully migrated" << AVATARBOOKMARKS_FILENAME;
45 } else {
46 qCDebug(interfaceapp) << "Failed to migrate" << AVATARBOOKMARKS_FILENAME;
47 }
48 }
49
50 Bookmarks::readFromFile();
51}
52
33void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {53void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
34 // Add menus/actions54 // Add menus/actions
35 auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar);55 auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar);
interface/src/AvatarBookmarks.h+1−0
@@ -29,6 +29,7 @@ public slots: · 28 unmodified lines
29 29
30protected:30protected:
31 void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;31 void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
32 void readFromFile();
32 33
33private:34private:
34 const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";35 const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
interface/src/CrashHandler.cpp+20−8
@@ -24,12 +24,15 @@ · 23 unmodified lines
24 24
25#include "Application.h"25#include "Application.h"
26#include "Menu.h"26#include "Menu.h"
27#include <SettingHandle.h>
28 27
29#include <RunningMarker.h>28#include <RunningMarker.h>
29#include <SettingHandle.h>
30#include <SettingHelpers.h>
31
30 32
31bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) {33bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) {
32 Settings settings;34 QSettings::setDefaultFormat(JSON_FORMAT);
35 QSettings settings;
33 settings.beginGroup("Developer");36 settings.beginGroup("Developer");
34 QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions);37 QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions);
35 QVariant askToResetSettingsOption = settings.value(MenuOption::AskToResetSettings);38 QVariant askToResetSettingsOption = settings.value(MenuOption::AskToResetSettings);
@@ -70,7 +73,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) { · 34 unmodified lines
70 layout->addWidget(label);73 layout->addWidget(label);
71 74
72 QRadioButton* option1 = new QRadioButton("Reset all my settings");75 QRadioButton* option1 = new QRadioButton("Reset all my settings");
73 QRadioButton* option2 = new QRadioButton("Reset my settings but retain avatar info.");76 QRadioButton* option2 = new QRadioButton("Reset my settings but keep essential info");
74 QRadioButton* option3 = new QRadioButton("Continue with my current settings");77 QRadioButton* option3 = new QRadioButton("Continue with my current settings");
75 option3->setChecked(true);78 option3->setChecked(true);
76 layout->addWidget(option1);79 layout->addWidget(option1);
@@ -92,7 +95,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) { · 15 unmodified lines
92 return CrashHandler::DELETE_INTERFACE_INI;95 return CrashHandler::DELETE_INTERFACE_INI;
93 }96 }
94 if (option2->isChecked()) {97 if (option2->isChecked()) {
95 return CrashHandler::RETAIN_AVATAR_INFO;98 return CrashHandler::RETAIN_IMPORTANT_INFO;
96 }99 }
97 }100 }
98 101
@@ -101,24 +104,27 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) { · 2 unmodified lines
101}104}
102 105
103void CrashHandler::handleCrash(CrashHandler::Action action) {106void CrashHandler::handleCrash(CrashHandler::Action action) {
104 if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_AVATAR_INFO) {107 if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_IMPORTANT_INFO) {
105 // CrashHandler::DO_NOTHING or unexpected value108 // CrashHandler::DO_NOTHING or unexpected value
106 return;109 return;
107 }110 }
108 111
109 Settings settings;112 QSettings settings;
110 const QString ADDRESS_MANAGER_GROUP = "AddressManager";113 const QString ADDRESS_MANAGER_GROUP = "AddressManager";
111 const QString ADDRESS_KEY = "address";114 const QString ADDRESS_KEY = "address";
112 const QString AVATAR_GROUP = "Avatar";115 const QString AVATAR_GROUP = "Avatar";
113 const QString DISPLAY_NAME_KEY = "displayName";116 const QString DISPLAY_NAME_KEY = "displayName";
114 const QString FULL_AVATAR_URL_KEY = "fullAvatarURL";117 const QString FULL_AVATAR_URL_KEY = "fullAvatarURL";
115 const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName";118 const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName";
119 const QString TUTORIAL_COMPLETE_FLAG_KEY = "tutorialComplete";
120
116 QString displayName;121 QString displayName;
117 QUrl fullAvatarURL;122 QUrl fullAvatarURL;
118 QString fullAvatarModelName;123 QString fullAvatarModelName;
119 QUrl address;124 QUrl address;
125 bool tutorialComplete = false;
120 126
121 if (action == CrashHandler::RETAIN_AVATAR_INFO) {127 if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
122 // Read avatar info128 // Read avatar info
123 129
124 // Location and orientation130 // Location and orientation
@@ -132,6 +138,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { · 7 unmodified lines
132 fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl();138 fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl();
133 fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString();139 fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString();
134 settings.endGroup();140 settings.endGroup();
141
142 // Tutorial complete
143 tutorialComplete = settings.value(TUTORIAL_COMPLETE_FLAG_KEY).toBool();
135 }144 }
136 145
137 // Delete Interface.ini146 // Delete Interface.ini
@@ -140,7 +149,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { · 2 unmodified lines
140 settingsFile.remove();149 settingsFile.remove();
141 }150 }
142 151
143 if (action == CrashHandler::RETAIN_AVATAR_INFO) {152 if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
144 // Write avatar info153 // Write avatar info
145 154
146 // Location and orientation155 // Location and orientation
@@ -154,6 +163,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { · 7 unmodified lines
154 settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL);163 settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL);
155 settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName);164 settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName);
156 settings.endGroup();165 settings.endGroup();
166
167 // Tutorial complete
168 settings.setValue(TUTORIAL_COMPLETE_FLAG_KEY, tutorialComplete);
157 }169 }
158}170}
159 171
interface/src/CrashHandler.h+1−1
@@ -22,7 +22,7 @@ public: · 21 unmodified lines
22private:22private:
23 enum Action {23 enum Action {
24 DELETE_INTERFACE_INI,24 DELETE_INTERFACE_INI,
25 RETAIN_AVATAR_INFO,25 RETAIN_IMPORTANT_INFO,
26 DO_NOTHING26 DO_NOTHING
27 };27 };
28 28
interface/src/DiscoverabilityManager.cpp+1−1
@@ -25,7 +25,7 @@ · 24 unmodified lines
25 25
26#include <QThread>26#include <QThread>
27 27
28const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Friends;28const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Connections;
29 29
30DiscoverabilityManager::DiscoverabilityManager() :30DiscoverabilityManager::DiscoverabilityManager() :
31 _mode("discoverabilityMode", DEFAULT_DISCOVERABILITY_MODE)31 _mode("discoverabilityMode", DEFAULT_DISCOVERABILITY_MODE)
interface/src/Menu.cpp+6−0
@@ -405,6 +405,12 @@ Menu::Menu() { · 404 unmodified lines
405#endif405#endif
406 406
407 407
408 {
409 auto action = addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderClearKtxCache);
410 connect(action, &QAction::triggered, []{
411 Setting::Handle<int>(KTXCache::SETTING_VERSION_NAME, KTXCache::INVALID_VERSION).set(KTXCache::INVALID_VERSION);
412 });
413 }
408 414
409 // Developer > Render > LOD Tools415 // Developer > Render > LOD Tools
410 addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0,416 addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0,
interface/src/Menu.h+1−0
@@ -142,6 +142,7 @@ namespace MenuOption { · 141 unmodified lines
142 const QString Quit = "Quit";142 const QString Quit = "Quit";
143 const QString ReloadAllScripts = "Reload All Scripts";143 const QString ReloadAllScripts = "Reload All Scripts";
144 const QString ReloadContent = "Reload Content (Clears all caches)";144 const QString ReloadContent = "Reload Content (Clears all caches)";
145 const QString RenderClearKtxCache = "Clear KTX Cache (requires restart)";
145 const QString RenderMaxTextureMemory = "Maximum Texture Memory";146 const QString RenderMaxTextureMemory = "Maximum Texture Memory";
146 const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";147 const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";
147 const QString RenderMaxTexture4MB = "4 MB";148 const QString RenderMaxTexture4MB = "4 MB";
interface/src/avatar/MyAvatar.cpp+276−57
@@ -294,6 +294,7 @@ void MyAvatar::simulateAttachments(float deltaTime) { · 293 unmodified lines
294QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) {294QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) {
295 CameraMode mode = qApp->getCamera().getMode();295 CameraMode mode = qApp->getCamera().getMode();
296 _globalPosition = getPosition();296 _globalPosition = getPosition();
297 // This might not be right! Isn't the capsule local offset in avatar space, and don't we need to add the radius to the y as well? -HRS 5/26/17
297 _globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();298 _globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();
298 _globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight();299 _globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight();
299 _globalBoundingBoxDimensions.z = _characterController.getCapsuleRadius();300 _globalBoundingBoxDimensions.z = _characterController.getCapsuleRadius();
@@ -409,7 +410,7 @@ void MyAvatar::update(float deltaTime) { · 109 unmodified lines
409 // update moving average of HMD facing in xz plane.410 // update moving average of HMD facing in xz plane.
410 const float HMD_FACING_TIMESCALE = 4.0f; // very slow average411 const float HMD_FACING_TIMESCALE = 4.0f; // very slow average
411 float tau = deltaTime / HMD_FACING_TIMESCALE;412 float tau = deltaTime / HMD_FACING_TIMESCALE;
412 _hmdSensorFacingMovingAverage = lerp(_hmdSensorFacingMovingAverage, _hmdSensorFacing, tau);413 _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau);
413 414
414 if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) {415 if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) {
415 _rotationChanged = usecTimestampNow();416 _rotationChanged = usecTimestampNow();
@@ -417,16 +418,18 @@ void MyAvatar::update(float deltaTime) { · 1 unmodified line
417 }418 }
418 419
419#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE420#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE
420 glm::vec3 p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacingMovingAverage.x, 0.0f, _hmdSensorFacingMovingAverage.y));421 glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getHeadControllerPoseInAvatarFrame() *
422 glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y));
421 DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f));423 DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f));
422 p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacing.x, 0.0f, _hmdSensorFacing.y));424 p = transformPoint(getSensorToWorldMatrix(), getHMDSensorPosition() +
425 glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y));
423 DebugDraw::getInstance().addMarker("facing", getOrientation(), p, glm::vec4(1.0f));426 DebugDraw::getInstance().addMarker("facing", getOrientation(), p, glm::vec4(1.0f));
424#endif427#endif
425 428
426 if (_goToPending) {429 if (_goToPending) {
427 setPosition(_goToPosition);430 setPosition(_goToPosition);
428 setOrientation(_goToOrientation);431 setOrientation(_goToOrientation);
429 _hmdSensorFacingMovingAverage = _hmdSensorFacing; // reset moving average432 _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average
430 _goToPending = false;433 _goToPending = false;
431 // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes434 // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes
432 // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so).435 // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so).
@@ -434,6 +437,13 @@ void MyAvatar::update(float deltaTime) { · 1 unmodified line
434 // so we update now. It's ok if it updates again in the normal way.437 // so we update now. It's ok if it updates again in the normal way.
435 updateSensorToWorldMatrix();438 updateSensorToWorldMatrix();
436 emit positionGoneTo();439 emit positionGoneTo();
440 // Run safety tests as soon as we can after goToLocation, or clear if we're not colliding.
441 _physicsSafetyPending = getCollisionsEnabled();
442 }
443 if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) {
444 // When needed and ready, arrange to check and fix.
445 _physicsSafetyPending = false;
446 safeLanding(_goToPosition); // no-op if already safe
437 }447 }
438 448
439 Head* head = getHead();449 Head* head = getHead();
@@ -448,6 +458,7 @@ void MyAvatar::update(float deltaTime) { · 8 unmodified lines
448 setAudioAverageLoudness(audio->getAudioAverageInputLoudness());458 setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
449 459
450 glm::vec3 halfBoundingBoxDimensions(_characterController.getCapsuleRadius(), _characterController.getCapsuleHalfHeight(), _characterController.getCapsuleRadius());460 glm::vec3 halfBoundingBoxDimensions(_characterController.getCapsuleRadius(), _characterController.getCapsuleHalfHeight(), _characterController.getCapsuleRadius());
461 // This might not be right! Isn't the capsule local offset in avatar space? -HRS 5/26/17
451 halfBoundingBoxDimensions += _characterController.getCapsuleLocalOffset();462 halfBoundingBoxDimensions += _characterController.getCapsuleLocalOffset();
452 QMetaObject::invokeMethod(audio.data(), "setAvatarBoundingBoxParameters",463 QMetaObject::invokeMethod(audio.data(), "setAvatarBoundingBoxParameters",
453 Q_ARG(glm::vec3, (getPosition() - halfBoundingBoxDimensions)),464 Q_ARG(glm::vec3, (getPosition() - halfBoundingBoxDimensions)),
@@ -633,15 +644,21 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { · 179 unmodified lines
633 _hmdSensorMatrix = hmdSensorMatrix;644 _hmdSensorMatrix = hmdSensorMatrix;
634 auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix);645 auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix);
635 646
636 if (newHmdSensorPosition != _hmdSensorPosition &&647 if (newHmdSensorPosition != getHMDSensorPosition() &&
637 glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) {648 glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) {
638 qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition;649 qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition;
639 // Ignore unreasonable HMD sensor data650 // Ignore unreasonable HMD sensor data
640 return;651 return;
641 }652 }
653
642 _hmdSensorPosition = newHmdSensorPosition;654 _hmdSensorPosition = newHmdSensorPosition;
643 _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);655 _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
644 _hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);656 auto headPose = _headControllerPoseInSensorFrameCache.get();
657 if (headPose.isValid()) {
658 _headControllerFacing = getFacingDir2D(headPose.rotation);
659 } else {
660 _headControllerFacing = glm::vec2(1.0f, 0.0f);
661 }
645}662}
646 663
647void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache<glm::mat4>& matrixCache) {664void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache<glm::mat4>& matrixCache) {
@@ -679,7 +696,7 @@ void MyAvatar::updateSensorToWorldMatrix() { · 31 unmodified lines
679 696
680// Update avatar head rotation with sensor data697// Update avatar head rotation with sensor data
681void MyAvatar::updateFromTrackers(float deltaTime) {698void MyAvatar::updateFromTrackers(float deltaTime) {
682 glm::vec3 estimatedPosition, estimatedRotation;699 glm::vec3 estimatedRotation;
683 700
684 bool inHmd = qApp->isHMDMode();701 bool inHmd = qApp->isHMDMode();
685 bool playing = DependencyManager::get<recording::Deck>()->isPlaying();702 bool playing = DependencyManager::get<recording::Deck>()->isPlaying();
@@ -690,11 +707,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 4 unmodified lines
690 FaceTracker* tracker = qApp->getActiveFaceTracker();707 FaceTracker* tracker = qApp->getActiveFaceTracker();
691 bool inFacetracker = tracker && !FaceTracker::isMuted();708 bool inFacetracker = tracker && !FaceTracker::isMuted();
692 709
693 if (inHmd) {710 if (inFacetracker) {
694 estimatedPosition = extractTranslation(getHMDSensorMatrix());
695 estimatedPosition.x *= -1.0f;
696 } else if (inFacetracker) {
697 estimatedPosition = tracker->getHeadTranslation();
698 estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));711 estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));
699 }712 }
700 713
@@ -781,6 +794,77 @@ controller::Pose MyAvatar::getRightHandTipPose() const { · 80 unmodified lines
781 return pose;794 return pose;
782}795}
783 796
797glm::vec3 MyAvatar::worldToJointPoint(const glm::vec3& position, const int jointIndex) const {
798 glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified
799 glm::quat jointRot = getRotation();//default value if no or invalid joint specified
800 if (jointIndex != -1) {
801 if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
802 _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
803 } else {
804 qWarning() << "Invalid joint index specified: " << jointIndex;
805 }
806 }
807 glm::vec3 modelOffset = position - jointPos;
808 glm::vec3 jointSpacePosition = glm::inverse(jointRot) * modelOffset;
809
810 return jointSpacePosition;
811}
812
813glm::vec3 MyAvatar::worldToJointDirection(const glm::vec3& worldDir, const int jointIndex) const {
814 glm::quat jointRot = getRotation();//default value if no or invalid joint specified
815 if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
816 qWarning() << "Invalid joint index specified: " << jointIndex;
817 }
818
819 glm::vec3 jointSpaceDir = glm::inverse(jointRot) * worldDir;
820 return jointSpaceDir;
821}
822
823glm::quat MyAvatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const {
824 glm::quat jointRot = getRotation();//default value if no or invalid joint specified
825 if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
826 qWarning() << "Invalid joint index specified: " << jointIndex;
827 }
828 glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot;
829 return jointSpaceRot;
830}
831
832glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int jointIndex) const {
833 glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified
834 glm::quat jointRot = getRotation();//default value if no or invalid joint specified
835
836 if (jointIndex != -1) {
837 if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
838 _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
839 } else {
840 qWarning() << "Invalid joint index specified: " << jointIndex;
841 }
842 }
843
844 glm::vec3 worldOffset = jointRot * jointSpacePos;
845 glm::vec3 worldPos = jointPos + worldOffset;
846
847 return worldPos;
848}
849
850glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const {
851 glm::quat jointRot = getRotation();//default value if no or invalid joint specified
852 if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
853 qWarning() << "Invalid joint index specified: " << jointIndex;
854 }
855 glm::vec3 worldDir = jointRot * jointSpaceDir;
856 return worldDir;
857}
858
859glm::quat MyAvatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const {
860 glm::quat jointRot = getRotation();//default value if no or invalid joint specified
861 if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
862 qWarning() << "Invalid joint index specified: " << jointIndex;
863 }
864 glm::quat worldRot = jointRot * jointSpaceRot;
865 return worldRot;
866}
867
784// virtual868// virtual
785void MyAvatar::render(RenderArgs* renderArgs) {869void MyAvatar::render(RenderArgs* renderArgs) {
786 // don't render if we've been asked to disable local rendering870 // don't render if we've been asked to disable local rendering
@@ -1479,12 +1563,12 @@ void MyAvatar::updateMotors() { · 692 unmodified lines
1479 if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {1563 if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
1480 if (_characterController.getState() == CharacterController::State::Hover ||1564 if (_characterController.getState() == CharacterController::State::Hover ||
1481 _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {1565 _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
1482 motorRotation = getMyHead()->getCameraOrientation();1566 motorRotation = getMyHead()->getHeadOrientation();
1483 } else {1567 } else {
1484 // non-hovering = walking: follow camera twist about vertical but not lift1568 // non-hovering = walking: follow camera twist about vertical but not lift
1485 // so we decompose camera's rotation and store the twist part in motorRotation1569 // so we decompose camera's rotation and store the twist part in motorRotation
1486 glm::quat liftRotation;1570 glm::quat liftRotation;
1487 swingTwistDecomposition(getMyHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation);1571 swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation);
1488 }1572 }
1489 const float DEFAULT_MOTOR_TIMESCALE = 0.2f;1573 const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
1490 const float INVALID_MOTOR_TIMESCALE = 1.0e6f;1574 const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
@@ -1498,7 +1582,7 @@ void MyAvatar::updateMotors() { · 7 unmodified lines
1498 }1582 }
1499 if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) {1583 if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) {
1500 if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) {1584 if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) {
1501 motorRotation = getMyHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);1585 motorRotation = getMyHead()->getHeadOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
1502 } else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) {1586 } else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) {
1503 motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);1587 motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
1504 } else {1588 } else {
@@ -1548,6 +1632,10 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { · 43 unmodified lines
1548 1632
1549 if (_characterController.isEnabledAndReady()) {1633 if (_characterController.isEnabledAndReady()) {
1550 setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());1634 setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
1635 if (_characterController.isStuck()) {
1636 _physicsSafetyPending = true;
1637 _goToPosition = getPosition();
1638 }
1551 } else {1639 } else {
1552 setVelocity(getVelocity() + _characterController.getFollowVelocity());1640 setVelocity(getVelocity() + _characterController.getFollowVelocity());
1553 }1641 }
@@ -1847,7 +1935,7 @@ void MyAvatar::updateOrientation(float deltaTime) { · 293 unmodified lines
1847 if (getCharacterController()->getState() == CharacterController::State::Hover) {1935 if (getCharacterController()->getState() == CharacterController::State::Hover) {
1848 1936
1849 // This is the direction the user desires to fly in.1937 // This is the direction the user desires to fly in.
1850 glm::vec3 desiredFacing = getMyHead()->getCameraOrientation() * Vectors::UNIT_Z;1938 glm::vec3 desiredFacing = getMyHead()->getHeadOrientation() * Vectors::UNIT_Z;
1851 desiredFacing.y = 0.0f;1939 desiredFacing.y = 0.0f;
1852 1940
1853 // This is our reference frame, it is captured when the user begins to move.1941 // This is our reference frame, it is captured when the user begins to move.
@@ -1886,11 +1974,9 @@ void MyAvatar::updateOrientation(float deltaTime) { · 32 unmodified lines
1886 1974
1887 getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);1975 getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
1888 1976
1889 if (qApp->isHMDMode()) {1977 auto headPose = getHeadControllerPoseInAvatarFrame();
1890 glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();1978 if (headPose.isValid()) {
1891 glm::quat bodyOrientation = getWorldBodyOrientation();1979 glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
1892 glm::quat localOrientation = glm::inverse(bodyOrientation) * orientation;
1893
1894 // these angles will be in radians1980 // these angles will be in radians
1895 // ... so they need to be converted to degrees before we do math...1981 // ... so they need to be converted to degrees before we do math...
1896 glm::vec3 euler = glm::eulerAngles(localOrientation) * DEGREES_PER_RADIAN;1982 glm::vec3 euler = glm::eulerAngles(localOrientation) * DEGREES_PER_RADIAN;
@@ -2004,11 +2090,14 @@ void MyAvatar::updatePosition(float deltaTime) { · 107 unmodified lines
2004 }2090 }
2005 2091
2006 // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly.2092 // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly.
2007 if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) {2093 if (!_hoverReferenceCameraFacingIsCaptured &&
2094 (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) {
2008 _hoverReferenceCameraFacingIsCaptured = true;2095 _hoverReferenceCameraFacingIsCaptured = true;
2009 // transform the camera facing vector into sensor space.2096 // transform the camera facing vector into sensor space.
2010 _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getMyHead()->getCameraOrientation() * Vectors::UNIT_Z);2097 _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix),
2011 } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) {2098 getMyHead()->getHeadOrientation() * Vectors::UNIT_Z);
2099 } else if (_hoverReferenceCameraFacingIsCaptured &&
2100 (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) {
2012 _hoverReferenceCameraFacingIsCaptured = false;2101 _hoverReferenceCameraFacingIsCaptured = false;
2013 }2102 }
2014}2103}
@@ -2219,6 +2308,144 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, · 204 unmodified lines
2219 emit transformChanged();2308 emit transformChanged();
2220}2309}
2221 2310
2311void MyAvatar::goToLocationAndEnableCollisions(const glm::vec3& position) { // See use case in safeLanding.
2312 goToLocation(position);
2313 QMetaObject::invokeMethod(this, "setCollisionsEnabled", Qt::QueuedConnection, Q_ARG(bool, true));
2314}
2315bool MyAvatar::safeLanding(const glm::vec3& position) {
2316 // Considers all collision hull or non-collisionless primitive intersections on a vertical line through the point.
2317 // There needs to be a "landing" if:
2318 // a) the closest above and the closest below are less than the avatar capsule height apart, or
2319 // b) the above point is the top surface of an entity, indicating that we are inside it.
2320 // If no landing is required, we go to that point directly and return false;
2321 // When a landing is required by a, we find the highest intersection on that closest-agbove entity
2322 // (which may be that same "nearest above intersection"). That highest intersection is the candidate landing point.
2323 // For b, use that top surface point.
2324 // We then place our feet there, recurse with new capsule center point, and return true.
2325
2326 if (QThread::currentThread() != thread()) {
2327 bool result;
2328 QMetaObject::invokeMethod(this, "safeLanding", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const glm::vec3&, position));
2329 return result;
2330 }
2331 glm::vec3 better;
2332 if (!requiresSafeLanding(position, better)) {
2333 return false;
2334 }
2335 if (!getCollisionsEnabled()) {
2336 goToLocation(better); // recurses on next update
2337 } else { // If you try to go while stuck, physics will keep you stuck.
2338 setCollisionsEnabled(false);
2339 // Don't goToLocation just yet. Yield so that physics can act on the above.
2340 QMetaObject::invokeMethod(this, "goToLocationAndEnableCollisions", Qt::QueuedConnection, // The equivalent of javascript nextTick
2341 Q_ARG(glm::vec3, better));
2342 }
2343 return true;
2344}
2345
2346// If position is not reliably safe from being stuck by physics, answer true and place a candidate better position in betterPositionOut.
2347bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& betterPositionOut) {
2348 // We begin with utilities and tests. The Algorithm in four parts is below.
2349 auto halfHeight = _characterController.getCapsuleHalfHeight() + _characterController.getCapsuleRadius();
2350 if (halfHeight == 0) {
2351 return false; // zero height avatar
2352 }
2353 auto entityTree = DependencyManager::get<EntityTreeRenderer>()->getTree();
2354 if (!entityTree) {
2355 return false; // no entity tree
2356 }
2357 // More utilities.
2358 const auto offset = getOrientation() *_characterController.getCapsuleLocalOffset();
2359 const auto capsuleCenter = positionIn + offset;
2360 const auto up = _worldUpDirection, down = -up;
2361 glm::vec3 upperIntersection, upperNormal, lowerIntersection, lowerNormal;
2362 EntityItemID upperId, lowerId;
2363 QVector<EntityItemID> include{}, ignore{};
2364 auto mustMove = [&] { // Place bottom of capsule at the upperIntersection, and check again based on the capsule center.
2365 betterPositionOut = upperIntersection + (up * halfHeight) - offset;
2366 return true;
2367 };
2368 auto findIntersection = [&](const glm::vec3& startPointIn, const glm::vec3& directionIn, glm::vec3& intersectionOut, EntityItemID& entityIdOut, glm::vec3& normalOut) {
2369 OctreeElementPointer element;
2370 EntityItemPointer intersectedEntity = NULL;
2371 float distance;
2372 BoxFace face;
2373 const bool visibleOnly = false;
2374 // This isn't quite what we really want here. findRayIntersection always works on mesh, skipping entirely based on collidable.
2375 // What we really want is to use the collision hull!
2376 // See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders
2377 const bool collidableOnly = true;
2378 const bool precisionPicking = true;
2379 const auto lockType = Octree::Lock; // Should we refactor to take a lock just once?
2380 bool* accurateResult = NULL;
2381
2382 bool intersects = entityTree->findRayIntersection(startPointIn, directionIn, include, ignore, visibleOnly, collidableOnly, precisionPicking,
2383 element, distance, face, normalOut, (void**)&intersectedEntity, lockType, accurateResult);
2384 if (!intersects || !intersectedEntity) {
2385 return false;
2386 }
2387 intersectionOut = startPointIn + (directionIn * distance);
2388 entityIdOut = intersectedEntity->getEntityItemID();
2389 return true;
2390 };
2391
2392 // The Algorithm, in four parts:
2393
2394 if (!findIntersection(capsuleCenter, up, upperIntersection, upperId, upperNormal)) {
2395 // We currently believe that physics will reliably push us out if our feet are embedded,
2396 // as long as our capsule center is out and there's room above us. Here we have those
2397 // conditions, so no need to check our feet below.
2398 return false; // nothing above
2399 }
2400
2401 if (!findIntersection(capsuleCenter, down, lowerIntersection, lowerId, lowerNormal)) {
2402 // Our head may be embedded, but our center is out and there's room below. See corresponding comment above.
2403 return false; // nothing below
2404 }
2405
2406 // See if we have room between entities above and below, but that we are not contained.
2407 // First check if the surface above us is the bottom of something, and the surface below us it the top of something.
2408 // I.e., we are in a clearing between two objects.
2409 if (isDown(upperNormal) && isUp(lowerNormal)) {
2410 auto spaceBetween = glm::distance(upperIntersection, lowerIntersection);
2411 const float halfHeightFactor = 2.5f; // Until case 5003 is fixed (and maybe after?), we need a fudge factor. Also account for content modelers not being precise.
2412 if (spaceBetween > (halfHeightFactor * halfHeight)) {
2413 // There is room for us to fit in that clearing. If there wasn't, physics would oscilate us between the objects above and below.
2414 // We're now going to iterate upwards through successive upperIntersections, testing to see if we're contained within the top surface of some entity.
2415 // There will be one of two outcomes:
2416 // a) We're not contained, so we have enough room and our position is good.
2417 // b) We are contained, so we'll bail out of this but try again at a position above the containing entity.
2418 const int iterationLimit = 1000;
2419 for (int counter = 0; counter < iterationLimit; counter++) {
2420 ignore.push_back(upperId);
2421 if (!findIntersection(upperIntersection, up, upperIntersection, upperId, upperNormal)) {
2422 // We're not inside an entity, and from the nested tests, we have room between what is above and below. So position is good!
2423 return false; // enough room
2424 }
2425 if (isUp(upperNormal)) {
2426 // This new intersection is the top surface of an entity that we have not yet seen, which means we're contained within it.
2427 // We could break here and recurse from the top of the original ceiling, but since we've already done the work to find the top
2428 // of the enclosing entity, let's put our feet at upperIntersection and start over.
2429 return mustMove();
2430 }
2431 // We found a new bottom surface, which we're not interested in.
2432 // But there could still be a top surface above us for an entity we haven't seen, so keep looking upward.
2433 }
2434 qCDebug(interfaceapp) << "Loop in requiresSafeLanding. Floor/ceiling do not make sense.";
2435 }
2436 }
2437
2438 include.push_back(upperId); // We're now looking for the intersection from above onto this entity.
2439 const float big = (float)TREE_SCALE;
2440 const auto skyHigh = up * big;
2441 auto fromAbove = capsuleCenter + skyHigh;
2442 if (!findIntersection(fromAbove, down, upperIntersection, upperId, upperNormal)) {
2443 return false; // Unable to find a landing
2444 }
2445 // Our arbitrary rule is to always go up. There's no need to look down or sideways for a "closer" safe candidate.
2446 return mustMove();
2447}
2448
2222void MyAvatar::updateMotionBehaviorFromMenu() {2449void MyAvatar::updateMotionBehaviorFromMenu() {
2223 2450
2224 if (QThread::currentThread() != thread()) {2451 if (QThread::currentThread() != thread()) {
@@ -2322,36 +2549,27 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const { · 97 unmodified lines
2322 }2549 }
2323}2550}
2324 2551
2325glm::vec3 MyAvatar::getWorldBodyPosition() const {
2326 return transformPoint(_sensorToWorldMatrix, extractTranslation(_bodySensorMatrix));
2327}
2328
2329glm::quat MyAvatar::getWorldBodyOrientation() const {
2330 return glm::quat_cast(_sensorToWorldMatrix * _bodySensorMatrix);
2331}
2332
2333// old school meat hook style2552// old school meat hook style
2334glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {2553glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
2335 2554 glm::vec3 headPosition;
2336 // HMD is in sensor space.2555 glm::quat headOrientation;
2337 const glm::vec3 hmdPosition = getHMDSensorPosition();2556 auto headPose = getHeadControllerPoseInSensorFrame();
2338 const glm::quat hmdOrientation = getHMDSensorOrientation();2557 if (headPose.isValid()) {
2339 const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation);2558 headPosition = getHeadControllerPoseInSensorFrame().translation;
2559 headOrientation = getHeadControllerPoseInSensorFrame().rotation * Quaternions::Y_180;
2560 }
2561 const glm::quat headOrientationYawOnly = cancelOutRollAndPitch(headOrientation);
2340 2562
2341 const Rig& rig = _skeletonModel->getRig();2563 const Rig& rig = _skeletonModel->getRig();
2342 int rightEyeIndex = rig.indexOfJoint("RightEye");2564 int headIndex = rig.indexOfJoint("Head");
2343 int leftEyeIndex = rig.indexOfJoint("LeftEye");
2344 int neckIndex = rig.indexOfJoint("Neck");2565 int neckIndex = rig.indexOfJoint("Neck");
2345 int hipsIndex = rig.indexOfJoint("Hips");2566 int hipsIndex = rig.indexOfJoint("Hips");
2346 2567
2347 glm::vec3 rigMiddleEyePos = DEFAULT_AVATAR_MIDDLE_EYE_POS;2568 glm::vec3 rigHeadPos = headIndex != -1 ? rig.getAbsoluteDefaultPose(headIndex).trans() : DEFAULT_AVATAR_HEAD_POS;
2348 if (leftEyeIndex >= 0 && rightEyeIndex >= 0) {
2349 rigMiddleEyePos = (rig.getAbsoluteDefaultPose(leftEyeIndex).trans() + rig.getAbsoluteDefaultPose(rightEyeIndex).trans()) / 2.0f;
2350 }
2351 glm::vec3 rigNeckPos = neckIndex != -1 ? rig.getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS;2569 glm::vec3 rigNeckPos = neckIndex != -1 ? rig.getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS;
2352 glm::vec3 rigHipsPos = hipsIndex != -1 ? rig.getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS;2570 glm::vec3 rigHipsPos = hipsIndex != -1 ? rig.getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS;
2353 2571
2354 glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos);2572 glm::vec3 localHead = (rigHeadPos - rigHipsPos);
2355 glm::vec3 localNeck = (rigNeckPos - rigHipsPos);2573 glm::vec3 localNeck = (rigNeckPos - rigHipsPos);
2356 2574
2357 // apply simplistic head/neck model2575 // apply simplistic head/neck model
@@ -2360,11 +2578,11 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { · 2 unmodified lines
2360 // eyeToNeck offset is relative full HMD orientation.2578 // eyeToNeck offset is relative full HMD orientation.
2361 // while neckToRoot offset is only relative to HMDs yaw.2579 // while neckToRoot offset is only relative to HMDs yaw.
2362 // Y_180 is necessary because rig is z forward and hmdOrientation is -z forward2580 // Y_180 is necessary because rig is z forward and hmdOrientation is -z forward
2363 glm::vec3 eyeToNeck = hmdOrientation * Quaternions::Y_180 * (localNeck - localEyes);2581 glm::vec3 headToNeck = headOrientation * Quaternions::Y_180 * (localNeck - localHead);
2364 glm::vec3 neckToRoot = hmdOrientationYawOnly * Quaternions::Y_180 * -localNeck;2582 glm::vec3 neckToRoot = headOrientationYawOnly * Quaternions::Y_180 * -localNeck;
2365 glm::vec3 bodyPos = hmdPosition + eyeToNeck + neckToRoot;2583 glm::vec3 bodyPos = headPosition + headToNeck + neckToRoot;
2366 2584
2367 return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos);2585 return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos);
2368}2586}
2369 2587
2370glm::vec3 MyAvatar::getPositionForAudio() {2588glm::vec3 MyAvatar::getPositionForAudio() {
@@ -2480,7 +2698,7 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co · 109 unmodified lines
2480 } else {2698 } else {
2481 const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees2699 const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
2482 glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);2700 glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
2483 return glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;2701 return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
2484 }2702 }
2485}2703}
2486 2704
@@ -2517,8 +2735,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co · 30 unmodified lines
2517 return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);2735 return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
2518}2736}
2519 2737
2520void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) {2738void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
2521 _desiredBodyMatrix = desiredBodyMatrix;2739 const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
2522 2740
2523 if (myAvatar.getHMDLeanRecenterEnabled()) {2741 if (myAvatar.getHMDLeanRecenterEnabled()) {
2524 if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {2742 if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
@@ -2532,7 +2750,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat · 7 unmodified lines
2532 }2750 }
2533 }2751 }
2534 2752
2535 glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix;2753 glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix;
2536 glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix;2754 glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix;
2537 2755
2538 AnimPose followWorldPose(currentWorldMatrix);2756 AnimPose followWorldPose(currentWorldMatrix);
@@ -2627,9 +2845,10 @@ glm::mat4 MyAvatar::computeCameraRelativeHandControllerMatrix(const glm::mat4& c · 88 unmodified lines
2627 cameraWorldMatrix *= createMatFromScaleQuatAndPos(vec3(-1.0f, 1.0f, 1.0f), glm::quat(), glm::vec3());2845 cameraWorldMatrix *= createMatFromScaleQuatAndPos(vec3(-1.0f, 1.0f, 1.0f), glm::quat(), glm::vec3());
2628 }2846 }
2629 2847
2630 // compute a NEW sensorToWorldMatrix for the camera. The equation is cameraWorldMatrix = cameraSensorToWorldMatrix * _hmdSensorMatrix.2848 // compute a NEW sensorToWorldMatrix for the camera.
2849 // The equation is cameraWorldMatrix = cameraSensorToWorldMatrix * _hmdSensorMatrix.
2631 // here we solve for the unknown cameraSensorToWorldMatrix.2850 // here we solve for the unknown cameraSensorToWorldMatrix.
2632 glm::mat4 cameraSensorToWorldMatrix = cameraWorldMatrix * glm::inverse(_hmdSensorMatrix);2851 glm::mat4 cameraSensorToWorldMatrix = cameraWorldMatrix * glm::inverse(getHMDSensorMatrix());
2633 2852
2634 // Using the new cameraSensorToWorldMatrix, compute where the controller is in world space.2853 // Using the new cameraSensorToWorldMatrix, compute where the controller is in world space.
2635 glm::mat4 controllerWorldMatrix = cameraSensorToWorldMatrix * controllerSensorMatrix;2854 glm::mat4 controllerWorldMatrix = cameraSensorToWorldMatrix * controllerSensorMatrix;
@@ -2770,7 +2989,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const { · 134 unmodified lines
2770 auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);2989 auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);
2771 return createMatFromQuatAndPos(leftFootRot, leftFootPos);2990 return createMatFromQuatAndPos(leftFootRot, leftFootPos);
2772 } else {2991 } else {
2773 return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_POS, DEFAULT_AVATAR_LEFTFOOT_POS);2992 return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS);
2774 }2993 }
2775}2994}
2776 2995
@@ -2782,7 +3001,7 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const { · 5 unmodified lines
2782 auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);3001 auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);
2783 return createMatFromQuatAndPos(rightFootRot, rightFootPos);3002 return createMatFromQuatAndPos(rightFootRot, rightFootPos);
2784 } else {3003 } else {
2785 return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_POS, DEFAULT_AVATAR_RIGHTFOOT_POS);3004 return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS);
2786 }3005 }
2787}3006}
2788 3007
@@ -2805,7 +3024,7 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const { · 16 unmodified lines
2805 auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);3024 auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);
2806 return createMatFromQuatAndPos(leftArmRot, leftArmPos);3025 return createMatFromQuatAndPos(leftArmRot, leftArmPos);
2807 } else {3026 } else {
2808 return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS);3027 return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS);
2809 }3028 }
2810}3029}
2811 3030
interface/src/avatar/MyAvatar.h+24−10
@@ -185,7 +185,6 @@ public: · 184 unmodified lines
185 const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }185 const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
186 const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }186 const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
187 const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }187 const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }
188 const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; }
189 188
190 Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar);189 Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar);
191 Q_INVOKABLE QVariant getOrientationVar() const;190 Q_INVOKABLE QVariant getOrientationVar() const;
@@ -379,6 +378,15 @@ public: · 187 unmodified lines
379 Q_INVOKABLE controller::Pose getLeftHandTipPose() const;378 Q_INVOKABLE controller::Pose getLeftHandTipPose() const;
380 Q_INVOKABLE controller::Pose getRightHandTipPose() const;379 Q_INVOKABLE controller::Pose getRightHandTipPose() const;
381 380
381 // world-space to avatar-space rigconversion functions
382 Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const;
383 Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const;
384 Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const;
385
386 Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const;
387 Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const;
388 Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const;
389
382 AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }390 AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
383 void updateLookAtTargetAvatar();391 void updateLookAtTargetAvatar();
384 void clearLookAtTargetAvatar();392 void clearLookAtTargetAvatar();
@@ -470,6 +478,8 @@ public: · 85 unmodified lines
470 controller::Pose getHeadControllerPoseInSensorFrame() const;478 controller::Pose getHeadControllerPoseInSensorFrame() const;
471 controller::Pose getHeadControllerPoseInWorldFrame() const;479 controller::Pose getHeadControllerPoseInWorldFrame() const;
472 controller::Pose getHeadControllerPoseInAvatarFrame() const;480 controller::Pose getHeadControllerPoseInAvatarFrame() const;
481 const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; }
482
473 483
474 void setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);484 void setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
475 controller::Pose getLeftArmControllerPoseInSensorFrame() const;485 controller::Pose getLeftArmControllerPoseInSensorFrame() const;
@@ -509,6 +519,10 @@ public: · 33 unmodified lines
509 // results are in HMD frame519 // results are in HMD frame
510 glm::mat4 deriveBodyFromHMDSensor() const;520 glm::mat4 deriveBodyFromHMDSensor() const;
511 521
522 Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up.
523 Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; };
524
525
512public slots:526public slots:
513 void increaseSize();527 void increaseSize();
514 void decreaseSize();528 void decreaseSize();
@@ -518,6 +532,8 @@ public slots: · 3 unmodified lines
518 bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),532 bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),
519 bool shouldFaceLocation = false);533 bool shouldFaceLocation = false);
520 void goToLocation(const QVariant& properties);534 void goToLocation(const QVariant& properties);
535 void goToLocationAndEnableCollisions(const glm::vec3& newPosition);
536 bool safeLanding(const glm::vec3& position);
521 537
522 void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject);538 void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject);
523 void clearScaleRestriction();539 void clearScaleRestriction();
@@ -563,9 +579,7 @@ signals: · 39 unmodified lines
563 579
564private:580private:
565 581
566 glm::vec3 getWorldBodyPosition() const;582 bool requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& positionOut);
567 glm::quat getWorldBodyOrientation() const;
568
569 583
570 virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail) override;584 virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail) override;
571 585
@@ -676,13 +690,13 @@ private: · 104 unmodified lines
676 // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access690 // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
677 glm::mat4 _sensorToWorldMatrix { glm::mat4() };691 glm::mat4 _sensorToWorldMatrix { glm::mat4() };
678 692
679 // cache of the current HMD sensor position and orientation693 // cache of the current HMD sensor position and orientation in sensor space.
680 // in sensor space.
681 glm::mat4 _hmdSensorMatrix;694 glm::mat4 _hmdSensorMatrix;
682 glm::quat _hmdSensorOrientation;695 glm::quat _hmdSensorOrientation;
683 glm::vec3 _hmdSensorPosition;696 glm::vec3 _hmdSensorPosition;
684 glm::vec2 _hmdSensorFacing; // facing vector in xz plane697 // cache head controller pose in sensor space
685 glm::vec2 _hmdSensorFacingMovingAverage { 0, 0 }; // facing vector in xz plane698 glm::vec2 _headControllerFacing; // facing vector in xz plane
699 glm::vec2 _headControllerFacingMovingAverage { 0, 0 }; // facing vector in xz plane
686 700
687 // cache of the current body position and orientation of the avatar's body,701 // cache of the current body position and orientation of the avatar's body,
688 // in sensor space.702 // in sensor space.
@@ -697,7 +711,6 @@ private: · 8 unmodified lines
697 Vertical,711 Vertical,
698 NumFollowTypes712 NumFollowTypes
699 };713 };
700 glm::mat4 _desiredBodyMatrix;
701 float _timeRemaining[NumFollowTypes];714 float _timeRemaining[NumFollowTypes];
702 715
703 void deactivate();716 void deactivate();
@@ -716,7 +729,8 @@ private: · 12 unmodified lines
716 };729 };
717 FollowHelper _follow;730 FollowHelper _follow;
718 731
719 bool _goToPending;732 bool _goToPending { false };
733 bool _physicsSafetyPending { false };
720 glm::vec3 _goToPosition;734 glm::vec3 _goToPosition;
721 glm::quat _goToOrientation;735 glm::quat _goToOrientation;
722 736
interface/src/avatar/MyHead.cpp+9−8
@@ -26,19 +26,20 @@ using namespace std; · 25 unmodified lines
26MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) {26MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) {
27}27}
28 28
29glm::quat MyHead::getCameraOrientation() const {29glm::quat MyHead::getHeadOrientation() const {
30 // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so30 // NOTE: Head::getHeadOrientation() is not used for orienting the camera "view" while in Oculus mode, so
31 // you may wonder why this code is here. This method will be called while in Oculus mode to determine how31 // you may wonder why this code is here. This method will be called while in Oculus mode to determine how
32 // to change the driving direction while in Oculus mode. It is used to support driving toward where you're32 // to change the driving direction while in Oculus mode. It is used to support driving toward where you're
33 // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not33 // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
34 // always the same.34 // always the same.
35 if (qApp->isHMDMode()) {35
36 MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);36 MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
37 return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();37 auto headPose = myAvatar->getHeadControllerPoseInWorldFrame();
38 } else {38 if (headPose.isValid()) {
39 Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);39 return headPose.rotation * Quaternions::Y_180;
40 return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
41 }40 }
41
42 return myAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
42}43}
43 44
44void MyHead::simulate(float deltaTime) {45void MyHead::simulate(float deltaTime) {
interface/src/avatar/MyHead.h+1−1
@@ -18,7 +18,7 @@ public: · 17 unmodified lines
18 explicit MyHead(MyAvatar* owningAvatar);18 explicit MyHead(MyAvatar* owningAvatar);
19 19
20 /// \return orientationBody * orientationBasePitch20 /// \return orientationBody * orientationBasePitch
21 glm::quat getCameraOrientation() const;21 glm::quat getHeadOrientation() const;
22 void simulate(float deltaTime) override;22 void simulate(float deltaTime) override;
23 23
24private:24private:
interface/src/avatar/MySkeletonModel.cpp+8−16
@@ -52,26 +52,18 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 51 unmodified lines
52 // input action is the highest priority source for head orientation.52 // input action is the highest priority source for head orientation.
53 auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame();53 auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame();
54 if (avatarHeadPose.isValid()) {54 if (avatarHeadPose.isValid()) {
55 glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());55 glm::mat4 rigHeadMat = Matrices::Y_180 *
56 createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
56 headParams.rigHeadPosition = extractTranslation(rigHeadMat);57 headParams.rigHeadPosition = extractTranslation(rigHeadMat);
57 headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat);58 headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat);
58 headParams.headEnabled = true;59 headParams.headEnabled = true;
59 } else {60 } else {
60 if (qApp->isHMDMode()) {61 // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and
61 // get HMD position from sensor space into world space, and back into rig space62 // down in desktop mode.
62 glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();63 // preMult 180 is necessary to convert from avatar to rig coordinates.
63 glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation());64 // postMult 180 is necessary to convert head from -z forward to z forward.
64 glm::mat4 worldToRig = glm::inverse(rigToWorld);65 headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
65 glm::mat4 rigHMDMat = worldToRig * worldHMDMat;66 headParams.headEnabled = false;
66 _rig.computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation);
67 headParams.headEnabled = true;
68 } else {
69 // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode.
70 // preMult 180 is necessary to convert from avatar to rig coordinates.
71 // postMult 180 is necessary to convert head from -z forward to z forward.
72 headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
73 headParams.headEnabled = false;
74 }
75 }67 }
76 68
77 auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame();69 auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame();
interface/src/main.cpp+2−6
@@ -24,7 +24,6 @@ · 23 unmodified lines
24#include <SandboxUtils.h>24#include <SandboxUtils.h>
25#include <SharedUtil.h>25#include <SharedUtil.h>
26 26
27
28#include "AddressManager.h"27#include "AddressManager.h"
29#include "Application.h"28#include "Application.h"
30#include "InterfaceLogging.h"29#include "InterfaceLogging.h"
@@ -191,7 +190,7 @@ int main(int argc, const char* argv[]) { · 160 unmodified lines
191 190
192 int exitCode;191 int exitCode;
193 {192 {
194 RunningMarker runningMarker(nullptr, RUNNING_MARKER_FILENAME);193 RunningMarker runningMarker(RUNNING_MARKER_FILENAME);
195 bool runningMarkerExisted = runningMarker.fileExists();194 bool runningMarkerExisted = runningMarker.fileExists();
196 runningMarker.writeRunningMarkerFile();195 runningMarker.writeRunningMarkerFile();
197 196
@@ -200,14 +199,11 @@ int main(int argc, const char* argv[]) { · 2 unmodified lines
200 bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);199 bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
201 QString serverContentPath = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();200 QString serverContentPath = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
202 if (runServer) {201 if (runServer) {
203 SandboxUtils::runLocalSandbox(serverContentPath, true, RUNNING_MARKER_FILENAME, noUpdater);202 SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater);
204 }203 }
205 204
206 Application app(argc, const_cast<char**>(argv), startupTime, runningMarkerExisted);205 Application app(argc, const_cast<char**>(argv), startupTime, runningMarkerExisted);
207 206
208 // Now that the main event loop is setup, launch running marker thread
209 runningMarker.startRunningMarker();
210
211 // If we failed the OpenGLVersion check, log it.207 // If we failed the OpenGLVersion check, log it.
212 if (override) {208 if (override) {
213 auto accountManager = DependencyManager::get<AccountManager>();209 auto accountManager = DependencyManager::get<AccountManager>();
interface/src/networking/CloseEventSender.cpp+95−0
@@ -0,0 +1,95 @@
1//
2// CloseEventSender.cpp
3// interface/src/networking
4//
5// Created by Stephen Birarda on 5/31/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#include <QtCore/QDateTime>
13#include <QtCore/QEventLoop>
14#include <QtCore/QJsonDocument>
15#include <QtNetwork/QNetworkReply>
16
17#include <ThreadHelpers.h>
18#include <AccountManager.h>
19#include <NetworkAccessManager.h>
20#include <NetworkingConstants.h>
21#include <NetworkLogging.h>
22#include <UserActivityLogger.h>
23#include <UUID.h>
24
25#include "CloseEventSender.h"
26
27QNetworkRequest createNetworkRequest() {
28
29 QNetworkRequest request;
30
31 QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
32 requestURL.setPath(USER_ACTIVITY_URL);
33
34 request.setUrl(requestURL);
35
36 auto accountManager = DependencyManager::get<AccountManager>();
37
38 if (accountManager->hasValidAccessToken()) {
39 request.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
40 accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue());
41 }
42
43 request.setRawHeader(METAVERSE_SESSION_ID_HEADER,
44 uuidStringWithoutCurlyBraces(accountManager->getSessionID()).toLocal8Bit());
45
46 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
47
48 request.setPriority(QNetworkRequest::HighPriority);
49
50 return request;
51}
52
53QByteArray postDataForAction(QString action) {
54 return QString("{\"action_name\": \"" + action + "\"}").toUtf8();
55}
56
57QNetworkReply* replyForAction(QString action) {
58 QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
59 return networkAccessManager.post(createNetworkRequest(), postDataForAction(action));
60}
61
62void CloseEventSender::sendQuitEventAsync() {
63 if (UserActivityLogger::getInstance().isEnabled()) {
64 QNetworkReply* reply = replyForAction("quit");
65 connect(reply, &QNetworkReply::finished, this, &CloseEventSender::handleQuitEventFinished);
66 _quitEventStartTimestamp = QDateTime::currentMSecsSinceEpoch();
67 } else {
68 _hasFinishedQuitEvent = true;
69 }
70}
71
72void CloseEventSender::handleQuitEventFinished() {
73 _hasFinishedQuitEvent = true;
74
75 auto reply = qobject_cast<QNetworkReply*>(sender());
76 if (reply->error() == QNetworkReply::NoError) {
77 qCDebug(networking) << "Quit event sent successfully";
78 } else {
79 qCDebug(networking) << "Failed to send quit event -" << reply->errorString();
80 }
81
82 reply->deleteLater();
83}
84
85bool CloseEventSender::hasTimedOutQuitEvent() {
86 const int CLOSURE_EVENT_TIMEOUT_MS = 5000;
87 return _quitEventStartTimestamp != 0
88 && QDateTime::currentMSecsSinceEpoch() - _quitEventStartTimestamp > CLOSURE_EVENT_TIMEOUT_MS;
89}
90
91void CloseEventSender::startThread() {
92 moveToNewNamedThread(this, "CloseEvent Logger Thread", [this] {
93 sendQuitEventAsync();
94 });
95}
interface/src/networking/CloseEventSender.h+42−0
@@ -0,0 +1,42 @@
1//
2// CloseEventSender.h
3// interface/src/networking
4//
5// Created by Stephen Birarda on 5/31/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#ifndef hifi_CloseEventSender_h
13#define hifi_CloseEventSender_h
14
15#include <atomic>
16
17#include <QtCore/QString>
18#include <QtCore/QUuid>
19
20#include <DependencyManager.h>
21
22class CloseEventSender : public QObject, public Dependency {
23 Q_OBJECT
24 SINGLETON_DEPENDENCY
25
26public:
27 void startThread();
28 bool hasTimedOutQuitEvent();
29 bool hasFinishedQuitEvent() { return _hasFinishedQuitEvent; }
30
31public slots:
32 void sendQuitEventAsync();
33
34private slots:
35 void handleQuitEventFinished();
36
37private:
38 std::atomic<bool> _hasFinishedQuitEvent { false };
39 std::atomic<int64_t> _quitEventStartTimestamp;
40};
41
42#endif // hifi_CloseEventSender_h
interface/src/scripting/HMDScriptingInterface.cpp+4−0
@@ -54,6 +54,10 @@ bool HMDScriptingInterface::isHMDAvailable(const QString& name) { · 53 unmodified lines
54 return PluginUtils::isHMDAvailable(name);54 return PluginUtils::isHMDAvailable(name);
55}55}
56 56
57bool HMDScriptingInterface::isHeadControllerAvailable(const QString& name) {
58 return PluginUtils::isHeadControllerAvailable(name);
59}
60
57bool HMDScriptingInterface::isHandControllerAvailable(const QString& name) {61bool HMDScriptingInterface::isHandControllerAvailable(const QString& name) {
58 return PluginUtils::isHandControllerAvailable(name);62 return PluginUtils::isHandControllerAvailable(name);
59}63}
interface/src/scripting/HMDScriptingInterface.h+1−0
@@ -43,6 +43,7 @@ public: · 42 unmodified lines
43 Q_INVOKABLE QString preferredAudioOutput() const;43 Q_INVOKABLE QString preferredAudioOutput() const;
44 44
45 Q_INVOKABLE bool isHMDAvailable(const QString& name = "");45 Q_INVOKABLE bool isHMDAvailable(const QString& name = "");
46 Q_INVOKABLE bool isHeadControllerAvailable(const QString& name = "");
46 Q_INVOKABLE bool isHandControllerAvailable(const QString& name = "");47 Q_INVOKABLE bool isHandControllerAvailable(const QString& name = "");
47 Q_INVOKABLE bool isSubdeviceContainingNameAvailable(const QString& name);48 Q_INVOKABLE bool isSubdeviceContainingNameAvailable(const QString& name);
48 49
interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp+5−4
@@ -9,9 +9,12 @@ · 8 unmodified lines
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//10//
11 11
12#include <QtConcurrent/QtConcurrentRun>
13
14#include <ThreadHelpers.h>
12#include <src/InterfaceLogging.h>15#include <src/InterfaceLogging.h>
13#include <src/ui/AvatarInputs.h>16#include <src/ui/AvatarInputs.h>
14#include <QtConcurrent/QtConcurrentRun>17
15#include "LimitlessVoiceRecognitionScriptingInterface.h"18#include "LimitlessVoiceRecognitionScriptingInterface.h"
16 19
17const float LimitlessVoiceRecognitionScriptingInterface::_audioLevelThreshold = 0.33f;20const float LimitlessVoiceRecognitionScriptingInterface::_audioLevelThreshold = 0.33f;
@@ -24,9 +27,7 @@ LimitlessVoiceRecognitionScriptingInterface::LimitlessVoiceRecognitionScriptingI · 6 unmodified lines
24 connect(&_voiceTimer, &QTimer::timeout, this, &LimitlessVoiceRecognitionScriptingInterface::voiceTimeout);27 connect(&_voiceTimer, &QTimer::timeout, this, &LimitlessVoiceRecognitionScriptingInterface::voiceTimeout);
25 connect(&_connection, &LimitlessConnection::onReceivedTranscription, this, [this](QString transcription){emit onReceivedTranscription(transcription);});28 connect(&_connection, &LimitlessConnection::onReceivedTranscription, this, [this](QString transcription){emit onReceivedTranscription(transcription);});
26 connect(&_connection, &LimitlessConnection::onFinishedSpeaking, this, [this](QString transcription){emit onFinishedSpeaking(transcription);});29 connect(&_connection, &LimitlessConnection::onFinishedSpeaking, this, [this](QString transcription){emit onFinishedSpeaking(transcription);});
27 _connection.moveToThread(&_connectionThread);30 moveToNewNamedThread(&_connection, "Limitless Connection");
28 _connectionThread.setObjectName("Limitless Connection");
29 _connectionThread.start();
30}31}
31 32
32void LimitlessVoiceRecognitionScriptingInterface::update() {33void LimitlessVoiceRecognitionScriptingInterface::update() {
interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.h+0−1
@@ -41,7 +41,6 @@ private: · 40 unmodified lines
41 static const int _voiceTimeoutDuration;41 static const int _voiceTimeoutDuration;
42 42
43 QTimer _voiceTimer;43 QTimer _voiceTimer;
44 QThread _connectionThread;
45 LimitlessConnection _connection;44 LimitlessConnection _connection;
46 45
47 void voiceTimeout();46 void voiceTimeout();
interface/src/ui/ModelsBrowser.cpp+2−6
@@ -23,6 +23,7 @@ · 22 unmodified lines
23#include <QUrlQuery>23#include <QUrlQuery>
24#include <QXmlStreamReader>24#include <QXmlStreamReader>
25 25
26#include <ThreadHelpers.h>
26#include <NetworkAccessManager.h>27#include <NetworkAccessManager.h>
27#include <SharedUtil.h>28#include <SharedUtil.h>
28 29
@@ -84,12 +85,7 @@ ModelsBrowser::ModelsBrowser(FSTReader::ModelType modelsType, QWidget* parent) : · 55 unmodified lines
84 _handler->connect(this, SIGNAL(destroyed()), SLOT(exit()));85 _handler->connect(this, SIGNAL(destroyed()), SLOT(exit()));
85 86
86 // Setup and launch update thread87 // Setup and launch update thread
87 QThread* thread = new QThread();88 moveToNewNamedThread(_handler, "Models Browser");
88 thread->setObjectName("Models Browser");
89 thread->connect(_handler, SIGNAL(destroyed()), SLOT(quit()));
90 thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
91 _handler->moveToThread(thread);
92 thread->start();
93 emit startDownloading();89 emit startDownloading();
94 90
95 // Initialize the view91 // Initialize the view
interface/src/ui/overlays/Text3DOverlay.cpp+13−3
@@ -51,6 +51,16 @@ Text3DOverlay::~Text3DOverlay() { · 50 unmodified lines
51 }51 }
52}52}
53 53
54const QString Text3DOverlay::getText() const {
55 QMutexLocker lock(&_mutex);
56 return _text;
57}
58
59void Text3DOverlay::setText(const QString& text) {
60 QMutexLocker lock(&_mutex);
61 _text = text;
62}
63
54xColor Text3DOverlay::getBackgroundColor() {64xColor Text3DOverlay::getBackgroundColor() {
55 if (_colorPulse == 0.0f) {65 if (_colorPulse == 0.0f) {
56 return _backgroundColor;66 return _backgroundColor;
@@ -125,7 +135,7 @@ void Text3DOverlay::render(RenderArgs* args) { · 68 unmodified lines
125 // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline135 // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline
126 // for a gpu performance increase. Currently,136 // for a gpu performance increase. Currently,
127 // Text renderer sets its own pipeline,137 // Text renderer sets its own pipeline,
128 _textRenderer->draw(batch, 0, 0, _text, textColor, glm::vec2(-1.0f), getDrawInFront());138 _textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), getDrawInFront());
129 // so before we continue, we must reset the pipeline139 // so before we continue, we must reset the pipeline
130 batch.setPipeline(args->_pipeline->pipeline);140 batch.setPipeline(args->_pipeline->pipeline);
131 args->_pipeline->prepare(batch);141 args->_pipeline->prepare(batch);
@@ -188,7 +198,7 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { · 56 unmodified lines
188 198
189QVariant Text3DOverlay::getProperty(const QString& property) {199QVariant Text3DOverlay::getProperty(const QString& property) {
190 if (property == "text") {200 if (property == "text") {
191 return _text;201 return getText();
192 }202 }
193 if (property == "textAlpha") {203 if (property == "textAlpha") {
194 return _textAlpha;204 return _textAlpha;
@@ -231,7 +241,7 @@ QSizeF Text3DOverlay::textSize(const QString& text) const { · 36 unmodified lines
231 return QSizeF(extents.x, extents.y) * pointToWorldScale;241 return QSizeF(extents.x, extents.y) * pointToWorldScale;
232}242}
233 243
234bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, 244bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance,
235 BoxFace &face, glm::vec3& surfaceNormal) {245 BoxFace &face, glm::vec3& surfaceNormal) {
236 Transform transform = getTransform();246 Transform transform = getTransform();
237 applyTransformTo(transform, true);247 applyTransformTo(transform, true);
interface/src/ui/overlays/Text3DOverlay.h+7−6
@@ -12,14 +12,14 @@ · 11 unmodified lines
12#define hifi_Text3DOverlay_h12#define hifi_Text3DOverlay_h
13 13
14#include <QString>14#include <QString>
15 15#include <QtCore/QMutex>
16#include "Billboard3DOverlay.h"16#include "Billboard3DOverlay.h"
17 17
18class TextRenderer3D;18class TextRenderer3D;
19 19
20class Text3DOverlay : public Billboard3DOverlay {20class Text3DOverlay : public Billboard3DOverlay {
21 Q_OBJECT21 Q_OBJECT
22 22
23public:23public:
24 static QString const TYPE;24 static QString const TYPE;
25 virtual QString getType() const override { return TYPE; }25 virtual QString getType() const override { return TYPE; }
@@ -34,7 +34,7 @@ public: · 8 unmodified lines
34 virtual const render::ShapeKey getShapeKey() override;34 virtual const render::ShapeKey getShapeKey() override;
35 35
36 // getters36 // getters
37 const QString& getText() const { return _text; }37 const QString getText() const;
38 float getLineHeight() const { return _lineHeight; }38 float getLineHeight() const { return _lineHeight; }
39 float getLeftMargin() const { return _leftMargin; }39 float getLeftMargin() const { return _leftMargin; }
40 float getTopMargin() const { return _topMargin; }40 float getTopMargin() const { return _topMargin; }
@@ -45,7 +45,7 @@ public: · 4 unmodified lines
45 float getBackgroundAlpha() { return getAlpha(); }45 float getBackgroundAlpha() { return getAlpha(); }
46 46
47 // setters47 // setters
48 void setText(const QString& text) { _text = text; }48 void setText(const QString& text);
49 void setTextAlpha(float alpha) { _textAlpha = alpha; }49 void setTextAlpha(float alpha) { _textAlpha = alpha; }
50 void setLineHeight(float value) { _lineHeight = value; }50 void setLineHeight(float value) { _lineHeight = value; }
51 void setLeftMargin(float margin) { _leftMargin = margin; }51 void setLeftMargin(float margin) { _leftMargin = margin; }
@@ -58,15 +58,16 @@ public: · 6 unmodified lines
58 58
59 QSizeF textSize(const QString& test) const; // Meters59 QSizeF textSize(const QString& test) const; // Meters
60 60
61 virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, 61 virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
62 BoxFace& face, glm::vec3& surfaceNormal) override;62 BoxFace& face, glm::vec3& surfaceNormal) override;
63 63
64 virtual Text3DOverlay* createClone() const override;64 virtual Text3DOverlay* createClone() const override;
65 65
66private:66private:
67 TextRenderer3D* _textRenderer = nullptr;67 TextRenderer3D* _textRenderer = nullptr;
68 68
69 QString _text;69 QString _text;
70 mutable QMutex _mutex; // used to make get/setText threadsafe, mutable so can be used in const functions
70 xColor _backgroundColor = xColor { 0, 0, 0 };71 xColor _backgroundColor = xColor { 0, 0, 0 };
71 float _textAlpha { 1.0f };72 float _textAlpha { 1.0f };
72 float _lineHeight { 1.0f };73 float _lineHeight { 1.0f };
interface/src/ui/overlays/Web3DOverlay.cpp+35−33
@@ -134,7 +134,7 @@ Web3DOverlay::~Web3DOverlay() { · 133 unmodified lines
134void Web3DOverlay::update(float deltatime) {134void Web3DOverlay::update(float deltatime) {
135 if (_webSurface) {135 if (_webSurface) {
136 // update globalPosition136 // update globalPosition
137 _webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));137 _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
138 }138 }
139}139}
140 140
@@ -162,57 +162,56 @@ void Web3DOverlay::loadSourceURL() { · 21 unmodified lines
162 _webSurface->resume();162 _webSurface->resume();
163 _webSurface->getRootItem()->setProperty("url", _url);163 _webSurface->getRootItem()->setProperty("url", _url);
164 _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);164 _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
165 _webSurface->getRootContext()->setContextProperty("ApplicationInterface", qApp);
166 165
167 } else {166 } else {
168 _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath()));167 _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath()));
169 _webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {});168 _webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {});
170 _webSurface->resume();169 _webSurface->resume();
171 170
172 _webSurface->getRootContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());171 _webSurface->getSurfaceContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
173 _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());172 _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
174 _webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());173 _webSurface->getSurfaceContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
175 _webSurface->getRootContext()->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());174 _webSurface->getSurfaceContext()->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
176 _webSurface->getRootContext()->setContextProperty("Vec3", new Vec3());175 _webSurface->getSurfaceContext()->setContextProperty("Vec3", new Vec3());
177 _webSurface->getRootContext()->setContextProperty("Quat", new Quat());176 _webSurface->getSurfaceContext()->setContextProperty("Quat", new Quat());
178 _webSurface->getRootContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());177 _webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
179 _webSurface->getRootContext()->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());178 _webSurface->getSurfaceContext()->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
180 _webSurface->getRootContext()->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());179 _webSurface->getSurfaceContext()->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
181 180
182 if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {181 if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
183 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();182 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
184 auto flags = tabletScriptingInterface->getFlags();183 auto flags = tabletScriptingInterface->getFlags();
185 _webSurface->getRootContext()->setContextProperty("offscreenFlags", flags);184 _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
186 _webSurface->getRootContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());185 _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
187 _webSurface->getRootContext()->setContextProperty("Account", AccountScriptingInterface::getInstance());186 _webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance());
188 _webSurface->getRootContext()->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());187 _webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
189 _webSurface->getRootContext()->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());188 _webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
190 _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());189 _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
191 _webSurface->getRootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());190 _webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
192 _webSurface->getRootContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());191 _webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
193 _webSurface->getRootContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());192 _webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
194 _webSurface->getRootContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());193 _webSurface->getSurfaceContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
195 _webSurface->getRootContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());194 _webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
196 _webSurface->getRootContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());195 _webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
197 _webSurface->getRootContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());196 _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
198 _webSurface->getRootContext()->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());197 _webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
199 _webSurface->getRootContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance());198 _webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
200 _webSurface->getRootContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());199 _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
201 _webSurface->getRootContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());200 _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
202 _webSurface->getRootContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());201 _webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
203 202
204 _webSurface->getRootContext()->setContextProperty("pathToFonts", "../../");203 _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
205 tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());204 tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
206 205
207 // mark the TabletProxy object as cpp ownership.206 // mark the TabletProxy object as cpp ownership.
208 QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");207 QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
209 _webSurface->getRootContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership);208 _webSurface->getSurfaceContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership);
210 209
211 // Override min fps for tablet UI, for silky smooth scrolling210 // Override min fps for tablet UI, for silky smooth scrolling
212 setMaxFPS(90);211 setMaxFPS(90);
213 }212 }
214 }213 }
215 _webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));214 _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
216}215}
217 216
218void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {217void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {
@@ -443,6 +442,9 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { · 224 unmodified lines
443 442
444 QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);443 QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
445 444
445 if (this->_pressed && event.getType() == PointerEvent::Move) {
446 return;
447 }
446 // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.448 // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
447 // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). 449 // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
448 // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".450 // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
libraries/animation/src/AnimInverseKinematics.cpp+294−23
@@ -20,6 +20,8 @@ · 19 unmodified lines
20#include "ElbowConstraint.h"20#include "ElbowConstraint.h"
21#include "SwingTwistConstraint.h"21#include "SwingTwistConstraint.h"
22#include "AnimationLogging.h"22#include "AnimationLogging.h"
23#include "CubicHermiteSpline.h"
24#include "AnimUtil.h"
23 25
24AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn,26AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn,
25 const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn) :27 const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn) :
@@ -59,7 +61,8 @@ AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimN · 33 unmodified lines
59 61
60AnimInverseKinematics::~AnimInverseKinematics() {62AnimInverseKinematics::~AnimInverseKinematics() {
61 clearConstraints();63 clearConstraints();
62 _accumulators.clear();64 _rotationAccumulators.clear();
65 _translationAccumulators.clear();
63 _targetVarVec.clear();66 _targetVarVec.clear();
64}67}
65 68
@@ -72,10 +75,12 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) { · 6 unmodified lines
72 assert(_skeleton && ((poses.size() == 0) || (_skeleton->getNumJoints() == (int)poses.size())));75 assert(_skeleton && ((poses.size() == 0) || (_skeleton->getNumJoints() == (int)poses.size())));
73 if (_skeleton->getNumJoints() == (int)poses.size()) {76 if (_skeleton->getNumJoints() == (int)poses.size()) {
74 _relativePoses = poses;77 _relativePoses = poses;
75 _accumulators.resize(_relativePoses.size());78 _rotationAccumulators.resize(_relativePoses.size());
79 _translationAccumulators.resize(_relativePoses.size());
76 } else {80 } else {
77 _relativePoses.clear();81 _relativePoses.clear();
78 _accumulators.clear();82 _rotationAccumulators.clear();
83 _translationAccumulators.clear();
79 }84 }
80}85}
81 86
@@ -175,14 +180,17 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: · 93 unmodified lines
175 }180 }
176}181}
177 182
178void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const AnimContext& context, const std::vector<IKTarget>& targets) {183void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<IKTarget>& targets) {
179 // compute absolute poses that correspond to relative target poses184 // compute absolute poses that correspond to relative target poses
180 AnimPoseVec absolutePoses;185 AnimPoseVec absolutePoses;
181 absolutePoses.resize(_relativePoses.size());186 absolutePoses.resize(_relativePoses.size());
182 computeAbsolutePoses(absolutePoses);187 computeAbsolutePoses(absolutePoses);
183 188
184 // clear the accumulators before we start the IK solver189 // clear the accumulators before we start the IK solver
185 for (auto& accumulator: _accumulators) {190 for (auto& accumulator : _rotationAccumulators) {
191 accumulator.clearAndClean();
192 }
193 for (auto& accumulator : _translationAccumulators) {
186 accumulator.clearAndClean();194 accumulator.clearAndClean();
187 }195 }
188 196
@@ -197,14 +205,22 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const AnimContext& · 8 unmodified lines
197 205
198 // solve all targets206 // solve all targets
199 for (auto& target: targets) {207 for (auto& target: targets) {
200 solveTargetWithCCD(context, target, absolutePoses, debug);208 if (target.getType() == IKTarget::Type::Spline) {
209 solveTargetWithSpline(context, target, absolutePoses, debug);
210 } else {
211 solveTargetWithCCD(context, target, absolutePoses, debug);
212 }
201 }213 }
202 214
203 // harvest accumulated rotations and apply the average215 // harvest accumulated rotations and apply the average
204 for (int i = 0; i < (int)_relativePoses.size(); ++i) {216 for (int i = 0; i < (int)_relativePoses.size(); ++i) {
205 if (_accumulators[i].size() > 0) {217 if (_rotationAccumulators[i].size() > 0) {
206 _relativePoses[i].rot() = _accumulators[i].getAverage();218 _relativePoses[i].rot() = _rotationAccumulators[i].getAverage();
207 _accumulators[i].clear();219 _rotationAccumulators[i].clear();
220 }
221 if (_translationAccumulators[i].size() > 0) {
222 _relativePoses[i].trans() = _translationAccumulators[i].getAverage();
223 _translationAccumulators[i].clear();
208 }224 }
209 }225 }
210 226
@@ -236,7 +252,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const AnimContext& · 25 unmodified lines
236 int parentIndex = _skeleton->getParentIndex(tipIndex);252 int parentIndex = _skeleton->getParentIndex(tipIndex);
237 253
238 // update rotationOnly targets that don't lie on the ik chain of other ik targets.254 // update rotationOnly targets that don't lie on the ik chain of other ik targets.
239 if (parentIndex != -1 && !_accumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {255 if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {
240 const glm::quat& targetRotation = target.getRotation();256 const glm::quat& targetRotation = target.getRotation();
241 // compute tip's new parent-relative rotation257 // compute tip's new parent-relative rotation
242 // Q = Qp * q --> q' = Qp^ * Q258 // Q = Qp * q --> q' = Qp^ * Q
@@ -311,10 +327,13 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const · 68 unmodified lines
311 }327 }
312 328
313 // store the relative rotation change in the accumulator329 // store the relative rotation change in the accumulator
314 _accumulators[tipIndex].add(tipRelativeRotation, target.getWeight());330 _rotationAccumulators[tipIndex].add(tipRelativeRotation, target.getWeight());
331
332 glm::vec3 tipRelativeTranslation = _relativePoses[target.getIndex()].trans();
333 _translationAccumulators[tipIndex].add(tipRelativeTranslation);
315 334
316 if (debug) {335 if (debug) {
317 debugJointMap[tipIndex] = DebugJoint(tipRelativeRotation, constrained);336 debugJointMap[tipIndex] = DebugJoint(tipRelativeRotation, tipRelativeTranslation, constrained);
318 }337 }
319 }338 }
320 339
@@ -422,10 +441,13 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const · 101 unmodified lines
422 }441 }
423 442
424 // store the relative rotation change in the accumulator443 // store the relative rotation change in the accumulator
425 _accumulators[pivotIndex].add(newRot, target.getWeight());444 _rotationAccumulators[pivotIndex].add(newRot, target.getWeight());
445
446 glm::vec3 newTrans = _relativePoses[pivotIndex].trans();
447 _translationAccumulators[pivotIndex].add(newTrans);
426 448
427 if (debug) {449 if (debug) {
428 debugJointMap[pivotIndex] = DebugJoint(newRot, constrained);450 debugJointMap[pivotIndex] = DebugJoint(newRot, newTrans, constrained);
429 }451 }
430 452
431 // keep track of tip's new transform as we descend towards root453 // keep track of tip's new transform as we descend towards root
@@ -444,6 +466,187 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const · 12 unmodified lines
444 }466 }
445}467}
446 468
469static CubicHermiteSplineFunctorWithArcLength computeSplineFromTipAndBase(const AnimPose& tipPose, const AnimPose& basePose, float baseGain = 1.0f, float tipGain = 1.0f) {
470 float linearDistance = glm::length(basePose.trans() - tipPose.trans());
471 glm::vec3 p0 = basePose.trans();
472 glm::vec3 m0 = baseGain * linearDistance * (basePose.rot() * Vectors::UNIT_Y);
473 glm::vec3 p1 = tipPose.trans();
474 glm::vec3 m1 = tipGain * linearDistance * (tipPose.rot() * Vectors::UNIT_Y);
475
476 return CubicHermiteSplineFunctorWithArcLength(p0, m0, p1, m1);
477}
478
479// pre-compute information about each joint influeced by this spline IK target.
480void AnimInverseKinematics::computeSplineJointInfosForIKTarget(const AnimContext& context, const IKTarget& target) {
481 std::vector<SplineJointInfo> splineJointInfoVec;
482
483 // build spline between the default poses.
484 AnimPose tipPose = _skeleton->getAbsoluteDefaultPose(target.getIndex());
485 AnimPose basePose = _skeleton->getAbsoluteDefaultPose(_hipsIndex);
486
487 CubicHermiteSplineFunctorWithArcLength spline;
488 if (target.getIndex() == _headIndex) {
489 // set gain factors so that more curvature occurs near the tip of the spline.
490 const float HIPS_GAIN = 0.5f;
491 const float HEAD_GAIN = 1.0f;
492 spline = computeSplineFromTipAndBase(tipPose, basePose, HIPS_GAIN, HEAD_GAIN);
493 } else {
494 spline = computeSplineFromTipAndBase(tipPose, basePose);
495 }
496
497 // measure the total arc length along the spline
498 float totalArcLength = spline.arcLength(1.0f);
499
500 glm::vec3 baseToTip = tipPose.trans() - basePose.trans();
501 float baseToTipLength = glm::length(baseToTip);
502 glm::vec3 baseToTipNormal = baseToTip / baseToTipLength;
503
504 int index = target.getIndex();
505 int endIndex = _skeleton->getParentIndex(_hipsIndex);
506 while (index != endIndex) {
507 AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index);
508
509 float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength;
510
511 // compute offset from spline to the default pose.
512 float t = spline.arcLengthInverse(ratio * totalArcLength);
513
514 // compute the rotation by using the derivative of the spline as the y-axis, and the defaultPose x-axis
515 glm::vec3 y = glm::normalize(spline.d(t));
516 glm::vec3 x = defaultPose.rot() * Vectors::UNIT_X;
517 glm::vec3 u, v, w;
518 generateBasisVectors(y, x, v, u, w);
519 glm::mat3 m(u, v, glm::cross(u, v));
520 glm::quat rot = glm::normalize(glm::quat_cast(m));
521
522 AnimPose pose(glm::vec3(1.0f), rot, spline(t));
523 AnimPose offsetPose = pose.inverse() * defaultPose;
524
525 SplineJointInfo splineJointInfo = { index, ratio, offsetPose };
526 splineJointInfoVec.push_back(splineJointInfo);
527 index = _skeleton->getParentIndex(index);
528 }
529
530 _splineJointInfoMap[target.getIndex()] = splineJointInfoVec;
531}
532
533const std::vector<AnimInverseKinematics::SplineJointInfo>* AnimInverseKinematics::findOrCreateSplineJointInfo(const AnimContext& context, const IKTarget& target) {
534 // find or create splineJointInfo for this target
535 auto iter = _splineJointInfoMap.find(target.getIndex());
536 if (iter != _splineJointInfoMap.end()) {
537 return &(iter->second);
538 } else {
539 computeSplineJointInfosForIKTarget(context, target);
540 auto iter = _splineJointInfoMap.find(target.getIndex());
541 if (iter != _splineJointInfoMap.end()) {
542 return &(iter->second);
543 }
544 }
545
546 return nullptr;
547}
548
549void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug) {
550
551 std::map<int, DebugJoint> debugJointMap;
552
553 const int baseIndex = _hipsIndex;
554
555 // build spline from tip to base
556 AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation());
557 AnimPose basePose = absolutePoses[baseIndex];
558 CubicHermiteSplineFunctorWithArcLength spline;
559 if (target.getIndex() == _headIndex) {
560 // set gain factors so that more curvature occurs near the tip of the spline.
561 const float HIPS_GAIN = 0.5f;
562 const float HEAD_GAIN = 1.0f;
563 spline = computeSplineFromTipAndBase(tipPose, basePose, HIPS_GAIN, HEAD_GAIN);
564 } else {
565 spline = computeSplineFromTipAndBase(tipPose, basePose);
566 }
567 float totalArcLength = spline.arcLength(1.0f);
568
569 // This prevents the rotation interpolation from rotating the wrong physical way (but correct mathematical way)
570 // when the head is arched backwards very far.
571 glm::quat halfRot = glm::normalize(glm::lerp(basePose.rot(), tipPose.rot(), 0.5f));
572 if (glm::dot(halfRot * Vectors::UNIT_Z, basePose.rot() * Vectors::UNIT_Z) < 0.0f) {
573 tipPose.rot() = -tipPose.rot();
574 }
575
576 // find or create splineJointInfo for this target
577 const std::vector<SplineJointInfo>* splineJointInfoVec = findOrCreateSplineJointInfo(context, target);
578
579 if (splineJointInfoVec && splineJointInfoVec->size() > 0) {
580 const int baseParentIndex = _skeleton->getParentIndex(baseIndex);
581 AnimPose parentAbsPose = (baseParentIndex >= 0) ? absolutePoses[baseParentIndex] : AnimPose();
582
583 // go thru splineJointInfoVec backwards (base to tip)
584 for (int i = (int)splineJointInfoVec->size() - 1; i >= 0; i--) {
585 const SplineJointInfo& splineJointInfo = (*splineJointInfoVec)[i];
586 float t = spline.arcLengthInverse(splineJointInfo.ratio * totalArcLength);
587 glm::vec3 trans = spline(t);
588
589 // for head splines, preform most twist toward the tip by using ease in function. t^2
590 float rotT = t;
591 if (target.getIndex() == _headIndex) {
592 rotT = t * t;
593 }
594 glm::quat twistRot = glm::normalize(glm::lerp(basePose.rot(), tipPose.rot(), rotT));
595
596 // compute the rotation by using the derivative of the spline as the y-axis, and the twistRot x-axis
597 glm::vec3 y = glm::normalize(spline.d(t));
598 glm::vec3 x = twistRot * Vectors::UNIT_X;
599 glm::vec3 u, v, w;
600 generateBasisVectors(y, x, v, u, w);
601 glm::mat3 m(u, v, glm::cross(u, v));
602 glm::quat rot = glm::normalize(glm::quat_cast(m));
603
604 AnimPose desiredAbsPose = AnimPose(glm::vec3(1.0f), rot, trans) * splineJointInfo.offsetPose;
605
606 // apply flex coefficent
607 AnimPose flexedAbsPose;
608 ::blend(1, &absolutePoses[splineJointInfo.jointIndex], &desiredAbsPose, target.getFlexCoefficient(i), &flexedAbsPose);
609
610 AnimPose relPose = parentAbsPose.inverse() * flexedAbsPose;
611 _rotationAccumulators[splineJointInfo.jointIndex].add(relPose.rot(), target.getWeight());
612
613 bool constrained = false;
614 if (splineJointInfo.jointIndex != _hipsIndex) {
615 // constrain the amount the spine can stretch or compress
616 float length = glm::length(relPose.trans());
617 const float EPSILON = 0.0001f;
618 if (length > EPSILON) {
619 float defaultLength = glm::length(_skeleton->getRelativeDefaultPose(splineJointInfo.jointIndex).trans());
620 const float STRETCH_COMPRESS_PERCENTAGE = 0.15f;
621 const float MAX_LENGTH = defaultLength * (1.0f + STRETCH_COMPRESS_PERCENTAGE);
622 const float MIN_LENGTH = defaultLength * (1.0f - STRETCH_COMPRESS_PERCENTAGE);
623 if (length > MAX_LENGTH) {
624 relPose.trans() = (relPose.trans() / length) * MAX_LENGTH;
625 constrained = true;
626 } else if (length < MIN_LENGTH) {
627 relPose.trans() = (relPose.trans() / length) * MIN_LENGTH;
628 constrained = true;
629 }
630 } else {
631 relPose.trans() = glm::vec3(0.0f);
632 }
633 }
634
635 _translationAccumulators[splineJointInfo.jointIndex].add(relPose.trans(), target.getWeight());
636
637 if (debug) {
638 debugJointMap[splineJointInfo.jointIndex] = DebugJoint(relPose.rot(), relPose.trans(), constrained);
639 }
640
641 parentAbsPose = flexedAbsPose;
642 }
643 }
644
645 if (debug) {
646 debugDrawIKChain(debugJointMap, context);
647 }
648}
649
447//virtual650//virtual
448const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) {651const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) {
449 // don't call this function, call overlay() instead652 // don't call this function, call overlay() instead
@@ -453,14 +656,9 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar · 3 unmodified lines
453 656
454//virtual657//virtual
455const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {658const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
456
457 // allows solutionSource to be overridden by an animVar659 // allows solutionSource to be overridden by an animVar
458 auto solutionSource = animVars.lookup(_solutionSourceVar, (int)_solutionSource);660 auto solutionSource = animVars.lookup(_solutionSourceVar, (int)_solutionSource);
459 661
460 if (context.getEnableDebugDrawIKConstraints()) {
461 debugDrawConstraints(context);
462 }
463
464 const float MAX_OVERLAY_DT = 1.0f / 30.0f; // what to clamp delta-time to in AnimInverseKinematics::overlay662 const float MAX_OVERLAY_DT = 1.0f / 30.0f; // what to clamp delta-time to in AnimInverseKinematics::overlay
465 if (dt > MAX_OVERLAY_DT) {663 if (dt > MAX_OVERLAY_DT) {
466 dt = MAX_OVERLAY_DT;664 dt = MAX_OVERLAY_DT;
@@ -569,7 +767,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars · 102 unmodified lines
569 767
570 {768 {
571 PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);769 PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
572 solveWithCyclicCoordinateDescent(context, targets);770 solve(context, targets);
573 }771 }
574 772
575 if (_hipsTargetIndex < 0) {773 if (_hipsTargetIndex < 0) {
@@ -579,6 +777,20 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars · 3 unmodified lines
579 _hipsOffset = Vectors::ZERO;777 _hipsOffset = Vectors::ZERO;
580 }778 }
581 }779 }
780
781 if (context.getEnableDebugDrawIKConstraints()) {
782 debugDrawConstraints(context);
783 }
784 }
785
786 if (_leftHandIndex > -1) {
787 _uncontrolledLeftHandPose = _skeleton->getAbsolutePose(_leftHandIndex, underPoses);
788 }
789 if (_rightHandIndex > -1) {
790 _uncontrolledRightHandPose = _skeleton->getAbsolutePose(_rightHandIndex, underPoses);
791 }
792 if (_hipsIndex > -1) {
793 _uncontrolledHipsPose = _skeleton->getAbsolutePose(_hipsIndex, underPoses);
582 }794 }
583 795
584 return _relativePoses;796 return _relativePoses;
@@ -722,8 +934,10 @@ void AnimInverseKinematics::initConstraints() { · 137 unmodified lines
722 934
723 loadDefaultPoses(_skeleton->getRelativeBindPoses());935 loadDefaultPoses(_skeleton->getRelativeBindPoses());
724 936
725 // compute corresponding absolute poses
726 int numJoints = (int)_defaultRelativePoses.size();937 int numJoints = (int)_defaultRelativePoses.size();
938
939 /* KEEP THIS CODE for future experimentation
940 // compute corresponding absolute poses
727 AnimPoseVec absolutePoses;941 AnimPoseVec absolutePoses;
728 absolutePoses.resize(numJoints);942 absolutePoses.resize(numJoints);
729 for (int i = 0; i < numJoints; ++i) {943 for (int i = 0; i < numJoints; ++i) {
@@ -734,6 +948,7 @@ void AnimInverseKinematics::initConstraints() { · 4 unmodified lines
734 absolutePoses[i] = absolutePoses[parentIndex] * _defaultRelativePoses[i];948 absolutePoses[i] = absolutePoses[parentIndex] * _defaultRelativePoses[i];
735 }949 }
736 }950 }
951 */
737 952
738 clearConstraints();953 clearConstraints();
739 for (int i = 0; i < numJoints; ++i) {954 for (int i = 0; i < numJoints; ++i) {
@@ -1045,7 +1260,10 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele · 305 unmodified lines
1045 1260
1046 _maxTargetIndex = -1;1261 _maxTargetIndex = -1;
1047 1262
1048 for (auto& accumulator: _accumulators) {1263 for (auto& accumulator: _rotationAccumulators) {
1264 accumulator.clearAndClean();
1265 }
1266 for (auto& accumulator: _translationAccumulators) {
1049 accumulator.clearAndClean();1267 accumulator.clearAndClean();
1050 }1268 }
1051 1269
@@ -1061,12 +1279,21 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele · 9 unmodified lines
1061 } else {1279 } else {
1062 _hipsParentIndex = -1;1280 _hipsParentIndex = -1;
1063 }1281 }
1282
1283 _leftHandIndex = _skeleton->nameToJointIndex("LeftHand");
1284 _rightHandIndex = _skeleton->nameToJointIndex("RightHand");
1064 } else {1285 } else {
1065 clearConstraints();1286 clearConstraints();
1066 _headIndex = -1;1287 _headIndex = -1;
1067 _hipsIndex = -1;1288 _hipsIndex = -1;
1068 _hipsParentIndex = -1;1289 _hipsParentIndex = -1;
1290 _leftHandIndex = -1;
1291 _rightHandIndex = -1;
1069 }1292 }
1293
1294 _uncontrolledLeftHandPose = AnimPose();
1295 _uncontrolledRightHandPose = AnimPose();
1296 _uncontrolledHipsPose = AnimPose();
1070}1297}
1071 1298
1072static glm::vec3 sphericalToCartesian(float phi, float theta) {1299static glm::vec3 sphericalToCartesian(float phi, float theta) {
@@ -1117,6 +1344,7 @@ void AnimInverseKinematics::debugDrawIKChain(std::map<int, DebugJoint>& debugJoi · 44 unmodified lines
1117 // copy debug joint rotations into the relative poses1344 // copy debug joint rotations into the relative poses
1118 for (auto& debugJoint : debugJointMap) {1345 for (auto& debugJoint : debugJointMap) {
1119 poses[debugJoint.first].rot() = debugJoint.second.relRot;1346 poses[debugJoint.first].rot() = debugJoint.second.relRot;
1347 poses[debugJoint.first].trans() = debugJoint.second.relTrans;
1120 }1348 }
1121 1349
1122 // convert relative poses to absolute1350 // convert relative poses to absolute
@@ -1282,7 +1510,7 @@ void AnimInverseKinematics::blendToPoses(const AnimPoseVec& targetPoses, const A · 159 unmodified lines
1282 int numJoints = (int)_relativePoses.size();1510 int numJoints = (int)_relativePoses.size();
1283 for (int i = 0; i < numJoints; ++i) {1511 for (int i = 0; i < numJoints; ++i) {
1284 float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot(), targetPoses[i].rot()));1512 float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot(), targetPoses[i].rot()));
1285 if (_accumulators[i].isDirty()) {1513 if (_rotationAccumulators[i].isDirty()) {
1286 // this joint is affected by IK --> blend toward the targetPoses rotation1514 // this joint is affected by IK --> blend toward the targetPoses rotation
1287 _relativePoses[i].rot() = glm::normalize(glm::lerp(_relativePoses[i].rot(), dotSign * targetPoses[i].rot(), blendFactor));1515 _relativePoses[i].rot() = glm::normalize(glm::lerp(_relativePoses[i].rot(), dotSign * targetPoses[i].rot(), blendFactor));
1288 } else {1516 } else {
@@ -1316,3 +1544,46 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s · 27 unmodified lines
1316 break;1544 break;
1317 }1545 }
1318}1546}
1547
1548void AnimInverseKinematics::debugDrawSpineSplines(const AnimContext& context, const std::vector<IKTarget>& targets) const {
1549
1550 for (auto& target : targets) {
1551
1552 if (target.getType() != IKTarget::Type::Spline) {
1553 continue;
1554 }
1555
1556 const int baseIndex = _hipsIndex;
1557
1558 // build spline
1559 AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation());
1560 AnimPose basePose = _skeleton->getAbsolutePose(baseIndex, _relativePoses);
1561
1562 CubicHermiteSplineFunctorWithArcLength spline;
1563 if (target.getIndex() == _headIndex) {
1564 // set gain factors so that more curvature occurs near the tip of the spline.
1565 const float HIPS_GAIN = 0.5f;
1566 const float HEAD_GAIN = 1.0f;
1567 spline = computeSplineFromTipAndBase(tipPose, basePose, HIPS_GAIN, HEAD_GAIN);
1568 } else {
1569 spline = computeSplineFromTipAndBase(tipPose, basePose);
1570 }
1571 float totalArcLength = spline.arcLength(1.0f);
1572
1573 const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
1574 const glm::vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f);
1575
1576 // draw red and white stripped spline, parameterized by arc length.
1577 // i.e. each stripe should be the same length.
1578 AnimPose geomToWorldPose = AnimPose(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix());
1579 const int NUM_SEGMENTS = 20;
1580 const float dArcLength = totalArcLength / NUM_SEGMENTS;
1581 float arcLength = 0.0f;
1582 for (int i = 0; i < NUM_SEGMENTS; i++) {
1583 float prevT = spline.arcLengthInverse(arcLength);
1584 float nextT = spline.arcLengthInverse(arcLength + dArcLength);
1585 DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(spline(prevT)), geomToWorldPose.xformPoint(spline(nextT)), (i % 2) == 0 ? RED : WHITE);
1586 arcLength += dArcLength;
1587 }
1588 }
1589}
libraries/animation/src/AnimInverseKinematics.h+29−3
@@ -19,6 +19,7 @@ · 18 unmodified lines
19#include "IKTarget.h"19#include "IKTarget.h"
20 20
21#include "RotationAccumulator.h"21#include "RotationAccumulator.h"
22#include "TranslationAccumulator.h"
22 23
23class RotationConstraint;24class RotationConstraint;
24 25
@@ -56,23 +57,39 @@ public: · 31 unmodified lines
56 void setSolutionSource(SolutionSource solutionSource) { _solutionSource = solutionSource; }57 void setSolutionSource(SolutionSource solutionSource) { _solutionSource = solutionSource; }
57 void setSolutionSourceVar(const QString& solutionSourceVar) { _solutionSourceVar = solutionSourceVar; }58 void setSolutionSourceVar(const QString& solutionSourceVar) { _solutionSourceVar = solutionSourceVar; }
58 59
60 const AnimPose& getUncontrolledLeftHandPose() { return _uncontrolledLeftHandPose; }
61 const AnimPose& getUncontrolledRightHandPose() { return _uncontrolledRightHandPose; }
62 const AnimPose& getUncontrolledHipPose() { return _uncontrolledHipsPose; }
63
59protected:64protected:
60 void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);65 void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
61 void solveWithCyclicCoordinateDescent(const AnimContext& context, const std::vector<IKTarget>& targets);66 void solve(const AnimContext& context, const std::vector<IKTarget>& targets);
62 void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);67 void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
68 void solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
63 virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;69 virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
64 struct DebugJoint {70 struct DebugJoint {
65 DebugJoint() : relRot(), constrained(false) {}71 DebugJoint() : relRot(), constrained(false) {}
66 DebugJoint(const glm::quat& relRotIn, bool constrainedIn) : relRot(relRotIn), constrained(constrainedIn) {}72 DebugJoint(const glm::quat& relRotIn, const glm::vec3& relTransIn, bool constrainedIn) : relRot(relRotIn), relTrans(relTransIn), constrained(constrainedIn) {}
67 glm::quat relRot;73 glm::quat relRot;
74 glm::vec3 relTrans;
68 bool constrained;75 bool constrained;
69 };76 };
70 void debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const;77 void debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const;
71 void debugDrawRelativePoses(const AnimContext& context) const;78 void debugDrawRelativePoses(const AnimContext& context) const;
72 void debugDrawConstraints(const AnimContext& context) const;79 void debugDrawConstraints(const AnimContext& context) const;
80 void debugDrawSpineSplines(const AnimContext& context, const std::vector<IKTarget>& targets) const;
73 void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);81 void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);
74 void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);82 void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);
75 83
84 // used to pre-compute information about each joint influeced by a spline IK target.
85 struct SplineJointInfo {
86 int jointIndex; // joint in the skeleton that this information pertains to.
87 float ratio; // percentage (0..1) along the spline for this joint.
88 AnimPose offsetPose; // local offset from the spline to the joint.
89 };
90
91 void computeSplineJointInfosForIKTarget(const AnimContext& context, const IKTarget& target);
92 const std::vector<SplineJointInfo>* findOrCreateSplineJointInfo(const AnimContext& context, const IKTarget& target);
76 93
77 // for AnimDebugDraw rendering94 // for AnimDebugDraw rendering
78 virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }95 virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }
@@ -105,12 +122,15 @@ protected: · 26 unmodified lines
105 };122 };
106 123
107 std::map<int, RotationConstraint*> _constraints;124 std::map<int, RotationConstraint*> _constraints;
108 std::vector<RotationAccumulator> _accumulators;125 std::vector<RotationAccumulator> _rotationAccumulators;
126 std::vector<TranslationAccumulator> _translationAccumulators;
109 std::vector<IKTargetVar> _targetVarVec;127 std::vector<IKTargetVar> _targetVarVec;
110 AnimPoseVec _defaultRelativePoses; // poses of the relaxed state128 AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
111 AnimPoseVec _relativePoses; // current relative poses129 AnimPoseVec _relativePoses; // current relative poses
112 AnimPoseVec _limitCenterPoses; // relative130 AnimPoseVec _limitCenterPoses; // relative
113 131
132 std::map<int, std::vector<SplineJointInfo>> _splineJointInfoMap;
133
114 // experimental data for moving hips during IK134 // experimental data for moving hips during IK
115 glm::vec3 _hipsOffset { Vectors::ZERO };135 glm::vec3 _hipsOffset { Vectors::ZERO };
116 float _maxHipsOffsetLength{ FLT_MAX };136 float _maxHipsOffsetLength{ FLT_MAX };
@@ -118,6 +138,8 @@ protected: · 1 unmodified line
118 int _hipsIndex { -1 };138 int _hipsIndex { -1 };
119 int _hipsParentIndex { -1 };139 int _hipsParentIndex { -1 };
120 int _hipsTargetIndex { -1 };140 int _hipsTargetIndex { -1 };
141 int _leftHandIndex { -1 };
142 int _rightHandIndex { -1 };
121 143
122 // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses144 // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses
123 // during the the cyclic coordinate descent algorithm145 // during the the cyclic coordinate descent algorithm
@@ -127,6 +149,10 @@ protected: · 3 unmodified lines
127 bool _previousEnableDebugIKTargets { false };149 bool _previousEnableDebugIKTargets { false };
128 SolutionSource _solutionSource { SolutionSource::RelaxToUnderPoses };150 SolutionSource _solutionSource { SolutionSource::RelaxToUnderPoses };
129 QString _solutionSourceVar;151 QString _solutionSourceVar;
152
153 AnimPose _uncontrolledLeftHandPose { AnimPose() };
154 AnimPose _uncontrolledRightHandPose { AnimPose() };
155 AnimPose _uncontrolledHipsPose { AnimPose() };
130};156};
131 157
132#endif // hifi_AnimInverseKinematics_h158#endif // hifi_AnimInverseKinematics_h
libraries/animation/src/IKTarget.cpp+3−0
@@ -44,6 +44,9 @@ void IKTarget::setType(int type) { · 43 unmodified lines
44 case (int)Type::HipsRelativeRotationAndPosition:44 case (int)Type::HipsRelativeRotationAndPosition:
45 _type = Type::HipsRelativeRotationAndPosition;45 _type = Type::HipsRelativeRotationAndPosition;
46 break;46 break;
47 case (int)Type::Spline:
48 _type = Type::Spline;
49 break;
47 default:50 default:
48 _type = Type::Unknown;51 _type = Type::Unknown;
49 }52 }
libraries/animation/src/IKTarget.h+1−0
@@ -21,6 +21,7 @@ public: · 20 unmodified lines
21 RotationOnly,21 RotationOnly,
22 HmdHead,22 HmdHead,
23 HipsRelativeRotationAndPosition,23 HipsRelativeRotationAndPosition,
24 Spline,
24 Unknown25 Unknown
25 };26 };
26 27
libraries/animation/src/Rig.cpp+66−32
@@ -28,6 +28,7 @@ · 27 unmodified lines
28#include "AnimClip.h"28#include "AnimClip.h"
29#include "AnimInverseKinematics.h"29#include "AnimInverseKinematics.h"
30#include "AnimSkeleton.h"30#include "AnimSkeleton.h"
31#include "AnimUtil.h"
31#include "IKTarget.h"32#include "IKTarget.h"
32 33
33static bool isEqual(const glm::vec3& u, const glm::vec3& v) {34static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
@@ -401,16 +402,6 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo · 367 unmodified lines
401 }402 }
402}403}
403 404
404void Rig::restoreJointRotation(int index, float fraction, float priority) {
405 // AJT: DEAD CODE?
406 ASSERT(false);
407}
408
409void Rig::restoreJointTranslation(int index, float fraction, float priority) {
410 // AJT: DEAD CODE?
411 ASSERT(false);
412}
413
414bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {405bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {
415 if (isIndexValid(jointIndex)) {406 if (isIndexValid(jointIndex)) {
416 position = (rotation * _internalPoseSet._absolutePoses[jointIndex].trans()) + translation;407 position = (rotation * _internalPoseSet._absolutePoses[jointIndex].trans()) + translation;
@@ -1040,8 +1031,8 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { · 623 unmodified lines
1040 _animVars.set("hipsType", (int)IKTarget::Type::Unknown);1031 _animVars.set("hipsType", (int)IKTarget::Type::Unknown);
1041 }1032 }
1042 1033
1043 if (params.spine2Enabled) {1034 if (params.hipsEnabled && params.spine2Enabled) {
1044 _animVars.set("spine2Type", (int)IKTarget::Type::RotationAndPosition);1035 _animVars.set("spine2Type", (int)IKTarget::Type::Spline);
1045 _animVars.set("spine2Position", extractTranslation(params.spine2Matrix));1036 _animVars.set("spine2Position", extractTranslation(params.spine2Matrix));
1046 _animVars.set("spine2Rotation", glmExtractRotation(params.spine2Matrix));1037 _animVars.set("spine2Rotation", glmExtractRotation(params.spine2Matrix));
1047 } else {1038 } else {
@@ -1051,7 +1042,7 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { · 3 unmodified lines
1051 if (params.leftArmEnabled) {1042 if (params.leftArmEnabled) {
1052 _animVars.set("leftArmType", (int)IKTarget::Type::RotationAndPosition);1043 _animVars.set("leftArmType", (int)IKTarget::Type::RotationAndPosition);
1053 _animVars.set("leftArmPosition", params.leftArmPosition);1044 _animVars.set("leftArmPosition", params.leftArmPosition);
1054 _animVars.set("leftArmRotation", params.leftArmRotation); 1045 _animVars.set("leftArmRotation", params.leftArmRotation);
1055 } else {1046 } else {
1056 _animVars.set("leftArmType", (int)IKTarget::Type::Unknown);1047 _animVars.set("leftArmType", (int)IKTarget::Type::Unknown);
1057 }1048 }
@@ -1101,9 +1092,9 @@ void Rig::updateHeadAnimVars(const HeadParameters& params) { · 43 unmodified lines
1101 _animVars.set("headPosition", params.rigHeadPosition);1092 _animVars.set("headPosition", params.rigHeadPosition);
1102 _animVars.set("headRotation", params.rigHeadOrientation);1093 _animVars.set("headRotation", params.rigHeadOrientation);
1103 if (params.hipsEnabled) {1094 if (params.hipsEnabled) {
1104 // Since there is an explicit hips ik target, switch the head to use the more generic RotationAndPosition IK chain type.1095 // Since there is an explicit hips ik target, switch the head to use the more flexible Spline IK chain type.
1105 // this will allow the spine to bend more, ensuring that it can reach the head target position.1096 // this will allow the spine to compress/expand and bend more natrually, ensuring that it can reach the head target position.
1106 _animVars.set("headType", (int)IKTarget::Type::RotationAndPosition);1097 _animVars.set("headType", (int)IKTarget::Type::Spline);
1107 _animVars.unset("headWeight"); // use the default weight for this target.1098 _animVars.unset("headWeight"); // use the default weight for this target.
1108 } else {1099 } else {
1109 // When there is no hips IK target, use the HmdHead IK chain type. This will make the spine very stiff,1100 // When there is no hips IK target, use the HmdHead IK chain type. This will make the spine very stiff,
@@ -1135,9 +1126,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm · 25 unmodified lines
1135 }1126 }
1136 1127
1137 glm::vec3 headUp = headQuat * Vectors::UNIT_Y;1128 glm::vec3 headUp = headQuat * Vectors::UNIT_Y;
1138 glm::vec3 z, y, x;1129 glm::vec3 z, y, zCrossY;
1139 generateBasisVectors(lookAtVector, headUp, z, y, x);1130 generateBasisVectors(lookAtVector, headUp, z, y, zCrossY);
1140 glm::mat3 m(glm::cross(y, z), y, z);1131 glm::mat3 m(-zCrossY, y, z);
1141 glm::quat desiredQuat = glm::normalize(glm::quat_cast(m));1132 glm::quat desiredQuat = glm::normalize(glm::quat_cast(m));
1142 1133
1143 glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);1134 glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);
@@ -1172,10 +1163,10 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f · 28 unmodified lines
1172 // TODO: add isHipsEnabled1163 // TODO: add isHipsEnabled
1173 bool bodySensorTrackingEnabled = params.isLeftFootEnabled || params.isRightFootEnabled;1164 bool bodySensorTrackingEnabled = params.isLeftFootEnabled || params.isRightFootEnabled;
1174 1165
1175 if (params.isLeftEnabled) {1166 const float RELAX_DURATION = 0.6f;
1176 1167
1168 if (params.isLeftEnabled) {
1177 glm::vec3 handPosition = params.leftPosition;1169 glm::vec3 handPosition = params.leftPosition;
1178
1179 if (!bodySensorTrackingEnabled) {1170 if (!bodySensorTrackingEnabled) {
1180 // prevent the hand IK targets from intersecting the body capsule1171 // prevent the hand IK targets from intersecting the body capsule
1181 glm::vec3 displacement;1172 glm::vec3 displacement;
@@ -1187,16 +1178,38 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f · 5 unmodified lines
1187 _animVars.set("leftHandPosition", handPosition);1178 _animVars.set("leftHandPosition", handPosition);
1188 _animVars.set("leftHandRotation", params.leftOrientation);1179 _animVars.set("leftHandRotation", params.leftOrientation);
1189 _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);1180 _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
1181
1182 _isLeftHandControlled = true;
1183 _lastLeftHandControlledPose = AnimPose(glm::vec3(1.0f), params.leftOrientation, handPosition);
1190 } else {1184 } else {
1191 _animVars.unset("leftHandPosition");1185 if (_isLeftHandControlled) {
1192 _animVars.unset("leftHandRotation");1186 _leftHandRelaxDuration = RELAX_DURATION;
1193 _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);1187 _isLeftHandControlled = false;
1188 }
1189
1190 if (_leftHandRelaxDuration > 0) {
1191 // Move hand from controlled position to non-controlled position.
1192 _leftHandRelaxDuration = std::max(_leftHandRelaxDuration - dt, 0.0f);
1193 auto ikNode = getAnimInverseKinematicsNode();
1194 if (ikNode) {
1195 float alpha = 1.0f - _leftHandRelaxDuration / RELAX_DURATION;
1196 const AnimPose geometryToRigTransform(_geometryToRigTransform);
1197 AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose();
1198 AnimPose handPose;
1199 ::blend(1, &_lastLeftHandControlledPose, &uncontrolledHandPose, alpha, &handPose);
1200 _animVars.set("leftHandPosition", handPose.trans());
1201 _animVars.set("leftHandRotation", handPose.rot());
1202 _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
1203 }
1204 } else {
1205 _animVars.unset("leftHandPosition");
1206 _animVars.unset("leftHandRotation");
1207 _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
1208 }
1194 }1209 }
1195 1210
1196 if (params.isRightEnabled) {1211 if (params.isRightEnabled) {
1197
1198 glm::vec3 handPosition = params.rightPosition;1212 glm::vec3 handPosition = params.rightPosition;
1199
1200 if (!bodySensorTrackingEnabled) {1213 if (!bodySensorTrackingEnabled) {
1201 // prevent the hand IK targets from intersecting the body capsule1214 // prevent the hand IK targets from intersecting the body capsule
1202 glm::vec3 displacement;1215 glm::vec3 displacement;
@@ -1208,10 +1221,34 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f · 5 unmodified lines
1208 _animVars.set("rightHandPosition", handPosition);1221 _animVars.set("rightHandPosition", handPosition);
1209 _animVars.set("rightHandRotation", params.rightOrientation);1222 _animVars.set("rightHandRotation", params.rightOrientation);
1210 _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);1223 _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
1224
1225 _isRightHandControlled = true;
1226 _lastRightHandControlledPose = AnimPose(glm::vec3(1.0f), params.rightOrientation, handPosition);
1211 } else {1227 } else {
1212 _animVars.unset("rightHandPosition");1228 if (_isRightHandControlled) {
1213 _animVars.unset("rightHandRotation");1229 _rightHandRelaxDuration = RELAX_DURATION;
1214 _animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);1230 _isRightHandControlled = false;
1231 }
1232
1233 if (_rightHandRelaxDuration > 0) {
1234 // Move hand from controlled position to non-controlled position.
1235 _rightHandRelaxDuration = std::max(_rightHandRelaxDuration - dt, 0.0f);
1236 auto ikNode = getAnimInverseKinematicsNode();
1237 if (ikNode) {
1238 float alpha = 1.0f - _rightHandRelaxDuration / RELAX_DURATION;
1239 const AnimPose geometryToRigTransform(_geometryToRigTransform);
1240 AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose();
1241 AnimPose handPose;
1242 ::blend(1, &_lastRightHandControlledPose, &uncontrolledHandPose, alpha, &handPose);
1243 _animVars.set("rightHandPosition", handPose.trans());
1244 _animVars.set("rightHandRotation", handPose.rot());
1245 _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
1246 }
1247 } else {
1248 _animVars.unset("rightHandPosition");
1249 _animVars.unset("rightHandRotation");
1250 _animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
1251 }
1215 }1252 }
1216 1253
1217 if (params.isLeftFootEnabled) {1254 if (params.isLeftFootEnabled) {
@@ -1233,7 +1270,6 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f · 15 unmodified lines
1233 _animVars.unset("rightFootRotation");1270 _animVars.unset("rightFootRotation");
1234 _animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);1271 _animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
1235 }1272 }
1236
1237 }1273 }
1238}1274}
1239 1275
@@ -1509,5 +1545,3 @@ void Rig::computeAvatarBoundingCapsule( · 269 unmodified lines
1509 glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum)));1545 glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum)));
1510 localOffsetOut = rigCenter - (geometryToRig * rootPosition);1546 localOffsetOut = rigCenter - (geometryToRig * rootPosition);
1511}1547}
1512
1513
libraries/animation/src/Rig.h+7−4
@@ -134,10 +134,6 @@ public: · 133 unmodified lines
134 void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority);134 void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority);
135 void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority);135 void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority);
136 136
137 // legacy
138 void restoreJointRotation(int index, float fraction, float priority);
139 void restoreJointTranslation(int index, float fraction, float priority);
140
141 // if translation and rotation is identity, position will be in rig space137 // if translation and rotation is identity, position will be in rig space
142 bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,138 bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
143 glm::vec3 translation, glm::quat rotation) const;139 glm::vec3 translation, glm::quat rotation) const;
@@ -355,6 +351,13 @@ private: · 211 unmodified lines
355 QMap<int, StateHandler> _stateHandlers;351 QMap<int, StateHandler> _stateHandlers;
356 int _nextStateHandlerId { 0 };352 int _nextStateHandlerId { 0 };
357 QMutex _stateMutex;353 QMutex _stateMutex;
354
355 bool _isLeftHandControlled { false };
356 bool _isRightHandControlled { false };
357 float _leftHandRelaxDuration { 0.0f };
358 float _rightHandRelaxDuration { 0.0f };
359 AnimPose _lastLeftHandControlledPose;
360 AnimPose _lastRightHandControlledPose;
358};361};
359 362
360#endif /* defined(__hifi__Rig__) */363#endif /* defined(__hifi__Rig__) */
libraries/animation/src/RotationAccumulator.cpp+2−2
@@ -1,5 +1,5 @@
1//1//
2// RotationAccumulator.h2// RotationAccumulator.cpp
3//3//
4// Copyright 2015 High Fidelity, Inc.4// Copyright 2015 High Fidelity, Inc.
5//5//
@@ -27,7 +27,7 @@ void RotationAccumulator::clear() { · 21 unmodified lines
27 _numRotations = 0;27 _numRotations = 0;
28}28}
29 29
30void RotationAccumulator::clearAndClean() { 30void RotationAccumulator::clearAndClean() {
31 clear();31 clear();
32 _isDirty = false;32 _isDirty = false;
33}33}
libraries/animation/src/TranslationAccumulator.cpp+34−0
@@ -0,0 +1,34 @@
1//
2// TranslationAccumulator.cpp
3//
4// Copyright 2015 High Fidelity, Inc.
5//
6// Distributed under the Apache License, Version 2.0.
7// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8//
9
10#include "TranslationAccumulator.h"
11
12void TranslationAccumulator::add(const glm::vec3& translation, float weight) {
13 _accum += weight * translation;
14 _totalWeight += weight;
15 _isDirty = true;
16}
17
18glm::vec3 TranslationAccumulator::getAverage() {
19 if (_totalWeight > 0.0f) {
20 return _accum / _totalWeight;
21 } else {
22 return glm::vec3();
23 }
24}
25
26void TranslationAccumulator::clear() {
27 _accum *= 0.0f;
28 _totalWeight = 0.0f;
29}
30
31void TranslationAccumulator::clearAndClean() {
32 clear();
33 _isDirty = false;
34}
libraries/animation/src/TranslationAccumulator.h+42−0
@@ -0,0 +1,42 @@
1//
2// TranslationAccumulator.h
3//
4// Copyright 2017 High Fidelity, Inc.
5//
6// Distributed under the Apache License, Version 2.0.
7// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8//
9
10#ifndef hifi_TranslationAccumulator_h
11#define hifi_TranslationAccumulator_h
12
13#include <glm/glm.hpp>
14
15class TranslationAccumulator {
16public:
17 TranslationAccumulator() : _accum(0.0f, 0.0f, 0.0f), _totalWeight(0), _isDirty(false) { }
18
19 int size() const { return _totalWeight > 0.0f; }
20
21 /// \param translation translation to add
22 /// \param weight contribution factor of this translation to total accumulation
23 void add(const glm::vec3& translation, float weight = 1.0f);
24
25 glm::vec3 getAverage();
26
27 /// \return true if any translation were accumulated
28 bool isDirty() const { return _isDirty; }
29
30 /// \brief clear accumulated translation but don't change _isDirty
31 void clear();
32
33 /// \brief clear accumulated translation and set _isDirty to false
34 void clearAndClean();
35
36private:
37 glm::vec3 _accum;
38 float _totalWeight;
39 bool _isDirty;
40};
41
42#endif // hifi_TranslationAccumulator_h
libraries/audio-client/src/AudioClient.cpp+37−78
@@ -31,10 +31,13 @@ · 30 unmodified lines
31#include <VersionHelpers.h>31#include <VersionHelpers.h>
32#endif32#endif
33 33
34#include <QtConcurrent/QtConcurrent>
35#include <QtCore/QThreadPool>
34#include <QtCore/QBuffer>36#include <QtCore/QBuffer>
35#include <QtMultimedia/QAudioInput>37#include <QtMultimedia/QAudioInput>
36#include <QtMultimedia/QAudioOutput>38#include <QtMultimedia/QAudioOutput>
37 39
40#include <ThreadHelpers.h>
38#include <NodeList.h>41#include <NodeList.h>
39#include <plugins/CodecPlugin.h>42#include <plugins/CodecPlugin.h>
40#include <plugins/PluginManager.h>43#include <plugins/PluginManager.h>
@@ -83,52 +86,11 @@ QList<QAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) { · 42 unmodified lines
83 return QAudioDeviceInfo::availableDevices(mode);86 return QAudioDeviceInfo::availableDevices(mode);
84}87}
85 88
86class BackgroundThread : public QThread {
87public:
88 BackgroundThread(AudioClient* client) : QThread((QObject*)client), _client(client) {}
89 virtual void join() = 0;
90protected:
91 AudioClient* _client;
92};
93
94// background thread continuously polling device changes
95class CheckDevicesThread : public BackgroundThread {
96public:
97 CheckDevicesThread(AudioClient* client) : BackgroundThread(client) {}
98
99 void join() override {
100 _shouldQuit = true;
101 std::unique_lock<std::mutex> lock(_joinMutex);
102 _joinCondition.wait(lock, [&]{ return !_isRunning; });
103 }
104
105protected:
106 void run() override {
107 while (!_shouldQuit) {
108 _client->checkDevices();
109
110 const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
111 QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS);
112 }
113 std::lock_guard<std::mutex> lock(_joinMutex);
114 _isRunning = false;
115 _joinCondition.notify_one();
116 }
117
118private:
119 std::atomic<bool> _shouldQuit { false };
120 bool _isRunning { true };
121 std::mutex _joinMutex;
122 std::condition_variable _joinCondition;
123};
124
125// now called from a background thread, to keep blocking operations off the audio thread89// now called from a background thread, to keep blocking operations off the audio thread
126void AudioClient::checkDevices() {90void AudioClient::checkDevices() {
127 auto inputDevices = getAvailableDevices(QAudio::AudioInput);91 auto inputDevices = getAvailableDevices(QAudio::AudioInput);
128 auto outputDevices = getAvailableDevices(QAudio::AudioOutput);92 auto outputDevices = getAvailableDevices(QAudio::AudioOutput);
129 93
130 Lock lock(_deviceMutex);
131
132 if (inputDevices != _inputDevices) {94 if (inputDevices != _inputDevices) {
133 _inputDevices.swap(inputDevices);95 _inputDevices.swap(inputDevices);
134 emit devicesChanged(QAudio::AudioInput, _inputDevices);96 emit devicesChanged(QAudio::AudioInput, _inputDevices);
@@ -160,21 +122,6 @@ QList<QAudioDeviceInfo> AudioClient::getAudioDevices(QAudio::Mode mode) const { · 25 unmodified lines
160 }122 }
161}123}
162 124
163
164// background thread buffering local injectors
165class LocalInjectorsThread : public BackgroundThread {
166 Q_OBJECT
167public:
168 LocalInjectorsThread(AudioClient* client) : BackgroundThread(client) {}
169
170 void join() override { return; }
171
172private slots:
173 void prepare() { _client->prepareLocalAudioInjectors(); }
174};
175
176#include "AudioClient.moc"
177
178static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {125static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
179 for (int i = 0; i < numSamples/2; i++) {126 for (int i = 0; i < numSamples/2; i++) {
180 127
@@ -262,7 +209,10 @@ AudioClient::AudioClient() : · 81 unmodified lines
262 209
263 connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,210 connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
264 this, &AudioClient::processReceivedSamples, Qt::DirectConnection);211 this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
265 connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { switchOutputToAudioDevice(outputDeviceInfo); });212 connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) {
213 qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
214 switchOutputToAudioDevice(outputDeviceInfo);
215 });
266 216
267 connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);217 connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);
268 218
@@ -270,16 +220,15 @@ AudioClient::AudioClient() : · 1 unmodified line
270 getAvailableDevices(QAudio::AudioInput);220 getAvailableDevices(QAudio::AudioInput);
271 getAvailableDevices(QAudio::AudioOutput);221 getAvailableDevices(QAudio::AudioOutput);
272 222
223
273 // start a thread to detect any device changes224 // start a thread to detect any device changes
274 _checkDevicesThread = new CheckDevicesThread(this);225 _checkDevicesTimer = new QTimer(this);
275 _checkDevicesThread->setObjectName("AudioClient CheckDevices Thread");226 connect(_checkDevicesTimer, &QTimer::timeout, [this] {
276 _checkDevicesThread->setPriority(QThread::LowPriority);227 QtConcurrent::run(QThreadPool::globalInstance(), [this] {
277 _checkDevicesThread->start();228 checkDevices();
229 });
230 });
278 231
279 // start a thread to process local injectors
280 _localInjectorsThread = new LocalInjectorsThread(this);
281 _localInjectorsThread->setObjectName("AudioClient LocalInjectors Thread");
282 _localInjectorsThread->start();
283 232
284 configureReverb();233 configureReverb();
285 234
@@ -310,15 +259,7 @@ void AudioClient::cleanupBeforeQuit() { · 24 unmodified lines
310 259
311 stop();260 stop();
312 261
313 if (_checkDevicesThread) {262 _checkDevicesTimer->stop();
314 static_cast<BackgroundThread*>(_checkDevicesThread)->join();
315 delete _checkDevicesThread;
316 }
317
318 if (_localInjectorsThread) {
319 static_cast<BackgroundThread*>(_localInjectorsThread)->join();
320 delete _localInjectorsThread;
321 }
322}263}
323 264
324void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {265void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
@@ -478,7 +419,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { · 153 unmodified lines
478 CoUninitialize();419 CoUninitialize();
479 }420 }
480 421
481 qCDebug(audioclient) << "[" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";422 qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input")
423 << " [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
482 424
483 return getNamedAudioDeviceForMode(mode, deviceName);425 return getNamedAudioDeviceForMode(mode, deviceName);
484#endif426#endif
@@ -654,8 +596,12 @@ void AudioClient::start() { · 169 unmodified lines
654}596}
655 597
656void AudioClient::stop() {598void AudioClient::stop() {
599
657 // "switch" to invalid devices in order to shut down the state600 // "switch" to invalid devices in order to shut down the state
601 qCDebug(audioclient) << "AudioClient::stop(), about to call switchInputToAudioDevice(null)";
658 switchInputToAudioDevice(QAudioDeviceInfo());602 switchInputToAudioDevice(QAudioDeviceInfo());
603
604 qCDebug(audioclient) << "AudioClient::stop(), about to call switchOutputToAudioDevice(null)";
659 switchOutputToAudioDevice(QAudioDeviceInfo());605 switchOutputToAudioDevice(QAudioDeviceInfo());
660}606}
661 607
@@ -1402,10 +1348,8 @@ bool AudioClient::outputLocalInjector(AudioInjector* injector) { · 740 unmodified lines
1402 if (!_activeLocalAudioInjectors.contains(injector)) {1348 if (!_activeLocalAudioInjectors.contains(injector)) {
1403 qCDebug(audioclient) << "adding new injector";1349 qCDebug(audioclient) << "adding new injector";
1404 _activeLocalAudioInjectors.append(injector);1350 _activeLocalAudioInjectors.append(injector);
1405
1406 // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop())1351 // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop())
1407 injectorBuffer->setParent(nullptr);1352 injectorBuffer->setParent(nullptr);
1408 injectorBuffer->moveToThread(_localInjectorsThread);
1409 1353
1410 // update the flag1354 // update the flag
1411 _localInjectorsAvailable.exchange(true, std::memory_order_release);1355 _localInjectorsAvailable.exchange(true, std::memory_order_release);
@@ -1428,6 +1372,7 @@ void AudioClient::outputFormatChanged() { · 16 unmodified lines
1428}1372}
1429 1373
1430bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {1374bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
1375 qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]";
1431 bool supportedFormat = false;1376 bool supportedFormat = false;
1432 1377
1433 // NOTE: device start() uses the Qt internal device list1378 // NOTE: device start() uses the Qt internal device list
@@ -1542,6 +1487,8 @@ void AudioClient::outputNotify() { · 108 unmodified lines
1542}1487}
1543 1488
1544bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {1489bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
1490 qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
1491
1545 bool supportedFormat = false;1492 bool supportedFormat = false;
1546 1493
1547 // NOTE: device start() uses the Qt internal device list1494 // NOTE: device start() uses the Qt internal device list
@@ -1678,7 +1625,11 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice · 130 unmodified lines
1678}1625}
1679 1626
1680int AudioClient::setOutputBufferSize(int numFrames, bool persist) {1627int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
1628 qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist;
1629
1681 numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES);1630 numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES);
1631 qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames;
1632
1682 if (numFrames != _sessionOutputBufferSizeFrames) {1633 if (numFrames != _sessionOutputBufferSizeFrames) {
1683 qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames);1634 qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames);
1684 _sessionOutputBufferSizeFrames = numFrames;1635 _sessionOutputBufferSizeFrames = numFrames;
@@ -1690,6 +1641,7 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { · 5 unmodified lines
1690 // The buffer size can't be adjusted after QAudioOutput::start() has been called, so1641 // The buffer size can't be adjusted after QAudioOutput::start() has been called, so
1691 // recreate the device by switching to the default.1642 // recreate the device by switching to the default.
1692 QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);1643 QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
1644 qCDebug(audioclient) << __FUNCTION__ << "about to send changeDevice signal outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
1693 emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread.1645 emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread.
1694 }1646 }
1695 }1647 }
@@ -1817,7 +1769,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { · 121 unmodified lines
1817 }1769 }
1818 1770
1819 // prepare injectors for the next callback1771 // prepare injectors for the next callback
1820 QMetaObject::invokeMethod(_audio->_localInjectorsThread, "prepare", Qt::QueuedConnection);1772 QtConcurrent::run(QThreadPool::globalInstance(), [this] {
1773 _audio->prepareLocalAudioInjectors();
1774 });
1821 1775
1822 int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);1776 int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);
1823 int framesPopped = samplesPopped / AudioConstants::STEREO;1777 int framesPopped = samplesPopped / AudioConstants::STEREO;
@@ -1877,3 +1831,8 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca · 53 unmodified lines
1877 avatarBoundingBoxCorner = corner;1831 avatarBoundingBoxCorner = corner;
1878 avatarBoundingBoxScale = scale;1832 avatarBoundingBoxScale = scale;
1879}1833}
1834
1835
1836void AudioClient::startThread() {
1837 moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
1838}
libraries/audio-client/src/AudioClient.h+2−2
@@ -104,6 +104,7 @@ public: · 103 unmodified lines
104 int _unfulfilledReads;104 int _unfulfilledReads;
105 };105 };
106 106
107 void startThread();
107 void negotiateAudioFormat();108 void negotiateAudioFormat();
108 void selectAudioFormat(const QString& selectedCodecName);109 void selectAudioFormat(const QString& selectedCodecName);
109 110
@@ -386,8 +387,7 @@ private: · 276 unmodified lines
386 RateCounter<> _silentInbound;387 RateCounter<> _silentInbound;
387 RateCounter<> _audioInbound;388 RateCounter<> _audioInbound;
388 389
389 QThread* _checkDevicesThread { nullptr };390 QTimer* _checkDevicesTimer { nullptr };
390 QThread* _localInjectorsThread { nullptr };
391};391};
392 392
393 393
libraries/audio/src/Sound.cpp+45−16
@@ -13,6 +13,8 @@ · 12 unmodified lines
13 13
14#include <glm/glm.hpp>14#include <glm/glm.hpp>
15 15
16#include <QRunnable>
17#include <QThreadPool>
16#include <QDataStream>18#include <QDataStream>
17#include <QtCore/QDebug>19#include <QtCore/QDebug>
18#include <QtNetwork/QNetworkRequest>20#include <QtNetwork/QNetworkRequest>
@@ -49,13 +51,43 @@ Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) : · 30 unmodified lines
49 _isAmbisonic(isAmbisonic),51 _isAmbisonic(isAmbisonic),
50 _isReady(false)52 _isReady(false)
51{53{
52
53}54}
54 55
55void Sound::downloadFinished(const QByteArray& data) {56void Sound::downloadFinished(const QByteArray& data) {
57 // this is a QRunnable, will delete itself after it has finished running
58 SoundProcessor* soundProcessor = new SoundProcessor(_url, data, _isStereo, _isAmbisonic);
59 connect(soundProcessor, &SoundProcessor::onSuccess, this, &Sound::soundProcessSuccess);
60 connect(soundProcessor, &SoundProcessor::onError, this, &Sound::soundProcessError);
61 QThreadPool::globalInstance()->start(soundProcessor);
62}
63
64void Sound::soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration) {
65
66 qCDebug(audio) << "Setting ready state for sound file" << _url.toDisplayString();
67
68 _byteArray = data;
69 _isStereo = stereo;
70 _isAmbisonic = ambisonic;
71 _duration = duration;
72 _isReady = true;
73 finishedLoading(true);
74
75 emit ready();
76}
77
78void Sound::soundProcessError(int error, QString str) {
79 qCCritical(audio) << "Failed to process sound file" << _url.toDisplayString() << "code =" << error << str;
80 emit failed(QNetworkReply::UnknownContentError);
81 finishedLoading(false);
82}
83
84void SoundProcessor::run() {
85
86 qCDebug(audio) << "Processing sound file" << _url.toDisplayString();
87
56 // replace our byte array with the downloaded data88 // replace our byte array with the downloaded data
57 QByteArray rawAudioByteArray = QByteArray(data);89 QByteArray rawAudioByteArray = QByteArray(_data);
58 QString fileName = getURL().fileName().toLower();90 QString fileName = _url.fileName().toLower();
59 91
60 static const QString WAV_EXTENSION = ".wav";92 static const QString WAV_EXTENSION = ".wav";
61 static const QString RAW_EXTENSION = ".raw";93 static const QString RAW_EXTENSION = ".raw";
@@ -72,31 +104,28 @@ void Sound::downloadFinished(const QByteArray& data) { · 10 unmodified lines
72 // since it's raw the only way for us to know that is if the file was called .stereo.raw104 // since it's raw the only way for us to know that is if the file was called .stereo.raw
73 if (fileName.toLower().endsWith("stereo.raw")) {105 if (fileName.toLower().endsWith("stereo.raw")) {
74 _isStereo = true;106 _isStereo = true;
75 qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";107 qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << _url << "as stereo audio file.";
76 }108 }
77 109
78 // Process as 48khz RAW file110 // Process as 48khz RAW file
79 downSample(rawAudioByteArray, 48000);111 downSample(rawAudioByteArray, 48000);
80 } else {112 } else {
81 qCDebug(audio) << "Unknown sound file type";113 qCDebug(audio) << "Unknown sound file type";
114 emit onError(300, "Failed to load sound file, reason: unknown sound file type");
115 return;
82 }116 }
83 117
84 finishedLoading(true);118 emit onSuccess(_data, _isStereo, _isAmbisonic, _duration);
85
86 _isReady = true;
87 emit ready();
88}119}
89 120
90void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) {121void SoundProcessor::downSample(const QByteArray& rawAudioByteArray, int sampleRate) {
91 122
92 // we want to convert it to the format that the audio-mixer wants123 // we want to convert it to the format that the audio-mixer wants
93 // which is signed, 16-bit, 24Khz124 // which is signed, 16-bit, 24Khz
94 125
95 if (sampleRate == AudioConstants::SAMPLE_RATE) {126 if (sampleRate == AudioConstants::SAMPLE_RATE) {
96
97 // no resampling needed127 // no resampling needed
98 _byteArray = rawAudioByteArray;128 _data = rawAudioByteArray;
99
100 } else {129 } else {
101 130
102 int numChannels = _isAmbisonic ? AudioConstants::AMBISONIC : (_isStereo ? AudioConstants::STEREO : AudioConstants::MONO);131 int numChannels = _isAmbisonic ? AudioConstants::AMBISONIC : (_isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
@@ -106,15 +135,15 @@ void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { · 3 unmodified lines
106 int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample));135 int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample));
107 int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames);136 int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames);
108 int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);137 int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
109 _byteArray.resize(maxDestinationBytes);138 _data.resize(maxDestinationBytes);
110 139
111 int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(), 140 int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(),
112 (int16_t*)_byteArray.data(), 141 (int16_t*)_data.data(),
113 numSourceFrames);142 numSourceFrames);
114 143
115 // truncate to actual output144 // truncate to actual output
116 int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);145 int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
117 _byteArray.resize(numDestinationBytes);146 _data.resize(numDestinationBytes);
118 }147 }
119}148}
120 149
@@ -163,7 +192,7 @@ struct WAVEFormat { · 42 unmodified lines
163};192};
164 193
165// returns wavfile sample rate, used for resampling194// returns wavfile sample rate, used for resampling
166int Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) {195int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) {
167 196
168 // Create a data stream to analyze the data197 // Create a data stream to analyze the data
169 QDataStream waveStream(const_cast<QByteArray *>(&inputAudioByteArray), QIODevice::ReadOnly);198 QDataStream waveStream(const_cast<QByteArray *>(&inputAudioByteArray), QIODevice::ReadOnly);
libraries/audio/src/Sound.h+30−3
@@ -12,6 +12,7 @@ · 11 unmodified lines
12#ifndef hifi_Sound_h12#ifndef hifi_Sound_h
13#define hifi_Sound_h13#define hifi_Sound_h
14 14
15#include <QRunnable>
15#include <QtCore/QObject>16#include <QtCore/QObject>
16#include <QtNetwork/QNetworkReply>17#include <QtNetwork/QNetworkReply>
17#include <QtScript/qscriptengine.h>18#include <QtScript/qscriptengine.h>
@@ -28,12 +29,15 @@ public: · 10 unmodified lines
28 bool isAmbisonic() const { return _isAmbisonic; } 29 bool isAmbisonic() const { return _isAmbisonic; }
29 bool isReady() const { return _isReady; }30 bool isReady() const { return _isReady; }
30 float getDuration() const { return _duration; }31 float getDuration() const { return _duration; }
31
32 32
33 const QByteArray& getByteArray() const { return _byteArray; }33 const QByteArray& getByteArray() const { return _byteArray; }
34 34
35signals:35signals:
36 void ready();36 void ready();
37
38protected slots:
39 void soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration);
40 void soundProcessError(int error, QString str);
37 41
38private:42private:
39 QByteArray _byteArray;43 QByteArray _byteArray;
@@ -42,10 +46,33 @@ private: · 2 unmodified lines
42 bool _isReady;46 bool _isReady;
43 float _duration; // In seconds47 float _duration; // In seconds
44 48
49 virtual void downloadFinished(const QByteArray& data) override;
50};
51
52class SoundProcessor : public QObject, public QRunnable {
53 Q_OBJECT
54
55public:
56 SoundProcessor(const QUrl& url, const QByteArray& data, bool stereo, bool ambisonic)
57 : _url(url), _data(data), _isStereo(stereo), _isAmbisonic(ambisonic)
58 {
59 }
60
61 virtual void run() override;
62
45 void downSample(const QByteArray& rawAudioByteArray, int sampleRate);63 void downSample(const QByteArray& rawAudioByteArray, int sampleRate);
46 int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);64 int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
47 65
48 virtual void downloadFinished(const QByteArray& data) override;66signals:
67 void onSuccess(QByteArray data, bool stereo, bool ambisonic, float duration);
68 void onError(int error, QString str);
69
70private:
71 QUrl _url;
72 QByteArray _data;
73 bool _isStereo;
74 bool _isAmbisonic;
75 float _duration;
49};76};
50 77
51typedef QSharedPointer<Sound> SharedSoundPointer;78typedef QSharedPointer<Sound> SharedSoundPointer;
libraries/avatars-renderer/src/avatars-renderer/Head.cpp+0−12
@@ -304,18 +304,6 @@ glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { · 303 unmodified lines
304 return rotationBetween(orientation * IDENTITY_FORWARD, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;304 return rotationBetween(orientation * IDENTITY_FORWARD, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;
305}305}
306 306
307void Head::setFinalPitch(float finalPitch) {
308 _deltaPitch = glm::clamp(finalPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH) - _basePitch;
309}
310
311void Head::setFinalYaw(float finalYaw) {
312 _deltaYaw = glm::clamp(finalYaw, MIN_HEAD_YAW, MAX_HEAD_YAW) - _baseYaw;
313}
314
315void Head::setFinalRoll(float finalRoll) {
316 _deltaRoll = glm::clamp(finalRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL) - _baseRoll;
317}
318
319float Head::getFinalYaw() const {307float Head::getFinalYaw() const {
320 return glm::clamp(_baseYaw + _deltaYaw, MIN_HEAD_YAW, MAX_HEAD_YAW);308 return glm::clamp(_baseYaw + _deltaYaw, MIN_HEAD_YAW, MAX_HEAD_YAW);
321}309}
libraries/avatars-renderer/src/avatars-renderer/Head.h+0−3
@@ -71,9 +71,6 @@ public: · 70 unmodified lines
71 void setDeltaRoll(float roll) { _deltaRoll = roll; }71 void setDeltaRoll(float roll) { _deltaRoll = roll; }
72 float getDeltaRoll() const { return _deltaRoll; }72 float getDeltaRoll() const { return _deltaRoll; }
73 73
74 virtual void setFinalYaw(float finalYaw) override;
75 virtual void setFinalPitch(float finalPitch) override;
76 virtual void setFinalRoll(float finalRoll) override;
77 virtual float getFinalPitch() const override;74 virtual float getFinalPitch() const override;
78 virtual float getFinalYaw() const override;75 virtual float getFinalYaw() const override;
79 virtual float getFinalRoll() const override;76 virtual float getFinalRoll() const override;
libraries/avatars/src/AvatarData.cpp+4−1
@@ -850,7 +850,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { · 849 unmodified lines
850 glm::quat sensorToWorldQuat;850 glm::quat sensorToWorldQuat;
851 unpackOrientationQuatFromSixBytes(data->sensorToWorldQuat, sensorToWorldQuat);851 unpackOrientationQuatFromSixBytes(data->sensorToWorldQuat, sensorToWorldQuat);
852 float sensorToWorldScale;852 float sensorToWorldScale;
853 unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&data->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX);853 // Grab a local copy of sensorToWorldScale to be able to use the unpack function with a pointer on it,
854 // a direct pointer on the struct attribute triggers warnings because of potential misalignement.
855 auto srcSensorToWorldScale = data->sensorToWorldScale;
856 unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&srcSensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX);
854 glm::vec3 sensorToWorldTrans(data->sensorToWorldTrans[0], data->sensorToWorldTrans[1], data->sensorToWorldTrans[2]);857 glm::vec3 sensorToWorldTrans(data->sensorToWorldTrans[0], data->sensorToWorldTrans[1], data->sensorToWorldTrans[2]);
855 glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);858 glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);
856 if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) {859 if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) {
libraries/avatars/src/HeadData.h+0−3
@@ -45,9 +45,6 @@ public: · 44 unmodified lines
45 float getBaseRoll() const { return _baseRoll; }45 float getBaseRoll() const { return _baseRoll; }
46 void setBaseRoll(float roll) { _baseRoll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); }46 void setBaseRoll(float roll) { _baseRoll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); }
47 47
48 virtual void setFinalYaw(float finalYaw) { _baseYaw = finalYaw; }
49 virtual void setFinalPitch(float finalPitch) { _basePitch = finalPitch; }
50 virtual void setFinalRoll(float finalRoll) { _baseRoll = finalRoll; }
51 virtual float getFinalYaw() const { return _baseYaw; }48 virtual float getFinalYaw() const { return _baseYaw; }
52 virtual float getFinalPitch() const { return _basePitch; }49 virtual float getFinalPitch() const { return _basePitch; }
53 virtual float getFinalRoll() const { return _baseRoll; }50 virtual float getFinalRoll() const { return _baseRoll; }
libraries/controllers/src/controllers/Actions.h+2−2
@@ -42,8 +42,6 @@ enum class Action { · 41 unmodified lines
42 42
43 LEFT_HAND = NUM_COMBINED_AXES,43 LEFT_HAND = NUM_COMBINED_AXES,
44 RIGHT_HAND,44 RIGHT_HAND,
45 LEFT_ARM,
46 RIGHT_ARM,
47 LEFT_FOOT,45 LEFT_FOOT,
48 RIGHT_FOOT,46 RIGHT_FOOT,
49 HIPS,47 HIPS,
@@ -103,6 +101,8 @@ enum class Action { · 53 unmodified lines
103 // Bisected aliases for TRANSLATE_CAMERA_Z101 // Bisected aliases for TRANSLATE_CAMERA_Z
104 BOOM_IN,102 BOOM_IN,
105 BOOM_OUT,103 BOOM_OUT,
104 LEFT_ARM,
105 RIGHT_ARM,
106 106
107 107
108 NUM_ACTIONS,108 NUM_ACTIONS,
libraries/controllers/src/controllers/InputRecorder.cpp+77−46
@@ -9,7 +9,6 @@ · 8 unmodified lines
9#include "InputRecorder.h"9#include "InputRecorder.h"
10 10
11#include <QJsonArray>11#include <QJsonArray>
12#include <QJsonObject>
13#include <QJsonDocument>12#include <QJsonDocument>
14#include <QFile>13#include <QFile>
15#include <QDir>14#include <QDir>
@@ -19,13 +18,17 @@ · 3 unmodified lines
19#include <QByteArray>18#include <QByteArray>
20#include <QStandardPaths>19#include <QStandardPaths>
21#include <PathUtils.h>20#include <PathUtils.h>
21#include <QUrl>
22#include <Gzip.h>
22 23
23#include <BuildInfo.h>24#include <BuildInfo.h>
24#include <GLMHelpers.h>25#include <GLMHelpers.h>
26#include <DependencyManager.h>
27#include "UserInputMapper.h"
25 28
26QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/";29QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/";
27QString FILE_PREFIX_NAME = "input-recording-";30QString FILE_PREFIX_NAME = "input-recording-";
28QString COMPRESS_EXTENSION = "json.gz";31QString COMPRESS_EXTENSION = ".json.gz";
29namespace controller {32namespace controller {
30 33
31 QJsonObject poseToJsonObject(const Pose pose) {34 QJsonObject poseToJsonObject(const Pose pose) {
@@ -91,23 +94,27 @@ namespace controller { · 59 unmodified lines
91 }94 }
92 95
93 96
94 void exportToFile(QJsonObject& object) {97 void exportToFile(const QJsonObject& object, const QString& fileName) {
95 if (!QDir(SAVE_DIRECTORY).exists()) {98 if (!QDir(SAVE_DIRECTORY).exists()) {
96 QDir().mkdir(SAVE_DIRECTORY);99 QDir().mkdir(SAVE_DIRECTORY);
97 }100 }
98 101
99 QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate);
100 timeStamp.replace(":", "-");
101 QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION;
102 qDebug() << fileName;
103 QFile saveFile (fileName);102 QFile saveFile (fileName);
104 if (!saveFile.open(QIODevice::WriteOnly)) {103 if (!saveFile.open(QIODevice::WriteOnly)) {
105 qWarning() << "could not open file: " << fileName;104 qWarning() << "could not open file: " << fileName;
106 return;105 return;
107 }106 }
108 QJsonDocument saveData(object);107 QJsonDocument saveData(object);
109 QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact));108 QByteArray jsonData = saveData.toJson(QJsonDocument::Indented);
110 saveFile.write(compressedData);109 QByteArray jsonDataForFile;
110
111 if (!gzip(jsonData, jsonDataForFile, -1)) {
112 qCritical("unable to gzip while saving to json.");
113 return;
114 }
115
116 saveFile.write(jsonDataForFile);
117 saveFile.close();
111 }118 }
112 119
113 QJsonObject openFile(const QString& file, bool& status) {120 QJsonObject openFile(const QString& file, bool& status) {
@@ -118,10 +125,19 @@ namespace controller { · 4 unmodified lines
118 status = false;125 status = false;
119 return object;126 return object;
120 }127 }
121 QByteArray compressedData = qUncompress(openFile.readAll());128 QByteArray compressedData = openFile.readAll();
122 QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData);129 QByteArray jsonData;
130
131 if (!gunzip(compressedData, jsonData)) {
132 qCritical() << "json file not in gzip format: " << file;
133 status = false;
134 return object;
135 }
136
137 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
123 object = jsonDoc.object();138 object = jsonDoc.object();
124 status = true;139 status = true;
140 openFile.close();
125 return object;141 return object;
126 }142 }
127 143
@@ -146,31 +162,47 @@ namespace controller { · 18 unmodified lines
146 _actionStateList.clear();162 _actionStateList.clear();
147 }163 }
148 164
149 void InputRecorder::saveRecording() {165 QJsonObject InputRecorder::recordDataToJson() {
150 QJsonObject data;166 QJsonObject data;
151 data["frameCount"] = _framesRecorded;167 data["frameCount"] = _framesRecorded;
152 168 data["version"] = "0.0";
169
153 QJsonArray actionArrayList;170 QJsonArray actionArrayList;
154 QJsonArray poseArrayList;171 QJsonArray poseArrayList;
155 for(const ActionStates actionState: _actionStateList) {172 for(const ActionStates actionState: _actionStateList) {
156 QJsonArray actionArray;173 QJsonArray actionArray;
157 for (const float value: actionState) {174 for (const auto action: actionState) {
158 actionArray.append(value);175 QJsonObject actionJson;
176 actionJson["name"] = action.first;
177 actionJson["value"] = action.second;
178 actionArray.append(actionJson);
159 }179 }
160 actionArrayList.append(actionArray);180 actionArrayList.append(actionArray);
161 }181 }
162 182
163 for (const PoseStates poseState: _poseStateList) {183 for (const PoseStates poseState: _poseStateList) {
164 QJsonArray poseArray;184 QJsonArray poseArray;
165 for (const Pose pose: poseState) {185 for (const auto pose: poseState) {
166 poseArray.append(poseToJsonObject(pose));186 QJsonObject poseJson;
187 poseJson["name"] = pose.first;
188 poseJson["pose"] = poseToJsonObject(pose.second);
189 poseArray.append(poseJson);
167 }190 }
168 poseArrayList.append(poseArray);191 poseArrayList.append(poseArray);
169 }192 }
170 193
171 data["actionList"] = actionArrayList;194 data["actionList"] = actionArrayList;
172 data["poseList"] = poseArrayList;195 data["poseList"] = poseArrayList;
173 exportToFile(data);196
197 return data;
198 }
199
200 void InputRecorder::saveRecording() {
201 QJsonObject jsonData = recordDataToJson();
202 QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate);
203 timeStamp.replace(":", "-");
204 QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION;
205 exportToFile(jsonData, fileName);
174 }206 }
175 207
176 void InputRecorder::loadRecording(const QString& path) {208 void InputRecorder::loadRecording(const QString& path) {
@@ -181,17 +213,20 @@ namespace controller { · 4 unmodified lines
181 resetFrame();213 resetFrame();
182 _poseStateList.clear();214 _poseStateList.clear();
183 _actionStateList.clear();215 _actionStateList.clear();
184 QString filePath = path;216 QUrl urlPath(path);
185 filePath.remove(0,8);217 QString filePath = urlPath.toLocalFile();
186 QFileInfo info(filePath);218 QFileInfo info(filePath);
187 QString extension = info.suffix();219 QString extension = info.suffix();
220
188 if (extension != "gz") {221 if (extension != "gz") {
189 qWarning() << "can not load file with exentsion of " << extension;222 qWarning() << "can not load file with exentsion of " << extension;
190 return;223 return;
191 }224 }
225
192 bool success = false;226 bool success = false;
193 QJsonObject data = openFile(info.absoluteFilePath(), success);227 QJsonObject data = openFile(filePath, success);
194 if (success) {228 auto keyValue = data.find("version");
229 if (success && keyValue != data.end()) {
195 _framesRecorded = data["frameCount"].toInt();230 _framesRecorded = data["frameCount"].toInt();
196 QJsonArray actionArrayList = data["actionList"].toArray();231 QJsonArray actionArrayList = data["actionList"].toArray();
197 QJsonArray poseArrayList = data["poseList"].toArray();232 QJsonArray poseArrayList = data["poseList"].toArray();
@@ -199,30 +234,31 @@ namespace controller { · 1 unmodified line
199 for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) {234 for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) {
200 QJsonArray actionState = actionArrayList[actionIndex].toArray();235 QJsonArray actionState = actionArrayList[actionIndex].toArray();
201 for (int index = 0; index < actionState.size(); index++) {236 for (int index = 0; index < actionState.size(); index++) {
202 _currentFrameActions[index] = actionState[index].toDouble();237 QJsonObject actionObject = actionState[index].toObject();
238 _currentFrameActions[actionObject["name"].toString()] = actionObject["value"].toDouble();
203 }239 }
204 _actionStateList.push_back(_currentFrameActions);240 _actionStateList.push_back(_currentFrameActions);
205 _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS));241 _currentFrameActions.clear();
206 }242 }
207 243
208 for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) {244 for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) {
209 QJsonArray poseState = poseArrayList[poseIndex].toArray();245 QJsonArray poseState = poseArrayList[poseIndex].toArray();
210 for (int index = 0; index < poseState.size(); index++) {246 for (int index = 0; index < poseState.size(); index++) {
211 _currentFramePoses[index] = jsonObjectToPose(poseState[index].toObject());247 QJsonObject poseObject = poseState[index].toObject();
248 _currentFramePoses[poseObject["name"].toString()] = jsonObjectToPose(poseObject["pose"].toObject());
212 }249 }
213 _poseStateList.push_back(_currentFramePoses);250 _poseStateList.push_back(_currentFramePoses);
214 _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS));251 _currentFramePoses.clear();
215 }252 }
216 }253 }
217
218 _loading = false;254 _loading = false;
219 }255 }
220 256
221 void InputRecorder::stopRecording() {257 void InputRecorder::stopRecording() {
222 _recording = false;258 _recording = false;
223 _framesRecorded = (int)_actionStateList.size();259 _framesRecorded = (int)_actionStateList.size();
224 }260 }
225 261
226 void InputRecorder::startPlayback() {262 void InputRecorder::startPlayback() {
227 _playback = true;263 _playback = true;
228 _recording = false;264 _recording = false;
@@ -234,41 +270,36 @@ namespace controller { · 5 unmodified lines
234 _playCount = 0;270 _playCount = 0;
235 }271 }
236 272
237 void InputRecorder::setActionState(controller::Action action, float value) {273 void InputRecorder::setActionState(const QString& action, float value) {
238 if (_recording) {274 if (_recording) {
239 _currentFrameActions[toInt(action)] += value;275 _currentFrameActions[action] += value;
240 }276 }
241 }277 }
242 278
243 void InputRecorder::setActionState(controller::Action action, const controller::Pose pose) {279 void InputRecorder::setActionState(const QString& action, const controller::Pose& pose) {
244 if (_recording) {280 if (_recording) {
245 _currentFramePoses[toInt(action)] = pose;281 _currentFramePoses[action] = pose;
246 }282 }
247 }283 }
248 284
249 void InputRecorder::resetFrame() {285 void InputRecorder::resetFrame() {
250 if (_recording) {286 if (_recording) {
251 for(auto& channel : _currentFramePoses) {287 _currentFramePoses.clear();
252 channel = Pose();288 _currentFrameActions.clear();
253 }
254
255 for(auto& channel : _currentFrameActions) {
256 channel = 0.0f;
257 }
258 }289 }
259 }290 }
260 291
261 float InputRecorder::getActionState(controller::Action action) {292 float InputRecorder::getActionState(const QString& action) {
262 if (_actionStateList.size() > 0 ) {293 if (_actionStateList.size() > 0 ) {
263 return _actionStateList[_playCount][toInt(action)];294 return _actionStateList[_playCount][action];
264 }295 }
265 296
266 return 0.0f;297 return 0.0f;
267 }298 }
268 299
269 controller::Pose InputRecorder::getPoseState(controller::Action action) {300 controller::Pose InputRecorder::getPoseState(const QString& action) {
270 if (_poseStateList.size() > 0) {301 if (_poseStateList.size() > 0) {
271 return _poseStateList[_playCount][toInt(action)];302 return _poseStateList[_playCount][action];
272 }303 }
273 304
274 return Pose();305 return Pose();
libraries/controllers/src/controllers/InputRecorder.h+11−8
@@ -12,8 +12,10 @@ · 11 unmodified lines
12#include <mutex>12#include <mutex>
13#include <atomic>13#include <atomic>
14#include <vector>14#include <vector>
15#include <map>
15 16
16#include <QString>17#include <QString>
18#include <QJsonObject>
17 19
18#include "Pose.h"20#include "Pose.h"
19#include "Actions.h"21#include "Actions.h"
@@ -21,8 +23,8 @@ · 1 unmodified line
21namespace controller {23namespace controller {
22 class InputRecorder {24 class InputRecorder {
23 public:25 public:
24 using PoseStates = std::vector<Pose>;26 using PoseStates = std::map<QString, Pose>;
25 using ActionStates = std::vector<float>;27 using ActionStates = std::map<QString, float>;
26 28
27 InputRecorder();29 InputRecorder();
28 ~InputRecorder();30 ~InputRecorder();
@@ -40,20 +42,21 @@ namespace controller { · 11 unmodified lines
40 void resetFrame();42 void resetFrame();
41 bool isRecording() { return _recording; }43 bool isRecording() { return _recording; }
42 bool isPlayingback() { return (_playback && !_loading); }44 bool isPlayingback() { return (_playback && !_loading); }
43 void setActionState(controller::Action action, float value);45 void setActionState(const QString& action, float value);
44 void setActionState(controller::Action action, const controller::Pose pose);46 void setActionState(const QString& action, const controller::Pose& pose);
45 float getActionState(controller::Action action);47 float getActionState(const QString& action);
46 controller::Pose getPoseState(controller::Action action);48 controller::Pose getPoseState(const QString& action);
47 QString getSaveDirectory();49 QString getSaveDirectory();
48 void frameTick();50 void frameTick();
49 private:51 private:
52 QJsonObject recordDataToJson();
50 bool _recording { false };53 bool _recording { false };
51 bool _playback { false };54 bool _playback { false };
52 bool _loading { false };55 bool _loading { false };
53 std::vector<PoseStates> _poseStateList = std::vector<PoseStates>();56 std::vector<PoseStates> _poseStateList = std::vector<PoseStates>();
54 std::vector<ActionStates> _actionStateList = std::vector<ActionStates>();57 std::vector<ActionStates> _actionStateList = std::vector<ActionStates>();
55 PoseStates _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS));58 PoseStates _currentFramePoses;
56 ActionStates _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS));59 ActionStates _currentFrameActions;
57 60
58 int _framesRecorded { 0 };61 int _framesRecorded { 0 };
59 int _playCount { 0 };62 int _playCount { 0 };
libraries/controllers/src/controllers/Pose.h+1−0
@@ -39,6 +39,7 @@ namespace controller { · 38 unmodified lines
39 quat getRotation() const { return rotation; }39 quat getRotation() const { return rotation; }
40 vec3 getVelocity() const { return velocity; }40 vec3 getVelocity() const { return velocity; }
41 vec3 getAngularVelocity() const { return angularVelocity; }41 vec3 getAngularVelocity() const { return angularVelocity; }
42 mat4 getMatrix() const { return createMatFromQuatAndPos(rotation, translation); }
42 43
43 Pose transform(const glm::mat4& mat) const;44 Pose transform(const glm::mat4& mat) const;
44 Pose postTransform(const glm::mat4& mat) const;45 Pose postTransform(const glm::mat4& mat) const;
libraries/controllers/src/controllers/UserInputMapper.cpp+10−0
@@ -329,6 +329,16 @@ QString UserInputMapper::getActionName(Action action) const { · 328 unmodified lines
329 return QString();329 return QString();
330}330}
331 331
332QString UserInputMapper::getStandardPoseName(uint16_t pose) {
333 Locker locker(_lock);
334 for (auto posePair : getStandardInputs()) {
335 if (posePair.first.channel == pose && posePair.first.getType() == ChannelType::POSE) {
336 return posePair.second;
337 }
338 }
339 return QString();
340}
341
332QVector<QString> UserInputMapper::getActionNames() const {342QVector<QString> UserInputMapper::getActionNames() const {
333 Locker locker(_lock);343 Locker locker(_lock);
334 QVector<QString> result;344 QVector<QString> result;
libraries/controllers/src/controllers/UserInputMapper.h+1−0
@@ -80,6 +80,7 @@ namespace controller { · 79 unmodified lines
80 80
81 QVector<Action> getAllActions() const;81 QVector<Action> getAllActions() const;
82 QString getActionName(Action action) const;82 QString getActionName(Action action) const;
83 QString getStandardPoseName(uint16_t pose);
83 float getActionState(Action action) const { return _actionStates[toInt(action)]; }84 float getActionState(Action action) const { return _actionStates[toInt(action)]; }
84 Pose getPoseState(Action action) const;85 Pose getPoseState(Action action) const;
85 int findAction(const QString& actionName) const;86 int findAction(const QString& actionName) const;
libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp+7−8
@@ -17,31 +17,30 @@ using namespace controller; · 16 unmodified lines
17 17
18void ActionEndpoint::apply(float newValue, const Pointer& source) {18void ActionEndpoint::apply(float newValue, const Pointer& source) {
19 InputRecorder* inputRecorder = InputRecorder::getInstance();19 InputRecorder* inputRecorder = InputRecorder::getInstance();
20 auto userInputMapper = DependencyManager::get<UserInputMapper>();
21 QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
20 if(inputRecorder->isPlayingback()) {22 if(inputRecorder->isPlayingback()) {
21 newValue = inputRecorder->getActionState(Action(_input.getChannel()));23 newValue = inputRecorder->getActionState(actionName);
22 }24 }
23 25
24 _currentValue += newValue;26 _currentValue += newValue;
25 if (_input != Input::INVALID_INPUT) {27 if (_input != Input::INVALID_INPUT) {
26 auto userInputMapper = DependencyManager::get<UserInputMapper>();
27 userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);28 userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
28 }29 }
29 inputRecorder->setActionState(Action(_input.getChannel()), newValue);30 inputRecorder->setActionState(actionName, newValue);
30}31}
31 32
32void ActionEndpoint::apply(const Pose& value, const Pointer& source) {33void ActionEndpoint::apply(const Pose& value, const Pointer& source) {
33 _currentPose = value;34 _currentPose = value;
34 InputRecorder* inputRecorder = InputRecorder::getInstance();35 InputRecorder* inputRecorder = InputRecorder::getInstance();
35 inputRecorder->setActionState(Action(_input.getChannel()), _currentPose);36 auto userInputMapper = DependencyManager::get<UserInputMapper>();
36 if (inputRecorder->isPlayingback()) {37 QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
37 _currentPose = inputRecorder->getPoseState(Action(_input.getChannel()));38 inputRecorder->setActionState(actionName, _currentPose);
38 }
39 39
40 if (!_currentPose.isValid()) {40 if (!_currentPose.isValid()) {
41 return;41 return;
42 }42 }
43 if (_input != Input::INVALID_INPUT) {43 if (_input != Input::INVALID_INPUT) {
44 auto userInputMapper = DependencyManager::get<UserInputMapper>();
45 userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);44 userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
46 }45 }
47}46}
libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h+11−0
@@ -12,6 +12,11 @@ · 11 unmodified lines
12 12
13#include "../Endpoint.h"13#include "../Endpoint.h"
14 14
15#include <DependencyManager.h>
16
17#include "../../InputRecorder.h"
18#include "../../UserInputMapper.h"
19
15namespace controller {20namespace controller {
16 21
17class StandardEndpoint : public VirtualEndpoint {22class StandardEndpoint : public VirtualEndpoint {
@@ -40,6 +45,12 @@ public: · 22 unmodified lines
40 45
41 virtual Pose pose() override {46 virtual Pose pose() override {
42 _read = true;47 _read = true;
48 InputRecorder* inputRecorder = InputRecorder::getInstance();
49 if (inputRecorder->isPlayingback()) {
50 auto userInputMapper = DependencyManager::get<UserInputMapper>();
51 QString actionName = userInputMapper->getStandardPoseName(_input.getChannel());
52 return inputRecorder->getPoseState(actionName);
53 }
43 return VirtualEndpoint::pose();54 return VirtualEndpoint::pose();
44 }55 }
45 56
libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h+3−3
@@ -21,9 +21,9 @@ namespace controller { · 20 unmodified lines
21 LowVelocityFilter(float rotationConstant, float translationConstant) :21 LowVelocityFilter(float rotationConstant, float translationConstant) :
22 _translationConstant(translationConstant), _rotationConstant(rotationConstant) {}22 _translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
23 23
24 virtual float apply(float value) const override { return value; }24 float apply(float value) const override { return value; }
25 virtual Pose apply(Pose newPose) const;25 Pose apply(Pose newPose) const override;
26 virtual bool parseParameters(const QJsonValue& parameters) override;26 bool parseParameters(const QJsonValue& parameters) override;
27 27
28 private:28 private:
29 float _translationConstant { 0.1f };29 float _translationConstant { 0.1f };
libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp+12−0
@@ -339,6 +339,18 @@ void OpenGLDisplayPlugin::deactivate() { · 338 unmodified lines
339 Parent::deactivate();339 Parent::deactivate();
340}340}
341 341
342bool OpenGLDisplayPlugin::startStandBySession() {
343 if (!activateStandBySession()) {
344 return false;
345 }
346 return Parent::startStandBySession();
347}
348
349void OpenGLDisplayPlugin::endSession() {
350 deactivateSession();
351 Parent::endSession();
352}
353
342void OpenGLDisplayPlugin::customizeContext() {354void OpenGLDisplayPlugin::customizeContext() {
343 auto presentThread = DependencyManager::get<PresentThread>();355 auto presentThread = DependencyManager::get<PresentThread>();
344 Q_ASSERT(thread() == presentThread->thread());356 Q_ASSERT(thread() == presentThread->thread());
libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h+6−0
@@ -42,6 +42,8 @@ public: · 41 unmodified lines
42 // between the main thread and the presentation thread42 // between the main thread and the presentation thread
43 bool activate() override final;43 bool activate() override final;
44 void deactivate() override final;44 void deactivate() override final;
45 bool startStandBySession() override final;
46 void endSession() override final;
45 bool eventFilter(QObject* receiver, QEvent* event) override;47 bool eventFilter(QObject* receiver, QEvent* event) override;
46 bool isDisplayVisible() const override { return true; }48 bool isDisplayVisible() const override { return true; }
47 49
@@ -99,6 +101,10 @@ protected: · 51 unmodified lines
99 // Returns true on successful activation101 // Returns true on successful activation
100 virtual bool internalActivate() { return true; }102 virtual bool internalActivate() { return true; }
101 virtual void internalDeactivate() {}103 virtual void internalDeactivate() {}
104
105 // Returns true on successful activation of standby session
106 virtual bool activateStandBySession() { return true; }
107 virtual void deactivateSession() {}
102 108
103 // Plugin specific functionality to send the composed scene to the output window or device109 // Plugin specific functionality to send the composed scene to the output window or device
104 virtual void internalPresent();110 virtual void internalPresent();
libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp+6−0
@@ -221,6 +221,12 @@ void HmdDisplayPlugin::internalPresent() { · 220 unmodified lines
221 float shiftLeftBy = getLeftCenterPixel() - (sourceSize.x / 2);221 float shiftLeftBy = getLeftCenterPixel() - (sourceSize.x / 2);
222 float newWidth = sourceSize.x - shiftLeftBy;222 float newWidth = sourceSize.x - shiftLeftBy;
223 223
224 // Experimentally adjusted the region presented in preview to avoid seeing the masked pixels and recenter the center...
225 static float SCALE_WIDTH = 0.9f;
226 static float SCALE_OFFSET = 2.0f;
227 newWidth *= SCALE_WIDTH;
228 shiftLeftBy *= SCALE_OFFSET;
229
224 const unsigned int RATIO_Y = 9;230 const unsigned int RATIO_Y = 9;
225 const unsigned int RATIO_X = 16;231 const unsigned int RATIO_X = 16;
226 glm::uvec2 originalClippedSize { newWidth, newWidth * RATIO_Y / RATIO_X };232 glm::uvec2 originalClippedSize { newWidth, newWidth * RATIO_Y / RATIO_X };
libraries/entities-renderer/src/EntityTreeRenderer.cpp+1−221
@@ -23,7 +23,6 @@ · 22 unmodified lines
23#include <PerfStat.h>23#include <PerfStat.h>
24#include <SceneScriptingInterface.h>24#include <SceneScriptingInterface.h>
25#include <ScriptEngine.h>25#include <ScriptEngine.h>
26#include <procedural/ProceduralSkybox.h>
27 26
28#include "EntityTreeRenderer.h"27#include "EntityTreeRenderer.h"
29 28
@@ -141,7 +140,6 @@ void EntityTreeRenderer::clear() { · 111 unmodified lines
141 140
142 // reset the zone to the default (while we load the next scene)141 // reset the zone to the default (while we load the next scene)
143 _layeredZones.clear();142 _layeredZones.clear();
144 applyZoneAndHasSkybox(nullptr);
145 143
146 OctreeRenderer::clear();144 OctreeRenderer::clear();
147}145}
@@ -194,15 +192,7 @@ void EntityTreeRenderer::update() { · 46 unmodified lines
194 tree->update();192 tree->update();
195 193
196 // Handle enter/leave entity logic194 // Handle enter/leave entity logic
197 bool updated = checkEnterLeaveEntities();195 checkEnterLeaveEntities();
198
199 // If we haven't already updated and previously attempted to load a texture,
200 // check if the texture loaded and apply it
201 if (!updated &&
202 ((_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())) ||
203 (_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())))) {
204 applySkyboxAndHasAmbient();
205 }
206 196
207 // Even if we're not moving the mouse, if we started clicking on an entity and we have197 // Even if we're not moving the mouse, if we started clicking on an entity and we have
208 // not yet released the hold then this is still considered a holdingClickOnEntity event198 // not yet released the hold then this is still considered a holdingClickOnEntity event
@@ -371,176 +361,6 @@ bool EntityTreeRenderer::applyLayeredZones() { · 162 unmodified lines
371 return true;361 return true;
372}362}
373 363
374
375bool EntityTreeRenderer::applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
376 auto textureCache = DependencyManager::get<TextureCache>();
377 auto scene = DependencyManager::get<SceneScriptingInterface>();
378 auto sceneStage = scene->getStage();
379 auto skyStage = scene->getSkyStage();
380 auto sceneKeyLight = sceneStage->getKeyLight();
381
382 // If there is no zone, use the default background
383 if (!zone) {
384 _zoneUserData = QString();
385 skyStage->getSkybox()->clear();
386
387 _pendingSkyboxTexture = false;
388 _skyboxTexture.clear();
389
390 _pendingAmbientTexture = false;
391 _ambientTexture.clear();
392
393 sceneKeyLight->resetAmbientSphere();
394 sceneKeyLight->setAmbientMap(nullptr);
395
396 skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
397 return false;
398 }
399
400 // Set the keylight
401 sceneKeyLight->setColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor()));
402 sceneKeyLight->setIntensity(zone->getKeyLightProperties().getIntensity());
403 sceneKeyLight->setAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity());
404 sceneKeyLight->setDirection(zone->getKeyLightProperties().getDirection());
405
406 // Set the stage
407 bool isSunModelEnabled = zone->getStageProperties().getSunModelEnabled();
408 sceneStage->setSunModelEnable(isSunModelEnabled);
409 if (isSunModelEnabled) {
410 sceneStage->setLocation(zone->getStageProperties().getLongitude(),
411 zone->getStageProperties().getLatitude(),
412 zone->getStageProperties().getAltitude());
413
414 auto sceneTime = sceneStage->getTime();
415 sceneTime->setHour(zone->getStageProperties().calculateHour());
416 sceneTime->setDay(zone->getStageProperties().calculateDay());
417 }
418
419 // Set the ambient texture
420 _ambientTextureURL = zone->getKeyLightProperties().getAmbientURL();
421 if (_ambientTextureURL.isEmpty()) {
422 _pendingAmbientTexture = false;
423 _ambientTexture.clear();
424 } else {
425 _pendingAmbientTexture = true;
426 }
427
428 // Set the skybox texture
429 return layerZoneAndHasSkybox(zone);
430}
431
432bool EntityTreeRenderer::layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
433 assert(zone);
434
435 auto textureCache = DependencyManager::get<TextureCache>();
436 auto scene = DependencyManager::get<SceneScriptingInterface>();
437 auto skyStage = scene->getSkyStage();
438 auto skybox = skyStage->getSkybox();
439
440 bool hasSkybox = false;
441
442 switch (zone->getBackgroundMode()) {
443 case BACKGROUND_MODE_SKYBOX:
444 hasSkybox = true;
445
446 skybox->setColor(zone->getSkyboxProperties().getColorVec3());
447
448 if (_zoneUserData != zone->getUserData()) {
449 _zoneUserData = zone->getUserData();
450 std::dynamic_pointer_cast<ProceduralSkybox>(skybox)->parse(_zoneUserData);
451 }
452
453 _skyboxTextureURL = zone->getSkyboxProperties().getURL();
454 if (_skyboxTextureURL.isEmpty()) {
455 _pendingSkyboxTexture = false;
456 _skyboxTexture.clear();
457 } else {
458 _pendingSkyboxTexture = true;
459 }
460
461 applySkyboxAndHasAmbient();
462 skyStage->setBackgroundMode(model::SunSkyStage::SKY_BOX);
463
464 break;
465
466 case BACKGROUND_MODE_INHERIT:
467 default:
468 // Clear the skybox to release its textures
469 skybox->clear();
470 _zoneUserData = QString();
471
472 _pendingSkyboxTexture = false;
473 _skyboxTexture.clear();
474
475 // Let the application background through
476 if (applySkyboxAndHasAmbient()) {
477 skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE);
478 } else {
479 skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE);
480 }
481
482 break;
483 }
484
485 return hasSkybox;
486}
487
488bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
489 auto textureCache = DependencyManager::get<TextureCache>();
490 auto scene = DependencyManager::get<SceneScriptingInterface>();
491 auto sceneStage = scene->getStage();
492 auto skyStage = scene->getSkyStage();
493 auto sceneKeyLight = sceneStage->getKeyLight();
494 auto skybox = skyStage->getSkybox();
495
496 bool isAmbientSet = false;
497 if (_pendingAmbientTexture && !_ambientTexture) {
498 _ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE);
499 }
500 if (_ambientTexture && _ambientTexture->isLoaded()) {
501 _pendingAmbientTexture = false;
502
503 auto texture = _ambientTexture->getGPUTexture();
504 if (texture) {
505 isAmbientSet = true;
506 sceneKeyLight->setAmbientSphere(texture->getIrradiance());
507 sceneKeyLight->setAmbientMap(texture);
508 } else {
509 qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
510 }
511 }
512
513 if (_pendingSkyboxTexture &&
514 (!_skyboxTexture || (_skyboxTexture->getURL() != _skyboxTextureURL))) {
515 _skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::CUBE_TEXTURE);
516 }
517 if (_skyboxTexture && _skyboxTexture->isLoaded()) {
518 _pendingSkyboxTexture = false;
519
520 auto texture = _skyboxTexture->getGPUTexture();
521 if (texture) {
522 skybox->setCubemap(texture);
523 if (!isAmbientSet) {
524 sceneKeyLight->setAmbientSphere(texture->getIrradiance());
525 sceneKeyLight->setAmbientMap(texture);
526 isAmbientSet = true;
527 }
528 } else {
529 qCDebug(entitiesrenderer) << "Failed to load skybox texture:" << _skyboxTexture->getURL();
530 skybox->setCubemap(nullptr);
531 }
532 } else {
533 skybox->setCubemap(nullptr);
534 }
535
536 if (!isAmbientSet) {
537 sceneKeyLight->resetAmbientSphere();
538 sceneKeyLight->setAmbientMap(nullptr);
539 }
540
541 return isAmbientSet;
542}
543
544const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {364const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
545 const FBXGeometry* result = NULL;365 const FBXGeometry* result = NULL;
546 366
@@ -1186,8 +1006,6 @@ std::pair<EntityTreeRenderer::LayeredZones::iterator, bool> EntityTreeRenderer:: · 639 unmodified lines
1186 1006
1187void EntityTreeRenderer::LayeredZones::apply() {1007void EntityTreeRenderer::LayeredZones::apply() {
1188 assert(_entityTreeRenderer);1008 assert(_entityTreeRenderer);
1189
1190 applyPartial(begin());
1191}1009}
1192 1010
1193void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zone) {1011void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zone) {
@@ -1202,12 +1020,6 @@ void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zo · 8 unmodified lines
1202 } else {1020 } else {
1203 LayeredZone zoneLayer(zone);1021 LayeredZone zoneLayer(zone);
1204 1022
1205 // should we update? only if this zone is tighter than the current skybox zone
1206 bool shouldUpdate = false;
1207 if (_skyboxLayer == end() || zoneLayer <= *_skyboxLayer) {
1208 shouldUpdate = true;
1209 }
1210
1211 // find this zone's layer, if it exists1023 // find this zone's layer, if it exists
1212 iterator layer = end();1024 iterator layer = end();
1213 auto it = _map.find(zoneLayer.id);1025 auto it = _map.find(zoneLayer.id);
@@ -1227,41 +1039,9 @@ void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zo · 13 unmodified lines
1227 std::tie(layer, std::ignore) = insert(zoneLayer);1039 std::tie(layer, std::ignore) = insert(zoneLayer);
1228 _map.emplace(layer->id, layer);1040 _map.emplace(layer->id, layer);
1229 }1041 }
1230
1231 if (shouldUpdate) {
1232 applyPartial(layer);
1233 }
1234 }1042 }
1235}1043}
1236 1044
1237void EntityTreeRenderer::LayeredZones::applyPartial(iterator layer) {
1238 bool hasSkybox = false;
1239 _skyboxLayer = end();
1240
1241 if (layer == end()) {
1242 if (empty()) {
1243 _entityTreeRenderer->applyZoneAndHasSkybox(nullptr);
1244 return;
1245 } else { // a layer was removed - reapply from beginning
1246 layer = begin();
1247 }
1248 }
1249
1250 if (layer == begin()) {
1251 hasSkybox = _entityTreeRenderer->applyZoneAndHasSkybox(layer->zone);
1252 } else {
1253 hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
1254 }
1255
1256 if (layer != end()) {
1257 while (!hasSkybox && ++layer != end()) {
1258 hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
1259 }
1260 }
1261
1262 _skyboxLayer = layer;
1263}
1264
1265bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {1045bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
1266 bool result = std::equal(other.begin(), other._skyboxLayer, begin());1046 bool result = std::equal(other.begin(), other._skyboxLayer, begin());
1267 if (result) {1047 if (result) {
libraries/entities-renderer/src/EntityTreeRenderer.h+0−3
@@ -147,9 +147,6 @@ private: · 146 unmodified lines
147 void addEntityToScene(EntityItemPointer entity);147 void addEntityToScene(EntityItemPointer entity);
148 bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);148 bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
149 149
150 bool applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
151 bool layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
152 bool applySkyboxAndHasAmbient();
153 bool applyLayeredZones();150 bool applyLayeredZones();
154 151
155 void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);152 void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
libraries/entities-renderer/src/RenderableModelEntityItem.cpp+1−1
@@ -605,7 +605,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori · 604 unmodified lines
605 605
606 QString extraInfo;606 QString extraInfo;
607 return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,607 return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,
608 face, surfaceNormal, extraInfo, precisionPicking);608 face, surfaceNormal, extraInfo, precisionPicking, false);
609}609}
610 610
611void RenderableModelEntityItem::getCollisionGeometryResource() {611void RenderableModelEntityItem::getCollisionGeometryResource() {
libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp+3−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
13#include <DependencyManager.h>13#include <DependencyManager.h>
14#include <PerfStat.h>14#include <PerfStat.h>
15#include <GeometryCache.h>15#include <GeometryCache.h>
16#include <StencilMaskPass.h>
16#include <AbstractViewStateInterface.h>17#include <AbstractViewStateInterface.h>
17#include "EntitiesRendererLogging.h"18#include "EntitiesRendererLogging.h"
18 19
@@ -292,6 +293,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { · 273 unmodified lines
292 state->setDepthTest(true, false, gpu::LESS_EQUAL);293 state->setDepthTest(true, false, gpu::LESS_EQUAL);
293 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,294 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
294 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);295 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
296 PrepareStencil::testMask(*state);
295 297
296 auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert));298 auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert));
297 auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag));299 auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag));
@@ -305,6 +307,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { · 7 unmodified lines
305 state->setDepthTest(true, false, gpu::LESS_EQUAL);307 state->setDepthTest(true, false, gpu::LESS_EQUAL);
306 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,308 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
307 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);309 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
310 PrepareStencil::testMask(*state);
308 311
309 auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));312 auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
310 auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));313 auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp+2−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
12#include <glm/gtx/quaternion.hpp>12#include <glm/gtx/quaternion.hpp>
13 13
14#include <GeometryCache.h>14#include <GeometryCache.h>
15#include <StencilMaskPass.h>
15#include <TextureCache.h>16#include <TextureCache.h>
16#include <PathUtils.h>17#include <PathUtils.h>
17#include <PerfStat.h>18#include <PerfStat.h>
@@ -69,6 +70,7 @@ void RenderablePolyLineEntityItem::createPipeline() { · 51 unmodified lines
69 70
70 gpu::StatePointer state = gpu::StatePointer(new gpu::State());71 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
71 state->setDepthTest(true, true, gpu::LESS_EQUAL);72 state->setDepthTest(true, true, gpu::LESS_EQUAL);
73 PrepareStencil::testMask(*state);
72 state->setBlendFunction(true,74 state->setBlendFunction(true,
73 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,75 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
74 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);76 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp+5−0
@@ -46,6 +46,9 @@ · 45 unmodified lines
46#endif46#endif
47 47
48#include "model/Geometry.h"48#include "model/Geometry.h"
49
50#include "StencilMaskPass.h"
51
49#include "EntityTreeRenderer.h"52#include "EntityTreeRenderer.h"
50#include "polyvox_vert.h"53#include "polyvox_vert.h"
51#include "polyvox_frag.h"54#include "polyvox_frag.h"
@@ -743,6 +746,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { · 691 unmodified lines
743 auto state = std::make_shared<gpu::State>();746 auto state = std::make_shared<gpu::State>();
744 state->setCullMode(gpu::State::CULL_BACK);747 state->setCullMode(gpu::State::CULL_BACK);
745 state->setDepthTest(true, true, gpu::LESS_EQUAL);748 state->setDepthTest(true, true, gpu::LESS_EQUAL);
749 PrepareStencil::testMaskDrawShape(*state);
746 750
747 _pipeline = gpu::Pipeline::create(program, state);751 _pipeline = gpu::Pipeline::create(program, state);
748 752
@@ -750,6 +754,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { · 1 unmodified line
750 wireframeState->setCullMode(gpu::State::CULL_BACK);754 wireframeState->setCullMode(gpu::State::CULL_BACK);
751 wireframeState->setDepthTest(true, true, gpu::LESS_EQUAL);755 wireframeState->setDepthTest(true, true, gpu::LESS_EQUAL);
752 wireframeState->setFillMode(gpu::State::FILL_LINE);756 wireframeState->setFillMode(gpu::State::FILL_LINE);
757 PrepareStencil::testMaskDrawShape(*wireframeState);
753 758
754 _wireframePipeline = gpu::Pipeline::create(program, wireframeState);759 _wireframePipeline = gpu::Pipeline::create(program, wireframeState);
755 }760 }
libraries/entities-renderer/src/RenderableShapeEntityItem.cpp+2−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
13#include <gpu/Batch.h>13#include <gpu/Batch.h>
14 14
15#include <DependencyManager.h>15#include <DependencyManager.h>
16#include <StencilMaskPass.h>
16#include <GeometryCache.h>17#include <GeometryCache.h>
17#include <PerfStat.h>18#include <PerfStat.h>
18 19
@@ -93,6 +94,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { · 74 unmodified lines
93 _procedural->_fragmentSource = simple_frag;94 _procedural->_fragmentSource = simple_frag;
94 _procedural->_opaqueState->setCullMode(gpu::State::CULL_NONE);95 _procedural->_opaqueState->setCullMode(gpu::State::CULL_NONE);
95 _procedural->_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);96 _procedural->_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
97 PrepareStencil::testMaskDrawShape(*_procedural->_opaqueState);
96 _procedural->_opaqueState->setBlendFunction(false,98 _procedural->_opaqueState->setBlendFunction(false,
97 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,99 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
98 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);100 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
libraries/entities-renderer/src/RenderableWebEntityItem.cpp+4−4
@@ -127,7 +127,7 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer> · 126 unmodified lines
127 127
128 _webSurface->resume();128 _webSurface->resume();
129 _webSurface->getRootItem()->setProperty("url", _sourceUrl);129 _webSurface->getRootItem()->setProperty("url", _sourceUrl);
130 _webSurface->getRootContext()->setContextProperty("desktop", QVariant());130 _webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
131 // FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.131 // FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
132 132
133 // forward web events to EntityScriptingInterface133 // forward web events to EntityScriptingInterface
@@ -271,7 +271,7 @@ void RenderableWebEntityItem::loadSourceURL() { · 137 unmodified lines
271 });271 });
272 272
273 _webSurface->getRootItem()->setProperty("url", _sourceUrl);273 _webSurface->getRootItem()->setProperty("url", _sourceUrl);
274 _webSurface->getRootContext()->setContextProperty("desktop", QVariant());274 _webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
275 275
276 } else {276 } else {
277 _contentType = qmlContent;277 _contentType = qmlContent;
@@ -284,7 +284,7 @@ void RenderableWebEntityItem::loadSourceURL() { · 6 unmodified lines
284 _webSurface->getRootItem(), _webSurface.data());284 _webSurface->getRootItem(), _webSurface.data());
285 }285 }
286 }286 }
287 _webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));287 _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
288}288}
289 289
290 290
@@ -420,7 +420,7 @@ void RenderableWebEntityItem::update(const quint64& now) { · 129 unmodified lines
420 420
421 if (_webSurface) {421 if (_webSurface) {
422 // update globalPosition422 // update globalPosition
423 _webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));423 _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
424 }424 }
425 425
426 auto interval = now - _lastRenderTime;426 auto interval = now - _lastRenderTime;
libraries/entities-renderer/src/RenderableZoneEntityItem.cpp+408−38
@@ -13,14 +13,90 @@ · 12 unmodified lines
13 13
14#include <gpu/Batch.h>14#include <gpu/Batch.h>
15 15
16#include <model/Stage.h>
17
16#include <AbstractViewStateInterface.h>18#include <AbstractViewStateInterface.h>
17#include <DependencyManager.h>19#include <DependencyManager.h>
18#include <GeometryCache.h>20#include <GeometryCache.h>
19#include <PerfStat.h>21#include <PerfStat.h>
22#include <procedural/ProceduralSkybox.h>
20 23
21#include "EntityTreeRenderer.h"24#include "EntityTreeRenderer.h"
22#include "RenderableEntityItem.h"25#include "RenderableEntityItem.h"
23 26
27#include <LightPayload.h>
28#include "DeferredLightingEffect.h"
29
30
31class RenderableZoneEntityItemMeta {
32public:
33 RenderableZoneEntityItemMeta(EntityItemPointer entity);
34 ~RenderableZoneEntityItemMeta();
35
36 typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
37 typedef Payload::DataPointer Pointer;
38
39 EntityItemPointer entity;
40
41 void render(RenderArgs* args);
42
43 void setVisible(bool visible) { _isVisible = visible; }
44 bool isVisible() const { return _isVisible; }
45
46 render::Item::Bound& editBound() { _needUpdate = true; return _bound; }
47
48 model::LightPointer editSunLight() { _needSunUpdate = true; return _sunLight; }
49 model::LightPointer editAmbientLight() { _needAmbientUpdate = true; return _ambientLight; }
50 model::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; }
51 model::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); }
52
53 void setAmbientURL(const QString& ambientUrl);
54
55 void setSkyboxURL(const QString& skyboxUrl);
56
57 void setBackgroundMode(BackgroundMode mode);
58 void setSkyboxColor(const glm::vec3& color);
59 void setProceduralUserData(QString userData);
60
61protected:
62 render::Item::Bound _bound;
63
64 model::LightPointer _sunLight;
65 model::LightPointer _ambientLight;
66 model::SunSkyStagePointer _background;
67 BackgroundMode _backgroundMode { BACKGROUND_MODE_INHERIT };
68
69 LightStagePointer _stage;
70 LightStage::Index _sunIndex { LightStage::INVALID_INDEX };
71 LightStage::Index _ambientIndex { LightStage::INVALID_INDEX };
72
73 BackgroundStagePointer _backgroundStage;
74 BackgroundStage::Index _backgroundIndex { BackgroundStage::INVALID_INDEX };
75
76 bool _needUpdate { true };
77 bool _needSunUpdate { true };
78 bool _needAmbientUpdate { true };
79 bool _needBackgroundUpdate { true };
80 bool _isVisible { true };
81
82
83 void updateAmbientMap();
84 void updateSkyboxMap();
85
86 // More attributes used for rendering:
87 QString _ambientTextureURL;
88 NetworkTexturePointer _ambientTexture;
89 bool _pendingAmbientTexture { false };
90 bool _validAmbientTexture { false };
91
92 QString _skyboxTextureURL;
93 NetworkTexturePointer _skyboxTexture;
94 bool _pendingSkyboxTexture { false };
95 bool _validSkyboxTexture { false };
96
97 QString _proceduralUserData;
98};
99
24// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1100// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
25// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.101// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
26static const float SPHERE_ENTITY_SCALE = 0.5f;102static const float SPHERE_ENTITY_SCALE = 0.5f;
@@ -67,8 +143,15 @@ bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& propert · 40 unmodified lines
67 143
68void RenderableZoneEntityItem::somethingChangedNotification() {144void RenderableZoneEntityItem::somethingChangedNotification() {
69 DependencyManager::get<EntityTreeRenderer>()->updateZone(_id);145 DependencyManager::get<EntityTreeRenderer>()->updateZone(_id);
146
147 // If graphics elements are changed, we need to update the render items
148 notifyChangedRenderItem();
149
150 // Poopagate back to parent
151 ZoneEntityItem::somethingChangedNotification();
70}152}
71 153
154
72int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,155int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
73 ReadBitstreamToTreeParams& args,156 ReadBitstreamToTreeParams& args,
74 EntityPropertyFlags& propertyFlags, bool overwriteLocalData,157 EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
@@ -174,18 +257,157 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { · 99 unmodified lines
174 return false;257 return false;
175}258}
176 259
177class RenderableZoneEntityItemMeta {260bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
178public:261 render::Transaction& transaction) {
179 RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ }262 _myMetaItem = scene->allocateID();
180 typedef render::Payload<RenderableZoneEntityItemMeta> Payload;263
181 typedef Payload::DataPointer Pointer;264 auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
265 auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);
266 updateKeyZoneItemFromEntity((*renderData));
267 updateKeySunFromEntity((*renderData));
268 updateKeyAmbientFromEntity((*renderData));
269 updateKeyBackgroundFromEntity((*renderData));
270
271 render::Item::Status::Getters statusGetters;
272 makeEntityItemStatusGetters(getThisPointer(), statusGetters);
273 renderPayload->addStatusGetters(statusGetters);
274
275 transaction.resetItem(_myMetaItem, renderPayload);
276
277 return true;
278}
279
280void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
281 render::Transaction& transaction) {
282 transaction.removeItem(_myMetaItem);
283 render::Item::clearID(_myMetaItem);
284 if (_model) {
285 _model->removeFromScene(scene, transaction);
286 }
287}
288
289void RenderableZoneEntityItem::notifyBoundChanged() {
290 notifyChangedRenderItem();
291}
292
293void RenderableZoneEntityItem::updateKeySunFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
294 auto sunLight = keyZonePayload.editSunLight();
295 sunLight->setType(model::Light::SUN);
296
297 sunLight->setPosition(this->getPosition());
298 sunLight->setOrientation(this->getRotation());
299
300 // Set the keylight
301 sunLight->setColor(ColorUtils::toVec3(this->getKeyLightProperties().getColor()));
302 sunLight->setIntensity(this->getKeyLightProperties().getIntensity());
303 sunLight->setDirection(this->getKeyLightProperties().getDirection());
304}
305
306void RenderableZoneEntityItem::updateKeyAmbientFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
307 auto ambientLight = keyZonePayload.editAmbientLight();
308 ambientLight->setType(model::Light::AMBIENT);
309
310 ambientLight->setPosition(this->getPosition());
311 ambientLight->setOrientation(this->getRotation());
312
313
314 // Set the keylight
315 ambientLight->setAmbientIntensity(this->getKeyLightProperties().getAmbientIntensity());
316
317 if (this->getKeyLightProperties().getAmbientURL().isEmpty()) {
318 keyZonePayload.setAmbientURL(this->getSkyboxProperties().getURL());
319 } else {
320 keyZonePayload.setAmbientURL(this->getKeyLightProperties().getAmbientURL());
321 }
322
323}
324
325void RenderableZoneEntityItem::updateKeyBackgroundFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
326 auto background = keyZonePayload.editBackground();
327
328 keyZonePayload.setBackgroundMode(this->getBackgroundMode());
329 keyZonePayload.setSkyboxColor(this->getSkyboxProperties().getColorVec3());
330 keyZonePayload.setProceduralUserData(this->getUserData());
331 keyZonePayload.setSkyboxURL(this->getSkyboxProperties().getURL());
332}
333
334
335void RenderableZoneEntityItem::updateKeyZoneItemFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
336
337 keyZonePayload.setVisible(this->getVisible());
338
339 bool success;
340 keyZonePayload.editBound() = this->getAABox(success);
341 if (!success) {
342 keyZonePayload.editBound() = render::Item::Bound();
343 }
344
345 /* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we
346 {
347 // Set the stage
348 bool isSunModelEnabled = this->getStageProperties().getSunModelEnabled();
349 sceneStage->setSunModelEnable(isSunModelEnabled);
350 if (isSunModelEnabled) {
351 sceneStage->setLocation(this->getStageProperties().getLongitude(),
352 this->getStageProperties().getLatitude(),
353 this->getStageProperties().getAltitude());
354
355 auto sceneTime = sceneStage->getTime();
356 sceneTime->setHour(this->getStageProperties().calculateHour());
357 sceneTime->setDay(this->getStageProperties().calculateDay());
358 }
359 }*/
360}
361
362
363void RenderableZoneEntityItem::sceneUpdateRenderItemFromEntity(render::Transaction& transaction) {
364 if (!render::Item::isValidID(_myMetaItem)) {
365 return;
366 }
367
368 bool sunChanged = _keyLightPropertiesChanged;
369 bool backgroundChanged = _backgroundPropertiesChanged;
370 bool skyboxChanged = _skyboxPropertiesChanged;
371
372 transaction.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [=](RenderableZoneEntityItemMeta& data) {
373
374 updateKeyZoneItemFromEntity(data);
375
376 if (sunChanged) {
377 updateKeySunFromEntity(data);
378 }
379
380 if (sunChanged || skyboxChanged) {
381 updateKeyAmbientFromEntity(data);
382 }
383 if (backgroundChanged || skyboxChanged) {
384 updateKeyBackgroundFromEntity(data);
385 }
386 });
387}
388
389void RenderableZoneEntityItem::notifyChangedRenderItem() {
390 if (!render::Item::isValidID(_myMetaItem)) {
391 return;
392 }
393
394 render::Transaction transaction;
395 render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
396 sceneUpdateRenderItemFromEntity(transaction);
397 scene->enqueueTransaction(transaction);
398}
399
400
401
402
403
404
405
182 406
183 EntityItemPointer entity;
184};
185 407
186namespace render {408namespace render {
187 template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {409 template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {
188 return ItemKey::Builder::opaqueShape();410 return ItemKey::Builder().withTypeMeta().build();
189 }411 }
190 412
191 template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {413 template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
@@ -200,51 +422,199 @@ namespace render { · 8 unmodified lines
200 return render::Item::Bound();422 return render::Item::Bound();
201 }423 }
202 template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) {424 template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) {
203 if (args) {425 payload->render(args);
204 if (payload && payload->entity) {
205 payload->entity->render(args);
206 }
207 }
208 }426 }
209}427}
210 428
211bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,429RenderableZoneEntityItemMeta::RenderableZoneEntityItemMeta(EntityItemPointer entity) :
212 render::Transaction& transaction) {430 entity(entity),
213 _myMetaItem = scene->allocateID();431 _sunLight(std::make_shared<model::Light>()),
432 _ambientLight(std::make_shared<model::Light>()),
433 _background(std::make_shared<model::SunSkyStage>())
434{
435 _background->setSkybox(std::make_shared<ProceduralSkybox>());
436}
214 437
215 auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
216 auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);
217 438
218 render::Item::Status::Getters statusGetters;439RenderableZoneEntityItemMeta::~RenderableZoneEntityItemMeta() {
219 makeEntityItemStatusGetters(getThisPointer(), statusGetters);440 if (_stage) {
220 renderPayload->addStatusGetters(statusGetters);441 if (!LightStage::isIndexInvalid(_sunIndex)) {
442 _stage->removeLight(_sunIndex);
443 }
444 if (!LightStage::isIndexInvalid(_ambientIndex)) {
445 _stage->removeLight(_ambientIndex);
446
447 }
448 }
221 449
222 transaction.resetItem(_myMetaItem, renderPayload);450 if (_backgroundStage) {
223 return true;451 if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
452 _backgroundStage->removeBackground(_backgroundIndex);
453 }
454 }
224}455}
225 456
226void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,457void RenderableZoneEntityItemMeta::setAmbientURL(const QString& ambientUrl) {
227 render::Transaction& transaction) {458 // nothing change if nothing change
228 transaction.removeItem(_myMetaItem);459 if (_ambientTextureURL == ambientUrl) {
229 render::Item::clearID(_myMetaItem);460 return;
230 if (_model) {461 }
231 _model->removeFromScene(scene, transaction);462 _ambientTextureURL = ambientUrl;
463
464 if (_ambientTextureURL.isEmpty()) {
465 _validAmbientTexture = false;
466 _pendingAmbientTexture = false;
467 _ambientTexture.clear();
468
469 _ambientLight->setAmbientMap(nullptr);
470 _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
471 } else {
472 _pendingAmbientTexture = true;
473 auto textureCache = DependencyManager::get<TextureCache>();
474 _ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE);
475
476 // keep whatever is assigned on the ambient map/sphere until texture is loaded
232 }477 }
233}478}
234 479
480void RenderableZoneEntityItemMeta::updateAmbientMap() {
481 if (_pendingAmbientTexture) {
482 if (_ambientTexture && _ambientTexture->isLoaded()) {
483 _pendingAmbientTexture = false;
235 484
236void RenderableZoneEntityItem::notifyBoundChanged() {485 auto texture = _ambientTexture->getGPUTexture();
237 if (!render::Item::isValidID(_myMetaItem)) {486 if (texture) {
487 if (texture->getIrradiance()) {
488 _ambientLight->setAmbientSphere(*texture->getIrradiance());
489 } else {
490 _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
491 }
492 editAmbientLight()->setAmbientMap(texture);
493 _validAmbientTexture = true;
494 } else {
495 qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
496 }
497 }
498 }
499}
500
501
502void RenderableZoneEntityItemMeta::setSkyboxURL(const QString& skyboxUrl) {
503 // nothing change if nothing change
504 if (_skyboxTextureURL == skyboxUrl) {
238 return;505 return;
239 }506 }
240 render::Transaction transaction;507 _skyboxTextureURL = skyboxUrl;
241 render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
242 if (scene) {
243 transaction.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [](RenderableZoneEntityItemMeta& data) {
244 });
245 508
246 scene->enqueueTransaction(transaction);509 if (_skyboxTextureURL.isEmpty()) {
510 _validSkyboxTexture = false;
511 _pendingSkyboxTexture = false;
512 _skyboxTexture.clear();
513
514 editSkybox()->setCubemap(nullptr);
247 } else {515 } else {
248 qCWarning(entitiesrenderer) << "RenderableZoneEntityItem::notifyBoundChanged(), Unexpected null scene, possibly during application shutdown";516 _pendingSkyboxTexture = true;
517 auto textureCache = DependencyManager::get<TextureCache>();
518 _skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::CUBE_TEXTURE);
519 }
520}
521
522void RenderableZoneEntityItemMeta::updateSkyboxMap() {
523 if (_pendingSkyboxTexture) {
524 if (_skyboxTexture && _skyboxTexture->isLoaded()) {
525 _pendingSkyboxTexture = false;
526
527 auto texture = _skyboxTexture->getGPUTexture();
528 if (texture) {
529 editSkybox()->setCubemap(texture);
530 _validSkyboxTexture = true;
531 } else {
532 qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL();
533 }
534 }
535 }
536}
537
538void RenderableZoneEntityItemMeta::setBackgroundMode(BackgroundMode mode) {
539 _backgroundMode = mode;
540}
541
542void RenderableZoneEntityItemMeta::setSkyboxColor(const glm::vec3& color) {
543 editSkybox()->setColor(color);
544}
545
546void RenderableZoneEntityItemMeta::setProceduralUserData(QString userData) {
547 if (_proceduralUserData != userData) {
548 _proceduralUserData = userData;
549 std::dynamic_pointer_cast<ProceduralSkybox>(editSkybox())->parse(_proceduralUserData);
550 }
551}
552
553
554
555void RenderableZoneEntityItemMeta::render(RenderArgs* args) {
556 if (!_stage) {
557 _stage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
558 }
559
560 if (!_backgroundStage) {
561 _backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
562 }
563
564 { // Sun
565 // Need an update ?
566 if (_needSunUpdate) {
567 // Do we need to allocate the light in the stage ?
568 if (LightStage::isIndexInvalid(_sunIndex)) {
569 _sunIndex = _stage->addLight(_sunLight);
570 } else {
571 _stage->updateLightArrayBuffer(_sunIndex);
572 }
573 _needSunUpdate = false;
574 }
575 }
576
577 { // Ambient
578 updateAmbientMap();
579
580 // Need an update ?
581 if (_needAmbientUpdate) {
582 // Do we need to allocate the light in the stage ?
583 if (LightStage::isIndexInvalid(_ambientIndex)) {
584 _ambientIndex = _stage->addLight(_ambientLight);
585 } else {
586 _stage->updateLightArrayBuffer(_ambientIndex);
587 }
588 _needAmbientUpdate = false;
589 }
590 }
591
592 { // Skybox
593 updateSkyboxMap();
594
595 if (_needBackgroundUpdate) {
596 if (BackgroundStage::isIndexInvalid(_backgroundIndex)) {
597 _backgroundIndex = _backgroundStage->addBackground(_background);
598 } else {
599
600 }
601 _needBackgroundUpdate = false;
602 }
603 }
604
605 if (isVisible()) {
606 // FInally, push the light visible in the frame
607 // THe directional key light for sure
608 _stage->_currentFrame.pushSunLight(_sunIndex);
609
610 // The ambient light only if it has a valid texture to render with
611 if (_validAmbientTexture || _validSkyboxTexture) {
612 _stage->_currentFrame.pushAmbientLight(_ambientIndex);
613 }
614
615 // The background only if the mode is not inherit
616 if (_backgroundMode != BACKGROUND_MODE_INHERIT) {
617 _backgroundStage->_currentFrame.pushBackground(_backgroundIndex);
618 }
249 }619 }
250}620}
libraries/entities-renderer/src/RenderableZoneEntityItem.h+14−3
@@ -16,6 +16,9 @@ · 15 unmodified lines
16#include <ZoneEntityItem.h>16#include <ZoneEntityItem.h>
17 17
18class NetworkGeometry;18class NetworkGeometry;
19class KeyLightPayload;
20
21class RenderableZoneEntityItemMeta;
19 22
20class RenderableZoneEntityItem : public ZoneEntityItem {23class RenderableZoneEntityItem : public ZoneEntityItem {
21public:24public:
@@ -42,7 +45,7 @@ public: · 20 unmodified lines
42 virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;45 virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
43 46
44 render::ItemID getRenderItemID() const { return _myMetaItem; }47 render::ItemID getRenderItemID() const { return _myMetaItem; }
45 48
46private:49private:
47 virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }50 virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }
48 virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }51 virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
@@ -52,10 +55,18 @@ private: · 3 unmodified lines
52 55
53 template<typename Lambda>56 template<typename Lambda>
54 void changeProperties(Lambda functor);57 void changeProperties(Lambda functor);
55 58
59 void notifyChangedRenderItem();
60 void sceneUpdateRenderItemFromEntity(render::Transaction& transaction);
61 void updateKeyZoneItemFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
62
63 void updateKeySunFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
64 void updateKeyAmbientFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
65 void updateKeyBackgroundFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
66
56 ModelPointer _model;67 ModelPointer _model;
57 bool _needsInitialSimulation;68 bool _needsInitialSimulation;
58 69
59 render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };70 render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };
60};71};
61 72
libraries/entities/src/EntityItem.cpp+7−17
@@ -681,7 +681,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef · 680 unmodified lines
681 // and pretend that we own it (we assume we'll recover it soon)681 // and pretend that we own it (we assume we'll recover it soon)
682 682
683 // However, for now, when the server uses a newer time than what we sent, listen to what we're told.683 // However, for now, when the server uses a newer time than what we sent, listen to what we're told.
684 if (overwriteLocalData) weOwnSimulation = false;684 if (overwriteLocalData) {
685 weOwnSimulation = false;
686 }
685 } else if (_simulationOwner.set(newSimOwner)) {687 } else if (_simulationOwner.set(newSimOwner)) {
686 markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);688 markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
687 somethingChanged = true;689 somethingChanged = true;
@@ -1293,27 +1295,15 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c · 605 unmodified lines
1293 properties._accelerationChanged = true;1295 properties._accelerationChanged = true;
1294}1296}
1295 1297
1296void EntityItem::pokeSimulationOwnership() {1298void EntityItem::flagForOwnershipBid(uint8_t priority) {
1297 markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE);1299 markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
1298 auto nodeList = DependencyManager::get<NodeList>();
1299 if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
1300 // we already own it
1301 _simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY);
1302 } else {
1303 // we don't own it yet
1304 _simulationOwner.setPendingPriority(SCRIPT_POKE_SIMULATION_PRIORITY, usecTimestampNow());
1305 }
1306}
1307
1308void EntityItem::grabSimulationOwnership() {
1309 markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB);
1310 auto nodeList = DependencyManager::get<NodeList>();1300 auto nodeList = DependencyManager::get<NodeList>();
1311 if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {1301 if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
1312 // we already own it1302 // we already own it
1313 _simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY);1303 _simulationOwner.promotePriority(priority);
1314 } else {1304 } else {
1315 // we don't own it yet1305 // we don't own it yet
1316 _simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow());1306 _simulationOwner.setPendingPriority(priority, usecTimestampNow());
1317 }1307 }
1318}1308}
1319 1309
libraries/entities/src/EntityItem.h+2−2
@@ -321,6 +321,7 @@ public: · 320 unmodified lines
321 void updateSimulationOwner(const SimulationOwner& owner);321 void updateSimulationOwner(const SimulationOwner& owner);
322 void clearSimulationOwnership();322 void clearSimulationOwnership();
323 void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);323 void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);
324 uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); }
324 void rememberHasSimulationOwnershipBid() const;325 void rememberHasSimulationOwnershipBid() const;
325 326
326 QString getMarketplaceID() const;327 QString getMarketplaceID() const;
@@ -394,8 +395,7 @@ public: · 67 unmodified lines
394 395
395 void getAllTerseUpdateProperties(EntityItemProperties& properties) const;396 void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
396 397
397 void pokeSimulationOwnership();398 void flagForOwnershipBid(uint8_t priority);
398 void grabSimulationOwnership();
399 void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }399 void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
400 400
401 QString actionsToDebugString();401 QString actionsToDebugString();
libraries/entities/src/EntityScriptingInterface.cpp+6−4
@@ -230,6 +230,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties · 229 unmodified lines
230 }230 }
231 231
232 entity->setLastBroadcast(usecTimestampNow());232 entity->setLastBroadcast(usecTimestampNow());
233 // since we're creating this object we will immediately volunteer to own its simulation
234 entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
233 propertiesWithSimID.setLastEdited(entity->getLastEdited());235 propertiesWithSimID.setLastEdited(entity->getLastEdited());
234 } else {236 } else {
235 qCDebug(entities) << "script failed to add new Entity to local Octree";237 qCDebug(entities) << "script failed to add new Entity to local Octree";
@@ -440,7 +442,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& · 204 unmodified lines
440 } else {442 } else {
441 // we make a bid for simulation ownership443 // we make a bid for simulation ownership
442 properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);444 properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
443 entity->pokeSimulationOwnership();445 entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
444 entity->rememberHasSimulationOwnershipBid();446 entity->rememberHasSimulationOwnershipBid();
445 }447 }
446 }448 }
@@ -1194,7 +1196,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, · 747 unmodified lines
1194 }1196 }
1195 action->setIsMine(true);1197 action->setIsMine(true);
1196 success = entity->addAction(simulation, action);1198 success = entity->addAction(simulation, action);
1197 entity->grabSimulationOwnership();1199 entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
1198 return false; // Physics will cause a packet to be sent, so don't send from here.1200 return false; // Physics will cause a packet to be sent, so don't send from here.
1199 });1201 });
1200 if (success) {1202 if (success) {
@@ -1210,7 +1212,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& · 9 unmodified lines
1210 return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {1212 return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
1211 bool success = entity->updateAction(simulation, actionID, arguments);1213 bool success = entity->updateAction(simulation, actionID, arguments);
1212 if (success) {1214 if (success) {
1213 entity->grabSimulationOwnership();1215 entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
1214 }1216 }
1215 return success;1217 return success;
1216 });1218 });
@@ -1224,7 +1226,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& · 7 unmodified lines
1224 success = entity->removeAction(simulation, actionID);1226 success = entity->removeAction(simulation, actionID);
1225 if (success) {1227 if (success) {
1226 // reduce from grab to poke1228 // reduce from grab to poke
1227 entity->pokeSimulationOwnership();1229 entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
1228 }1230 }
1229 return false; // Physics will cause a packet to be sent, so don't send from here.1231 return false; // Physics will cause a packet to be sent, so don't send from here.
1230 });1232 });
libraries/entities/src/SimulationFlags.h+1−3
@@ -26,12 +26,10 @@ namespace Simulation { · 25 unmodified lines
26 const uint32_t DIRTY_MATERIAL = 0x00400;26 const uint32_t DIRTY_MATERIAL = 0x00400;
27 const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine27 const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine
28 const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed28 const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed
29 const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_POKE = 0x2000; // bid for simulation ownership at "poke"29 const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed
30 const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB = 0x4000; // bid for simulation ownership at "grab"
31 30
32 const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;31 const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
33 const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;32 const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
34 const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = DIRTY_SIMULATION_OWNERSHIP_FOR_POKE | DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB;
35};33};
36 34
37#endif // hifi_SimulationFlags_h35#endif // hifi_SimulationFlags_h
libraries/entities/src/SimulationOwner.cpp+11−11
@@ -26,9 +26,9 @@ const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1; · 25 unmodified lines
26SimulationOwner::SimulationOwner() :26SimulationOwner::SimulationOwner() :
27 _id(),27 _id(),
28 _expiry(0),28 _expiry(0),
29 _pendingTimestamp(0),29 _pendingBidTimestamp(0),
30 _priority(0),30 _priority(0),
31 _pendingPriority(0),31 _pendingBidPriority(0),
32 _pendingState(PENDING_STATE_NOTHING)32 _pendingState(PENDING_STATE_NOTHING)
33{33{
34}34}
@@ -36,9 +36,9 @@ SimulationOwner::SimulationOwner() : · 1 unmodified line
36SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) :36SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) :
37 _id(id),37 _id(id),
38 _expiry(0),38 _expiry(0),
39 _pendingTimestamp(0),39 _pendingBidTimestamp(0),
40 _priority(priority),40 _priority(priority),
41 _pendingPriority(0)41 _pendingBidPriority(0)
42{42{
43}43}
44 44
@@ -61,9 +61,9 @@ bool SimulationOwner::fromByteArray(const QByteArray& data) { · 16 unmodified lines
61void SimulationOwner::clear() {61void SimulationOwner::clear() {
62 _id = QUuid();62 _id = QUuid();
63 _expiry = 0;63 _expiry = 0;
64 _pendingTimestamp = 0;64 _pendingBidTimestamp = 0;
65 _priority = 0;65 _priority = 0;
66 _pendingPriority = 0;66 _pendingBidPriority = 0;
67 _pendingState = PENDING_STATE_NOTHING;67 _pendingState = PENDING_STATE_NOTHING;
68}68}
69 69
@@ -102,9 +102,9 @@ bool SimulationOwner::set(const SimulationOwner& owner) { · 32 unmodified lines
102}102}
103 103
104void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) {104void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) {
105 _pendingPriority = priority;105 _pendingBidPriority = priority;
106 _pendingTimestamp = timestamp;106 _pendingBidTimestamp = timestamp;
107 _pendingState = (_pendingPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;107 _pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
108}108}
109 109
110void SimulationOwner::updateExpiry() {110void SimulationOwner::updateExpiry() {
@@ -113,11 +113,11 @@ void SimulationOwner::updateExpiry() { · 2 unmodified lines
113}113}
114 114
115bool SimulationOwner::pendingRelease(const quint64& timestamp) {115bool SimulationOwner::pendingRelease(const quint64& timestamp) {
116 return _pendingPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingTimestamp >= timestamp;116 return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp;
117}117}
118 118
119bool SimulationOwner::pendingTake(const quint64& timestamp) {119bool SimulationOwner::pendingTake(const quint64& timestamp) {
120 return _pendingPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingTimestamp >= timestamp;120 return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp;
121}121}
122 122
123void SimulationOwner::clearCurrentOwner() {123void SimulationOwner::clearCurrentOwner() {
libraries/entities/src/SimulationOwner.h+3−2
@@ -66,6 +66,7 @@ public: · 65 unmodified lines
66 66
67 bool hasExpired() const { return usecTimestampNow() > _expiry; }67 bool hasExpired() const { return usecTimestampNow() > _expiry; }
68 68
69 uint8_t getPendingPriority() const { return _pendingBidPriority; }
69 bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE70 bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE
70 bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE71 bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE
71 void clearCurrentOwner();72 void clearCurrentOwner();
@@ -84,9 +85,9 @@ public: · 12 unmodified lines
84private:85private:
85 QUuid _id; // owner86 QUuid _id; // owner
86 quint64 _expiry; // time when ownership can transition at equal priority87 quint64 _expiry; // time when ownership can transition at equal priority
87 quint64 _pendingTimestamp; // time when pending update was set88 quint64 _pendingBidTimestamp; // time when pending bid was set
88 quint8 _priority; // priority of current owner89 quint8 _priority; // priority of current owner
89 quint8 _pendingPriority; // priority of pendingTake90 quint8 _pendingBidPriority; // priority at which we'd like to own it
90 quint8 _pendingState; // NOTHING, TAKE, or RELEASE91 quint8 _pendingState; // NOTHING, TAKE, or RELEASE
91};92};
92 93
libraries/entities/src/ZoneEntityItem.cpp+39−20
@@ -71,9 +71,27 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { · 70 unmodified lines
71 bool somethingChanged = false;71 bool somethingChanged = false;
72 somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class72 somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
73 73
74 bool somethingChangedInKeyLight = _keyLightProperties.setProperties(properties);74 if (somethingChanged) {
75 75 bool wantDebug = false;
76 bool somethingChangedInStage = _stageProperties.setProperties(properties);76 if (wantDebug) {
77 uint64_t now = usecTimestampNow();
78 int elapsed = now - getLastEdited();
79 qCDebug(entities) << "ZoneEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
80 "now=" << now << " getLastEdited()=" << getLastEdited();
81 }
82 setLastEdited(properties._lastEdited);
83 }
84
85 return somethingChanged;
86}
87
88bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
89 bool somethingChanged = EntityItem::setSubClassProperties(properties); // set the properties in our base class
90
91
92 _keyLightPropertiesChanged = _keyLightProperties.setProperties(properties);
93
94 _stagePropertiesChanged = _stageProperties.setProperties(properties);
77 95
78 SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);96 SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
79 SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);97 SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
@@ -83,20 +101,10 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { · 3 unmodified lines
83 SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);101 SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
84 SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);102 SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);
85 103
86 bool somethingChangedInSkybox = _skyboxProperties.setProperties(properties);104 _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties);
87 105
88 somethingChanged = somethingChanged || somethingChangedInKeyLight || somethingChangedInStage || somethingChangedInSkybox;106 somethingChanged = somethingChanged || _keyLightPropertiesChanged || _stagePropertiesChanged || _skyboxPropertiesChanged;
89 107
90 if (somethingChanged) {
91 bool wantDebug = false;
92 if (wantDebug) {
93 uint64_t now = usecTimestampNow();
94 int elapsed = now - getLastEdited();
95 qCDebug(entities) << "ZoneEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
96 "now=" << now << " getLastEdited()=" << getLastEdited();
97 }
98 setLastEdited(properties._lastEdited);
99 }
100 108
101 return somethingChanged;109 return somethingChanged;
102}110}
@@ -109,14 +117,14 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, · 6 unmodified lines
109 const unsigned char* dataAt = data;117 const unsigned char* dataAt = data;
110 118
111 int bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,119 int bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
112 propertyFlags, overwriteLocalData, somethingChanged);120 propertyFlags, overwriteLocalData, _keyLightPropertiesChanged);
113 121 somethingChanged = somethingChanged || _keyLightPropertiesChanged;
114 bytesRead += bytesFromKeylight;122 bytesRead += bytesFromKeylight;
115 dataAt += bytesFromKeylight;123 dataAt += bytesFromKeylight;
116 124
117 int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, 125 int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
118 propertyFlags, overwriteLocalData, somethingChanged);126 propertyFlags, overwriteLocalData, _stagePropertiesChanged);
119 127 somethingChanged = somethingChanged || _stagePropertiesChanged;
120 bytesRead += bytesFromStage;128 bytesRead += bytesFromStage;
121 dataAt += bytesFromStage;129 dataAt += bytesFromStage;
122 130
@@ -125,7 +133,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, · 2 unmodified lines
125 READ_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);133 READ_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
126 134
127 int bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, 135 int bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
128 propertyFlags, overwriteLocalData, somethingChanged);136 propertyFlags, overwriteLocalData, _skyboxPropertiesChanged);
137 somethingChanged = somethingChanged || _skyboxPropertiesChanged;
129 bytesRead += bytesFromSkybox;138 bytesRead += bytesFromSkybox;
130 dataAt += bytesFromSkybox;139 dataAt += bytesFromSkybox;
131 140
@@ -185,6 +194,16 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits · 53 unmodified lines
185 APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL());194 APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL());
186}195}
187 196
197void ZoneEntityItem::somethingChangedNotification() {
198 EntityItem::somethingChangedNotification();
199 withWriteLock([&] {
200 _keyLightPropertiesChanged = false;
201 _backgroundPropertiesChanged = false;
202 _stagePropertiesChanged = false;
203 _skyboxPropertiesChanged = false;
204 });
205}
206
188void ZoneEntityItem::debugDump() const {207void ZoneEntityItem::debugDump() const {
189 quint64 now = usecTimestampNow();208 quint64 now = usecTimestampNow();
190 qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------";209 qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------";
libraries/entities/src/ZoneEntityItem.h+15−1
@@ -29,10 +29,16 @@ public: · 28 unmodified lines
29 // methods for getting/setting all properties of an entity29 // methods for getting/setting all properties of an entity
30 virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;30 virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
31 virtual bool setProperties(const EntityItemProperties& properties) override;31 virtual bool setProperties(const EntityItemProperties& properties) override;
32 virtual bool setSubClassProperties(const EntityItemProperties& properties) override;
32 33
33 // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time34 // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
34 virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;35 virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
35 36
37 /// Override this in your derived class if you'd like to be informed when something about the state of the entity
38 /// has changed. This will be called with properties change or when new data is loaded from a stream
39 /// Overriding this function to capture the information that a keylight / Ambient / skybox properties has changed
40 virtual void somethingChangedNotification() override;
41
36 virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,42 virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
37 EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,43 EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
38 EntityPropertyFlags& requestedProperties,44 EntityPropertyFlags& requestedProperties,
@@ -64,7 +70,7 @@ public: · 25 unmodified lines
64 70
65 const KeyLightPropertyGroup& getKeyLightProperties() const { return _keyLightProperties; }71 const KeyLightPropertyGroup& getKeyLightProperties() const { return _keyLightProperties; }
66 72
67 void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; }73 void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; _backgroundPropertiesChanged = true; }
68 BackgroundMode getBackgroundMode() const { return _backgroundMode; }74 BackgroundMode getBackgroundMode() const { return _backgroundMode; }
69 75
70 const SkyboxPropertyGroup& getSkyboxProperties() const { return _skyboxProperties; }76 const SkyboxPropertyGroup& getSkyboxProperties() const { return _skyboxProperties; }
@@ -106,6 +112,14 @@ protected: · 35 unmodified lines
106 bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };112 bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
107 QString _filterURL { DEFAULT_FILTER_URL };113 QString _filterURL { DEFAULT_FILTER_URL };
108 114
115 // Dirty flags turn true when either keylight properties is changing values.
116 // This gets back to false in the somethingChangedNotification() call
117 // Which is called after a setProperties() or a readEntitySubClassFromBUfferCall on the entity.
118 bool _keyLightPropertiesChanged { false };
119 bool _backgroundPropertiesChanged { false };
120 bool _skyboxPropertiesChanged { false };
121 bool _stagePropertiesChanged { false };
122
109 static bool _drawZoneBoundaries;123 static bool _drawZoneBoundaries;
110 static bool _zonesArePickable;124 static bool _zonesArePickable;
111};125};
libraries/fbx/src/FBXReader.cpp+4−10
@@ -460,17 +460,11 @@ FBXLight extractLight(const FBXNode& object) { · 459 unmodified lines
460}460}
461 461
462QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) {462QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) {
463 QString path = QFileInfo(url).path();463 // in order to match the behaviour when loading models from remote URLs
464 QByteArray filename = filepath;464 // we assume that all external textures are right beside the loaded model
465 QFileInfo checkFile(path + "/" + filepath);465 // ignoring any relative paths or absolute paths inside of models
466
467 // check if the file exists at the RelativeFilename
468 if (!(checkFile.exists() && checkFile.isFile())) {
469 // if not, assume it is in the fbx directory
470 filename = filename.mid(filename.lastIndexOf('/') + 1);
471 }
472 466
473 return filename;467 return filepath.mid(filepath.lastIndexOf('/') + 1);
474}468}
475 469
476FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) {470FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) {
libraries/gl/CMakeLists.txt+1−1
@@ -1,6 +1,6 @@
1set(TARGET_NAME gl)1set(TARGET_NAME gl)
2setup_hifi_library(OpenGL Qml Quick)2setup_hifi_library(OpenGL Qml Quick)
3link_hifi_libraries(shared)3link_hifi_libraries(shared networking)
4 4
5target_opengl()5target_opengl()
6 6
libraries/gl/src/gl/OffscreenQmlSurface.cpp+152−75
@@ -33,6 +33,9 @@ · 32 unmodified lines
33#include <NetworkAccessManager.h>33#include <NetworkAccessManager.h>
34#include <GLMHelpers.h>34#include <GLMHelpers.h>
35#include <shared/GlobalAppProperties.h>35#include <shared/GlobalAppProperties.h>
36#include <FileTypeProfile.h>
37#include <HFWebEngineProfile.h>
38#include <HFTabletWebEngineProfile.h>
36 39
37#include "OffscreenGLCanvas.h"40#include "OffscreenGLCanvas.h"
38#include "GLHelpers.h"41#include "GLHelpers.h"
@@ -41,6 +44,7 @@ · 2 unmodified lines
41 44
42Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml")45Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml")
43Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")46Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")
47Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")
44 48
45struct TextureSet {49struct TextureSet {
46 // The number of surfaces with this size50 // The number of surfaces with this size
@@ -254,8 +258,89 @@ QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { · 207 unmodified lines
254 return new QmlNetworkAccessManager(parent);258 return new QmlNetworkAccessManager(parent);
255}259}
256 260
257Q_DECLARE_LOGGING_CATEGORY(offscreenFocus)261static QQmlEngine* globalEngine { nullptr };
258Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")262static size_t globalEngineRefCount { 0 };
263
264QString getEventBridgeJavascript() {
265 // FIXME: Refactor with similar code in RenderableWebEntityItem
266 QString javaScriptToInject;
267 QFile webChannelFile(":qtwebchannel/qwebchannel.js");
268 QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js");
269 if (webChannelFile.open(QFile::ReadOnly | QFile::Text) &&
270 createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) {
271 QString webChannelStr = QTextStream(&webChannelFile).readAll();
272 QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
273 javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
274 } else {
275 qCWarning(glLogging) << "Unable to find qwebchannel.js or createGlobalEventBridge.js";
276 }
277 return javaScriptToInject;
278}
279
280class EventBridgeWrapper : public QObject {
281 Q_OBJECT
282 Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT);
283
284public:
285 EventBridgeWrapper(QObject* eventBridge, QObject* parent = nullptr) : QObject(parent), _eventBridge(eventBridge) {
286 }
287
288 QObject* getEventBridge() {
289 return _eventBridge;
290 }
291
292private:
293 QObject* _eventBridge;
294};
295
296
297
298QQmlEngine* acquireEngine(QQuickWindow* window) {
299 Q_ASSERT(QThread::currentThread() == qApp->thread());
300 if (!globalEngine) {
301 Q_ASSERT(0 == globalEngineRefCount);
302 globalEngine = new QQmlEngine();
303 globalEngine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory);
304
305 auto importList = globalEngine->importPathList();
306 importList.insert(importList.begin(), PathUtils::resourcesPath());
307 globalEngine->setImportPathList(importList);
308 for (const auto& path : importList) {
309 qDebug() << path;
310 }
311
312 if (!globalEngine->incubationController()) {
313 globalEngine->setIncubationController(window->incubationController());
314 }
315 auto rootContext = globalEngine->rootContext();
316 rootContext->setContextProperty("GL", ::getGLContextData());
317 rootContext->setContextProperty("urlHandler", new UrlHandler());
318 rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
319 rootContext->setContextProperty("pathToFonts", "../../");
320 rootContext->setContextProperty("ApplicationInterface", qApp);
321 auto javaScriptToInject = getEventBridgeJavascript();
322 if (!javaScriptToInject.isEmpty()) {
323 rootContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
324 }
325 rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
326 rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
327 rootContext->setContextProperty("HFTabletWebEngineProfile", new HFTabletWebEngineProfile(rootContext));
328
329
330 }
331
332 ++globalEngineRefCount;
333 return globalEngine;
334}
335
336void releaseEngine() {
337 Q_ASSERT(QThread::currentThread() == qApp->thread());
338 Q_ASSERT(0 != globalEngineRefCount);
339 if (0 == --globalEngineRefCount) {
340 globalEngine->deleteLater();
341 globalEngine = nullptr;
342 }
343}
259 344
260void OffscreenQmlSurface::cleanup() {345void OffscreenQmlSurface::cleanup() {
261 _canvas->makeCurrent();346 _canvas->makeCurrent();
@@ -294,6 +379,7 @@ void OffscreenQmlSurface::render() { · 32 unmodified lines
294 GLuint texture = offscreenTextures.getNextTexture(_size);379 GLuint texture = offscreenTextures.getNextTexture(_size);
295 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);380 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
296 glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);381 glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
382 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
297 _renderControl->render();383 _renderControl->render();
298 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);384 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
299 glBindTexture(GL_TEXTURE_2D, texture);385 glBindTexture(GL_TEXTURE_2D, texture);
@@ -361,9 +447,8 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { · 61 unmodified lines
361 447
362 _canvas->deleteLater();448 _canvas->deleteLater();
363 _rootItem->deleteLater();449 _rootItem->deleteLater();
364 _qmlComponent->deleteLater();
365 _qmlEngine->deleteLater();
366 _quickWindow->deleteLater();450 _quickWindow->deleteLater();
451 releaseEngine();
367}452}
368 453
369void OffscreenQmlSurface::onAboutToQuit() {454void OffscreenQmlSurface::onAboutToQuit() {
@@ -375,6 +460,8 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { · 5 unmodified lines
375 qCDebug(glLogging) << "Building QML surface";460 qCDebug(glLogging) << "Building QML surface";
376 461
377 _renderControl = new QMyQuickRenderControl();462 _renderControl = new QMyQuickRenderControl();
463 connect(_renderControl, &QQuickRenderControl::renderRequested, this, [this] { _render = true; });
464 connect(_renderControl, &QQuickRenderControl::sceneChanged, this, [this] { _render = _polish = true; });
378 465
379 QQuickWindow::setDefaultAlphaBuffer(true);466 QQuickWindow::setDefaultAlphaBuffer(true);
380 467
@@ -385,7 +472,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { · 4 unmodified lines
385 // so we wait until after its ctor to move object/context to this thread.472 // so we wait until after its ctor to move object/context to this thread.
386 _quickWindow = new QQuickWindow(_renderControl);473 _quickWindow = new QQuickWindow(_renderControl);
387 _quickWindow->setColor(QColor(255, 255, 255, 0));474 _quickWindow->setColor(QColor(255, 255, 255, 0));
388 _quickWindow->setFlags(_quickWindow->flags() | static_cast<Qt::WindowFlags>(Qt::WA_TranslucentBackground));475 _quickWindow->setClearBeforeRendering(false);
389 476
390 _renderControl->_renderWindow = _proxyWindow;477 _renderControl->_renderWindow = _proxyWindow;
391 478
@@ -398,32 +485,21 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { · 6 unmodified lines
398 connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged);485 connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged);
399 486
400 // Create a QML engine.487 // Create a QML engine.
401 _qmlEngine = new QQmlEngine;488 auto qmlEngine = acquireEngine(_quickWindow);
402 489 _qmlContext = new QQmlContext(qmlEngine->rootContext());
403 _qmlEngine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory);
404
405 auto importList = _qmlEngine->importPathList();
406 importList.insert(importList.begin(), PathUtils::resourcesPath());
407 _qmlEngine->setImportPathList(importList);
408 if (!_qmlEngine->incubationController()) {
409 _qmlEngine->setIncubationController(_quickWindow->incubationController());
410 }
411
412 // FIXME
413 _glData = ::getGLContextData(); // Initialize JSON structure so that it can be filled in later and then used in QML.
414 _qmlEngine->rootContext()->setContextProperty("GL", _glData);
415 _qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
416 _qmlComponent = new QQmlComponent(_qmlEngine);
417 490
491 _qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
492 _qmlContext->setContextProperty("eventBridge", this);
493 _qmlContext->setContextProperty("webEntity", this);
418 494
419 connect(_renderControl, &QQuickRenderControl::renderRequested, this, [this] { _render = true; });495 // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
420 connect(_renderControl, &QQuickRenderControl::sceneChanged, this, [this] { _render = _polish = true; });496 // Find a way to flag older scripts using this mechanism and wanr that this is deprecated
497 _qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext));
421 498
422 if (!_canvas->makeCurrent()) {499 if (!_canvas->makeCurrent()) {
423 qWarning("Failed to make context current for QML Renderer");500 qWarning("Failed to make context current for QML Renderer");
424 return;501 return;
425 }502 }
426 _glData = ::getGLContextData();
427 _renderControl->initialize(_canvas->getContext());503 _renderControl->initialize(_canvas->getContext());
428 504
429 // When Quick says there is a need to render, we will not render immediately. Instead,505 // When Quick says there is a need to render, we will not render immediately. Instead,
@@ -433,11 +509,6 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { · 3 unmodified lines
433 _updateTimer.setTimerType(Qt::PreciseTimer);509 _updateTimer.setTimerType(Qt::PreciseTimer);
434 _updateTimer.setInterval(MIN_TIMER_MS); // 5ms, Qt::PreciseTimer required510 _updateTimer.setInterval(MIN_TIMER_MS); // 5ms, Qt::PreciseTimer required
435 _updateTimer.start();511 _updateTimer.start();
436
437 auto rootContext = getRootContext();
438 rootContext->setContextProperty("urlHandler", new UrlHandler());
439 rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
440 rootContext->setContextProperty("pathToFonts", "../../");
441}512}
442 513
443static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {514static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {
@@ -460,7 +531,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { · 16 unmodified lines
460 return;531 return;
461 }532 }
462 533
463 _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize);534 _qmlContext->setContextProperty("surfaceSize", newSize);
464 535
465 if (_rootItem) {536 if (_rootItem) {
466 _rootItem->setSize(newSize);537 _rootItem->setSize(newSize);
@@ -520,73 +591,82 @@ QQuickItem* OffscreenQmlSurface::getRootItem() { · 53 unmodified lines
520}591}
521 592
522void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {593void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
523 _qmlEngine->setBaseUrl(baseUrl);594 _qmlContext->setBaseUrl(baseUrl);
524}595}
525 596
526QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {597QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f) {
527 // Synchronous loading may take a while; restart the deadlock timer598 // Synchronous loading may take a while; restart the deadlock timer
528 QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);599 QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
529 600
530 qDebug() << qmlSource;601 QQmlContext* targetContext = _qmlContext;
531 _qmlComponent->loadUrl(qmlSource, QQmlComponent::PreferSynchronous);602 if (_rootItem && createNewContext) {
603 targetContext = new QQmlContext(targetContext);
604 }
532 605
533 if (_qmlComponent->isLoading()) {606 QUrl finalQmlSource = qmlSource;
534 connect(_qmlComponent, &QQmlComponent::statusChanged, this,607 if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
535 [this, f](QQmlComponent::Status){608 finalQmlSource = _qmlContext->resolvedUrl(qmlSource);
536 finishQmlLoad(f);609 }
537 });610
611 auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
612 if (qmlComponent->isLoading()) {
613 connect(qmlComponent, &QQmlComponent::statusChanged, this,
614 [this, qmlComponent, targetContext, f](QQmlComponent::Status) {
615 finishQmlLoad(qmlComponent, targetContext, f);
616 });
538 return nullptr;617 return nullptr;
539 }618 }
540 619
541 return finishQmlLoad(f);620 return finishQmlLoad(qmlComponent, targetContext, f);
621}
622
623QObject* OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
624 return load(qmlSource, true, f);
625}
626
627QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
628 return load(qmlSource, false, f);
542}629}
543 630
544void OffscreenQmlSurface::clearCache() {631void OffscreenQmlSurface::clearCache() {
545 getRootContext()->engine()->clearComponentCache();632 _qmlContext->engine()->clearComponentCache();
546}633}
547 634
548QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f) {635QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f) {
549 disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0);636 disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
550 if (_qmlComponent->isError()) {637 if (qmlComponent->isError()) {
551 QList<QQmlError> errorList = _qmlComponent->errors();638 for (const auto& error : qmlComponent->errors()) {
552 foreach(const QQmlError& error, errorList) {639 qCWarning(glLogging) << error.url() << error.line() << error;
553 qWarning() << error.url() << error.line() << error;
554 }640 }
641 qmlComponent->deleteLater();
555 return nullptr;642 return nullptr;
556 }643 }
557 644
558 // FIXME: Refactor with similar code in RenderableWebEntityItem
559 QString javaScriptToInject;
560 QFile webChannelFile(":qtwebchannel/qwebchannel.js");
561 QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js");
562 if (webChannelFile.open(QFile::ReadOnly | QFile::Text) &&
563 createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) {
564 QString webChannelStr = QTextStream(&webChannelFile).readAll();
565 QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
566 javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
567 } else {
568 qCWarning(glLogging) << "Unable to find qwebchannel.js or createGlobalEventBridge.js";
569 }
570 645
571 QQmlContext* newContext = new QQmlContext(_qmlEngine, qApp);646 QObject* newObject = qmlComponent->beginCreate(qmlContext);
572 QObject* newObject = _qmlComponent->beginCreate(newContext);647 if (qmlComponent->isError()) {
573 if (_qmlComponent->isError()) {648 for (const auto& error : qmlComponent->errors()) {
574 QList<QQmlError> errorList = _qmlComponent->errors();
575 foreach(const QQmlError& error, errorList)
576 qCWarning(glLogging) << error.url() << error.line() << error;649 qCWarning(glLogging) << error.url() << error.line() << error;
650 }
577 if (!_rootItem) {651 if (!_rootItem) {
578 qFatal("Unable to finish loading QML root");652 qFatal("Unable to finish loading QML root");
579 }653 }
654 qmlComponent->deleteLater();
580 return nullptr;655 return nullptr;
581 }656 }
582 657
583 _qmlEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);658 qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
584 newObject->setProperty("eventBridge", QVariant::fromValue(this));659 f(qmlContext, newObject);
585 660
586 newContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));661 QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
662 if (qmlContext != _qmlContext && eventBridge && eventBridge != this) {
663 // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
664 // Find a way to flag older scripts using this mechanism and wanr that this is deprecated
665 qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext));
666 }
587 667
588 f(newContext, newObject);668 qmlComponent->completeCreate();
589 _qmlComponent->completeCreate();669 qmlComponent->deleteLater();
590 670
591 671
592 // All quick items should be focusable672 // All quick items should be focusable
@@ -736,7 +816,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even · 143 unmodified lines
736 mouseEvent->screenPos(), mouseEvent->button(),816 mouseEvent->screenPos(), mouseEvent->button(),
737 mouseEvent->buttons(), mouseEvent->modifiers());817 mouseEvent->buttons(), mouseEvent->modifiers());
738 if (event->type() == QEvent::MouseMove) {818 if (event->type() == QEvent::MouseMove) {
739 _qmlEngine->rootContext()->setContextProperty("lastMousePosition", transformedPos);819 _qmlContext->setContextProperty("lastMousePosition", transformedPos);
740 }820 }
741 mappedEvent.ignore();821 mappedEvent.ignore();
742 if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) {822 if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) {
@@ -763,9 +843,6 @@ void OffscreenQmlSurface::resume() { · 20 unmodified lines
763 if (getRootItem()) {843 if (getRootItem()) {
764 getRootItem()->setProperty("eventBridge", QVariant::fromValue(this));844 getRootItem()->setProperty("eventBridge", QVariant::fromValue(this));
765 }845 }
766 if (getRootContext()) {
767 getRootContext()->setContextProperty("webEntity", this);
768 }
769}846}
770 847
771bool OffscreenQmlSurface::isPaused() const {848bool OffscreenQmlSurface::isPaused() const {
@@ -791,8 +868,8 @@ QSize OffscreenQmlSurface::size() const { · 19 unmodified lines
791 return _quickWindow->geometry().size();868 return _quickWindow->geometry().size();
792}869}
793 870
794QQmlContext* OffscreenQmlSurface::getRootContext() {871QQmlContext* OffscreenQmlSurface::getSurfaceContext() {
795 return _qmlEngine->rootContext();872 return _qmlContext;
796}873}
797 874
798Q_DECLARE_METATYPE(std::function<void()>);875Q_DECLARE_METATYPE(std::function<void()>);
libraries/gl/src/gl/OffscreenQmlSurface.h+5−5
@@ -48,6 +48,8 @@ public: · 47 unmodified lines
48 void resize(const QSize& size, bool forceResize = false);48 void resize(const QSize& size, bool forceResize = false);
49 QSize size() const;49 QSize size() const;
50 50
51 Q_INVOKABLE QObject* load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
52 Q_INVOKABLE QObject* loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
51 Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});53 Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
52 Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {54 Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
53 return load(QUrl(qmlSourceFile), f);55 return load(QUrl(qmlSourceFile), f);
@@ -73,7 +75,7 @@ public: · 19 unmodified lines
73 QQuickItem* getRootItem();75 QQuickItem* getRootItem();
74 QQuickWindow* getWindow();76 QQuickWindow* getWindow();
75 QObject* getEventHandler();77 QObject* getEventHandler();
76 QQmlContext* getRootContext();78 QQmlContext* getSurfaceContext();
77 79
78 QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);80 QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
79 bool eventFilter(QObject* originalDestination, QEvent* event) override;81 bool eventFilter(QObject* originalDestination, QEvent* event) override;
@@ -118,7 +120,7 @@ protected: · 38 unmodified lines
118 void setFocusText(bool newFocusText);120 void setFocusText(bool newFocusText);
119 121
120private:122private:
121 QObject* finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f);123 QObject* finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f);
122 QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);124 QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
123 void setupFbo();125 void setupFbo();
124 bool allowNewFrame(uint8_t fps);126 bool allowNewFrame(uint8_t fps);
@@ -133,11 +135,9 @@ private slots: · 8 unmodified lines
133private:135private:
134 QQuickWindow* _quickWindow { nullptr };136 QQuickWindow* _quickWindow { nullptr };
135 QMyQuickRenderControl* _renderControl{ nullptr };137 QMyQuickRenderControl* _renderControl{ nullptr };
136 QQmlEngine* _qmlEngine { nullptr };138 QQmlContext* _qmlContext { nullptr };
137 QQmlComponent* _qmlComponent { nullptr };
138 QQuickItem* _rootItem { nullptr };139 QQuickItem* _rootItem { nullptr };
139 OffscreenGLCanvas* _canvas { nullptr };140 OffscreenGLCanvas* _canvas { nullptr };
140 QJsonObject _glData;
141 141
142 QTimer _updateTimer;142 QTimer _updateTimer;
143 uint32_t _fbo { 0 };143 uint32_t _fbo { 0 };
libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp+0−1
@@ -49,7 +49,6 @@ QSharedPointer<OffscreenQmlSurface> OffscreenQmlSurfaceCache::buildSurface(const · 48 unmodified lines
49 surface->create(currentContext);49 surface->create(currentContext);
50 surface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));50 surface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
51 surface->load(rootSource);51 surface->load(rootSource);
52 surface->getRootContext()->setContextProperty("ApplicationInterface", qApp);
53 surface->resize(QSize(100, 100));52 surface->resize(QSize(100, 100));
54 currentContext->makeCurrent(currentSurface);53 currentContext->makeCurrent(currentSurface);
55 return surface;54 return surface;
libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp+13−2
@@ -63,11 +63,17 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { · 62 unmodified lines
63 int useScissor = batch._params[paramOffset + 0]._int;63 int useScissor = batch._params[paramOffset + 0]._int;
64 64
65 GLuint glmask = 0;65 GLuint glmask = 0;
66 bool restoreStencilMask = false;
67 uint8_t cacheStencilMask = 0xFF;
66 if (masks & Framebuffer::BUFFER_STENCIL) {68 if (masks & Framebuffer::BUFFER_STENCIL) {
67 glClearStencil(stencil);69 glClearStencil(stencil);
68 glmask |= GL_STENCIL_BUFFER_BIT;70 glmask |= GL_STENCIL_BUFFER_BIT;
69 // TODO: we will probably need to also check the write mask of stencil like we do71
70 // for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it"72 cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront();
73 if (cacheStencilMask != 0xFF) {
74 restoreStencilMask = true;
75 glStencilMask( 0xFF);
76 }
71 }77 }
72 78
73 bool restoreDepthMask = false;79 bool restoreDepthMask = false;
@@ -121,6 +127,11 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { · 47 unmodified lines
121 glDisable(GL_SCISSOR_TEST);127 glDisable(GL_SCISSOR_TEST);
122 }128 }
123 129
130 // Restore Stencil write mask
131 if (restoreStencilMask) {
132 glStencilMask(cacheStencilMask);
133 }
134
124 // Restore write mask meaning turn back off135 // Restore write mask meaning turn back off
125 if (restoreDepthMask) {136 if (restoreDepthMask) {
126 glDepthMask(GL_FALSE);137 glDepthMask(GL_FALSE);
libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp+4−0
@@ -211,6 +211,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { · 210 unmodified lines
211 break;211 break;
212 case gpu::NUINT32:212 case gpu::NUINT32:
213 case gpu::NINT32:213 case gpu::NINT32:
214 case gpu::COMPRESSED:
214 case gpu::NUM_TYPES: // quiet compiler215 case gpu::NUM_TYPES: // quiet compiler
215 Q_UNREACHABLE();216 Q_UNREACHABLE();
216 }217 }
@@ -484,6 +485,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E · 267 unmodified lines
484 texel.internalFormat = GL_R8_SNORM;485 texel.internalFormat = GL_R8_SNORM;
485 break;486 break;
486 }487 }
488 case gpu::COMPRESSED:
487 case gpu::NUM_TYPES: { // quiet compiler489 case gpu::NUM_TYPES: { // quiet compiler
488 Q_UNREACHABLE();490 Q_UNREACHABLE();
489 }491 }
@@ -527,6 +529,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E · 37 unmodified lines
527 texel.internalFormat = GL_DEPTH_COMPONENT24;529 texel.internalFormat = GL_DEPTH_COMPONENT24;
528 break;530 break;
529 }531 }
532 case gpu::COMPRESSED:
530 case gpu::NUM_TYPES: { // quiet compiler533 case gpu::NUM_TYPES: { // quiet compiler
531 Q_UNREACHABLE();534 Q_UNREACHABLE();
532 }535 }
@@ -641,6 +644,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E · 108 unmodified lines
641 break;644 break;
642 case gpu::NUINT32:645 case gpu::NUINT32:
643 case gpu::NINT32:646 case gpu::NINT32:
647 case gpu::COMPRESSED:
644 case gpu::NUM_TYPES: // quiet compiler648 case gpu::NUM_TYPES: // quiet compiler
645 Q_UNREACHABLE();649 Q_UNREACHABLE();
646 }650 }
libraries/gpu/src/gpu/DrawTransformVertexPosition.slv+28−0
@@ -0,0 +1,28 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// Draw and transform the fed vertex position with the standard MVP stack
6// Output the clip position
7//
8// Created by Sam Gateau on 5/30/2017
9// Copyright 2017 High Fidelity, Inc.
10//
11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//
14
15<@include gpu/Transform.slh@>
16
17<$declareStandardTransform()$>
18
19layout(location = 0) in vec4 inPosition;
20
21out vec3 varWorldPos;
22
23void main(void) {
24 // standard transform
25 TransformCamera cam = getTransformCamera();
26 TransformObject obj = getTransformObject();
27 <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
28}
libraries/gpu/src/gpu/DrawVertexPosition.slvrenamed from libraries/render-utils/src/hit_effect.slv+6−13
@@ -2,25 +2,18 @@ · 1 unmodified line
2<$VERSION_HEADER$>2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>3// Generated on <$_SCRIBE_DATE$>
4//4//
5// hit_effect.vert5// Draw the fed vertex position, pass straight as clip pos
6// vertex shader6// Output the clip position
7//7//
8// Created by Eric Levin on 7/20/15.8// Created by Sam Gateau on 5/30/2017
9// Copyright 2015 High Fidelity, Inc.9// Copyright 2017 High Fidelity, Inc.
10//10//
11// Distributed under the Apache License, Version 2.0.11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//13//
14 14
15<@include gpu/Inputs.slh@>15layout(location = 0) in vec4 inPosition;
16
17<@include gpu/Transform.slh@>
18
19<$declareStandardTransform()$>
20
21out vec2 varQuadPosition;
22 16
23void main(void) {17void main(void) {
24 varQuadPosition = inPosition.xy;
25 gl_Position = inPosition;18 gl_Position = inPosition;
26}19}
libraries/gpu/src/gpu/DrawWhite.slfrenamed from libraries/render-utils/src/drawOpaqueStencil.slf+6−4
@@ -2,15 +2,17 @@ · 1 unmodified line
2<$VERSION_HEADER$>2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>3// Generated on <$_SCRIBE_DATE$>
4//4//
5// drawOpaqueStencil.frag5// Draw white
6// fragment shader
7//6//
8// Created by Sam Gateau on 9/29/15.7// Created by Sam Gateau on 5/30/2017
9// Copyright 2015 High Fidelity, Inc.8// Copyright 2017 High Fidelity, Inc.
10//9//
11// Distributed under the Apache License, Version 2.0.10// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html11// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//12//
14 13
14out vec4 outFragColor;
15
15void main(void) {16void main(void) {
17 outFragColor = vec4(1.0);
16}18}
libraries/gpu/src/gpu/StandardShaderLib.cpp+38−2
@@ -16,6 +16,12 @@ · 15 unmodified lines
16#include "DrawTransformUnitQuad_vert.h"16#include "DrawTransformUnitQuad_vert.h"
17#include "DrawTexcoordRectTransformUnitQuad_vert.h"17#include "DrawTexcoordRectTransformUnitQuad_vert.h"
18#include "DrawViewportQuadTransformTexcoord_vert.h"18#include "DrawViewportQuadTransformTexcoord_vert.h"
19#include "DrawVertexPosition_vert.h"
20#include "DrawTransformVertexPosition_vert.h"
21
22const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple...
23
24#include "DrawWhite_frag.h"
19#include "DrawTexture_frag.h"25#include "DrawTexture_frag.h"
20#include "DrawTextureOpaque_frag.h"26#include "DrawTextureOpaque_frag.h"
21#include "DrawColoredTexture_frag.h"27#include "DrawColoredTexture_frag.h"
@@ -26,6 +32,10 @@ ShaderPointer StandardShaderLib::_drawUnitQuadTexcoordVS; · 4 unmodified lines
26ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;32ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;
27ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS;33ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS;
28ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;34ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;
35ShaderPointer StandardShaderLib::_drawVertexPositionVS;
36ShaderPointer StandardShaderLib::_drawTransformVertexPositionVS;
37ShaderPointer StandardShaderLib::_drawNadaPS;
38ShaderPointer StandardShaderLib::_drawWhitePS;
29ShaderPointer StandardShaderLib::_drawTexturePS;39ShaderPointer StandardShaderLib::_drawTexturePS;
30ShaderPointer StandardShaderLib::_drawTextureOpaquePS;40ShaderPointer StandardShaderLib::_drawTextureOpaquePS;
31ShaderPointer StandardShaderLib::_drawColoredTexturePS;41ShaderPointer StandardShaderLib::_drawColoredTexturePS;
@@ -85,6 +95,34 @@ ShaderPointer StandardShaderLib::getDrawViewportQuadTransformTexcoordVS() { · 53 unmodified lines
85 return _drawViewportQuadTransformTexcoordVS;95 return _drawViewportQuadTransformTexcoordVS;
86}96}
87 97
98ShaderPointer StandardShaderLib::getDrawVertexPositionVS() {
99 if (!_drawVertexPositionVS) {
100 _drawVertexPositionVS = gpu::Shader::createVertex(std::string(DrawVertexPosition_vert));
101 }
102 return _drawVertexPositionVS;
103}
104
105ShaderPointer StandardShaderLib::getDrawTransformVertexPositionVS() {
106 if (!_drawTransformVertexPositionVS) {
107 _drawTransformVertexPositionVS = gpu::Shader::createVertex(std::string(DrawTransformVertexPosition_vert));
108 }
109 return _drawTransformVertexPositionVS;
110}
111
112ShaderPointer StandardShaderLib::getDrawNadaPS() {
113 if (!_drawNadaPS) {
114 _drawNadaPS = gpu::Shader::createPixel(std::string(DrawNada_frag));
115 }
116 return _drawNadaPS;
117}
118
119ShaderPointer StandardShaderLib::getDrawWhitePS() {
120 if (!_drawWhitePS) {
121 _drawWhitePS = gpu::Shader::createPixel(std::string(DrawWhite_frag));
122 }
123 return _drawWhitePS;
124}
125
88ShaderPointer StandardShaderLib::getDrawTexturePS() {126ShaderPointer StandardShaderLib::getDrawTexturePS() {
89 if (!_drawTexturePS) {127 if (!_drawTexturePS) {
90 _drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));128 _drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));
@@ -99,8 +137,6 @@ ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() { · 8 unmodified lines
99 return _drawTextureOpaquePS;137 return _drawTextureOpaquePS;
100}138}
101 139
102
103
104ShaderPointer StandardShaderLib::getDrawColoredTexturePS() {140ShaderPointer StandardShaderLib::getDrawColoredTexturePS() {
105 if (!_drawColoredTexturePS) {141 if (!_drawColoredTexturePS) {
106 _drawColoredTexturePS = gpu::Shader::createPixel(std::string(DrawColoredTexture_frag));142 _drawColoredTexturePS = gpu::Shader::createPixel(std::string(DrawColoredTexture_frag));
libraries/gpu/src/gpu/StandardShaderLib.h+15−0
@@ -37,6 +37,15 @@ public: · 36 unmodified lines
37 // Shader draws the unit quad in the full viewport clipPos = ([(-1,-1),(1,1)]) and transform the texcoord = [(0,0),(1,1)] by the model transform.37 // Shader draws the unit quad in the full viewport clipPos = ([(-1,-1),(1,1)]) and transform the texcoord = [(0,0),(1,1)] by the model transform.
38 static ShaderPointer getDrawViewportQuadTransformTexcoordVS();38 static ShaderPointer getDrawViewportQuadTransformTexcoordVS();
39 39
40 // Shader draw the fed vertex position and transform it by the full model transform stack (Model, View, Proj).
41 // simply output the world pos and the clip pos to the next stage
42 static ShaderPointer getDrawVertexPositionVS();
43 static ShaderPointer getDrawTransformVertexPositionVS();
44
45 // PShader does nothing, no really nothing, but still needed for defining a program triggering rasterization
46 static ShaderPointer getDrawNadaPS();
47
48 static ShaderPointer getDrawWhitePS();
40 static ShaderPointer getDrawTexturePS();49 static ShaderPointer getDrawTexturePS();
41 static ShaderPointer getDrawTextureOpaquePS();50 static ShaderPointer getDrawTextureOpaquePS();
42 static ShaderPointer getDrawColoredTexturePS();51 static ShaderPointer getDrawColoredTexturePS();
@@ -51,6 +60,12 @@ protected: · 8 unmodified lines
51 static ShaderPointer _drawTransformUnitQuadVS;60 static ShaderPointer _drawTransformUnitQuadVS;
52 static ShaderPointer _drawTexcoordRectTransformUnitQuadVS;61 static ShaderPointer _drawTexcoordRectTransformUnitQuadVS;
53 static ShaderPointer _drawViewportQuadTransformTexcoordVS;62 static ShaderPointer _drawViewportQuadTransformTexcoordVS;
63
64 static ShaderPointer _drawVertexPositionVS;
65 static ShaderPointer _drawTransformVertexPositionVS;
66
67 static ShaderPointer _drawNadaPS;
68 static ShaderPointer _drawWhitePS;
54 static ShaderPointer _drawTexturePS;69 static ShaderPointer _drawTexturePS;
55 static ShaderPointer _drawTextureOpaquePS;70 static ShaderPointer _drawTextureOpaquePS;
56 static ShaderPointer _drawColoredTexturePS;71 static ShaderPointer _drawColoredTexturePS;
libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h+0−2
@@ -70,8 +70,6 @@ public: · 69 unmodified lines
70 bool isSupported() const override { return true; }70 bool isSupported() const override { return true; }
71 const QString getName() const override { return NAME; }71 const QString getName() const override { return NAME; }
72 72
73 bool isHandController() const override { return false; }
74
75 void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }73 void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
76 void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;74 void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
77 75
libraries/input-plugins/src/input-plugins/TouchscreenDevice.h+0−2
@@ -39,8 +39,6 @@ public: · 38 unmodified lines
39 virtual bool isSupported() const override;39 virtual bool isSupported() const override;
40 virtual const QString getName() const override { return NAME; }40 virtual const QString getName() const override { return NAME; }
41 41
42 bool isHandController() const override { return false; }
43
44 virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }42 virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
45 virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;43 virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
46 44
libraries/ktx/src/khronos/KHR.h+2−0
@@ -211,6 +211,8 @@ namespace khronos { · 210 unmodified lines
211 211
212 template <uint32_t ALIGNMENT> 212 template <uint32_t ALIGNMENT>
213 inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) {213 inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) {
214 enum { val = ALIGNMENT && !(ALIGNMENT & (ALIGNMENT - 1)) };
215 static_assert(val, "template parameter ALIGNMENT must be a power of 2");
214 // FIXME add static assert that ALIGNMENT is a power of 2216 // FIXME add static assert that ALIGNMENT is a power of 2
215 static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1;217 static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1;
216 return (value + ALIGNMENT_REMAINDER) / ALIGNMENT;218 return (value + ALIGNMENT_REMAINDER) / ALIGNMENT;
libraries/ktx/src/ktx/Writer.cpp+1−1
@@ -229,7 +229,7 @@ namespace ktx { · 228 unmodified lines
229 } else {229 } else {
230 Image::FaceBytes faceBytes(NUM_CUBEMAPFACES);230 Image::FaceBytes faceBytes(NUM_CUBEMAPFACES);
231 auto faceSize = srcImages[l]._faceSize;231 auto faceSize = srcImages[l]._faceSize;
232 for (int face = 0; face < NUM_CUBEMAPFACES; face++) {232 for (uint32_t face = 0; face < NUM_CUBEMAPFACES; face++) {
233 memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize);233 memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize);
234 faceBytes[face] = currentPtr;234 faceBytes[face] = currentPtr;
235 currentPtr += faceSize;235 currentPtr += faceSize;
libraries/model-networking/src/model-networking/KTXCache.cpp+14−0
@@ -11,14 +11,28 @@ · 10 unmodified lines
11 11
12#include "KTXCache.h"12#include "KTXCache.h"
13 13
14#include <SettingHandle.h>
14#include <ktx/KTX.h>15#include <ktx/KTX.h>
15 16
16using File = cache::File;17using File = cache::File;
17using FilePointer = cache::FilePointer;18using FilePointer = cache::FilePointer;
18 19
20// Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible,
21// this value should be incremented. This will force the KTX cache to be wiped
22const int KTXCache::CURRENT_VERSION = 0x01;
23const int KTXCache::INVALID_VERSION = 0x00;
24const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version";
25
19KTXCache::KTXCache(const std::string& dir, const std::string& ext) :26KTXCache::KTXCache(const std::string& dir, const std::string& ext) :
20 FileCache(dir, ext) {27 FileCache(dir, ext) {
21 initialize();28 initialize();
29
30 Setting::Handle<int> cacheVersionHandle(SETTING_VERSION_NAME, INVALID_VERSION);
31 auto cacheVersion = cacheVersionHandle.get();
32 if (cacheVersion != CURRENT_VERSION) {
33 wipe();
34 cacheVersionHandle.set(CURRENT_VERSION);
35 }
22}36}
23 37
24KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) {38KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) {
libraries/model-networking/src/model-networking/KTXCache.h+6−0
@@ -27,6 +27,12 @@ class KTXCache : public cache::FileCache { · 26 unmodified lines
27 Q_OBJECT27 Q_OBJECT
28 28
29public:29public:
30 // Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible,
31 // this value should be incremented. This will force the KTX cache to be wiped
32 static const int CURRENT_VERSION;
33 static const int INVALID_VERSION;
34 static const char* SETTING_VERSION_NAME;
35
30 KTXCache(const std::string& dir, const std::string& ext);36 KTXCache(const std::string& dir, const std::string& ext);
31 37
32 KTXFilePointer writeFile(const char* data, Metadata&& metadata);38 KTXFilePointer writeFile(const char* data, Metadata&& metadata);
libraries/model/src/model/Geometry.cpp+38−0
@@ -241,6 +241,42 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc, · 240 unmodified lines
241 }241 }
242}242}
243 243
244MeshPointer Mesh::createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numIndices, const glm::vec3* vertices, const uint32_t* indices) {
245 MeshPointer mesh;
246 if (numVertices == 0) { return mesh; }
247 if (numIndices < 3) { return mesh; }
248
249 mesh = std::make_shared<Mesh>();
250
251 // Vertex buffer
252 mesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(numVertices * sizeof(glm::vec3), (gpu::Byte*) vertices), gpu::Element::VEC3F_XYZ));
253
254 // trim down the indices to shorts if possible
255 if (numIndices < std::numeric_limits<uint16_t>::max()) {
256 Indices16 shortIndicesVector;
257 int16_t* shortIndices = nullptr;
258 if (indices) {
259 shortIndicesVector.resize(numIndices);
260 for (uint32_t i = 0; i < numIndices; i++) {
261 shortIndicesVector[i] = indices[i];
262 }
263 shortIndices = shortIndicesVector.data();
264 }
265
266 mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(numIndices * sizeof(uint16_t), (gpu::Byte*) shortIndices), gpu::Element::INDEX_UINT16));
267 } else {
268
269 mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(numIndices * sizeof(uint32_t), (gpu::Byte*) indices), gpu::Element::INDEX_INT32));
270 }
271
272
273 std::vector<model::Mesh::Part> parts;
274 parts.push_back(model::Mesh::Part(0, numIndices, 0, model::Mesh::TRIANGLES));
275 mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
276
277 return mesh;
278}
279
244 280
245Geometry::Geometry() {281Geometry::Geometry() {
246}282}
@@ -256,3 +292,5 @@ Geometry::~Geometry() { · 9 unmodified lines
256void Geometry::setMesh(const MeshPointer& mesh) {292void Geometry::setMesh(const MeshPointer& mesh) {
257 _mesh = mesh;293 _mesh = mesh;
258}294}
295
296
libraries/model/src/model/Geometry.h+6−0
@@ -65,6 +65,9 @@ public: · 64 unmodified lines
65 const gpu::BufferStream& getVertexStream() const { return _vertexStream; }65 const gpu::BufferStream& getVertexStream() const { return _vertexStream; }
66 66
67 // Index Buffer67 // Index Buffer
68 using Indices16 = std::vector<int16_t>;
69 using Indices32 = std::vector<int32_t>;
70
68 void setIndexBuffer(const BufferView& buffer);71 void setIndexBuffer(const BufferView& buffer);
69 const BufferView& getIndexBuffer() const { return _indexBuffer; }72 const BufferView& getIndexBuffer() const { return _indexBuffer; }
70 size_t getNumIndices() const { return _indexBuffer.getNumElements(); }73 size_t getNumIndices() const { return _indexBuffer.getNumElements(); }
@@ -127,6 +130,9 @@ public: · 56 unmodified lines
127 std::function<void(glm::vec3)> normalFunc,130 std::function<void(glm::vec3)> normalFunc,
128 std::function<void(uint32_t)> indexFunc);131 std::function<void(uint32_t)> indexFunc);
129 132
133
134 static MeshPointer createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numTriangles, const glm::vec3* vertices = nullptr, const uint32_t* indices = nullptr);
135
130protected:136protected:
131 137
132 gpu::Stream::FormatPointer _vertexFormat;138 gpu::Stream::FormatPointer _vertexFormat;
libraries/model/src/model/Light.cpp+1−1
@@ -145,7 +145,7 @@ void Light::setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset) { · 144 unmodified lines
145 _ambientSchemaBuffer.edit().ambientSphere.assignPreset(preset);145 _ambientSchemaBuffer.edit().ambientSphere.assignPreset(preset);
146}146}
147 147
148void Light::setAmbientMap(gpu::TexturePointer ambientMap) {148void Light::setAmbientMap(const gpu::TexturePointer& ambientMap) {
149 _ambientMap = ambientMap;149 _ambientMap = ambientMap;
150 if (ambientMap) {150 if (ambientMap) {
151 setAmbientMapNumMips(_ambientMap->getNumMips());151 setAmbientMapNumMips(_ambientMap->getNumMips());
libraries/model/src/model/Light.h+3−3
@@ -68,7 +68,8 @@ public: · 67 unmodified lines
68 68
69 69
70 enum Type {70 enum Type {
71 SUN = 0,71 AMBIENT = 0,
72 SUN,
72 POINT,73 POINT,
73 SPOT,74 SPOT,
74 75
@@ -112,7 +113,6 @@ public: · 37 unmodified lines
112 void setIntensity(float intensity);113 void setIntensity(float intensity);
113 114
114 bool isRanged() const { return (getType() == POINT) || (getType() == SPOT); }115 bool isRanged() const { return (getType() == POINT) || (getType() == SPOT); }
115 bool hasAmbient() const { return (getType() == SUN); }
116 116
117 // FalloffRradius is the physical radius of the light sphere through which energy shines,117 // FalloffRradius is the physical radius of the light sphere through which energy shines,
118 // expressed in meters. It is used only to calculate the falloff curve of the light.118 // expressed in meters. It is used only to calculate the falloff curve of the light.
@@ -143,7 +143,7 @@ public: · 24 unmodified lines
143 const gpu::SphericalHarmonics& getAmbientSphere() const { return _ambientSchemaBuffer->ambientSphere; }143 const gpu::SphericalHarmonics& getAmbientSphere() const { return _ambientSchemaBuffer->ambientSphere; }
144 void setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset);144 void setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset);
145 145
146 void setAmbientMap(gpu::TexturePointer ambientMap);146 void setAmbientMap(const gpu::TexturePointer& ambientMap);
147 gpu::TexturePointer getAmbientMap() const { return _ambientMap; }147 gpu::TexturePointer getAmbientMap() const { return _ambientMap; }
148 148
149 void setAmbientMapNumMips(uint16_t numMips);149 void setAmbientMapNumMips(uint16_t numMips);
libraries/model/src/model/Skybox.cpp+1−1
@@ -97,7 +97,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky · 96 unmodified lines
97 }97 }
98 98
99 auto skyState = std::make_shared<gpu::State>();99 auto skyState = std::make_shared<gpu::State>();
100 skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));100 skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(1, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
101 101
102 thePipeline = gpu::Pipeline::create(skyShader, skyState);102 thePipeline = gpu::Pipeline::create(skyShader, skyState);
103 }103 }
libraries/model/src/model/Skybox.h+4−0
@@ -25,6 +25,8 @@ typedef glm::vec3 Color; · 24 unmodified lines
25 25
26class Skybox {26class Skybox {
27public:27public:
28 typedef gpu::BufferView UniformBufferView;
29
28 Skybox();30 Skybox();
29 Skybox& operator= (const Skybox& skybox);31 Skybox& operator= (const Skybox& skybox);
30 virtual ~Skybox() {};32 virtual ~Skybox() {};
@@ -43,6 +45,8 @@ public: · 12 unmodified lines
43 45
44 static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);46 static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
45 47
48 const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
49
46protected:50protected:
47 static const int SKYBOX_SKYMAP_SLOT { 0 };51 static const int SKYBOX_SKYMAP_SLOT { 0 };
48 static const int SKYBOX_CONSTANTS_SLOT { 0 };52 static const int SKYBOX_CONSTANTS_SLOT { 0 };
libraries/networking/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1set(TARGET_NAME networking)1set(TARGET_NAME networking)
2setup_hifi_library(Network)2setup_hifi_library(Network WebEngine)
3link_hifi_libraries(shared)3link_hifi_libraries(shared)
4 4
5target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")5target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")
libraries/networking/src/AccountManager.cpp+7−1
@@ -45,7 +45,6 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation) · 44 unmodified lines
45Q_DECLARE_METATYPE(JSONCallbackParameters)45Q_DECLARE_METATYPE(JSONCallbackParameters)
46 46
47const QString ACCOUNTS_GROUP = "accounts";47const QString ACCOUNTS_GROUP = "accounts";
48static const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
49 48
50JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,49JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
51 QObject* errorCallbackReceiver, const QString& errorCallbackMethod,50 QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
@@ -201,6 +200,13 @@ void AccountManager::setAuthURL(const QUrl& authURL) { · 149 unmodified lines
201 }200 }
202}201}
203 202
203void AccountManager::setSessionID(const QUuid& sessionID) {
204 if (_sessionID != sessionID) {
205 qCDebug(networking) << "Metaverse session ID changed to" << uuidStringWithoutCurlyBraces(sessionID);
206 _sessionID = sessionID;
207 }
208}
209
204void AccountManager::sendRequest(const QString& path,210void AccountManager::sendRequest(const QString& path,
205 AccountManagerAuth::Type authType,211 AccountManagerAuth::Type authType,
206 QNetworkAccessManager::Operation operation,212 QNetworkAccessManager::Operation operation,
libraries/networking/src/AccountManager.h+2−1
@@ -52,6 +52,7 @@ namespace AccountManagerAuth { · 51 unmodified lines
52Q_DECLARE_METATYPE(AccountManagerAuth::Type);52Q_DECLARE_METATYPE(AccountManagerAuth::Type);
53 53
54const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";54const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
55const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
55 56
56using UserAgentGetter = std::function<QString()>;57using UserAgentGetter = std::function<QString()>;
57 58
@@ -90,7 +91,7 @@ public: · 32 unmodified lines
90 static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply);91 static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply);
91 92
92 QUuid getSessionID() const { return _sessionID; }93 QUuid getSessionID() const { return _sessionID; }
93 void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; }94 void setSessionID(const QUuid& sessionID);
94 95
95 void setTemporaryDomain(const QUuid& domainID, const QString& key);96 void setTemporaryDomain(const QUuid& domainID, const QString& key);
96 const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }97 const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }
libraries/networking/src/AssetRequest.cpp+7−2
@@ -15,11 +15,13 @@ · 14 unmodified lines
15 15
16#include <QtCore/QThread>16#include <QtCore/QThread>
17 17
18#include <StatTracker.h>
19#include <Trace.h>
20
18#include "AssetClient.h"21#include "AssetClient.h"
19#include "NetworkLogging.h"22#include "NetworkLogging.h"
20#include "NodeList.h"23#include "NodeList.h"
21#include "ResourceCache.h"24#include "ResourceCache.h"
22#include <Trace.h>
23 25
24static int requestID = 0;26static int requestID = 0;
25 27
@@ -62,9 +64,12 @@ void AssetRequest::start() { · 36 unmodified lines
62 _data = loadFromCache(getUrl());64 _data = loadFromCache(getUrl());
63 if (!_data.isNull()) {65 if (!_data.isNull()) {
64 _error = NoError;66 _error = NoError;
65 67
68 _loadedFromCache = true;
69
66 _state = Finished;70 _state = Finished;
67 emit finished(this);71 emit finished(this);
72
68 return;73 return;
69 }74 }
70 75
libraries/networking/src/AssetRequest.h+3−0
@@ -52,6 +52,8 @@ public: · 51 unmodified lines
52 QUrl getUrl() const { return ::getATPUrl(_hash); }52 QUrl getUrl() const { return ::getATPUrl(_hash); }
53 QString getHash() const { return _hash; }53 QString getHash() const { return _hash; }
54 54
55 bool loadedFromCache() const { return _loadedFromCache; }
56
55signals:57signals:
56 void finished(AssetRequest* thisRequest);58 void finished(AssetRequest* thisRequest);
57 void progress(qint64 totalReceived, qint64 total);59 void progress(qint64 totalReceived, qint64 total);
@@ -66,6 +68,7 @@ private: · 8 unmodified lines
66 int _numPendingRequests { 0 };68 int _numPendingRequests { 0 };
67 MessageID _assetRequestID { INVALID_MESSAGE_ID };69 MessageID _assetRequestID { INVALID_MESSAGE_ID };
68 const ByteRange _byteRange;70 const ByteRange _byteRange;
71 bool _loadedFromCache { false };
69};72};
70 73
71#endif74#endif
libraries/networking/src/AssetResourceRequest.cpp+33−3
@@ -13,12 +13,14 @@ · 12 unmodified lines
13 13
14#include <QtCore/QLoggingCategory>14#include <QtCore/QLoggingCategory>
15 15
16#include <Trace.h>
17#include <Profile.h>
18#include <StatTracker.h>
19
16#include "AssetClient.h"20#include "AssetClient.h"
17#include "AssetUtils.h"21#include "AssetUtils.h"
18#include "MappingRequest.h"22#include "MappingRequest.h"
19#include "NetworkLogging.h"23#include "NetworkLogging.h"
20#include <Trace.h>
21#include <Profile.h>
22 24
23static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5;25static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5;
24 26
@@ -48,6 +50,8 @@ bool AssetResourceRequest::urlIsAssetHash(const QUrl& url) { · 23 unmodified lines
48}50}
49 51
50void AssetResourceRequest::doSend() {52void AssetResourceRequest::doSend() {
53 DependencyManager::get<StatTracker>()->incrementStat(STAT_ATP_REQUEST_STARTED);
54
51 // We'll either have a hash or an ATP path to a file (that maps to a hash)55 // We'll either have a hash or an ATP path to a file (that maps to a hash)
52 if (urlIsAssetHash(_url)) {56 if (urlIsAssetHash(_url)) {
53 // We've detected that this is a hash - simply use AssetClient to request that asset57 // We've detected that this is a hash - simply use AssetClient to request that asset
@@ -65,11 +69,16 @@ void AssetResourceRequest::doSend() { · 11 unmodified lines
65}69}
66 70
67void AssetResourceRequest::requestMappingForPath(const AssetPath& path) {71void AssetResourceRequest::requestMappingForPath(const AssetPath& path) {
72 auto statTracker = DependencyManager::get<StatTracker>();
73 statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_STARTED);
74
68 auto assetClient = DependencyManager::get<AssetClient>();75 auto assetClient = DependencyManager::get<AssetClient>();
69 _assetMappingRequest = assetClient->createGetMappingRequest(path);76 _assetMappingRequest = assetClient->createGetMappingRequest(path);
70 77
71 // make sure we'll hear about the result of the get mapping request78 // make sure we'll hear about the result of the get mapping request
72 connect(_assetMappingRequest, &GetMappingRequest::finished, this, [this, path](GetMappingRequest* request){79 connect(_assetMappingRequest, &GetMappingRequest::finished, this, [this, path](GetMappingRequest* request){
80 auto statTracker = DependencyManager::get<StatTracker>();
81
73 Q_ASSERT(_state == InProgress);82 Q_ASSERT(_state == InProgress);
74 Q_ASSERT(request == _assetMappingRequest);83 Q_ASSERT(request == _assetMappingRequest);
75 84
@@ -80,6 +89,8 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { · 4 unmodified lines
80 89
81 requestHash(request->getHash());90 requestHash(request->getHash());
82 91
92 statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_SUCCESS);
93
83 break;94 break;
84 default: {95 default: {
85 switch (request->getError()) {96 switch (request->getError()) {
@@ -100,6 +111,9 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { · 14 unmodified lines
100 _state = Finished;111 _state = Finished;
101 emit finished();112 emit finished();
102 113
114 statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_FAILED);
115 statTracker->incrementStat(STAT_ATP_REQUEST_FAILED);
116
103 break;117 break;
104 }118 }
105 }119 }
@@ -140,10 +154,26 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) { · 34 unmodified lines
140 _result = Error;154 _result = Error;
141 break;155 break;
142 }156 }
143 157
158 auto statTracker = DependencyManager::get<StatTracker>();
159
160 if (_assetRequest->loadedFromCache()) {
161 _loadedFromCache = true;
162 }
163
144 _state = Finished;164 _state = Finished;
145 emit finished();165 emit finished();
146 166
167 if (_result == Success) {
168 statTracker->incrementStat(STAT_ATP_REQUEST_SUCCESS);
169
170 if (loadedFromCache()) {
171 statTracker->incrementStat(STAT_ATP_REQUEST_CACHE);
172 }
173 } else {
174 statTracker->incrementStat(STAT_ATP_REQUEST_FAILED);
175 }
176
147 _assetRequest->deleteLater();177 _assetRequest->deleteLater();
148 _assetRequest = nullptr;178 _assetRequest = nullptr;
149 });179 });
libraries/networking/src/FileCache.cpp+146−64
@@ -11,52 +11,81 @@ · 10 unmodified lines
11 11
12#include "FileCache.h"12#include "FileCache.h"
13 13
14#include <cstdio>14
15#include <cassert>
16#include <fstream>
17#include <unordered_set>15#include <unordered_set>
16#include <queue>
17#include <cassert>
18 18
19#include <QDir>19#include <QtCore/QDateTime>
20#include <QSaveFile>20#include <QtCore/QDir>
21#include <QtCore/QSaveFile>
22#include <QtCore/QStorageInfo>
21 23
22#include <PathUtils.h>24#include <PathUtils.h>
25#include <NumericalConstants.h>
23 26
24Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg)27#ifdef Q_OS_WIN
28#include <sys/utime.h>
29#else
30#include <utime.h>
31#endif
25 32
33#ifdef NDEBUG
34Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg)
35#else
36Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache")
37#endif
26using namespace cache;38using namespace cache;
27 39
28static const std::string MANIFEST_NAME = "manifest";40static const char DIR_SEP = '/';
41static const char EXT_SEP = '.';
42
43const size_t FileCache::DEFAULT_MAX_SIZE { GB_TO_BYTES(5) };
44const size_t FileCache::MAX_MAX_SIZE { GB_TO_BYTES(100) };
45const size_t FileCache::DEFAULT_MIN_FREE_STORAGE_SPACE { GB_TO_BYTES(1) };
46
29 47
30static const size_t BYTES_PER_MEGABYTES = 1024 * 1024;48std::string getCacheName(const std::string& dirname_str) {
31static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES;49 QString dirname { dirname_str.c_str() };
32const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB50 QDir dir(dirname);
33const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB51 if (!dir.isAbsolute()) {
34const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB52 return dirname_str;
53 }
54 return dir.dirName().toStdString();
55}
35 56
36void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) {57std::string getCachePath(const std::string& dirname_str) {
37 _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE);58 QString dirname { dirname_str.c_str() };
38 reserve(0);59 QDir dir(dirname);
60 if (dir.isAbsolute()) {
61 return dirname_str;
62 }
63 return PathUtils::getAppLocalDataFilePath(dirname).toStdString();
64}
65
66void FileCache::setMinFreeSize(size_t size) {
67 _minFreeSpaceSize = size;
68 clean();
39 emit dirty();69 emit dirty();
40}70}
41 71
42void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) {72void FileCache::setMaxSize(size_t maxSize) {
43 _offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE);73 _maxSize = std::min(maxSize, MAX_MAX_SIZE);
74 clean();
75 emit dirty();
44}76}
45 77
46FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) :78FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) :
47 QObject(parent),79 QObject(parent),
48 _ext(ext),80 _ext(ext),
49 _dirname(dirname),81 _dirname(getCacheName(dirname)),
50 _dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {}82 _dirpath(getCachePath(dirname)) {
83}
51 84
52FileCache::~FileCache() {85FileCache::~FileCache() {
53 clear();86 clear();
54}87}
55 88
56void fileDeleter(File* file) {
57 file->deleter();
58}
59
60void FileCache::initialize() {89void FileCache::initialize() {
61 QDir dir(_dirpath.c_str());90 QDir dir(_dirpath.c_str());
62 91
@@ -84,7 +113,7 @@ void FileCache::initialize() { · 21 unmodified lines
84}113}
85 114
86FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) {115FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) {
87 FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter);116 FilePointer file(createFile(std::move(metadata), filepath).release(), std::mem_fn(&File::deleter));
88 if (file) {117 if (file) {
89 _numTotalFiles += 1;118 _numTotalFiles += 1;
90 _totalFilesSize += file->getLength();119 _totalFilesSize += file->getLength();
@@ -141,6 +170,7 @@ FilePointer FileCache::getFile(const Key& key) { · 50 unmodified lines
141 if (it != _files.cend()) {170 if (it != _files.cend()) {
142 file = it->second.lock();171 file = it->second.lock();
143 if (file) {172 if (file) {
173 file->touch();
144 // if it exists, it is active - remove it from the cache174 // if it exists, it is active - remove it from the cache
145 removeUnusedFile(file);175 removeUnusedFile(file);
146 qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str());176 qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str());
@@ -155,82 +185,127 @@ FilePointer FileCache::getFile(const Key& key) { · 8 unmodified lines
155}185}
156 186
157std::string FileCache::getFilepath(const Key& key) {187std::string FileCache::getFilepath(const Key& key) {
158 return _dirpath + '/' + key + '.' + _ext;188 return _dirpath + DIR_SEP + key + EXT_SEP + _ext;
159}189}
160 190
161void FileCache::addUnusedFile(const FilePointer file) {191void FileCache::addUnusedFile(const FilePointer& file) {
162 {192 {
163 Lock lock(_filesMutex);193 Lock lock(_filesMutex);
164 _files[file->getKey()] = file;194 _files[file->getKey()] = file;
165 }195 }
166 196
167 reserve(file->getLength());
168 file->_LRUKey = ++_lastLRUKey;
169
170 {197 {
171 Lock lock(_unusedFilesMutex);198 Lock lock(_unusedFilesMutex);
172 _unusedFiles.insert({ file->_LRUKey, file });199 _unusedFiles.insert(file);
173 _numUnusedFiles += 1;200 _numUnusedFiles += 1;
174 _unusedFilesSize += file->getLength();201 _unusedFilesSize += file->getLength();
175 }202 }
203 clean();
176 204
177 emit dirty();205 emit dirty();
178}206}
179 207
180void FileCache::removeUnusedFile(const FilePointer file) {208void FileCache::removeUnusedFile(const FilePointer& file) {
181 Lock lock(_unusedFilesMutex);209 Lock lock(_unusedFilesMutex);
182 const auto it = _unusedFiles.find(file->_LRUKey);210 if (_unusedFiles.erase(file)) {
183 if (it != _unusedFiles.cend()) {
184 _unusedFiles.erase(it);
185 _numUnusedFiles -= 1;211 _numUnusedFiles -= 1;
186 _unusedFilesSize -= file->getLength();212 _unusedFilesSize -= file->getLength();
187 }213 }
188}214}
189 215
190void FileCache::reserve(size_t length) {216size_t FileCache::getOverbudgetAmount() const {
191 Lock unusedLock(_unusedFilesMutex);217 size_t result = 0;
192 while (!_unusedFiles.empty() &&218
193 _unusedFilesSize + length > _unusedFilesMaxSize) {219 size_t currentFreeSpace = QStorageInfo(_dirpath.c_str()).bytesFree();
194 auto it = _unusedFiles.begin();220 if (_minFreeSpaceSize > currentFreeSpace) {
195 auto file = it->second;221 result = _minFreeSpaceSize - currentFreeSpace;
196 auto length = file->getLength();222 }
197 223
198 unusedLock.unlock();224 if (_totalFilesSize > _maxSize) {
199 {225 result = std::max(_totalFilesSize - _maxSize, result);
200 file->_cache = nullptr;226 }
201 Lock lock(_filesMutex);227
202 _files.erase(file->getKey());228 return result;
229}
230
231namespace cache {
232 struct FilePointerComparator {
233 bool operator()(const FilePointer& a, const FilePointer& b) {
234 return a->_modified > b->_modified;
203 }235 }
204 unusedLock.lock();236 };
237}
205 238
206 _unusedFiles.erase(it);239void FileCache::eject(const FilePointer& file) {
207 _numTotalFiles -= 1;240 file->_cache = nullptr;
208 _numUnusedFiles -= 1;241 const auto& length = file->getLength();
209 _totalFilesSize -= length;242 const auto& key = file->getKey();
210 _unusedFilesSize -= length;243
244 {
245 Lock lock(_filesMutex);
246 if (0 != _files.erase(key)) {
247 _numTotalFiles -= 1;
248 _totalFilesSize -= length;
249 }
250 }
251
252 {
253 Lock unusedLock(_unusedFilesMutex);
254 if (0 != _unusedFiles.erase(file)) {
255 _numUnusedFiles -= 1;
256 _unusedFilesSize -= length;
257 }
258 }
259}
260
261void FileCache::clean() {
262 size_t overbudgetAmount = getOverbudgetAmount();
263
264 // Avoid sorting the unused files by LRU if we're not over budget / under free space
265 if (0 == overbudgetAmount) {
266 return;
267 }
268
269 Lock unusedLock(_unusedFilesMutex);
270 using Queue = std::priority_queue<FilePointer, std::vector<FilePointer>, FilePointerComparator>;
271 Queue queue;
272 for (const auto& file : _unusedFiles) {
273 queue.push(file);
274 }
275
276 while (!queue.empty() && overbudgetAmount > 0) {
277 auto file = queue.top();
278 queue.pop();
279 eject(file);
280 auto length = file->getLength();
281 overbudgetAmount -= std::min(length, overbudgetAmount);
282 }
283}
284
285void FileCache::wipe() {
286 Lock unusedFilesLock(_unusedFilesMutex);
287 while (!_unusedFiles.empty()) {
288 eject(*_unusedFiles.begin());
211 }289 }
212}290}
213 291
214void FileCache::clear() {292void FileCache::clear() {
293 // Eliminate any overbudget files
294 clean();
295
296 // Mark everything remaining as persisted
215 Lock unusedFilesLock(_unusedFilesMutex);297 Lock unusedFilesLock(_unusedFilesMutex);
216 for (const auto& pair : _unusedFiles) {298 for (auto& file : _unusedFiles) {
217 auto& file = pair.second;299 file->_shouldPersist = true;
218 file->_cache = nullptr;300 file->_cache = nullptr;
219 301 qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str());
220 if (_totalFilesSize > _offlineFilesMaxSize) {
221 _totalFilesSize -= file->getLength();
222 } else {
223 file->_shouldPersist = true;
224 qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str());
225 }
226 }302 }
227 _unusedFiles.clear();303 _unusedFiles.clear();
228}304}
229 305
230void File::deleter() {306void File::deleter() {
231 if (_cache) {307 if (_cache) {
232 FilePointer self(this, &fileDeleter);308 _cache->addUnusedFile(FilePointer(this, std::mem_fn(&File::deleter)));
233 _cache->addUnusedFile(self);
234 } else {309 } else {
235 deleteLater();310 deleteLater();
236 }311 }
@@ -239,7 +314,9 @@ void File::deleter() { · 2 unmodified lines
239File::File(Metadata&& metadata, const std::string& filepath) :314File::File(Metadata&& metadata, const std::string& filepath) :
240 _key(std::move(metadata.key)),315 _key(std::move(metadata.key)),
241 _length(metadata.length),316 _length(metadata.length),
242 _filepath(filepath) {}317 _filepath(filepath),
318 _modified(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch()) {
319}
243 320
244File::~File() {321File::~File() {
245 QFile file(getFilepath().c_str());322 QFile file(getFilepath().c_str());
@@ -248,3 +325,8 @@ File::~File() { · 2 unmodified lines
248 file.remove();325 file.remove();
249 }326 }
250}327}
328
329void File::touch() {
330 utime(_filepath.c_str(), nullptr);
331 _modified = std::max<int64_t>(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified);
332}
libraries/networking/src/FileCache.h+42−24
@@ -14,6 +14,7 @@ · 13 unmodified lines
14#include <memory>14#include <memory>
15#include <cstddef>15#include <cstddef>
16#include <map>16#include <map>
17#include <unordered_set>
17#include <mutex>18#include <mutex>
18#include <string>19#include <string>
19#include <unordered_map>20#include <unordered_map>
@@ -35,24 +36,31 @@ class FileCache : public QObject { · 15 unmodified lines
35 Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty)36 Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty)
36 Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty)37 Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty)
37 38
38 static const size_t DEFAULT_UNUSED_MAX_SIZE;39 static const size_t DEFAULT_MAX_SIZE;
39 static const size_t MAX_UNUSED_MAX_SIZE;40 static const size_t MAX_MAX_SIZE;
40 static const size_t DEFAULT_OFFLINE_MAX_SIZE;41 static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE;
41 42
42public:43public:
44 // You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path
45 // The file cache will ignore any file that doesn't match the extension provided
46 FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr);
47 virtual ~FileCache();
48
49 // Remove all unlocked items from the cache
50 void wipe();
51
43 size_t getNumTotalFiles() const { return _numTotalFiles; }52 size_t getNumTotalFiles() const { return _numTotalFiles; }
44 size_t getNumCachedFiles() const { return _numUnusedFiles; }53 size_t getNumCachedFiles() const { return _numUnusedFiles; }
45 size_t getSizeTotalFiles() const { return _totalFilesSize; }54 size_t getSizeTotalFiles() const { return _totalFilesSize; }
46 size_t getSizeCachedFiles() const { return _unusedFilesSize; }55 size_t getSizeCachedFiles() const { return _unusedFilesSize; }
47 56
48 void setUnusedFileCacheSize(size_t unusedFilesMaxSize);57 // Set the maximum amount of disk space to use on disk
49 size_t getUnusedFileCacheSize() const { return _unusedFilesSize; }58 void setMaxSize(size_t maxCacheSize);
50 59
51 void setOfflineFileCacheSize(size_t offlineFilesMaxSize);60 // Set the minumum amount of free disk space to retain. This supercedes the max size,
52 61 // so if the cache is consuming all but 500 MB of the drive, unused entries will be ejected
53 // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg")62 // to free up more space, regardless of the cache max size
54 FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr);63 void setMinFreeSize(size_t size);
55 virtual ~FileCache();
56 64
57 using Key = std::string;65 using Key = std::string;
58 struct Metadata {66 struct Metadata {
@@ -76,10 +84,11 @@ public: · 17 unmodified lines
76signals:84signals:
77 void dirty();85 void dirty();
78 86
79protected:87public:
80 /// must be called after construction to create the cache on the fs and restore persisted files88 /// must be called after construction to create the cache on the fs and restore persisted files
81 void initialize();89 void initialize();
82 90
91 // Add file to the cache and return the cache entry.
83 FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false);92 FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false);
84 FilePointer getFile(const Key& key);93 FilePointer getFile(const Key& key);
85 94
@@ -89,17 +98,28 @@ protected: · 3 unmodified lines
89private:98private:
90 using Mutex = std::recursive_mutex;99 using Mutex = std::recursive_mutex;
91 using Lock = std::unique_lock<Mutex>;100 using Lock = std::unique_lock<Mutex>;
101 using Map = std::unordered_map<Key, std::weak_ptr<File>>;
102 using Set = std::unordered_set<FilePointer>;
103 using KeySet = std::unordered_set<Key>;
92 104
93 friend class File;105 friend class File;
94 106
95 std::string getFilepath(const Key& key);107 std::string getFilepath(const Key& key);
96 108
97 FilePointer addFile(Metadata&& metadata, const std::string& filepath);109 FilePointer addFile(Metadata&& metadata, const std::string& filepath);
98 void addUnusedFile(const FilePointer file);110 void addUnusedFile(const FilePointer& file);
99 void removeUnusedFile(const FilePointer file);111 void removeUnusedFile(const FilePointer& file);
100 void reserve(size_t length);112 void clean();
101 void clear();113 void clear();
114 // Remove a file from the cache
115 void eject(const FilePointer& file);
102 116
117 size_t getOverbudgetAmount() const;
118
119 // FIXME it might be desirable to have the min free space variable be static so it can be
120 // shared among multiple instances of FileCache
121 std::atomic<size_t> _minFreeSpaceSize { DEFAULT_MIN_FREE_STORAGE_SPACE };
122 std::atomic<size_t> _maxSize { DEFAULT_MAX_SIZE };
103 std::atomic<size_t> _numTotalFiles { 0 };123 std::atomic<size_t> _numTotalFiles { 0 };
104 std::atomic<size_t> _numUnusedFiles { 0 };124 std::atomic<size_t> _numUnusedFiles { 0 };
105 std::atomic<size_t> _totalFilesSize { 0 };125 std::atomic<size_t> _totalFilesSize { 0 };
@@ -110,15 +130,11 @@ private: · 4 unmodified lines
110 std::string _dirpath;130 std::string _dirpath;
111 bool _initialized { false };131 bool _initialized { false };
112 132
113 std::unordered_map<Key, std::weak_ptr<File>> _files;133 Map _files;
114 Mutex _filesMutex;134 Mutex _filesMutex;
115 135
116 std::map<int, FilePointer> _unusedFiles;136 Set _unusedFiles;
117 Mutex _unusedFilesMutex;137 Mutex _unusedFilesMutex;
118 size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE };
119 int _lastLRUKey { 0 };
120
121 size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE };
122};138};
123 139
124class File : public QObject {140class File : public QObject {
@@ -128,8 +144,8 @@ public: · 3 unmodified lines
128 using Key = FileCache::Key;144 using Key = FileCache::Key;
129 using Metadata = FileCache::Metadata;145 using Metadata = FileCache::Metadata;
130 146
131 Key getKey() const { return _key; }147 const Key& getKey() const { return _key; }
132 size_t getLength() const { return _length; }148 const size_t& getLength() const { return _length; }
133 std::string getFilepath() const { return _filepath; }149 std::string getFilepath() const { return _filepath; }
134 150
135 virtual ~File();151 virtual ~File();
@@ -142,13 +158,15 @@ protected: · 6 unmodified lines
142 158
143private:159private:
144 friend class FileCache;160 friend class FileCache;
161 friend struct FilePointerComparator;
145 162
146 const Key _key;163 const Key _key;
147 const size_t _length;164 const size_t _length;
148 const std::string _filepath;165 const std::string _filepath;
149 166
150 FileCache* _cache;167 void touch();
151 int _LRUKey { 0 };168 FileCache* _cache { nullptr };
169 int64_t _modified { 0 };
152 170
153 bool _shouldPersist { false };171 bool _shouldPersist { false };
154};172};
libraries/networking/src/FileResourceRequest.cpp+11−0
@@ -15,7 +15,12 @@ · 14 unmodified lines
15 15
16#include <QFile>16#include <QFile>
17 17
18#include <StatTracker.h>
19
18void FileResourceRequest::doSend() {20void FileResourceRequest::doSend() {
21 auto statTracker = DependencyManager::get<StatTracker>();
22 statTracker->incrementStat(STAT_FILE_REQUEST_STARTED);
23
19 QString filename = _url.toLocalFile();24 QString filename = _url.toLocalFile();
20 25
21 // sometimes on windows, we see the toLocalFile() return null,26 // sometimes on windows, we see the toLocalFile() return null,
@@ -60,4 +65,10 @@ void FileResourceRequest::doSend() { · 38 unmodified lines
60 65
61 _state = Finished;66 _state = Finished;
62 emit finished();67 emit finished();
68
69 if (_result == ResourceRequest::Success) {
70 statTracker->incrementStat(STAT_FILE_REQUEST_SUCCESS);
71 } else {
72 statTracker->incrementStat(STAT_FILE_REQUEST_FAILED);
73 }
63}74}
libraries/networking/src/FileTypeProfile.cpprenamed from interface/src/networking/FileTypeProfile.cpp+0−0

File renamed without changes.

libraries/networking/src/FileTypeProfile.hrenamed from interface/src/networking/FileTypeProfile.h+0−0

File renamed without changes.

libraries/networking/src/FileTypeRequestInterceptor.cpprenamed from interface/src/networking/FileTypeRequestInterceptor.cpp+0−0

File renamed without changes.

libraries/networking/src/FileTypeRequestInterceptor.hrenamed from interface/src/networking/FileTypeRequestInterceptor.h+0−0

File renamed without changes.

libraries/networking/src/HFTabletWebEngineProfile.cpprenamed from interface/src/networking/HFTabletWebEngineProfile.cpp+1−0
@@ -19,6 +19,7 @@ HFTabletWebEngineProfile::HFTabletWebEngineProfile(QObject* parent) : QQuickWebE · 18 unmodified lines
19 static const QString WEB_ENGINE_USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";19 static const QString WEB_ENGINE_USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
20 20
21 setHttpUserAgent(WEB_ENGINE_USER_AGENT);21 setHttpUserAgent(WEB_ENGINE_USER_AGENT);
22 setStorageName(QML_WEB_ENGINE_NAME);
22 23
23 auto requestInterceptor = new HFTabletWebEngineRequestInterceptor(this);24 auto requestInterceptor = new HFTabletWebEngineRequestInterceptor(this);
24 setRequestInterceptor(requestInterceptor);25 setRequestInterceptor(requestInterceptor);
libraries/networking/src/HFTabletWebEngineProfile.hrenamed from interface/src/networking/HFTabletWebEngineProfile.h+0−0

File renamed without changes.

libraries/networking/src/HFTabletWebEngineRequestInterceptor.cpprenamed from interface/src/networking/HFTabletWebEngineRequestInterceptor.cpp+1−1
@@ -11,7 +11,7 @@ · 10 unmodified lines
11 11
12#include "HFTabletWebEngineRequestInterceptor.h"12#include "HFTabletWebEngineRequestInterceptor.h"
13#include <QtCore/QDebug>13#include <QtCore/QDebug>
14#include <AccountManager.h>14#include "AccountManager.h"
15 15
16bool isTabletAuthableHighFidelityURL(const QUrl& url) {16bool isTabletAuthableHighFidelityURL(const QUrl& url) {
17 static const QStringList HF_HOSTS = {17 static const QStringList HF_HOSTS = {
libraries/networking/src/HFTabletWebEngineRequestInterceptor.hrenamed from interface/src/networking/HFTabletWebEngineRequestInterceptor.h+0−0

File renamed without changes.

libraries/networking/src/HFWebEngineProfile.cpprenamed from interface/src/networking/HFWebEngineProfile.cpp+1−0
@@ -20,6 +20,7 @@ HFWebEngineProfile::HFWebEngineProfile(QObject* parent) : · 19 unmodified lines
20{20{
21 static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";21 static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
22 setHttpUserAgent(WEB_ENGINE_USER_AGENT);22 setHttpUserAgent(WEB_ENGINE_USER_AGENT);
23 setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
23 24
24 // we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user25 // we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
25 auto requestInterceptor = new HFWebEngineRequestInterceptor(this);26 auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
libraries/networking/src/HFWebEngineProfile.hrenamed from interface/src/networking/HFWebEngineProfile.h+0−0

File renamed without changes.

libraries/networking/src/HFWebEngineRequestInterceptor.cpprenamed from interface/src/networking/HFWebEngineRequestInterceptor.cpp+1−1
@@ -13,7 +13,7 @@ · 12 unmodified lines
13 13
14#include <QtCore/QDebug>14#include <QtCore/QDebug>
15 15
16#include <AccountManager.h>16#include "AccountManager.h"
17#include "RequestFilters.h"17#include "RequestFilters.h"
18 18
19void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {19void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
libraries/networking/src/HFWebEngineRequestInterceptor.hrenamed from interface/src/networking/HFWebEngineRequestInterceptor.h+0−0

File renamed without changes.

libraries/networking/src/HTTPResourceRequest.cpp+16−0
@@ -17,6 +17,7 @@ · 16 unmodified lines
17#include <QMetaEnum>17#include <QMetaEnum>
18 18
19#include <SharedUtil.h>19#include <SharedUtil.h>
20#include <StatTracker.h>
20 21
21#include "NetworkAccessManager.h"22#include "NetworkAccessManager.h"
22#include "NetworkLogging.h"23#include "NetworkLogging.h"
@@ -49,6 +50,8 @@ void HTTPResourceRequest::cleanupTimer() { · 26 unmodified lines
49}50}
50 51
51void HTTPResourceRequest::doSend() {52void HTTPResourceRequest::doSend() {
53 DependencyManager::get<StatTracker>()->incrementStat(STAT_HTTP_REQUEST_STARTED);
54
52 QNetworkRequest networkRequest(_url);55 QNetworkRequest networkRequest(_url);
53 networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);56 networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
54 networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);57 networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
@@ -178,6 +181,17 @@ void HTTPResourceRequest::onRequestFinished() { · 123 unmodified lines
178 181
179 _state = Finished;182 _state = Finished;
180 emit finished();183 emit finished();
184
185 auto statTracker = DependencyManager::get<StatTracker>();
186 if (_result == Success) {
187 statTracker->incrementStat(STAT_HTTP_REQUEST_SUCCESS);
188
189 if (loadedFromCache()) {
190 statTracker->incrementStat(STAT_HTTP_REQUEST_CACHE);
191 }
192 } else {
193 statTracker->incrementStat(STAT_HTTP_REQUEST_FAILED);
194 }
181}195}
182 196
183void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {197void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
@@ -202,4 +216,6 @@ void HTTPResourceRequest::onTimeout() { · 18 unmodified lines
202 _result = Timeout;216 _result = Timeout;
203 _state = Finished;217 _state = Finished;
204 emit finished();218 emit finished();
219
220 DependencyManager::get<StatTracker>()->incrementStat(STAT_HTTP_REQUEST_FAILED);
205}221}
libraries/networking/src/LimitedNodeList.cpp+2−4
@@ -22,13 +22,12 @@ · 21 unmodified lines
22#include <QtCore/QUrl>22#include <QtCore/QUrl>
23#include <QtNetwork/QHostInfo>23#include <QtNetwork/QHostInfo>
24 24
25#include <tbb/parallel_for.h>
26
27#include <LogHandler.h>25#include <LogHandler.h>
28#include <shared/NetworkUtils.h>26#include <shared/NetworkUtils.h>
29#include <NumericalConstants.h>27#include <NumericalConstants.h>
30#include <SettingHandle.h>28#include <SettingHandle.h>
31#include <SharedUtil.h>29#include <SharedUtil.h>
30#include <StatTracker.h>
32#include <UUID.h>31#include <UUID.h>
33 32
34#include "AccountManager.h"33#include "AccountManager.h"
@@ -37,7 +36,6 @@ · 2 unmodified lines
37#include "HifiSockAddr.h"36#include "HifiSockAddr.h"
38#include "NetworkLogging.h"37#include "NetworkLogging.h"
39#include "udt/Packet.h"38#include "udt/Packet.h"
40#include <Trace.h>
41 39
42static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);40static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
43 41
@@ -1044,10 +1042,10 @@ void LimitedNodeList::setLocalSocket(const HifiSockAddr& sockAddr) { · 1000 unmodified lines
1044 qCInfo(networking) << "Local socket is" << sockAddr;1042 qCInfo(networking) << "Local socket is" << sockAddr;
1045 } else {1043 } else {
1046 qCInfo(networking) << "Local socket has changed from" << _localSockAddr << "to" << sockAddr;1044 qCInfo(networking) << "Local socket has changed from" << _localSockAddr << "to" << sockAddr;
1045 DependencyManager::get<StatTracker>()->incrementStat(LOCAL_SOCKET_CHANGE_STAT);
1047 }1046 }
1048 1047
1049 _localSockAddr = sockAddr;1048 _localSockAddr = sockAddr;
1050
1051 emit localSockAddrChanged(_localSockAddr);1049 emit localSockAddrChanged(_localSockAddr);
1052 }1050 }
1053}1051}
libraries/networking/src/LimitedNodeList.h+4−3
@@ -31,7 +31,7 @@ · 30 unmodified lines
31#include <QtNetwork/QUdpSocket>31#include <QtNetwork/QUdpSocket>
32#include <QtNetwork/QHostAddress>32#include <QtNetwork/QHostAddress>
33 33
34#include <tbb/concurrent_unordered_map.h>34#include <TBBHelpers.h>
35 35
36#include <DependencyManager.h>36#include <DependencyManager.h>
37#include <SharedUtil.h>37#include <SharedUtil.h>
@@ -66,9 +66,10 @@ const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::Lo · 28 unmodified lines
66 66
67const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";67const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
68 68
69using namespace tbb;69const QString LOCAL_SOCKET_CHANGE_STAT = "LocalSocketChanges";
70
70typedef std::pair<QUuid, SharedNodePointer> UUIDNodePair;71typedef std::pair<QUuid, SharedNodePointer> UUIDNodePair;
71typedef concurrent_unordered_map<QUuid, SharedNodePointer, UUIDHasher> NodeHash;72typedef tbb::concurrent_unordered_map<QUuid, SharedNodePointer, UUIDHasher> NodeHash;
72 73
73typedef quint8 PingType_t;74typedef quint8 PingType_t;
74namespace PingType {75namespace PingType {
libraries/networking/src/MessagesClient.cpp+6−6
@@ -16,6 +16,8 @@ · 15 unmodified lines
16#include <QtCore/QBuffer>16#include <QtCore/QBuffer>
17#include <QtCore/QThread>17#include <QtCore/QThread>
18 18
19#include <ThreadHelpers.h>
20
19#include "NetworkLogging.h"21#include "NetworkLogging.h"
20#include "NodeList.h"22#include "NodeList.h"
21#include "PacketReceiver.h"23#include "PacketReceiver.h"
@@ -30,12 +32,6 @@ MessagesClient::MessagesClient() { · 8 unmodified lines
30 connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);32 connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);
31}33}
32 34
33void MessagesClient::init() {
34 if (QThread::currentThread() != thread()) {
35 QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
36 }
37}
38
39void MessagesClient::decodeMessagesPacket(QSharedPointer<ReceivedMessage> receivedMessage, QString& channel, 35void MessagesClient::decodeMessagesPacket(QSharedPointer<ReceivedMessage> receivedMessage, QString& channel,
40 bool& isText, QString& message, QByteArray& data, QUuid& senderID) {36 bool& isText, QString& message, QByteArray& data, QUuid& senderID) {
41 quint16 channelLength;37 quint16 channelLength;
@@ -185,3 +181,7 @@ void MessagesClient::handleNodeActivated(SharedNodePointer node) { · 143 unmodified lines
185 }181 }
186 }182 }
187}183}
184
185void MessagesClient::startThread() {
186 moveToNewNamedThread(this, "Messages Client Thread");
187}
libraries/networking/src/MessagesClient.h+1−1
@@ -28,7 +28,7 @@ class MessagesClient : public QObject, public Dependency { · 27 unmodified lines
28public:28public:
29 MessagesClient();29 MessagesClient();
30 30
31 Q_INVOKABLE void init();31 void startThread();
32 32
33 Q_INVOKABLE void sendMessage(QString channel, QString message, bool localOnly = false);33 Q_INVOKABLE void sendMessage(QString channel, QString message, bool localOnly = false);
34 Q_INVOKABLE void sendLocalMessage(QString channel, QString message);34 Q_INVOKABLE void sendLocalMessage(QString channel, QString message);
libraries/networking/src/Node.h+1−1
@@ -24,7 +24,7 @@ · 23 unmodified lines
24#include <QReadLocker>24#include <QReadLocker>
25#include <UUIDHasher.h>25#include <UUIDHasher.h>
26 26
27#include <tbb/concurrent_unordered_set.h>27#include <TBBHelpers.h>
28 28
29#include "HifiSockAddr.h"29#include "HifiSockAddr.h"
30#include "NetworkPeer.h"30#include "NetworkPeer.h"
libraries/networking/src/NodeList.cpp+6−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
20#include <QtNetwork/QHostInfo>20#include <QtNetwork/QHostInfo>
21#include <QtNetwork/QNetworkInterface>21#include <QtNetwork/QNetworkInterface>
22 22
23#include <ThreadHelpers.h>
23#include <LogHandler.h>24#include <LogHandler.h>
24#include <UUID.h>25#include <UUID.h>
25 26
@@ -1115,3 +1116,8 @@ void NodeList::setRequestsDomainListData(bool isRequesting) { · 1089 unmodified lines
1115 });1116 });
1116 _requestsDomainListData = isRequesting;1117 _requestsDomainListData = isRequesting;
1117}1118}
1119
1120
1121void NodeList::startThread() {
1122 moveToNewNamedThread(this, "NodeList Thread", QThread::TimeCriticalPriority);
1123}
libraries/networking/src/NodeList.h+2−1
@@ -20,7 +20,7 @@ · 19 unmodified lines
20#include <unistd.h> // not on windows, not needed for mac or windows20#include <unistd.h> // not on windows, not needed for mac or windows
21#endif21#endif
22 22
23#include <tbb/concurrent_unordered_set.h>23#include <TBBHelpers.h>
24 24
25#include <QtCore/QElapsedTimer>25#include <QtCore/QElapsedTimer>
26#include <QtCore/QMutex>26#include <QtCore/QMutex>
@@ -52,6 +52,7 @@ class NodeList : public LimitedNodeList { · 25 unmodified lines
52 SINGLETON_DEPENDENCY52 SINGLETON_DEPENDENCY
53 53
54public:54public:
55 void startThread();
55 NodeType_t getOwnerType() const { return _ownerType.load(); }56 NodeType_t getOwnerType() const { return _ownerType.load(); }
56 void setOwnerType(NodeType_t ownerType) { _ownerType.store(ownerType); }57 void setOwnerType(NodeType_t ownerType) { _ownerType.store(ownerType); }
57 58
libraries/networking/src/RequestFilters.cpprenamed from interface/src/networking/RequestFilters.cpp+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
14 14
15#include <QtCore/QDebug>15#include <QtCore/QDebug>
16 16
17#include <AccountManager.h>17#include "AccountManager.h"
18 18
19namespace {19namespace {
20 20
libraries/networking/src/RequestFilters.hrenamed from interface/src/networking/RequestFilters.h+0−0

File renamed without changes.

libraries/networking/src/ResourceRequest.h+15−0
@@ -19,6 +19,21 @@ · 18 unmodified lines
19 19
20#include "ByteRange.h"20#include "ByteRange.h"
21 21
22const QString STAT_ATP_REQUEST_STARTED = "StartedATPRequest";
23const QString STAT_HTTP_REQUEST_STARTED = "StartedHTTPRequest";
24const QString STAT_FILE_REQUEST_STARTED = "StartedFileRequest";
25const QString STAT_ATP_REQUEST_SUCCESS = "SuccessfulATPRequest";
26const QString STAT_HTTP_REQUEST_SUCCESS = "SuccessfulHTTPRequest";
27const QString STAT_FILE_REQUEST_SUCCESS = "SuccessfulFileRequest";
28const QString STAT_ATP_REQUEST_FAILED = "FailedATPRequest";
29const QString STAT_HTTP_REQUEST_FAILED = "FailedHTTPRequest";
30const QString STAT_FILE_REQUEST_FAILED = "FailedFileRequest";
31const QString STAT_ATP_REQUEST_CACHE = "CacheATPRequest";
32const QString STAT_HTTP_REQUEST_CACHE = "CacheHTTPRequest";
33const QString STAT_ATP_MAPPING_REQUEST_STARTED = "StartedATPMappingRequest";
34const QString STAT_ATP_MAPPING_REQUEST_FAILED = "FailedATPMappingRequest";
35const QString STAT_ATP_MAPPING_REQUEST_SUCCESS = "SuccessfulATPMappingRequest";
36
22class ResourceRequest : public QObject {37class ResourceRequest : public QObject {
23 Q_OBJECT38 Q_OBJECT
24public:39public:
libraries/networking/src/SandboxUtils.cpp+3−4
@@ -52,9 +52,8 @@ bool readStatus(QByteArray statusData) { · 51 unmodified lines
52 return false;52 return false;
53}53}
54 54
55void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName, bool noUpdater) {55void runLocalSandbox(QString contentPath, bool autoShutdown, bool noUpdater) {
56 QString serverPath = "./server-console/server-console.exe";56 QString serverPath = "./server-console/server-console.exe";
57 qCDebug(networking) << "Running marker path is: " << runningMarkerName;
58 qCDebug(networking) << "Server path is: " << serverPath;57 qCDebug(networking) << "Server path is: " << serverPath;
59 qCDebug(networking) << "autoShutdown: " << autoShutdown;58 qCDebug(networking) << "autoShutdown: " << autoShutdown;
60 qCDebug(networking) << "noUpdater: " << noUpdater;59 qCDebug(networking) << "noUpdater: " << noUpdater;
@@ -74,8 +73,8 @@ void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMark · 13 unmodified lines
74 }73 }
75 74
76 if (autoShutdown) {75 if (autoShutdown) {
77 QString interfaceRunningStateFile = RunningMarker::getMarkerFilePath(runningMarkerName);76 auto pid = QCoreApplication::applicationPid();
78 args << "--shutdownWatcher" << interfaceRunningStateFile;77 args << "--shutdownWith" << QString::number(pid);
79 }78 }
80 79
81 if (noUpdater) {80 if (noUpdater) {
libraries/networking/src/SandboxUtils.h+1−1
@@ -21,7 +21,7 @@ namespace SandboxUtils { · 20 unmodified lines
21 21
22 QNetworkReply* getStatus();22 QNetworkReply* getStatus();
23 bool readStatus(QByteArray statusData);23 bool readStatus(QByteArray statusData);
24 void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName, bool noUpdater);24 void runLocalSandbox(QString contentPath, bool autoShutdown, bool noUpdater);
25};25};
26 26
27#endif // hifi_SandboxUtils_h27#endif // hifi_SandboxUtils_h
libraries/networking/src/UserActivityLogger.cpp+9−1
@@ -20,7 +20,9 @@ · 19 unmodified lines
20#include <DependencyManager.h>20#include <DependencyManager.h>
21#include "AddressManager.h"21#include "AddressManager.h"
22 22
23static const QString USER_ACTIVITY_URL = "/api/v1/user_activities";23UserActivityLogger::UserActivityLogger() {
24 _timer.start();
25}
24 26
25UserActivityLogger& UserActivityLogger::getInstance() {27UserActivityLogger& UserActivityLogger::getInstance() {
26 static UserActivityLogger sharedInstance;28 static UserActivityLogger sharedInstance;
@@ -44,6 +46,12 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall · 17 unmodified lines
44 actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\"");46 actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\"");
45 actionPart.setBody(QByteArray().append(action));47 actionPart.setBody(QByteArray().append(action));
46 multipart->append(actionPart);48 multipart->append(actionPart);
49
50 // Log the local-time that this event was logged
51 QHttpPart elapsedPart;
52 elapsedPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"elapsed_ms\"");
53 elapsedPart.setBody(QString::number(_timer.elapsed()).toLocal8Bit());
54 multipart->append(elapsedPart);
47 55
48 // If there are action details, add them to the multipart56 // If there are action details, add them to the multipart
49 if (!details.isEmpty()) {57 if (!details.isEmpty()) {
libraries/networking/src/UserActivityLogger.h+6−1
@@ -18,10 +18,13 @@ · 17 unmodified lines
18#include <QString>18#include <QString>
19#include <QJsonObject>19#include <QJsonObject>
20#include <QNetworkReply>20#include <QNetworkReply>
21#include <QElapsedTimer>
21 22
22#include <SettingHandle.h>23#include <SettingHandle.h>
23#include "AddressManager.h"24#include "AddressManager.h"
24 25
26const QString USER_ACTIVITY_URL = "/api/v1/user_activities";
27
25class UserActivityLogger : public QObject {28class UserActivityLogger : public QObject {
26 Q_OBJECT29 Q_OBJECT
27 30
@@ -49,8 +52,10 @@ private slots: · 21 unmodified lines
49 void requestError(QNetworkReply& errorReply);52 void requestError(QNetworkReply& errorReply);
50 53
51private:54private:
52 UserActivityLogger() {};55 UserActivityLogger();
53 Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", false };56 Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", false };
57
58 QElapsedTimer _timer;
54};59};
55 60
56#endif // hifi_UserActivityLogger_h61#endif // hifi_UserActivityLogger_h
libraries/networking/src/UserActivityLoggerScriptingInterface.cpp+9−1
@@ -56,7 +56,7 @@ void UserActivityLoggerScriptingInterface::palAction(QString action, QString tar · 55 unmodified lines
56}56}
57 57
58void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) {58void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) {
59 doLogAction("pal_opened", { 59 doLogAction("pal_opened", {
60 { "seconds_opened", secondsOpened }60 { "seconds_opened", secondsOpened }
61 });61 });
62}62}
@@ -71,6 +71,14 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b · 8 unmodified lines
71 doLogAction("makeUserConnection", payload);71 doLogAction("makeUserConnection", payload);
72}72}
73 73
74void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) {
75 doLogAction(newValue ? "bubbleOn" : "bubbleOff");
76}
77
78void UserActivityLoggerScriptingInterface::bubbleActivated() {
79 doLogAction("bubbleActivated");
80}
81
74void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) {82void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) {
75 doLogAction(action, QJsonObject::fromVariantMap(details));83 doLogAction(action, QJsonObject::fromVariantMap(details));
76}84}
libraries/networking/src/UserActivityLoggerScriptingInterface.h+2−0
@@ -30,6 +30,8 @@ public: · 29 unmodified lines
30 Q_INVOKABLE void palAction(QString action, QString target);30 Q_INVOKABLE void palAction(QString action, QString target);
31 Q_INVOKABLE void palOpened(float secondsOpen);31 Q_INVOKABLE void palOpened(float secondsOpen);
32 Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = "");32 Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = "");
33 Q_INVOKABLE void bubbleToggled(bool newValue);
34 Q_INVOKABLE void bubbleActivated();
33 Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});35 Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
34private:36private:
35 void doLogAction(QString action, QJsonObject details = {});37 void doLogAction(QString action, QJsonObject details = {});
libraries/networking/src/udt/PacketHeaders.cpp+1−1
@@ -56,7 +56,7 @@ PacketVersion versionForPacketType(PacketType packetType) { · 55 unmodified lines
56 case PacketType::AvatarData:56 case PacketType::AvatarData:
57 case PacketType::BulkAvatarData:57 case PacketType::BulkAvatarData:
58 case PacketType::KillAvatar:58 case PacketType::KillAvatar:
59 return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarIdentitySequenceId);59 return static_cast<PacketVersion>(AvatarMixerPacketVersion::MannequinDefaultAvatar);
60 case PacketType::MessagesData:60 case PacketType::MessagesData:
61 return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);61 return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
62 case PacketType::ICEServerHeartbeat:62 case PacketType::ICEServerHeartbeat:
libraries/networking/src/udt/PacketHeaders.h+2−1
@@ -236,7 +236,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { · 235 unmodified lines
236 AvatarAsChildFixes,236 AvatarAsChildFixes,
237 StickAndBallDefaultAvatar,237 StickAndBallDefaultAvatar,
238 IdentityPacketsIncludeUpdateTime,238 IdentityPacketsIncludeUpdateTime,
239 AvatarIdentitySequenceId239 AvatarIdentitySequenceId,
240 MannequinDefaultAvatar
240};241};
241 242
242enum class DomainConnectRequestVersion : PacketVersion {243enum class DomainConnectRequestVersion : PacketVersion {
libraries/networking/src/udt/SendQueue.cpp+3−0
@@ -241,6 +241,9 @@ void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) { · 240 unmodified lines
241 std::lock_guard<std::mutex> locker { _handshakeMutex };241 std::lock_guard<std::mutex> locker { _handshakeMutex };
242 _hasReceivedHandshakeACK = true;242 _hasReceivedHandshakeACK = true;
243 }243 }
244
245 _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
246
244 // Notify on the handshake ACK condition247 // Notify on the handshake ACK condition
245 _handshakeACKCondition.notify_one();248 _handshakeACKCondition.notify_one();
246 }249 }
libraries/physics/src/CharacterController.cpp+13−3
@@ -152,6 +152,7 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 151 unmodified lines
152 btDispatcher* dispatcher = collisionWorld->getDispatcher();152 btDispatcher* dispatcher = collisionWorld->getDispatcher();
153 int numManifolds = dispatcher->getNumManifolds();153 int numManifolds = dispatcher->getNumManifolds();
154 bool hasFloor = false;154 bool hasFloor = false;
155 bool isStuck = false;
155 156
156 btTransform rotation = _rigidBody->getWorldTransform();157 btTransform rotation = _rigidBody->getWorldTransform();
157 rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part158 rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part
@@ -169,10 +170,18 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 11 unmodified lines
169 btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame170 btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
170 btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character171 btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character
171 btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp);172 btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp);
173 // If there's non-trivial penetration with a big impulse for several steps, we're probably stuck.
174 // Note it here in the controller, and let MyAvatar figure out what to do about it.
175 const float STUCK_PENETRATION = -0.05f; // always negative into the object.
176 const float STUCK_IMPULSE = 500.0f;
177 const int STUCK_LIFETIME = 3;
178 if ((contact.getDistance() < STUCK_PENETRATION) && (contact.getAppliedImpulse() > STUCK_IMPULSE) && (contact.getLifeTime() > STUCK_LIFETIME)) {
179 isStuck = true; // latch on
180 }
172 if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) {181 if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) {
173 hasFloor = true;182 hasFloor = true;
174 if (!pushing) {183 if (!pushing && isStuck) {
175 // we're not pushing against anything so we can early exit184 // we're not pushing against anything and we're stuck so we can early exit
176 // (all we need to know is that there is a floor)185 // (all we need to know is that there is a floor)
177 break;186 break;
178 }187 }
@@ -198,12 +207,13 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 19 unmodified lines
198 _stepHeight = highestStep;207 _stepHeight = highestStep;
199 _stepPoint = rotation * pointOnCharacter; // rotate into world-frame208 _stepPoint = rotation * pointOnCharacter; // rotate into world-frame
200 }209 }
201 if (hasFloor && !(pushing && _stepUpEnabled)) {210 if (hasFloor && isStuck && !(pushing && _stepUpEnabled)) {
202 // early exit since all we need to know is that we're on a floor211 // early exit since all we need to know is that we're on a floor
203 break;212 break;
204 }213 }
205 }214 }
206 }215 }
216 _isStuck = isStuck;
207 return hasFloor;217 return hasFloor;
208}218}
209 219
libraries/physics/src/CharacterController.h+2−0
@@ -110,6 +110,7 @@ public: · 109 unmodified lines
110 void setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale);110 void setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale);
111 111
112 bool isEnabledAndReady() const { return _dynamicsWorld; }112 bool isEnabledAndReady() const { return _dynamicsWorld; }
113 bool isStuck() const { return _isStuck; }
113 114
114 void setCollisionless(bool collisionless);115 void setCollisionless(bool collisionless);
115 int16_t computeCollisionGroup() const;116 int16_t computeCollisionGroup() const;
@@ -192,6 +193,7 @@ protected: · 76 unmodified lines
192 193
193 State _state;194 State _state;
194 bool _isPushingUp;195 bool _isPushingUp;
196 bool _isStuck { false };
195 197
196 btDynamicsWorld* _dynamicsWorld { nullptr };198 btDynamicsWorld* _dynamicsWorld { nullptr };
197 btRigidBody* _rigidBody { nullptr };199 btRigidBody* _rigidBody { nullptr };
libraries/physics/src/EntityMotionState.cpp+22−15
@@ -65,8 +65,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer · 64 unmodified lines
65 _lastStep(0),65 _lastStep(0),
66 _loopsWithoutOwner(0),66 _loopsWithoutOwner(0),
67 _accelerationNearlyGravityCount(0),67 _accelerationNearlyGravityCount(0),
68 _numInactiveUpdates(1),68 _numInactiveUpdates(1)
69 _outgoingPriority(0)
70{69{
71 _type = MOTIONSTATE_TYPE_ENTITY;70 _type = MOTIONSTATE_TYPE_ENTITY;
72 assert(_entity);71 assert(_entity);
@@ -75,6 +74,8 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer · 2 unmodified lines
75 // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here74 // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here
76 // rather than pass the legit shape pointer to the ObjectMotionState ctor above.75 // rather than pass the legit shape pointer to the ObjectMotionState ctor above.
77 setShape(shape);76 setShape(shape);
77
78 _outgoingPriority = _entity->getPendingOwnershipPriority();
78}79}
79 80
80EntityMotionState::~EntityMotionState() {81EntityMotionState::~EntityMotionState() {
@@ -84,7 +85,7 @@ EntityMotionState::~EntityMotionState() { · 3 unmodified lines
84 85
85void EntityMotionState::updateServerPhysicsVariables() {86void EntityMotionState::updateServerPhysicsVariables() {
86 assert(entityTreeIsLocked());87 assert(entityTreeIsLocked());
87 if (_entity->getSimulatorID() == Physics::getSessionUUID()) {88 if (isLocallyOwned()) {
88 // don't slam these values if we are the simulation owner89 // don't slam these values if we are the simulation owner
89 return;90 return;
90 }91 }
@@ -114,6 +115,7 @@ void EntityMotionState::handleDeactivation() { · 23 unmodified lines
114 115
115// virtual116// virtual
116void EntityMotionState::handleEasyChanges(uint32_t& flags) {117void EntityMotionState::handleEasyChanges(uint32_t& flags) {
118 assert(_entity);
117 assert(entityTreeIsLocked());119 assert(entityTreeIsLocked());
118 updateServerPhysicsVariables();120 updateServerPhysicsVariables();
119 ObjectMotionState::handleEasyChanges(flags);121 ObjectMotionState::handleEasyChanges(flags);
@@ -135,23 +137,23 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { · 15 unmodified lines
135 _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;137 _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
136 }138 }
137 _loopsWithoutOwner = 0;139 _loopsWithoutOwner = 0;
138 } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) {140 _numInactiveUpdates = 0;
141 } else if (isLocallyOwned()) {
139 // we just inherited ownership, make sure our desired priority matches what we have142 // we just inherited ownership, make sure our desired priority matches what we have
140 upgradeOutgoingPriority(_entity->getSimulationPriority());143 upgradeOutgoingPriority(_entity->getSimulationPriority());
141 } else {144 } else {
142 _outgoingPriority = 0;145 _outgoingPriority = 0;
143 _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;146 _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
147 _numInactiveUpdates = 0;
144 }148 }
145 }149 }
146 if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) {150 if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) {
147 // The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bits really mean "we should bid for ownership because151 // The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following:
148 // a local script has been changing physics properties, or we should adjust our own ownership priority".152 // (1) we own it but may need to change the priority OR...
149 // The desired priority is determined by which bits were set.153 // (2) we don't own it but should bid (because a local script has been changing physics properties)
150 if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB) {154 uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority();
151 _outgoingPriority = SCRIPT_GRAB_SIMULATION_PRIORITY;155 _outgoingPriority = glm::max(_outgoingPriority, newPriority);
152 } else {156
153 _outgoingPriority = SCRIPT_POKE_SIMULATION_PRIORITY;
154 }
155 // reset bid expiry so that we bid ASAP157 // reset bid expiry so that we bid ASAP
156 _nextOwnershipBid = 0;158 _nextOwnershipBid = 0;
157 }159 }
@@ -170,6 +172,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { · 12 unmodified lines
170 172
171// virtual173// virtual
172bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {174bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
175 assert(_entity);
173 updateServerPhysicsVariables();176 updateServerPhysicsVariables();
174 return ObjectMotionState::handleHardAndEasyChanges(flags, engine);177 return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
175}178}
@@ -315,7 +318,7 @@ bool EntityMotionState::isCandidateForOwnership() const { · 139 unmodified lines
315 assert(_entity);318 assert(_entity);
316 assert(entityTreeIsLocked());319 assert(entityTreeIsLocked());
317 return _outgoingPriority != 0320 return _outgoingPriority != 0
318 || Physics::getSessionUUID() == _entity->getSimulatorID()321 || isLocallyOwned()
319 || _entity->dynamicDataNeedsTransmit();322 || _entity->dynamicDataNeedsTransmit();
320}323}
321 324
@@ -489,7 +492,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { · 167 unmodified lines
489 return true;492 return true;
490 }493 }
491 494
492 if (_entity->getSimulatorID() != Physics::getSessionUUID()) {495 if (!isLocallyOwned()) {
493 // we don't own the simulation496 // we don't own the simulation
494 497
495 // NOTE: we do not volunteer to own kinematic or static objects498 // NOTE: we do not volunteer to own kinematic or static objects
@@ -597,7 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ · 101 unmodified lines
597 properties.clearSimulationOwner();600 properties.clearSimulationOwner();
598 _outgoingPriority = 0;601 _outgoingPriority = 0;
599 _entity->setPendingOwnershipPriority(_outgoingPriority, now);602 _entity->setPendingOwnershipPriority(_outgoingPriority, now);
600 } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) {603 } else if (!isLocallyOwned()) {
601 // we don't own the simulation for this entity yet, but we're sending a bid for it604 // we don't own the simulation for this entity yet, but we're sending a bid for it
602 quint8 bidPriority = glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY);605 quint8 bidPriority = glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY);
603 properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);606 properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
@@ -786,6 +789,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma · 182 unmodified lines
786 _entity->computeCollisionGroupAndFinalMask(group, mask);789 _entity->computeCollisionGroupAndFinalMask(group, mask);
787}790}
788 791
792bool EntityMotionState::isLocallyOwned() const {
793 return _entity->getSimulatorID() == Physics::getSessionUUID();
794}
795
789bool EntityMotionState::shouldBeLocallyOwned() const {796bool EntityMotionState::shouldBeLocallyOwned() const {
790 return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) ||797 return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) ||
791 _entity->getSimulatorID() == Physics::getSessionUUID();798 _entity->getSimulatorID() == Physics::getSessionUUID();
libraries/physics/src/EntityMotionState.h+1−0
@@ -79,6 +79,7 @@ public: · 78 unmodified lines
79 79
80 virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;80 virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;
81 81
82 bool isLocallyOwned() const override;
82 bool shouldBeLocallyOwned() const override;83 bool shouldBeLocallyOwned() const override;
83 84
84 friend class PhysicalEntitySimulation;85 friend class PhysicalEntitySimulation;
libraries/physics/src/ObjectMotionState.cpp+2−0
@@ -202,6 +202,7 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) { · 201 unmodified lines
202}202}
203 203
204void ObjectMotionState::handleEasyChanges(uint32_t& flags) {204void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
205 assert(_body && _shape);
205 if (flags & Simulation::DIRTY_POSITION) {206 if (flags & Simulation::DIRTY_POSITION) {
206 btTransform worldTrans = _body->getWorldTransform();207 btTransform worldTrans = _body->getWorldTransform();
207 btVector3 newPosition = glmToBullet(getObjectPosition());208 btVector3 newPosition = glmToBullet(getObjectPosition());
@@ -282,6 +283,7 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { · 74 unmodified lines
282}283}
283 284
284bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {285bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
286 assert(_body && _shape);
285 if (flags & Simulation::DIRTY_SHAPE) {287 if (flags & Simulation::DIRTY_SHAPE) {
286 // make sure the new shape is valid288 // make sure the new shape is valid
287 if (!isReadyToComputeShape()) {289 if (!isReadyToComputeShape()) {
libraries/physics/src/ObjectMotionState.h+2−1
@@ -79,7 +79,7 @@ public: · 78 unmodified lines
79 static ShapeManager* getShapeManager();79 static ShapeManager* getShapeManager();
80 80
81 ObjectMotionState(const btCollisionShape* shape);81 ObjectMotionState(const btCollisionShape* shape);
82 ~ObjectMotionState();82 virtual ~ObjectMotionState();
83 83
84 virtual void handleEasyChanges(uint32_t& flags);84 virtual void handleEasyChanges(uint32_t& flags);
85 virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);85 virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
@@ -146,6 +146,7 @@ public: · 60 unmodified lines
146 void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; }146 void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; }
147 void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; }147 void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; }
148 148
149 virtual bool isLocallyOwned() const { return false; }
149 virtual bool shouldBeLocallyOwned() const { return false; }150 virtual bool shouldBeLocallyOwned() const { return false; }
150 151
151 friend class PhysicsEngine;152 friend class PhysicsEngine;
libraries/physics/src/PhysicalEntitySimulation.cpp+1−1
@@ -130,7 +130,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { · 129 unmodified lines
130 }130 }
131 131
132 // then remove the objects (aka MotionStates) from physics132 // then remove the objects (aka MotionStates) from physics
133 _physicsEngine->removeObjects(_physicalObjects);133 _physicsEngine->removeSetOfObjects(_physicalObjects);
134 134
135 // delete the MotionStates135 // delete the MotionStates
136 // TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete136 // TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete
libraries/physics/src/PhysicsEngine.cpp+25−20
@@ -129,6 +129,9 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { · 128 unmodified lines
129 }129 }
130 body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);130 body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
131 body->updateInertiaTensor();131 body->updateInertiaTensor();
132 if (motionState->isLocallyOwned()) {
133 _activeStaticBodies.insert(body);
134 }
132 break;135 break;
133 }136 }
134 }137 }
@@ -174,19 +177,9 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { · 39 unmodified lines
174 // frame (because the framerate is faster than our physics simulation rate). When this happens we must scan177 // frame (because the framerate is faster than our physics simulation rate). When this happens we must scan
175 // _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer.178 // _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer.
176 for (auto object : objects) {179 for (auto object : objects) {
177 btRigidBody* body = object->getRigidBody();180 std::set<btRigidBody*>::iterator itr = _activeStaticBodies.find(object->getRigidBody());
178 181 if (itr != _activeStaticBodies.end()) {
179 std::vector<btRigidBody*>::reverse_iterator itr = _activeStaticBodies.rbegin();182 _activeStaticBodies.erase(itr);
180 while (itr != _activeStaticBodies.rend()) {
181 if (body == *itr) {
182 if (*itr != *(_activeStaticBodies.rbegin())) {
183 // swap with rbegin
184 *itr = *(_activeStaticBodies.rbegin());
185 }
186 _activeStaticBodies.pop_back();
187 break;
188 }
189 ++itr;
190 }183 }
191 }184 }
192 }185 }
@@ -207,7 +200,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { · 14 unmodified lines
207}200}
208 201
209// Same as above, but takes a Set instead of a Vector. Should only be called during teardown.202// Same as above, but takes a Set instead of a Vector. Should only be called during teardown.
210void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {203void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) {
211 _contactMap.clear();204 _contactMap.clear();
212 for (auto object : objects) {205 for (auto object : objects) {
213 btRigidBody* body = object->getRigidBody();206 btRigidBody* body = object->getRigidBody();
@@ -245,14 +238,16 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob · 31 unmodified lines
245 object->clearIncomingDirtyFlags();238 object->clearIncomingDirtyFlags();
246 }239 }
247 if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {240 if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {
248 _activeStaticBodies.push_back(object->getRigidBody());241 _activeStaticBodies.insert(object->getRigidBody());
249 }242 }
250 }243 }
251 // active static bodies have changed (in an Easy way) and need their Aabbs updated244 // active static bodies have changed (in an Easy way) and need their Aabbs updated
252 // but we've configured Bullet to NOT update them automatically (for improved performance)245 // but we've configured Bullet to NOT update them automatically (for improved performance)
253 // so we must do it ourselves246 // so we must do it ourselves
254 for (size_t i = 0; i < _activeStaticBodies.size(); ++i) {247 std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
255 _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]);248 while (itr != _activeStaticBodies.end()) {
249 _dynamicsWorld->updateSingleAabb(*itr);
250 ++itr;
256 }251 }
257 return stillNeedChange;252 return stillNeedChange;
258}253}
@@ -496,13 +491,23 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { · 237 unmodified lines
496 491
497const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() {492const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() {
498 BT_PROFILE("copyOutgoingChanges");493 BT_PROFILE("copyOutgoingChanges");
494
495 _dynamicsWorld->synchronizeMotionStates();
496
499 // Bullet will not deactivate static objects (it doesn't expect them to be active)497 // Bullet will not deactivate static objects (it doesn't expect them to be active)
500 // so we must deactivate them ourselves498 // so we must deactivate them ourselves
501 for (size_t i = 0; i < _activeStaticBodies.size(); ++i) {499 std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
502 _activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING);500 while (itr != _activeStaticBodies.end()) {
501 btRigidBody* body = *itr;
502 body->forceActivationState(ISLAND_SLEEPING);
503 ObjectMotionState* motionState = static_cast<ObjectMotionState*>(body->getUserPointer());
504 if (motionState) {
505 _dynamicsWorld->addChangedMotionState(motionState);
506 }
507 ++itr;
503 }508 }
504 _activeStaticBodies.clear();509 _activeStaticBodies.clear();
505 _dynamicsWorld->synchronizeMotionStates();510
506 _hasOutgoingChanges = false;511 _hasOutgoingChanges = false;
507 return _dynamicsWorld->getChangedMotionStates();512 return _dynamicsWorld->getChangedMotionStates();
508}513}
libraries/physics/src/PhysicsEngine.h+3−2
@@ -13,6 +13,7 @@ · 12 unmodified lines
13#define hifi_PhysicsEngine_h13#define hifi_PhysicsEngine_h
14 14
15#include <stdint.h>15#include <stdint.h>
16#include <set>
16#include <vector>17#include <vector>
17 18
18#include <QUuid>19#include <QUuid>
@@ -53,7 +54,7 @@ public: · 34 unmodified lines
53 uint32_t getNumSubsteps();54 uint32_t getNumSubsteps();
54 55
55 void removeObjects(const VectorOfMotionStates& objects);56 void removeObjects(const VectorOfMotionStates& objects);
56 void removeObjects(const SetOfMotionStates& objects); // only called during teardown57 void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown
57 58
58 void addObjects(const VectorOfMotionStates& objects);59 void addObjects(const VectorOfMotionStates& objects);
59 VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);60 VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);
@@ -114,7 +115,7 @@ private: · 54 unmodified lines
114 CollisionEvents _collisionEvents;115 CollisionEvents _collisionEvents;
115 QHash<QUuid, EntityDynamicPointer> _objectDynamics;116 QHash<QUuid, EntityDynamicPointer> _objectDynamics;
116 QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;117 QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
117 std::vector<btRigidBody*> _activeStaticBodies;118 std::set<btRigidBody*> _activeStaticBodies;
118 119
119 glm::vec3 _originOffset;120 glm::vec3 _originOffset;
120 121
libraries/physics/src/ThreadSafeDynamicsWorld.h+2−0
@@ -51,6 +51,8 @@ public: · 50 unmodified lines
51 const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; }51 const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; }
52 const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; }52 const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; }
53 53
54 void addChangedMotionState(ObjectMotionState* motionState) { _changedMotionStates.push_back(motionState); }
55
54private:56private:
55 // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState()57 // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState()
56 void synchronizeMotionState(btRigidBody* body);58 void synchronizeMotionState(btRigidBody* body);
libraries/plugins/src/plugins/DisplayPlugin.h+1−0
@@ -200,6 +200,7 @@ public: · 199 unmodified lines
200 virtual float newFramePresentRate() const { return -1.0f; }200 virtual float newFramePresentRate() const { return -1.0f; }
201 // Rate at which rendered frames are being skipped201 // Rate at which rendered frames are being skipped
202 virtual float droppedFrameRate() const { return -1.0f; }202 virtual float droppedFrameRate() const { return -1.0f; }
203 virtual bool getSupportsAutoSwitch() { return false; }
203 204
204 // Hardware specific stats205 // Hardware specific stats
205 virtual QJsonObject getHardwareStats() const { return QJsonObject(); }206 virtual QJsonObject getHardwareStats() const { return QJsonObject(); }
libraries/plugins/src/plugins/InputPlugin.h+2−1
@@ -24,6 +24,7 @@ public: · 23 unmodified lines
24 // Some input plugins are comprised of multiple subdevices (SDL2, for instance).24 // Some input plugins are comprised of multiple subdevices (SDL2, for instance).
25 // If an input plugin is only a single device, it will only return it's primary name.25 // If an input plugin is only a single device, it will only return it's primary name.
26 virtual QStringList getSubdeviceNames() { return { getName() }; };26 virtual QStringList getSubdeviceNames() { return { getName() }; };
27 virtual bool isHandController() const = 0;27 virtual bool isHandController() const { return false; }
28 virtual bool isHeadController() const { return false; }
28};29};
29 30
libraries/plugins/src/plugins/Plugin.h+13−0
@@ -53,6 +53,18 @@ public: · 52 unmodified lines
53 virtual bool isActive() {53 virtual bool isActive() {
54 return _active;54 return _active;
55 }55 }
56 virtual bool startStandBySession() {
57 _sessionStatus = true;
58 return _sessionStatus;
59 }
60
61 virtual void endSession() {
62 _sessionStatus = false;
63 }
64
65 virtual bool isSessionActive() {
66 return _sessionStatus;
67 }
56 68
57 /**69 /**
58 * Called by the application during it's idle phase. If the plugin needs to do70 * Called by the application during it's idle phase. If the plugin needs to do
@@ -73,6 +85,7 @@ signals: · 14 unmodified lines
73 85
74protected:86protected:
75 bool _active { false };87 bool _active { false };
88 bool _sessionStatus { false };
76 PluginContainer* _container { nullptr };89 PluginContainer* _container { nullptr };
77 static const char* UNKNOWN_PLUGIN_ID;90 static const char* UNKNOWN_PLUGIN_ID;
78 91
libraries/plugins/src/plugins/PluginUtils.cpp+9−0
@@ -24,6 +24,15 @@ bool PluginUtils::isHMDAvailable(const QString& pluginName) { · 23 unmodified lines
24 return false;24 return false;
25}25}
26 26
27bool PluginUtils::isHeadControllerAvailable(const QString& pluginName) {
28 for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
29 if (inputPlugin->isHeadController() && (pluginName.isEmpty() || inputPlugin->getName() == pluginName)) {
30 return true;
31 }
32 }
33 return false;
34};
35
27bool PluginUtils::isHandControllerAvailable(const QString& pluginName) {36bool PluginUtils::isHandControllerAvailable(const QString& pluginName) {
28 for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) {37 for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
29 if (inputPlugin->isHandController() && (pluginName.isEmpty() || inputPlugin->getName() == pluginName)) {38 if (inputPlugin->isHandController() && (pluginName.isEmpty() || inputPlugin->getName() == pluginName)) {
libraries/plugins/src/plugins/PluginUtils.h+1−0
@@ -16,6 +16,7 @@ class PluginUtils { · 15 unmodified lines
16public:16public:
17 static bool isHMDAvailable(const QString& pluginName = "");17 static bool isHMDAvailable(const QString& pluginName = "");
18 static bool isHandControllerAvailable(const QString& pluginName = "");18 static bool isHandControllerAvailable(const QString& pluginName = "");
19 static bool isHeadControllerAvailable(const QString& pluginName = "");
19 static bool isSubdeviceContainingNameAvailable(QString name);20 static bool isSubdeviceContainingNameAvailable(QString name);
20 static bool isViveControllerAvailable();21 static bool isViveControllerAvailable();
21 static bool isOculusTouchControllerAvailable();22 static bool isOculusTouchControllerAvailable();
libraries/procedural/src/procedural/ProceduralSkybox.cpp+1−1
@@ -23,7 +23,7 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() { · 22 unmodified lines
23 _procedural._fragmentSource = skybox_frag;23 _procedural._fragmentSource = skybox_frag;
24 // Adjust the pipeline state for background using the stencil test24 // Adjust the pipeline state for background using the stencil test
25 _procedural.setDoesFade(false);25 _procedural.setDoesFade(false);
26 _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));26 _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(1, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
27}27}
28 28
29bool ProceduralSkybox::empty() {29bool ProceduralSkybox::empty() {
libraries/render-utils/CMakeLists.txt+1−1
@@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) · 2 unmodified lines
3# pull in the resources.qrc file3# pull in the resources.qrc file
4qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")4qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
5setup_hifi_library(Widgets OpenGL Network Qml Quick Script)5setup_hifi_library(Widgets OpenGL Network Qml Quick Script)
6link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image)6link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image procedural)
7 7
8if (NOT ANDROID)8if (NOT ANDROID)
9 target_nsight()9 target_nsight()
libraries/render-utils/src/AnimDebugDraw.cpp+1−1
@@ -260,7 +260,7 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPo · 259 unmodified lines
260 // there is room, so lets draw a nice bone260 // there is room, so lets draw a nice bone
261 261
262 glm::vec3 uAxis, vAxis, wAxis;262 glm::vec3 uAxis, vAxis, wAxis;
263 generateBasisVectors(boneAxis0, glm::vec3(1, 0, 0), uAxis, vAxis, wAxis);263 generateBasisVectors(boneAxis0, glm::vec3(1.0f, 0.0f, 0.0f), uAxis, vAxis, wAxis);
264 264
265 glm::vec3 boneBaseCorners[NUM_BASE_CORNERS];265 glm::vec3 boneBaseCorners[NUM_BASE_CORNERS];
266 boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius));266 boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius));
libraries/render-utils/src/AntialiasingEffect.cpp+4−4
@@ -17,6 +17,7 @@ · 16 unmodified lines
17#include <gpu/Context.h>17#include <gpu/Context.h>
18 18
19#include "AntialiasingEffect.h"19#include "AntialiasingEffect.h"
20#include "StencilMaskPass.h"
20#include "TextureCache.h"21#include "TextureCache.h"
21#include "FramebufferCache.h"22#include "FramebufferCache.h"
22#include "DependencyManager.h"23#include "DependencyManager.h"
@@ -70,6 +71,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { · 47 unmodified lines
70 71
71 gpu::StatePointer state = gpu::StatePointer(new gpu::State());72 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
72 73
74 PrepareStencil::testMask(*state);
75
73 state->setDepthTest(false, false, gpu::LESS_EQUAL);76 state->setDepthTest(false, false, gpu::LESS_EQUAL);
74 77
75 // Good to go add the brand new pipeline78 // Good to go add the brand new pipeline
@@ -93,6 +96,7 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { · 17 unmodified lines
93 gpu::StatePointer state = gpu::StatePointer(new gpu::State());96 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
94 97
95 state->setDepthTest(false, false, gpu::LESS_EQUAL);98 state->setDepthTest(false, false, gpu::LESS_EQUAL);
99 PrepareStencil::testMask(*state);
96 100
97 // Good to go add the brand new pipeline101 // Good to go add the brand new pipeline
98 _blendPipeline = gpu::Pipeline::create(program, state);102 _blendPipeline = gpu::Pipeline::create(program, state);
@@ -106,10 +110,6 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const · 7 unmodified lines
106 110
107 RenderArgs* args = renderContext->args;111 RenderArgs* args = renderContext->args;
108 112
109 if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
110 return;
111 }
112
113 gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {113 gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
114 batch.enableStereo(false);114 batch.enableStereo(false);
115 batch.setViewportTransform(args->_viewport);115 batch.setViewportTransform(args->_viewport);
libraries/render-utils/src/BackgroundStage.cpp+140−0
@@ -0,0 +1,140 @@
1//
2// BackgroundStage.cpp
3//
4// Created by Sam Gateau on 5/9/2017.
5// Copyright 2015 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9//
10#include "BackgroundStage.h"
11
12#include "DeferredLightingEffect.h"
13
14#include <gpu/Context.h>
15
16BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
17 auto found = _backgroundMap.find(background);
18 if (found != _backgroundMap.end()) {
19 return INVALID_INDEX;
20 } else {
21 return (*found).second;
22 }
23
24}
25
26BackgroundStage::Index BackgroundStage::addBackground(const BackgroundPointer& background) {
27
28 auto found = _backgroundMap.find(background);
29 if (found == _backgroundMap.end()) {
30 auto backgroundId = _backgrounds.newElement(background);
31 // Avoid failing to allocate a background, just pass
32 if (backgroundId != INVALID_INDEX) {
33
34 // Insert the background and its index in the reverse map
35 _backgroundMap.insert(BackgroundMap::value_type(background, backgroundId));
36 }
37 return backgroundId;
38 } else {
39 return (*found).second;
40 }
41}
42
43
44BackgroundStage::BackgroundPointer BackgroundStage::removeBackground(Index index) {
45 BackgroundPointer removed = _backgrounds.freeElement(index);
46
47 if (removed) {
48 _backgroundMap.erase(removed);
49 }
50 return removed;
51}
52
53
54void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
55
56 const auto& lightingModel = inputs;
57 if (!lightingModel->isBackgroundEnabled()) {
58 return;
59 }
60
61
62 // Background rendering decision
63 auto backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
64 model::SunSkyStagePointer background;
65 model::SkyboxPointer skybox;
66 if (backgroundStage->_currentFrame._backgrounds.size()) {
67 auto backgroundId = backgroundStage->_currentFrame._backgrounds.front();
68 auto background = backgroundStage->getBackground(backgroundId);
69 if (background) {
70 skybox = background->getSkybox();
71 }
72 } else {
73 skybox = DependencyManager::get<DeferredLightingEffect>()->getDefaultSkybox();
74 }
75
76 /* auto backgroundMode = skyStage->getBackgroundMode();
77
78 switch (backgroundMode) {
79 case model::SunSkyStage::SKY_DEFAULT: {
80 auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
81 auto sceneKeyLight = scene->getKeyLight();
82
83 scene->setSunModelEnable(false);
84 sceneKeyLight->setColor(ColorUtils::toVec3(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR));
85 sceneKeyLight->setIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY);
86 sceneKeyLight->setAmbientIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY);
87 sceneKeyLight->setDirection(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION);
88 // fall through: render a skybox (if available), or the defaults (if requested)
89 }
90
91 case model::SunSkyStage::SKY_BOX: {*/
92 if (skybox && !skybox->empty()) {
93 PerformanceTimer perfTimer("skybox");
94 auto args = renderContext->args;
95
96 gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
97 args->_batch = &batch;
98
99 batch.enableSkybox(true);
100
101 batch.setViewportTransform(args->_viewport);
102 batch.setStateScissorRect(args->_viewport);
103
104 glm::mat4 projMat;
105 Transform viewMat;
106 args->getViewFrustum().evalProjectionMatrix(projMat);
107 args->getViewFrustum().evalViewTransform(viewMat);
108
109 batch.setProjectionTransform(projMat);
110 batch.setViewTransform(viewMat);
111
112 skybox->render(batch, args->getViewFrustum());
113 });
114 args->_batch = nullptr;
115
116 // break;
117 }
118 // fall through: render defaults (if requested)
119// }
120/*
121 case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: {
122 if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
123 auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
124 auto sceneKeyLight = scene->getKeyLight();
125 auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
126 if (defaultSkyboxAmbientTexture) {
127 sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
128 sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
129 } else {
130 static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(
131 "Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step");
132 }
133 // fall through: render defaults skybox
134 } else {
135 break;
136 }
137 }
138 */
139
140}
libraries/render-utils/src/BackgroundStage.h+80−0
@@ -0,0 +1,80 @@
1//
2// BackgroundStage.h
3
4// Created by Sam Gateau on 5/9/2017.
5// Copyright 2015 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9//
10
11#ifndef hifi_render_utils_BackgroundStage_h
12#define hifi_render_utils_BackgroundStage_h
13
14#include <model/Stage.h>
15#include <set>
16#include <unordered_map>
17#include <render/IndexedContainer.h>
18
19#include "LightingModel.h"
20
21
22// Background stage to set up background-related rendering tasks
23class BackgroundStage {
24public:
25 using Index = render::indexed_container::Index;
26 static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
27 static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
28
29 using BackgroundPointer = model::SunSkyStagePointer;
30 using Backgrounds = render::indexed_container::IndexedPointerVector<model::SunSkyStage>;
31 using BackgroundMap = std::unordered_map<BackgroundPointer, Index>;
32
33 using BackgroundIndices = std::vector<Index>;
34
35
36 Index findBackground(const BackgroundPointer& background) const;
37 Index addBackground(const BackgroundPointer& background);
38
39 BackgroundPointer removeBackground(Index index);
40
41 bool checkBackgroundId(Index index) const { return _backgrounds.checkIndex(index); }
42
43 Index getNumBackgrounds() const { return _backgrounds.getNumElements(); }
44 Index getNumFreeBackgrounds() const { return _backgrounds.getNumFreeIndices(); }
45 Index getNumAllocatedBackgrounds() const { return _backgrounds.getNumAllocatedIndices(); }
46
47 BackgroundPointer getBackground(Index backgroundId) const {
48 return _backgrounds.get(backgroundId);
49 }
50
51 Backgrounds _backgrounds;
52 BackgroundMap _backgroundMap;
53
54 class Frame {
55 public:
56 Frame() {}
57
58 void clear() { _backgrounds.clear(); }
59
60 void pushBackground(BackgroundStage::Index index) { _backgrounds.emplace_back(index); }
61
62 BackgroundStage::BackgroundIndices _backgrounds;
63 };
64
65 Frame _currentFrame;
66};
67using BackgroundStagePointer = std::shared_ptr<BackgroundStage>;
68
69
70class DrawBackgroundStage {
71public:
72 using Inputs = LightingModelPointer;
73 using JobModel = render::Job::ModelI<DrawBackgroundStage, Inputs>;
74
75 void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
76
77protected:
78};
79
80#endif
libraries/render-utils/src/DeferredLightingEffect.cpp+51−126
@@ -18,6 +18,7 @@ · 17 unmodified lines
18#include <gpu/Batch.h>18#include <gpu/Batch.h>
19#include <gpu/Context.h>19#include <gpu/Context.h>
20 20
21#include "StencilMaskPass.h"
21#include "AbstractViewStateInterface.h"22#include "AbstractViewStateInterface.h"
22#include "GeometryCache.h"23#include "GeometryCache.h"
23#include "TextureCache.h"24#include "TextureCache.h"
@@ -27,18 +28,15 @@ · 3 unmodified lines
27#include "deferred_light_point_vert.h"28#include "deferred_light_point_vert.h"
28#include "deferred_light_spot_vert.h"29#include "deferred_light_spot_vert.h"
29 30
30#include "directional_light_frag.h"
31#include "directional_ambient_light_frag.h"31#include "directional_ambient_light_frag.h"
32#include "directional_skybox_light_frag.h"32#include "directional_skybox_light_frag.h"
33 33
34#include "directional_light_shadow_frag.h"
35#include "directional_ambient_light_shadow_frag.h"34#include "directional_ambient_light_shadow_frag.h"
36#include "directional_skybox_light_shadow_frag.h"35#include "directional_skybox_light_shadow_frag.h"
37 36
38#include "local_lights_shading_frag.h"37#include "local_lights_shading_frag.h"
39#include "local_lights_drawOutline_frag.h"38#include "local_lights_drawOutline_frag.h"
40#include "point_light_frag.h"39
41#include "spot_light_frag.h"
42 40
43using namespace render;41using namespace render;
44 42
@@ -82,48 +80,26 @@ enum DeferredShader_BufferSlot { · 37 unmodified lines
82};80};
83 81
84static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);82static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);
85static void loadLightVolumeProgram(const char* vertSource, const char* fragSource, bool front, gpu::PipelinePointer& program, LightLocationsPtr& locations);
86
87const char no_light_frag[] =
88R"SCRIBE(
89out vec4 _fragColor;
90
91void main(void) {
92 _fragColor = vec4(1.0, 1.0, 1.0, 1.0);
93}
94)SCRIBE"
95;
96 83
97void DeferredLightingEffect::init() {84void DeferredLightingEffect::init() {
98 _directionalLightLocations = std::make_shared<LightLocations>();
99 _directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();85 _directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();
100 _directionalSkyboxLightLocations = std::make_shared<LightLocations>();86 _directionalSkyboxLightLocations = std::make_shared<LightLocations>();
101 87
102 _directionalLightShadowLocations = std::make_shared<LightLocations>();
103 _directionalAmbientSphereLightShadowLocations = std::make_shared<LightLocations>();88 _directionalAmbientSphereLightShadowLocations = std::make_shared<LightLocations>();
104 _directionalSkyboxLightShadowLocations = std::make_shared<LightLocations>();89 _directionalSkyboxLightShadowLocations = std::make_shared<LightLocations>();
105 90
106 _localLightLocations = std::make_shared<LightLocations>();91 _localLightLocations = std::make_shared<LightLocations>();
107 _localLightOutlineLocations = std::make_shared<LightLocations>();92 _localLightOutlineLocations = std::make_shared<LightLocations>();
108 _pointLightLocations = std::make_shared<LightLocations>();
109 _spotLightLocations = std::make_shared<LightLocations>();
110 93
111 loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);
112 loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);94 loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
113 loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);95 loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
114 96
115 loadLightProgram(deferred_light_vert, directional_light_shadow_frag, false, _directionalLightShadow, _directionalLightShadowLocations);
116 loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations);97 loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations);
117 loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations);98 loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations);
118 99
119 loadLightProgram(deferred_light_vert, local_lights_shading_frag, true, _localLight, _localLightLocations);100 loadLightProgram(deferred_light_vert, local_lights_shading_frag, true, _localLight, _localLightLocations);
120 loadLightProgram(deferred_light_vert, local_lights_drawOutline_frag, true, _localLightOutline, _localLightOutlineLocations);101 loadLightProgram(deferred_light_vert, local_lights_drawOutline_frag, true, _localLightOutline, _localLightOutlineLocations);
121 102
122 loadLightVolumeProgram(deferred_light_point_vert, no_light_frag, false, _pointLightBack, _pointLightLocations);
123 loadLightVolumeProgram(deferred_light_point_vert, no_light_frag, true, _pointLightFront, _pointLightLocations);
124 loadLightVolumeProgram(deferred_light_spot_vert, no_light_frag, false, _spotLightBack, _spotLightLocations);
125 loadLightVolumeProgram(deferred_light_spot_vert, no_light_frag, true, _spotLightFront, _spotLightLocations);
126
127 // Light Stage and clusters103 // Light Stage and clusters
128 _lightStage = std::make_shared<LightStage>();104 _lightStage = std::make_shared<LightStage>();
129 105
@@ -141,79 +117,69 @@ void DeferredLightingEffect::init() { · 11 unmodified lines
141 _globalLights.push_back(_lightStage->addLight(lp));117 _globalLights.push_back(_lightStage->addLight(lp));
142 _lightStage->addShadow(_globalLights[0]);118 _lightStage->addShadow(_globalLights[0]);
143 119
144}
145 120
146void DeferredLightingEffect::addLight(const model::LightPointer& light) {121 _backgroundStage = std::make_shared<BackgroundStage>();
147 assert(light);
148 auto lightID = _lightStage->addLight(light);
149 if (light->getType() == model::Light::POINT) {
150 _pointLights.push_back(lightID);
151 } else {
152 _spotLights.push_back(lightID);
153 }
154}
155 122
123 auto textureCache = DependencyManager::get<TextureCache>();
156 124
157void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color,125 {
158 float intensity, float falloffRadius) {126 PROFILE_RANGE(render, "Process Default Skybox");
159 addSpotLight(position, radius, color, intensity, falloffRadius);127 auto textureCache = DependencyManager::get<TextureCache>();
160}
161 128
162void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color,129 auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx";
163 float intensity, float falloffRadius, const glm::quat& orientation, float exponent, float cutoff) {130
164 131 _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl);
165 unsigned int lightID = (unsigned int)(_pointLights.size() + _spotLights.size() + _globalLights.size());132 _defaultSkyboxAmbientTexture = _defaultSkyboxTexture;
166 if (lightID >= _allocatedLights.size()) {133
167 _allocatedLights.push_back(std::make_shared<model::Light>());134 _defaultSkybox->setCubemap(_defaultSkyboxTexture);
168 }135 }
169 model::LightPointer lp = _allocatedLights[lightID];
170 136
171 lp->setPosition(position);
172 lp->setMaximumRadius(radius);
173 lp->setColor(color);
174 lp->setIntensity(intensity);
175 lp->setFalloffRadius(falloffRadius);
176 137
177 if (exponent == 0.0f && cutoff == PI) {138 lp->setAmbientIntensity(0.5f);
178 lp->setType(model::Light::POINT);139 lp->setAmbientMap(_defaultSkyboxAmbientTexture);
179 _pointLights.push_back(lightID);140 auto irradianceSH = _defaultSkyboxAmbientTexture->getIrradiance();
180 141 if (irradianceSH) {
181 } else {142 lp->setAmbientSphere((*irradianceSH));
182 lp->setOrientation(orientation);
183 lp->setSpotAngle(cutoff);
184 lp->setSpotExponent(exponent);
185 lp->setType(model::Light::SPOT);
186 _spotLights.push_back(lightID);
187 }143 }
188}144}
189 145
190void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {146void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
191 PerformanceTimer perfTimer("DLE->setupBatch()");147 PerformanceTimer perfTimer("DLE->setupBatch()");
192 auto keyLight = _allocatedLights[_globalLights.front()];148 model::LightPointer keySunLight;
149 if (_lightStage && _lightStage->_currentFrame._sunLights.size()) {
150 keySunLight = _lightStage->getLight(_lightStage->_currentFrame._sunLights.front());
151 } else {
152 keySunLight = _allocatedLights[_globalLights.front()];
153 }
154
155 model::LightPointer keyAmbiLight;
156 if (_lightStage && _lightStage->_currentFrame._ambientLights.size()) {
157 keyAmbiLight = _lightStage->getLight(_lightStage->_currentFrame._ambientLights.front());
158 } else {
159 keyAmbiLight = _allocatedLights[_globalLights.front()];
160 }
193 161
194 if (lightBufferUnit >= 0) {162 if (lightBufferUnit >= 0) {
195 batch.setUniformBuffer(lightBufferUnit, keyLight->getLightSchemaBuffer());163 batch.setUniformBuffer(lightBufferUnit, keySunLight->getLightSchemaBuffer());
196 }164 }
197 if (keyLight->hasAmbient() && (ambientBufferUnit >= 0)) {165 if (ambientBufferUnit >= 0) {
198 batch.setUniformBuffer(ambientBufferUnit, keyLight->getAmbientSchemaBuffer());166 batch.setUniformBuffer(ambientBufferUnit, keyAmbiLight->getAmbientSchemaBuffer());
199 }167 }
200 168
201 if (keyLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {169 if (keyAmbiLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {
202 batch.setResourceTexture(skyboxCubemapUnit, keyLight->getAmbientMap());170 batch.setResourceTexture(skyboxCubemapUnit, keyAmbiLight->getAmbientMap());
203 }171 }
204}172}
205 173
206void DeferredLightingEffect::unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {174void DeferredLightingEffect::unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
207 auto keyLight = _allocatedLights[_globalLights.front()];
208
209 if (lightBufferUnit >= 0) {175 if (lightBufferUnit >= 0) {
210 batch.setUniformBuffer(lightBufferUnit, nullptr);176 batch.setUniformBuffer(lightBufferUnit, nullptr);
211 }177 }
212 if (keyLight->hasAmbient() && (ambientBufferUnit >= 0)) {178 if ((ambientBufferUnit >= 0)) {
213 batch.setUniformBuffer(ambientBufferUnit, nullptr);179 batch.setUniformBuffer(ambientBufferUnit, nullptr);
214 }180 }
215 181
216 if (keyLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {182 if ((skyboxCubemapUnit >= 0)) {
217 batch.setResourceTexture(skyboxCubemapUnit, nullptr);183 batch.setResourceTexture(skyboxCubemapUnit, nullptr);
218 }184 }
219}185}
@@ -277,7 +243,7 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo · 57 unmodified lines
277 state->setColorWriteMask(true, true, true, false);243 state->setColorWriteMask(true, true, true, false);
278 244
279 if (lightVolume) {245 if (lightVolume) {
280 state->setStencilTest(true, 0x00, gpu::State::StencilTest(1, 0xFF, gpu::LESS_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));246 PrepareStencil::testShape(*state);
281 247
282 state->setCullMode(gpu::State::CULL_BACK);248 state->setCullMode(gpu::State::CULL_BACK);
283 // state->setCullMode(gpu::State::CULL_FRONT);249 // state->setCullMode(gpu::State::CULL_FRONT);
@@ -290,7 +256,7 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo · 6 unmodified lines
290 256
291 } else {257 } else {
292 // Stencil test all the light passes for objects pixels only, not the background258 // Stencil test all the light passes for objects pixels only, not the background
293 state->setStencilTest(true, 0x00, gpu::State::StencilTest(0, 0x01, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));259 PrepareStencil::testShape(*state);
294 260
295 state->setCullMode(gpu::State::CULL_BACK);261 state->setCullMode(gpu::State::CULL_BACK);
296 // additive blending262 // additive blending
@@ -300,49 +266,21 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo · 3 unmodified lines
300 266
301}267}
302 268
303
304static void loadLightVolumeProgram(const char* vertSource, const char* fragSource, bool front, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) {
305 gpu::ShaderPointer program = makeLightProgram(vertSource, fragSource, locations);
306
307 auto state = std::make_shared<gpu::State>();
308
309 // Stencil test all the light passes for objects pixels only, not the background
310
311 if (front) {
312 state->setCullMode(gpu::State::CULL_BACK);
313 state->setDepthTest(true, false, gpu::LESS_EQUAL);
314 state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_DECR, gpu::State::STENCIL_OP_KEEP));
315
316 // state->setDepthClampEnable(true);
317 // TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases
318 // additive blending
319 // state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
320
321 //state->setColorWriteMask(true, true, true, false);
322 state->setColorWriteMask(false, false, false, false);
323 } else {
324 state->setCullMode(gpu::State::CULL_FRONT);
325 state->setDepthTest(true, false, gpu::LESS_EQUAL);
326 state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_INCR, gpu::State::STENCIL_OP_KEEP));
327 // additive blending
328 // state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
329 // state->setColorWriteMask(true, true, true, false);
330 state->setColorWriteMask(false, false, false, false);
331 }
332 pipeline = gpu::Pipeline::create(program, state);
333
334}
335
336void DeferredLightingEffect::setGlobalLight(const model::LightPointer& light) {269void DeferredLightingEffect::setGlobalLight(const model::LightPointer& light) {
337 auto globalLight = _allocatedLights.front();270 /* auto globalLight = _allocatedLights.front();
338 globalLight->setDirection(light->getDirection());271 globalLight->setDirection(light->getDirection());
339 globalLight->setColor(light->getColor());272 globalLight->setColor(light->getColor());
340 globalLight->setIntensity(light->getIntensity());273 globalLight->setIntensity(light->getIntensity());
341 globalLight->setAmbientIntensity(light->getAmbientIntensity());274 globalLight->setAmbientIntensity(light->getAmbientIntensity());
342 globalLight->setAmbientSphere(light->getAmbientSphere());275 globalLight->setAmbientSphere(light->getAmbientSphere());
343 globalLight->setAmbientMap(light->getAmbientMap());276 globalLight->setAmbientMap(light->getAmbientMap());*/
277}
278
279const model::LightPointer& DeferredLightingEffect::getGlobalLight() const {
280 return _allocatedLights.front();
344}281}
345 282
283
346#include <shared/Shapes.h>284#include <shared/Shapes.h>
347 285
348model::MeshPointer DeferredLightingEffect::getPointLightMesh() {286model::MeshPointer DeferredLightingEffect::getPointLightMesh() {
@@ -540,7 +478,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input · 191 unmodified lines
540 gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_COLOR1 | gpu::Framebuffer::BUFFER_COLOR2 | gpu::Framebuffer::BUFFER_COLOR3 |478 gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_COLOR1 | gpu::Framebuffer::BUFFER_COLOR2 | gpu::Framebuffer::BUFFER_COLOR3 |
541 gpu::Framebuffer::BUFFER_DEPTH |479 gpu::Framebuffer::BUFFER_DEPTH |
542 gpu::Framebuffer::BUFFER_STENCIL,480 gpu::Framebuffer::BUFFER_STENCIL,
543 vec4(vec3(0), 0), 1.0, 0.0, true);481 vec4(vec3(0), 0), 1.0, 1, true);
544 482
545 // For the rest of the rendering, bind the lighting model483 // For the rest of the rendering, bind the lighting model
546 batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, lightingModel->getParametersBuffer());484 batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, lightingModel->getParametersBuffer());
@@ -566,10 +504,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, · 19 unmodified lines
566 {504 {
567 // Framebuffer copy operations cannot function as multipass stereo operations.505 // Framebuffer copy operations cannot function as multipass stereo operations.
568 batch.enableStereo(false);506 batch.enableStereo(false);
569 507
570 // perform deferred lighting, rendering to free fbo
571 auto framebufferCache = DependencyManager::get<FramebufferCache>();
572
573 auto textureCache = DependencyManager::get<TextureCache>();508 auto textureCache = DependencyManager::get<TextureCache>();
574 auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();509 auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
575 510
@@ -624,8 +559,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, · 48 unmodified lines
624 batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map);559 batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map);
625 }560 }
626 561
627 auto& program = deferredLightingEffect->_shadowMapEnabled ? deferredLightingEffect->_directionalLightShadow : deferredLightingEffect->_directionalLight;562 auto& program = deferredLightingEffect->_directionalSkyboxLight;
628 LightLocationsPtr locations = deferredLightingEffect->_shadowMapEnabled ? deferredLightingEffect->_directionalLightShadowLocations : deferredLightingEffect->_directionalLightLocations;563 LightLocationsPtr locations = deferredLightingEffect->_directionalSkyboxLightLocations;
629 const auto& keyLight = deferredLightingEffect->_allocatedLights[deferredLightingEffect->_globalLights.front()];564 const auto& keyLight = deferredLightingEffect->_allocatedLights[deferredLightingEffect->_globalLights.front()];
630 565
631 // Setup the global directional pass pipeline566 // Setup the global directional pass pipeline
@@ -772,16 +707,6 @@ void RenderDeferredCleanup::run(const render::RenderContextPointer& renderContex · 140 unmodified lines
772 batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, nullptr);707 batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, nullptr);
773 708
774 }709 }
775
776 auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
777
778 // End of the Lighting pass
779 if (!deferredLightingEffect->_pointLights.empty()) {
780 deferredLightingEffect->_pointLights.clear();
781 }
782 if (!deferredLightingEffect->_spotLights.empty()) {
783 deferredLightingEffect->_spotLights.clear();
784 }
785}710}
786 711
787RenderDeferred::RenderDeferred() {712RenderDeferred::RenderDeferred() {
libraries/render-utils/src/DeferredLightingEffect.h+17−26
@@ -20,6 +20,8 @@ · 19 unmodified lines
20#include "model/Light.h"20#include "model/Light.h"
21#include "model/Geometry.h"21#include "model/Geometry.h"
22 22
23#include <procedural/ProceduralSkybox.h>
24
23#include <render/CullTask.h>25#include <render/CullTask.h>
24 26
25#include "DeferredFrameTransform.h"27#include "DeferredFrameTransform.h"
@@ -28,11 +30,13 @@ · 2 unmodified lines
28 30
29#include "LightStage.h"31#include "LightStage.h"
30#include "LightClusters.h"32#include "LightClusters.h"
33#include "BackgroundStage.h"
31 34
32#include "SurfaceGeometryPass.h"35#include "SurfaceGeometryPass.h"
33#include "SubsurfaceScattering.h"36#include "SubsurfaceScattering.h"
34#include "AmbientOcclusionEffect.h"37#include "AmbientOcclusionEffect.h"
35 38
39
36class RenderArgs;40class RenderArgs;
37struct LightLocations;41struct LightLocations;
38using LightLocationsPtr = std::shared_ptr<LightLocations>;42using LightLocationsPtr = std::shared_ptr<LightLocations>;
@@ -43,34 +47,30 @@ class DeferredLightingEffect : public Dependency { · 4 unmodified lines
43 47
44public:48public:
45 void init();49 void init();
46 50
47 void addLight(const model::LightPointer& light);
48
49 /// Adds a point light to render for the current frame.
50 void addPointLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(0.0f, 0.0f, 0.0f),
51 float intensity = 0.5f, float falloffRadius = 0.01f);
52
53 /// Adds a spot light to render for the current frame.
54 void addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f),
55 float intensity = 0.5f, float falloffRadius = 0.01f,
56 const glm::quat& orientation = glm::quat(), float exponent = 0.0f, float cutoff = PI);
57
58 void setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);51 void setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);
59 void unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);52 void unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);
60 53
61 // update global lighting54 // update global lighting
62 void setGlobalLight(const model::LightPointer& light);55 void setGlobalLight(const model::LightPointer& light);
56 const model::LightPointer& getGlobalLight() const;
63 57
64 const LightStagePointer getLightStage() { return _lightStage; }58 const LightStagePointer& getLightStage() { return _lightStage; }
59 const BackgroundStagePointer& getBackgroundStage() { return _backgroundStage; }
65 60
66 void setShadowMapEnabled(bool enable) { _shadowMapEnabled = enable; };61 void setShadowMapEnabled(bool enable) { _shadowMapEnabled = enable; };
67 void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; }62 void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; }
68 bool isAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; }63 bool isAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; }
69 64
65 model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
66 gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; }
67 gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
68
70private:69private:
71 DeferredLightingEffect() = default;70 DeferredLightingEffect() = default;
72 71
73 LightStagePointer _lightStage;72 LightStagePointer _lightStage;
73 BackgroundStagePointer _backgroundStage;
74 74
75 bool _shadowMapEnabled{ false };75 bool _shadowMapEnabled{ false };
76 bool _ambientOcclusionEnabled{ false };76 bool _ambientOcclusionEnabled{ false };
@@ -82,39 +82,30 @@ private: · 5 unmodified lines
82 82
83 gpu::PipelinePointer _directionalSkyboxLight;83 gpu::PipelinePointer _directionalSkyboxLight;
84 gpu::PipelinePointer _directionalAmbientSphereLight;84 gpu::PipelinePointer _directionalAmbientSphereLight;
85 gpu::PipelinePointer _directionalLight;
86 85
87 gpu::PipelinePointer _directionalSkyboxLightShadow;86 gpu::PipelinePointer _directionalSkyboxLightShadow;
88 gpu::PipelinePointer _directionalAmbientSphereLightShadow;87 gpu::PipelinePointer _directionalAmbientSphereLightShadow;
89 gpu::PipelinePointer _directionalLightShadow;
90 88
91 gpu::PipelinePointer _localLight;89 gpu::PipelinePointer _localLight;
92 gpu::PipelinePointer _localLightOutline;90 gpu::PipelinePointer _localLightOutline;
93 91
94 gpu::PipelinePointer _pointLightBack;
95 gpu::PipelinePointer _pointLightFront;
96 gpu::PipelinePointer _spotLightBack;
97 gpu::PipelinePointer _spotLightFront;
98
99 LightLocationsPtr _directionalSkyboxLightLocations;92 LightLocationsPtr _directionalSkyboxLightLocations;
100 LightLocationsPtr _directionalAmbientSphereLightLocations;93 LightLocationsPtr _directionalAmbientSphereLightLocations;
101 LightLocationsPtr _directionalLightLocations;
102 94
103 LightLocationsPtr _directionalSkyboxLightShadowLocations;95 LightLocationsPtr _directionalSkyboxLightShadowLocations;
104 LightLocationsPtr _directionalAmbientSphereLightShadowLocations;96 LightLocationsPtr _directionalAmbientSphereLightShadowLocations;
105 LightLocationsPtr _directionalLightShadowLocations;
106 97
107 LightLocationsPtr _localLightLocations;98 LightLocationsPtr _localLightLocations;
108 LightLocationsPtr _localLightOutlineLocations;99 LightLocationsPtr _localLightOutlineLocations;
109 LightLocationsPtr _pointLightLocations;
110 LightLocationsPtr _spotLightLocations;
111 100
112 using Lights = std::vector<model::LightPointer>;101 using Lights = std::vector<model::LightPointer>;
113 102
114 Lights _allocatedLights;103 Lights _allocatedLights;
115 std::vector<int> _globalLights;104 std::vector<int> _globalLights;
116 std::vector<int> _pointLights;105
117 std::vector<int> _spotLights;106 model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() };
107 gpu::TexturePointer _defaultSkyboxTexture;
108 gpu::TexturePointer _defaultSkyboxAmbientTexture;
118 109
119 friend class LightClusteringPass;110 friend class LightClusteringPass;
120 friend class RenderDeferredSetup;111 friend class RenderDeferredSetup;
libraries/render-utils/src/GeometryCache.cpp+94−30
@@ -24,6 +24,7 @@ · 23 unmodified lines
24 24
25#include "TextureCache.h"25#include "TextureCache.h"
26#include "RenderUtilsLogging.h"26#include "RenderUtilsLogging.h"
27#include "StencilMaskPass.h"
27 28
28#include "gpu/StandardShaderLib.h"29#include "gpu/StandardShaderLib.h"
29 30
@@ -68,7 +69,7 @@ std::vector<vec3> polygon() { · 38 unmodified lines
68 std::vector<vec3> result;69 std::vector<vec3> result;
69 result.reserve(SIDES);70 result.reserve(SIDES);
70 double angleIncrement = 2.0 * M_PI / SIDES;71 double angleIncrement = 2.0 * M_PI / SIDES;
71 for (size_t i = 0; i < SIDES; ++i) {72 for (size_t i = 0; i < SIDES; i++) {
72 double angle = (double)i * angleIncrement;73 double angle = (double)i * angleIncrement;
73 result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 });74 result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 });
74 }75 }
@@ -171,20 +172,20 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N · 96 unmodified lines
171 vertices.reserve(N * faceCount * 2);172 vertices.reserve(N * faceCount * 2);
172 solidIndices.reserve(faceIndexCount * faceCount);173 solidIndices.reserve(faceIndexCount * faceCount);
173 174
174 for (size_t f = 0; f < faceCount; ++f) {175 for (size_t f = 0; f < faceCount; f++) {
175 const Face<N>& face = shape.faces[f];176 const Face<N>& face = shape.faces[f];
176 // Compute the face normal177 // Compute the face normal
177 vec3 faceNormal = shape.getFaceNormal(f);178 vec3 faceNormal = shape.getFaceNormal(f);
178 179
179 // Create the vertices for the face180 // Create the vertices for the face
180 for (Index i = 0; i < N; ++i) {181 for (Index i = 0; i < N; i++) {
181 Index originalIndex = face[i];182 Index originalIndex = face[i];
182 vertices.push_back(shape.vertices[originalIndex]);183 vertices.push_back(shape.vertices[originalIndex]);
183 vertices.push_back(faceNormal);184 vertices.push_back(faceNormal);
184 }185 }
185 186
186 // Create the wire indices for unseen edges187 // Create the wire indices for unseen edges
187 for (Index i = 0; i < N; ++i) {188 for (Index i = 0; i < N; i++) {
188 Index a = i;189 Index a = i;
189 Index b = (i + 1) % N;190 Index b = (i + 1) % N;
190 auto token = indexToken(face[a], face[b]);191 auto token = indexToken(face[a], face[b]);
@@ -196,7 +197,7 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N · 5 unmodified lines
196 }197 }
197 198
198 // Create the solid face indices199 // Create the solid face indices
199 for (Index i = 0; i < N - 2; ++i) {200 for (Index i = 0; i < N - 2; i++) {
200 solidIndices.push_back(0 + baseVertex);201 solidIndices.push_back(0 + baseVertex);
201 solidIndices.push_back(i + 1 + baseVertex);202 solidIndices.push_back(i + 1 + baseVertex);
202 solidIndices.push_back(i + 2 + baseVertex);203 solidIndices.push_back(i + 2 + baseVertex);
@@ -228,10 +229,10 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid · 25 unmodified lines
228 229
229 solidIndices.reserve(faceIndexCount * faceCount);230 solidIndices.reserve(faceIndexCount * faceCount);
230 231
231 for (size_t f = 0; f < faceCount; ++f) {232 for (size_t f = 0; f < faceCount; f++) {
232 const Face<N>& face = shape.faces[f];233 const Face<N>& face = shape.faces[f];
233 // Create the wire indices for unseen edges234 // Create the wire indices for unseen edges
234 for (Index i = 0; i < N; ++i) {235 for (Index i = 0; i < N; i++) {
235 Index a = face[i];236 Index a = face[i];
236 Index b = face[(i + 1) % N];237 Index b = face[(i + 1) % N];
237 auto token = indexToken(a, b);238 auto token = indexToken(a, b);
@@ -243,7 +244,7 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid · 5 unmodified lines
243 }244 }
244 245
245 // Create the solid face indices246 // Create the solid face indices
246 for (Index i = 0; i < N - 2; ++i) {247 for (Index i = 0; i < N - 2; i++) {
247 solidIndices.push_back(face[i] + baseVertex);248 solidIndices.push_back(face[i] + baseVertex);
248 solidIndices.push_back(face[i + 1] + baseVertex);249 solidIndices.push_back(face[i + 1] + baseVertex);
249 solidIndices.push_back(face[i + 2] + baseVertex);250 solidIndices.push_back(face[i + 2] + baseVertex);
@@ -255,23 +256,30 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid · 5 unmodified lines
255}256}
256 257
257template <uint32_t N>258template <uint32_t N>
258void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {259void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) {
259 using namespace geometry;260 using namespace geometry;
260 Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);261 Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
261 VertexVector vertices;262 VertexVector vertices;
262 IndexVector solidIndices, wireIndices;263 IndexVector solidIndices, wireIndices;
263 264
264 // Top and bottom faces265 // Top (if not conical) and bottom faces
265 std::vector<vec3> shape = polygon<N>();266 std::vector<vec3> shape = polygon<N>();
266 for (const vec3& v : shape) {267 if (isConical) {
267 vertices.push_back(vec3(v.x, 0.5f, v.z));268 for (uint32_t i = 0; i < N; i++) {
268 vertices.push_back(vec3(0, 1, 0));269 vertices.push_back(vec3(0.0f, 0.5f, 0.0f));
270 vertices.push_back(vec3(0.0f, 1.0f, 0.0f));
271 }
272 } else {
273 for (const vec3& v : shape) {
274 vertices.push_back(vec3(v.x, 0.5f, v.z));
275 vertices.push_back(vec3(0.0f, 1.0f, 0.0f));
276 }
269 }277 }
270 for (const vec3& v : shape) {278 for (const vec3& v : shape) {
271 vertices.push_back(vec3(v.x, -0.5f, v.z));279 vertices.push_back(vec3(v.x, -0.5f, v.z));
272 vertices.push_back(vec3(0, -1, 0));280 vertices.push_back(vec3(0.0f, -1.0f, 0.0f));
273 }281 }
274 for (uint32_t i = 2; i < N; ++i) {282 for (uint32_t i = 2; i < N; i++) {
275 solidIndices.push_back(baseVertex + 0);283 solidIndices.push_back(baseVertex + 0);
276 solidIndices.push_back(baseVertex + i);284 solidIndices.push_back(baseVertex + i);
277 solidIndices.push_back(baseVertex + i - 1);285 solidIndices.push_back(baseVertex + i - 1);
@@ -279,7 +287,7 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver · 1 unmodified line
279 solidIndices.push_back(baseVertex + i + N - 1);287 solidIndices.push_back(baseVertex + i + N - 1);
280 solidIndices.push_back(baseVertex + i + N);288 solidIndices.push_back(baseVertex + i + N);
281 }289 }
282 for (uint32_t i = 1; i <= N; ++i) {290 for (uint32_t i = 1; i <= N; i++) {
283 wireIndices.push_back(baseVertex + (i % N));291 wireIndices.push_back(baseVertex + (i % N));
284 wireIndices.push_back(baseVertex + i - 1);292 wireIndices.push_back(baseVertex + i - 1);
285 wireIndices.push_back(baseVertex + (i % N) + N);293 wireIndices.push_back(baseVertex + (i % N) + N);
@@ -289,12 +297,12 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver · 3 unmodified lines
289 // Now do the sides297 // Now do the sides
290 baseVertex += 2 * N;298 baseVertex += 2 * N;
291 299
292 for (uint32_t i = 0; i < N; ++i) {300 for (uint32_t i = 0; i < N; i++) {
293 vec3 left = shape[i];301 vec3 left = shape[i];
294 vec3 right = shape[(i + 1) % N];302 vec3 right = shape[(i + 1) % N];
295 vec3 normal = glm::normalize(left + right);303 vec3 normal = glm::normalize(left + right);
296 vec3 topLeft = vec3(left.x, 0.5f, left.z);304 vec3 topLeft = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(left.x, 0.5f, left.z));
297 vec3 topRight = vec3(right.x, 0.5f, right.z);305 vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z));
298 vec3 bottomLeft = vec3(left.x, -0.5f, left.z);306 vec3 bottomLeft = vec3(left.x, -0.5f, left.z);
299 vec3 bottomRight = vec3(right.x, -0.5f, right.z);307 vec3 bottomRight = vec3(right.x, -0.5f, right.z);
300 308
@@ -324,6 +332,41 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver · 23 unmodified lines
324 shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);332 shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
325}333}
326 334
335void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
336 // Draw a circle with radius 1/4th the size of the bounding box
337 using namespace geometry;
338
339 Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
340 VertexVector vertices;
341 IndexVector solidIndices, wireIndices;
342 const int NUM_CIRCLE_VERTICES = 64;
343
344 std::vector<vec3> shape = polygon<NUM_CIRCLE_VERTICES>();
345 for (const vec3& v : shape) {
346 vertices.push_back(vec3(v.x, 0.0f, v.z));
347 vertices.push_back(vec3(0.0f, 0.0f, 0.0f));
348 }
349
350 for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) {
351 solidIndices.push_back(baseVertex + 0);
352 solidIndices.push_back(baseVertex + i);
353 solidIndices.push_back(baseVertex + i - 1);
354 solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES);
355 solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1);
356 solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES);
357 }
358
359 for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) {
360 wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES));
361 wireIndices.push_back(baseVertex + i - 1);
362 wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES);
363 wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES);
364 }
365
366 shapeData.setupVertices(vertexBuffer, vertices);
367 shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
368}
369
327// FIXME solids need per-face vertices, but smooth shaded370// FIXME solids need per-face vertices, but smooth shaded
328// components do not. Find a way to support using draw elements371// components do not. Find a way to support using draw elements
329// or draw arrays as appropriate372// or draw arrays as appropriate
@@ -356,8 +399,8 @@ void GeometryCache::buildShapes() { · 26 unmodified lines
356 Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE);399 Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE);
357 ShapeData& shapeData = _shapes[Line];400 ShapeData& shapeData = _shapes[Line];
358 shapeData.setupVertices(_shapeVertices, VertexVector {401 shapeData.setupVertices(_shapeVertices, VertexVector {
359 vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0),402 vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f),
360 vec3(0.5f, 0, 0), vec3(0.5f, 0, 0)403 vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f)
361 });404 });
362 IndexVector wireIndices;405 IndexVector wireIndices;
363 // Only two indices406 // Only two indices
@@ -366,20 +409,22 @@ void GeometryCache::buildShapes() { · 2 unmodified lines
366 shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);409 shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);
367 }410 }
368 411
369 // Not implememented yet:
370
371 //Triangle,412 //Triangle,
372 extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices);413 extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices);
373 //Hexagon,414 //Hexagon,
374 extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices);415 extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices);
375 //Octagon,416 //Octagon,
376 extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices);417 extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices);
377
378 //Quad,
379 //Circle,
380 //Torus,
381 //Cone,
382 //Cylinder,418 //Cylinder,
419 extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices);
420 //Cone,
421 extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true);
422 //Circle
423 drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices);
424 // Not implememented yet:
425 //Quad,
426 //Torus,
427
383}428}
384 429
385gpu::Stream::FormatPointer& getSolidStreamFormat() {430gpu::Stream::FormatPointer& getSolidStreamFormat() {
@@ -596,7 +641,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, con · 210 unmodified lines
596 auto pointCount = points.size();641 auto pointCount = points.size();
597 auto colorCount = colors.size();642 auto colorCount = colors.size();
598 int compactColor = 0;643 int compactColor = 0;
599 for (auto i = 0; i < pointCount; ++i) {644 for (auto i = 0; i < pointCount; i++) {
600 const auto& point = points[i];645 const auto& point = points[i];
601 *(vertex++) = point.x;646 *(vertex++) = point.x;
602 *(vertex++) = point.y;647 *(vertex++) = point.y;
@@ -673,7 +718,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con · 70 unmodified lines
673 const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);718 const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
674 auto pointCount = points.size();719 auto pointCount = points.size();
675 auto colorCount = colors.size();720 auto colorCount = colors.size();
676 for (auto i = 0; i < pointCount; ++i) {721 for (auto i = 0; i < pointCount; i++) {
677 const glm::vec3& point = points[i];722 const glm::vec3& point = points[i];
678 if (i < colorCount) {723 if (i < colorCount) {
679 const glm::vec4& color = colors[i];724 const glm::vec4& color = colors[i];
@@ -1610,6 +1655,9 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const · 930 unmodified lines
1610 state->setBlendFunction(true, 1655 state->setBlendFunction(true,
1611 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,1656 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
1612 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);1657 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
1658
1659 PrepareStencil::testMask(*state);
1660
1613 gpu::Shader::BindingSet slotBindings;1661 gpu::Shader::BindingSet slotBindings;
1614 slotBindings.insert(gpu::Shader::Binding(std::string("lineData"), LINE_DATA_SLOT));1662 slotBindings.insert(gpu::Shader::Binding(std::string("lineData"), LINE_DATA_SLOT));
1615 gpu::Shader::makeProgram(*program, slotBindings);1663 gpu::Shader::makeProgram(*program, slotBindings);
@@ -1663,11 +1711,14 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { · 47 unmodified lines
1663 1711
1664 // enable decal blend1712 // enable decal blend
1665 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);1713 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
1714 PrepareStencil::testMask(*state);
1666 1715
1667 _standardDrawPipeline = gpu::Pipeline::create(program, state);1716 _standardDrawPipeline = gpu::Pipeline::create(program, state);
1668 1717
1669 1718
1670 auto stateNoBlend = std::make_shared<gpu::State>();1719 auto stateNoBlend = std::make_shared<gpu::State>();
1720 PrepareStencil::testMaskDrawShape(*state);
1721
1671 auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();1722 auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
1672 auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);1723 auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);
1673 gpu::Shader::makeProgram((*programNoBlend));1724 gpu::Shader::makeProgram((*programNoBlend));
@@ -1690,12 +1741,14 @@ void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bo · 16 unmodified lines
1690 1741
1691 auto stateLayered = std::make_shared<gpu::State>();1742 auto stateLayered = std::make_shared<gpu::State>();
1692 stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);1743 stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
1744 PrepareStencil::testMask(*stateLayered);
1693 _gridPipelineLayered = gpu::Pipeline::create(program, stateLayered);1745 _gridPipelineLayered = gpu::Pipeline::create(program, stateLayered);
1694 1746
1695 auto state = std::make_shared<gpu::State>(stateLayered->getValues());1747 auto state = std::make_shared<gpu::State>(stateLayered->getValues());
1696 const float DEPTH_BIAS = 0.001f;1748 const float DEPTH_BIAS = 0.001f;
1697 state->setDepthBias(DEPTH_BIAS);1749 state->setDepthBias(DEPTH_BIAS);
1698 state->setDepthTest(true, false, gpu::LESS_EQUAL);1750 state->setDepthTest(true, false, gpu::LESS_EQUAL);
1751 PrepareStencil::testMaskDrawShape(*state);
1699 _gridPipeline = gpu::Pipeline::create(program, state);1752 _gridPipeline = gpu::Pipeline::create(program, state);
1700 }1753 }
1701 1754
@@ -1773,6 +1826,11 @@ static void buildWebShader(const std::string& vertShaderText, const std::string& · 71 unmodified lines
1773 state->setBlendFunction(blendEnable,1826 state->setBlendFunction(blendEnable,
1774 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,1827 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
1775 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);1828 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
1829 if (blendEnable) {
1830 PrepareStencil::testMask(*state);
1831 } else {
1832 PrepareStencil::testMaskDrawShape(*state);
1833 }
1776 1834
1777 pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);1835 pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
1778}1836}
@@ -1858,6 +1916,12 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp · 79 unmodified lines
1858 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,1916 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
1859 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);1917 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
1860 1918
1919 if (config.isTransparent()) {
1920 PrepareStencil::testMask(*state);
1921 } else {
1922 PrepareStencil::testMaskDrawShape(*state);
1923 }
1924
1861 gpu::ShaderPointer program = (config.isUnlit()) ? _unlitShader : _simpleShader;1925 gpu::ShaderPointer program = (config.isUnlit()) ? _unlitShader : _simpleShader;
1862 gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state);1926 gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state);
1863 _simplePrograms.insert(config, pipeline);1927 _simplePrograms.insert(config, pipeline);
libraries/render-utils/src/GeometryCache.h+2−2
@@ -142,8 +142,8 @@ public: · 141 unmodified lines
142 Dodecahedron,142 Dodecahedron,
143 Icosahedron,143 Icosahedron,
144 Torus, // not yet implemented144 Torus, // not yet implemented
145 Cone, // not yet implemented145 Cone,
146 Cylinder, // not yet implemented146 Cylinder,
147 NUM_SHAPES,147 NUM_SHAPES,
148 };148 };
149 149
libraries/render-utils/src/HitEffect.cpp+0−94
@@ -1,94 +0,0 @@
1//
2// HitEffect.cpp
3// interface/src/renderer
4//
5// Created by Andrzej Kapolka on 7/14/13.
6// Copyright 2013 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
13
14
15#include <glm/gtc/random.hpp>
16
17#include <DependencyManager.h>
18#include <PathUtils.h>
19#include <SharedUtil.h>
20
21#include "AbstractViewStateInterface.h"
22#include "HitEffect.h"
23
24#include "TextureCache.h"
25#include "DependencyManager.h"
26#include "ViewFrustum.h"
27#include "GeometryCache.h"
28
29#include <gpu/Context.h>
30
31#include "hit_effect_vert.h"
32#include "hit_effect_frag.h"
33
34
35HitEffect::HitEffect() {
36 _geometryId = DependencyManager::get<GeometryCache>()->allocateID();
37}
38
39HitEffect::~HitEffect() {
40 auto geometryCache = DependencyManager::get<GeometryCache>();
41 if (_geometryId && geometryCache) {
42 geometryCache->releaseID(_geometryId);
43 }
44}
45
46const gpu::PipelinePointer& HitEffect::getHitEffectPipeline() {
47 if (!_hitEffectPipeline) {
48 auto vs = gpu::Shader::createVertex(std::string(hit_effect_vert));
49 auto ps = gpu::Shader::createPixel(std::string(hit_effect_frag));
50 gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
51
52
53 gpu::Shader::BindingSet slotBindings;
54 gpu::Shader::makeProgram(*program, slotBindings);
55
56
57 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
58
59 state->setDepthTest(false, false, gpu::LESS_EQUAL);
60
61 // Blend on transparent
62 state->setBlendFunction(true,
63 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
64
65 // Good to go add the brand new pipeline
66 _hitEffectPipeline = gpu::Pipeline::create(program, state);
67 }
68 return _hitEffectPipeline;
69}
70
71void HitEffect::run(const render::RenderContextPointer& renderContext) {
72 assert(renderContext->args);
73 assert(renderContext->args->hasViewFrustum());
74 RenderArgs* args = renderContext->args;
75
76 gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
77
78 glm::mat4 projMat;
79 Transform viewMat;
80 args->getViewFrustum().evalProjectionMatrix(projMat);
81 args->getViewFrustum().evalViewTransform(viewMat);
82 batch.setProjectionTransform(projMat);
83 batch.setViewTransform(viewMat);
84 batch.setModelTransform(Transform());
85
86 batch.setPipeline(getHitEffectPipeline());
87
88 static const glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
89 static const glm::vec2 bottomLeft(-1.0f, -1.0f);
90 static const glm::vec2 topRight(1.0f, 1.0f);
91 DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, color, _geometryId);
92 });
93}
94
libraries/render-utils/src/HitEffect.h+0−38
@@ -1,38 +0,0 @@
1//
2// hitEffect.h
3// hifi
4//
5// Created by eric levin on 7/17/15.
6//
7//
8
9#ifndef hifi_hitEffect_h
10#define hifi_hitEffect_h
11
12#include <render/DrawTask.h>
13
14class HitEffectConfig : public render::Job::Config {
15 Q_OBJECT
16 Q_PROPERTY(bool enabled MEMBER enabled)
17public:
18 HitEffectConfig() : render::Job::Config(false) {}
19};
20
21class HitEffect {
22public:
23 using Config = HitEffectConfig;
24 using JobModel = render::Job::Model<HitEffect, Config>;
25
26 HitEffect();
27 ~HitEffect();
28 void configure(const Config& config) {}
29 void run(const render::RenderContextPointer& renderContext);
30
31 const gpu::PipelinePointer& getHitEffectPipeline();
32
33private:
34 int _geometryId { 0 };
35 gpu::PipelinePointer _hitEffectPipeline;
36};
37
38#endif
libraries/render-utils/src/LightClusters.cpp+2−0
@@ -16,6 +16,8 @@ · 15 unmodified lines
16 16
17#include <gpu/StandardShaderLib.h>17#include <gpu/StandardShaderLib.h>
18 18
19#include "StencilMaskPass.h"
20
19#include "lightClusters_drawGrid_vert.h"21#include "lightClusters_drawGrid_vert.h"
20#include "lightClusters_drawGrid_frag.h"22#include "lightClusters_drawGrid_frag.h"
21 23
libraries/render-utils/src/LightClusters.h+3−3
@@ -152,9 +152,9 @@ public: · 151 unmodified lines
152 int numInputLights { 0 };152 int numInputLights { 0 };
153 int numClusteredLights { 0 };153 int numClusteredLights { 0 };
154 154
155 void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; emit dirty(); }155 void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; }
156 void setNumInputLights(int numLights) { numInputLights = numLights; emit dirty(); }156 void setNumInputLights(int numLights) { numInputLights = numLights; }
157 void setNumClusteredLights(int numLights) { numClusteredLights = numLights; emit dirty(); }157 void setNumClusteredLights(int numLights) { numClusteredLights = numLights; }
158 158
159 int numSceneLights { 0 };159 int numSceneLights { 0 };
160 int numFreeSceneLights { 0 };160 int numFreeSceneLights { 0 };
libraries/render-utils/src/LightPayload.cpp+68−0
@@ -81,3 +81,71 @@ void LightPayload::render(RenderArgs* args) { · 80 unmodified lines
81 }81 }
82}82}
83 83
84
85namespace render {
86 template <> const ItemKey payloadGetKey(const KeyLightPayload::Pointer& payload) {
87 ItemKey::Builder builder;
88 builder.withTypeLight();
89 if (!payload || !payload->isVisible()) {
90 builder.withInvisible();
91 }
92 return builder.build();
93 }
94
95 template <> const Item::Bound payloadGetBound(const KeyLightPayload::Pointer& payload) {
96 if (payload) {
97 return payload->editBound();
98 }
99 return render::Item::Bound();
100 }
101 template <> void payloadRender(const KeyLightPayload::Pointer& payload, RenderArgs* args) {
102 if (args) {
103 if (payload) {
104 payload->render(args);
105 }
106 }
107 }
108}
109
110KeyLightPayload::KeyLightPayload() :
111_light(std::make_shared<model::Light>())
112{
113}
114
115
116KeyLightPayload::~KeyLightPayload() {
117 if (!LightStage::isIndexInvalid(_index)) {
118 if (_stage) {
119 _stage->removeLight(_index);
120 }
121 }
122}
123
124void KeyLightPayload::render(RenderArgs* args) {
125 if (!_stage) {
126 _stage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
127 }
128 // Do we need to allocate the light in the stage ?
129 if (LightStage::isIndexInvalid(_index)) {
130 _index = _stage->addLight(_light);
131 _needUpdate = false;
132 }
133 // Need an update ?
134 if (_needUpdate) {
135 _stage->updateLightArrayBuffer(_index);
136 _needUpdate = false;
137 }
138
139 if (isVisible()) {
140 // FInally, push the light visible in the frame
141 _stage->_currentFrame.pushLight(_index, _light->getType());
142
143#ifdef WANT_DEBUG
144 Q_ASSERT(args->_batch);
145 gpu::Batch& batch = *args->_batch;
146 batch.setModelTransform(getTransformToCenter());
147 DependencyManager::get<GeometryCache>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
148#endif
149 }
150}
151
libraries/render-utils/src/LightPayload.h+38−0
@@ -15,6 +15,7 @@ · 14 unmodified lines
15#include <model/Light.h>15#include <model/Light.h>
16#include <render/Item.h>16#include <render/Item.h>
17#include "LightStage.h"17#include "LightStage.h"
18#include "TextureCache.h"
18 19
19class LightPayload {20class LightPayload {
20public:21public:
@@ -46,4 +47,41 @@ namespace render { · 25 unmodified lines
46 template <> void payloadRender(const LightPayload::Pointer& payload, RenderArgs* args);47 template <> void payloadRender(const LightPayload::Pointer& payload, RenderArgs* args);
47}48}
48 49
50class KeyLightPayload {
51public:
52 using Payload = render::Payload<KeyLightPayload>;
53 using Pointer = Payload::DataPointer;
54
55 KeyLightPayload();
56 ~KeyLightPayload();
57 void render(RenderArgs* args);
58
59 model::LightPointer editLight() { _needUpdate = true; return _light; }
60 render::Item::Bound& editBound() { _needUpdate = true; return _bound; }
61
62 void setVisible(bool visible) { _isVisible = visible; }
63 bool isVisible() const { return _isVisible; }
64
65
66 // More attributes used for rendering:
67 NetworkTexturePointer _ambientTexture;
68 QString _ambientTextureURL;
69 bool _pendingAmbientTexture { false };
70 bool _validAmbientTextureURL { false };
71
72protected:
73 model::LightPointer _light;
74 render::Item::Bound _bound;
75 LightStagePointer _stage;
76 LightStage::Index _index { LightStage::INVALID_INDEX };
77 bool _needUpdate { true };
78 bool _isVisible { true };
79};
80
81namespace render {
82 template <> const ItemKey payloadGetKey(const KeyLightPayload::Pointer& payload);
83 template <> const Item::Bound payloadGetBound(const KeyLightPayload::Pointer& payload);
84 template <> void payloadRender(const KeyLightPayload::Pointer& payload, RenderArgs* args);
85}
86
49#endif87#endif
libraries/render-utils/src/LightStage.h+8−3
@@ -77,7 +77,6 @@ public: · 76 unmodified lines
77 };77 };
78 using Descs = std::vector<Desc>;78 using Descs = std::vector<Desc>;
79 79
80
81 Index findLight(const LightPointer& light) const;80 Index findLight(const LightPointer& light) const;
82 Index addLight(const LightPointer& light);81 Index addLight(const LightPointer& light);
83 82
@@ -118,19 +117,25 @@ public: · 34 unmodified lines
118 public:117 public:
119 Frame() {}118 Frame() {}
120 119
121 void clear() { _pointLights.clear(); _spotLights.clear(); }120 void clear() { _pointLights.clear(); _spotLights.clear(); _sunLights.clear(); _ambientLights.clear(); }
122 void pushLight(LightStage::Index index, model::Light::Type type) {121 void pushLight(LightStage::Index index, model::Light::Type type) {
123 switch (type) {122 switch (type) {
124 case model::Light::POINT: { pushPointLight(index); break; }123 case model::Light::POINT: { pushPointLight(index); break; }
125 case model::Light::SPOT: { pushSpotLight(index); break; }124 case model::Light::SPOT: { pushSpotLight(index); break; }
125 case model::Light::SUN: { pushSunLight(index); break; }
126 case model::Light::AMBIENT: { pushAmbientLight(index); break; }
126 default: { break; }127 default: { break; }
127 }128 }
128 }129 }
129 void pushPointLight(LightStage::Index index) { _pointLights.emplace_back(index); }130 void pushPointLight(LightStage::Index index) { _pointLights.emplace_back(index); }
130 void pushSpotLight(LightStage::Index index) { _spotLights.emplace_back(index); }131 void pushSpotLight(LightStage::Index index) { _spotLights.emplace_back(index); }
131 132 void pushSunLight(LightStage::Index index) { _sunLights.emplace_back(index); }
133 void pushAmbientLight(LightStage::Index index) { _ambientLights.emplace_back(index); }
134
132 LightStage::LightIndices _pointLights;135 LightStage::LightIndices _pointLights;
133 LightStage::LightIndices _spotLights;136 LightStage::LightIndices _spotLights;
137 LightStage::LightIndices _sunLights;
138 LightStage::LightIndices _ambientLights;
134 };139 };
135 140
136 Frame _currentFrame;141 Frame _currentFrame;
libraries/render-utils/src/Model.cpp+2−2
@@ -332,7 +332,7 @@ void Model::initJointStates() { · 331 unmodified lines
332 332
333bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,333bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
334 BoxFace& face, glm::vec3& surfaceNormal,334 BoxFace& face, glm::vec3& surfaceNormal,
335 QString& extraInfo, bool pickAgainstTriangles) {335 QString& extraInfo, bool pickAgainstTriangles, bool allowBackface) {
336 336
337 bool intersectedSomething = false;337 bool intersectedSomething = false;
338 338
@@ -381,7 +381,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g · 42 unmodified lines
381 float triangleSetDistance = 0.0f;381 float triangleSetDistance = 0.0f;
382 BoxFace triangleSetFace;382 BoxFace triangleSetFace;
383 glm::vec3 triangleSetNormal;383 glm::vec3 triangleSetNormal;
384 if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetNormal, pickAgainstTriangles)) {384 if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetNormal, pickAgainstTriangles, allowBackface)) {
385 385
386 glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);386 glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
387 glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));387 glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
libraries/render-utils/src/Model.h+1−1
@@ -156,7 +156,7 @@ public: · 155 unmodified lines
156 156
157 bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,157 bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
158 BoxFace& face, glm::vec3& surfaceNormal, 158 BoxFace& face, glm::vec3& surfaceNormal,
159 QString& extraInfo, bool pickAgainstTriangles = false);159 QString& extraInfo, bool pickAgainstTriangles = false, bool allowBackface = false);
160 160
161 void setOffset(const glm::vec3& offset);161 void setOffset(const glm::vec3& offset);
162 const glm::vec3& getOffset() const { return _offset; }162 const glm::vec3& getOffset() const { return _offset; }
libraries/render-utils/src/RenderDeferredTask.cpp+15−95
@@ -27,12 +27,12 @@ · 26 unmodified lines
27#include <render/BlurTask.h>27#include <render/BlurTask.h>
28 28
29#include "LightingModel.h"29#include "LightingModel.h"
30#include "StencilMaskPass.h"
30#include "DebugDeferredBuffer.h"31#include "DebugDeferredBuffer.h"
31#include "DeferredFramebuffer.h"32#include "DeferredFramebuffer.h"
32#include "DeferredLightingEffect.h"33#include "DeferredLightingEffect.h"
33#include "SurfaceGeometryPass.h"34#include "SurfaceGeometryPass.h"
34#include "FramebufferCache.h"35#include "FramebufferCache.h"
35#include "HitEffect.h"
36#include "TextureCache.h"36#include "TextureCache.h"
37#include "ZoneRenderer.h"37#include "ZoneRenderer.h"
38 38
@@ -43,8 +43,6 @@ · 4 unmodified lines
43 43
44#include <gpu/StandardShaderLib.h>44#include <gpu/StandardShaderLib.h>
45 45
46#include "drawOpaqueStencil_frag.h"
47
48 46
49using namespace render;47using namespace render;
50extern void initOverlay3DPipelines(render::ShapePlumber& plumber);48extern void initOverlay3DPipelines(render::ShapePlumber& plumber);
@@ -75,7 +73,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren · 24 unmodified lines
75 const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");73 const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
76 const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");74 const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
77 75
78
79 // GPU jobs: Start preparing the primary, deferred and lighting buffer76 // GPU jobs: Start preparing the primary, deferred and lighting buffer
80 const auto primaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");77 const auto primaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
81 78
@@ -86,13 +83,13 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren · 4 unmodified lines
86 const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0);83 const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0);
87 const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1);84 const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1);
88 85
86 // draw a stencil mask in hidden regions of the framebuffer.
87 task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
88
89 // Render opaque objects in DeferredBuffer89 // Render opaque objects in DeferredBuffer
90 const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying();90 const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying();
91 task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);91 task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
92 92
93 // Once opaque is all rendered create stencil background
94 task.addJob<DrawStencilDeferred>("DrawOpaqueStencil", deferredFramebuffer);
95
96 task.addJob<EndGPURangeTimer>("OpaqueRangeTimer", opaqueRangeTimer);93 task.addJob<EndGPURangeTimer>("OpaqueRangeTimer", opaqueRangeTimer);
97 94
98 95
@@ -124,6 +121,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren · 25 unmodified lines
124 // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.121 // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.
125 task.addJob<DrawLight>("DrawLight", lights);122 task.addJob<DrawLight>("DrawLight", lights);
126 123
124 // Filter zones from the general metas bucket
125 const auto zones = task.addJob<ZoneRendererTask>("ZoneRenderer", metas);
126
127 // Light Clustering127 // Light Clustering
128 // Create the cluster grid of lights, cpu job for now128 // Create the cluster grid of lights, cpu job for now
129 const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, linearDepthTarget).hasVarying();129 const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, linearDepthTarget).hasVarying();
@@ -136,11 +136,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren · 6 unmodified lines
136 136
137 task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);137 task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);
138 138
139 // Use Stencil and draw background in Lighting buffer to complete filling in the opaque139 // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job
140 const auto backgroundInputs = DrawBackgroundDeferred::Inputs(background, lightingModel).hasVarying();140 task.addJob<DrawBackgroundStage>("DrawBackgroundDeferred", lightingModel);
141 task.addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred", backgroundInputs);141
142
143
144 // Render transparent objects forward in LightingBuffer142 // Render transparent objects forward in LightingBuffer
145 const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).hasVarying();143 const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).hasVarying();
146 task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);144 task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
@@ -162,7 +160,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren · 15 unmodified lines
162 task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);160 task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
163 task.addJob<DrawBounds>("DrawTransparentBounds", transparents);161 task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
164 162
165 task.addJob<ZoneRendererTask>("ZoneRenderer", opaques);163 task.addJob<DrawBounds>("DrawLightBounds", lights);
164 task.addJob<DrawBounds>("DrawZones", zones);
166 }165 }
167 166
168 // Overlays167 // Overlays
@@ -202,6 +201,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren · 33 unmodified lines
202 auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE);201 auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE);
203 task.addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));202 task.addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
204 }203 }
204
205 task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
205 }206 }
206 207
207 208
@@ -384,88 +385,6 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& · 176 unmodified lines
384 }385 }
385}386}
386 387
387
388gpu::PipelinePointer DrawStencilDeferred::getOpaquePipeline() {
389 if (!_opaquePipeline) {
390 const gpu::int8 STENCIL_OPAQUE = 1;
391 auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
392 auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag));
393 auto program = gpu::Shader::createProgram(vs, ps);
394 gpu::Shader::makeProgram((*program));
395
396 auto state = std::make_shared<gpu::State>();
397 state->setDepthTest(true, false, gpu::LESS_EQUAL);
398 state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP));
399 state->setColorWriteMask(0);
400
401 _opaquePipeline = gpu::Pipeline::create(program, state);
402 }
403 return _opaquePipeline;
404}
405
406void DrawStencilDeferred::run(const RenderContextPointer& renderContext, const DeferredFramebufferPointer& deferredFramebuffer) {
407 assert(renderContext->args);
408 assert(renderContext->args->hasViewFrustum());
409
410 // from the touched pixel generate the stencil buffer
411 RenderArgs* args = renderContext->args;
412 doInBatch(args->_context, [&](gpu::Batch& batch) {
413 args->_batch = &batch;
414
415 auto deferredFboColorDepthStencil = deferredFramebuffer->getDeferredFramebufferDepthColor();
416
417
418 batch.enableStereo(false);
419
420 batch.setFramebuffer(deferredFboColorDepthStencil);
421 batch.setViewportTransform(args->_viewport);
422 batch.setStateScissorRect(args->_viewport);
423
424 batch.setPipeline(getOpaquePipeline());
425
426 batch.draw(gpu::TRIANGLE_STRIP, 4);
427 batch.setResourceTexture(0, nullptr);
428
429 });
430 args->_batch = nullptr;
431}
432
433void DrawBackgroundDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) {
434 assert(renderContext->args);
435 assert(renderContext->args->hasViewFrustum());
436
437 const auto& inItems = inputs.get0();
438 const auto& lightingModel = inputs.get1();
439 if (!lightingModel->isBackgroundEnabled()) {
440 return;
441 }
442
443 RenderArgs* args = renderContext->args;
444 doInBatch(args->_context, [&](gpu::Batch& batch) {
445 args->_batch = &batch;
446 // _gpuTimer.begin(batch);
447
448 batch.enableSkybox(true);
449
450 batch.setViewportTransform(args->_viewport);
451 batch.setStateScissorRect(args->_viewport);
452
453 glm::mat4 projMat;
454 Transform viewMat;
455 args->getViewFrustum().evalProjectionMatrix(projMat);
456 args->getViewFrustum().evalViewTransform(viewMat);
457
458 batch.setProjectionTransform(projMat);
459 batch.setViewTransform(viewMat);
460
461 renderItems(renderContext, inItems);
462 // _gpuTimer.end(batch);
463 });
464 args->_batch = nullptr;
465
466 // std::static_pointer_cast<Config>(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage();
467}
468
469void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {388void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
470 assert(renderContext->args);389 assert(renderContext->args);
471 assert(renderContext->args->_context);390 assert(renderContext->args->_context);
@@ -535,3 +454,4 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer · 63 unmodified lines
535 }454 }
536 });455 });
537}456}
457
libraries/render-utils/src/RenderDeferredTask.h+0−29
@@ -120,35 +120,6 @@ protected: · 119 unmodified lines
120 bool _stateSort;120 bool _stateSort;
121};121};
122 122
123class DeferredFramebuffer;
124class DrawStencilDeferred {
125public:
126 using JobModel = render::Job::ModelI<DrawStencilDeferred, std::shared_ptr<DeferredFramebuffer>>;
127
128 void run(const render::RenderContextPointer& renderContext, const std::shared_ptr<DeferredFramebuffer>& deferredFramebuffer);
129
130protected:
131 gpu::PipelinePointer _opaquePipeline;
132
133 gpu::PipelinePointer getOpaquePipeline();
134};
135
136using DrawBackgroundDeferredConfig = render::GPUJobConfig;
137
138class DrawBackgroundDeferred {
139public:
140 using Inputs = render::VaryingSet2 <render::ItemBounds, LightingModelPointer>;
141
142 using Config = DrawBackgroundDeferredConfig;
143 using JobModel = render::Job::ModelI<DrawBackgroundDeferred, Inputs, Config>;
144
145 void configure(const Config& config) {}
146 void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
147
148protected:
149 gpu::RangeTimerPointer _gpuTimer;
150};
151
152class DrawOverlay3DConfig : public render::Job::Config {123class DrawOverlay3DConfig : public render::Job::Config {
153 Q_OBJECT124 Q_OBJECT
154 Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)125 Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
libraries/render-utils/src/RenderPipelines.cpp+2−0
@@ -15,6 +15,7 @@ · 14 unmodified lines
15#include <gpu/Context.h>15#include <gpu/Context.h>
16#include <gpu/StandardShaderLib.h>16#include <gpu/StandardShaderLib.h>
17 17
18#include "StencilMaskPass.h"
18#include "DeferredLightingEffect.h"19#include "DeferredLightingEffect.h"
19#include "TextureCache.h"20#include "TextureCache.h"
20#include "render/DrawTask.h"21#include "render/DrawTask.h"
@@ -330,6 +331,7 @@ void addPlumberPipeline(ShapePlumber& plumber, · 309 unmodified lines
330 bool isWireframed = (i & 4);331 bool isWireframed = (i & 4);
331 332
332 auto state = std::make_shared<gpu::State>();333 auto state = std::make_shared<gpu::State>();
334 PrepareStencil::testMaskDrawShape(*state);
333 335
334 // Depth test depends on transparency336 // Depth test depends on transparency
335 state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL);337 state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL);
libraries/render-utils/src/RenderShadowTask.cpp+3−2
@@ -48,6 +48,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, · 47 unmodified lines
48 RenderArgs* args = renderContext->args;48 RenderArgs* args = renderContext->args;
49 gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {49 gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
50 args->_batch = &batch;50 args->_batch = &batch;
51 batch.enableStereo(false);
51 52
52 glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()};53 glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()};
53 batch.setViewportTransform(viewport);54 batch.setViewportTransform(viewport);
@@ -114,7 +115,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende · 60 unmodified lines
114 skinProgram, state);115 skinProgram, state);
115 }116 }
116 117
117 const auto cachedMode = task.addJob<RenderShadowSetup>("Setup");118 const auto cachedMode = task.addJob<RenderShadowSetup>("ShadowSetup");
118 119
119 // CPU jobs:120 // CPU jobs:
120 // Fetch and cull the items from the scene121 // Fetch and cull the items from the scene
@@ -129,7 +130,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende · 8 unmodified lines
129 // GPU jobs: Render to shadow map130 // GPU jobs: Render to shadow map
130 task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);131 task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
131 132
132 task.addJob<RenderShadowTeardown>("Teardown", cachedMode);133 task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode);
133}134}
134 135
135void RenderShadowTask::configure(const Config& configuration) {136void RenderShadowTask::configure(const Config& configuration) {
libraries/render-utils/src/RenderViewTask.cpp+33−0
@@ -0,0 +1,33 @@
1//
2// RenderViewTask.cpp
3// render-utils/src/
4//
5// Created by Sam Gateau on 5/25/2017.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11#include "RenderViewTask.h"
12
13#include "RenderShadowTask.h"
14#include "RenderDeferredTask.h"
15#include "RenderForwardTask.h"
16
17
18
19void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) {
20 // auto items = input.get<Input>();
21
22 task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
23
24 const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
25 assert(items.canCast<RenderFetchCullSortTask::Output>());
26
27 if (isDeferred) {
28 task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
29 } else {
30 task.addJob<RenderForwardTask>("Forward", items);
31 }
32}
33
libraries/render-utils/src/RenderViewTask.h+31−0
@@ -0,0 +1,31 @@
1//
2// RenderViewTask.h
3// render-utils/src/
4//
5// Created by Sam Gateau on 5/25/2017.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11#pragma once
12#ifndef hifi_RenderViewTask_h
13#define hifi_RenderViewTask_h
14
15#include <render/Engine.h>
16#include <render/RenderFetchCullSortTask.h>
17
18
19class RenderViewTask {
20public:
21 using Input = RenderFetchCullSortTask::Output;
22 using JobModel = render::Task::ModelI<RenderViewTask, Input>;
23
24 RenderViewTask() {}
25
26 void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred);
27
28};
29
30
31#endif // hifi_RenderViewTask_h
libraries/render-utils/src/StencilMaskPass.cpp+126−0
@@ -0,0 +1,126 @@
1//
2// StencilMaskPass.cpp
3// render-utils/src/
4//
5// Created by Sam Gateau on 5/31/17.
6// Copyright 2016 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#include "StencilMaskPass.h"
13
14#include <RenderArgs.h>
15#include <ViewFrustum.h>
16#include <gpu/Context.h>
17
18
19#include <gpu/StandardShaderLib.h>
20
21#include "stencil_drawMask_frag.h"
22
23using namespace render;
24
25void PrepareStencil::configure(const Config& config) {
26 _maskMode = config.maskMode;
27 _forceDraw = config.forceDraw;
28}
29
30model::MeshPointer PrepareStencil::getMesh() {
31 if (!_mesh) {
32
33 std::vector<glm::vec3> vertices {
34 { -1.0f, -1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f },
35 { -1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f },
36 { 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f },
37 { 1.0f, -1.0f, 0.0f }, { 0.0f, -1.0f, 0.0f } };
38
39 std::vector<uint32_t> indices { 0, 7, 1, 1, 3, 2, 3, 5, 4, 5, 7, 6 };
40 _mesh = model::Mesh::createIndexedTriangles_P3F((uint32_t) vertices.size(), (uint32_t) indices.size(), vertices.data(), indices.data());
41 }
42 return _mesh;
43}
44
45gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() {
46 if (!_meshStencilPipeline) {
47 auto vs = gpu::StandardShaderLib::getDrawVertexPositionVS();
48 auto ps = gpu::StandardShaderLib::getDrawNadaPS();
49 auto program = gpu::Shader::createProgram(vs, ps);
50 gpu::Shader::makeProgram((*program));
51
52 auto state = std::make_shared<gpu::State>();
53 drawMask(*state);
54 state->setColorWriteMask(0);
55
56 _meshStencilPipeline = gpu::Pipeline::create(program, state);
57 }
58 return _meshStencilPipeline;
59}
60
61gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() {
62 if (!_paintStencilPipeline) {
63 auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
64 auto ps = gpu::Shader::createPixel(std::string(stencil_drawMask_frag));
65 auto program = gpu::Shader::createProgram(vs, ps);
66 gpu::Shader::makeProgram((*program));
67
68 auto state = std::make_shared<gpu::State>();
69 drawMask(*state);
70 state->setColorWriteMask(0);
71
72 _paintStencilPipeline = gpu::Pipeline::create(program, state);
73 }
74 return _paintStencilPipeline;
75}
76
77void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
78 RenderArgs* args = renderContext->args;
79
80 // Only draw the stencil mask if in HMD mode or not forced.
81 if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) {
82 return;
83 }
84
85 doInBatch(args->_context, [&](gpu::Batch& batch) {
86 batch.enableStereo(false);
87
88 batch.setViewportTransform(args->_viewport);
89
90 if (_maskMode < 0) {
91 batch.setPipeline(getMeshStencilPipeline());
92
93 auto mesh = getMesh();
94 batch.setIndexBuffer(mesh->getIndexBuffer());
95 batch.setInputFormat((mesh->getVertexFormat()));
96 batch.setInputStream(0, mesh->getVertexStream());
97
98 // Draw
99 auto part = mesh->getPartBuffer().get<model::Mesh::Part>(0);
100 batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
101 } else {
102 batch.setPipeline(getPaintStencilPipeline());
103 batch.draw(gpu::TRIANGLE_STRIP, 4);
104 }
105 });
106}
107
108void PrepareStencil::drawMask(gpu::State& state) {
109 state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE));
110}
111
112void PrepareStencil::testMask(gpu::State& state) {
113 state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
114}
115
116void PrepareStencil::testBackground(gpu::State& state) {
117 state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
118}
119
120void PrepareStencil::testMaskDrawShape(gpu::State& state) {
121 state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO));
122}
123
124void PrepareStencil::testShape(gpu::State& state) {
125 state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_SHAPE, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
126}
libraries/render-utils/src/StencilMaskPass.h+71−0
@@ -0,0 +1,71 @@
1//
2// StencilMaskPass.h
3// render-utils/src/
4//
5// Created by Sam Gateau on 5/31/17.
6// Copyright 20154 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11#pragma once
12#ifndef hifi_StencilMaskPass_h
13#define hifi_StencilMaskPass_h
14
15#include <render/Engine.h>
16#include <gpu/Pipeline.h>
17#include <model/Geometry.h>
18
19class PrepareStencilConfig : public render::Job::Config {
20 Q_OBJECT
21 Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty)
22 Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty)
23
24public:
25 PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {}
26
27 int maskMode { 0 };
28 bool forceDraw { false };
29
30signals:
31 void dirty();
32};
33
34class PrepareStencil {
35public:
36 using Config = PrepareStencilConfig;
37
38 using JobModel = render::Job::ModelI<PrepareStencil, gpu::FramebufferPointer, Config>;
39
40 void configure(const Config& config);
41
42 void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& dstFramebuffer);
43
44 static const gpu::int8 STENCIL_MASK = 2;
45 static const gpu::int8 STENCIL_BACKGROUND = 1;
46 static const gpu::int8 STENCIL_SHAPE = 0;
47
48
49 static void drawMask(gpu::State& state);
50 static void testMask(gpu::State& state);
51 static void testBackground(gpu::State& state);
52 static void testMaskDrawShape(gpu::State& state);
53 static void testShape(gpu::State& state);
54
55
56private:
57 gpu::PipelinePointer _meshStencilPipeline;
58 gpu::PipelinePointer getMeshStencilPipeline();
59
60 gpu::PipelinePointer _paintStencilPipeline;
61 gpu::PipelinePointer getPaintStencilPipeline();
62
63 model::MeshPointer _mesh;
64 model::MeshPointer getMesh();
65
66 int _maskMode { 0 };
67 bool _forceDraw { false };
68};
69
70
71#endif // hifi_StencilMaskPass_h
libraries/render-utils/src/SurfaceGeometryPass.cpp+4−3
@@ -14,7 +14,7 @@ · 13 unmodified lines
14 14
15#include <gpu/Context.h>15#include <gpu/Context.h>
16#include <gpu/StandardShaderLib.h>16#include <gpu/StandardShaderLib.h>
17 17#include "StencilMaskPass.h"
18 18
19const int DepthLinearPass_FrameTransformSlot = 0;19const int DepthLinearPass_FrameTransformSlot = 0;
20const int DepthLinearPass_DepthMapSlot = 0;20const int DepthLinearPass_DepthMapSlot = 0;
@@ -224,7 +224,7 @@ const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { · 203 unmodified lines
224 gpu::StatePointer state = gpu::StatePointer(new gpu::State());224 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
225 225
226 // Stencil test the curvature pass for objects pixels only, not the background226 // Stencil test the curvature pass for objects pixels only, not the background
227 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));227 PrepareStencil::testShape(*state);
228 228
229 state->setColorWriteMask(true, false, false, false);229 state->setColorWriteMask(true, false, false, false);
230 230
@@ -250,6 +250,7 @@ const gpu::PipelinePointer& LinearDepthPass::getDownsamplePipeline() { · 19 unmodified lines
250 250
251 251
252 gpu::StatePointer state = gpu::StatePointer(new gpu::State());252 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
253 PrepareStencil::testShape(*state);
253 254
254 state->setColorWriteMask(true, true, true, false);255 state->setColorWriteMask(true, true, true, false);
255 256
@@ -554,7 +555,7 @@ const gpu::PipelinePointer& SurfaceGeometryPass::getCurvaturePipeline() { · 298 unmodified lines
554 555
555#ifdef USE_STENCIL_TEST556#ifdef USE_STENCIL_TEST
556 // Stencil test the curvature pass for objects pixels only, not the background557 // Stencil test the curvature pass for objects pixels only, not the background
557 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));558 PrepareStencil::testShape(*state);
558#endif559#endif
559 // Good to go add the brand new pipeline560 // Good to go add the brand new pipeline
560 _curvaturePipeline = gpu::Pipeline::create(program, state);561 _curvaturePipeline = gpu::Pipeline::create(program, state);
libraries/render-utils/src/ToneMappingEffect.cpp+1−1
@@ -15,7 +15,7 @@ · 14 unmodified lines
15#include <gpu/StandardShaderLib.h>15#include <gpu/StandardShaderLib.h>
16 16
17#include <RenderArgs.h>17#include <RenderArgs.h>
18 18#include "StencilMaskPass.h"
19#include "FramebufferCache.h"19#include "FramebufferCache.h"
20 20
21#include "toneMapping_frag.h"21#include "toneMapping_frag.h"
libraries/render-utils/src/ZoneRenderer.cpp+188−3
@@ -10,18 +10,203 @@ · 9 unmodified lines
10//10//
11#include "ZoneRenderer.h"11#include "ZoneRenderer.h"
12 12
13
14#include <gpu/Context.h>
15#include <gpu/StandardShaderLib.h>
16
13#include <render/FilterTask.h>17#include <render/FilterTask.h>
14#include <render/DrawTask.h>18#include <render/DrawTask.h>
15 19
20#include "StencilMaskPass.h"
21#include "DeferredLightingEffect.h"
22
23#include "zone_drawKeyLight_frag.h"
24#include "zone_drawAmbient_frag.h"
25#include "zone_drawSkybox_frag.h"
26
27
16using namespace render;28using namespace render;
17 29
30class SetupZones {
31public:
32 using Inputs = render::ItemBounds;
33 using JobModel = render::Job::ModelI<SetupZones, Inputs>;
34
35 SetupZones() {}
36
37 void run(const RenderContextPointer& context, const Inputs& inputs);
38
39protected:
40};
41
18const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" };42const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" };
19 43
20void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& ouput) {44void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& ouput) {
21 45 // Filter out the sorted list of zones
22 const auto zoneItems = task.addJob<render::SelectSortItems>("FilterZones", input, ZONES_SELECTION.c_str());46 const auto zoneItems = task.addJob<render::SelectSortItems>("FilterZones", input, ZONES_SELECTION.c_str());
23 47
24 // just draw them...48 // just setup the current zone env
25 task.addJob<DrawBounds>("DrawZones", zoneItems);49 task.addJob<SetupZones>("SetupZones", zoneItems);
50
51 ouput = zoneItems;
52}
53
54void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) {
55
56 auto backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
57 backgroundStage->_currentFrame.clear();
58
59 // call render in the correct order first...
60 render::renderItems(context, inputs);
61
62}
63
64const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {
65 if (!_keyLightPipeline) {
66 auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
67 auto ps = gpu::Shader::createPixel(std::string(zone_drawKeyLight_frag));
68 gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
69
70 gpu::Shader::BindingSet slotBindings;
71 slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER));
72 slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), ZONE_KEYLIGHT_BUFFER));
73
74 gpu::Shader::makeProgram(*program, slotBindings);
75
76 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
77
78 PrepareStencil::testMask(*state);
79 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
80 _keyLightPipeline = gpu::Pipeline::create(program, state);
81 }
82 return _keyLightPipeline;
83}
84
85const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() {
86 if (!_ambientPipeline) {
87 auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
88 auto ps = gpu::Shader::createPixel(std::string(zone_drawAmbient_frag));
89 gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
90
91 gpu::Shader::BindingSet slotBindings;
92 slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER));
93 slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), ZONE_AMBIENT_BUFFER));
94 slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), ZONE_AMBIENT_MAP));
95
96 gpu::Shader::makeProgram(*program, slotBindings);
97
98 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
99
100 PrepareStencil::testMask(*state);
101 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
102 _ambientPipeline = gpu::Pipeline::create(program, state);
103 }
104 return _ambientPipeline;
26}105}
106const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() {
107 if (!_backgroundPipeline) {
108 auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
109 auto ps = gpu::Shader::createPixel(std::string(zone_drawSkybox_frag));
110 gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
111
112 gpu::Shader::BindingSet slotBindings;
113 slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER));
114 slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), ZONE_SKYBOX_MAP));
115 slotBindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), ZONE_SKYBOX_BUFFER));
116
117 gpu::Shader::makeProgram(*program, slotBindings);
118
119 gpu::StatePointer state = gpu::StatePointer(new gpu::State());
120
121 PrepareStencil::testMask(*state);
122 state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
123 _backgroundPipeline = gpu::Pipeline::create(program, state);
124 }
125 return _backgroundPipeline;
126}
127
128void DebugZoneLighting::run(const render::RenderContextPointer& context, const Inputs& inputs) {
129 RenderArgs* args = context->args;
130
131 auto deferredTransform = inputs;
132
133 auto lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
134 std::vector<model::LightPointer> keyLightStack;
135 if (lightStage && lightStage->_currentFrame._sunLights.size()) {
136 for (auto index : lightStage->_currentFrame._sunLights) {
137 keyLightStack.push_back(lightStage->getLight(index));
138 }
139 }
140 keyLightStack.push_back(DependencyManager::get<DeferredLightingEffect>()->getGlobalLight());
27 141
142 std::vector<model::LightPointer> ambientLightStack;
143 if (lightStage && lightStage->_currentFrame._ambientLights.size()) {
144 for (auto index : lightStage->_currentFrame._ambientLights) {
145 ambientLightStack.push_back(lightStage->getLight(index));
146 }
147 }
148 ambientLightStack.push_back(DependencyManager::get<DeferredLightingEffect>()->getGlobalLight());
149
150
151 auto backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
152 std::vector<model::SkyboxPointer> skyboxStack;
153 if (backgroundStage && backgroundStage->_currentFrame._backgrounds.size()) {
154 for (auto index : backgroundStage->_currentFrame._backgrounds) {
155 auto background = backgroundStage->getBackground(index);
156 if (background) {
157 skyboxStack.push_back(background->getSkybox());
158 }
159 }
160 }
161 skyboxStack.push_back(DependencyManager::get<DeferredLightingEffect>()->getDefaultSkybox());
162
163
164 gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
165
166 batch.setViewportTransform(args->_viewport);
167 auto viewFrustum = args->getViewFrustum();
168 batch.setProjectionTransform(viewFrustum.getProjection());
169 batch.resetViewTransform();
170
171 Transform model;
172
173 batch.setUniformBuffer(ZONE_DEFERRED_TRANSFORM_BUFFER, deferredTransform->getFrameTransformBuffer());
174
175 batch.setPipeline(getKeyLightPipeline());
176 auto numKeys = (int) keyLightStack.size();
177 for (int i = numKeys - 1; i >= 0; i--) {
178 model.setTranslation(glm::vec3(-4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
179 batch.setModelTransform(model);
180 if (keyLightStack[i]) {
181 batch.setUniformBuffer(ZONE_KEYLIGHT_BUFFER, keyLightStack[i]->getLightSchemaBuffer());
182 batch.draw(gpu::TRIANGLE_STRIP, 4);
183 }
184 }
185
186 batch.setPipeline(getAmbientPipeline());
187 auto numAmbients = (int) ambientLightStack.size();
188 for (int i = numAmbients - 1; i >= 0; i--) {
189 model.setTranslation(glm::vec3(0.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
190 batch.setModelTransform(model);
191 if (ambientLightStack[i]) {
192 batch.setUniformBuffer(ZONE_AMBIENT_BUFFER, ambientLightStack[i]->getAmbientSchemaBuffer());
193 if (ambientLightStack[i]->getAmbientMap()) {
194 batch.setResourceTexture(ZONE_AMBIENT_MAP, ambientLightStack[i]->getAmbientMap());
195 }
196 batch.draw(gpu::TRIANGLE_STRIP, 4);
197 }
198 }
199
200 batch.setPipeline(getBackgroundPipeline());
201 auto numBackgrounds = (int) skyboxStack.size();
202 for (int i = numBackgrounds - 1; i >= 0; i--) {
203 model.setTranslation(glm::vec3(4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
204 batch.setModelTransform(model);
205 if (skyboxStack[i]) {
206 batch.setResourceTexture(ZONE_SKYBOX_MAP, skyboxStack[i]->getCubemap());
207 batch.setUniformBuffer(ZONE_SKYBOX_BUFFER, skyboxStack[i]->getSchemaBuffer());
208 batch.draw(gpu::TRIANGLE_STRIP, 4);
209 }
210 }
211 });
212}
libraries/render-utils/src/ZoneRenderer.h+40−1
@@ -14,12 +14,15 @@ · 13 unmodified lines
14 14
15#include "render/Engine.h"15#include "render/Engine.h"
16 16
17#include "DeferredFrameTransform.h"
18
17class ZoneRendererConfig : public render::Task::Config {19class ZoneRendererConfig : public render::Task::Config {
18 Q_OBJECT20 Q_OBJECT
19 Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)21 Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
20public:22public:
21 23
22 ZoneRendererConfig() : render::Task::Config(false) {}24 ZoneRendererConfig() : render::Task::Config(
25 ) {}
23 26
24 int maxDrawn { -1 };27 int maxDrawn { -1 };
25 28
@@ -49,4 +52,40 @@ protected: · 23 unmodified lines
49 int _maxDrawn; // initialized by Config52 int _maxDrawn; // initialized by Config
50};53};
51 54
55class DebugZoneLighting {
56public:
57 class Config : public render::JobConfig {
58 public:
59 Config(bool enabled = false) : JobConfig(enabled) {}
60 };
61
62 using Inputs = DeferredFrameTransformPointer;
63 using JobModel = render::Job::ModelI<DebugZoneLighting, Inputs, Config>;
64
65 DebugZoneLighting() {}
66
67 void configure(const Config& configuration) {}
68 void run(const render::RenderContextPointer& context, const Inputs& inputs);
69
70protected:
71
72 enum Slots {
73 ZONE_DEFERRED_TRANSFORM_BUFFER = 0,
74 ZONE_KEYLIGHT_BUFFER,
75 ZONE_AMBIENT_BUFFER,
76 ZONE_AMBIENT_MAP,
77 ZONE_SKYBOX_BUFFER,
78 ZONE_SKYBOX_MAP,
79 };
80
81 gpu::PipelinePointer _keyLightPipeline;
82 gpu::PipelinePointer _ambientPipeline;
83 gpu::PipelinePointer _backgroundPipeline;
84
85 const gpu::PipelinePointer& getKeyLightPipeline();
86 const gpu::PipelinePointer& getAmbientPipeline();
87 const gpu::PipelinePointer& getBackgroundPipeline();
88};
89
90
52#endif91#endif
libraries/render-utils/src/directional_light.slf+0−47
@@ -1,47 +0,0 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// directional_light.frag
6// fragment shader
7//
8// Created by Andrzej Kapolka on 9/3/14.
9// Copyright 2016 High Fidelity, Inc.
10//
11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//
14
15<@include DeferredBufferRead.slh@>
16<@include DeferredGlobalLight.slh@>
17
18<$declareEvalLightmappedColor()$>
19<$declareEvalAmbientGlobalColor()$>
20
21in vec2 _texCoord0;
22out vec4 _fragColor;
23
24void main(void) {
25 DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
26 DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
27
28 float shadowAttenuation = 1.0;
29
30 if (frag.mode == FRAG_MODE_UNLIT) {
31 discard;
32 } else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
33 discard;
34 } else {
35 vec3 color = evalAmbientGlobalColor(
36 getViewInverse(),
37 shadowAttenuation,
38 frag.obscurance,
39 frag.position.xyz,
40 frag.normal,
41 frag.albedo,
42 frag.fresnel,
43 frag.metallic,
44 frag.roughness);
45 _fragColor = vec4(color, 1.0);
46 }
47}
libraries/render-utils/src/directional_light_shadow.slf+0−49
@@ -1,49 +0,0 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// directional_light_shadow.frag
6// fragment shader
7//
8// Created by Zach Pomerantz on 1/18/2016.
9// Copyright 2016 High Fidelity, Inc.
10//
11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//
14
15<@include Shadow.slh@>
16<@include DeferredBufferRead.slh@>
17<@include DeferredGlobalLight.slh@>
18
19<$declareEvalLightmappedColor()$>
20<$declareEvalAmbientGlobalColor()$>
21
22in vec2 _texCoord0;
23out vec4 _fragColor;
24
25void main(void) {
26 DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
27 DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
28
29 vec4 worldPos = getViewInverse() * vec4(frag.position.xyz, 1.0);
30 float shadowAttenuation = evalShadowAttenuation(worldPos);
31
32 if (frag.mode == FRAG_MODE_UNLIT) {
33 discard;
34 } else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
35 discard;
36 } else {
37 vec3 color = evalAmbientGlobalColor(
38 getViewInverse(),
39 shadowAttenuation,
40 frag.obscurance,
41 frag.position.xyz,
42 frag.normal,
43 frag.albedo,
44 frag.fresnel,
45 frag.metallic,
46 frag.roughness);
47 _fragColor = vec4(color, 1.0);
48 }
49}
libraries/render-utils/src/hit_effect.slf+0−27
@@ -1,27 +0,0 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5
6// hit_effect.frag
7// fragment shader
8//
9// Created by Eric Levin on 7/20
10// Copyright 2015 High Fidelity, Inc.
11//
12// Distributed under the Apache License, Version 2.0.
13// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
14//
15
16
17<@include DeferredBufferWrite.slh@>
18
19in vec2 varQuadPosition;
20out vec4 outFragColor;
21
22void main(void) {
23 vec2 center = vec2(0.0, 0.0);
24 float distFromCenter = distance( vec2(0.0, 0.0), varQuadPosition);
25 float alpha = mix(0.0, 0.5, pow(distFromCenter,5.));
26 outFragColor = vec4(1.0, 0.0, 0.0, alpha);
27}
libraries/render-utils/src/point_light.slf+0−85
@@ -1,85 +0,0 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// point_light.frag
6// fragment shader
7//
8// Created by Sam Gateau on 9/18/15.
9// Copyright 2014 High Fidelity, Inc.
10//
11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//
14<!
15// Everything about deferred buffer
16<@include DeferredBufferRead.slh@>
17
18<$declareDeferredCurvature()$>
19
20// Everything about light
21<@include model/Light.slh@>
22<$declareLightBuffer()$>
23
24<@include LightingModel.slh@>
25
26<@include LightPoint.slh@>
27<$declareLightingPoint(supportScattering)$>
28
29
30uniform vec4 texcoordFrameTransform;
31
32in vec4 _texCoord0;!>
33out vec4 _fragColor;
34
35void main(void) {
36 _fragColor = vec4(1.0, 1.0, 1.0, 1.0);
37
38 <!
39 DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
40
41 // Grab the fragment data from the uv
42 vec2 texCoord = _texCoord0.st / _texCoord0.q;
43 texCoord *= texcoordFrameTransform.zw;
44 texCoord += texcoordFrameTransform.xy;
45
46 DeferredFragment frag = unpackDeferredFragment(deferredTransform, texCoord);
47
48 if (frag.mode == FRAG_MODE_UNLIT) {
49 discard;
50 }
51
52 // Need the light now
53 Light light = getLight();
54
55 // Frag pos in world
56 mat4 invViewMat = getViewInverse();
57 vec4 fragPos = invViewMat * frag.position;
58
59 // Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
60 vec4 fragLightVecLen2;
61 if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
62 discard;
63 }
64
65 // Frag to eye vec
66 vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
67 vec3 fragEyeDir = normalize(fragEyeVector.xyz);
68
69
70 vec3 diffuse;
71 vec3 specular;
72 vec4 midNormalCurvature;
73 vec4 lowNormalCurvature;
74 if (frag.mode == FRAG_MODE_SCATTERING) {
75 unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
76 }
77 evalLightingPoint(diffuse, specular, light,
78 fragLightVecLen2.xyz, fragEyeDir, frag.normal, frag.roughness,
79 frag.metallic, frag.fresnel, frag.albedo, 1.0,
80 frag.scattering, midNormalCurvature, lowNormalCurvature);
81
82 _fragColor.rgb += diffuse;
83 _fragColor.rgb += specular;
84 !>
85}
libraries/render-utils/src/spot_light.slf+0−115
@@ -1,115 +0,0 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// spot_light.frag
6// fragment shader
7//
8// Created by Sam Gateau on 9/18/15.
9// Copyright 2014 High Fidelity, Inc.
10//
11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//
14
15// Everything about deferred buffer
16<!<@include DeferredBufferRead.slh@>
17
18<$declareDeferredCurvature()$>
19
20// Everything about light
21<@include model/Light.slh@>
22<$declareLightBuffer(256)$>
23uniform lightIndexBuffer {
24 int lightIndex[256];
25};
26<@include LightingModel.slh@>
27
28<@include LightPoint.slh@>
29<$declareLightingPoint(supportScattering)$>
30<@include LightSpot.slh@>
31<$declareLightingSpot(supportScattering)$>
32
33//uniform vec4 texcoordFrameTransform;
34!>
35
36
37//in vec4 _texCoord0;
38//flat in int instanceID;
39out vec4 _fragColor;
40
41void main(void) {
42 _fragColor = vec4(1.0, 1.0, 1.0, 1.0);
43
44// DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
45
46 // Grab the fragment data from the uv
47 //vec2 texCoord = _texCoord0.st;/* / _texCoord0.q;
48 /*texCoord *= texcoordFrameTransform.zw;
49 texCoord += texcoordFrameTransform.xy;*/
50 /*
51 vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord);
52 DeferredFragment frag = unpackDeferredFragmentNoPosition(texCoord);
53
54 if (frag.mode == FRAG_MODE_UNLIT) {
55 discard;
56 }
57
58
59 // frag.depthVal = depthValue;
60 frag.position = fragPosition;
61
62 vec4 midNormalCurvature;
63 vec4 lowNormalCurvature;
64 if (frag.mode == FRAG_MODE_SCATTERING) {
65 unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
66 }
67
68 // Frag pos in world
69 mat4 invViewMat = getViewInverse();
70 vec4 fragPos = invViewMat * fragPosition;
71
72 // Frag to eye vec
73 vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
74 vec3 fragEyeDir = normalize(fragEyeVector.xyz);
75
76 int numLights = lightIndex[0];
77 for (int i = 0; i < numLights; i++) {
78 // Need the light now
79 Light light = getLight(lightIndex[i + 1]);
80 bool isSpot = light_isSpot(light);
81 // Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
82 vec4 fragLightVecLen2;
83 vec4 fragLightDirLen;
84 float cosSpotAngle;
85 if (isSpot) {
86 if (!clipFragToLightVolumeSpot(light, fragPos.xyz, fragLightVecLen2, fragLightDirLen, cosSpotAngle)) {
87 continue;
88 }
89 } else {
90 if (!clipFragToLightVolumePoint(light, fragPos.xyz, fragLightVecLen2)) {
91 continue;
92 }
93 }
94
95 vec3 diffuse;
96 vec3 specular;
97
98 if (isSpot) {
99 evalLightingSpot(diffuse, specular, light,
100 fragLightDirLen.xyzw, cosSpotAngle, fragEyeDir, frag.normal, frag.roughness,
101 frag.metallic, frag.fresnel, frag.albedo, 1.0,
102 frag.scattering, midNormalCurvature, lowNormalCurvature);
103 } else {
104 evalLightingPoint(diffuse, specular, light,
105 fragLightVecLen2.xyz, fragEyeDir, frag.normal, frag.roughness,
106 frag.metallic, frag.fresnel, frag.albedo, 1.0,
107 frag.scattering, midNormalCurvature, lowNormalCurvature);
108 }
109
110 _fragColor.rgb += diffuse;
111 _fragColor.rgb += specular;
112 }
113 */
114}
115
libraries/render-utils/src/stencil_drawMask.slf+23−0
@@ -0,0 +1,23 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// stencil_drawMask.slf
6// fragment shader
7//
8// Created by Sam Gateau on 5/31/17.
9// Copyright 2017 High Fidelity, Inc.
10//
11// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//
14
15in vec2 varTexCoord0;
16
17float aspectRatio = 0.95;
18
19void main(void) {
20 vec2 pos = varTexCoord0 * 2.0 - vec2(1.0);
21 pos.x = aspectRatio * (pos.x * (pos.x > 0.0 ? 2.0 : -2.0) - 1.0);
22 if (1.0 - dot(pos.xy, pos.xy) > 0.0 ) discard;
23}
libraries/render-utils/src/text/Font.cpp+3−0
@@ -14,6 +14,8 @@ · 13 unmodified lines
14#include "../RenderUtilsLogging.h"14#include "../RenderUtilsLogging.h"
15#include "FontFamilies.h"15#include "FontFamilies.h"
16 16
17static std::mutex fontMutex;
18
17struct TextureVertex {19struct TextureVertex {
18 glm::vec2 pos;20 glm::vec2 pos;
19 glm::vec2 tex;21 glm::vec2 tex;
@@ -56,6 +58,7 @@ Font::Pointer Font::load(QIODevice& fontFile) { · 36 unmodified lines
56}58}
57 59
58Font::Pointer Font::load(const QString& family) {60Font::Pointer Font::load(const QString& family) {
61 std::lock_guard<std::mutex> lock(fontMutex);
59 if (!LOADED_FONTS.contains(family)) {62 if (!LOADED_FONTS.contains(family)) {
60 63
61 static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" };64 static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" };
libraries/render-utils/src/text/Font.h+1−1
@@ -31,10 +31,10 @@ public: · 30 unmodified lines
31 const glm::vec4* color, EffectType effectType,31 const glm::vec4* color, EffectType effectType,
32 const glm::vec2& bound, bool layered = false);32 const glm::vec2& bound, bool layered = false);
33 33
34 static Pointer load(QIODevice& fontFile);
35 static Pointer load(const QString& family);34 static Pointer load(const QString& family);
36 35
37private:36private:
37 static Pointer load(QIODevice& fontFile);
38 QStringList tokenizeForWrapping(const QString& str) const;38 QStringList tokenizeForWrapping(const QString& str) const;
39 QStringList splitLines(const QString& str) const;39 QStringList splitLines(const QString& str) const;
40 glm::vec2 computeTokenExtent(const QString& str) const;40 glm::vec2 computeTokenExtent(const QString& str) const;
libraries/render-utils/src/zone_draw.slh+37−0
@@ -0,0 +1,37 @@
1
2// Generated on <$_SCRIBE_DATE$>
3//
4// Created by Sam Gateau on 5/17/17.
5// Copyright 2017 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9//
10
11<@include DeferredTransform.slh@>
12<$declareDeferredFrameTransform()$>
13
14<@func evalGlobeWidget()@>
15 const float SCOPE_RADIUS = 1.0;
16 const float SCOPE_RADIUS2 = SCOPE_RADIUS * SCOPE_RADIUS;
17 const float EDGE_HALFWIDTH = 0.025;
18 const float EDGE_HALFWIDTH2 = EDGE_HALFWIDTH * EDGE_HALFWIDTH;
19 const float OUT_RADIUS = SCOPE_RADIUS + EDGE_HALFWIDTH;
20
21 vec2 sphereUV = (varTexCoord0.xy * 2.0 - vec2(1.0)) * OUT_RADIUS;
22 float sphereR2 = dot(sphereUV.xy, sphereUV.xy);
23 if (sphereR2 > OUT_RADIUS * OUT_RADIUS) {
24 discard;
25 }
26 float sphereR = sqrt(sphereR2);
27
28 float edgeFalloff = (SCOPE_RADIUS - sphereR) / (EDGE_HALFWIDTH);
29 float edgeFalloff2 = min(1.0, edgeFalloff * edgeFalloff);
30
31 vec4 base = vec4(0.0, 0.0, 0.0, 1.0 - edgeFalloff2);
32 if (sphereR2 > SCOPE_RADIUS2) {
33 _fragColor = base;
34 return;
35 }
36<@endfunc@>
37
libraries/render-utils/src/zone_drawAmbient.slf+54−0
@@ -0,0 +1,54 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// Created by Sam Gateau on 5/16/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11<@include zone_draw.slh@>
12
13<@include model/Light.slh@>
14
15<@include LightingModel.slh@>
16<$declareLightAmbientBuffer()$>
17
18<@include LightAmbient.slh@>
19
20<$declareLightingAmbient(_SCRIBE_NULL, 1, _SCRIBE_NULL, _SCRIBE_NULL)$>
21
22in vec2 varTexCoord0;
23out vec4 _fragColor;
24
25void main(void) {
26
27 <$evalGlobeWidget()$>
28
29 vec3 spherePos = normalize(vec3(sphereUV, sqrt(1.0 - sphereR2)));
30
31
32 vec3 fragNormal = vec3(getViewInverse() * vec4(spherePos, 0.0));
33
34
35 LightAmbient lightAmbient = getLightAmbient();
36
37
38 float roughness = 0.1;
39 float levels = getLightAmbientMapNumMips(lightAmbient);
40 float lod = min(((roughness)* levels), levels);
41 vec3 ambientMap = evalSkyboxLight(fragNormal, lod).xyz;
42 vec3 ambientSH = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(lightAmbient), fragNormal).xyz;
43
44 // vec3 ambient = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(lightAmbient), fragNormal).xyz;
45 // _fragColor = vec4( 0.5 * (fragNormal + vec3(1.0)), 1.0);
46
47 vec3 color = (sphereUV.x > 0 ? ambientMap : ambientSH);
48
49 color = color * 1.0 - base.w + base.xyz * base.w;
50 const float INV_GAMMA_22 = 1.0 / 2.2;
51 _fragColor = vec4(pow(color, vec3(INV_GAMMA_22)), 1.0);
52}
53
54
libraries/render-utils/src/zone_drawKeyLight.slf+57−0
@@ -0,0 +1,57 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// Created by Sam Gateau on 5/16/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11<@include zone_draw.slh@>
12
13<@include model/Light.slh@>
14
15<@include LightingModel.slh@>
16<$declareLightBuffer()$>
17
18<@include LightDirectional.slh@>
19<$declareLightingDirectional(_SCRIBE_NULL)$>
20
21in vec2 varTexCoord0;
22out vec4 _fragColor;
23
24void main(void) {
25
26 <$evalGlobeWidget()$>
27
28 Light light = getLight();
29 vec3 lightDirection = normalize(getLightDirection(light));
30 vec3 lightIrradiance = getLightIrradiance(light);
31 vec3 color = vec3(0.0);
32
33 const float INOUT_RATIO = 0.4;
34 const float SUN_THRESHOLD = 0.99;
35
36 vec3 outSpherePos = normalize(vec3(sphereUV, -sqrt(1.0 - sphereR2)));
37 vec3 outNormal = vec3(getViewInverse() * vec4(outSpherePos, 0.0));
38
39 float val = step(SUN_THRESHOLD, dot(-lightDirection, outNormal));
40
41 color = lightIrradiance * vec3(val);
42
43 if (sphereR2 < INOUT_RATIO * INOUT_RATIO * SCOPE_RADIUS2) {
44 vec2 inSphereUV = sphereUV / INOUT_RATIO;
45 vec3 inSpherePos = normalize(vec3(inSphereUV, sqrt(1.0 - dot(inSphereUV.xy, inSphereUV.xy))));
46 vec3 inNormal = vec3(getViewInverse() * vec4(inSpherePos, 0.0));
47
48 vec3 marbleColor = max(lightIrradiance * vec3(dot(-lightDirection, inNormal)), vec3(0.01));
49 color += marbleColor;
50 }
51
52 color = color * 1.0 - base.w + base.xyz * base.w;
53 const float INV_GAMMA_22 = 1.0 / 2.2;
54 _fragColor = vec4(pow(color, vec3(INV_GAMMA_22)), 1.0);
55}
56
57
libraries/render-utils/src/zone_drawSkybox.slf+48−0
@@ -0,0 +1,48 @@
1<@include gpu/Config.slh@>
2<$VERSION_HEADER$>
3// Generated on <$_SCRIBE_DATE$>
4//
5// Created by Sam Gateau on 5/16/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11<@include zone_draw.slh@>
12
13uniform samplerCube skyboxMap;
14
15struct Skybox {
16 vec4 color;
17};
18
19uniform skyboxBuffer {
20 Skybox skybox;
21};
22
23in vec2 varTexCoord0;
24out vec4 _fragColor;
25
26void main(void) {
27 <$evalGlobeWidget()$>
28
29 vec3 spherePos = normalize(vec3(sphereUV, -sqrt(1.0 - sphereR2)));
30
31 vec3 direction = vec3(getViewInverse() * vec4(spherePos, 0.0));
32
33 vec3 color = skybox.color.rgb;
34
35 // blend is only set if there is a cubemap
36 if (skybox.color.a > 0.0) {
37 color = texture(skyboxMap, direction).rgb;
38 if (skybox.color.a < 1.0) {
39 color *= skybox.color.rgb;
40 }
41 }
42
43 color = color * 1.0 - base.w + base.xyz * base.w;
44 const float INV_GAMMA_22 = 1.0 / 2.2;
45 _fragColor = vec4(pow(color, vec3(INV_GAMMA_22)), 1.0);
46}
47
48
libraries/render/src/render/EngineStats.cpp+0−2
@@ -63,6 +63,4 @@ void EngineStats::run(const RenderContextPointer& renderContext) { · 62 unmodified lines
63 63
64 config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines;64 config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines;
65 config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges;65 config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges;
66
67 config->emitDirty();
68}66}
libraries/render/src/task/Config.cpp+2−0
@@ -34,6 +34,7 @@ void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::strin · 33 unmodified lines
34 if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {34 if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {
35 // Connect dirty->refresh if defined35 // Connect dirty->refresh if defined
36 QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh()));36 QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh()));
37 QObject::connect(childConfig.get(), SIGNAL(dirtyEnabled()), this, SLOT(refresh()));
37 }38 }
38}39}
39 40
@@ -50,6 +51,7 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) { · 10 unmodified lines
50 if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {51 if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {
51 // Connect dirty->refresh if defined52 // Connect dirty->refresh if defined
52 QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh()));53 QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh()));
54 QObject::connect(child, SIGNAL(dirtyEnabled()), this, SLOT(refresh()));
53 }55 }
54 }56 }
55}57}
libraries/render/src/task/Config.h+3−2
@@ -89,7 +89,7 @@ protected: · 88 unmodified lines
89class JobConfig : public QObject {89class JobConfig : public QObject {
90 Q_OBJECT90 Q_OBJECT
91 Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms91 Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms
92 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)92 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY dirtyEnabled())
93 93
94 double _msCPURunTime{ 0.0 };94 double _msCPURunTime{ 0.0 };
95public:95public:
@@ -99,7 +99,7 @@ public: · 3 unmodified lines
99 JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {}99 JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {}
100 100
101 bool isEnabled() { return alwaysEnabled || enabled; }101 bool isEnabled() { return alwaysEnabled || enabled; }
102 void setEnabled(bool enable) { enabled = alwaysEnabled || enable; }102 void setEnabled(bool enable) { enabled = alwaysEnabled || enable; emit dirtyEnabled(); }
103 103
104 bool alwaysEnabled{ true };104 bool alwaysEnabled{ true };
105 bool enabled{ true };105 bool enabled{ true };
@@ -121,6 +121,7 @@ public slots: · 15 unmodified lines
121signals:121signals:
122 void loaded();122 void loaded();
123 void newStats();123 void newStats();
124 void dirtyEnabled();
124};125};
125 126
126class TaskConfig : public JobConfig {127class TaskConfig : public JobConfig {
libraries/render/src/task/Task.h+1−0
@@ -170,6 +170,7 @@ protected: · 169 unmodified lines
170 std::string _name = "";170 std::string _name = "";
171};171};
172 172
173
173// A task is a specialized job to run a collection of other jobs174// A task is a specialized job to run a collection of other jobs
174// It can be created on any type T by aliasing the type JobModel in the class T175// It can be created on any type T by aliasing the type JobModel in the class T
175// using JobModel = Task::Model<T>176// using JobModel = Task::Model<T>
libraries/script-engine/src/TabletScriptingInterface.cpp+27−19
@@ -542,7 +542,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS · 541 unmodified lines
542 542
543QObject* TabletProxy::addButton(const QVariant& properties) {543QObject* TabletProxy::addButton(const QVariant& properties) {
544 auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));544 auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
545 std::lock_guard<std::mutex> guard(_tabletMutex);545 std::unique_lock<std::mutex> guard(_tabletMutex);
546 _tabletButtonProxies.push_back(tabletButtonProxy);546 _tabletButtonProxies.push_back(tabletButtonProxy);
547 if (!_toolbarMode && _qmlTabletRoot) {547 if (!_toolbarMode && _qmlTabletRoot) {
548 auto tablet = getQmlTablet();548 auto tablet = getQmlTablet();
@@ -552,7 +552,6 @@ QObject* TabletProxy::addButton(const QVariant& properties) { · 3 unmodified lines
552 qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";552 qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
553 }553 }
554 } else if (_toolbarMode) {554 } else if (_toolbarMode) {
555
556 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();555 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
557 QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();556 QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
558 557
@@ -561,6 +560,8 @@ QObject* TabletProxy::addButton(const QVariant& properties) { · 2 unmodified lines
561 connectionType = Qt::BlockingQueuedConnection;560 connectionType = Qt::BlockingQueuedConnection;
562 }561 }
563 562
563 guard.unlock();
564
564 // copy properties from tablet button proxy to toolbar button proxy.565 // copy properties from tablet button proxy to toolbar button proxy.
565 QObject* toolbarButtonProxy = nullptr;566 QObject* toolbarButtonProxy = nullptr;
566 bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties()));567 bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties()));
@@ -578,31 +579,38 @@ bool TabletProxy::onHomeScreen() { · 11 unmodified lines
578}579}
579 580
580void TabletProxy::removeButton(QObject* tabletButtonProxy) {581void TabletProxy::removeButton(QObject* tabletButtonProxy) {
581 std::lock_guard<std::mutex> guard(_tabletMutex);582 std::unique_lock<std::mutex> guard(_tabletMutex);
582 583
583 auto tablet = getQmlTablet();584 auto tablet = getQmlTablet();
584 if (!tablet) {585 if (!tablet) {
585 qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";586 qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
586 }587 }
587 588
588 auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);589 QSharedPointer<TabletButtonProxy> buttonProxy;
589 if (iter != _tabletButtonProxies.end()) {590 {
590 if (!_toolbarMode && _qmlTabletRoot) {591 auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);
591 (*iter)->setQmlButton(nullptr);592 if (iter == _tabletButtonProxies.end()) {
592 if (tablet) {593 qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
593 QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties()));594 return;
594 }
595 } else if (_toolbarMode) {
596 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
597 QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
598
599 // remove button from toolbarProxy
600 QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getUuid().toString()));
601 (*iter)->setToolbarButtonProxy(nullptr);
602 }595 }
596 buttonProxy = *iter;
603 _tabletButtonProxies.erase(iter);597 _tabletButtonProxies.erase(iter);
604 } else {598 }
605 qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;599
600 if (!_toolbarMode && _qmlTabletRoot) {
601 buttonProxy->setQmlButton(nullptr);
602 if (tablet) {
603 guard.unlock();
604 QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties()));
605 }
606 } else if (_toolbarMode) {
607 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
608 QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
609
610 // remove button from toolbarProxy
611 guard.unlock();
612 QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString()));
613 buttonProxy->setToolbarButtonProxy(nullptr);
606 }614 }
607}615}
608 616
libraries/shared/src/CubicHermiteSpline.h+113−0
@@ -0,0 +1,113 @@
1//
2// CubicHermiteSpline.h
3//
4// Copyright 2017 High Fidelity, Inc.
5//
6// Distributed under the Apache License, Version 2.0.
7// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8//
9
10#ifndef hifi_CubicHermiteSpline_h
11#define hifi_CubicHermiteSpline_h
12
13#include "GLMHelpers.h"
14
15class CubicHermiteSplineFunctor {
16public:
17 CubicHermiteSplineFunctor() : _p0(), _m0(), _p1(), _m1() {}
18 CubicHermiteSplineFunctor(const glm::vec3& p0, const glm::vec3& m0, const glm::vec3& p1, const glm::vec3& m1) : _p0(p0), _m0(m0), _p1(p1), _m1(m1) {}
19
20 CubicHermiteSplineFunctor(const CubicHermiteSplineFunctor& orig) : _p0(orig._p0), _m0(orig._m0), _p1(orig._p1), _m1(orig._m1) {}
21
22 virtual ~CubicHermiteSplineFunctor() {}
23
24 // evalute the hermite curve at parameter t (0..1)
25 glm::vec3 operator()(float t) const {
26 float t2 = t * t;
27 float t3 = t2 * t;
28 float w0 = 2.0f * t3 - 3.0f * t2 + 1.0f;
29 float w1 = t3 - 2.0f * t2 + t;
30 float w2 = -2.0f * t3 + 3.0f * t2;
31 float w3 = t3 - t2;
32 return w0 * _p0 + w1 * _m0 + w2 * _p1 + w3 * _m1;
33 }
34
35 // evaulate the first derivative of the hermite curve at parameter t (0..1)
36 glm::vec3 d(float t) const {
37 float t2 = t * t;
38 float w0 = -6.0f * t + 6.0f * t2;
39 float w1 = 1.0f - 4.0f * t + 3.0f * t2;
40 float w2 = 6.0f * t - 6.0f * t2;
41 float w3 = -2.0f * t + 3.0f * t2;
42 return w0 * _p0 + w1 * _m0 + w2 * _p1 + w3 * _m1;
43 }
44
45 // evaulate the second derivative of the hermite curve at paramter t (0..1)
46 glm::vec3 d2(float t) const {
47 float w0 = -6.0f + 12.0f * t;
48 float w1 = -4.0f + 6.0f * t;
49 float w2 = 6.0f - 12.0f * t;
50 float w3 = -2.0f + 6.0f * t;
51 return w0 + _p0 + w1 * _m0 + w2 * _p1 + w3 * _m1;
52 }
53
54protected:
55 glm::vec3 _p0;
56 glm::vec3 _m0;
57 glm::vec3 _p1;
58 glm::vec3 _m1;
59};
60
61class CubicHermiteSplineFunctorWithArcLength : public CubicHermiteSplineFunctor {
62public:
63 enum Constants { NUM_SUBDIVISIONS = 30 };
64
65 CubicHermiteSplineFunctorWithArcLength() : CubicHermiteSplineFunctor() {
66 memset(_values, 0, sizeof(float) * (NUM_SUBDIVISIONS + 1));
67 }
68 CubicHermiteSplineFunctorWithArcLength(const glm::vec3& p0, const glm::vec3& m0, const glm::vec3& p1, const glm::vec3& m1) : CubicHermiteSplineFunctor(p0, m0, p1, m1) {
69 // initialize _values with the accumulated arcLength along the spline.
70 const float DELTA = 1.0f / NUM_SUBDIVISIONS;
71 float alpha = 0.0f;
72 float accum = 0.0f;
73 _values[0] = 0.0f;
74 for (int i = 1; i < NUM_SUBDIVISIONS + 1; i++) {
75 accum += glm::distance(this->operator()(alpha),
76 this->operator()(alpha + DELTA));
77 alpha += DELTA;
78 _values[i] = accum;
79 }
80 }
81
82 CubicHermiteSplineFunctorWithArcLength(const CubicHermiteSplineFunctorWithArcLength& orig) : CubicHermiteSplineFunctor(orig) {
83 memcpy(_values, orig._values, sizeof(float) * (NUM_SUBDIVISIONS + 1));
84 }
85
86 // given the spline parameter (0..1) output the arcLength of the spline up to that point.
87 float arcLength(float t) const {
88 float index = t * NUM_SUBDIVISIONS;
89 int prevIndex = std::min(std::max(0, (int)glm::floor(index)), (int)NUM_SUBDIVISIONS);
90 int nextIndex = std::min(std::max(0, (int)glm::ceil(index)), (int)NUM_SUBDIVISIONS);
91 float alpha = glm::fract(index);
92 return lerp(_values[prevIndex], _values[nextIndex], alpha);
93 }
94
95 // given an arcLength compute the spline parameter (0..1) that cooresponds to that arcLength.
96 float arcLengthInverse(float s) const {
97 // find first item in _values that is > s.
98 int nextIndex;
99 for (nextIndex = 0; nextIndex < NUM_SUBDIVISIONS; nextIndex++) {
100 if (_values[nextIndex] > s) {
101 break;
102 }
103 }
104 int prevIndex = std::min(std::max(0, nextIndex - 1), (int)NUM_SUBDIVISIONS);
105 float alpha = glm::clamp((s - _values[prevIndex]) / (_values[nextIndex] - _values[prevIndex]), 0.0f, 1.0f);
106 const float DELTA = 1.0f / NUM_SUBDIVISIONS;
107 return lerp(prevIndex * DELTA, nextIndex * DELTA, alpha);
108 }
109protected:
110 float _values[NUM_SUBDIVISIONS + 1];
111};
112
113#endif // hifi_CubicHermiteSpline_h
libraries/shared/src/Extents.h+1−1
@@ -69,7 +69,7 @@ public: · 68 unmodified lines
69 glm::vec3 size() const { return maximum - minimum; }69 glm::vec3 size() const { return maximum - minimum; }
70 float largestDimension() const { return glm::compMax(size()); }70 float largestDimension() const { return glm::compMax(size()); }
71 71
72 /// \return new Extents which is original rotated around orign by rotation72 /// \return new Extents which is original rotated around origin by rotation
73 Extents getRotated(const glm::quat& rotation) const {73 Extents getRotated(const glm::quat& rotation) const {
74 Extents temp(minimum, maximum);74 Extents temp(minimum, maximum);
75 temp.rotate(rotation);75 temp.rotate(rotation);
libraries/shared/src/GLMHelpers.cpp+24−25
@@ -49,7 +49,7 @@ const mat4 Matrices::Z_180 { createMatFromQuatAndPos(Quaternions::Z_180, Vectors · 48 unmodified lines
49glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {49glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
50 float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;50 float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
51 float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;51 float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
52 52
53 // adjust signs if necessary53 // adjust signs if necessary
54 if (cosa < 0.0f) {54 if (cosa < 0.0f) {
55 cosa = -cosa;55 cosa = -cosa;
@@ -58,19 +58,19 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) { · 2 unmodified lines
58 oz = -oz;58 oz = -oz;
59 ow = -ow;59 ow = -ow;
60 }60 }
61 61
62 // calculate coefficients; if the angle is too close to zero, we must fall back62 // calculate coefficients; if the angle is too close to zero, we must fall back
63 // to linear interpolation63 // to linear interpolation
64 if ((1.0f - cosa) > EPSILON) {64 if ((1.0f - cosa) > EPSILON) {
65 float angle = acosf(cosa), sina = sinf(angle);65 float angle = acosf(cosa), sina = sinf(angle);
66 s0 = sinf((1.0f - proportion) * angle) / sina;66 s0 = sinf((1.0f - proportion) * angle) / sina;
67 s1 = sinf(proportion * angle) / sina;67 s1 = sinf(proportion * angle) / sina;
68 68
69 } else {69 } else {
70 s0 = 1.0f - proportion;70 s0 = 1.0f - proportion;
71 s1 = proportion;71 s1 = proportion;
72 }72 }
73 73
74 return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));74 return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
75}75}
76 76
@@ -105,10 +105,10 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm · 28 unmodified lines
105 105
106int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {106int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
107 const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0f);107 const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0f);
108 108
109 uint16_t angleHolder = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO);109 uint16_t angleHolder = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO);
110 memcpy(buffer, &angleHolder, sizeof(uint16_t));110 memcpy(buffer, &angleHolder, sizeof(uint16_t));
111 111
112 return sizeof(uint16_t);112 return sizeof(uint16_t);
113}113}
114 114
@@ -125,7 +125,7 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput · 10 unmodified lines
125 quatParts[1] = floorf((quatNormalized.y + 1.0f) * QUAT_PART_CONVERSION_RATIO);125 quatParts[1] = floorf((quatNormalized.y + 1.0f) * QUAT_PART_CONVERSION_RATIO);
126 quatParts[2] = floorf((quatNormalized.z + 1.0f) * QUAT_PART_CONVERSION_RATIO);126 quatParts[2] = floorf((quatNormalized.z + 1.0f) * QUAT_PART_CONVERSION_RATIO);
127 quatParts[3] = floorf((quatNormalized.w + 1.0f) * QUAT_PART_CONVERSION_RATIO);127 quatParts[3] = floorf((quatNormalized.w + 1.0f) * QUAT_PART_CONVERSION_RATIO);
128 128
129 memcpy(buffer, &quatParts, sizeof(quatParts));129 memcpy(buffer, &quatParts, sizeof(quatParts));
130 return sizeof(quatParts);130 return sizeof(quatParts);
131}131}
@@ -133,12 +133,12 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput · 1 unmodified line
133int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {133int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
134 uint16_t quatParts[4];134 uint16_t quatParts[4];
135 memcpy(&quatParts, buffer, sizeof(quatParts));135 memcpy(&quatParts, buffer, sizeof(quatParts));
136 136
137 quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;137 quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
138 quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;138 quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
139 quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;139 quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
140 quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;140 quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
141 141
142 return sizeof(quatParts);142 return sizeof(quatParts);
143}143}
144 144
@@ -235,7 +235,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) { · 90 unmodified lines
235 atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),235 atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
236 asinf(sy),236 asinf(sy),
237 atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));237 atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
238 238
239 } else {239 } else {
240 // not a unique solution; x + z = atan2(-m21, m11)240 // not a unique solution; x + z = atan2(-m21, m11)
241 eulers = glm::vec3(241 eulers = glm::vec3(
@@ -250,7 +250,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) { · 8 unmodified lines
250 PI_OVER_TWO,250 PI_OVER_TWO,
251 -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));251 -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
252 }252 }
253 253
254 // adjust so that z, rather than y, is in [-pi/2, pi/2]254 // adjust so that z, rather than y, is in [-pi/2, pi/2]
255 if (eulers.z < -PI_OVER_TWO) {255 if (eulers.z < -PI_OVER_TWO) {
256 if (eulers.x < 0.0f) {256 if (eulers.x < 0.0f) {
@@ -265,7 +265,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) { · 8 unmodified lines
265 eulers.y -= PI;265 eulers.y -= PI;
266 }266 }
267 eulers.z += PI;267 eulers.z += PI;
268 268
269 } else if (eulers.z > PI_OVER_TWO) {269 } else if (eulers.z > PI_OVER_TWO) {
270 if (eulers.x < 0.0f) {270 if (eulers.x < 0.0f) {
271 eulers.x += PI;271 eulers.x += PI;
@@ -320,7 +320,7 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) { · 48 unmodified lines
320 for (int i = 0; i < 10; i++) {320 for (int i = 0; i < 10; i++) {
321 // store the results of the previous iteration321 // store the results of the previous iteration
322 glm::mat3 previous = upper;322 glm::mat3 previous = upper;
323 323
324 // compute average of the matrix with its inverse transpose324 // compute average of the matrix with its inverse transpose
325 float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];325 float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];
326 float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];326 float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];
@@ -334,15 +334,15 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) { · 7 unmodified lines
334 upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;334 upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;
335 upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;335 upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;
336 upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;336 upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;
337 337
338 upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;338 upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;
339 upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;339 upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;
340 upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;340 upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;
341 341
342 upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;342 upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;
343 upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;343 upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;
344 upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;344 upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;
345 345
346 // compute the difference; if it's small enough, we're done346 // compute the difference; if it's small enough, we're done
347 glm::mat3 diff = upper - previous;347 glm::mat3 diff = upper - previous;
348 if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +348 if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +
@@ -352,7 +352,7 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) { · 3 unmodified lines
352 }352 }
353 }353 }
354 }354 }
355 355
356 // now that we have a nice orthogonal matrix, we can extract the rotation quaternion356 // now that we have a nice orthogonal matrix, we can extract the rotation quaternion
357 // using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions357 // using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions
358 float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);358 float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);
@@ -473,7 +473,7 @@ glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat& · 114 unmodified lines
473 glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));473 glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
474}474}
475 475
476// cancel out roll 476// cancel out roll
477glm::quat cancelOutRoll(const glm::quat& q) {477glm::quat cancelOutRoll(const glm::quat& q) {
478 glm::vec3 forward = q * Vectors::FRONT;478 glm::vec3 forward = q * Vectors::FRONT;
479 return glm::quat_cast(glm::inverse(glm::lookAt(Vectors::ZERO, forward, Vectors::UP)));479 return glm::quat_cast(glm::inverse(glm::lookAt(Vectors::ZERO, forward, Vectors::UP)));
@@ -538,17 +538,16 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda · 58 unmodified lines
538 uAxisOut = glm::normalize(primaryAxis);538 uAxisOut = glm::normalize(primaryAxis);
539 glm::vec3 normSecondary = glm::normalize(secondaryAxis);539 glm::vec3 normSecondary = glm::normalize(secondaryAxis);
540 540
541 // if secondaryAxis is parallel with the primaryAxis, pick another axis.541 // if normSecondary is parallel with the primaryAxis, pick another secondary.
542 const float EPSILON = 1.0e-4f;542 const float EPSILON = 1.0e-4f;
543 if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {543 if (fabsf(fabsf(glm::dot(uAxisOut, normSecondary)) - 1.0f) < EPSILON) {
544 // pick a better secondaryAxis.544 normSecondary = Vectors::UNIT_X;
545 normSecondary = glm::vec3(1.0f, 0.0f, 0.0f);545 if (fabsf(fabsf(glm::dot(uAxisOut, normSecondary)) - 1.0f) < EPSILON) {
546 if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {546 normSecondary = Vectors::UNIT_Y;
547 normSecondary = glm::vec3(0.0f, 1.0f, 0.0f);
548 }547 }
549 }548 }
550 549
551 wAxisOut = glm::normalize(glm::cross(uAxisOut, secondaryAxis));550 wAxisOut = glm::normalize(glm::cross(uAxisOut, normSecondary));
552 vAxisOut = glm::cross(wAxisOut, uAxisOut);551 vAxisOut = glm::cross(wAxisOut, uAxisOut);
553}552}
554 553
libraries/shared/src/GeometryUtil.cpp+2−2
@@ -290,12 +290,12 @@ glm::vec3 Triangle::getNormal() const { · 289 unmodified lines
290}290}
291 291
292bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,292bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
293 const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance) {293 const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance, bool allowBackface) {
294 glm::vec3 firstSide = v0 - v1;294 glm::vec3 firstSide = v0 - v1;
295 glm::vec3 secondSide = v2 - v1;295 glm::vec3 secondSide = v2 - v1;
296 glm::vec3 normal = glm::cross(secondSide, firstSide);296 glm::vec3 normal = glm::cross(secondSide, firstSide);
297 float dividend = glm::dot(normal, v1) - glm::dot(origin, normal);297 float dividend = glm::dot(normal, v1) - glm::dot(origin, normal);
298 if (dividend > 0.0f) {298 if (!allowBackface && dividend > 0.0f) {
299 return false; // origin below plane299 return false; // origin below plane
300 }300 }
301 float divisor = glm::dot(normal, direction);301 float divisor = glm::dot(normal, direction);
libraries/shared/src/GeometryUtil.h+3−3
@@ -83,7 +83,7 @@ bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& dire · 82 unmodified lines
83 const glm::vec3& position, const glm::vec2& dimensions, float& distance);83 const glm::vec3& position, const glm::vec2& dimensions, float& distance);
84 84
85bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,85bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
86 const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance);86 const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance, bool allowBackface = false);
87 87
88/// \brief decomposes rotation into its components such that: rotation = swing * twist88/// \brief decomposes rotation into its components such that: rotation = swing * twist
89/// \param rotation[in] rotation to decompose89/// \param rotation[in] rotation to decompose
@@ -104,8 +104,8 @@ public: · 14 unmodified lines
104};104};
105 105
106inline bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,106inline bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
107 const Triangle& triangle, float& distance) {107 const Triangle& triangle, float& distance, bool allowBackface = false) {
108 return findRayTriangleIntersection(origin, direction, triangle.v0, triangle.v1, triangle.v2, distance);108 return findRayTriangleIntersection(origin, direction, triangle.v0, triangle.v1, triangle.v2, distance, allowBackface);
109}109}
110 110
111 111
libraries/shared/src/NumericalConstants.h+3−0
@@ -50,10 +50,13 @@ const int KILO_PER_MEGA = 1000; · 49 unmodified lines
50 50
51#define KB_TO_BYTES_SHIFT 1051#define KB_TO_BYTES_SHIFT 10
52#define MB_TO_BYTES_SHIFT 2052#define MB_TO_BYTES_SHIFT 20
53#define GB_TO_BYTES_SHIFT 30
53 54
55#define GB_TO_BYTES(X) ((size_t)(X) << GB_TO_BYTES_SHIFT)
54#define MB_TO_BYTES(X) ((size_t)(X) << MB_TO_BYTES_SHIFT)56#define MB_TO_BYTES(X) ((size_t)(X) << MB_TO_BYTES_SHIFT)
55#define KB_TO_BYTES(X) ((size_t)(X) << KB_TO_BYTES_SHIFT)57#define KB_TO_BYTES(X) ((size_t)(X) << KB_TO_BYTES_SHIFT)
56 58
59#define BYTES_TO_GB(X) (X >> GB_TO_BYTES_SHIFT)
57#define BYTES_TO_MB(X) (X >> MB_TO_BYTES_SHIFT)60#define BYTES_TO_MB(X) (X >> MB_TO_BYTES_SHIFT)
58#define BYTES_TO_KB(X) (X >> KB_TO_BYTES_SHIFT)61#define BYTES_TO_KB(X) (X >> KB_TO_BYTES_SHIFT)
59 62
libraries/shared/src/RenderArgs.h+4−4
@@ -76,7 +76,7 @@ public: · 75 unmodified lines
76class RenderArgs {76class RenderArgs {
77public:77public:
78 enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE };78 enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE };
79 enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT };79 enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD };
80 enum DebugFlags {80 enum DebugFlags {
81 RENDER_DEBUG_NONE = 0,81 RENDER_DEBUG_NONE = 0,
82 RENDER_DEBUG_HULLS = 182 RENDER_DEBUG_HULLS = 1
@@ -87,7 +87,7 @@ public: · 4 unmodified lines
87 float sizeScale = 1.0f,87 float sizeScale = 1.0f,
88 int boundaryLevelAdjust = 0,88 int boundaryLevelAdjust = 0,
89 RenderMode renderMode = DEFAULT_RENDER_MODE,89 RenderMode renderMode = DEFAULT_RENDER_MODE,
90 RenderSide renderSide = MONO,90 DisplayMode displayMode = MONO,
91 DebugFlags debugFlags = RENDER_DEBUG_NONE,91 DebugFlags debugFlags = RENDER_DEBUG_NONE,
92 gpu::Batch* batch = nullptr) :92 gpu::Batch* batch = nullptr) :
93 _context(context),93 _context(context),
@@ -95,7 +95,7 @@ public: · 1 unmodified line
95 _sizeScale(sizeScale),95 _sizeScale(sizeScale),
96 _boundaryLevelAdjust(boundaryLevelAdjust),96 _boundaryLevelAdjust(boundaryLevelAdjust),
97 _renderMode(renderMode),97 _renderMode(renderMode),
98 _renderSide(renderSide),98 _displayMode(displayMode),
99 _debugFlags(debugFlags),99 _debugFlags(debugFlags),
100 _batch(batch) {100 _batch(batch) {
101 }101 }
@@ -121,7 +121,7 @@ public: · 19 unmodified lines
121 float _sizeScale = 1.0f;121 float _sizeScale = 1.0f;
122 int _boundaryLevelAdjust = 0;122 int _boundaryLevelAdjust = 0;
123 RenderMode _renderMode = DEFAULT_RENDER_MODE;123 RenderMode _renderMode = DEFAULT_RENDER_MODE;
124 RenderSide _renderSide = MONO;124 DisplayMode _displayMode = MONO;
125 DebugFlags _debugFlags = RENDER_DEBUG_NONE;125 DebugFlags _debugFlags = RENDER_DEBUG_NONE;
126 gpu::Batch* _batch = nullptr;126 gpu::Batch* _batch = nullptr;
127 127
libraries/shared/src/RunningMarker.cpp+1−34
@@ -13,44 +13,16 @@ · 12 unmodified lines
13 13
14#include <QFile>14#include <QFile>
15#include <QStandardPaths>15#include <QStandardPaths>
16#include <QThread>
17#include <QTimer>
18 16
19#include "NumericalConstants.h"
20#include "PathUtils.h"17#include "PathUtils.h"
21 18
22RunningMarker::RunningMarker(QObject* parent, QString name) :19RunningMarker::RunningMarker(QString name) :
23 _parent(parent),
24 _name(name)20 _name(name)
25{21{
26}22}
27 23
28void RunningMarker::startRunningMarker() {
29 static const int RUNNING_STATE_CHECK_IN_MSECS = MSECS_PER_SECOND;
30
31 // start the nodeThread so its event loop is running
32 _runningMarkerThread = new QThread(_parent);
33 _runningMarkerThread->setObjectName("Running Marker Thread");
34 _runningMarkerThread->start();
35
36 writeRunningMarkerFile(); // write the first file, even before timer
37
38 _runningMarkerTimer = new QTimer();
39 QObject::connect(_runningMarkerTimer, &QTimer::timeout, [=](){
40 writeRunningMarkerFile();
41 });
42 _runningMarkerTimer->start(RUNNING_STATE_CHECK_IN_MSECS);
43
44 // put the time on the thread
45 _runningMarkerTimer->moveToThread(_runningMarkerThread);
46}
47
48RunningMarker::~RunningMarker() {24RunningMarker::~RunningMarker() {
49 deleteRunningMarkerFile();25 deleteRunningMarkerFile();
50 QMetaObject::invokeMethod(_runningMarkerTimer, "stop", Qt::BlockingQueuedConnection);
51 _runningMarkerThread->quit();
52 _runningMarkerTimer->deleteLater();
53 _runningMarkerThread->deleteLater();
54}26}
55 27
56bool RunningMarker::fileExists() const {28bool RunningMarker::fileExists() const {
@@ -77,8 +49,3 @@ void RunningMarker::deleteRunningMarkerFile() { · 20 unmodified lines
77QString RunningMarker::getFilePath() const {49QString RunningMarker::getFilePath() const {
78 return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name;50 return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name;
79}51}
80
81QString RunningMarker::getMarkerFilePath(QString name) {
82 return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + name;
83}
84
libraries/shared/src/RunningMarker.h+1−11
@@ -12,21 +12,14 @@ · 11 unmodified lines
12#ifndef hifi_RunningMarker_h12#ifndef hifi_RunningMarker_h
13#define hifi_RunningMarker_h13#define hifi_RunningMarker_h
14 14
15#include <QObject>
16#include <QString>15#include <QString>
17 16
18class QThread;
19class QTimer;
20
21class RunningMarker {17class RunningMarker {
22public:18public:
23 RunningMarker(QObject* parent, QString name);19 RunningMarker(QString name);
24 ~RunningMarker();20 ~RunningMarker();
25 21
26 void startRunningMarker();
27
28 QString getFilePath() const;22 QString getFilePath() const;
29 static QString getMarkerFilePath(QString name);
30 23
31 bool fileExists() const;24 bool fileExists() const;
32 25
@@ -34,10 +27,7 @@ public: · 1 unmodified line
34 void deleteRunningMarkerFile();27 void deleteRunningMarkerFile();
35 28
36private:29private:
37 QObject* _parent { nullptr };
38 QString _name;30 QString _name;
39 QThread* _runningMarkerThread { nullptr };
40 QTimer* _runningMarkerTimer { nullptr };
41};31};
42 32
43#endif // hifi_RunningMarker_h33#endif // hifi_RunningMarker_h
libraries/shared/src/TBBHelpers.h+28−0
@@ -0,0 +1,28 @@
1//
2// Created by Bradley Austin Davis on 2017/06/06
3// Copyright 2013-2017 High Fidelity, Inc.
4//
5// Distributed under the Apache License, Version 2.0.
6// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7//
8
9#pragma once
10#ifndef hifi_TBBHelpers_h
11#define hifi_TBBHelpers_h
12
13#ifdef _WIN32
14#pragma warning( push )
15#pragma warning( disable : 4334 )
16#endif
17
18#include <tbb/concurrent_queue.h>
19#include <tbb/concurrent_unordered_map.h>
20#include <tbb/concurrent_unordered_set.h>
21#include <tbb/concurrent_vector.h>
22#include <tbb/parallel_for.h>
23
24#ifdef _WIN32
25#pragma warning( pop )
26#endif
27
28#endif // hifi_TBBHelpers_h
libraries/shared/src/ThreadHelpers.cpp+39−0
@@ -0,0 +1,39 @@
1//
2// Created by Bradley Austin Davis on 2017/06/06
3// Copyright 2013-2017 High Fidelity, Inc.
4//
5// Distributed under the Apache License, Version 2.0.
6// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7//
8
9#include "ThreadHelpers.h"
10
11#include <QtCore/QDebug>
12
13
14void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority) {
15 Q_ASSERT(QThread::currentThread() == object->thread());
16 // setup a thread for the NodeList and its PacketReceiver
17 QThread* thread = new QThread();
18 thread->setObjectName(name);
19
20 if (priority != QThread::InheritPriority) {
21 thread->setPriority(priority);
22 }
23
24 QString tempName = name;
25 QObject::connect(thread, &QThread::started, [startCallback] {
26 startCallback();
27 });
28 // Make sure the thread will be destroyed and cleaned up
29 QObject::connect(object, &QObject::destroyed, thread, &QThread::quit);
30 QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
31
32 // put the object on the thread
33 object->moveToThread(thread);
34 thread->start();
35}
36
37void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) {
38 moveToNewNamedThread(object, name, [] {}, priority);
39}
libraries/shared/src/ThreadHelpers.h+10−2
@@ -12,8 +12,13 @@ · 11 unmodified lines
12#define hifi_ThreadHelpers_h12#define hifi_ThreadHelpers_h
13 13
14#include <exception>14#include <exception>
15#include <QMutex>15#include <functional>
16#include <QMutexLocker>16
17#include <QtCore/QMutex>
18#include <QtCore/QMutexLocker>
19#include <QtCore/QObject>
20#include <QtCore/QString>
21#include <QtCore/QThread>
17 22
18template <typename L, typename F>23template <typename L, typename F>
19void withLock(L lock, F function) {24void withLock(L lock, F function) {
@@ -26,4 +31,7 @@ void withLock(QMutex& lock, F function) { · 6 unmodified lines
26 function();31 function();
27}32}
28 33
34void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority = QThread::InheritPriority);
35void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority);
36
29#endif37#endif
libraries/shared/src/TriangleSet.cpp+6−6
@@ -31,7 +31,7 @@ void TriangleSet::clear() { · 30 unmodified lines
31}31}
32 32
33bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,33bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
34 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) {34 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, bool allowBackface) {
35 35
36 // reset our distance to be the max possible, lower level tests will store best distance here36 // reset our distance to be the max possible, lower level tests will store best distance here
37 distance = std::numeric_limits<float>::max();37 distance = std::numeric_limits<float>::max();
@@ -41,7 +41,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& · 3 unmodified lines
41 }41 }
42 42
43 int trianglesTouched = 0;43 int trianglesTouched = 0;
44 auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched);44 auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched, allowBackface);
45 45
46 #if WANT_DEBUGGING46 #if WANT_DEBUGGING
47 if (precision) {47 if (precision) {
@@ -95,7 +95,7 @@ void TriangleSet::balanceOctree() { · 47 unmodified lines
95// Determine of the given ray (origin/direction) in model space intersects with any triangles95// Determine of the given ray (origin/direction) in model space intersects with any triangles
96// in the set. If an intersection occurs, the distance and surface normal will be provided.96// in the set. If an intersection occurs, the distance and surface normal will be provided.
97bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,97bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,
98 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) {98 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched, bool allowBackface) {
99 99
100 bool intersectedSomething = false;100 bool intersectedSomething = false;
101 float boxDistance = distance;101 float boxDistance = distance;
@@ -114,7 +114,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec · 12 unmodified lines
114 const auto& triangle = _allTriangles[triangleIndex];114 const auto& triangle = _allTriangles[triangleIndex];
115 float thisTriangleDistance;115 float thisTriangleDistance;
116 trianglesTouched++;116 trianglesTouched++;
117 if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {117 if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance, allowBackface)) {
118 if (thisTriangleDistance < bestDistance) {118 if (thisTriangleDistance < bestDistance) {
119 bestDistance = thisTriangleDistance;119 bestDistance = thisTriangleDistance;
120 intersectedSomething = true;120 intersectedSomething = true;
@@ -203,7 +203,7 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { · 82 unmodified lines
203}203}
204 204
205bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,205bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
206 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) {206 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched, bool allowBackface) {
207 207
208 if (_population < 1) {208 if (_population < 1) {
209 return false; // no triangles below here, so we can't intersect209 return false; // no triangles below here, so we can't intersect
@@ -247,7 +247,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi · 37 unmodified lines
247 }247 }
248 }248 }
249 // also check our local triangle set249 // also check our local triangle set
250 if (findRayIntersectionInternal(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) {250 if (findRayIntersectionInternal(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched, allowBackface)) {
251 if (childDistance < bestLocalDistance) {251 if (childDistance < bestLocalDistance) {
252 bestLocalDistance = childDistance;252 bestLocalDistance = childDistance;
253 bestLocalFace = childFace;253 bestLocalFace = childFace;
libraries/shared/src/TriangleSet.h+3−3
@@ -27,7 +27,7 @@ class TriangleSet { · 26 unmodified lines
27 void clear();27 void clear();
28 28
29 bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,29 bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
30 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched);30 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched, bool allowBackface = false);
31 31
32 const AABox& getBounds() const { return _bounds; }32 const AABox& getBounds() const { return _bounds; }
33 33
@@ -38,7 +38,7 @@ class TriangleSet { · 4 unmodified lines
38 38
39 // checks our internal list of triangles39 // checks our internal list of triangles
40 bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,40 bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,
41 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched);41 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched, bool allowBackface = false);
42 42
43 std::vector<Triangle>& _allTriangles;43 std::vector<Triangle>& _allTriangles;
44 std::map<AABox::OctreeChild, TriangleOctreeCell> _children;44 std::map<AABox::OctreeChild, TriangleOctreeCell> _children;
@@ -60,7 +60,7 @@ public: · 15 unmodified lines
60 void insert(const Triangle& t);60 void insert(const Triangle& t);
61 61
62 bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,62 bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
63 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision);63 float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, bool allowBackface = false);
64 64
65 void balanceOctree();65 void balanceOctree();
66 66
libraries/ui/src/OffscreenUi.cpp+8−8
@@ -91,14 +91,14 @@ QObject* OffscreenUi::getFlags() { · 90 unmodified lines
91 91
92void OffscreenUi::create(QOpenGLContext* context) {92void OffscreenUi::create(QOpenGLContext* context) {
93 OffscreenQmlSurface::create(context);93 OffscreenQmlSurface::create(context);
94 auto rootContext = getRootContext();94 auto myContext = getSurfaceContext();
95 95
96 rootContext->setContextProperty("OffscreenUi", this);96 myContext->setContextProperty("OffscreenUi", this);
97 rootContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags());97 myContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags());
98 rootContext->setContextProperty("fileDialogHelper", new FileDialogHelper());98 myContext->setContextProperty("fileDialogHelper", new FileDialogHelper());
99 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();99 auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
100 TabletProxy* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");100 TabletProxy* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
101 rootContext->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership);101 myContext->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership);
102}102}
103 103
104void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {104void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
@@ -547,14 +547,14 @@ void OffscreenUi::createDesktop(const QUrl& url) { · 442 unmodified lines
547 }547 }
548 548
549#ifdef DEBUG549#ifdef DEBUG
550 getRootContext()->setContextProperty("DebugQML", QVariant(true));550 getSurfaceContext()->setContextProperty("DebugQML", QVariant(true));
551#else 551#else
552 getRootContext()->setContextProperty("DebugQML", QVariant(false));552 getSurfaceContext()->setContextProperty("DebugQML", QVariant(false));
553#endif553#endif
554 554
555 _desktop = dynamic_cast<QQuickItem*>(load(url));555 _desktop = dynamic_cast<QQuickItem*>(load(url));
556 Q_ASSERT(_desktop);556 Q_ASSERT(_desktop);
557 getRootContext()->setContextProperty("desktop", _desktop);557 getSurfaceContext()->setContextProperty("desktop", _desktop);
558 558
559 _toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");559 _toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
560 560
libraries/ui/src/QmlWindowClass.cpp+2−2
@@ -104,9 +104,9 @@ void QmlWindowClass::initQml(QVariantMap properties) { · 103 unmodified lines
104 Q_ASSERT(invokeResult);104 Q_ASSERT(invokeResult);
105 } else {105 } else {
106 // Build the event bridge and wrapper on the main thread106 // Build the event bridge and wrapper on the main thread
107 offscreenUi->load(qmlSource(), [&](QQmlContext* context, QObject* object) {107 offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
108 _qmlWindow = object;108 _qmlWindow = object;
109 _qmlWindow->setProperty("eventBridge", QVariant::fromValue(this));109 context->setContextProperty("eventBridge", this);
110 context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);110 context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
111 context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);111 context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);
112 if (properties.contains(TITLE_PROPERTY)) {112 if (properties.contains(TITLE_PROPERTY)) {
libraries/ui/src/VrMenu.cpp+1−1
@@ -115,7 +115,7 @@ private: · 114 unmodified lines
115 115
116VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) {116VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) {
117 _rootMenu = parent->getRootItem()->findChild<QObject*>("rootMenu");117 _rootMenu = parent->getRootItem()->findChild<QObject*>("rootMenu");
118 parent->getRootContext()->setContextProperty("rootMenu", _rootMenu);118 parent->getSurfaceContext()->setContextProperty("rootMenu", _rootMenu);
119}119}
120 120
121QObject* VrMenu::findMenuObject(const QString& menuOption) {121QObject* VrMenu::findMenuObject(const QString& menuOption) {
plugins/hifiKinect/src/KinectPlugin.cpp+6−2
@@ -273,7 +273,7 @@ bool KinectPlugin::activate() { · 272 unmodified lines
273 return false;273 return false;
274}274}
275 275
276bool KinectPlugin::isHandController() const { 276bool KinectPlugin::isHandController() const {
277 bool sensorAvailable = false;277 bool sensorAvailable = false;
278#ifdef HAVE_KINECT278#ifdef HAVE_KINECT
279 if (_kinectSensor) {279 if (_kinectSensor) {
@@ -285,6 +285,10 @@ bool KinectPlugin::isHandController() const { · 5 unmodified lines
285 return _enabled && _initialized && sensorAvailable;285 return _enabled && _initialized && sensorAvailable;
286}286}
287 287
288bool KinectPlugin::isHeadController() const {
289 return isHandController();
290}
291
288 292
289bool KinectPlugin::initializeDefaultSensor() const {293bool KinectPlugin::initializeDefaultSensor() const {
290#ifdef HAVE_KINECT294#ifdef HAVE_KINECT
@@ -654,4 +658,4 @@ void KinectPlugin::InputDevice::clearState() { · 363 unmodified lines
654 int poseIndex = KinectJointIndexToPoseIndex((KinectJointIndex)i);658 int poseIndex = KinectJointIndexToPoseIndex((KinectJointIndex)i);
655 _poseStateMap[poseIndex] = controller::Pose();659 _poseStateMap[poseIndex] = controller::Pose();
656 }660 }
657}661}
plugins/hifiKinect/src/KinectPlugin.h+1−0
@@ -43,6 +43,7 @@ class KinectPlugin : public InputPlugin { · 42 unmodified lines
43 Q_OBJECT43 Q_OBJECT
44public:44public:
45 bool isHandController() const override;45 bool isHandController() const override;
46 bool isHeadController() const override;
46 47
47 // Plugin functions48 // Plugin functions
48 virtual void init() override;49 virtual void init() override;
plugins/hifiNeuron/src/NeuronPlugin.h+0−2
@@ -25,8 +25,6 @@ class NeuronPlugin : public InputPlugin { · 24 unmodified lines
25public:25public:
26 friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);26 friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);
27 27
28 bool isHandController() const override { return false; }
29
30 // Plugin functions28 // Plugin functions
31 virtual void init() override;29 virtual void init() override;
32 virtual bool isSupported() const override;30 virtual bool isSupported() const override;
plugins/hifiSdl2/src/SDL2Manager.h+0−1
@@ -27,7 +27,6 @@ public: · 26 unmodified lines
27 const QString getName() const override { return NAME; }27 const QString getName() const override { return NAME; }
28 28
29 QStringList getSubdeviceNames() override;29 QStringList getSubdeviceNames() override;
30 bool isHandController() const override { return false; }
31 30
32 void init() override;31 void init() override;
33 void deinit() override;32 void deinit() override;
plugins/hifiSixense/src/SixenseManager.h+1−1
@@ -33,7 +33,7 @@ public: · 32 unmodified lines
33 33
34 // Sixense always seems to initialize even if the hydras are not present. Is there34 // Sixense always seems to initialize even if the hydras are not present. Is there
35 // a way we can properly detect whether the hydras are present?35 // a way we can properly detect whether the hydras are present?
36 bool isHandController() const override { return false; }36 // bool isHandController() const override { return true; }
37 37
38 virtual bool activate() override;38 virtual bool activate() override;
39 virtual void deactivate() override;39 virtual void deactivate() override;
plugins/oculus/src/OculusBaseDisplayPlugin.cpp+10−1
@@ -123,10 +123,19 @@ bool OculusBaseDisplayPlugin::internalActivate() { · 122 unmodified lines
123 123
124void OculusBaseDisplayPlugin::internalDeactivate() {124void OculusBaseDisplayPlugin::internalDeactivate() {
125 Parent::internalDeactivate();125 Parent::internalDeactivate();
126}
127
128bool OculusBaseDisplayPlugin::activateStandBySession() {
129 _session = acquireOculusSession();
130 if (!_session) {
131 return false;
132 }
133 return true;
134}
135void OculusBaseDisplayPlugin::deactivateSession() {
126 releaseOculusSession();136 releaseOculusSession();
127 _session = nullptr;137 _session = nullptr;
128}138}
129
130void OculusBaseDisplayPlugin::updatePresentPose() {139void OculusBaseDisplayPlugin::updatePresentPose() {
131 //mat4 sensorResetMat;140 //mat4 sensorResetMat;
132 //_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();141 //_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
plugins/oculus/src/OculusBaseDisplayPlugin.h+3−0
@@ -26,6 +26,7 @@ public: · 25 unmodified lines
26 void resetSensors() override final;26 void resetSensors() override final;
27 bool beginFrameRender(uint32_t frameIndex) override;27 bool beginFrameRender(uint32_t frameIndex) override;
28 float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }28 float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
29 bool getSupportsAutoSwitch() override final { return true; }
29 30
30 31
31protected:32protected:
@@ -33,6 +34,8 @@ protected: · 1 unmodified line
33 void uncustomizeContext() override;34 void uncustomizeContext() override;
34 bool internalActivate() override;35 bool internalActivate() override;
35 void internalDeactivate() override;36 void internalDeactivate() override;
37 bool activateStandBySession() override;
38 void deactivateSession() override;
36 void updatePresentPose() override;39 void updatePresentPose() override;
37 40
38protected:41protected:
plugins/oculus/src/OculusControllerManager.cpp+48−11
@@ -19,6 +19,8 @@ · 18 unmodified lines
19 19
20#include <PerfStat.h>20#include <PerfStat.h>
21#include <PathUtils.h>21#include <PathUtils.h>
22#include <NumericalConstants.h>
23#include <StreamUtils.h>
22 24
23#include <OVR_CAPI.h>25#include <OVR_CAPI.h>
24 26
@@ -208,13 +210,18 @@ void OculusControllerManager::RemoteDevice::focusOutEvent() { · 183 unmodified lines
208 _buttonPressedMap.clear();210 _buttonPressedMap.clear();
209}211}
210 212
211void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {213bool OculusControllerManager::isHeadControllerMounted() const {
212 _buttonPressedMap.clear();
213 ovrSessionStatus status;214 ovrSessionStatus status;
214 if (!OVR_SUCCESS(ovr_GetSessionStatus(_parent._session, &status)) || (ovrFalse == status.HmdMounted)) {215 bool success = OVR_SUCCESS(ovr_GetSessionStatus(_session, &status));
215 // if the HMD isn't on someone's head, don't take input from the controllers216 if (!success) {
216 return;217 return false;
217 }218 }
219 return status.HmdMounted == ovrTrue;
220}
221
222void OculusControllerManager::TouchDevice::update(float deltaTime,
223 const controller::InputCalibrationData& inputCalibrationData) {
224 _buttonPressedMap.clear();
218 225
219 int numTrackedControllers = 0;226 int numTrackedControllers = 0;
220 quint64 currentTime = usecTimestampNow();227 quint64 currentTime = usecTimestampNow();
@@ -230,14 +237,14 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control · 9 unmodified lines
230 _lastControllerPose[controller] = tracking.HandPoses[hand];237 _lastControllerPose[controller] = tracking.HandPoses[hand];
231 return;238 return;
232 }239 }
233 240
234 if (_lostTracking[controller]) {241 if (_lostTracking[controller]) {
235 if (currentTime > _regainTrackingDeadline[controller]) {242 if (currentTime > _regainTrackingDeadline[controller]) {
236 _poseStateMap.erase(controller);243 _poseStateMap.erase(controller);
237 _poseStateMap[controller].valid = false;244 _poseStateMap[controller].valid = false;
238 return;245 return;
239 }246 }
240 247
241 } else {248 } else {
242 quint64 deadlineToRegainTracking = currentTime + LOST_TRACKING_DELAY;249 quint64 deadlineToRegainTracking = currentTime + LOST_TRACKING_DELAY;
243 _regainTrackingDeadline[controller] = deadlineToRegainTracking;250 _regainTrackingDeadline[controller] = deadlineToRegainTracking;
@@ -245,6 +252,13 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control · 1 unmodified line
245 }252 }
246 handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]);253 handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]);
247 });254 });
255
256 if (_parent.isHeadControllerMounted()) {
257 handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose);
258 } else {
259 _poseStateMap[controller::HEAD].valid = false;
260 }
261
248 using namespace controller;262 using namespace controller;
249 // Axes263 // Axes
250 const auto& inputState = _parent._inputState;264 const auto& inputState = _parent._inputState;
@@ -269,7 +283,7 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control · 18 unmodified lines
269 if (inputState.Touches & pair.first) {283 if (inputState.Touches & pair.first) {
270 _buttonPressedMap.insert(pair.second);284 _buttonPressedMap.insert(pair.second);
271 }285 }
272 } 286 }
273 287
274 // Haptics288 // Haptics
275 {289 {
@@ -292,16 +306,38 @@ void OculusControllerManager::TouchDevice::focusOutEvent() { · 16 unmodified lines
292 _buttonPressedMap.clear();306 _buttonPressedMap.clear();
293};307};
294 308
295void OculusControllerManager::TouchDevice::handlePose(float deltaTime, 309void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
296 const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, 310 const controller::InputCalibrationData& inputCalibrationData,
297 const ovrPoseStatef& handPose) {311 ovrHandType hand, const ovrPoseStatef& handPose) {
298 auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND;312 auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND;
299 auto& pose = _poseStateMap[poseId];313 auto& pose = _poseStateMap[poseId];
300 pose = ovrControllerPoseToHandPose(hand, handPose);314 pose = ovrControllerPoseToHandPose(hand, handPose);
301 // transform into avatar frame315 // transform into avatar frame
302 glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;316 glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
303 pose = pose.transform(controllerToAvatar);317 pose = pose.transform(controllerToAvatar);
318}
319
320void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime,
321 const controller::InputCalibrationData& inputCalibrationData,
322 const ovrPoseStatef& headPose) {
323 glm::mat4 mat = createMatFromQuatAndPos(toGlm(headPose.ThePose.Orientation),
324 toGlm(headPose.ThePose.Position));
325
326 //perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z
327 glm::mat4 matYFlip = mat * Matrices::Y_180;
328 controller::Pose pose(extractTranslation(matYFlip),
329 glmExtractRotation(matYFlip),
330 toGlm(headPose.LinearVelocity), // XXX * matYFlip ?
331 toGlm(headPose.AngularVelocity));
332
333 glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
334 glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) *
335 inputCalibrationData.defaultHeadMat;
336
337 controller::Pose hmdHeadPose = pose.transform(sensorToAvatar);
304 338
339 pose.valid = true;
340 _poseStateMap[controller::HEAD] = hmdHeadPose.postTransform(defaultHeadOffset);
305}341}
306 342
307void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData,343void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData,
@@ -382,6 +418,7 @@ controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailabl · 74 unmodified lines
382 418
383 makePair(LEFT_HAND, "LeftHand"),419 makePair(LEFT_HAND, "LeftHand"),
384 makePair(RIGHT_HAND, "RightHand"),420 makePair(RIGHT_HAND, "RightHand"),
421 makePair(HEAD, "Head"),
385 422
386 makePair(LEFT_PRIMARY_THUMB_TOUCH, "LeftPrimaryThumbTouch"),423 makePair(LEFT_PRIMARY_THUMB_TOUCH, "LeftPrimaryThumbTouch"),
387 makePair(LEFT_SECONDARY_THUMB_TOUCH, "LeftSecondaryThumbTouch"),424 makePair(LEFT_SECONDARY_THUMB_TOUCH, "LeftSecondaryThumbTouch"),
plugins/oculus/src/OculusControllerManager.h+9−2
@@ -28,6 +28,8 @@ public: · 27 unmodified lines
28 const QString getName() const override { return NAME; }28 const QString getName() const override { return NAME; }
29 29
30 bool isHandController() const override { return _touch != nullptr; }30 bool isHandController() const override { return _touch != nullptr; }
31 bool isHeadController() const override { return true; }
32 bool isHeadControllerMounted() const;
31 QStringList getSubdeviceNames() override;33 QStringList getSubdeviceNames() override;
32 34
33 bool activate() override;35 bool activate() override;
@@ -75,8 +77,13 @@ private: · 41 unmodified lines
75 77
76 private:78 private:
77 void stopHapticPulse(bool leftHand);79 void stopHapticPulse(bool leftHand);
78 void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose);80 void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
79 void handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose);81 ovrHandType hand, const ovrPoseStatef& handPose);
82 void handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData,
83 ovrHandType hand, const ovrPoseStatef& handPose);
84 void handleHeadPose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
85 const ovrPoseStatef& headPose);
86
80 int _trackedControllers { 0 };87 int _trackedControllers { 0 };
81 88
82 // perform an action when the TouchDevice mutex is acquired.89 // perform an action when the TouchDevice mutex is acquired.
plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp+1−1
@@ -24,7 +24,7 @@ · 23 unmodified lines
24#include <gl/QOpenGLContextWrapper.h>24#include <gl/QOpenGLContextWrapper.h>
25#include <PerfStat.h>25#include <PerfStat.h>
26#include <ViewFrustum.h>26#include <ViewFrustum.h>
27#include <gpu/gl/GLbackend.h>27#include <gpu/gl/GLBackend.h>
28 28
29#include <ui-plugins/PluginContainer.h>29#include <ui-plugins/PluginContainer.h>
30 30
plugins/openvr/CMakeLists.txt+3−2
@@ -10,10 +10,10 @@ if (WIN32) · 9 unmodified lines
10 # we're using static GLEW, so define GLEW_STATIC10 # we're using static GLEW, so define GLEW_STATIC
11 add_definitions(-DGLEW_STATIC)11 add_definitions(-DGLEW_STATIC)
12 set(TARGET_NAME openvr)12 set(TARGET_NAME openvr)
13 setup_hifi_plugin(OpenGL Script Qml Widgets)13 setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia)
14 link_hifi_libraries(shared gl networking controllers ui 14 link_hifi_libraries(shared gl networking controllers ui
15 plugins display-plugins ui-plugins input-plugins script-engine15 plugins display-plugins ui-plugins input-plugins script-engine
16 render-utils model gpu gpu-gl render model-networking fbx ktx image)16 audio-client render-utils model gpu gpu-gl render model-networking fbx ktx image procedural)
17 17
18 include_hifi_library_headers(octree)18 include_hifi_library_headers(octree)
19 19
@@ -21,4 +21,5 @@ if (WIN32) · 1 unmodified line
21 find_package(OpenVR REQUIRED)21 find_package(OpenVR REQUIRED)
22 target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})22 target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
23 target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})23 target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
24 target_link_libraries(${TARGET_NAME} Winmm.lib)
24endif()25endif()
plugins/openvr/src/OpenVrDisplayPlugin.cpp+30−0
@@ -7,6 +7,9 @@ · 6 unmodified lines
7//7//
8#include "OpenVrDisplayPlugin.h"8#include "OpenVrDisplayPlugin.h"
9 9
10// Odd ordering of header is required to avoid 'macro redinition warnings'
11#include <AudioClient.h>
12
10#include <QtCore/QThread>13#include <QtCore/QThread>
11#include <QtCore/QLoggingCategory>14#include <QtCore/QLoggingCategory>
12#include <QtCore/QFileInfo>15#include <QtCore/QFileInfo>
@@ -713,3 +716,30 @@ bool OpenVrDisplayPlugin::isKeyboardVisible() { · 700 unmodified lines
713int OpenVrDisplayPlugin::getRequiredThreadCount() const { 716int OpenVrDisplayPlugin::getRequiredThreadCount() const {
714 return Parent::getRequiredThreadCount() + (_threadedSubmit ? 1 : 0);717 return Parent::getRequiredThreadCount() + (_threadedSubmit ? 1 : 0);
715}718}
719
720QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const {
721 QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String);
722 if (!device.isEmpty()) {
723 static const WCHAR INIT = 0;
724 size_t size = device.size() + 1;
725 std::vector<WCHAR> deviceW;
726 deviceW.assign(size, INIT);
727 device.toWCharArray(deviceW.data());
728 device = AudioClient::friendlyNameForAudioDevice(deviceW.data());
729 }
730 return device;
731}
732
733QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const {
734 QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String);
735 if (!device.isEmpty()) {
736 static const WCHAR INIT = 0;
737 size_t size = device.size() + 1;
738 std::vector<WCHAR> deviceW;
739 deviceW.assign(size, INIT);
740 device.toWCharArray(deviceW.data());
741 device = AudioClient::friendlyNameForAudioDevice(deviceW.data());
742 }
743 return device;
744}
745
plugins/openvr/src/OpenVrDisplayPlugin.h+4−1
@@ -56,7 +56,10 @@ public: · 55 unmodified lines
56 bool isKeyboardVisible() override;56 bool isKeyboardVisible() override;
57 57
58 // Possibly needs an additional thread for VR submission58 // Possibly needs an additional thread for VR submission
59 int getRequiredThreadCount() const override; 59 int getRequiredThreadCount() const override;
60
61 QString getPreferredAudioInDevice() const override;
62 QString getPreferredAudioOutDevice() const override;
60 63
61protected:64protected:
62 bool internalActivate() override;65 bool internalActivate() override;
plugins/openvr/src/OpenVrHelpers.cpp+16−0
@@ -72,6 +72,21 @@ bool openVrSupported() { · 71 unmodified lines
72 return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent();72 return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent();
73}73}
74 74
75QString getVrSettingString(const char* section, const char* setting) {
76 QString result;
77 static const uint32_t BUFFER_SIZE = 1024;
78 static char BUFFER[BUFFER_SIZE];
79 vr::IVRSettings * vrSettings = vr::VRSettings();
80 if (vrSettings) {
81 vr::EVRSettingsError error = vr::VRSettingsError_None;
82 vrSettings->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, BUFFER, BUFFER_SIZE, &error);
83 if (error == vr::VRSettingsError_None) {
84 result = BUFFER;
85 }
86 }
87 return result;
88}
89
75vr::IVRSystem* acquireOpenVrSystem() {90vr::IVRSystem* acquireOpenVrSystem() {
76 bool hmdPresent = vr::VR_IsHmdPresent();91 bool hmdPresent = vr::VR_IsHmdPresent();
77 if (hmdPresent) {92 if (hmdPresent) {
@@ -82,6 +97,7 @@ vr::IVRSystem* acquireOpenVrSystem() { · 4 unmodified lines
82 #endif97 #endif
83 vr::EVRInitError eError = vr::VRInitError_None;98 vr::EVRInitError eError = vr::VRInitError_None;
84 activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);99 activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
100
85 #if DEV_BUILD101 #if DEV_BUILD
86 qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError;102 qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError;
87 #endif103 #endif
plugins/openvr/src/OpenVrHelpers.h+1−0
@@ -25,6 +25,7 @@ bool openVrQuitRequested(); · 24 unmodified lines
25void enableOpenVrKeyboard(PluginContainer* container);25void enableOpenVrKeyboard(PluginContainer* container);
26void disableOpenVrKeyboard();26void disableOpenVrKeyboard();
27bool isOpenVrKeyboardShown();27bool isOpenVrKeyboardShown();
28QString getVrSettingString(const char* section, const char* setting);
28 29
29 30
30template<typename F>31template<typename F>
plugins/openvr/src/ViveControllerManager.cpp+174−8
@@ -50,11 +50,15 @@ static const char* RENDER_CONTROLLERS = "Render Hand Controllers"; · 49 unmodified lines
50static const int MIN_PUCK_COUNT = 2;50static const int MIN_PUCK_COUNT = 2;
51static const int MIN_FEET_AND_HIPS = 3;51static const int MIN_FEET_AND_HIPS = 3;
52static const int MIN_FEET_HIPS_CHEST = 4;52static const int MIN_FEET_HIPS_CHEST = 4;
53static const int MIN_FEET_HIPS_HEAD = 4;
53static const int MIN_FEET_HIPS_SHOULDERS = 5;54static const int MIN_FEET_HIPS_SHOULDERS = 5;
55static const int MIN_FEET_HIPS_CHEST_HEAD = 5;
54static const int FIRST_FOOT = 0;56static const int FIRST_FOOT = 0;
55static const int SECOND_FOOT = 1;57static const int SECOND_FOOT = 1;
56static const int HIP = 2;58static const int HIP = 2;
57static const int CHEST = 3;59static const int CHEST = 3;
60static float HEAD_PUCK_Y_OFFSET = -0.0254f;
61static float HEAD_PUCK_Z_OFFSET = -0.152f;
58 62
59const char* ViveControllerManager::NAME { "OpenVR" };63const char* ViveControllerManager::NAME { "OpenVR" };
60 64
@@ -80,6 +84,28 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) · 19 unmodified lines
80 return (firstPuck.second.translation.x < secondPuck.second.translation.x);84 return (firstPuck.second.translation.x < secondPuck.second.translation.x);
81}85}
82 86
87static bool determineFeetOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) {
88 glm::vec3 poseAPosition = poseA.getTranslation();
89 glm::vec3 poseBPosition = poseB.getTranslation();
90
91 glm::vec3 poseAVector = poseAPosition - axisOrigin;
92 glm::vec3 poseBVector = poseBPosition - axisOrigin;
93
94 float poseAProjection = glm::dot(poseAVector, axis);
95 float poseBProjection = glm::dot(poseBVector, axis);
96 return (poseAProjection > poseBProjection);
97}
98
99static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) {
100 glm::mat4 finalHead = defaultToReferenceMat * defaultHead;
101 return glmExtractRotation(finalHead) * Vectors::UNIT_X;
102}
103
104static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) {
105 glm::mat4 finalHead = defaultToReferenceMat * defaultHead;
106 return extractTranslation(finalHead);
107}
108
83static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) {109static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) {
84 QString result;110 QString result;
85 auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult);111 auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult);
@@ -97,15 +123,18 @@ bool ViveControllerManager::isSupported() const { · 11 unmodified lines
97bool ViveControllerManager::activate() {123bool ViveControllerManager::activate() {
98 InputPlugin::activate();124 InputPlugin::activate();
99 125
100 _container->addMenu(MENU_PATH);
101 _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
102 [this] (bool clicked) { this->setRenderControllers(clicked); },
103 true, true);
104
105 if (!_system) {126 if (!_system) {
106 _system = acquireOpenVrSystem();127 _system = acquireOpenVrSystem();
107 }128 }
108 Q_ASSERT(_system);129
130 if (!_system) {
131 return false;
132 }
133
134 _container->addMenu(MENU_PATH);
135 _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
136 [this](bool clicked) { this->setRenderControllers(clicked); },
137 true, true);
109 138
110 enableOpenVrKeyboard(_container);139 enableOpenVrKeyboard(_container);
111 140
@@ -138,6 +167,14 @@ void ViveControllerManager::deactivate() { · 26 unmodified lines
138 _registeredWithInputMapper = false;167 _registeredWithInputMapper = false;
139}168}
140 169
170bool ViveControllerManager::isHeadControllerMounted() const {
171 if (_inputDevice && _inputDevice->isHeadControllerMounted()) {
172 return true;
173 }
174 vr::EDeviceActivityLevel activityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd);
175 return activityLevel == vr::k_EDeviceActivityLevel_UserInteraction;
176}
177
141void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {178void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
142 179
143 if (!_system) {180 if (!_system) {
@@ -175,6 +212,8 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro · 31 unmodified lines
175 _configStringMap[Config::FeetAndHips] = QString("FeetAndHips");212 _configStringMap[Config::FeetAndHips] = QString("FeetAndHips");
176 _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");213 _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");
177 _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");214 _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");
215 _configStringMap[Config::FeetHipsChestAndHead] = QString("FeetHipsChestAndHead");
216 _configStringMap[Config::FeetHipsAndHead] = QString("FeetHipsAndHead");
178 217
179 if (openVrSupported()) {218 if (openVrSupported()) {
180 createPreferences();219 createPreferences();
@@ -355,6 +394,23 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr · 174 unmodified lines
355 int firstShoulderIndex = 3;394 int firstShoulderIndex = 3;
356 int secondShoulderIndex = 4;395 int secondShoulderIndex = 4;
357 calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);396 calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);
397 } else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) {
398 glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
399 glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
400 glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
401 calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
402 calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
403 calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
404 _overrideHead = true;
405 } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) {
406 glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
407 glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
408 glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
409 calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
410 calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
411 calibrateChest(headPuckDefaultToReferenceMat, inputCalibration);
412 calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
413 _overrideHead = true;
358 } else {414 } else {
359 qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";415 qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
360 uncalibrate();416 uncalibrate();
@@ -369,6 +425,7 @@ void ViveControllerManager::InputDevice::uncalibrate() { · 8 unmodified lines
369 _pucksOffset.clear();425 _pucksOffset.clear();
370 _jointToPuckMap.clear();426 _jointToPuckMap.clear();
371 _calibrated = false;427 _calibrated = false;
428 _overrideHead = false;
372}429}
373 430
374void ViveControllerManager::InputDevice::updateCalibratedLimbs() {431void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
@@ -378,6 +435,10 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { · 3 unmodified lines
378 _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2);435 _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2);
379 _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM);436 _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM);
380 _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM);437 _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM);
438
439 if (_overrideHead) {
440 _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD);
441 }
381}442}
382 443
383controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const {444controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const {
@@ -443,6 +504,43 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u · 59 unmodified lines
443 }504 }
444}505}
445 506
507glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
508 glm::mat4 avatarToSensorMat = glm::inverse(inputCalibration.sensorToWorldMat) * inputCalibration.avatarMat;
509 glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
510 size_t headPuckIndex = _validTrackedObjects.size() - 1;
511 controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second;
512 glm::mat4 headPuckAvatarMat = createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()) * Matrices::Y_180;
513 glm::vec3 headPuckTranslation = extractTranslation(headPuckAvatarMat);
514 glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f);
515 glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
516
517 // check that the head puck z axis is not parrallel to the world up
518 const float EPSILON = 1.0e-4f;
519 glm::vec3 zAxis = glmExtractRotation(headPuckAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f);
520 if (fabsf(fabsf(glm::dot(glm::normalize(worldUp), glm::normalize(zAxis))) - 1.0f) < EPSILON) {
521 headPuckZAxis = glm::vec3(1.0f, 0.0f, 0.0f);
522 }
523
524 glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f);
525 glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis));
526 glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime));
527 glm::mat4 newHeadPuck = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f),
528 glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f));
529
530 glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f),
531 glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, HEAD_PUCK_Y_OFFSET, HEAD_PUCK_Z_OFFSET, 1.0f));
532
533 glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset;
534
535 glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
536
537 glm::mat4 currentHead = finalHeadPuck * defaultHeadOffset;
538
539 // calculate the defaultToRefrenceXform
540 glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
541 return defaultToReferenceMat;
542}
543
446void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) {544void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) {
447 // Populate the L/RS_CENTER/OUTER pseudo buttons, corresponding to a partition of the L/RS space based on the X/Y values.545 // Populate the L/RS_CENTER/OUTER pseudo buttons, corresponding to a partition of the L/RS space based on the X/Y values.
448 const float CENTER_DEADBAND = 0.6f;546 const float CENTER_DEADBAND = 0.6f;
@@ -630,6 +728,26 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer · 181 unmodified lines
630 }728 }
631}729}
632 730
731
732void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition) {
733 auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
734 auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
735 controller::Pose& firstFootPose = firstFoot.second;
736 controller::Pose& secondFootPose = secondFoot.second;
737
738 if (determineFeetOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) {
739 _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
740 _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
741 _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
742 _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose);
743 } else {
744 _jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first;
745 _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose);
746 _jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first;
747 _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose);
748 }
749}
750
633void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {751void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
634 _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;752 _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;
635 _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);753 _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);
@@ -658,7 +776,19 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo · 22 unmodified lines
658 _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first;776 _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first;
659 _pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, firstShoulder.second);777 _pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, firstShoulder.second);
660 }778 }
661} 779}
780
781void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
782 size_t headIndex = _validTrackedObjects.size() - 1;
783 const PuckPosePair& head = _validTrackedObjects[headIndex];
784
785 // assume the person is wearing the head puck on his/her forehead
786 glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
787 controller::Pose newHead = head.second.postTransform(defaultHeadOffset);
788
789 _jointToPuckMap[controller::HEAD] = head.first;
790 _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead);
791}
662 792
663 793
664void ViveControllerManager::InputDevice::loadSettings() {794void ViveControllerManager::InputDevice::loadSettings() {
@@ -694,6 +824,10 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu · 29 unmodified lines
694 _preferedConfig = Config::FeetHipsAndChest;824 _preferedConfig = Config::FeetHipsAndChest;
695 } else if (value == "FeetHipsAndShoulders") {825 } else if (value == "FeetHipsAndShoulders") {
696 _preferedConfig = Config::FeetHipsAndShoulders;826 _preferedConfig = Config::FeetHipsAndShoulders;
827 } else if (value == "FeetHipsChestAndHead") {
828 _preferedConfig = Config::FeetHipsChestAndHead;
829 } else if (value == "FeetHipsAndHead") {
830 _preferedConfig = Config::FeetHipsAndHead;
697 }831 }
698}832}
699 833
@@ -702,11 +836,43 @@ void ViveControllerManager::InputDevice::createPreferences() { · 2 unmodified lines
702 auto preferences = DependencyManager::get<Preferences>();836 auto preferences = DependencyManager::get<Preferences>();
703 static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration";837 static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration";
704 838
839 {
840 static const float MIN_VALUE = -3.0f;
841 static const float MAX_VALUE = 3.0f;
842 static const float STEP = 0.01f;
843
844 auto getter = [this]()->float { return HEAD_PUCK_Y_OFFSET; };
845 auto setter = [this](const float& value) { HEAD_PUCK_Y_OFFSET = value; };
846
847 auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckYOffset", getter, setter);
848 preference->setMin(MIN_VALUE);
849 preference->setMax(MAX_VALUE);
850 preference->setDecimals(3);
851 preference->setStep(STEP);
852 preferences->addPreference(preference);
853 }
854
855 {
856 static const float MIN_VALUE = -3.0f;
857 static const float MAX_VALUE = 3.0f;
858 static const float STEP = 0.01f;
859
860 auto getter = [this]()->float { return HEAD_PUCK_Z_OFFSET; };
861 auto setter = [this](const float& value) { HEAD_PUCK_Z_OFFSET = value; };
862
863 auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckXOffset", getter, setter);
864 preference->setMin(MIN_VALUE);
865 preference->setMax(MAX_VALUE);
866 preference->setStep(STEP);
867 preference->setDecimals(3);
868 preferences->addPreference(preference);
869 }
870
705 {871 {
706 auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; };872 auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; };
707 auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };873 auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };
708 auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);874 auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);
709 QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders"};875 QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders", "FeetHipsAndHead"};
710 preference->setItems(list);876 preference->setItems(list);
711 preferences->addPreference(preference);877 preferences->addPreference(preference);
712 878
plugins/openvr/src/ViveControllerManager.h+11−1
@@ -41,6 +41,8 @@ public: · 40 unmodified lines
41 const QString getName() const override { return NAME; }41 const QString getName() const override { return NAME; }
42 42
43 bool isHandController() const override { return true; }43 bool isHandController() const override { return true; }
44 bool isHeadController() const override { return true; }
45 bool isHeadControllerMounted() const;
44 46
45 bool activate() override;47 bool activate() override;
46 void deactivate() override;48 void deactivate() override;
@@ -54,6 +56,7 @@ private: · 7 unmodified lines
54 class InputDevice : public controller::InputDevice {56 class InputDevice : public controller::InputDevice {
55 public:57 public:
56 InputDevice(vr::IVRSystem*& system);58 InputDevice(vr::IVRSystem*& system);
59 bool isHeadControllerMounted() const { return _overrideHead; }
57 private:60 private:
58 // Device functions61 // Device functions
59 controller::Input::NamedVector getAvailableInputs() const override;62 controller::Input::NamedVector getAvailableInputs() const override;
@@ -67,6 +70,7 @@ private: · 7 unmodified lines
67 void calibrate(const controller::InputCalibrationData& inputCalibration);70 void calibrate(const controller::InputCalibrationData& inputCalibration);
68 void uncalibrate();71 void uncalibrate();
69 controller::Pose addOffsetToPuckPose(int joint) const;72 controller::Pose addOffsetToPuckPose(int joint) const;
73 glm::mat4 recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration);
70 void updateCalibratedLimbs();74 void updateCalibratedLimbs();
71 bool checkForCalibrationEvent();75 bool checkForCalibrationEvent();
72 void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);76 void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
@@ -84,11 +88,14 @@ private: · 11 unmodified lines
84 void loadSettings();88 void loadSettings();
85 void saveSettings() const;89 void saveSettings() const;
86 void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);90 void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
91 void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition);
87 void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);92 void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
88 void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);93 void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
89 94
90 void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,95 void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
91 int firstShoulderIndex, int secondShoulderIndex);96 int firstShoulderIndex, int secondShoulderIndex);
97 void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
98
92 99
93 class FilteredStick {100 class FilteredStick {
94 public:101 public:
@@ -119,6 +126,8 @@ private: · 24 unmodified lines
119 FeetAndHips,126 FeetAndHips,
120 FeetHipsAndChest,127 FeetHipsAndChest,
121 FeetHipsAndShoulders,128 FeetHipsAndShoulders,
129 FeetHipsChestAndHead,
130 FeetHipsAndHead
122 };131 };
123 Config _config { Config::Auto };132 Config _config { Config::Auto };
124 Config _preferedConfig { Config::Auto };133 Config _preferedConfig { Config::Auto };
@@ -137,7 +146,7 @@ private: · 12 unmodified lines
137 146
138 int _trackedControllers { 0 };147 int _trackedControllers { 0 };
139 vr::IVRSystem*& _system;148 vr::IVRSystem*& _system;
140 quint64 _timeTilCalibration { 0.0f };149 quint64 _timeTilCalibration { 0 };
141 float _leftHapticStrength { 0.0f };150 float _leftHapticStrength { 0.0f };
142 float _leftHapticDuration { 0.0f };151 float _leftHapticDuration { 0.0f };
143 float _rightHapticStrength { 0.0f };152 float _rightHapticStrength { 0.0f };
@@ -145,6 +154,7 @@ private: · 1 unmodified line
145 bool _triggersPressedHandled { false };154 bool _triggersPressedHandled { false };
146 bool _calibrated { false };155 bool _calibrated { false };
147 bool _timeTilCalibrationSet { false };156 bool _timeTilCalibrationSet { false };
157 bool _overrideHead { false };
148 mutable std::recursive_mutex _lock;158 mutable std::recursive_mutex _lock;
149 159
150 QString configToString(Config config);160 QString configToString(Config config);
scripts/defaultScripts.js+2−1
@@ -31,7 +31,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ · 30 unmodified lines
31 "system/tablet-ui/tabletUI.js"31 "system/tablet-ui/tabletUI.js"
32];32];
33var DEFAULT_SCRIPTS_SEPARATE = [33var DEFAULT_SCRIPTS_SEPARATE = [
34 "system/controllers/controllerScripts.js"34 "system/controllers/controllerScripts.js",
35 "system/chat.js"
35];36];
36 37
37// add a menu item for debugging38// add a menu item for debugging
scripts/developer/tests/avatarToWorldTests.js+127−0
@@ -0,0 +1,127 @@
1var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}";
2
3var debugSphereBaseProperties = {
4 type: "Sphere",
5 dimensions: { x: 0.2, y: 0.2, z: 0.2 },
6 dynamic: false,
7 collisionless: true,
8 gravity: { x: 0, y: 0, z: 0 },
9 lifetime: 10.0,
10 userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
11};
12
13var debugBoxBaseProperties = {
14 type: "Box",
15 dimensions: { x: 0.2, y: 0.2, z: 0.2 },
16 dynamic: false,
17 collisionless: true,
18 gravity: { x: 0, y: 0, z: 0 },
19 lifetime: 10.0,
20 userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
21};
22
23//jointToWorldPoint
24// create sphere for finger on left hand
25// each frame, calculate world position of finger, with some offset
26// update sphere to match this position
27var jointToWorldPointTest_sphereEntity;
28function jointToWorldPointTest() {
29 var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
30 var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
31 var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex);
32
33 var jointSphereProps = Object.create(debugSphereBaseProperties);
34 jointSphereProps.name = "jointToWorldPointTest_Sphere";
35 jointSphereProps.color = { blue: 240, green: 150, red: 150 };
36 jointSphereProps.position = worldPos;
37 jointToWorldPointTest_sphereEntity = Entities.addEntity(jointSphereProps);
38}
39function jointToWorldPointTest_update(deltaTime) {
40 var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
41 var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
42 var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex);
43 var newProperties = { position: worldPos };
44 Entities.editEntity(jointToWorldPointTest_sphereEntity, newProperties);
45}
46
47//jointToWorldDirection
48// create line in world space
49// each frame calculate world space direction of players head z axis
50// update line to match
51var jointToWorldDirectionTest_lineEntity;
52function jointToWorldDirectionTest() {
53 var jointIndex = MyAvatar.getJointIndex("Head");
54 var avatarPos = MyAvatar.getJointPosition(jointIndex);
55
56 var jointDir = { x: 1, y: 0, z: 1 };
57 var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex);
58 print(worldDir.x);
59 print(worldDir.y);
60 print(worldDir.z);
61 jointToWorldDirectionTest_lineEntity = Entities.addEntity({
62 type: "Line",
63 color: {red: 250, green: 0, blue: 0},
64 dimensions: {x: 5, y: 5, z: 5},
65 lifetime: 10.0,
66 linePoints: [{
67 x: 0,
68 y: 0,
69 z: 0
70 }, worldDir
71 ],
72 position : avatarPos,
73 });
74}
75function jointToWorldDirection_update(deltaTime) {
76 var jointIndex = MyAvatar.getJointIndex("Head");
77 var avatarPos = MyAvatar.getJointPosition(jointIndex);
78 var jointDir = { x: 1, y: 0, z: 0 };
79 var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex);
80 var newProperties = {
81 linePoints: [{
82 x: 0,
83 y: 0,
84 z: 0
85 }, worldDir
86 ],
87 position : avatarPos
88 };
89
90 Entities.editEntity(jointToWorldDirectionTest_lineEntity, newProperties);
91}
92
93//jointToWorldRotation
94// create box in world space
95// each frame calculate world space rotation of players head
96// update box rotation to match
97var jointToWorldRotationTest_boxEntity;
98function jointToWorldRotationTest() {
99 var jointIndex = MyAvatar.getJointIndex("Head");
100 var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
101 var jointRot = MyAvatar.getJointRotation(jointIndex);
102 var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex);
103
104 var boxProps = Object.create(debugBoxBaseProperties);
105 boxProps.name = "jointToWorldRotationTest_Box";
106 boxProps.color = { blue: 250, green: 250, red: 250 };
107 boxProps.position = jointPosition_WorldSpace;
108 boxProps.rotation = jointRot_WorldSpace;
109 jointToWorldRotationTest_boxEntity = Entities.addEntity(boxProps);
110}
111function jointToWorldRotationTest_update(deltaTime) {
112 var jointIndex = MyAvatar.getJointIndex("Head");
113 var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
114 var jointRot = MyAvatar.getJointRotation(jointIndex);
115 var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex);
116 var newProperties = { position: jointPosition_WorldSpace, rotation: jointRot_WorldSpace };
117 Entities.editEntity(jointToWorldRotationTest_boxEntity, newProperties);
118}
119
120jointToWorldPointTest();
121Script.update.connect(jointToWorldPointTest_update);
122
123jointToWorldDirectionTest();
124Script.update.connect(jointToWorldDirection_update);
125
126jointToWorldRotationTest();
127Script.update.connect(jointToWorldRotationTest_update);
scripts/developer/tests/worldToAvatarTests.js+134−0
@@ -0,0 +1,134 @@
1var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}";
2
3var debugSphereBaseProperties = {
4 type: "Sphere",
5 dimensions: { x: 0.2, y: 0.2, z: 0.2 },
6 dynamic: false,
7 collisionless: true,
8 gravity: { x: 0, y: 0, z: 0 },
9 lifetime: 10.0,
10 userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
11};
12
13var debugBoxBaseProperties = {
14 type: "Box",
15 dimensions: { x: 0.2, y: 0.2, z: 0.2 },
16 dynamic: false,
17 collisionless: true,
18 gravity: { x: 0, y: 0, z: 0 },
19 lifetime: 10.0,
20 userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
21};
22
23//worldToJointPoint
24// calculate position offset from joint using getJointPosition
25// pass through worldToJointPoint to get offset in joint space of players joint
26// create a blue sphere and attach it to players joint using the joint space offset
27// The two spheres should appear in the same place, but the blue sphere will follow the avatar
28function worldToJointPointTest() {
29 var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
30 var avatarPos = MyAvatar.position;
31
32 var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
33 var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
34 var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
35 var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
36
37 var jointSphereProps = Object.create(debugSphereBaseProperties);
38 jointSphereProps.name = "worldToJointPointTest_Joint";
39 jointSphereProps.color = { blue: 240, green: 150, red: 150 };
40 jointSphereProps.localPosition = jointPosition_JointSpaceOffset;
41 jointSphereProps.parentID = AVATAR_SELF_ID;
42 jointSphereProps.parentJointIndex = jointIndex;
43 Entities.addEntity(jointSphereProps);
44
45 var worldSphereProps = Object.create(debugSphereBaseProperties);
46 worldSphereProps.name = "worldToJointPointTest_World";
47 worldSphereProps.color = { blue: 150, green: 250, red: 150 };
48 worldSphereProps.position = jointPosition_WorldSpaceOffset;
49 Entities.addEntity(worldSphereProps);
50}
51
52//worldToJointDirection
53// create line and attach to avatars head
54// each frame calculate direction of world x axis in joint space of players head
55// update arrow orientation to match
56var worldToJointDirectionTest_lineEntity;
57function worldToJointDirectionTest() {
58 var jointIndex = MyAvatar.getJointIndex("Head");
59
60 var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
61 var jointOffset_WorldSpace = { x: 0, y: 0, z: 0 };
62 var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
63 var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
64
65 var worldDir = { x: 1, y: 0, z: 0 };
66 var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex);
67
68 worldToJointDirectionTest_lineEntity = Entities.addEntity({
69 type: "Line",
70 color: {red: 200, green: 250, blue: 0},
71 dimensions: {x: 5, y: 5, z: 5},
72 lifetime: 10.0,
73 linePoints: [{
74 x: 0,
75 y: 0,
76 z: 0
77 }, avatarDir
78 ],
79 localPosition : jointOffset_WorldSpace,
80 parentID : AVATAR_SELF_ID,
81 parentJointIndex : jointIndex
82 });
83}
84
85function worldToJointDirectionTest_update(deltaTime) {
86 var jointIndex = MyAvatar.getJointIndex("Head");
87 var worldDir = { x: 1, y: 0, z: 0 };
88 var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex);
89 var newProperties = { linePoints: [{
90 x: 0,
91 y: 0,
92 z: 0
93 }, avatarDir
94 ]};
95
96 Entities.editEntity(worldToJointDirectionTest_lineEntity, newProperties);
97}
98
99//worldToJointRotation
100// create box and parent to some player joint
101// convert world identity rotation to joint space rotation
102// each frame, update box with new orientation
103var worldToJointRotationTest_boxEntity;
104function worldToJointRotationTest() {
105 var jointIndex = MyAvatar.getJointIndex("RightHandPinky4");
106 var avatarPos = MyAvatar.position;
107
108 var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
109 var jointOffset_WorldSpace = { x: 0.0, y: 0, z: 0 };
110 var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
111 var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
112
113 var jointBoxProps = Object.create(debugBoxBaseProperties);
114 jointBoxProps.name = "worldToJointRotationTest_Box";
115 jointBoxProps.color = { blue: 0, green: 0, red: 250 };
116 jointBoxProps.localPosition = jointPosition_JointSpaceOffset;
117 jointBoxProps.parentID = AVATAR_SELF_ID;
118 jointBoxProps.parentJointIndex = jointIndex;
119 worldToJointRotationTest_boxEntity = Entities.addEntity(jointBoxProps);
120}
121function worldToJointRotationTest_update(deltaTime) {
122 var jointIndex = MyAvatar.getJointIndex("RightHandPinky4");
123 var worldRot = Quat.fromPitchYawRollDegrees(0,0,0);
124 var avatarRot = MyAvatar.worldToJointRotation(worldRot, jointIndex);
125 var newProperties = { localRotation: avatarRot };
126 Entities.editEntity(worldToJointRotationTest_boxEntity, newProperties);
127}
128
129worldToJointPointTest();
130worldToJointDirectionTest();
131worldToJointRotationTest();
132
133Script.update.connect(worldToJointDirectionTest_update);
134Script.update.connect(worldToJointRotationTest_update);
scripts/developer/utilities/render/deferredLighting.qml+16−6
@@ -64,6 +64,7 @@ Column { · 63 unmodified lines
64 "Point:LightingModel:enablePointLight",64 "Point:LightingModel:enablePointLight",
65 "Spot:LightingModel:enableSpotLight",65 "Spot:LightingModel:enableSpotLight",
66 "Light Contour:LightingModel:showLightContour",66 "Light Contour:LightingModel:showLightContour",
67 "Zone Stack:DrawZoneStack:enabled",
67 "Shadow:RenderShadowTask:enabled"68 "Shadow:RenderShadowTask:enabled"
68 ]69 ]
69 CheckBox {70 CheckBox {
@@ -162,13 +163,9 @@ Column { · 92 unmodified lines
162 }163 }
163 }164 }
164 165
166 Row {
165 Column {167 Column {
166 id: metas168
167 CheckBox {
168 text: "Metas"
169 checked: Render.getConfig("DrawMetaBounds")["enabled"]
170 onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked }
171 }
172 CheckBox {169 CheckBox {
173 text: "Opaques"170 text: "Opaques"
174 checked: Render.getConfig("DrawOpaqueBounds")["enabled"]171 checked: Render.getConfig("DrawOpaqueBounds")["enabled"]
@@ -189,11 +186,24 @@ Column { · 14 unmodified lines
189 checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"]186 checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"]
190 onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked }187 onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked }
191 }188 }
189 }
190 Column {
191 CheckBox {
192 text: "Metas"
193 checked: Render.getConfig("DrawMetaBounds")["enabled"]
194 onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked }
195 }
196 CheckBox {
197 text: "Lights"
198 checked: Render.getConfig("DrawLightBounds")["enabled"]
199 onCheckedChanged: { Render.getConfig("DrawLightBounds")["enabled"] = checked; }
200 }
192 CheckBox {201 CheckBox {
193 text: "Zones"202 text: "Zones"
194 checked: Render.getConfig("DrawZones")["enabled"]203 checked: Render.getConfig("DrawZones")["enabled"]
195 onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; }204 onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; }
196 }205 }
197 }206 }
207 }
198}208}
199 209
scripts/developer/utilities/render/stats.qml+6−7
@@ -20,9 +20,9 @@ Item { · 19 unmodified lines
20 id: stats20 id: stats
21 spacing: 821 spacing: 8
22 anchors.fill:parent22 anchors.fill:parent
23 23
24 property var config: Render.getConfig("Stats")24 property var config: Render.getConfig("Stats")
25 25
26 function evalEvenHeight() {26 function evalEvenHeight() {
27 // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?27 // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?
28 return (height - spacing * (children.length - 1)) / children.length28 return (height - spacing * (children.length - 1)) / children.length
@@ -81,7 +81,7 @@ Item { · 52 unmodified lines
81 color: "#1AC567"81 color: "#1AC567"
82 },82 },
83 {83 {
84 prop: "textureGPUTransferCount",84 prop: "texturePendingGPUTransferCount",
85 label: "Transfer",85 label: "Transfer",
86 color: "#9495FF"86 color: "#9495FF"
87 }87 }
@@ -158,7 +158,7 @@ Item { · 70 unmodified lines
158 }158 }
159 ]159 ]
160 }160 }
161 161
162 PlotPerf {162 PlotPerf {
163 title: "State Changes"163 title: "State Changes"
164 height: parent.evalEvenHeight()164 height: parent.evalEvenHeight()
@@ -180,7 +180,7 @@ Item { · 15 unmodified lines
180 color: "#1AC567"180 color: "#1AC567"
181 }181 }
182 ]182 ]
183 } 183 }
184 184
185 property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")185 property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")
186 property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")186 property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
@@ -211,7 +211,7 @@ Item { · 24 unmodified lines
211 color: "#FED959"211 color: "#FED959"
212 }212 }
213 ]213 ]
214 } 214 }
215 215
216 PlotPerf {216 PlotPerf {
217 title: "Timing"217 title: "Timing"
@@ -250,4 +250,3 @@ Item { · 32 unmodified lines
250 }250 }
251 251
252}252}
253
scripts/system/assets/sounds/snap.wav+0−0

Binary file not shown.

scripts/system/assets/sounds/sound-print-photo.wav+0−0

Binary file not shown.

scripts/system/bubble.js+11−6
@@ -10,7 +10,7 @@ · 9 unmodified lines
10// Distributed under the Apache License, Version 2.0.10// Distributed under the Apache License, Version 2.0.
11// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html11// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12//12//
13/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */13/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation, UserActivityLogger */
14 14
15(function () { // BEGIN LOCAL_SCOPE15(function () { // BEGIN LOCAL_SCOPE
16 var button;16 var button;
@@ -76,6 +76,7 @@ · 59 unmodified lines
76 // Called from the C++ scripting interface to show the bubble overlay76 // Called from the C++ scripting interface to show the bubble overlay
77 function enteredIgnoreRadius() {77 function enteredIgnoreRadius() {
78 createOverlays();78 createOverlays();
79 UserActivityLogger.bubbleActivated();
79 }80 }
80 81
81 // Used to set the state of the bubble HUD button82 // Used to set the state of the bubble HUD button
@@ -139,10 +140,14 @@ · 57 unmodified lines
139 }140 }
140 141
141 // When the space bubble is toggled...142 // When the space bubble is toggled...
142 function onBubbleToggled() {143 // NOTE: the c++ calls this with just the first param -- we added a second
143 var bubbleActive = Users.getIgnoreRadiusEnabled();144 // just for not logging the initial state of the bubble when we startup.
144 writeButtonProperties(bubbleActive);145 function onBubbleToggled(enabled, doNotLog) {
145 if (bubbleActive) {146 writeButtonProperties(enabled);
147 if (doNotLog !== true) {
148 UserActivityLogger.bubbleToggled(enabled);
149 }
150 if (enabled) {
146 createOverlays();151 createOverlays();
147 } else {152 } else {
148 hideOverlays();153 hideOverlays();
@@ -163,7 +168,7 @@ · 14 unmodified lines
163 sortOrder: 4168 sortOrder: 4
164 });169 });
165 170
166 onBubbleToggled();171 onBubbleToggled(Users.getIgnoreRadiusEnabled(), true); // pass in true so we don't log this initial one in the UserActivity table
167 172
168 button.clicked.connect(Users.toggleIgnoreRadius);173 button.clicked.connect(Users.toggleIgnoreRadius);
169 Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled);174 Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled);
scripts/system/chat.jsrenamed from unpublishedScripts/marketplace/chat/Chat.js+1−1
@@ -9,7 +9,7 @@ · 8 unmodified lines
9 9
10(function() {10(function() {
11 11
12 var webPageURL = "ChatPage.html"; // URL of tablet web page.12 var webPageURL = Script.resolvePath("html/ChatPage.html"); // URL of tablet web page.
13 var randomizeWebPageURL = true; // Set to true for debugging.13 var randomizeWebPageURL = true; // Set to true for debugging.
14 var lastWebPageURL = ""; // Last random URL of tablet web page.14 var lastWebPageURL = ""; // Last random URL of tablet web page.
15 var onChatPage = false; // True when chat web page is opened.15 var onChatPage = false; // True when chat web page is opened.
scripts/system/controllers/handControllerGrab.js+2−1
@@ -3334,7 +3334,8 @@ function MyController(hand) { · 3333 unmodified lines
3334 // If both secondary triggers squeezed, and the non-holding hand is empty, start scaling3334 // If both secondary triggers squeezed, and the non-holding hand is empty, start scaling
3335 if (this.secondarySqueezed() &&3335 if (this.secondarySqueezed() &&
3336 this.getOtherHandController().secondarySqueezed() &&3336 this.getOtherHandController().secondarySqueezed() &&
3337 this.getOtherHandController().state === STATE_OFF) {3337 this.grabbedThingID && this.getOtherHandController().grabbedThingID &&
3338 this.grabbedThingID == this.getOtherHandController().grabbedThingID) {
3338 this.scalingStartDistance = Vec3.length(Vec3.subtract(this.getHandPosition(),3339 this.scalingStartDistance = Vec3.length(Vec3.subtract(this.getHandPosition(),
3339 this.getOtherHandController().getHandPosition()));3340 this.getOtherHandController().getHandPosition()));
3340 this.scalingStartDimensions = props.dimensions;3341 this.scalingStartDimensions = props.dimensions;
scripts/system/controllers/teleport.js+17−37
@@ -366,45 +366,25 @@ function Teleporter() { · 365 unmodified lines
366}366}
367 367
368// related to repositioning the avatar after you teleport368// related to repositioning the avatar after you teleport
369var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"];
370var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5;
369function getAvatarFootOffset() {371function getAvatarFootOffset() {
370 var data = getJointData();
371 var upperLeg, lowerLeg, foot, toe, toeTop;
372 data.forEach(function(d) {
373
374 var jointName = d.joint;
375 if (jointName === "RightUpLeg") {
376 upperLeg = d.translation.y;
377 } else if (jointName === "RightLeg") {
378 lowerLeg = d.translation.y;
379 } else if (jointName === "RightFoot") {
380 foot = d.translation.y;
381 } else if (jointName === "RightToeBase") {
382 toe = d.translation.y;
383 } else if (jointName === "RightToe_End") {
384 toeTop = d.translation.y;
385 }
386 });
387
388 var offset = upperLeg + lowerLeg + foot + toe + toeTop;
389 offset = offset / 100;
390 return offset;
391}
392 372
393function getJointData() {373 // find a valid foot jointIndex
394 var allJointData = [];374 var footJointIndex = -1;
395 var jointNames = MyAvatar.jointNames;375 var i, l = FOOT_JOINT_NAMES.length;
396 jointNames.forEach(function(joint, index) {376 for (i = 0; i < l; i++) {
397 var translation = MyAvatar.getJointTranslation(index);377 footJointIndex = MyAvatar.getJointIndex(FOOT_JOINT_NAMES[i]);
398 var rotation = MyAvatar.getJointRotation(index);378 if (footJointIndex != -1) {
399 allJointData.push({379 break;
400 joint: joint,380 }
401 index: index,381 }
402 translation: translation,382 if (footJointIndex != -1) {
403 rotation: rotation383 // default vertical offset from foot to avatar root.
404 });384 return -MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex).y;
405 });385 } else {
406 386 return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale;
407 return allJointData;387 }
408}388}
409 389
410var leftPad = new ThumbPad('left');390var leftPad = new ThumbPad('left');
scripts/system/edit.js+173−63
@@ -12,7 +12,7 @@ · 11 unmodified lines
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13//13//
14 14
15/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, 15/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger,
16 Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */16 Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */
17 17
18(function() { // BEGIN LOCAL_SCOPE18(function() { // BEGIN LOCAL_SCOPE
@@ -80,27 +80,7 @@ selectionManager.addEventListener(function () { · 61 unmodified lines
80 }80 }
81 var type = Entities.getEntityProperties(selectedEntityID, "type").type;81 var type = Entities.getEntityProperties(selectedEntityID, "type").type;
82 if (type === "ParticleEffect") {82 if (type === "ParticleEffect") {
83 // Destroy the old particles web view first83 selectParticleEntity(selectedEntityID);
84 particleExplorerTool.destroyWebView();
85 particleExplorerTool.createWebView();
86 var properties = Entities.getEntityProperties(selectedEntityID);
87 var particleData = {
88 messageType: "particle_settings",
89 currentProperties: properties
90 };
91 selectedParticleEntityID = selectedEntityID;
92 particleExplorerTool.setActiveParticleEntity(selectedParticleEntityID);
93
94 particleExplorerTool.webView.webEventReceived.connect(function (data) {
95 data = JSON.parse(data);
96 if (data.messageType === "page_loaded") {
97 particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
98 }
99 });
100
101 // Switch to particle explorer
102 var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
103 tablet.sendToQml({method: 'selectTab', params: {id: 'particle'}});
104 } else {84 } else {
105 needToDestroyParticleExplorer = true;85 needToDestroyParticleExplorer = true;
106 }86 }
@@ -217,6 +197,32 @@ function hideMarketplace() { · 110 unmodified lines
217// }197// }
218// }198// }
219 199
200function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
201 // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
202 // position in the given direction.
203 var CORNERS = [
204 { x: 0, y: 0, z: 0 },
205 { x: 0, y: 0, z: 1 },
206 { x: 0, y: 1, z: 0 },
207 { x: 0, y: 1, z: 1 },
208 { x: 1, y: 0, z: 0 },
209 { x: 1, y: 0, z: 1 },
210 { x: 1, y: 1, z: 0 },
211 { x: 1, y: 1, z: 1 },
212 ];
213
214 // Go through all corners and find least (most negative) distance in front of position.
215 var distance = 0;
216 for (var i = 0, length = CORNERS.length; i < length; i++) {
217 var cornerVector =
218 Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions));
219 var cornerDistance = Vec3.dot(cornerVector, direction);
220 distance = Math.min(cornerDistance, distance);
221 }
222 position = Vec3.sum(Vec3.multiply(distance, direction), position);
223 return position;
224}
225
220var TOOLS_PATH = Script.resolvePath("assets/images/tools/");226var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
221var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit";227var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit";
222var GRABBABLE_ENTITIES_MENU_ITEM = "Create Entities As Grabbable";228var GRABBABLE_ENTITIES_MENU_ITEM = "Create Entities As Grabbable";
@@ -234,6 +240,32 @@ var toolBar = (function () { · 11 unmodified lines
234 var position = getPositionToCreateEntity();240 var position = getPositionToCreateEntity();
235 var entityID = null;241 var entityID = null;
236 if (position !== null && position !== undefined) {242 if (position !== null && position !== undefined) {
243 var direction;
244 if (Camera.mode === "entity" || Camera.mode === "independent") {
245 direction = Camera.orientation;
246 } else {
247 direction = MyAvatar.orientation;
248 }
249 direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z);
250
251 var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"];
252 if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
253 // Adjust position of entity per bounding box prior to creating it.
254 var registration = properties.registration;
255 if (registration === undefined) {
256 var DEFAULT_REGISTRATION = { x: 0.5, y: 0.5, z: 0.5 };
257 registration = DEFAULT_REGISTRATION;
258 }
259
260 var orientation = properties.orientation;
261 if (orientation === undefined) {
262 var DEFAULT_ORIENTATION = Quat.fromPitchYawRollDegrees(0, 0, 0);
263 orientation = DEFAULT_ORIENTATION;
264 }
265
266 position = adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation);
267 }
268
237 position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions);269 position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions);
238 properties.position = position;270 properties.position = position;
239 if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM)) {271 if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM)) {
@@ -243,6 +275,32 @@ var toolBar = (function () { · 3 unmodified lines
243 if (properties.type == "ParticleEffect") {275 if (properties.type == "ParticleEffect") {
244 selectParticleEntity(entityID);276 selectParticleEntity(entityID);
245 }277 }
278
279 var POST_ADJUST_ENTITY_TYPES = ["Model"];
280 if (POST_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
281 // Adjust position of entity per bounding box after it has been created and auto-resized.
282 var initialDimensions = Entities.getEntityProperties(entityID, ["dimensions"]).dimensions;
283 var DIMENSIONS_CHECK_INTERVAL = 200;
284 var MAX_DIMENSIONS_CHECKS = 10;
285 var dimensionsCheckCount = 0;
286 var dimensionsCheckFunction = function () {
287 dimensionsCheckCount++;
288 var properties = Entities.getEntityProperties(entityID, ["dimensions", "registrationPoint", "rotation"]);
289 if (!Vec3.equal(properties.dimensions, initialDimensions)) {
290 position = adjustPositionPerBoundingBox(position, direction, properties.registrationPoint,
291 properties.dimensions, properties.rotation);
292 position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions),
293 properties.dimensions);
294 Entities.editEntity(entityID, {
295 position: position
296 });
297 selectionManager._update();
298 } else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) {
299 Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL);
300 }
301 };
302 Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL);
303 }
246 } else {304 } else {
247 Window.notifyEditError("Can't create " + properties.type + ": " +305 Window.notifyEditError("Can't create " + properties.type + ": " +
248 properties.type + " would be out of bounds.");306 properties.type + " would be out of bounds.");
@@ -540,7 +598,6 @@ var toolBar = (function () { · 291 unmodified lines
540 598
541 that.toggle = function () {599 that.toggle = function () {
542 that.setActive(!isActive);600 that.setActive(!isActive);
543 activeButton.editProperties({isActive: isActive});
544 if (!isActive) {601 if (!isActive) {
545 tablet.gotoHomeScreen();602 tablet.gotoHomeScreen();
546 }603 }
@@ -564,6 +621,10 @@ var toolBar = (function () { · 17 unmodified lines
564 enabled: active621 enabled: active
565 }));622 }));
566 isActive = active;623 isActive = active;
624 activeButton.editProperties({isActive: isActive});
625
626 var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
627
567 if (!isActive) {628 if (!isActive) {
568 entityListTool.setVisible(false);629 entityListTool.setVisible(false);
569 gridTool.setVisible(false);630 gridTool.setVisible(false);
@@ -572,8 +633,8 @@ var toolBar = (function () { · 2 unmodified lines
572 selectionManager.clearSelections();633 selectionManager.clearSelections();
573 cameraManager.disable();634 cameraManager.disable();
574 selectionDisplay.triggerMapping.disable();635 selectionDisplay.triggerMapping.disable();
636 tablet.landscape = false;
575 } else {637 } else {
576 var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
577 tablet.loadQMLSource("Edit.qml");638 tablet.loadQMLSource("Edit.qml");
578 UserActivityLogger.enabledEdit();639 UserActivityLogger.enabledEdit();
579 entityListTool.setVisible(true);640 entityListTool.setVisible(true);
@@ -581,6 +642,8 @@ var toolBar = (function () { · 1 unmodified line
581 grid.setEnabled(true);642 grid.setEnabled(true);
582 propertiesTool.setVisible(true);643 propertiesTool.setVisible(true);
583 selectionDisplay.triggerMapping.enable();644 selectionDisplay.triggerMapping.enable();
645 print("starting tablet in landscape mode")
646 tablet.landscape = true;
584 // Not sure what the following was meant to accomplish, but it currently causes647 // Not sure what the following was meant to accomplish, but it currently causes
585 // everybody else to think that Interface has lost focus overall. fogbugzid:558648 // everybody else to think that Interface has lost focus overall. fogbugzid:558
586 // Window.setFocus();649 // Window.setFocus();
@@ -1290,7 +1353,7 @@ function parentSelectedEntities() { · 703 unmodified lines
1290 }1353 }
1291 });1354 });
1292 1355
1293 if(parentCheck) {1356 if (parentCheck) {
1294 Window.notify("Entities parented");1357 Window.notify("Entities parented");
1295 }else {1358 }else {
1296 Window.notify("Entities are already parented to last");1359 Window.notify("Entities are already parented to last");
@@ -1407,40 +1470,26 @@ function handeMenuEvent(menuItem) { · 110 unmodified lines
1407 }1470 }
1408 tooltip.show(false);1471 tooltip.show(false);
1409}1472}
1410function getPositionToCreateEntity() {
1411 var HALF_TREE_SCALE = 16384;
1412 var direction = Quat.getForward(MyAvatar.orientation);
1413 var distance = 1;
1414 var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, distance));
1415
1416 if (Camera.mode === "entity" || Camera.mode === "independent") {
1417 position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), distance));
1418 }
1419 position.y += 0.5;
1420 if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {
1421 return null;
1422 }
1423 return position;
1424}
1425 1473
1426function getPositionToImportEntity() {1474var HALF_TREE_SCALE = 16384;
1427 var dimensions = Clipboard.getContentsDimensions();
1428 var HALF_TREE_SCALE = 16384;
1429 var direction = Quat.getForward(MyAvatar.orientation);
1430 var longest = 1;
1431 longest = Math.sqrt(Math.pow(dimensions.x, 2) + Math.pow(dimensions.z, 2));
1432 var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, longest));
1433 1475
1476function getPositionToCreateEntity(extra) {
1477 var CREATE_DISTANCE = 2;
1478 var position;
1479 var delta = extra !== undefined ? extra : 0;
1434 if (Camera.mode === "entity" || Camera.mode === "independent") {1480 if (Camera.mode === "entity" || Camera.mode === "independent") {
1435 position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), longest));1481 position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta));
1482 } else {
1483 position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta));
1484 position.y += 0.5;
1436 }1485 }
1437 1486
1438 if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {1487 if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {
1439 return null;1488 return null;
1440 }1489 }
1441
1442 return position;1490 return position;
1443}1491}
1492
1444function importSVO(importURL) {1493function importSVO(importURL) {
1445 if (!Entities.canRez() && !Entities.canRezTmp()) {1494 if (!Entities.canRez() && !Entities.canRezTmp()) {
1446 Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);1495 Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);
@@ -1458,22 +1507,73 @@ function importSVO(importURL) { · 11 unmodified lines
1458 1507
1459 if (success) {1508 if (success) {
1460 var VERY_LARGE = 10000;1509 var VERY_LARGE = 10000;
1461 var position = {1510 var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE;
1462 x: 0,1511 var position = Vec3.ZERO;
1463 y: 0,1512 if (!isLargeImport) {
1464 z: 01513 position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2);
1465 };
1466 if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) {
1467 position = getPositionToImportEntity();
1468 }1514 }
1469 if (position !== null && position !== undefined) {1515 if (position !== null && position !== undefined) {
1470 var pastedEntityIDs = Clipboard.pasteEntities(position);1516 var pastedEntityIDs = Clipboard.pasteEntities(position);
1517 if (!isLargeImport) {
1518 // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move
1519 // entities after they're imported so that they're all the correct distance in front of and with geometric mean
1520 // centered on the avatar/camera direction.
1521 var deltaPosition = Vec3.ZERO;
1522
1523 var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]);
1524 var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"];
1525 if (NO_ADJUST_ENTITY_TYPES.indexOf(properties.type) === -1) {
1526 var targetDirection;
1527 if (Camera.mode === "entity" || Camera.mode === "independent") {
1528 targetDirection = Camera.orientation;
1529 } else {
1530 targetDirection = MyAvatar.orientation;
1531 }
1532 targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z);
1533
1534 var targetPosition = getPositionToCreateEntity();
1535 var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection.
1536 var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection.
1537 var entityPositions = [];
1538 for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
1539 var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
1540 "registrationPoint", "rotation"]);
1541 var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection,
1542 properties.registrationPoint, properties.dimensions, properties.rotation);
1543 var delta = Vec3.subtract(adjustedPosition, properties.position);
1544 var distance = Vec3.dot(delta, targetDirection);
1545 deltaParallel = Math.min(distance, deltaParallel);
1546 deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)),
1547 deltaPerpendicular);
1548 entityPositions[i] = properties.position;
1549 }
1550 deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular);
1551 deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular);
1552 }
1553
1554 if (grid.getSnapToGrid()) {
1555 var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
1556 "registrationPoint"]);
1557 var position = Vec3.sum(deltaPosition, properties.position);
1558 position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
1559 properties.registrationPoint), properties.dimensions, properties.registrationPoint);
1560 deltaPosition = Vec3.subtract(position, properties.position);
1561 }
1562
1563 if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
1564 for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
1565 Entities.editEntity(pastedEntityIDs[i], {
1566 position: Vec3.sum(deltaPosition, entityPositions[i])
1567 });
1568 }
1569 }
1570 }
1471 1571
1472 if (isActive) {1572 if (isActive) {
1473 selectionManager.setSelections(pastedEntityIDs);1573 selectionManager.setSelections(pastedEntityIDs);
1474 }1574 }
1475 } else {1575 } else {
1476 Window.notifyEditError("Can't import objects: objects would be out of bounds.");1576 Window.notifyEditError("Can't import entities: entities would be out of bounds.");
1477 }1577 }
1478 } else {1578 } else {
1479 Window.notifyEditError("There was an error importing the entity file.");1579 Window.notifyEditError("There was an error importing the entity file.");
@@ -1787,11 +1887,11 @@ var PropertiesTool = function (opts) { · 307 unmodified lines
1787 }1887 }
1788 pushCommandForSelections();1888 pushCommandForSelections();
1789 selectionManager._update();1889 selectionManager._update();
1790 } else if(data.type === 'parent') {1890 } else if (data.type === 'parent') {
1791 parentSelectedEntities();1891 parentSelectedEntities();
1792 } else if(data.type === 'unparent') {1892 } else if (data.type === 'unparent') {
1793 unparentSelectedEntities();1893 unparentSelectedEntities();
1794 } else if(data.type === 'saveUserData'){1894 } else if (data.type === 'saveUserData'){
1795 //the event bridge and json parsing handle our avatar id string differently.1895 //the event bridge and json parsing handle our avatar id string differently.
1796 var actualID = data.id.split('"')[1];1896 var actualID = data.id.split('"')[1];
1797 Entities.editEntity(actualID, data.properties);1897 Entities.editEntity(actualID, data.properties);
@@ -2083,6 +2183,10 @@ var selectedParticleEntityID = null; · 285 unmodified lines
2083 2183
2084function selectParticleEntity(entityID) {2184function selectParticleEntity(entityID) {
2085 var properties = Entities.getEntityProperties(entityID);2185 var properties = Entities.getEntityProperties(entityID);
2186
2187 if (properties.emitOrientation) {
2188 properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation);
2189 }
2086 var particleData = {2190 var particleData = {
2087 messageType: "particle_settings",2191 messageType: "particle_settings",
2088 currentProperties: properties2192 currentProperties: properties
@@ -2092,6 +2196,7 @@ function selectParticleEntity(entityID) { · 3 unmodified lines
2092 2196
2093 selectedParticleEntity = entityID;2197 selectedParticleEntity = entityID;
2094 particleExplorerTool.setActiveParticleEntity(entityID);2198 particleExplorerTool.setActiveParticleEntity(entityID);
2199
2095 particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));2200 particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
2096 2201
2097 // Switch to particle explorer2202 // Switch to particle explorer
@@ -2100,10 +2205,16 @@ function selectParticleEntity(entityID) { · 2 unmodified lines
2100}2205}
2101 2206
2102entityListTool.webView.webEventReceived.connect(function (data) {2207entityListTool.webView.webEventReceived.connect(function (data) {
2103 data = JSON.parse(data);2208 try {
2209 data = JSON.parse(data);
2210 } catch(e) {
2211 print("edit.js: Error parsing JSON: " + e.name + " data " + data)
2212 return;
2213 }
2214
2104 if (data.type === 'parent') {2215 if (data.type === 'parent') {
2105 parentSelectedEntities();2216 parentSelectedEntities();
2106 } else if(data.type === 'unparent') {2217 } else if (data.type === 'unparent') {
2107 unparentSelectedEntities();2218 unparentSelectedEntities();
2108 } else if (data.type === "selectionUpdate") {2219 } else if (data.type === "selectionUpdate") {
2109 var ids = data.entityIds;2220 var ids = data.entityIds;
@@ -2124,4 +2235,3 @@ entityListTool.webView.webEventReceived.connect(function (data) { · 14 unmodified lines
2124});2235});
2125 2236
2126}()); // END LOCAL_SCOPE2237}()); // END LOCAL_SCOPE
2127
scripts/system/html/ChatPage.htmlrenamed from unpublishedScripts/marketplace/chat/ChatPage.html+0−0

File renamed without changes.

scripts/system/html/SnapshotReview.html+5−1
@@ -28,7 +28,11 @@ · 27 unmodified lines
28 </form>28 </form>
29 </div>29 </div>
30 <input type="button" id="snap-button" onclick="takeSnapshot()" />30 <input type="button" id="snap-button" onclick="takeSnapshot()" />
31 <div id="snap-settings-right"></div>31 <div id="snap-settings-right">
32 <button type="image" class="blueButton" id="print-button" onclick="printToPolaroid()">
33 <div id="print-icon" class="print-icon print-icon-default">
34 </button>
35 </div>
32 </div>36 </div>
33</body>37</body>
34</html>38</html>
scripts/system/html/css/SnapshotReview.css+37−0
@@ -286,6 +286,43 @@ input[type=button].naked:active { · 285 unmodified lines
286// END styling of snapshot controls (bottom panel) and its contents286// END styling of snapshot controls (bottom panel) and its contents
287*/287*/
288 288
289
290/*
291// START polaroid styling
292*/
293
294#print-button {
295 width: 72px;
296 height: 72px;
297 margin-left: 30px;
298 margin-top: -10px;
299 box-sizing: content-box;
300 display: inline;
301 outline:none;
302}
303
304.print-icon {
305 margin: auto;
306}
307
308.print-icon-default {
309 background: url(../img/button-snap-print.svg) no-repeat;
310 margin-right: -1px;
311 width: 64px;
312 height: 64px;
313}
314
315.print-icon-loading {
316 background: url(../img/loader.gif) no-repeat;
317 width: 32px;
318 height: 32px;
319}
320
321/*
322// END polaroid styling
323*/
324
325
289/*326/*
290// START misc styling327// START misc styling
291*/328*/
scripts/system/html/css/edit-style.css+3−1
@@ -610,6 +610,7 @@ hr { · 609 unmodified lines
610.dropdown dl[dropped="true"] {610.dropdown dl[dropped="true"] {
611 color: #404040;611 color: #404040;
612 background: linear-gradient(#afafaf, #afafaf);612 background: linear-gradient(#afafaf, #afafaf);
613 z-index: 998;
613}614}
614 615
615.dropdown dt {616.dropdown dt {
@@ -657,7 +658,8 @@ hr { · 41 unmodified lines
657 font-family: FiraSans-SemiBold;658 font-family: FiraSans-SemiBold;
658 font-size: 15px;659 font-size: 15px;
659 color: #404040;660 color: #404040;
660 background-color: #afafaf661 background-color: #afafaf;
662 z-index: 999;
661}663}
662.dropdown li:hover {664.dropdown li:hover {
663 background-color: #00b4ef;665 background-color: #00b4ef;
scripts/system/html/css/hifi-style.css+1−1
@@ -172,4 +172,4 @@ input[type=radio]:active + label > span > span{ · 171 unmodified lines
172}172}
173.blueButton:disabled {173.blueButton:disabled {
174 background-image: linear-gradient(#FFFFFF, #AFAFAF);174 background-image: linear-gradient(#FFFFFF, #AFAFAF);
175}175}
scripts/system/html/css/jsoneditor.css+1−1
@@ -503,7 +503,7 @@ div.jsoneditor-contextmenu-root { · 502 unmodified lines
503div.jsoneditor-contextmenu {503div.jsoneditor-contextmenu {
504 position: absolute;504 position: absolute;
505 box-sizing: content-box;505 box-sizing: content-box;
506 z-index: 99999;506 z-index: 998;
507}507}
508 508
509div.jsoneditor-contextmenu ul,509div.jsoneditor-contextmenu ul,
scripts/system/html/entityProperties.html+3−0
@@ -51,6 +51,9 @@ · 50 unmodified lines
51 <option value="Hexagon">Hexagon</option>51 <option value="Hexagon">Hexagon</option>
52 <option value="Triangle">Triangle</option>52 <option value="Triangle">Triangle</option>
53 <option value="Octagon">Octagon</option>53 <option value="Octagon">Octagon</option>
54 <option value="Cylinder">Cylinder</option>
55 <option value="Cone">Cone</option>
56 <option value="Circle">Circle</option>
54 </select>57 </select>
55 </div>58 </div>
56 <div class="property text">59 <div class="property text">
scripts/system/html/img/button-snap-print.svg+25−0
@@ -0,0 +1,25 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 viewBox="0 0 199.8 231" style="enable-background:new 0 0 199.8 231;" xml:space="preserve">
5<style type="text/css">
6 .st0{fill:#FFFFFF;}
7</style>
8<g>
9 <g>
10 <path class="st0" d="M146.7,145.3H48V50.1h98.8V145.3z M56.2,122.8h83.1V57.6H56.2V122.8z"/>
11 <circle class="st0" cx="96.8" cy="83.5" r="10.9"/>
12 <path class="st0" d="M116.9,112.8h-40v-1.2c0-6.5,5.9-11.8,12.5-11.8H105c6.5,0,11.8,5.3,11.8,11.8V112.8z"/>
13 <polygon class="st0" points="155.5,80.6 155.5,125.7 170.9,39.2 74.6,19.6 70.3,43.7 78.3,43.7 81,28.8 161.7,45.6 "/>
14 </g>
15 <g>
16 <path class="st0" d="M12.4,213.7v-29.8h14c6.5,0,10,4.4,10,9.6c0,5.2-3.6,9.6-10,9.6h-7.6v10.6H12.4z M29.9,193.4
17 c0-2.5-1.9-4-4.4-4h-6.7v8h6.7C28,197.4,29.9,195.9,29.9,193.4z"/>
18 <path class="st0" d="M69.9,213.7L64,203.1h-4.7v10.6H53v-29.8h14c6.2,0,10.1,4.1,10.1,9.6c0,5.2-3.4,8.1-6.6,8.9l6.8,11.4H69.9z
19 M70.5,193.4c0-2.5-1.9-4-4.4-4h-6.7v8.1h6.7C68.6,197.5,70.5,195.9,70.5,193.4z"/>
20 <path class="st0" d="M94.4,213.7v-29.8h6.4v29.8H94.4z"/>
21 <path class="st0" d="M139.8,213.7l-14.2-19.5v19.5h-6.4v-29.8h6.5l13.8,18.7v-18.7h6.4v29.8H139.8z"/>
22 <path class="st0" d="M171.4,213.7v-24.2h-8.7v-5.6h23.8v5.6h-8.7v24.2H171.4z"/>
23 </g>
24</g>
25</svg>
scripts/system/html/js/SnapshotReview.js+101−20
@@ -21,6 +21,11 @@ var blastShareText = "Blast to my Connections", · 20 unmodified lines
21 hifiAlreadySharedText = "Already Shared to Snaps Feed",21 hifiAlreadySharedText = "Already Shared to Snaps Feed",
22 facebookShareText = "Share to Facebook",22 facebookShareText = "Share to Facebook",
23 twitterShareText = "Share to Twitter";23 twitterShareText = "Share to Twitter";
24
25function fileExtensionMatches(filePath, extension) {
26 return filePath.split('.').pop().toLowerCase() === extension;
27}
28
24function showSetupInstructions() {29function showSetupInstructions() {
25 var snapshotImagesDiv = document.getElementById("snapshot-images");30 var snapshotImagesDiv = document.getElementById("snapshot-images");
26 snapshotImagesDiv.className = "snapshotInstructions";31 snapshotImagesDiv.className = "snapshotInstructions";
@@ -276,10 +281,10 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi · 249 unmodified lines
276 if (!image_data.localPath) {281 if (!image_data.localPath) {
277 return;282 return;
278 }283 }
279 var id = "p" + (idCounter++),284 var imageContainer = document.createElement("DIV"),
280 imageContainer = document.createElement("DIV"),
281 img = document.createElement("IMG"),285 img = document.createElement("IMG"),
282 isGif;286 isGif = fileExtensionMatches(image_data.localPath, "gif"),
287 id = "p" + (isGif ? "1" : "0");
283 imageContainer.id = id;288 imageContainer.id = id;
284 imageContainer.style.width = "95%";289 imageContainer.style.width = "95%";
285 imageContainer.style.height = "240px";290 imageContainer.style.height = "240px";
@@ -290,22 +295,34 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi · 4 unmodified lines
290 imageContainer.style.position = "relative";295 imageContainer.style.position = "relative";
291 img.id = id + "img";296 img.id = id + "img";
292 img.src = image_data.localPath;297 img.src = image_data.localPath;
293 isGif = img.src.split('.').pop().toLowerCase() === "gif";
294 imageContainer.appendChild(img);298 imageContainer.appendChild(img);
295 document.getElementById("snapshot-images").appendChild(imageContainer);299 document.getElementById("snapshot-images").appendChild(imageContainer);
296 paths.push(image_data.localPath);300 paths.push(image_data.localPath);
297 if (isGif) {301 img.onload = function () {
298 imageContainer.innerHTML += '<span class="gifLabel">GIF</span>';302 if (isGif) {
299 }303 imageContainer.innerHTML += '<span class="gifLabel">GIF</span>';
300 if (!isGifLoading) {304 }
301 appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast);305 if (!isGifLoading) {
302 }306 appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast);
303 if (!isGifLoading || (isShowingPreviousImages && !image_data.story_id)) {307 }
304 shareForUrl(id);308 if ((!isShowingPreviousImages && ((isGif && !isGifLoading) || !isGif)) || (isShowingPreviousImages && !image_data.story_id)) {
305 }309 shareForUrl(id);
306 if (isShowingPreviousImages && isLoggedIn && image_data.story_id) {310 }
307 updateShareInfo(id, image_data.story_id);311 if (isShowingPreviousImages && isLoggedIn && image_data.story_id) {
308 }312 updateShareInfo(id, image_data.story_id);
313 }
314 if (isShowingPreviousImages) {
315 requestPrintButtonUpdate();
316 }
317 };
318 img.onerror = function () {
319 img.onload = null;
320 img.src = image_data.errorPath;
321 EventBridge.emitWebEvent(JSON.stringify({
322 type: "snapshot",
323 action: "alertSnapshotLoadFailed"
324 }));
325 };
309}326}
310function showConfirmationMessage(selectedID, destination) {327function showConfirmationMessage(selectedID, destination) {
311 if (selectedID.id) {328 if (selectedID.id) {
@@ -632,9 +649,8 @@ window.onload = function () { · 320 unmodified lines
632 // The last element of the message contents list contains a bunch of options,649 // The last element of the message contents list contains a bunch of options,
633 // including whether or not we can share stuff650 // including whether or not we can share stuff
634 // The other elements of the list contain image paths.651 // The other elements of the list contain image paths.
635 652 if (messageOptions.containsGif === true) {
636 if (messageOptions.containsGif) {653 if (messageOptions.processingGif === true) {
637 if (messageOptions.processingGif) {
638 imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon654 imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon
639 message.image_data.push({ localPath: messageOptions.loadingGifPath });655 message.image_data.push({ localPath: messageOptions.loadingGifPath });
640 message.image_data.forEach(function (element, idx) {656 message.image_data.forEach(function (element, idx) {
@@ -661,10 +677,22 @@ window.onload = function () { · 20 unmodified lines
661 break;677 break;
662 case 'captureSettings':678 case 'captureSettings':
663 handleCaptureSetting(message.setting);679 handleCaptureSetting(message.setting);
680 break;
681 case 'setPrintButtonEnabled':
682 setPrintButtonEnabled();
683 break;
684 case 'setPrintButtonLoading':
685 setPrintButtonLoading();
686 break;
687 case 'setPrintButtonDisabled':
688 setPrintButtonDisabled();
664 break;689 break;
665 case 'snapshotUploadComplete':690 case 'snapshotUploadComplete':
666 var isGif = message.image_url.split('.').pop().toLowerCase() === "gif";691 var isGif = fileExtensionMatches(message.image_url, "gif");
667 updateShareInfo(isGif ? "p1" : "p0", message.story_id);692 updateShareInfo(isGif ? "p1" : "p0", message.story_id);
693 if (isPrintProcessing()) {
694 setPrintButtonEnabled();
695 }
668 break;696 break;
669 default:697 default:
670 console.log("Unknown message action received in SnapshotReview.js.");698 console.log("Unknown message action received in SnapshotReview.js.");
@@ -694,6 +722,59 @@ function takeSnapshot() { · 23 unmodified lines
694 }722 }
695}723}
696 724
725function isPrintDisabled() {
726 var printElement = document.getElementById('print-icon');
727
728 return printElement.classList.contains("print-icon") &&
729 printElement.classList.contains("print-icon-default") &&
730 document.getElementById('print-button').disabled;
731}
732function isPrintProcessing() {
733 var printElement = document.getElementById('print-icon');
734
735 return printElement.classList.contains("print-icon") &&
736 printElement.classList.contains("print-icon-loading") &&
737 document.getElementById('print-button').disabled;
738}
739function isPrintEnabled() {
740 var printElement = document.getElementById('print-icon');
741
742 return printElement.classList.contains("print-icon") &&
743 printElement.classList.contains("print-icon-default") &&
744 !document.getElementById('print-button').disabled;
745}
746
747function setPrintButtonLoading() {
748 document.getElementById('print-icon').className = "print-icon print-icon-loading";
749 document.getElementById('print-button').disabled = true;
750}
751function setPrintButtonDisabled() {
752 document.getElementById('print-icon').className = "print-icon print-icon-default";
753 document.getElementById('print-button').disabled = true;
754}
755function setPrintButtonEnabled() {
756 document.getElementById('print-button').disabled = false;
757 document.getElementById('print-icon').className = "print-icon print-icon-default";
758}
759
760function requestPrintButtonUpdate() {
761 EventBridge.emitWebEvent(JSON.stringify({
762 type: "snapshot",
763 action: "requestPrintButtonUpdate"
764 }));
765}
766
767function printToPolaroid() {
768 if (isPrintEnabled()) {
769 EventBridge.emitWebEvent(JSON.stringify({
770 type: "snapshot",
771 action: "printToPolaroid"
772 }));
773 } else {
774 setPrintButtonLoading();
775 }
776}
777
697function testInBrowser(test) {778function testInBrowser(test) {
698 if (test === 0) {779 if (test === 0) {
699 showSetupInstructions();780 showSetupInstructions();
scripts/system/html/js/entityProperties.js+62−127
@@ -20,7 +20,8 @@ var ICON_FOR_TYPE = { · 19 unmodified lines
20 Light: "p",20 Light: "p",
21 Zone: "o",21 Zone: "o",
22 PolyVox: "&#xe005;",22 PolyVox: "&#xe005;",
23 Multiple: "&#xe000;"23 Multiple: "&#xe000;",
24 PolyLine: "&#xe01b;"
24}25}
25 26
26var EDITOR_TIMEOUT_DURATION = 1500;27var EDITOR_TIMEOUT_DURATION = 1500;
@@ -82,11 +83,23 @@ function showElements(els, show) { · 55 unmodified lines
82 }83 }
83}84}
84 85
86function updateProperty(propertyName, propertyValue) {
87 var properties = {};
88 properties[propertyName] = propertyValue;
89 updateProperties(properties);
90}
91
92function updateProperties(properties) {
93 EventBridge.emitWebEvent(JSON.stringify({
94 id: lastEntityID,
95 type: "update",
96 properties: properties
97 }));
98}
99
85function createEmitCheckedPropertyUpdateFunction(propertyName) {100function createEmitCheckedPropertyUpdateFunction(propertyName) {
86 return function() {101 return function() {
87 EventBridge.emitWebEvent(102 updateProperty(propertyName, this.checked);
88 '{"id":' + lastEntityID + ', "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}'
89 );
90 };103 };
91}104}
92 105
@@ -105,13 +118,7 @@ function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { · 12 unmodified lines
105 var properties = {};118 var properties = {};
106 properties[group] = {};119 properties[group] = {};
107 properties[group][propertyName] = this.checked;120 properties[group][propertyName] = this.checked;
108 EventBridge.emitWebEvent(121 updateProperties(properties);
109 JSON.stringify({
110 id: lastEntityID,
111 type: "update",
112 properties: properties
113 })
114 );
115 };122 };
116}123}
117 124
@@ -119,10 +126,7 @@ function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { · 1 unmodified line
119 decimals = decimals == undefined ? 4 : decimals;126 decimals = decimals == undefined ? 4 : decimals;
120 return function() {127 return function() {
121 var value = parseFloat(this.value).toFixed(decimals);128 var value = parseFloat(this.value).toFixed(decimals);
122 129 updateProperty(propertyName, value);
123 EventBridge.emitWebEvent(
124 '{"id":' + lastEntityID + ', "type":"update", "properties":{"' + propertyName + '":' + value + '}}'
125 );
126 };130 };
127}131}
128 132
@@ -131,28 +135,14 @@ function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { · 2 unmodified lines
131 var properties = {};135 var properties = {};
132 properties[group] = {};136 properties[group] = {};
133 properties[group][propertyName] = this.value;137 properties[group][propertyName] = this.value;
134 EventBridge.emitWebEvent(138 updateProperties(properties);
135 JSON.stringify({
136 id: lastEntityID,
137 type: "update",
138 properties: properties,
139 })
140 );
141 };139 };
142}140}
143 141
144 142
145function createEmitTextPropertyUpdateFunction(propertyName) {143function createEmitTextPropertyUpdateFunction(propertyName) {
146 return function() {144 return function() {
147 var properties = {};145 updateProperty(propertyName, this.value);
148 properties[propertyName] = this.value;
149 EventBridge.emitWebEvent(
150 JSON.stringify({
151 id: lastEntityID,
152 type: "update",
153 properties: properties,
154 })
155 );
156 };146 };
157}147}
158 148
@@ -161,62 +151,44 @@ function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { · 2 unmodified lines
161 var properties = {};151 var properties = {};
162 properties[group] = {};152 properties[group] = {};
163 properties[group][propertyName] = this.value;153 properties[group][propertyName] = this.value;
164 EventBridge.emitWebEvent(154 updateProperties(properties);
165 JSON.stringify({
166 id: lastEntityID,
167 type: "update",
168 properties: properties,
169 })
170 );
171 };155 };
172}156}
173 157
174function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {158function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
175 return function() {159 return function() {
176 var data = {160 var properties = {};
177 id: lastEntityID,161 properties[property] = {
178 type: "update",
179 properties: {}
180 };
181 data.properties[property] = {
182 x: elX.value,162 x: elX.value,
183 y: elY.value,163 y: elY.value,
184 z: elZ.value,164 z: elZ.value,
185 };165 };
186 EventBridge.emitWebEvent(JSON.stringify(data));166 updateProperties(properties);
187 }167 }
188};168};
189 169
190function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {170function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {
191 return function() {171 return function() {
192 var data = {172 var properties = {};
193 id: lastEntityID,173 properties[group] = {};
194 type: "update",174 properties[group][property] = {
195 properties: {}
196 };
197 data.properties[group] = {};
198 data.properties[group][property] = {
199 x: elX.value,175 x: elX.value,
200 y: elY.value,176 y: elY.value,
201 z: elZ ? elZ.value : 0,177 z: elZ ? elZ.value : 0,
202 };178 };
203 EventBridge.emitWebEvent(JSON.stringify(data));179 updateProperties(properties);
204 }180 }
205};181};
206 182
207function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {183function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
208 return function() {184 return function() {
209 var data = {185 var properties = {};
210 id: lastEntityID,186 properties[property] = {
211 type: "update",
212 properties: {}
213 };
214 data.properties[property] = {
215 x: elX.value * multiplier,187 x: elX.value * multiplier,
216 y: elY.value * multiplier,188 y: elY.value * multiplier,
217 z: elZ.value * multiplier,189 z: elZ.value * multiplier,
218 };190 };
219 EventBridge.emitWebEvent(JSON.stringify(data));191 updateProperties(properties);
220 }192 }
221};193};
222 194
@@ -227,44 +199,35 @@ function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) · 4 unmodified lines
227};199};
228 200
229function emitColorPropertyUpdate(property, red, green, blue, group) {201function emitColorPropertyUpdate(property, red, green, blue, group) {
230 var data = {202 var properties = {};
231 id: lastEntityID,
232 type: "update",
233 properties: {}
234 };
235 if (group) {203 if (group) {
236 data.properties[group] = {};204 properties[group] = {};
237 data.properties[group][property] = {205 properties[group][property] = {
238 red: red,206 red: red,
239 green: green,207 green: green,
240 blue: blue,208 blue: blue,
241 };209 };
242 } else {210 } else {
243 data.properties[property] = {211 properties[property] = {
244 red: red,212 red: red,
245 green: green,213 green: green,
246 blue: blue,214 blue: blue,
247 };215 };
248 }216 }
249 EventBridge.emitWebEvent(JSON.stringify(data));217 updateProperties(properties);
250};218};
251 219
252 220
253function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {221function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {
254 return function() {222 return function() {
255 var data = {223 var properties = {};
256 id: lastEntityID,224 properties[group] = {};
257 type: "update",225 properties[group][property] = {
258 properties: {}
259 };
260 data.properties[group] = {};
261
262 data.properties[group][property] = {
263 red: elRed.value,226 red: elRed.value,
264 green: elGreen.value,227 green: elGreen.value,
265 blue: elBlue.value,228 blue: elBlue.value,
266 };229 };
267 EventBridge.emitWebEvent(JSON.stringify(data));230 updateProperties(properties);
268 }231 }
269};232};
270 233
@@ -277,18 +240,7 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen · 6 unmodified lines
277 // We've unchecked, so remove240 // We've unchecked, so remove
278 propertyValue = propertyValue.replace(subPropertyString + ",", "");241 propertyValue = propertyValue.replace(subPropertyString + ",", "");
279 }242 }
280 243 updateProperty(propertyName, propertyValue);
281 var _properties = {}
282 _properties[propertyName] = propertyValue;
283
284 EventBridge.emitWebEvent(
285 JSON.stringify({
286 id: lastEntityID,
287 type: "update",
288 properties: _properties
289 })
290 );
291
292}244}
293 245
294function setUserDataFromEditor(noUpdate) {246function setUserDataFromEditor(noUpdate) {
@@ -314,18 +266,11 @@ function setUserDataFromEditor(noUpdate) { · 19 unmodified lines
314 );266 );
315 return;267 return;
316 } else {268 } else {
317 EventBridge.emitWebEvent(269 updateProperty('userData', text);
318 JSON.stringify({
319 id: lastEntityID,
320 type: "update",
321 properties: {
322 userData: text
323 },
324 })
325 );
326 }270 }
327 }271 }
328}272}
273
329function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) {274function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) {
330 var properties = {};275 var properties = {};
331 var parsedData = {};276 var parsedData = {};
@@ -372,13 +317,7 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) { · 40 unmodified lines
372 317
373 userDataElement.value = properties['userData'];318 userDataElement.value = properties['userData'];
374 319
375 EventBridge.emitWebEvent(320 updateProperties(properties);
376 JSON.stringify({
377 id: lastEntityID,
378 type: "update",
379 properties: properties,
380 })
381 );
382}321}
383function userDataChanger(groupName, keyName, values, userDataElement, defaultValue) {322function userDataChanger(groupName, keyName, values, userDataElement, defaultValue) {
384 var val = {}, def = {};323 var val = {}, def = {};
@@ -900,7 +839,6 @@ function loaded() { · 515 unmodified lines
900 elCloneable.checked = parsedUserData["grabbableKey"].cloneable;839 elCloneable.checked = parsedUserData["grabbableKey"].cloneable;
901 elCloneableGroup.style.display = elCloneable.checked ? "block": "none";840 elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
902 elCloneableDynamic.checked = parsedUserData["grabbableKey"].cloneDynamic ? parsedUserData["grabbableKey"].cloneDynamic : properties.dynamic;841 elCloneableDynamic.checked = parsedUserData["grabbableKey"].cloneDynamic ? parsedUserData["grabbableKey"].cloneDynamic : properties.dynamic;
903 elDynamic.checked = elCloneable.checked ? false: properties.dynamic;
904 if (elCloneable.checked) {842 if (elCloneable.checked) {
905 if ("cloneLifetime" in parsedUserData["grabbableKey"]) {843 if ("cloneLifetime" in parsedUserData["grabbableKey"]) {
906 elCloneableLifetime.value = parsedUserData["grabbableKey"].cloneLifetime ? parsedUserData["grabbableKey"].cloneLifetime : 300;844 elCloneableLifetime.value = parsedUserData["grabbableKey"].cloneLifetime ? parsedUserData["grabbableKey"].cloneLifetime : 300;
@@ -1202,8 +1140,8 @@ function loaded() { · 295 unmodified lines
1202 });1140 });
1203 1141
1204 elGrabbable.addEventListener('change', function() {1142 elGrabbable.addEventListener('change', function() {
1205 if(elCloneable.checked) {1143 if (elCloneable.checked) {
1206 elGrabbable.checked = false;1144 elGrabbable.checked = false;
1207 }1145 }
1208 userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic);1146 userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic);
1209 });1147 });
@@ -1213,17 +1151,22 @@ function loaded() { · 3 unmodified lines
1213 elCloneable.addEventListener('change', function (event) {1151 elCloneable.addEventListener('change', function (event) {
1214 var checked = event.target.checked;1152 var checked = event.target.checked;
1215 if (checked) {1153 if (checked) {
1216 multiDataUpdater("grabbableKey",1154 multiDataUpdater("grabbableKey", {
1217 {cloneLifetime: elCloneableLifetime, cloneLimit: elCloneableLimit, cloneDynamic: elCloneableDynamic, cloneable: event.target},1155 cloneLifetime: elCloneableLifetime,
1218 elUserData, {});1156 cloneLimit: elCloneableLimit,
1157 cloneDynamic: elCloneableDynamic,
1158 cloneable: event.target,
1159 grabbable: null
1160 }, elUserData, {});
1219 elCloneableGroup.style.display = "block";1161 elCloneableGroup.style.display = "block";
1220 EventBridge.emitWebEvent(1162 updateProperty('dynamic', false);
1221 '{"id":' + lastEntityID + ', "type":"update", "properties":{"dynamic":false, "grabbable": false}}'
1222 );
1223 } else {1163 } else {
1224 multiDataUpdater("grabbableKey",1164 multiDataUpdater("grabbableKey", {
1225 {cloneLifetime: null, cloneLimit: null, cloneDynamic: null, cloneable: false},1165 cloneLifetime: null,
1226 elUserData, {});1166 cloneLimit: null,
1167 cloneDynamic: null,
1168 cloneable: false
1169 }, elUserData, {});
1227 elCloneableGroup.style.display = "none";1170 elCloneableGroup.style.display = "none";
1228 }1171 }
1229 });1172 });
@@ -1258,15 +1201,7 @@ function loaded() { · 28 unmodified lines
1258 showUserDataTextArea();1201 showUserDataTextArea();
1259 showNewJSONEditorButton();1202 showNewJSONEditorButton();
1260 hideSaveUserDataButton();1203 hideSaveUserDataButton();
1261 var properties = {};1204 updateProperty('userData', elUserData.value)
1262 properties['userData'] = elUserData.value;
1263 EventBridge.emitWebEvent(
1264 JSON.stringify({
1265 id: lastEntityID,
1266 type: "update",
1267 properties: properties,
1268 })
1269 );
1270 });1205 });
1271 1206
1272 elSaveUserData.addEventListener("click", function() {1207 elSaveUserData.addEventListener("click", function() {
scripts/system/html/js/eventBridgeLoader.js+1−1
@@ -13,7 +13,7 @@ var WebChannel; · 12 unmodified lines
13 13
14openEventBridge = function(callback) {14openEventBridge = function(callback) {
15 WebChannel = new QWebChannel(qt.webChannelTransport, function (channel) {15 WebChannel = new QWebChannel(qt.webChannelTransport, function (channel) {
16 EventBridge = WebChannel.objects.eventBridgeWrapper.eventBridge;16 EventBridge = WebChannel.objects.eventBridge;
17 callback(EventBridge);17 callback(EventBridge);
18 });18 });
19}19}
scripts/system/html/js/marketplacesInject.js+2−9
@@ -25,9 +25,7 @@ · 24 unmodified lines
25 var canWriteAssets = false;25 var canWriteAssets = false;
26 var xmlHttpRequest = null;26 var xmlHttpRequest = null;
27 var isPreparing = false; // Explicitly track download request status.27 var isPreparing = false; // Explicitly track download request status.
28 28
29 var lastPage = "https://metaverse.highfidelity.com/marketplace?";
30
31 function injectCommonCode(isDirectoryPage) {29 function injectCommonCode(isDirectoryPage) {
32 30
33 // Supporting styles from marketplaces.css.31 // Supporting styles from marketplaces.css.
@@ -67,7 +65,7 @@ · 33 unmodified lines
67 65
68 // Footer actions.66 // Footer actions.
69 $("#back-button").on("click", function () {67 $("#back-button").on("click", function () {
70 window.location = lastPage;68 window.history.back();
71 });69 });
72 $("#all-markets").on("click", function () {70 $("#all-markets").on("click", function () {
73 EventBridge.emitWebEvent(GOTO_DIRECTORY);71 EventBridge.emitWebEvent(GOTO_DIRECTORY);
@@ -344,12 +342,7 @@ · 270 unmodified lines
344 }342 }
345 }343 }
346 344
347 function locationChanged() {
348 lastPage = location.href;
349 }
350
351 // Load / unload.345 // Load / unload.
352 window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready().346 window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready().
353 window.addEventListener("hashchange", locationChanged);
354 347
355}());348}());
scripts/system/libraries/WebTablet.js+38−31
@@ -22,7 +22,9 @@ var DEFAULT_WIDTH = 0.4375; · 21 unmodified lines
22var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees22var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees
23var SENSOR_TO_ROOM_MATRIX = -2;23var SENSOR_TO_ROOM_MATRIX = -2;
24var CAMERA_MATRIX = -7;24var CAMERA_MATRIX = -7;
25var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};25var ROT_Y_180 = {x: 0.0, y: 1.0, z: 0, w: 0};
26var ROT_LANDSCAPE = {x: 1.0, y: 1.0, z: 0, w: 0};
27var ROT_LANDSCAPE_WINDOW = {x: 0.0, y: 0.0, z: 0.0, w: 0};
26var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};28var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};
27var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };29var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
28var INCHES_TO_METERS = 1 / 39.3701;30var INCHES_TO_METERS = 1 / 39.3701;
@@ -41,7 +43,7 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home- · 12 unmodified lines
41// returns object with two fields:43// returns object with two fields:
42// * position - position in front of the user44// * position - position in front of the user
43// * rotation - rotation of entity so it faces the user.45// * rotation - rotation of entity so it faces the user.
44function calcSpawnInfo(hand, height) {46function calcSpawnInfo(hand, tabletHeight) {
45 var finalPosition;47 var finalPosition;
46 48
47 var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;49 var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
@@ -51,30 +53,35 @@ function calcSpawnInfo(hand, height) { · 3 unmodified lines
51 hand = NO_HANDS;53 hand = NO_HANDS;
52 }54 }
53 55
56 var handController = null;
54 if (HMD.active && hand !== NO_HANDS) {57 if (HMD.active && hand !== NO_HANDS) {
55 var handController = getControllerWorldLocation(hand, true);58 handController = getControllerWorldLocation(hand, true);
56 59 }
57 var TABLET_UP_OFFSET = 0.1;60
58 var TABLET_FORWARD_OFFSET = 0.1;61 if (handController && handController.valid) {
59 var normal = Vec3.multiplyQbyV(handController.rotation, {x: 0, y: -1, z: 0});62 // Orient tablet per hand pitch and yaw.
60 var pitch = Math.asin(normal.y);63 // Angle it back similar to holding it like a book.
61 var MAX_PITCH = Math.PI / 4;64 // Move tablet up so that hand is at bottom.
62 if (pitch < -MAX_PITCH) {65 // Move tablet back so that hand is in front.
63 pitch = -MAX_PITCH;
64 } else if (pitch > MAX_PITCH) {
65 pitch = MAX_PITCH;
66 }
67 66
68 // rebuild normal from pitch and heading.67 var position = handController.position;
69 var heading = Math.atan2(normal.z, normal.x);68 var rotation = handController.rotation;
70 normal = {x: Math.cos(heading), y: Math.sin(pitch), z: Math.sin(heading)};69
70 if (hand === Controller.Standard.LeftHand) {
71 rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0));
72 } else {
73 rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, -90, 0));
74 }
75 var normal = Vec3.multiplyQbyV(rotation, Vec3.UNIT_NEG_Y);
76 var lookAt = Quat.lookAt(Vec3.ZERO, normal, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
77 var TABLET_RAKE_ANGLE = 30;
78 rotation = Quat.multiply(Quat.angleAxis(TABLET_RAKE_ANGLE, Vec3.multiplyQbyV(lookAt, Vec3.UNIT_X)), lookAt);
71 79
72 var position = Vec3.sum(handController.position, {x: 0, y: TABLET_UP_OFFSET, z: 0});80 var RELATIVE_SPAWN_OFFSET = { x: 0, y: 0.4, z: 0.05 };
73 var rotation = Quat.lookAt({x: 0, y: 0, z: 0}, normal, Y_AXIS);81 position = Vec3.sum(position, Vec3.multiplyQbyV(rotation, Vec3.multiply(tabletHeight, RELATIVE_SPAWN_OFFSET)));
74 var offset = Vec3.multiplyQbyV(rotation, {x: 0, y: height / 2, z: TABLET_FORWARD_OFFSET});
75 82
76 return {83 return {
77 position: Vec3.sum(offset, position),84 position: position,
78 rotation: rotation85 rotation: rotation
79 };86 };
80 } else {87 } else {
@@ -243,29 +250,29 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { · 162 unmodified lines
243};250};
244 251
245WebTablet.prototype.getDimensions = function() {252WebTablet.prototype.getDimensions = function() {
246 if (this.landscape) {253 return { x: this.width, y: this.height, z: this.depth };
247 return { x: this.width * 2, y: this.height, z: this.depth };
248 } else {
249 return { x: this.width, y: this.height, z: this.depth };
250 }
251};254};
252 255
253WebTablet.prototype.getTabletTextureResolution = function() {256WebTablet.prototype.getTabletTextureResolution = function() {
254 if (this.landscape) {257 if (this.landscape) {
255 return { x: TABLET_TEXTURE_RESOLUTION.x * 2, y: TABLET_TEXTURE_RESOLUTION.y };258 return { x: TABLET_TEXTURE_RESOLUTION.y , y: TABLET_TEXTURE_RESOLUTION.x };
256 } else {259 } else {
257 return TABLET_TEXTURE_RESOLUTION;260 return TABLET_TEXTURE_RESOLUTION;
258 }261 }
259};262};
260 263
261WebTablet.prototype.setLandscape = function(newLandscapeValue) {264WebTablet.prototype.setLandscape = function(newLandscapeValue) {
262 if (this.landscape == newLandscapeValue) {265 if (this.landscape === newLandscapeValue) {
263 return;266 return;
264 }267 }
268
265 this.landscape = newLandscapeValue;269 this.landscape = newLandscapeValue;
266 Overlays.editOverlay(this.tabletEntityID, { dimensions: this.getDimensions() });270 Overlays.editOverlay(this.tabletEntityID,
271 { rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) :
272 Quat.multiply(Camera.orientation, ROT_Y_180) });
267 Overlays.editOverlay(this.webOverlayID, {273 Overlays.editOverlay(this.webOverlayID, {
268 resolution: this.getTabletTextureResolution()274 resolution: this.getTabletTextureResolution(),
275 rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW)
269 });276 });
270};277};
271 278
@@ -407,7 +414,7 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos · 135 unmodified lines
407 414
408 return {415 return {
409 position: worldMousePosition,416 position: worldMousePosition,
410 rotation: Quat.multiply(Camera.orientation, ROT_Y_180)417 rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) : Quat.multiply(Camera.orientation, ROT_Y_180)
411 };418 };
412};419};
413 420
scripts/system/libraries/entityList.js+7−1
@@ -109,7 +109,13 @@ EntityListTool = function(opts) { · 108 unmodified lines
109 };109 };
110 110
111 webView.webEventReceived.connect(function(data) {111 webView.webEventReceived.connect(function(data) {
112 data = JSON.parse(data);112 try {
113 data = JSON.parse(data);
114 } catch(e) {
115 print("entityList.js: Error parsing JSON: " + e.name + " data " + data)
116 return;
117 }
118
113 if (data.type == "selectionUpdate") {119 if (data.type == "selectionUpdate") {
114 var ids = data.entityIds;120 var ids = data.entityIds;
115 var entityIDs = [];121 var entityIDs = [];
scripts/system/libraries/gridTool.js+22−8
@@ -79,7 +79,7 @@ Grid = function(opts) { · 78 unmodified lines
79 }79 }
80 }80 }
81 81
82 that.snapToSurface = function(position, dimensions) {82 that.snapToSurface = function(position, dimensions, registration) {
83 if (!snapToGrid) {83 if (!snapToGrid) {
84 return position;84 return position;
85 }85 }
@@ -88,14 +88,18 @@ Grid = function(opts) { · 2 unmodified lines
88 dimensions = { x: 0, y: 0, z: 0 };88 dimensions = { x: 0, y: 0, z: 0 };
89 }89 }
90 90
91 if (registration === undefined) {
92 registration = { x: 0.5, y: 0.5, z: 0.5 };
93 }
94
91 return {95 return {
92 x: position.x,96 x: position.x,
93 y: origin.y + (dimensions.y / 2),97 y: origin.y + (registration.y * dimensions.y),
94 z: position.z98 z: position.z
95 };99 };
96 }100 }
97 101
98 that.snapToGrid = function(position, majorOnly, dimensions) {102 that.snapToGrid = function(position, majorOnly, dimensions, registration) {
99 if (!snapToGrid) {103 if (!snapToGrid) {
100 return position;104 return position;
101 }105 }
@@ -104,6 +108,10 @@ Grid = function(opts) { · 2 unmodified lines
104 dimensions = { x: 0, y: 0, z: 0 };108 dimensions = { x: 0, y: 0, z: 0 };
105 }109 }
106 110
111 if (registration === undefined) {
112 registration = { x: 0.5, y: 0.5, z: 0.5 };
113 }
114
107 var spacing = majorOnly ? majorGridEvery : minorGridEvery;115 var spacing = majorOnly ? majorGridEvery : minorGridEvery;
108 116
109 position = Vec3.subtract(position, origin);117 position = Vec3.subtract(position, origin);
@@ -112,7 +120,7 @@ Grid = function(opts) { · 2 unmodified lines
112 position.y = Math.round(position.y / spacing) * spacing;120 position.y = Math.round(position.y / spacing) * spacing;
113 position.z = Math.round(position.z / spacing) * spacing;121 position.z = Math.round(position.z / spacing) * spacing;
114 122
115 return Vec3.sum(Vec3.sum(position, Vec3.multiply(0.5, dimensions)), origin);123 return Vec3.sum(Vec3.sum(position, Vec3.multiplyVbyV(registration, dimensions)), origin);
116 }124 }
117 125
118 that.snapToSpacing = function(delta, majorOnly) {126 that.snapToSpacing = function(delta, majorOnly) {
@@ -161,9 +169,9 @@ Grid = function(opts) { · 42 unmodified lines
161 169
162 if (data.origin) {170 if (data.origin) {
163 var pos = data.origin;171 var pos = data.origin;
164 pos.x = pos.x === undefined ? origin.x : pos.x;172 pos.x = pos.x === undefined ? origin.x : parseFloat(pos.x);
165 pos.y = pos.y === undefined ? origin.y : pos.y;173 pos.y = pos.y === undefined ? origin.y : parseFloat(pos.y);
166 pos.z = pos.z === undefined ? origin.z : pos.z;174 pos.z = pos.z === undefined ? origin.z : parseFloat(pos.z);
167 that.setPosition(pos, true);175 that.setPosition(pos, true);
168 }176 }
169 177
@@ -238,7 +246,13 @@ GridTool = function(opts) { · 68 unmodified lines
238 });246 });
239 247
240 webView.webEventReceived.connect(function(data) {248 webView.webEventReceived.connect(function(data) {
241 data = JSON.parse(data);249 try {
250 data = JSON.parse(data);
251 } catch(e) {
252 print("gridTool.js: Error parsing JSON: " + e.name + " data " + data)
253 return;
254 }
255
242 if (data.type == "init") {256 if (data.type == "init") {
243 horizontalGrid.emitUpdate();257 horizontalGrid.emitUpdate();
244 } else if (data.type == "update") {258 } else if (data.type == "update") {
scripts/system/makeUserConnection.js+2−3
@@ -613,7 +613,6 @@ · 612 unmodified lines
613 error = "All participants must be logged in to connect.";613 error = "All participants must be logged in to connect.";
614 }614 }
615 result = error ? {status: 'error', connection: error} : response;615 result = error ? {status: 'error', connection: error} : response;
616 UserActivityLogger.makeUserConnection(connectingId, false, error || response);
617 connectionRequestCompleted();616 connectionRequestCompleted();
618 } else {617 } else {
619 result = response;618 result = response;
@@ -668,8 +667,8 @@ · 48 unmodified lines
668 // to be sure the hand is still close enough. If not, we terminate667 // to be sure the hand is still close enough. If not, we terminate
669 // the interval, go back to the waiting state. If we make it668 // the interval, go back to the waiting state. If we make it
670 // the entire CONNECTING_TIME, we make the connection. We pass in669 // the entire CONNECTING_TIME, we make the connection. We pass in
671 // whether or not the connecting id is actually logged in, as now we 670 // whether or not the connecting id is actually logged in, as now we
672 // will allow to start the connection process but have it stop with a 671 // will allow to start the connection process but have it stop with a
673 // fail message before trying to call the backend if the other guy isn't672 // fail message before trying to call the backend if the other guy isn't
674 // logged in.673 // logged in.
675 function startConnecting(id, jointIndex, isLoggedIn) {674 function startConnecting(id, jointIndex, isLoggedIn) {
scripts/system/particle_explorer/dat.gui.min.js+0−95
@@ -1,95 +0,0 @@
1/**
2 * dat-gui JavaScript Controller Library
3 * http://code.google.com/p/dat-gui
4 *
5 * Copyright 2011 Data Arts Team, Google Creative Lab
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 */
13var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(f,a){a=a||document;var d=a.createElement("link");d.type="text/css";d.rel="stylesheet";d.href=f;a.getElementsByTagName("head")[0].appendChild(d)},inject:function(f,a){a=a||document;var d=document.createElement("style");d.type="text/css";d.innerHTML=f;a.getElementsByTagName("head")[0].appendChild(d)}}}();
14dat.utils.common=function(){var f=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(a[c])||(d[c]=a[c])},this);return d},defaults:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(d[c])&&(d[c]=a[c])},this);return d},compose:function(){var d=a.call(arguments);return function(){for(var e=a.call(arguments),c=d.length-1;0<=c;c--)e=[d[c].apply(this,e)];return e[0]}},
15each:function(a,e,c){if(a)if(f&&a.forEach&&a.forEach===f)a.forEach(e,c);else if(a.length===a.length+0)for(var b=0,p=a.length;b<p&&!(b in a&&e.call(c,a[b],b)===this.BREAK);b++);else for(b in a)if(e.call(c,a[b],b)===this.BREAK)break},defer:function(a){setTimeout(a,0)},toArray:function(d){return d.toArray?d.toArray():a.call(d)},isUndefined:function(a){return void 0===a},isNull:function(a){return null===a},isNaN:function(a){return a!==a},isArray:Array.isArray||function(a){return a.constructor===Array},
16isObject:function(a){return a===Object(a)},isNumber:function(a){return a===a+0},isString:function(a){return a===a+""},isBoolean:function(a){return!1===a||!0===a},isFunction:function(a){return"[object Function]"===Object.prototype.toString.call(a)}}}();
17dat.controllers.Controller=function(f){var a=function(a,e){this.initialValue=a[e];this.domElement=document.createElement("div");this.object=a;this.property=e;this.__onFinishChange=this.__onChange=void 0};f.extend(a.prototype,{onChange:function(a){this.__onChange=a;return this},onFinishChange:function(a){this.__onFinishChange=a;return this},setValue:function(a){this.object[this.property]=a;this.__onChange&&this.__onChange.call(this,a);this.updateDisplay();return this},getValue:function(){return this.object[this.property]},
18updateDisplay:function(){return this},isModified:function(){return this.initialValue!==this.getValue()}});return a}(dat.utils.common);
19dat.dom.dom=function(f){function a(b){if("0"===b||f.isUndefined(b))return 0;b=b.match(e);return f.isNull(b)?0:parseFloat(b[1])}var d={};f.each({HTMLEvents:["change"],MouseEvents:["click","mousemove","mousedown","mouseup","mouseover"],KeyboardEvents:["keydown"]},function(b,a){f.each(b,function(b){d[b]=a})});var e=/(\d+(\.\d+)?)px/,c={makeSelectable:function(b,a){void 0!==b&&void 0!==b.style&&(b.onselectstart=a?function(){return!1}:function(){},b.style.MozUserSelect=a?"auto":"none",b.style.KhtmlUserSelect=
20a?"auto":"none",b.unselectable=a?"on":"off")},makeFullscreen:function(b,a,c){f.isUndefined(a)&&(a=!0);f.isUndefined(c)&&(c=!0);b.style.position="absolute";a&&(b.style.left=0,b.style.right=0);c&&(b.style.top=0,b.style.bottom=0)},fakeEvent:function(b,a,c,e){c=c||{};var r=d[a];if(!r)throw Error("Event type "+a+" not supported.");var n=document.createEvent(r);switch(r){case "MouseEvents":n.initMouseEvent(a,c.bubbles||!1,c.cancelable||!0,window,c.clickCount||1,0,0,c.x||c.clientX||0,c.y||c.clientY||0,!1,
21!1,!1,!1,0,null);break;case "KeyboardEvents":r=n.initKeyboardEvent||n.initKeyEvent;f.defaults(c,{cancelable:!0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,keyCode:void 0,charCode:void 0});r(a,c.bubbles||!1,c.cancelable,window,c.ctrlKey,c.altKey,c.shiftKey,c.metaKey,c.keyCode,c.charCode);break;default:n.initEvent(a,c.bubbles||!1,c.cancelable||!0)}f.defaults(n,e);b.dispatchEvent(n)},bind:function(a,e,d,f){a.addEventListener?a.addEventListener(e,d,f||!1):a.attachEvent&&a.attachEvent("on"+e,d);return c},
22unbind:function(a,e,d,f){a.removeEventListener?a.removeEventListener(e,d,f||!1):a.detachEvent&&a.detachEvent("on"+e,d);return c},addClass:function(a,e){if(void 0===a.className)a.className=e;else if(a.className!==e){var d=a.className.split(/ +/);-1==d.indexOf(e)&&(d.push(e),a.className=d.join(" ").replace(/^\s+/,"").replace(/\s+$/,""))}return c},removeClass:function(a,e){if(e){if(void 0!==a.className)if(a.className===e)a.removeAttribute("class");else{var d=a.className.split(/ +/),f=d.indexOf(e);-1!=
23f&&(d.splice(f,1),a.className=d.join(" "))}}else a.className=void 0;return c},hasClass:function(a,c){return(new RegExp("(?:^|\\s+)"+c+"(?:\\s+|$)")).test(a.className)||!1},getWidth:function(b){b=getComputedStyle(b);return a(b["border-left-width"])+a(b["border-right-width"])+a(b["padding-left"])+a(b["padding-right"])+a(b.width)},getHeight:function(b){b=getComputedStyle(b);return a(b["border-top-width"])+a(b["border-bottom-width"])+a(b["padding-top"])+a(b["padding-bottom"])+a(b.height)},getOffset:function(a){var c=
24{left:0,top:0};if(a.offsetParent){do c.left+=a.offsetLeft,c.top+=a.offsetTop;while(a=a.offsetParent)}return c},isActive:function(a){return a===document.activeElement&&(a.type||a.href)}};return c}(dat.utils.common);
25dat.controllers.OptionController=function(f,a,d){var e=function(c,b,f){e.superclass.call(this,c,b);var q=this;this.__select=document.createElement("select");if(d.isArray(f)){var l={};d.each(f,function(a){l[a]=a});f=l}d.each(f,function(a,b){var c=document.createElement("option");c.innerHTML=b;c.setAttribute("value",a);q.__select.appendChild(c)});this.updateDisplay();a.bind(this.__select,"change",function(){q.setValue(this.options[this.selectedIndex].value)});this.domElement.appendChild(this.__select)};
26e.superclass=f;d.extend(e.prototype,f.prototype,{setValue:function(a){a=e.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue());return a},updateDisplay:function(){this.__select.value=this.getValue();return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
27dat.controllers.NumberController=function(f,a){function d(a){a=a.toString();return-1<a.indexOf(".")?a.length-a.indexOf(".")-1:0}var e=function(c,b,f){e.superclass.call(this,c,b);f=f||{};this.__min=f.min;this.__max=f.max;this.__step=f.step;a.isUndefined(this.__step)?this.__impliedStep=0==this.initialValue?1:Math.pow(10,Math.floor(Math.log(Math.abs(this.initialValue))/Math.LN10))/10:this.__impliedStep=this.__step;this.__precision=d(this.__impliedStep)};e.superclass=f;a.extend(e.prototype,f.prototype,
28{setValue:function(a){void 0!==this.__min&&a<this.__min?a=this.__min:void 0!==this.__max&&a>this.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return e.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__impliedStep=this.__step=a;this.__precision=d(a);return this}});return e}(dat.controllers.Controller,dat.utils.common);
29dat.controllers.NumberControllerBox=function(f,a,d){var e=function(c,b,f){function q(){var a=parseFloat(n.__input.value);d.isNaN(a)||n.setValue(a)}function l(a){var b=u-a.clientY;n.setValue(n.getValue()+b*n.__impliedStep);u=a.clientY}function r(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",r)}this.__truncationSuspended=!1;e.superclass.call(this,c,b,f);var n=this,u;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",q);a.bind(this.__input,
30"blur",function(){q();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",l);a.bind(window,"mouseup",r);u=b.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,f.prototype,{updateDisplay:function(){var a=this.__input,b;if(this.__truncationSuspended)b=
31this.getValue();else{b=this.getValue();var d=Math.pow(10,this.__precision);b=Math.round(b*d)/d}a.value=b;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common);
32dat.controllers.NumberControllerSlider=function(f,a,d,e,c){function b(a,b,c,e,d){return e+(a-b)/(c-b)*(d-e)}var p=function(c,e,d,f,u){function A(c){c.preventDefault();var e=a.getOffset(k.__background),d=a.getWidth(k.__background);k.setValue(b(c.clientX,e.left,e.left+d,k.__min,k.__max));return!1}function g(){a.unbind(window,"mousemove",A);a.unbind(window,"mouseup",g);k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())}p.superclass.call(this,c,e,{min:d,max:f,step:u});var k=this;this.__background=
33document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",A);a.bind(window,"mouseup",g);A(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=f;p.useDefaultStyles=function(){d.inject(c)};e.extend(p.prototype,f.prototype,{updateDisplay:function(){var a=
34(this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}");
35dat.controllers.FunctionController=function(f,a,d){var e=function(c,b,d){e.superclass.call(this,c,b);var f=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===d?"Fire":d;a.bind(this.__button,"click",function(a){a.preventDefault();f.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};e.superclass=f;d.extend(e.prototype,f.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.getValue().call(this.object);
36this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
37dat.controllers.BooleanController=function(f,a,d){var e=function(c,b){e.superclass.call(this,c,b);var d=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){d.setValue(!d.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};e.superclass=f;d.extend(e.prototype,f.prototype,{setValue:function(a){a=e.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&
38this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
39dat.color.toString=function(f){return function(a){if(1==a.a||f.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common);
40dat.color.interpret=function(f,a){var d,e,c=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:f},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:f},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
41return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:f},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:f}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!=
42a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&&
43a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){e=!1;
44var b=1<arguments.length?a.toArray(arguments):arguments[0];a.each(c,function(c){if(c.litmus(b))return a.each(c.conversions,function(c,f){d=c.read(b);if(!1===e&&!1!==d)return e=d,d.conversionName=f,d.conversion=c,a.BREAK}),a.BREAK});return e}}(dat.color.toString,dat.utils.common);
45dat.GUI=dat.gui.GUI=function(f,a,d,e,c,b,p,q,l,r,n,u,A,g,k){function v(a,b,h,d){if(void 0===b[h])throw Error("Object "+b+' has no property "'+h+'"');d.color?b=new n(b,h):(b=[b,h].concat(d.factoryArgs),b=e.apply(a,b));d.before instanceof c&&(d.before=d.before.__li);y(a,b);g.addClass(b.domElement,"c");h=document.createElement("span");g.addClass(h,"property-name");h.innerHTML=b.property;var f=document.createElement("div");f.appendChild(h);f.appendChild(b.domElement);d=w(a,f,d.before);g.addClass(d,m.CLASS_CONTROLLER_ROW);
46g.addClass(d,typeof b.getValue());t(a,d,b);a.__controllers.push(b);return b}function w(a,b,h){var c=document.createElement("li");b&&c.appendChild(b);h?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function t(a,c,h){h.__li=c;h.__gui=a;k.extend(h,{options:function(b){if(1<arguments.length)return h.remove(),v(a,h.object,h.property,{before:h.__li.nextElementSibling,factoryArgs:[k.toArray(arguments)]});if(k.isArray(b)||k.isObject(b))return h.remove(),v(a,h.object,h.property,
47{before:h.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){h.__li.firstElementChild.firstElementChild.innerHTML=a;return h},listen:function(){h.__gui.listen(h);return h},remove:function(){h.__gui.remove(h);return h}});if(h instanceof l){var d=new q(h.object,h.property,{min:h.__min,max:h.__max,step:h.__step});k.each(["updateDisplay","onChange","onFinishChange"],function(a){var K=h[a],b=d[a];h[a]=d[a]=function(){var a=Array.prototype.slice.call(arguments);K.apply(h,a);return b.apply(d,a)}});
48g.addClass(c,"has-slider");h.domElement.insertBefore(d.domElement,h.domElement.firstElementChild)}else if(h instanceof q){var e=function(b){return k.isNumber(h.__min)&&k.isNumber(h.__max)?(h.remove(),v(a,h.object,h.property,{before:h.__li.nextElementSibling,factoryArgs:[h.__min,h.__max,h.__step]})):b};h.min=k.compose(e,h.min);h.max=k.compose(e,h.max)}else h instanceof b?(g.bind(c,"click",function(){g.fakeEvent(h.__checkbox,"click")}),g.bind(h.__checkbox,"click",function(a){a.stopPropagation()})):
49h instanceof p?(g.bind(c,"click",function(){g.fakeEvent(h.__button,"click")}),g.bind(c,"mouseover",function(){g.addClass(h.__button,"hover")}),g.bind(c,"mouseout",function(){g.removeClass(h.__button,"hover")})):h instanceof n&&(g.addClass(c,"color"),h.updateDisplay=k.compose(function(a){c.style.borderLeftColor=h.__color.toString();return a},h.updateDisplay),h.updateDisplay());h.setValue=k.compose(function(b){a.getRoot().__preset_select&&h.isModified()&&E(a.getRoot(),!0);return b},h.setValue)}function y(a,
50b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(-1!=d){var e=c.__rememberedObjectIndecesToControllers[d];void 0===e&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c.Default)c=c.Default;else return;c[d]&&void 0!==c[d][b.property]&&(d=c[d][b.property],b.initialValue=d,b.setValue(d))}}}function L(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement,"has-save");
51a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML="&nbsp;";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var r=a.__preset_select=document.createElement("select");
52a.load&&a.load.remembered?k.each(a.load.remembered,function(b,c){F(a,c,c==a.preset)}):F(a,"Default",!1);g.bind(r,"change",function(){for(var b=0;b<a.__preset_select.length;b++)a.__preset_select[b].innerHTML=a.__preset_select[b].value;a.preset=this.value});b.appendChild(r);b.appendChild(c);b.appendChild(d);b.appendChild(e);b.appendChild(f);if(x){var n=function(){u.style.display=a.useLocalStorage?"block":"none"},b=document.getElementById("dg-save-locally"),u=document.getElementById("dg-local-explain");
53b.style.display="block";b=document.getElementById("dg-local-storage");"true"===localStorage.getItem(document.location.href+".isLocal")&&b.setAttribute("checked","checked");n();g.bind(b,"change",function(){a.useLocalStorage=!a.useLocalStorage;n()})}var m=document.getElementById("dg-new-constructor");g.bind(m,"keydown",function(a){!a.metaKey||67!==a.which&&67!=a.keyCode||B.hide()});g.bind(c,"click",function(){m.innerHTML=JSON.stringify(a.getSaveObject(),void 0,2);B.show();m.focus();m.select()});g.bind(d,
54"click",function(){a.save()});g.bind(e,"click",function(){var b=prompt("Enter a new preset name.");b&&a.saveAs(b)});g.bind(f,"click",function(){a.revert()})}function M(a){function b(f){f.preventDefault();e=f.clientX;g.addClass(a.__closeButton,m.CLASS_DRAG);g.bind(window,"mousemove",c);g.bind(window,"mouseup",d);return!1}function c(b){b.preventDefault();a.width+=e-b.clientX;a.onResize();e=b.clientX;return!1}function d(){g.removeClass(a.__closeButton,m.CLASS_DRAG);g.unbind(window,"mousemove",c);g.unbind(window,
55"mouseup",d)}a.__resize_handle=document.createElement("div");k.extend(a.__resize_handle.style,{width:"6px",marginLeft:"-3px",height:"200px",cursor:"ew-resize",position:"absolute"});var e;g.bind(a.__resize_handle,"mousedown",b);g.bind(a.__closeButton,"mousedown",b);a.domElement.insertBefore(a.__resize_handle,a.domElement.firstElementChild)}function G(a,b){a.domElement.style.width=b+"px";a.__save_row&&a.autoPlace&&(a.__save_row.style.width=b+"px");a.__closeButton&&(a.__closeButton.style.width=b+"px")}
56function C(a,b){var c={};k.each(a.__rememberedObjects,function(d,e){var f={};k.each(a.__rememberedObjectIndecesToControllers[e],function(a,c){f[c]=b?a.initialValue:a.getValue()});c[e]=f});return c}function F(a,b,c){var d=document.createElement("option");d.innerHTML=b;d.value=b;a.__preset_select.appendChild(d);c&&(a.__preset_select.selectedIndex=a.__preset_select.length-1)}function E(a,b){var c=a.__preset_select[a.__preset_select.selectedIndex];c.innerHTML=b?c.value+"*":c.value}function H(a){0!=a.length&&
57u(function(){H(a)});k.each(a,function(a){a.updateDisplay()})}f.inject(d);var x;try{x="localStorage"in window&&null!==window.localStorage}catch(N){x=!1}var B,I=!0,z,D=!1,J=[],m=function(a){function b(){var a=c.getRoot();a.width+=1;k.defer(function(){--a.width})}var c=this;this.domElement=document.createElement("div");this.__ul=document.createElement("ul");this.domElement.appendChild(this.__ul);g.addClass(this.domElement,"dg");this.__folders={};this.__controllers=[];this.__rememberedObjects=[];this.__rememberedObjectIndecesToControllers=
58[];this.__listening=[];a=a||{};a=k.defaults(a,{autoPlace:!0,width:m.DEFAULT_WIDTH});a=k.defaults(a,{resizable:a.autoPlace,hideable:a.autoPlace});k.isUndefined(a.load)?a.load={preset:"Default"}:a.preset&&(a.load.preset=a.preset);k.isUndefined(a.parent)&&a.hideable&&J.push(this);a.resizable=k.isUndefined(a.parent)&&a.resizable;a.autoPlace&&k.isUndefined(a.scrollable)&&(a.scrollable=!0);var d=x&&"true"===localStorage.getItem(document.location.href+".isLocal"),e;Object.defineProperties(this,{parent:{get:function(){return a.parent}},
59scrollable:{get:function(){return a.scrollable}},autoPlace:{get:function(){return a.autoPlace}},preset:{get:function(){return c.parent?c.getRoot().preset:a.load.preset},set:function(b){c.parent?c.getRoot().preset=b:a.load.preset=b;for(b=0;b<this.__preset_select.length;b++)this.__preset_select[b].value==this.preset&&(this.__preset_select.selectedIndex=b);c.revert()}},width:{get:function(){return a.width},set:function(b){a.width=b;G(c,b)}},name:{get:function(){return a.name},set:function(b){a.name=
60b;r&&(r.innerHTML=a.name)}},closed:{get:function(){return a.closed},set:function(b){a.closed=b;a.closed?g.addClass(c.__ul,m.CLASS_CLOSED):g.removeClass(c.__ul,m.CLASS_CLOSED);this.onResize();c.__closeButton&&(c.__closeButton.innerHTML=b?m.TEXT_OPEN:m.TEXT_CLOSED)}},load:{get:function(){return a.load}},useLocalStorage:{get:function(){return d},set:function(a){x&&((d=a)?g.bind(window,"unload",e):g.unbind(window,"unload",e),localStorage.setItem(document.location.href+".isLocal",a))}}});if(k.isUndefined(a.parent)){a.closed=
61!1;g.addClass(this.domElement,m.CLASS_MAIN);g.makeSelectable(this.domElement,!1);if(x&&d){c.useLocalStorage=!0;var f=localStorage.getItem(document.location.href+".gui");f&&(a.load=JSON.parse(f))}this.__closeButton=document.createElement("div");this.__closeButton.innerHTML=m.TEXT_CLOSED;g.addClass(this.__closeButton,m.CLASS_CLOSE_BUTTON);this.domElement.appendChild(this.__closeButton);g.bind(this.__closeButton,"click",function(){c.closed=!c.closed})}else{void 0===a.closed&&(a.closed=!0);var r=document.createTextNode(a.name);
62g.addClass(r,"controller-name");f=w(c,r);g.addClass(this.__ul,m.CLASS_CLOSED);g.addClass(f,"title");g.bind(f,"click",function(a){a.preventDefault();c.closed=!c.closed;return!1});a.closed||(this.closed=!1)}a.autoPlace&&(k.isUndefined(a.parent)&&(I&&(z=document.createElement("div"),g.addClass(z,"dg"),g.addClass(z,m.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(z),I=!1),z.appendChild(this.domElement),g.addClass(this.domElement,m.CLASS_AUTO_PLACE)),this.parent||G(c,a.width));g.bind(window,"resize",
63function(){c.onResize()});g.bind(this.__ul,"webkitTransitionEnd",function(){c.onResize()});g.bind(this.__ul,"transitionend",function(){c.onResize()});g.bind(this.__ul,"oTransitionEnd",function(){c.onResize()});this.onResize();a.resizable&&M(this);this.saveToLocalStorageIfPossible=e=function(){x&&"true"===localStorage.getItem(document.location.href+".isLocal")&&localStorage.setItem(document.location.href+".gui",JSON.stringify(c.getSaveObject()))};c.getRoot();a.parent||b()};m.toggleHide=function(){D=
64!D;k.each(J,function(a){a.domElement.style.zIndex=D?-999:999;a.domElement.style.opacity=D?0:1})};m.CLASS_AUTO_PLACE="a";m.CLASS_AUTO_PLACE_CONTAINER="ac";m.CLASS_MAIN="main";m.CLASS_CONTROLLER_ROW="cr";m.CLASS_TOO_TALL="taller-than-window";m.CLASS_CLOSED="closed";m.CLASS_CLOSE_BUTTON="close-button";m.CLASS_DRAG="drag";m.DEFAULT_WIDTH=245;m.TEXT_CLOSED="Close Controls";m.TEXT_OPEN="Open Controls";g.bind(window,"keydown",function(a){"text"===document.activeElement.type||72!==a.which&&72!=a.keyCode||
65m.toggleHide()},!1);k.extend(m.prototype,{add:function(a,b){return v(this,a,b,{factoryArgs:Array.prototype.slice.call(arguments,2)})},addColor:function(a,b){return v(this,a,b,{color:!0})},remove:function(a){this.__ul.removeChild(a.__li);this.__controllers.splice(this.__controllers.indexOf(a),1);var b=this;k.defer(function(){b.onResize()})},destroy:function(){this.autoPlace&&z.removeChild(this.domElement)},addFolder:function(a){if(void 0!==this.__folders[a])throw Error('You already have a folder in this GUI by the name "'+
66a+'"');var b={name:a,parent:this};b.autoPlace=this.autoPlace;this.load&&this.load.folders&&this.load.folders[a]&&(b.closed=this.load.folders[a].closed,b.load=this.load.folders[a]);b=new m(b);this.__folders[a]=b;a=w(this,b.domElement);g.addClass(a,"folder");return b},open:function(){this.closed=!1},close:function(){this.closed=!0},onResize:function(){var a=this.getRoot();if(a.scrollable){var b=g.getOffset(a.__ul).top,c=0;k.each(a.__ul.childNodes,function(b){a.autoPlace&&b===a.__save_row||(c+=g.getHeight(b))});
67window.innerHeight-b-20<c?(g.addClass(a.domElement,m.CLASS_TOO_TALL),a.__ul.style.height=window.innerHeight-b-20+"px"):(g.removeClass(a.domElement,m.CLASS_TOO_TALL),a.__ul.style.height="auto")}a.__resize_handle&&k.defer(function(){a.__resize_handle.style.height=a.__ul.offsetHeight+"px"});a.__closeButton&&(a.__closeButton.style.width=a.width+"px")},remember:function(){k.isUndefined(B)&&(B=new A,B.domElement.innerHTML=a);if(this.parent)throw Error("You can only call remember on a top level GUI.");var b=
68this;k.each(Array.prototype.slice.call(arguments),function(a){0==b.__rememberedObjects.length&&L(b);-1==b.__rememberedObjects.indexOf(a)&&b.__rememberedObjects.push(a)});this.autoPlace&&G(this,this.width)},getRoot:function(){for(var a=this;a.parent;)a=a.parent;return a},getSaveObject:function(){var a=this.load;a.closed=this.closed;0<this.__rememberedObjects.length&&(a.preset=this.preset,a.remembered||(a.remembered={}),a.remembered[this.preset]=C(this));a.folders={};k.each(this.__folders,function(b,
69c){a.folders[c]=b.getSaveObject()});return a},save:function(){this.load.remembered||(this.load.remembered={});this.load.remembered[this.preset]=C(this);E(this,!1);this.saveToLocalStorageIfPossible()},saveAs:function(a){this.load.remembered||(this.load.remembered={},this.load.remembered.Default=C(this,!0));this.load.remembered[a]=C(this);this.preset=a;F(this,a,!0);this.saveToLocalStorageIfPossible()},revert:function(a){k.each(this.__controllers,function(b){this.getRoot().load.remembered?y(a||this.getRoot(),
70b):b.setValue(b.initialValue)},this);k.each(this.__folders,function(a){a.revert(a)});a||E(this.getRoot(),!1)},listen:function(a){var b=0==this.__listening.length;this.__listening.push(a);b&&H(this.__listening)}});return m}(dat.utils.css,'<div id="dg-save" class="dg dialogue">\n\n Here\'s the new load parameter for your <code>GUI</code>\'s constructor:\n\n <textarea id="dg-new-constructor"></textarea>\n\n <div id="dg-save-locally">\n\n <input id="dg-local-storage" type="checkbox"/> Automatically save\n values to <code>localStorage</code> on exit.\n\n <div id="dg-local-explain">The values saved to <code>localStorage</code> will\n override those passed to <code>dat.GUI</code>\'s constructor. This makes it\n easier to work incrementally, but <code>localStorage</code> is fragile,\n and your friends may not see the same values you do.\n \n </div>\n \n </div>\n\n</div>',
71".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row, <li> */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n",
72dat.controllers.factory=function(f,a,d,e,c,b,p){return function(q,l,r,n){var u=q[l];if(p.isArray(r)||p.isObject(r))return new f(q,l,r);if(p.isNumber(u))return p.isNumber(r)&&p.isNumber(n)?new d(q,l,r,n):new a(q,l,{min:r,max:n});if(p.isString(u))return new e(q,l);if(p.isFunction(u))return new c(q,l,"");if(p.isBoolean(u))return new b(q,l)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(f,a,d){var e=
73function(c,b){function d(){f.setValue(f.__input.value)}e.superclass.call(this,c,b);var f=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",d);a.bind(this.__input,"change",d);a.bind(this.__input,"blur",function(){f.__onFinishChange&&f.__onFinishChange.call(f,f.getValue())});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,
74f.prototype,{updateDisplay:function(){a.isActive(this.__input)||(this.__input.value=this.getValue());return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController,
75dat.controllers.ColorController=function(f,a,d,e,c){function b(a,b,d,e){a.style.background="";c.each(l,function(c){a.style.cssText+="background: "+c+"linear-gradient("+b+", "+d+" 0%, "+e+" 100%); "})}function p(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";
76a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var q=function(f,n){function u(b){v(b);a.bind(window,"mousemove",v);a.bind(window,
77"mouseup",l)}function l(){a.unbind(window,"mousemove",v);a.unbind(window,"mouseup",l)}function g(){var a=e(this.value);!1!==a?(t.__color.__state=a,t.setValue(t.__color.toOriginal())):this.value=t.__color.toString()}function k(){a.unbind(window,"mousemove",w);a.unbind(window,"mouseup",k)}function v(b){b.preventDefault();var c=a.getWidth(t.__saturation_field),d=a.getOffset(t.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c;b=1-(b.clientY-d.top+document.body.scrollTop)/c;1<b?b=1:0>
78b&&(b=0);1<e?e=1:0>e&&(e=0);t.__color.v=b;t.__color.s=e;t.setValue(t.__color.toOriginal());return!1}function w(b){b.preventDefault();var c=a.getHeight(t.__hue_field),d=a.getOffset(t.__hue_field);b=1-(b.clientY-d.top+document.body.scrollTop)/c;1<b?b=1:0>b&&(b=0);t.__color.h=360*b;t.setValue(t.__color.toOriginal());return!1}q.superclass.call(this,f,n);this.__color=new d(this.getValue());this.__temp=new d(0);var t=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,!1);
79this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=document.createElement("input");
80this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){13===a.keyCode&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(b){a.addClass(this,"drag").bind(window,"mouseup",function(b){a.removeClass(t.__selector,"drag")})});var y=document.createElement("div");c.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});c.extend(this.__field_knob.style,
81{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(.5>this.__color.v?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});c.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});c.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});c.extend(y.style,{width:"100%",height:"100%",
82background:"none"});b(y,"top","rgba(0,0,0,0)","#000");c.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});p(this.__hue_field);c.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",u);a.bind(this.__field_knob,"mousedown",u);a.bind(this.__hue_field,"mousedown",function(b){w(b);a.bind(window,
83"mousemove",w);a.bind(window,"mouseup",k)});this.__saturation_field.appendChild(y);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};q.superclass=f;c.extend(q.prototype,f.prototype,{updateDisplay:function(){var a=e(this.getValue());if(!1!==a){var f=!1;
84c.each(d.COMPONENTS,function(b){if(!c.isUndefined(a[b])&&!c.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return f=!0,{}},this);f&&c.extend(this.__color.__state,a)}c.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var l=.5>this.__color.v||.5<this.__color.s?255:0,p=255-l;c.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+"rgb("+l+
85","+l+","+l+")"});this.__hue_knob.style.marginTop=100*(1-this.__color.h/360)+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());c.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+l+","+l+","+l+")",textShadow:this.__input_textShadow+"rgba("+p+","+p+","+p+",.7)"})}});var l=["-moz-","-o-","-webkit-","-ms-",""];return q}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(f,a,d,e){function c(a,
86b,c){Object.defineProperty(a,b,{get:function(){if("RGB"===this.__state.space)return this.__state[b];p(this,b,c);return this.__state[b]},set:function(a){"RGB"!==this.__state.space&&(p(this,b,c),this.__state.space="RGB");this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if("HSV"===this.__state.space)return this.__state[b];q(this);return this.__state[b]},set:function(a){"HSV"!==this.__state.space&&(q(this),this.__state.space="HSV");this.__state[b]=a}})}function p(b,c,d){if("HEX"===
87b.__state.space)b.__state[c]=a.component_from_hex(b.__state.hex,d);else if("HSV"===b.__state.space)e.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function q(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);e.extend(b.__state,{s:c.s,v:c.v});e.isNaN(c.h)?e.isUndefined(b.__state.h)&&(b.__state.h=0):b.__state.h=c.h}var l=function(){this.__state=f.apply(this,arguments);if(!1===this.__state)throw"Failed to interpret color arguments";this.__state.a=this.__state.a||
881};l.COMPONENTS="r g b h s v hex a".split(" ");e.extend(l.prototype,{toString:function(){return d(this)},toOriginal:function(){return this.__state.conversion.write(this)}});c(l.prototype,"r",2);c(l.prototype,"g",1);c(l.prototype,"b",0);b(l.prototype,"h");b(l.prototype,"s");b(l.prototype,"v");Object.defineProperty(l.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(l.prototype,"hex",{get:function(){"HEX"!==!this.__state.space&&(this.__state.hex=
89a.rgb_to_hex(this.r,this.g,this.b));return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return l}(dat.color.interpret,dat.color.math=function(){var f;return{hsv_to_rgb:function(a,d,e){var c=a/60-Math.floor(a/60),b=e*(1-d),f=e*(1-c*d);d=e*(1-(1-c)*d);a=[[e,d,b],[f,e,b],[b,e,d],[b,f,e],[d,b,e],[e,b,f]][Math.floor(a/60)%6];return{r:255*a[0],g:255*a[1],b:255*a[2]}},rgb_to_hsv:function(a,d,e){var c=Math.min(a,d,e),b=Math.max(a,d,e),c=b-c;if(0==b)return{h:NaN,s:0,v:0};
90a=(a==b?(d-e)/c:d==b?2+(e-a)/c:4+(a-d)/c)/6;0>a&&(a+=1);return{h:360*a,s:c/b,v:b/255}},rgb_to_hex:function(a,d,e){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,d);return a=this.hex_with_component(a,0,e)},component_from_hex:function(a,d){return a>>8*d&255},hex_with_component:function(a,d,e){return e<<(f=8*d)|a&~(255<<f)}}}(),dat.color.toString,dat.utils.common),dat.color.interpret,dat.utils.common),dat.utils.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||
91window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(f,a){window.setTimeout(f,1E3/60)}}(),dat.dom.CenteredDiv=function(f,a){var d=function(){this.backgroundElement=document.createElement("div");a.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear",transition:"opacity 0.2s linear"});f.makeFullscreen(this.backgroundElement);this.backgroundElement.style.position=
92"fixed";this.domElement=document.createElement("div");a.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear",transition:"transform 0.2s ease-out, opacity 0.2s linear"});document.body.appendChild(this.backgroundElement);document.body.appendChild(this.domElement);var d=this;f.bind(this.backgroundElement,"click",function(){d.hide()})};d.prototype.show=function(){var d=this;this.backgroundElement.style.display=
93"block";this.domElement.style.display="block";this.domElement.style.opacity=0;this.domElement.style.webkitTransform="scale(1.1)";this.layout();a.defer(function(){d.backgroundElement.style.opacity=1;d.domElement.style.opacity=1;d.domElement.style.webkitTransform="scale(1)"})};d.prototype.hide=function(){var a=this,c=function(){a.domElement.style.display="none";a.backgroundElement.style.display="none";f.unbind(a.domElement,"webkitTransitionEnd",c);f.unbind(a.domElement,"transitionend",c);f.unbind(a.domElement,
94"oTransitionEnd",c)};f.bind(this.domElement,"webkitTransitionEnd",c);f.bind(this.domElement,"transitionend",c);f.bind(this.domElement,"oTransitionEnd",c);this.backgroundElement.style.opacity=0;this.domElement.style.opacity=0;this.domElement.style.webkitTransform="scale(1.1)"};d.prototype.layout=function(){this.domElement.style.left=window.innerWidth/2-f.getWidth(this.domElement)/2+"px";this.domElement.style.top=window.innerHeight/2-f.getHeight(this.domElement)/2+"px"};return d}(dat.dom.dom,dat.utils.common),
95dat.dom.dom,dat.utils.common);
scripts/system/particle_explorer/hifi-entity-ui.js+680−0
@@ -0,0 +1,680 @@
1/* global window, document, print, alert, console,setTimeout, clearTimeout, _ $ */
2/* eslint no-console: 0 */
3
4/**
5UI Builder V1.0
6
7Created by Matti 'Menithal' Lahtinen
824/5/2017
9Copyright 2017 High Fidelity, Inc.
10
11This can eventually be expanded to all of Edit, for now, starting
12with Particles Only.
13
14This is created for the sole purpose of streamliming the bridge, and to simplify
15the logic between an inputfield in WebView and Entities in High Fidelity.
16
17We also do not need anything as heavy as jquery or any other platform,
18as we are mostly only building for QT (while, all the other JS frameworks usually do alot of polyfilling)
19
20Available Types:
21
22 JSONInputField - Accepts JSON input, once one presses Save, it will be propegated.
23 Button- A Button that listens for a custom event as defined by callback
24 Boolean - Creates a checkbox that the user can either check or uncheck
25 SliderFloat - Creates a slider (with input) that has Float values from min to max.
26 Default is min 0, max 1
27 SliderInteger - Creates a slider (with input) that has a Integer value from min to max.
28 Default is min 1, max 10000
29 SliderRadian - Creates a slider (with input) that has Float values in degrees,
30 that are converted to radians. default is min 0, max Math.PI.
31 Texture - Creates a Image with an url input field that points to texture.
32 If image cannot form, show "cannot find image"
33 VecQuaternion - Creates a 3D Vector field that converts to quaternions.
34 Checkbox exists to show quaternions instead.
35 Color - Create field color button, that when pressed, opens the color picker.
36 Vector - Create a 3D Vector field that has one to one correspondence.
37
38The script will use this structure to build a UI that is connected The
39id fields within High Fidelity
40
41This should make editing, and everything related much more simpler to maintain,
42and If there is any changes to either the Entities or properties of
43
44**/
45
46var RADIANS_PER_DEGREE = Math.PI / 180;
47
48var roundFloat = function (input, round) {
49 round = round ? round : 1000;
50 var sanitizedInput;
51 if (typeof input === "string") {
52 sanitizedInput = parseFloat(input);
53 } else {
54 sanitizedInput = input;
55 }
56 return Math.round(sanitizedInput * round) / round;
57};
58
59function HifiEntityUI(parent) {
60 this.parent = parent;
61
62 var self = this;
63 this.webBridgeSync = _.debounce(function (id, val) {
64 if (self.EventBridge) {
65 var sendPackage = {};
66 sendPackage[id] = val;
67 self.submitChanges(sendPackage);
68 }
69 }, 125);
70}
71
72HifiEntityUI.prototype = {
73 setOnSelect: function (callback) {
74 this.onSelect = callback;
75 },
76 submitChanges: function (structure) {
77 var message = {
78 messageType: "settings_update",
79 updatedSettings: structure
80 };
81 this.EventBridge.emitWebEvent(JSON.stringify(message));
82 },
83 setUI: function (structure) {
84 this.structure = structure;
85 },
86 disableFields: function () {
87 var fields = document.getElementsByTagName("input");
88 for (var i = 0; i < fields.length; i++) {
89 if (fields[i].getAttribute("type") !== "button") {
90 fields[i].value = "";
91 }
92
93 fields[i].setAttribute("disabled", true);
94 }
95 var textures = document.getElementsByTagName("img");
96 for (i = 0; i < textures.length; i++) {
97 textures[i].src = "";
98 }
99
100 textures = document.getElementsByClassName("with-texture");
101 for (i = 0; i < textures.length; i++) {
102 textures[i].classList.remove("with-textures");
103 textures[i].classList.add("no-texture");
104 }
105
106 var textareas = document.getElementsByTagName("textarea");
107 for (var x = 0; x < textareas.length; x++) {
108 textareas[x].remove();
109 }
110 },
111 getSettings: function () {
112 var self = this;
113 var json = {};
114 var keys = Object.keys(self.builtRows);
115
116 for (var i = 0; i < keys.length; i++) {
117 var key = keys[i];
118 var el = self.builtRows[key];
119 if (el.className.indexOf("checkbox") !== -1) {
120 json[key] = document.getElementById(key)
121 .checked ? true : false;
122 } else if (el.className.indexOf("vector-section") !== -1) {
123 var vector = {};
124 if (el.className.indexOf("rgb") !== -1) {
125 var red = document.getElementById(key + "-red");
126 var blue = document.getElementById(key + "-blue");
127 var green = document.getElementById(key + "-green");
128 vector.red = red.value;
129 vector.blue = blue.value;
130 vector.green = green.value;
131 } else if (el.className.indexOf("pyr") !== -1) {
132 var p = document.getElementById(key + "-Pitch");
133 var y = document.getElementById(key + "-Yaw");
134 var r = document.getElementById(key + "-Roll");
135 vector.x = p.value;
136 vector.y = y.value;
137 vector.z = r.value;
138 } else {
139 var x = document.getElementById(key + "-x");
140 var ey = document.getElementById(key + "-y");
141 var z = document.getElementById(key + "-z");
142 vector.x = x.value;
143 vector.y = ey.value;
144 vector.z = z.value;
145 }
146 json[key] = vector;
147 } else if (el.className.length > 0) {
148 json[key] = document.getElementById(key)
149 .value;
150 }
151 }
152
153 return json;
154 },
155 fillFields: function (currentProperties) {
156 var self = this;
157 var fields = document.getElementsByTagName("input");
158
159 if (!currentProperties.locked) {
160 for (var i = 0; i < fields.length; i++) {
161 fields[i].removeAttribute("disabled");
162 }
163 }
164 if (self.onSelect) {
165 self.onSelect();
166 }
167 var keys = Object.keys(currentProperties);
168
169
170 for (var e in keys) {
171 if (keys.hasOwnProperty(e)) {
172 var value = keys[e];
173
174 var property = currentProperties[value];
175 var field = self.builtRows[value];
176 if (field) {
177 var el = document.getElementById(value);
178
179 if (field.className.indexOf("radian") !== -1) {
180 el.value = property / RADIANS_PER_DEGREE;
181 el.onchange({
182 target: el
183 });
184 } else if (field.className.indexOf("range") !== -1 || field.className.indexOf("texture") !== -1) {
185 el.value = property;
186 el.onchange({
187 target: el
188 });
189 } else if (field.className.indexOf("checkbox") !== -1) {
190 if (property) {
191 el.setAttribute("checked", property);
192 } else {
193 el.removeAttribute("checked");
194 }
195 } else if (field.className.indexOf("vector-section") !== -1) {
196 if (field.className.indexOf("rgb") !== -1) {
197 var red = document.getElementById(value + "-red");
198 var blue = document.getElementById(value + "-blue");
199 var green = document.getElementById(value + "-green");
200 red.value = parseInt(property.red);
201 blue.value = parseInt(property.blue);
202 green.value = parseInt(property.green);
203
204 red.oninput({
205 target: red
206 });
207 } else if (field.className.indexOf("xyz") !== -1) {
208 var x = document.getElementById(value + "-x");
209 var y = document.getElementById(value + "-y");
210 var z = document.getElementById(value + "-z");
211
212 x.value = roundFloat(property.x, 100);
213 y.value = roundFloat(property.y, 100);
214 z.value = roundFloat(property.z, 100);
215 } else if (field.className.indexOf("pyr") !== -1) {
216 var pitch = document.getElementById(value + "-Pitch");
217 var yaw = document.getElementById(value + "-Yaw");
218 var roll = document.getElementById(value + "-Roll");
219
220 pitch.value = roundFloat(property.x, 100);
221 yaw.value = roundFloat(property.y, 100);
222 roll.value = roundFloat(property.z, 100);
223
224 }
225 }
226 }
227 }
228 }
229 },
230 connect: function (EventBridge) {
231 this.EventBridge = EventBridge;
232
233 var self = this;
234
235 EventBridge.emitWebEvent(JSON.stringify({
236 messageType: 'page_loaded'
237 }));
238
239 EventBridge.scriptEventReceived.connect(function (data) {
240 data = JSON.parse(data);
241
242 if (data.messageType === 'particle_settings') {
243 // Update settings
244 var currentProperties = data.currentProperties;
245 self.fillFields(currentProperties);
246 // Do expected property match with structure;
247 } else if (data.messageType === 'particle_close') {
248 self.disableFields();
249 }
250 });
251 },
252 build: function () {
253 var self = this;
254 var sections = Object.keys(this.structure);
255 this.builtRows = {};
256 sections.forEach(function (section, index) {
257 var properties = self.structure[section];
258 self.addSection(self.parent, section, properties, index);
259 });
260 },
261 addSection: function (parent, section, properties, index) {
262 var self = this;
263
264 var sectionDivHeader = document.createElement("div");
265 var title = document.createElement("label");
266 var dropDown = document.createElement("span");
267
268 dropDown.className = "arrow";
269 sectionDivHeader.className = "section-header";
270 title.innerHTML = section;
271 sectionDivHeader.appendChild(title);
272 sectionDivHeader.appendChild(dropDown);
273 var collapsed = index !== 0;
274
275 dropDown.innerHTML = collapsed ? "L" : "M";
276 sectionDivHeader.setAttribute("collapsed", collapsed);
277 parent.appendChild(sectionDivHeader);
278
279 var sectionDivBody = document.createElement("div");
280 sectionDivBody.className = "property-group";
281
282 var animationWrapper = document.createElement("div");
283 animationWrapper.className = "section-wrap";
284
285 for (var property in properties) {
286 if (properties.hasOwnProperty(property)) {
287 var builtRow = self.addElement(animationWrapper, properties[property]);
288 var id = properties[property].id;
289 if (id) {
290 self.builtRows[id] = builtRow;
291 }
292 }
293 }
294 sectionDivBody.appendChild(animationWrapper);
295 parent.appendChild(sectionDivBody);
296 _.defer(function () {
297 var height = (animationWrapper.clientHeight) + "px";
298 if (collapsed) {
299 sectionDivBody.classList.remove("visible");
300 sectionDivBody.style.maxHeight = "0px";
301 } else {
302 sectionDivBody.classList.add("visible");
303 sectionDivBody.style.maxHeight = height;
304 }
305
306 sectionDivHeader.onclick = function () {
307 collapsed = !collapsed;
308 if (collapsed) {
309 sectionDivBody.classList.remove("visible");
310 sectionDivBody.style.maxHeight = "0px";
311 } else {
312 sectionDivBody.classList.add("visible");
313 sectionDivBody.style.maxHeight = (animationWrapper.clientHeight) + "px";
314 }
315 // sectionDivBody.style.display = collapsed ? "none": "block";
316 dropDown.innerHTML = collapsed ? "L" : "M";
317 sectionDivHeader.setAttribute("collapsed", collapsed);
318 };
319 });
320 },
321 addLabel: function (parent, group) {
322 var label = document.createElement("label");
323 label.innerHTML = group.name;
324 parent.appendChild(label);
325 if (group.unit) {
326 var span = document.createElement("span");
327 span.innerHTML = group.unit;
328 span.className = "unit";
329 label.appendChild(span);
330 }
331 return label;
332 },
333 addVector: function (parent, group, labels, domArray) {
334 var self = this;
335 var inputs = labels ? labels : ["x", "y", "z"];
336 domArray = domArray ? domArray : [];
337 parent.id = group.id;
338 for (var index in inputs) {
339 var element = document.createElement("input");
340
341 element.setAttribute("type", "number");
342 element.className = inputs[index];
343 element.id = group.id + "-" + inputs[index];
344
345 if (group.defaultRange) {
346 if (group.defaultRange.min) {
347 element.setAttribute("min", group.defaultRange.min);
348 }
349 if (group.defaultRange.max) {
350 element.setAttribute("max", group.defaultRange.max);
351 }
352 if (group.defaultRange.step) {
353 element.setAttribute("step", group.defaultRange.step);
354 }
355 }
356 if (group.oninput) {
357 element.oninput = group.oninput;
358 } else {
359 element.oninput = function (event) {
360 self.webBridgeSync(group.id, {
361 x: domArray[0].value,
362 y: domArray[1].value,
363 z: domArray[2].value
364 });
365 };
366 }
367 element.onchange = element.oninput;
368 domArray.push(element);
369 }
370
371 this.addLabel(parent, group);
372 var className = "";
373 for (var i = 0; i < inputs.length; i++) {
374 className += inputs[i].charAt(0)
375 .toLowerCase();
376 }
377 parent.className += " property vector-section " + className;
378
379 // Add Tuple and the rest
380 var tupleContainer = document.createElement("div");
381 tupleContainer.className = "tuple";
382 for (var domIndex in domArray) {
383 var container = domArray[domIndex];
384 var div = document.createElement("div");
385 var label = document.createElement("label");
386 label.innerHTML = inputs[domIndex] + ":";
387 label.setAttribute("for", container.id);
388 div.appendChild(container);
389 div.appendChild(label);
390 tupleContainer.appendChild(div);
391 }
392 parent.appendChild(tupleContainer);
393 },
394 addVectorQuaternion: function (parent, group) {
395 this.addVector(parent, group, ["Pitch", "Yaw", "Roll"]);
396 },
397 addColorPicker: function (parent, group) {
398 var self = this;
399 var $colPickContainer = $('<div>', {
400 id: group.id,
401 class: "color-picker"
402 });
403 var updateColors = function (red, green, blue) {
404 $colPickContainer.css('background-color', "rgb(" +
405 red + "," +
406 green + "," +
407 blue + ")");
408 };
409
410 var inputs = ["red", "green", "blue"];
411 var domArray = [];
412 group.oninput = function (event) {
413 $colPickContainer.colpickSetColor(
414 {
415 r: domArray[0].value,
416 g: domArray[1].value,
417 b: domArray[2].value
418 },
419 true);
420 };
421 group.defaultRange = {
422 min: 0,
423 max: 255,
424 step: 1
425 };
426
427 parent.appendChild($colPickContainer[0]);
428 self.addVector(parent, group, inputs, domArray);
429
430 updateColors(domArray[0].value, domArray[1].value, domArray[2].value);
431
432 // Could probably write a custom one for this to completely write out jquery,
433 // but for now, using the same as earlier.
434
435 /* Color Picker Logic Here */
436
437
438 $colPickContainer.colpick({
439 colorScheme: 'dark',
440 layout: 'hex',
441 color: {
442 r: domArray[0].value,
443 g: domArray[1].value,
444 b: domArray[2].value
445 },
446 onChange: function (hsb, hex, rgb, el) {
447 updateColors(rgb.r, rgb.g, rgb.b);
448
449 domArray[0].value = rgb.r;
450 domArray[1].value = rgb.g;
451 domArray[2].value = rgb.b;
452 self.webBridgeSync(group.id, {
453 red: rgb.r,
454 green: rgb.g,
455 blue: rgb.b
456 });
457 },
458 onSubmit: function (hsb, hex, rgb, el) {
459 $(el)
460 .css('background-color', '#' + hex);
461 $(el)
462 .colpickHide();
463 domArray[0].value = rgb.r;
464 domArray[1].value = rgb.g;
465 domArray[2].value = rgb.b;
466 self.webBridgeSync(group.id, {
467 red: rgb.r,
468 green: rgb.g,
469 blue: rgb.b
470 });
471 }
472 });
473 },
474 addTextureField: function (parent, group) {
475 var self = this;
476 this.addLabel(parent, group);
477 parent.className += " property texture";
478 var textureImage = document.createElement("div");
479 var textureUrl = document.createElement("input");
480 textureUrl.setAttribute("type", "text");
481 textureUrl.id = group.id;
482 textureImage.className = "texture-image no-texture";
483 var image = document.createElement("img");
484 var imageLoad = _.debounce(function (url) {
485 if (url.length > 0) {
486 textureImage.classList.remove("no-texture");
487 textureImage.classList.add("with-texture");
488 image.src = url;
489 image.style.display = "block";
490 } else {
491 image.src = "";
492 image.style.display = "none";
493 textureImage.classList.add("no-texture");
494 }
495 self.webBridgeSync(group.id, url);
496 }, 250);
497
498 textureUrl.oninput = function (event) {
499 // Add throttle
500 var url = event.target.value;
501 imageLoad(url);
502 };
503 textureUrl.onchange = textureUrl.oninput;
504 textureImage.appendChild(image);
505 parent.appendChild(textureImage);
506 parent.appendChild(textureUrl);
507 },
508 addSlider: function (parent, group) {
509 var self = this;
510 this.addLabel(parent, group);
511 parent.className += " property range";
512 var container = document.createElement("div");
513 container.className = "slider-wrapper";
514 var slider = document.createElement("input");
515 slider.setAttribute("type", "range");
516
517 var inputField = document.createElement("input");
518 inputField.setAttribute("type", "number");
519
520 container.appendChild(slider);
521 container.appendChild(inputField);
522 parent.appendChild(container);
523
524 if (group.type === "SliderInteger") {
525 inputField.setAttribute("min", group.min !== undefined ? group.min : 0);
526 inputField.setAttribute("step", 1);
527
528 slider.setAttribute("min", group.min !== undefined ? group.min : 0);
529 slider.setAttribute("max", group.max !== undefined ? group.max : 10000);
530 slider.setAttribute("step", 1);
531
532 inputField.oninput = function (event) {
533
534 if (parseInt(event.target.value) > parseInt(slider.getAttribute("max")) && group.max !== 1) {
535 slider.setAttribute("max", event.target.value);
536 }
537 slider.value = event.target.value;
538
539 self.webBridgeSync(group.id, slider.value);
540 };
541 inputField.onchange = inputField.oninput;
542 slider.oninput = function (event) {
543 inputField.value = event.target.value;
544 self.webBridgeSync(group.id, slider.value);
545 };
546
547 inputField.id = group.id;
548 } else if (group.type === "SliderRadian") {
549 slider.setAttribute("min", group.min !== undefined ? group.min : 0);
550 slider.setAttribute("max", group.max !== undefined ? group.max : 180);
551 slider.setAttribute("step", 1);
552 parent.className += " radian";
553 inputField.setAttribute("min", (group.min !== undefined ? group.min : 0));
554 inputField.setAttribute("max", (group.max !== undefined ? group.max : 180));
555
556 inputField.oninput = function (event) {
557 slider.value = event.target.value;
558 self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE);
559 };
560 inputField.onchange = inputField.oninput;
561
562 inputField.id = group.id;
563 slider.oninput = function (event) {
564 if (event.target.value > 0) {
565 inputField.value = Math.floor(event.target.value);
566 } else {
567 inputField.value = Math.ceil(event.target.value);
568 }
569 self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE);
570 };
571 var degrees = document.createElement("label");
572 degrees.innerHTML = "&#176;";
573 degrees.style.fontSize = "1.4rem";
574 degrees.style.display = "inline";
575 degrees.style.verticalAlign = "top";
576 degrees.style.paddingLeft = "0.4rem";
577 container.appendChild(degrees);
578
579 } else {
580 // Must then be Float
581 inputField.setAttribute("min", group.min !== undefined ? group.min : 0);
582 slider.setAttribute("step", 0.01);
583
584 slider.setAttribute("min", group.min !== undefined ? group.min : 0);
585 slider.setAttribute("max", group.max !== undefined ? group.max : 1);
586 slider.setAttribute("step", 0.01);
587
588 inputField.oninput = function (event) {
589 if (parseFloat(event.target.value) > parseFloat(slider.getAttribute("max")) && group.max !== 1) {
590 slider.setAttribute("max", event.target.value);
591 }
592
593 slider.value = event.target.value;
594 self.webBridgeSync(group.id, slider.value);
595 // bind web sock update here.
596 };
597 inputField.onchange = inputField.oninput;
598 slider.oninput = function (event) {
599 inputField.value = event.target.value;
600 self.webBridgeSync(group.id, inputField.value);
601 };
602
603 inputField.id = group.id;
604 }
605
606 // UpdateBinding
607 },
608 addCheckBox: function (parent, group) {
609 var checkBox = document.createElement("input");
610 checkBox.setAttribute("type", "checkbox");
611 var self = this;
612 checkBox.onchange = function (event) {
613 self.webBridgeSync(group.id, event.target.checked);
614 };
615 checkBox.id = group.id;
616 parent.appendChild(checkBox);
617 var label = this.addLabel(parent, group);
618 label.setAttribute("for", checkBox.id);
619 parent.className += " property checkbox";
620 },
621 addElement: function (parent, group) {
622 var self = this;
623 var property = document.createElement("div");
624 property.id = group.id;
625
626 var row = document.createElement("div");
627 switch (group.type) {
628 case "Button":
629 var button = document.createElement("input");
630 button.setAttribute("type", "button");
631 button.id = group.id;
632 if (group.disabled) {
633 button.disabled = group.disabled;
634 }
635 button.className = group.class;
636 button.value = group.name;
637
638 button.onclick = group.callback;
639 parent.appendChild(button);
640 break;
641 case "Row":
642 var hr = document.createElement("hr");
643 hr.className = "splitter";
644 if (group.id) {
645 hr.id = group.id;
646 }
647 parent.appendChild(hr);
648 break;
649 case "Boolean":
650 self.addCheckBox(row, group);
651 parent.appendChild(row);
652 break;
653 case "SliderFloat":
654 case "SliderInteger":
655 case "SliderRadian":
656 self.addSlider(row, group);
657 parent.appendChild(row);
658 break;
659 case "Texture":
660 self.addTextureField(row, group);
661 parent.appendChild(row);
662 break;
663 case "Color":
664 self.addColorPicker(row, group);
665 parent.appendChild(row);
666 break;
667 case "Vector":
668 self.addVector(row, group);
669 parent.appendChild(row);
670 break;
671 case "VectorQuaternion":
672 self.addVectorQuaternion(row, group);
673 parent.appendChild(row);
674 break;
675 default:
676 console.log("not defined");
677 }
678 return row;
679 }
680};
scripts/system/particle_explorer/particle-style.css+124−0
@@ -0,0 +1,124 @@
1/*
2// particle-style.css
3//
4// Created by Matti 'Menithal' Lahtinen on 21 May 2017
5// Copyright 2017 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9*/
10
11
12.property-group {
13 max-height: 0;
14 -webkit-transition: max-height 0.15s ease-out;
15 transition: max-height 0.15s ease-out;
16 overflow: hidden;
17}
18.property-group.visible {
19 transition: max-height 0.25s ease-in;
20}
21.section-wrap {
22 width: 100%;
23}
24.property {
25 padding: 0.4rem 0;
26 margin: 0;
27}
28.property.checkbox {
29 margin: 0;
30}
31.property.range label{
32 display: block;
33}
34
35input[type="button"] {
36 margin: 0.4rem;
37 min-width: 6rem;
38}
39input[type="text"] {
40 margin: 0;
41}
42.property.range input[type=number]{
43 margin-left: 0.8rem;
44 width: 5.4rem;
45 height: 1.8rem;
46}
47input[type=range] {
48 -webkit-appearance: none;
49 background: #2e2e2e;
50 height: 1.8rem;
51 border-radius: 1rem;
52}
53input[type=range]::-webkit-slider-thumb {
54 -webkit-appearance:none;
55 width: 0.6rem;
56 height: 1.8rem;
57 padding:0;
58 margin: 0;
59 background-color: #696969;
60 border-radius: 1rem;
61}
62input[type=range]::-webkit-slider-thumb:hover {
63 background-color: white;
64}
65input[type=range]:focus { /*#252525*/
66 outline: none;
67}
68.tuple label {
69 text-transform: capitalize;
70}
71.slider-wrapper {
72 display: table;
73 padding: 0.4rem 0;
74}
75hr.splitter{
76 width: 100%;
77 padding: 0.2rem 0 0 0;
78 margin: 0;
79 position: relative;
80 clear: both;
81}
82hr.splitter:last-of-type{
83 padding:0;
84}
85#rem {
86 height: 1rem;
87 width: 1rem;
88}
89.property {
90 min-height: 2rem;
91}
92.property.vector-section{
93
94 width: 24rem;
95}
96
97.property.texture {
98 display: block;
99}
100.property.texture input{
101 margin: 0.4rem 0;
102}
103.texture-image img{
104 padding: 0;
105 margin: 0;
106 width: 100%;
107 height: 100%;
108 display: none;
109}
110.texture-image {
111 display: block;
112 position: relative;
113 background-repeat: no-repeat;
114 background-position: center;
115 background-size: 100% 100%;
116 margin-top: 0.4rem;
117 height:128px;
118 width: 128px;
119 background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABhNJREFUeNrsnVFy4joQRVsSCwAqBMwqsrRsIavMEkICoeAf2+8j1R5ZGDBgpLzoUDVVmTT2dc8It/paOpi3t7faOSciImVZyn6/l6qqRETEWivj8VistYPFd7ud1HUtIiLGGBmPx5JKX0RkMplIzvmPnHNijBERaS7Ef1lrB40bY1oXgH5a/ZH+8P7+LlVVycfHR/MGa60URdGcYOi4MUaKomhGaGx9EZHlcplMP2X+Ly8vPwOgLEtxzklVVVJVVfOznqAsy9YFXhuvqqq5AF/Lj+srtr7+LpV+yvz1mNF+vxcRkdVqJdZaeXp6ap1ws9m0TjibzVoj6lJ8vV6fjJdlKev1ujViU+j7t8tc8p9Op1KWpYw06L9JL0Av0r9l+jXl3nhd11JV1VE8tn5YM3PI3xjzoxVOGvyDU7zQj6s/0tGmI++amtNV087F9Wf/FnVPzRtCXz8RdV1nlb/efUbaJy4Wi0FqzjU1yRgjs9ls0Jp3jb6IyPPzczL9lPkvFot/dwCtB/om/x9oyJoXxps65NW8mPpdNTeX/JtBEtYE/+AUL/Tj6g/qA3TVnD41a6g++Bp9rYOp9FPnH80HOBcvy1I2m81D++BL+o/2AX5r/vgA+AD4AOif8AH8EdpVcy71sX3jWp/8W2AKff/TkUv+Oufr9AF0YuKc66xJ18T7eNP3nP9WfZ0EzufzJPqp8y+KQuq67vYBdETqCDpVU/rEw5oUnr+rD46h73/qUuinzh8fAP22D6AjxznXcqq6akrf+KmaFB6vf4+t7/sAelfIJf/GB9jtdmKMkdVq1dQM3zg4VVNU/NY+1Bgjh8Oh6YM1+dj6X19fzXwgp/wbH0DFtS7oyf0RdKqmhPFr+1RdseKfP7a+Px/IKX98APTbPoDOJrv60L417d54TH3V8lfS5pT/yfUA6/X6qOZcqkm3xrUm6X9CTH3fB0ihnzr/Ix9A/3T1qbfWpGvjMfX9T0UK/dT54wOg/88H8EfGPTVr6D740frhLDmn/Hv5AH1qku9t31KTzh3/aP1LPsBfzr+XDxCO0K6ack/N6qp5MfUv+QB/Of/ePsCQfWmfc6EfV3/kjzZrrRwOh9YtKHSm/LjOH3yrMTzej4c1y//51PHoP0a/tR7AOSdFURw9rz5VU049zw7jl2qWrosP++BY+iI/+wJS6afMv9kXoA6gvimsieHzZr/m6MTp3PPuc3G9SP95OPpx9JtOgT4cHwA+QCJ9+ADwAeADsC+AfQHo/4b1APAB4APAB4APAB8APgB9OD4AfAD4AFFqEnwA+AD4APgA6P86HwA+AHyAZhIBHwA+AHwA+AD04X/eB4APAB8APgB8APgA8AHow/P0AeADwAeADwAfAD4AfAD68Px8APgA8AHgA8AHgA8AH0DO70/v6lHvjaOfVn8U/iLcXx5OUML96X49vRTX3/nPw9FPo9+sB5hMJuKck+VyeVRTrLWtdfNdcf95eldNCuOfn5+tSYy/Pz+2voi0fICc8p/P5z93gJAPEN4+wufN4evaePj99eH+ePTj6p/1Abp60kt9Ksf/v46HDwAfAD6A/6gUPgD7AtgXwPP4DNcDwAeADwAfAD4AfAD4ADyPz289AHyA+Pqp84cPIPAB8AHwAfAB8AHgA7Q+HfAB4APAB4APAB+APjw3HwA+AHwA+ADwAeADwAegD8/TB4APAB8APgB8APgA8AHow/PzAeADwAeADwAfAD4AfACJ//316KfVH/mjLeb31+vx/kWhH0+/tR7AOSdFUUT9/nq9oK4+OJa+iLT25+eUf7MvIOQDxPr+en2F++PRj6PfdAr04fgA8AES6cMHgA8AH4B9AewLQP83rAeADwAfAD4AfAD4APAB6MPxAeADwAeIUpPgA8AHwAfAB0D/1/kA8AHgAzSTCPgA8AHgA8AHoA//8z4AfAD4APAB4APAB4APQB+epw8AHwA+AHwA+ADwAeAD0Ifn5wPAB4APAB8APgB8gBz5AOb19bX2TYLpdNpqQ7bbbctJGjJeVZVst9vWLSu2/vf3t+Sc/yicFIRr0C7Fu76f/lw8XBePflr9/wYAqWwWUSLcO54AAAAASUVORK5CYII=');
120}
121
122.texture-image.no-texture{
123 background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAB81JREFUeNrsnTGPm0oXht97FWm2Ch2pTEeHpUihsyvTuXO67Ta/IPkr+Qfp3MWdO7Zad0SKZDo6XIWOrTzV9xVXZ8SygGHXG4/t96lW68GGw8vMmZlzDv98+/btfyBXy780wXXzTv74/fs3rXFFfPz4kT0AoQAoAJqAAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQCgAQgEQCoBQAIQCIBQAoQAIBUAoAHLmvDv3C7i7u4PjOMiyDOv1+mC75XKJoiga2wRBAN/34TgOHMdBWZYoigJpmiLPcwrARhzHAQD4vg/P81pvlLRrwvM8zGYz00ZrbY5xHAe+7yPPc9zf36MsSwrAVmazGX78+DHoGM/zsFgsAAB5nmOz2ZgeQimF8XiMMAxNu+VyaQRCH8Ai8jyH4zgIw7D3MUopzOdzAECaplitVk+GB601kiTBz58/obWG4ziIoohOoI38+vULABCGYWd3X2U6nUIphbIsEcdxa7uiKPDw8GCGGtd1KQDbKMsSWZZBKYXJZNLrGN/3zdN/iDRNTdcvx1EAFqGUwmazeeIQduG6LpRSAIAsy3r9hrRjD2BxL5AkiXEI+8wetNa9PXtp13eIoQBOQJIkxmHrcgjlJkov8JKpJwVgIVpr47CFYdh6g/f7/ZM5/9CehgKwmDRNURQFlFKYTqeNN/rx8dH0AH2faBn7KYAzQKZ1QRCYZd0qf/78MX+PRqNe3ymO5W63owBsR9bwZShoGirEq++zeBQEweBZAwVwYh4eHqC1RhAErQ6jOHVdK3yu65qhJE1TDgHn5BDKTW6auxdFYdYOgiDAYrF40k4phTAM8fnzZyilUBRF54rhOfIOF06SJMYPaPt8v99jOp3C8zx4nget9bPZQ5ZlF3fzL0IAZVke9OLv7+/Njl/brCHLMozHY4xGI3z48MH0EEVRIMuyi40H+EdqBbNS6HXBSqGEAiAUAAVAE1AAhAIgFAChAAgFQCgAQgGQq+Eom0GLxeJgGHYVSdCUhM02yrI0qV5hGGIymaAsy9b0LNd1cXt7CwDYbDa98wOA/zKLVquVSQGr/nYTbe2iKDIh53JtZVmiLEvsdjtst9tn5z7EDmfXA3QFXdaTMbvYbrdm568tgkdueJ7njbt3QwJA+8YJ1tsFQQDXdXFzc2N2E0Uwk8kEX758eXbMEDtY2QOsVqtn//v69SsAYL1eH9xK7dNGgjuiKMJ4PH4WmSN7+QBMFu/3798bn1oAzz47NvVrqmYgz2azRpv1scNV+wDVaN969y6JIEmSWBmyJenlIgZbcgvOzgmUqJxqkmY18ldCvGwkz/MntQcogBcgETrVMV98Aptvfh1JTKEAXsBms4HWGp7nYT6fw3Ec5Hlufbi253lQSkFr3VqmhgLoQVmW2G63ZigQx8/2my/FKCR17WLWAV7LfD5vzOFLkqS1W0/T1HT9RVFY5/jNZjMz3ouvorVGHMet9QheYoer7AGq478Y2LaiDTc3N3Bd90megSwG2YQVPcDQ+a/ccK01ttutWSWsetl/i7bfq16TzP1lGFgul0exw9X2AJLGJV3joRXCl3rnXbUDhmQKl2WJ9XoNrbV1vdXZCUCWWqvVQGR8HFIgqmuaKUiCSJcA+nrzWmvzdA/ZN6EAKlTz/eXmA3iSuXOoNEzfBRsA+PTpU+PnUjxSfnvo9/ZNR6cAakjFj2rqd3VtQJ6u1z5h1e+SdYbqdK5aWHLImC0OoFQgpRN4YPoD/LfRVC8C2TQlkhVC3/dfVDG0/l1xHCOKIvi+b572atJoURSdtYnbfAHxV0aj0TP/oY8dzqYH6OscHXK26tO+rqcujmNTIKqtJkDfc0vTFMvl8smu436/R57niOO4NSbh0HfLkFHtpYbY4dgwOfRKYXIooQAIBUAB0AQUAKEACAVAKABCARAKgFAA5Gp4s93AKIrw/v17ExsnFEWB/X6P3W6HLMtaN0+GJkwOad+W2FlPLq3GHFSRdq85h2PYyGoByG6cvJOnHiEryZJSg7e+s1ZNmOyzSza0ffWYJsIwbMzk7Tp+6Dm81kZWC0BoCnSU7dowDE2K12q1alT60EDJYwVWKqUQRdHgPf9jnfMQG52dDyA5fLKnLlGztiB5Bn1eP3fuNvr31IaWZM9jhHIdEwk5G1Jk4hxtdPJZQJZlJrLWlnBpx3FMmrnrup3RReduIyumgXJxtryRUxw4mQXIO4Yv0UZWCMDWN3I2vX7u0mxk1RtDmp6yoQmTbe27kjK7iOMYt7e3CIIA2+22VyLIWyZ5Hrsnsmol0Jac+fo51QtSXJKNrOgBuvLsTrUOUO8FxAP3ff/gTXiLc3irt5aevAdQSpmpja0vZqq+fm4ymfz18i5vaaOTC0DSvapv8rQRmRY6joPxeHwxNjqpAGSpUwx8ikKJQ5AyNFKb4BJsdBIfwPM8BEFgFjXSNG3debMJSUv7GyuWf8tGby6Aaq2c+qvaJce/a3p2ioTJQ73A3d3di6aBbef8WhtZKQDJ6K1fTJ7neHx8PFjWTcbbvvPePm8QbVtc6ft/+UwKUdfbDT3n19roGDA59EphciihAAgFQAHQBBQAoQAIBUAoAEIBEAqAUACEAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQC4CkxgiceKEPQC5Iv4/APgB2O7x8IXXAAAAAElFTkSuQmCC');
124}
scripts/system/particle_explorer/particleExplorer.html+28−81
@@ -1,95 +1,42 @@
1<!--1<!--
2// particleExplorer.hml2// particleExplorer.hml
3// 3//
4//4//
5// Created by James B. Pollack @imgntn on 9/26/20155// Created by James B. Pollack @imgntn on 9/26/2015
6// Copyright 2015 High Fidelity, Inc.6// Copyright 2015 High Fidelity, Inc.
7//7//
8// Loads dat.gui, underscore, and the app8
9// Quickly edit the aesthetics of a particle system. 9// Reworked by Menithal on 20/5/2017
10// Using a custom built system for High Fidelity
10//11//
11// Distributed under the Apache License, Version 2.0.12// Distributed under the Apache License, Version 2.0.
12// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html13// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
13// 14//
14 --> 15 -->
15<html>16<html>
16<head>17<head>
17<link rel="stylesheet" type="text/css" href="../html/css/colpick.css">18 <link rel="stylesheet" type="text/css" href="../html/css/colpick.css">
18<script type="text/javascript" src="dat.gui.min.js"></script>19 <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
19<script type="text/javascript" src="underscore-min.js"></script>20 <script type="text/javascript" src="../html/js/eventBridgeLoader.js"></script>
20<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>21 <!---->
21<script type="text/javascript" src="../html/js/eventBridgeLoader.js"></script>22 <script type="text/javascript" src="../html/js/jquery-2.1.4.min.js"></script>
22<script type="text/javascript" src="../html/js/jquery-2.1.4.min.js"></script>23 <script type="text/javascript" src="../html/js/colpick.js"></script>
23<script type="text/javascript" src="../html/js/colpick.js"></script>24
24<script type="text/javascript" src="particleExplorer.js"></script>25 <script type="text/javascript" src="underscore-min.js"></script>
25<script>26 <script type="text/javascript" src="hifi-entity-ui.js?v1"></script>
26 function loaded() {27
27 // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked28 <link rel="stylesheet" type="text/css" href="../html/css/hifi-style.css">
28 document.addEventListener("contextmenu", function (event) {29 <link rel="stylesheet" type="text/css" href="../html/css/edit-style.css">
29 event.preventDefault();30 <link rel="stylesheet" type="text/css" href="particle-style.css">
30 }, false);
31 }
32</script>
33<style>
34
35body{
36 background-color:black;
37 overflow-x: hidden;
38}
39
40#my-gui-container{
41
42}
43
44.importer{
45 margin-bottom:4px;
46}
47.exported-props-section {
48 width: 50%;
49 margin: 0 auto;
50
51}
52#exported-props {
53 /* Set the margin-left and margin-right automatically set */
54 color: white;
55 white-space: pre-wrap; /* css-3 */
56}
57
58.color-box {
59 display: block;
60 width: 60%;
61 height: 21px;
62 margin-top: 4px;
63 float: left;
64}
65
66.color-box.highlight {
67 width: 13.5pt;
68 height: 13.5pt;
69 border: 1.5pt solid black;
70}
71
72::-webkit-input-placeholder {
73 text-align: center;
74 font-family: Helvetica
75}
76
77#importer-input{
78 width:90%;
79 line-height: 2;
80 margin-left:5%;
81}
82
83</style>
84</head>31</head>
85<body onload="loaded();">32<body>
86<div class="importer">33 <div id="particle-explorer">
87<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">34 <div class="section-header">
88<div class = "exported-props-section">35 <label> Particle Explorer </label>
89 <div id = "exported-props"></div> 36 </div>
90</div>37 <!-- This will be filled by the script! -->
91<div id="my-gui-container">38 </div>
92</div>39 <div id="rem"></div>
93 40 <script type="text/javascript" src="particleExplorer.js"></script>
94</body>41</body>
95</html>42</html>
scripts/system/particle_explorer/particleExplorer.js+397−537
@@ -2,550 +2,410 @@ · 1 unmodified line
2// particleExplorer.js2// particleExplorer.js
3//3//
4// Created by James B. Pollack @imgntn on 9/26/20154// Created by James B. Pollack @imgntn on 9/26/2015
5// Copyright 2015 High Fidelity, Inc.5// Copyright 2017 High Fidelity, Inc.
6//
7// Reworked by Menithal on 20/5/2017
8//
6// Web app side of the App - contains GUI.9// Web app side of the App - contains GUI.
7// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. 10// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
8//11//
9// Distributed under the Apache License, Version 2.0.12// Distributed under the Apache License, Version 2.0.
10// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html13// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11//14//
12/* global window, alert, EventBridge, dat, listenForSettingsUpdates, createVec3Folder, createQuatFolder, writeVec3ToInterface, writeDataToInterface, 15/* global HifiEntityUI, openEventBridge, console, EventBridge, document, window */
13 $, document, _, openEventBridge */16/* eslint no-console: 0, no-global-assign: 0 */
14 17
15var Settings = function () {18(function () {
16 this.exportSettings = function () {19
17 // copyExportSettingsToClipboard();20 var root = document.getElementById("particle-explorer");
18 showPreselectedPrompt();21
19 };22 window.onload = function () {
20 this.importSettings = function () {23 var ui = new HifiEntityUI(root);
21 importSettings();24 var textarea = document.createElement("textarea");
22 };25 var properties = "";
23};26 var menuStructure = {
24 27 General: [
25// 2-way bindings-aren't quite ready yet. see bottom of file.28 {
26var AUTO_UPDATE = false;29 type: "Row",
27var UPDATE_ALL_FREQUENCY = 100;30 id: "export-import-field"
28 31 },
29var controllers = [];32 {
30var colpickKeys = [];33 id: "show-properties-button",
31var folders = [];34 name: "Show Properties",
32var gui = null;35 type: "Button",
33var settings = new Settings();36 class: "blue",
34var updateInterval;37 disabled: true,
35 38 callback: function (event) {
36var active = false;39 var insertZone = document.getElementById("export-import-field");
37 40 var json = ui.getSettings();
38var currentInputField;41 properties = JSON.stringify(json);
39var storedController;42 textarea.value = properties;
40// CHANGE TO WHITELIST43 if (!insertZone.contains(textarea)) {
41var keysToAllow = [44 insertZone.appendChild(textarea);
42 'isEmitting',45 insertZone.parentNode.parentNode.style.maxHeight =
43 'maxParticles',46 insertZone.parentNode.clientHeight + "px";
44 'lifespan',47 document.getElementById("export-properties-button").removeAttribute("disabled");
45 'emitRate',48 textarea.onchange = function (e) {
46 'emitSpeed',49 if (e.target.value !== properties) {
47 'speedSpread',50 document.getElementById("import-properties-button").removeAttribute("disabled");
48 'emitOrientation',51 }
49 'emitDimensions',52 };
50 'polarStart',53 textarea.oninput = textarea.onchange;
51 'polarFinish',54 } else {
52 'azimuthStart',55 textarea.onchange = function () {};
53 'azimuthFinish',56 textarea.oninput = textarea.onchange;
54 'emitAcceleration',57 textarea.value = "";
55 'accelerationSpread',58 textarea.remove();
56 'particleRadius',59 insertZone.parentNode.parentNode.style.maxHeight =
57 'radiusSpread',60 insertZone.parentNode.clientHeight + "px";
58 'radiusStart',61 document.getElementById("export-properties-button").setAttribute("disabled", true);
59 'radiusFinish',62 document.getElementById("import-properties-button").setAttribute("disabled", true);
60 'color',63 }
61 'colorSpread',64 }
62 'colorStart',65 },
63 'colorFinish',66 {
64 'alpha',67 id: "import-properties-button",
65 'alphaSpread',68 name: "Import",
66 'alphaStart',69 type: "Button",
67 'alphaFinish',70 class: "blue",
68 'emitterShouldTrail',71 disabled: true,
69 'textures'72 callback: function (event) {
70];73 ui.fillFields(JSON.parse(textarea.value));
71 74 ui.submitChanges(JSON.parse(textarea.value));
72var individualKeys = [];75 }
73var vec3Keys = [];76 },
74var quatKeys = [];77 {
75var colorKeys = [];78 id: "export-properties-button",
76 79 name: "Export",
77window.onload = function () {80 type: "Button",
78 openEventBridge(function () {81 class: "red",
79 var stringifiedData = JSON.stringify({82 disabled: true,
80 messageType: 'page_loaded'83 callback: function (event) {
84 textarea.select();
85 try {
86 var success = document.execCommand('copy');
87 if (!success) {
88 throw "Not success :(";
89 }
90 } catch (e) {
91 print("couldnt copy field");
92 }
93 }
94 },
95 {
96 type: "Row"
97 },
98 {
99 id: "isEmitting",
100 name: "Is Emitting",
101 type: "Boolean"
102 },
103 {
104 type: "Row"
105 },
106 {
107 id: "lifespan",
108 name: "Lifespan",
109 type: "SliderFloat",
110 min: 0.01,
111 max: 10
112 },
113 {
114 type: "Row"
115 },
116 {
117 id: "maxParticles",
118 name: "Max Particles",
119 type: "SliderInteger",
120 min: 1,
121 max: 10000
122 },
123 {
124 type: "Row"
125 },
126 {
127 id: "textures",
128 name: "Textures",
129 type: "Texture"
130 },
131 {
132 type: "Row"
133 }
134 ],
135 Emit: [
136 {
137 id: "emitRate",
138 name: "Emit Rate",
139 type: "SliderInteger",
140 max: 1000,
141 min: 1
142 },
143 {
144 type: "Row"
145 },
146 {
147 id: "emitSpeed",
148 name: "Emit Speed",
149 type: "SliderFloat",
150 max: 5
151 },
152 {
153 type: "Row"
154 },
155 {
156 id: "emitDimensions",
157 name: "Emit Dimension",
158 type: "Vector",
159 defaultRange: {
160 min: 0,
161 step: 0.01
162 }
163 },
164 {
165 type: "Row"
166 },
167 {
168 id: "emitOrientation",
169 unit: "deg",
170 name: "Emit Orientation",
171 type: "VectorQuaternion",
172 defaultRange: {
173 min: 0,
174 step: 0.01
175 }
176 },
177 {
178 type: "Row"
179 },
180 {
181 id: "emitShouldTrail",
182 name: "Emit Should Trail",
183 type: "Boolean"
184 },
185 {
186 type: "Row"
187 }
188 ],
189 Radius: [
190 {
191 id: "particleRadius",
192 name: "Particle Radius",
193 type: "SliderFloat",
194 max: 4.0
195 },
196 {
197 type: "Row"
198 },
199 {
200 id: "radiusSpread",
201 name: "Radius Spread",
202 type: "SliderFloat",
203 max: 4.0
204 },
205 {
206 type: "Row"
207 },
208 {
209 id: "radiusStart",
210 name: "Radius Start",
211 type: "SliderFloat",
212 max: 4.0
213 },
214 {
215 type: "Row"
216 },
217 {
218 id: "radiusFinish",
219 name: "Radius Finish",
220 type: "SliderFloat",
221 max: 4.0
222 },
223 {
224 type: "Row"
225 }
226 ],
227 Color: [
228 {
229 id: "color",
230 name: "Color",
231 type: "Color",
232 defaultColor: {
233 red: 255,
234 green: 255,
235 blue: 255
236 }
237 },
238 {
239 type: "Row"
240 },
241 {
242 id: "colorSpread",
243 name: "Color Spread",
244 type: "Color",
245 defaultColor: {
246 red: 0,
247 green: 0,
248 blue: 0
249 }
250 },
251 {
252 type: "Row"
253 },
254 {
255 id: "colorStart",
256 name: "Color Start",
257 type: "Color",
258 defaultColor: {
259 red: 255,
260 green: 255,
261 blue: 255
262 }
263 },
264 {
265 type: "Row"
266 },
267 {
268 id: "colorFinish",
269 name: "Color Finish",
270 type: "Color",
271 defaultColor: {
272 red: 255,
273 green: 255,
274 blue: 255
275 }
276 },
277 {
278 type: "Row"
279 }
280 ],
281 Acceleration: [
282 {
283 id: "emitAcceleration",
284 name: "Emit Acceleration",
285 type: "Vector",
286 defaultRange: {
287 step: 0.01
288 }
289 },
290 {
291 type: "Row"
292 },
293 {
294 id: "accelerationSpread",
295 name: "Acceleration Spread",
296 type: "Vector",
297 defaultRange: {
298 step: 0.01
299 }
300 },
301 {
302 type: "Row"
303 }
304 ],
305 Alpha: [
306 {
307 id: "alpha",
308 name: "Alpha",
309 type: "SliderFloat"
310 },
311 {
312 type: "Row"
313 },
314 {
315 id: "alphaSpread",
316 name: "Alpha Spread",
317 type: "SliderFloat"
318 },
319 {
320 type: "Row"
321 },
322 {
323 id: "alphaStart",
324 name: "Alpha Start",
325 type: "SliderFloat"
326 },
327 {
328 type: "Row"
329 },
330 {
331 id: "alphaFinish",
332 name: "Alpha Finish",
333 type: "SliderFloat"
334 },
335 {
336 type: "Row"
337 }
338 ],
339 Polar: [
340 {
341 id: "polarStart",
342 name: "Polar Start",
343 unit: "deg",
344 type: "SliderRadian"
345 },
346 {
347 type: "Row"
348 },
349 {
350 id: "polarFinish",
351 name: "Polar Finish",
352 unit: "deg",
353 type: "SliderRadian"
354 },
355 {
356 type: "Row"
357 }
358 ],
359 Azimuth: [
360 {
361 id: "azimuthStart",
362 name: "Azimuth Start",
363 unit: "deg",
364 type: "SliderRadian",
365 min: -180,
366 max: 0
367 },
368 {
369 type: "Row"
370 },
371 {
372 id: "azimuthFinish",
373 name: "Azimuth Finish",
374 unit: "deg",
375 type: "SliderRadian"
376 },
377 {
378 type: "Row"
379 }
380 ]
381 };
382 ui.setUI(menuStructure);
383 ui.setOnSelect(function () {
384 document.getElementById("show-properties-button").removeAttribute("disabled");
385 document.getElementById("export-properties-button").setAttribute("disabled", true);
386 document.getElementById("import-properties-button").setAttribute("disabled", true);
81 });387 });
82 388 ui.build();
83 EventBridge.emitWebEvent(389 var overrideLoad = false;
84 stringifiedData390 if (openEventBridge === undefined) {
85 );391 overrideLoad = true,
86 392 openEventBridge = function (callback) {
87 listenForSettingsUpdates();393 callback({
88 window.onresize = setGUIWidthToWindowWidth;394 emitWebEvent: function () {},
89 });395 submitChanges: function () {},
90 396 scriptEventReceived: {
91};397 connect: function () {
92 398
93function loadGUI() {399 }
94 // whether or not to autoplace400 }
95 gui = new dat.GUI({401 });
96 autoPlace: false402 };
97 });
98
99 // if not autoplacing, put gui in a custom container
100 if (gui.autoPlace === false) {
101 var customContainer = document.getElementById('my-gui-container');
102 customContainer.appendChild(gui.domElement);
103 }
104
105 // presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment.
106 // gui.remember(settings);
107
108 colpickKeys = [];
109 var keys = _.keys(settings);
110
111 _.each(keys, function(key) {
112 var shouldAllow = _.contains(keysToAllow, key);
113
114 if (shouldAllow) {
115 var subKeys = _.keys(settings[key]);
116 var hasX = _.contains(subKeys, 'x');
117 var hasY = _.contains(subKeys, 'y');
118 var hasZ = _.contains(subKeys, 'z');
119 var hasW = _.contains(subKeys, 'w');
120 var hasRed = _.contains(subKeys, 'red');
121 var hasGreen = _.contains(subKeys, 'green');
122 var hasBlue = _.contains(subKeys, 'blue');
123
124 if ((hasX && hasY && hasZ) && hasW === false) {
125 vec3Keys.push(key);
126 } else if (hasX && hasY && hasZ && hasW) {
127 quatKeys.push(key);
128 } else if (hasRed || hasGreen || hasBlue) {
129 colorKeys.push(key);
130
131 } else {
132 individualKeys.push(key);
133 }
134 }403 }
135 });404 openEventBridge(function (EventBridge) {
136 405 ui.connect(EventBridge);
137 // alphabetize our keys
138 individualKeys.sort();
139 vec3Keys.sort();
140 quatKeys.sort();
141 colorKeys.sort();
142
143 // add to gui in the order they should appear
144 gui.add(settings, 'importSettings');
145 gui.add(settings, 'exportSettings');
146 addIndividualKeys();
147 addFolders();
148
149 // set the gui width to match the web window width
150 gui.width = window.innerWidth;
151
152 // 2-way binding stuff
153 // if (AUTO_UPDATE) {
154 // setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY);
155 // registerDOMElementsForListenerBlocking();
156 // }
157
158}
159
160function addIndividualKeys() {
161 _.each(individualKeys, function(key) {
162 // temporary patch for not crashing when this goes below 0
163 var controller;
164
165 if (key.indexOf('emitRate') > -1) {
166 controller = gui.add(settings, key).min(0);
167 } else {
168 controller = gui.add(settings, key);
169 }
170
171 // 2-way - need to fix not being able to input exact values if constantly listening
172 // controller.listen();
173
174 // keep track of our controller
175 controllers.push(controller);
176
177 // hook into change events for this gui controller
178 controller.onChange(function(value) {
179 // Fires on every change, drag, keypress, etc.
180 writeDataToInterface(this.property, value);
181 });406 });
182 407 if (overrideLoad) {
183 });408 openEventBridge();
184}
185
186function addFolders() {
187 _.each(colorKeys, function(key) {
188 createColorPicker(key);
189 });
190 _.each(vec3Keys, function(key) {
191 createVec3Folder(key);
192 });
193 _.each(quatKeys, function(key) {
194 createQuatFolder(key);
195 });
196}
197
198function createColorPicker(key) {
199 var colorObject = settings[key];
200
201 // Embed colpick's color picker into dat.GUI
202 var name = document.createElement('span');
203 name.className = 'property-name';
204 name.innerHTML = key;
205
206 var container = document.createElement('div');
207 container.appendChild(name);
208
209 var $colPickContainer = $('<div>', {
210 id: key.toString(),
211 class: "color-box"
212 });
213 $colPickContainer.css('background-color', "rgb(" + colorObject.red + "," + colorObject.green + "," + colorObject.blue + ")");
214 container.appendChild($colPickContainer[0]);
215
216 var $li = $('<li>', { class: 'cr object color' });
217 $li.append(container);
218 gui.__ul.appendChild($li[0]);
219 gui.onResize();
220
221 $colPickContainer.colpick({
222 colorScheme: 'dark',
223 layout: 'hex',
224 color: { r: colorObject.red, g: colorObject.green, b: colorObject.blue },
225 onSubmit: function (hsb, hex, rgb, el) {
226 $(el).css('background-color', '#' + hex);
227 $(el).colpickHide();
228
229 var obj = {};
230 obj[key] = { red: rgb.r, green: rgb.g, blue: rgb.b };
231 writeVec3ToInterface(obj);
232 }409 }
233 });
234
235 colpickKeys.push(key);
236}
237
238function createVec3Folder(category) {
239 var folder = gui.addFolder(category);
240
241 folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
242 // Fires when a controller loses focus.
243 var obj = {};
244 obj[category] = {};
245 obj[category][this.property] = value;
246 obj[category].y = settings[category].y;
247 obj[category].z = settings[category].z;
248 writeVec3ToInterface(obj);
249 });
250
251 folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
252 // Fires when a controller loses focus.
253 var obj = {};
254 obj[category] = {};
255 obj[category].x = settings[category].x;
256 obj[category][this.property] = value;
257 obj[category].z = settings[category].z;
258 writeVec3ToInterface(obj);
259 });
260
261 folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
262 // Fires when a controller loses focus.
263 var obj = {};
264 obj[category] = {};
265 obj[category].y = settings[category].y;
266 obj[category].x = settings[category].x;
267 obj[category][this.property] = value;
268 writeVec3ToInterface(obj);
269 });
270
271 folders.push(folder);
272 folder.open();
273}
274
275function createQuatFolder(category) {
276 var folder = gui.addFolder(category);
277
278 folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
279 // Fires when a controller loses focus.
280 var obj = {};
281 obj[category] = {};
282 obj[category][this.property] = value;
283 obj[category].y = settings[category].y;
284 obj[category].z = settings[category].z;
285 obj[category].w = settings[category].w;
286 writeVec3ToInterface(obj);
287 });
288
289 folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
290 // Fires when a controller loses focus.
291 var obj = {};
292 obj[category] = {};
293 obj[category].x = settings[category].x;
294 obj[category][this.property] = value;
295 obj[category].z = settings[category].z;
296 obj[category].w = settings[category].w;
297 writeVec3ToInterface(obj);
298 });
299
300 folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
301 // Fires when a controller loses focus.
302 var obj = {};
303 obj[category] = {};
304 obj[category].x = settings[category].x;
305 obj[category].y = settings[category].y;
306 obj[category][this.property] = value;
307 obj[category].w = settings[category].w;
308 writeVec3ToInterface(obj);
309 });
310
311 folder.add(settings[category], 'w').step(0.1).onChange(function(value) {
312 // Fires when a controller loses focus.
313 var obj = {};
314 obj[category] = {};
315 obj[category].x = settings[category].x;
316 obj[category].y = settings[category].y;
317 obj[category].z = settings[category].z;
318 obj[category][this.property] = value;
319 writeVec3ToInterface(obj);
320 });
321
322 folders.push(folder);
323 folder.open();
324}
325
326function convertColorObjectToArray(colorObject) {
327 var colorArray = [];
328
329 _.each(colorObject, function(singleColor) {
330 colorArray.push(singleColor);
331 });
332
333 return colorArray;
334}
335
336function convertColorArrayToObject(colorArray) {
337 var colorObject = {
338 red: colorArray[0],
339 green: colorArray[1],
340 blue: colorArray[2]
341 };
342
343 return colorObject;
344}
345
346function writeDataToInterface(property, value) {
347 var data = {};
348 data[property] = value;
349
350 var sendData = {
351 messageType: "settings_update",
352 updatedSettings: data
353 };
354
355 var stringifiedData = JSON.stringify(sendData);
356
357 EventBridge.emitWebEvent(stringifiedData);
358}
359
360function writeVec3ToInterface(obj) {
361 var sendData = {
362 messageType: "settings_update",
363 updatedSettings: obj
364 };
365
366 var stringifiedData = JSON.stringify(sendData);
367
368 EventBridge.emitWebEvent(stringifiedData);
369}
370
371function listenForSettingsUpdates() {
372 EventBridge.scriptEventReceived.connect(function(data) {
373 data = JSON.parse(data);
374 if (data.messageType === 'particle_settings') {
375 _.each(data.currentProperties, function(value, key) {
376 settings[key] = {};
377 settings[key] = value;
378 });
379
380 if (gui) {
381 manuallyUpdateDisplay();
382 } else {
383 loadGUI();
384 }
385 if (!active) {
386 // gui.toggleHide();
387 gui.closed = false;
388 }
389 active = true;
390
391 } else if (data.messageType === "particle_close") {
392 // none of this seems to work.
393 // if (active) {
394 // gui.toggleHide();
395 // }
396 active = false;
397 gui.closed = true;
398 }
399 });
400}
401
402function manuallyUpdateDisplay() {
403 // Iterate over all controllers
404 // this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps.
405 var i;
406 for (i in gui.__controllers) {
407 gui.__controllers[i].updateDisplay();
408 }
409
410 // Update color pickers
411 for (i in colpickKeys) {
412 var color = settings[colpickKeys[i]];
413 var $object = $('#' + colpickKeys[i]);
414 $object.css('background-color', "rgb(" + color.red + "," + color.green + "," + color.blue + ")");
415 $object.colpickSetColor({ r: color.red, g: color.green, b: color.blue }, true);
416 }
417}
418
419function setGUIWidthToWindowWidth() {
420 if (gui !== null) {
421 gui.width = window.innerWidth;
422 }
423}
424
425function handleInputKeyPress(e) {
426 if (e.keyCode === 13) {
427 importSettings();
428 }
429 return false;
430}
431
432function importSettings() {
433 var importInput = document.getElementById('importer-input');
434
435 try {
436 var importedSettings = JSON.parse(importInput.value);
437
438 var keys = _.keys(importedSettings);
439
440 _.each(keys, function(key) {
441 var shouldAllow = _.contains(keysToAllow, key);
442
443 if (!shouldAllow) {
444 return;
445 }
446
447 settings[key] = importedSettings[key];
448 });
449
450 writeVec3ToInterface(settings);
451
452 manuallyUpdateDisplay();
453 } catch (e) {
454 alert('Not properly formatted JSON');
455 }
456}
457
458function prepareSettingsForExport() {
459 var keys = _.keys(settings);
460
461 var exportSettings = {};
462
463 _.each(keys, function(key) {
464 var shouldAllow = _.contains(keysToAllow, key);
465
466 if (!shouldAllow) {
467 return;
468 }
469
470 if (key.indexOf('color') > -1) {
471 var colorObject = convertColorArrayToObject(settings[key]);
472 settings[key] = colorObject;
473 }
474
475 exportSettings[key] = settings[key];
476 });
477
478 return JSON.stringify(exportSettings, null, 4);
479}
480
481function showPreselectedPrompt() {
482 var elem = document.getElementById("exported-props");
483 var exportSettings = prepareSettingsForExport();
484 elem.innerHTML = "";
485 var buttonnode = document.createElement('input');
486 buttonnode.setAttribute('type', 'button');
487 buttonnode.setAttribute('value', 'close');
488 elem.appendChild(document.createTextNode("COPY THE BELOW FIELD TO CLIPBOARD:"));
489 elem.appendChild(document.createElement("br"));
490 var textAreaNode = document.createElement("textarea");
491 textAreaNode.value = exportSettings;
492 elem.appendChild(textAreaNode);
493 elem.appendChild(document.createElement("br"));
494 elem.appendChild(buttonnode);
495
496 buttonnode.onclick = function() {
497 console.log("click");
498 elem.innerHTML = "";
499 };410 };
500 411})();
501 // window.alert("Ctrl-C to copy, then Enter.", prepareSettingsForExport());
502}
503
504function removeContainerDomElement() {
505 var elem = document.getElementById("my-gui-container");
506 elem.parentNode.removeChild(elem);
507}
508
509function removeListenerFromGUI(key) {
510 _.each(gui.__listening, function(controller, index) {
511 if (controller.property === key) {
512 storedController = controller;
513 gui.__listening.splice(index, 1);
514 }
515 });
516}
517
518// the section below is to try to work at achieving two way bindings;
519
520function addListenersBackToGUI() {
521 gui.__listening.push(storedController);
522 storedController = null;
523}
524
525function registerDOMElementsForListenerBlocking() {
526 _.each(gui.__controllers, function(controller) {
527 var input = controller.domElement.childNodes[0];
528 input.addEventListener('focus', function() {
529 console.log('INPUT ELEMENT GOT FOCUS!' + controller.property);
530 removeListenerFromGUI(controller.property);
531 });
532 });
533
534 _.each(gui.__controllers, function(controller) {
535 var input = controller.domElement.childNodes[0];
536 input.addEventListener('blur', function() {
537 console.log('INPUT ELEMENT GOT BLUR!' + controller.property);
538 addListenersBackToGUI();
539 });
540 });
541
542 // also listen to inputs inside of folders
543 _.each(gui.__folders, function(folder) {
544 _.each(folder.__controllers, function(controller) {
545 var input = controller.__input;
546 input.addEventListener('focus', function() {
547 console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property);
548 });
549 });
550 });
551}
scripts/system/particle_explorer/particleExplorerTool.js+6−4
@@ -4,23 +4,22 @@ · 3 unmodified lines
4// Created by Eric Levin on 2/15/164// Created by Eric Levin on 2/15/16
5// Copyright 2016 High Fidelity, Inc.5// Copyright 2016 High Fidelity, Inc.
6// Adds particleExplorer tool to the edit panel when a user selects a particle entity from the edit tool window6// Adds particleExplorer tool to the edit panel when a user selects a particle entity from the edit tool window
7// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. 7// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
8//8//
9// Distributed under the Apache License, Version 2.0.9// Distributed under the Apache License, Version 2.0.
10// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html10// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11//11//
12/*global window, alert, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/12/* global window, alert, ParticleExplorerTool, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
13 13
14 14
15var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html');15var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html');
16 16
17ParticleExplorerTool = function() {17ParticleExplorerTool = function() {
18 var that = {};18 var that = {};
19
20 that.createWebView = function() {19 that.createWebView = function() {
21 that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");20 that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
22 that.webView.setVisible = function(value) {};21 that.webView.setVisible = function(value) {};
23 that.webView.webEventReceived.connect(that.webEventReceived); 22 that.webView.webEventReceived.connect(that.webEventReceived);
24 }23 }
25 24
26 that.destroyWebView = function() {25 that.destroyWebView = function() {
@@ -38,6 +37,9 @@ ParticleExplorerTool = function() { · 11 unmodified lines
38 that.webEventReceived = function(data) {37 that.webEventReceived = function(data) {
39 var data = JSON.parse(data);38 var data = JSON.parse(data);
40 if (data.messageType === "settings_update") {39 if (data.messageType === "settings_update") {
40 if (data.updatedSettings.emitOrientation) {
41 data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation);
42 }
41 Entities.editEntity(that.activeParticleEntity, data.updatedSettings);43 Entities.editEntity(that.activeParticleEntity, data.updatedSettings);
42 }44 }
43 }45 }
scripts/system/progress.js+2−2
@@ -261,7 +261,7 @@ · 260 unmodified lines
261 }261 }
262 }262 }
263 263
264 gpuTextures = Render.getConfig("Stats").textureGPUTransferCount;264 gpuTextures = Render.getConfig("Stats").texturePendingGPUTransferCount;
265 265
266 // Update state266 // Update state
267 if (!visible) { // Not visible because no recent downloads267 if (!visible) { // Not visible because no recent downloads
@@ -290,7 +290,7 @@ · 22 unmodified lines
290 }, FADE_OUT_WAIT);290 }, FADE_OUT_WAIT);
291 }291 }
292 } else {292 } else {
293 if (displayProgress < 100 || gpuTextures > 0) { // Was finished and waiting to fade out but have resumed so 293 if (displayProgress < 100 || gpuTextures > 0) { // Was finished and waiting to fade out but have resumed so
294 // don't fade out294 // don't fade out
295 Script.clearInterval(fadeWaitTimer);295 Script.clearInterval(fadeWaitTimer);
296 fadeWaitTimer = null;296 fadeWaitTimer = null;
scripts/system/snapshot.js+172−18
@@ -36,6 +36,8 @@ var shareAfterLogin = false; · 35 unmodified lines
36var snapshotToShareAfterLogin = [];36var snapshotToShareAfterLogin = [];
37var METAVERSE_BASE = location.metaverseServerUrl;37var METAVERSE_BASE = location.metaverseServerUrl;
38var isLoggedIn;38var isLoggedIn;
39var numGifSnapshotUploadsPending = 0;
40var numStillSnapshotUploadsPending = 0;
39 41
40// It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story,42// It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story,
41// POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS43// POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS
@@ -56,6 +58,10 @@ function removeFromStoryIDsToMaybeDelete(story_id) { · 14 unmodified lines
56 print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));58 print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));
57}59}
58 60
61function fileExtensionMatches(filePath, extension) {
62 return filePath.split('.').pop().toLowerCase() === extension;
63}
64
59function onMessage(message) {65function onMessage(message) {
60 // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:66 // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:
61 // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)67 // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)
@@ -93,11 +99,13 @@ function onMessage(message) { · 31 unmodified lines
93 Settings.setValue("previousStillSnapStoryID", "");99 Settings.setValue("previousStillSnapStoryID", "");
94 Settings.setValue("previousStillSnapBlastingDisabled", false);100 Settings.setValue("previousStillSnapBlastingDisabled", false);
95 Settings.setValue("previousStillSnapHifiSharingDisabled", false);101 Settings.setValue("previousStillSnapHifiSharingDisabled", false);
102 Settings.setValue("previousStillSnapUrl", "");
96 Settings.setValue("previousAnimatedSnapPath", "");103 Settings.setValue("previousAnimatedSnapPath", "");
97 Settings.setValue("previousAnimatedSnapStoryID", "");104 Settings.setValue("previousAnimatedSnapStoryID", "");
98 Settings.setValue("previousAnimatedSnapBlastingDisabled", false);105 Settings.setValue("previousAnimatedSnapBlastingDisabled", false);
99 Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);106 Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
100 }107 }
108 updatePrintPermissions();
101 break;109 break;
102 case 'login':110 case 'login':
103 openLoginWindow();111 openLoginWindow();
@@ -134,16 +142,24 @@ function onMessage(message) { · 30 unmodified lines
134 break;142 break;
135 case 'shareSnapshotForUrl':143 case 'shareSnapshotForUrl':
136 isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) {144 isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) {
137 if (canShare) {145 var isGif = fileExtensionMatches(message.data, "gif");
138 isLoggedIn = Account.isLoggedIn();146 isLoggedIn = Account.isLoggedIn();
147 isUploadingPrintableStill = canShare && Account.isLoggedIn() && !isGif;
148 if (canShare) {
139 if (isLoggedIn) {149 if (isLoggedIn) {
140 print('Sharing snapshot with audience "for_url":', message.data);150 print('Sharing snapshot with audience "for_url":', message.data);
141 Window.shareSnapshot(message.data, Settings.getValue("previousSnapshotHref"));151 Window.shareSnapshot(message.data, Settings.getValue("previousSnapshotHref"));
152 if (isGif) {
153 numGifSnapshotUploadsPending++;
154 } else {
155 numStillSnapshotUploadsPending++;
156 }
142 } else {157 } else {
143 shareAfterLogin = true;158 shareAfterLogin = true;
144 snapshotToShareAfterLogin.push({ path: message.data, href: Settings.getValue("previousSnapshotHref") });159 snapshotToShareAfterLogin.push({ path: message.data, href: Settings.getValue("previousSnapshotHref") });
145 }160 }
146 }161 }
162 updatePrintPermissions();
147 });163 });
148 break;164 break;
149 case 'blastToConnections':165 case 'blastToConnections':
@@ -203,6 +219,18 @@ function onMessage(message) { · 53 unmodified lines
203 });219 });
204 }220 }
205 break;221 break;
222 case 'requestPrintButtonUpdate':
223 updatePrintPermissions();
224 break;
225 case 'printToPolaroid':
226 if (Entities.canRez() || Entities.canRezTmp()) {
227 printToPolaroid(Settings.getValue("previousStillSnapUrl"));
228 removeFromStoryIDsToMaybeDelete(Settings.getValue("previousStillSnapStoryID"));
229 }
230 break;
231 case 'alertSnapshotLoadFailed':
232 snapshotFailedToLoad = true;
233 break;
206 case 'shareSnapshotWithEveryone':234 case 'shareSnapshotWithEveryone':
207 isLoggedIn = Account.isLoggedIn();235 isLoggedIn = Account.isLoggedIn();
208 if (isLoggedIn) {236 if (isLoggedIn) {
@@ -250,6 +278,54 @@ function onMessage(message) { · 41 unmodified lines
250 }278 }
251}279}
252 280
281var POLAROID_PRINT_SOUND = SoundCache.getSound(Script.resolvePath("assets/sounds/sound-print-photo.wav"));
282var POLAROID_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Test/snapshot.fbx';
283
284function printToPolaroid(image_url) {
285 var polaroid_url = image_url;
286
287 var model_pos = Vec3.sum(MyAvatar.position, Vec3.multiply(1.25, Quat.getForward(MyAvatar.orientation)));
288
289 var model_q1 = MyAvatar.orientation;
290 var model_q2 = Quat.angleAxis(90, Quat.getRight(model_q1));
291 var model_rot = Quat.multiply(model_q2, model_q1);
292
293 var properties = {
294 "type": 'Model',
295 "shapeType": 'box',
296
297 "name": "New Snapshot",
298 "description": "Printed from Snaps",
299 "modelURL": POLAROID_MODEL_URL,
300
301 "position": model_pos,
302 "rotation": model_rot,
303
304 "textures": JSON.stringify( { "tex.picture": polaroid_url } ),
305
306 "density": 200,
307 "restitution": 0.15,
308 "gravity": { "x": 0, "y": -4.5, "z": 0 },
309
310 "velocity": { "x": 0, "y": 3.5, "z": 0 },
311 "angularVelocity": { "x": -1.0, "y": 0, "z": -1.3 },
312
313 "dynamic": true,
314 "collisionsWillMove": true,
315
316 "userData": {
317 "grabbableKey": { "grabbable" : true }
318 }
319 };
320
321 var polaroid = Entities.addEntity(properties);
322 Audio.playSound(POLAROID_PRINT_SOUND, {
323 position: model_pos,
324 localOnly: false,
325 volume: 0.2
326 });
327}
328
253function fillImageDataFromPrevious() {329function fillImageDataFromPrevious() {
254 isLoggedIn = Account.isLoggedIn();330 isLoggedIn = Account.isLoggedIn();
255 var previousStillSnapPath = Settings.getValue("previousStillSnapPath");331 var previousStillSnapPath = Settings.getValue("previousStillSnapPath");
@@ -273,7 +349,8 @@ function fillImageDataFromPrevious() { · 17 unmodified lines
273 localPath: previousStillSnapPath,349 localPath: previousStillSnapPath,
274 story_id: previousStillSnapStoryID,350 story_id: previousStillSnapStoryID,
275 blastButtonDisabled: previousStillSnapBlastingDisabled,351 blastButtonDisabled: previousStillSnapBlastingDisabled,
276 hifiButtonDisabled: previousStillSnapHifiSharingDisabled352 hifiButtonDisabled: previousStillSnapHifiSharingDisabled,
353 errorPath: Script.resolvePath(Script.resourcesPath() + 'snapshot/img/no-image.jpg')
277 });354 });
278 }355 }
279 if (previousAnimatedSnapPath !== "") {356 if (previousAnimatedSnapPath !== "") {
@@ -281,7 +358,8 @@ function fillImageDataFromPrevious() { · 1 unmodified line
281 localPath: previousAnimatedSnapPath,358 localPath: previousAnimatedSnapPath,
282 story_id: previousAnimatedSnapStoryID,359 story_id: previousAnimatedSnapStoryID,
283 blastButtonDisabled: previousAnimatedSnapBlastingDisabled,360 blastButtonDisabled: previousAnimatedSnapBlastingDisabled,
284 hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled361 hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled,
362 errorPath: Script.resolvePath(Script.resourcesPath() + 'snapshot/img/no-image.jpg')
285 });363 });
286 }364 }
287}365}
@@ -305,26 +383,45 @@ function onButtonClicked() { · 17 unmodified lines
305 383
306function snapshotUploaded(isError, reply) {384function snapshotUploaded(isError, reply) {
307 if (!isError) {385 if (!isError) {
308 var replyJson = JSON.parse(reply);386 var replyJson = JSON.parse(reply),
309 var storyID = replyJson.user_story.id;387 storyID = replyJson.user_story.id,
388 imageURL = replyJson.user_story.details.image_url,
389 isGif = fileExtensionMatches(imageURL, "gif"),
390 ignoreGifSnapshotData = false,
391 ignoreStillSnapshotData = false;
310 storyIDsToMaybeDelete.push(storyID);392 storyIDsToMaybeDelete.push(storyID);
311 var imageURL = replyJson.user_story.details.image_url;
312 var isGif = imageURL.split('.').pop().toLowerCase() === "gif";
313 print('SUCCESS: Snapshot uploaded! Story with audience:for_url created! ID:', storyID);
314 tablet.emitScriptEvent(JSON.stringify({
315 type: "snapshot",
316 action: "snapshotUploadComplete",
317 story_id: storyID,
318 image_url: imageURL,
319 }));
320 if (isGif) {393 if (isGif) {
321 Settings.setValue("previousAnimatedSnapStoryID", storyID);394 numGifSnapshotUploadsPending--;
395 if (numGifSnapshotUploadsPending !== 0) {
396 ignoreGifSnapshotData = true;
397 }
398 } else {
399 numStillSnapshotUploadsPending--;
400 if (numStillSnapshotUploadsPending !== 0) {
401 ignoreStillSnapshotData = true;
402 }
403 }
404 if ((isGif && !ignoreGifSnapshotData) || (!isGif && !ignoreStillSnapshotData)) {
405 print('SUCCESS: Snapshot uploaded! Story with audience:for_url created! ID:', storyID);
406 tablet.emitScriptEvent(JSON.stringify({
407 type: "snapshot",
408 action: "snapshotUploadComplete",
409 story_id: storyID,
410 image_url: imageURL,
411 }));
412 if (isGif) {
413 Settings.setValue("previousAnimatedSnapStoryID", storyID);
414 } else {
415 Settings.setValue("previousStillSnapStoryID", storyID);
416 Settings.setValue("previousStillSnapUrl", imageURL);
417 }
322 } else {418 } else {
323 Settings.setValue("previousStillSnapStoryID", storyID);419 print('Ignoring snapshotUploaded() callback for stale ' + (isGif ? 'GIF' : 'Still' ) + ' snapshot. Stale story ID:', storyID);
324 }420 }
325 } else {421 } else {
326 print(reply);422 print(reply);
327 }423 }
424 isUploadingPrintableStill = false;
328}425}
329var href, domainId;426var href, domainId;
330function takeSnapshot() {427function takeSnapshot() {
@@ -336,10 +433,16 @@ function takeSnapshot() { · 5 unmodified lines
336 Settings.setValue("previousStillSnapStoryID", "");433 Settings.setValue("previousStillSnapStoryID", "");
337 Settings.setValue("previousStillSnapBlastingDisabled", false);434 Settings.setValue("previousStillSnapBlastingDisabled", false);
338 Settings.setValue("previousStillSnapHifiSharingDisabled", false);435 Settings.setValue("previousStillSnapHifiSharingDisabled", false);
436 Settings.setValue("previousStillSnapUrl", "");
339 Settings.setValue("previousAnimatedSnapPath", "");437 Settings.setValue("previousAnimatedSnapPath", "");
340 Settings.setValue("previousAnimatedSnapStoryID", "");438 Settings.setValue("previousAnimatedSnapStoryID", "");
341 Settings.setValue("previousAnimatedSnapBlastingDisabled", false);439 Settings.setValue("previousAnimatedSnapBlastingDisabled", false);
342 Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);440 Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
441
442 // Since we are taking a snapshot, we should make the print button appear to be loading/processing
443 snapshotFailedToLoad = false;
444 isUploadingPrintableStill = true;
445 updatePrintPermissions();
343 446
344 // Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving.447 // Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving.
345 // Turn it off now, before we start futzing with things (and possibly moving).448 // Turn it off now, before we start futzing with things (and possibly moving).
@@ -379,8 +482,15 @@ function takeSnapshot() { · 33 unmodified lines
379 Menu.setIsOptionChecked("Overlays", false);482 Menu.setIsOptionChecked("Overlays", false);
380 }483 }
381 484
485 var snapActivateSound = SoundCache.getSound(Script.resolvePath("../../resources/sounds/snap.wav"));
486
382 // take snapshot (with no notification)487 // take snapshot (with no notification)
383 Script.setTimeout(function () {488 Script.setTimeout(function () {
489 Audio.playSound(snapActivateSound, {
490 position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
491 localOnly: true,
492 volume: 1.0
493 });
384 HMD.closeTablet();494 HMD.closeTablet();
385 Script.setTimeout(function () {495 Script.setTimeout(function () {
386 Window.takeSnapshot(false, includeAnimated, 1.91);496 Window.takeSnapshot(false, includeAnimated, 1.91);
@@ -566,8 +676,16 @@ function onUsernameChanged() { · 179 unmodified lines
566 snapshotToShareAfterLogin.forEach(function (element) {676 snapshotToShareAfterLogin.forEach(function (element) {
567 print('Uploading snapshot after login:', element.path);677 print('Uploading snapshot after login:', element.path);
568 Window.shareSnapshot(element.path, element.href);678 Window.shareSnapshot(element.path, element.href);
679 var isGif = fileExtensionMatches(element.path, "gif");
680 if (isGif) {
681 numGifSnapshotUploadsPending++;
682 } else {
683 numStillSnapshotUploadsPending++;
684 }
569 });685 });
570 }686 }
687 isUploadingPrintableStill = canShare;
688 updatePrintPermissions();
571 });689 });
572 690
573 shareAfterLogin = false;691 shareAfterLogin = false;
@@ -575,6 +693,7 @@ function onUsernameChanged() { · 1 unmodified line
575 }693 }
576 }694 }
577}695}
696
578function snapshotLocationSet(location) {697function snapshotLocationSet(location) {
579 if (location !== "") {698 if (location !== "") {
580 tablet.emitScriptEvent(JSON.stringify({699 tablet.emitScriptEvent(JSON.stringify({
@@ -584,12 +703,44 @@ function snapshotLocationSet(location) { · 3 unmodified lines
584 }703 }
585}704}
586 705
706function updatePrintPermissions() {
707 processRezPermissionChange(Entities.canRez() || Entities.canRezTmp());
708}
709
710var snapshotFailedToLoad = false;
711var isUploadingPrintableStill = false;
712function processRezPermissionChange(canRez) {
713 var action = "";
714
715 if (canRez && !snapshotFailedToLoad) {
716 if (Settings.getValue("previousStillSnapUrl")) {
717 action = 'setPrintButtonEnabled';
718 } else if (isUploadingPrintableStill) {
719 action = 'setPrintButtonLoading';
720 } else {
721 action = 'setPrintButtonDisabled';
722 }
723 } else {
724 action = 'setPrintButtonDisabled';
725 }
726
727 tablet.emitScriptEvent(JSON.stringify({
728 type: "snapshot",
729 action : action
730 }));
731}
732
587button.clicked.connect(onButtonClicked);733button.clicked.connect(onButtonClicked);
588buttonConnected = true;734buttonConnected = true;
735
589Window.snapshotShared.connect(snapshotUploaded);736Window.snapshotShared.connect(snapshotUploaded);
590tablet.screenChanged.connect(onTabletScreenChanged);737tablet.screenChanged.connect(onTabletScreenChanged);
591GlobalServices.myUsernameChanged.connect(onUsernameChanged);738GlobalServices.myUsernameChanged.connect(onUsernameChanged);
592Snapshot.snapshotLocationSet.connect(snapshotLocationSet);739Snapshot.snapshotLocationSet.connect(snapshotLocationSet);
740
741Entities.canRezChanged.connect(updatePrintPermissions);
742Entities.canRezTmpChanged.connect(updatePrintPermissions);
743
593Script.scriptEnding.connect(function () {744Script.scriptEnding.connect(function () {
594 if (buttonConnected) {745 if (buttonConnected) {
595 button.clicked.disconnect(onButtonClicked);746 button.clicked.disconnect(onButtonClicked);
@@ -601,6 +752,9 @@ Script.scriptEnding.connect(function () { · 5 unmodified lines
601 Window.snapshotShared.disconnect(snapshotUploaded);752 Window.snapshotShared.disconnect(snapshotUploaded);
602 tablet.screenChanged.disconnect(onTabletScreenChanged);753 tablet.screenChanged.disconnect(onTabletScreenChanged);
603 Snapshot.snapshotLocationSet.disconnect(snapshotLocationSet);754 Snapshot.snapshotLocationSet.disconnect(snapshotLocationSet);
755
756 Entities.canRezChanged.disconnect(processRezPermissionChange);
757 Entities.canRezTmpChanged.disconnect(processRezPermissionChange);
604});758});
605 759
606}()); // END LOCAL_SCOPE760}()); // END LOCAL_SCOPE
scripts/system/tablet-ui/tabletUI.js+2−6
@@ -188,16 +188,12 @@ · 187 unmodified lines
188 gTablet.updateAudioBar(getMicLevel());188 gTablet.updateAudioBar(getMicLevel());
189 }189 }
190 190
191 if (validCheckTime - now > MSECS_PER_SEC/4) {191 if (now - validCheckTime > MSECS_PER_SEC) {
192 //each 250ms should be just fine192 validCheckTime = now;
193 updateTabletWidthFromSettings();193 updateTabletWidthFromSettings();
194 if (UIWebTablet) {194 if (UIWebTablet) {
195 UIWebTablet.setLandscape(landscape);195 UIWebTablet.setLandscape(landscape);
196 }196 }
197 }
198
199 if (validCheckTime - now > MSECS_PER_SEC) {
200 validCheckTime = now;
201 if (tabletRezzed && UIWebTablet && !tabletIsValid()) {197 if (tabletRezzed && UIWebTablet && !tabletIsValid()) {
202 // when we switch domains, the tablet entity gets destroyed and recreated. this causes198 // when we switch domains, the tablet entity gets destroyed and recreated. this causes
203 // the overlay to be deleted, but not recreated. If the overlay is deleted for this or any199 // the overlay to be deleted, but not recreated. If the overlay is deleted for this or any
server-console/src/main.js+23−25
@@ -821,6 +821,17 @@ for (var key in trayIcons) { · 820 unmodified lines
821 821
822const notificationIcon = path.join(__dirname, '../resources/console-notification.png');822const notificationIcon = path.join(__dirname, '../resources/console-notification.png');
823 823
824function isProcessRunning(pid) {
825 try {
826 // Sending a signal of 0 is effectively a NOOP.
827 // If sending the signal is successful, kill will return true.
828 // If the process is not running, an exception will be thrown.
829 return process.kill(pid, 0);
830 } catch (e) {
831 }
832 return false;
833}
834
824function onContentLoaded() {835function onContentLoaded() {
825 // Disable splash window for now.836 // Disable splash window for now.
826 // maybeShowSplash();837 // maybeShowSplash();
@@ -882,31 +893,18 @@ function onContentLoaded() { · 55 unmodified lines
882 startInterface();893 startInterface();
883 }894 }
884 895
885 // If we were launched with the shutdownWatcher option, then we need to watch for the interface app896 // If we were launched with the shutdownWith option, then we need to shutdown when that process (pid)
886 // shutting down. The interface app will regularly update a running state file which we will check.897 // is no longer running.
887 // If the file doesn't exist or stops updating for a significant amount of time, we will shut down.898 if (argv.shutdownWith) {
888 if (argv.shutdownWatcher) {899 let pid = argv.shutdownWith;
889 log.debug("Shutdown watcher requested... argv.shutdownWatcher:", argv.shutdownWatcher);900 console.log("Shutting down with process: ", pid);
890 var MAX_TIME_SINCE_EDIT = 5000; // 5 seconds between updates901 let checkProcessInterval = setInterval(function() {
891 var firstAttemptToCheck = new Date().getTime();902 let isRunning = isProcessRunning(pid);
892 var shutdownWatchInterval = setInterval(function(){903 if (!isRunning) {
893 var stats = fs.stat(argv.shutdownWatcher, function(err, stats) {904 log.debug("Watched process is no longer running, shutting down");
894 if (err) {905 clearTimeout(checkProcessInterval);
895 var sinceFirstCheck = new Date().getTime() - firstAttemptToCheck;906 forcedShutdown();
896 if (sinceFirstCheck > MAX_TIME_SINCE_EDIT) {907 }
897 log.debug("Running state file is missing, assume interface has shutdown... shutting down snadbox.");
898 forcedShutdown();
899 clearTimeout(shutdownWatchInterval);
900 }
901 } else {
902 var sinceEdit = new Date().getTime() - stats.mtime.getTime();
903 if (sinceEdit > MAX_TIME_SINCE_EDIT) {
904 log.debug("Running state of interface hasn't updated in MAX time... shutting down.");
905 forcedShutdown();
906 clearTimeout(shutdownWatchInterval);
907 }
908 }
909 });
910 }, 1000);908 }, 1000);
911 }909 }
912}910}
tests/controllers/src/main.cpp+8−0
@@ -122,6 +122,10 @@ int main(int argc, char** argv) { · 121 unmodified lines
122 glm::mat4(),122 glm::mat4(),
123 glm::mat4(),123 glm::mat4(),
124 glm::mat4(),124 glm::mat4(),
125 glm::mat4(),
126 glm::mat4(),
127 glm::mat4(),
128 glm::mat4(),
125 glm::mat4()129 glm::mat4()
126 };130 };
127 131
@@ -144,6 +148,10 @@ int main(int argc, char** argv) { · 16 unmodified lines
144 glm::mat4(),148 glm::mat4(),
145 glm::mat4(),149 glm::mat4(),
146 glm::mat4(),150 glm::mat4(),
151 glm::mat4(),
152 glm::mat4(),
153 glm::mat4(),
154 glm::mat4(),
147 glm::mat4()155 glm::mat4()
148 };156 };
149 157
tests/ktx/CMakeLists.txt+7−13
@@ -1,15 +1,9 @@
1# Declare dependencies
2macro (SETUP_TESTCASE_DEPENDENCIES)
3 # link in the shared libraries
4 link_hifi_libraries(shared ktx gpu image)
1 5
2set(TARGET_NAME ktx-test)6 package_libraries_for_deployment()
3 7endmacro ()
4if (WIN32)
5 SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
6endif()
7 8
8# This is not a testcase -- just set it up as a regular hifi project9setup_hifi_testcase()
9setup_hifi_project(Quick Gui OpenGL)
10set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
11
12# link in the shared libraries
13link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image)
14
15package_libraries_for_deployment()
tests/ktx/src/KtxTests.cpp+195−0
@@ -0,0 +1,195 @@
1//
2// Created by Bradley Austin Davis on 2016/07/01
3// Copyright 2014 High Fidelity, Inc.
4//
5// Distributed under the Apache License, Version 2.0.
6// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7//
8
9#include "KtxTests.h"
10
11#include <mutex>
12
13#include <QtTest/QtTest>
14
15#include <ktx/KTX.h>
16#include <gpu/Texture.h>
17#include <image/Image.h>
18
19
20QTEST_GUILESS_MAIN(KtxTests)
21
22QString getRootPath() {
23 static std::once_flag once;
24 static QString result;
25 std::call_once(once, [&] {
26 QFileInfo file(__FILE__);
27 QDir parent = file.absolutePath();
28 result = QDir::cleanPath(parent.currentPath() + "/../../..");
29 });
30 return result;
31}
32
33void KtxTests::initTestCase() {
34}
35
36void KtxTests::cleanupTestCase() {
37}
38
39void KtxTests::testKhronosCompressionFunctions() {
40 using namespace khronos::gl::texture;
41 QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0);
42 QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1);
43 QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1);
44 QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2);
45 QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00);
46 QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01);
47 QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01);
48 QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02);
49 QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400);
50
51 QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error);
52}
53
54void KtxTests::testKtxEvalFunctions() {
55 QCOMPARE(sizeof(ktx::Header), (size_t)64);
56 QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0);
57 QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3);
58 QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2);
59 QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1);
60 QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0);
61 QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0);
62 QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3);
63 QCOMPARE(ktx::evalPaddedSize(0x0), 0x0);
64 QCOMPARE(ktx::evalPaddedSize(0x1), 0x4);
65 QCOMPARE(ktx::evalPaddedSize(0x2), 0x4);
66 QCOMPARE(ktx::evalPaddedSize(0x3), 0x4);
67 QCOMPARE(ktx::evalPaddedSize(0x4), 0x4);
68 QCOMPARE(ktx::evalPaddedSize(0x400), 0x400);
69 QCOMPARE(ktx::evalPaddedSize(0x401), 0x404);
70 QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0);
71 QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1);
72 QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1);
73 QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2);
74}
75
76void KtxTests::testKtxSerialization() {
77 const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png";
78 QImage image(TEST_IMAGE);
79 gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
80 auto ktxMemory = gpu::Texture::serialize(*testTexture);
81 QVERIFY(ktxMemory.get());
82
83 // Serialize the image to a file
84 QTemporaryFile TEST_IMAGE_KTX;
85 {
86 const auto& ktxStorage = ktxMemory->getStorage();
87 QVERIFY(ktx::KTX::validate(ktxStorage));
88 QVERIFY(ktxMemory->isValid());
89
90 auto& outFile = TEST_IMAGE_KTX;
91 if (!outFile.open()) {
92 QFAIL("Unable to open file");
93 }
94 auto ktxSize = ktxStorage->size();
95 outFile.resize(ktxSize);
96 auto dest = outFile.map(0, ktxSize);
97 memcpy(dest, ktxStorage->data(), ktxSize);
98 outFile.unmap(dest);
99 outFile.close();
100 }
101
102
103 {
104 auto ktxStorage = std::make_shared<storage::FileStorage>(TEST_IMAGE_KTX.fileName());
105 QVERIFY(ktx::KTX::validate(ktxStorage));
106 auto ktxFile = ktx::KTX::create(ktxStorage);
107 QVERIFY(ktxFile.get());
108 QVERIFY(ktxFile->isValid());
109 {
110 const auto& memStorage = ktxMemory->getStorage();
111 const auto& fileStorage = ktxFile->getStorage();
112 QVERIFY(memStorage->size() == fileStorage->size());
113 QVERIFY(memStorage->data() != fileStorage->data());
114 QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size()));
115 QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size());
116 auto imageCount = ktxFile->_images.size();
117 auto startMemory = ktxMemory->_storage->data();
118 auto startFile = ktxFile->_storage->data();
119 for (size_t i = 0; i < imageCount; ++i) {
120 auto memImages = ktxMemory->_images[i];
121 auto fileImages = ktxFile->_images[i];
122 QVERIFY(memImages._padding == fileImages._padding);
123 QVERIFY(memImages._numFaces == fileImages._numFaces);
124 QVERIFY(memImages._imageSize == fileImages._imageSize);
125 QVERIFY(memImages._faceSize == fileImages._faceSize);
126 QVERIFY(memImages._faceBytes.size() == memImages._numFaces);
127 QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces);
128 auto faceCount = fileImages._numFaces;
129 for (uint32_t face = 0; face < faceCount; ++face) {
130 auto memFace = memImages._faceBytes[face];
131 auto memOffset = memFace - startMemory;
132 auto fileFace = fileImages._faceBytes[face];
133 auto fileOffset = fileFace - startFile;
134 QVERIFY(memOffset % 4 == 0);
135 QVERIFY(memOffset == fileOffset);
136 }
137 }
138 }
139 }
140 testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString());
141}
142
143#if 0
144
145static const QString TEST_FOLDER { "H:/ktx_cacheold" };
146//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" };
147
148//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };
149static const QString EXTENSIONS { "*.ktx" };
150
151int mainTemp(int, char**) {
152 qInstallMessageHandler(messageHandler);
153 auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
154 for (auto fileInfo : fileInfoList) {
155 qDebug() << fileInfo.filePath();
156 std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } };
157
158 if (!ktx::KTX::validate(storage)) {
159 qDebug() << "KTX invalid";
160 }
161
162 auto ktxFile = ktx::KTX::create(storage);
163 ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor();
164
165 qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs";
166 for (const auto& kv : ktxDescriptor.keyValues) {
167 qDebug() << "\t" << kv._key.c_str();
168 }
169
170 auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
171 if (offsetToMinMipKV) {
172 auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV;
173 auto minMipLevelAvailable = *data;
174 qDebug() << "\tMin mip available " << minMipLevelAvailable;
175 assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels);
176 }
177 auto storageSize = storage->size();
178 for (const auto& faceImageDesc : ktxDescriptor.images) {
179 //assert(0 == (faceImageDesc._faceSize % 4));
180 for (const auto& faceOffset : faceImageDesc._faceOffsets) {
181 assert(0 == (faceOffset % 4));
182 auto faceEndOffset = faceOffset + faceImageDesc._faceSize;
183 assert(faceEndOffset <= storageSize);
184 }
185 }
186
187 for (const auto& faceImage : ktxFile->_images) {
188 for (const ktx::Byte* faceBytes : faceImage._faceBytes) {
189 assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4));
190 }
191 }
192 }
193 return 0;
194}
195#endif
tests/ktx/src/KtxTests.h+21−0
@@ -0,0 +1,21 @@
1//
2// Created by Bradley Austin Davis on 2016/07/01
3// Copyright 2014 High Fidelity, Inc.
4//
5// Distributed under the Apache License, Version 2.0.
6// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7//
8
9#include <QtCore/QObject>
10
11class KtxTests : public QObject {
12 Q_OBJECT
13private slots:
14 void initTestCase();
15 void cleanupTestCase();
16 void testKtxEvalFunctions();
17 void testKhronosCompressionFunctions();
18 void testKtxSerialization();
19};
20
21
tests/ktx/src/main.cpp+0−223
@@ -1,223 +0,0 @@
1//
2// Created by Bradley Austin Davis on 2016/07/01
3// Copyright 2014 High Fidelity, Inc.
4//
5// Distributed under the Apache License, Version 2.0.
6// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7//
8
9#include <iostream>
10#include <string>
11#include <vector>
12#include <sstream>
13
14#include <QProcessEnvironment>
15
16#include <QtCore/QDir>
17#include <QtCore/QElapsedTimer>
18#include <QtCore/QLoggingCategory>
19#include <QtCore/QRegularExpression>
20#include <QtCore/QSettings>
21#include <QtCore/QTimer>
22#include <QtCore/QThread>
23#include <QtCore/QThreadPool>
24#include <QtCore/QSaveFile>
25
26#include <QtGui/QGuiApplication>
27#include <QtGui/QResizeEvent>
28#include <QtGui/QWindow>
29
30#include <QtWidgets/QFileDialog>
31#include <QtWidgets/QInputDialog>
32#include <QtWidgets/QMessageBox>
33#include <QtWidgets/QApplication>
34
35#include <shared/RateCounter.h>
36#include <shared/NetworkUtils.h>
37#include <shared/FileLogger.h>
38#include <shared/FileUtils.h>
39#include <StatTracker.h>
40#include <LogHandler.h>
41
42#include <gpu/Texture.h>
43#include <gl/Config.h>
44#include <model/TextureMap.h>
45#include <ktx/KTX.h>
46#include <image/Image.h>
47
48QSharedPointer<FileLogger> logger;
49
50gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true);
51
52
53void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
54 QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message);
55
56 if (!logMessage.isEmpty()) {
57#ifdef Q_OS_WIN
58 OutputDebugStringA(logMessage.toLocal8Bit().constData());
59 OutputDebugStringA("\n");
60#endif
61 if (logger) {
62 logger->addMessage(qPrintable(logMessage + "\n"));
63 }
64 }
65}
66
67const char * LOG_FILTER_RULES = R"V0G0N(
68hifi.gpu=true
69)V0G0N";
70
71QString getRootPath() {
72 static std::once_flag once;
73 static QString result;
74 std::call_once(once, [&] {
75 QFileInfo file(__FILE__);
76 QDir parent = file.absolutePath();
77 result = QDir::cleanPath(parent.currentPath() + "/../../..");
78 });
79 return result;
80}
81
82const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png";
83const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx";
84
85int main(int argc, char** argv) {
86 QApplication app(argc, argv);
87 QCoreApplication::setApplicationName("KTX");
88 QCoreApplication::setOrganizationName("High Fidelity");
89 QCoreApplication::setOrganizationDomain("highfidelity.com");
90 logger.reset(new FileLogger());
91
92 Q_ASSERT(ktx::evalPadding(0) == 0);
93 Q_ASSERT(ktx::evalPadding(1) == 3);
94 Q_ASSERT(ktx::evalPadding(2) == 2);
95 Q_ASSERT(ktx::evalPadding(3) == 1);
96 Q_ASSERT(ktx::evalPadding(4) == 0);
97 Q_ASSERT(ktx::evalPadding(1024) == 0);
98 Q_ASSERT(ktx::evalPadding(1025) == 3);
99 Q_ASSERT(ktx::evalPaddedSize(0) == 0);
100 Q_ASSERT(ktx::evalPaddedSize(1) == 4);
101 Q_ASSERT(ktx::evalPaddedSize(2) == 4);
102 Q_ASSERT(ktx::evalPaddedSize(3) == 4);
103 Q_ASSERT(ktx::evalPaddedSize(4) == 4);
104 Q_ASSERT(ktx::evalPaddedSize(1024) == 1024);
105 Q_ASSERT(ktx::evalPaddedSize(1025) == 1028);
106 Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13));
107
108 DependencyManager::set<tracing::Tracer>();
109 qInstallMessageHandler(messageHandler);
110 QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
111
112 QImage image(TEST_IMAGE);
113 gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
114
115 auto ktxMemory = gpu::Texture::serialize(*testTexture);
116 {
117 const auto& ktxStorage = ktxMemory->getStorage();
118 Q_ASSERT_X(ktx::KTX::validate(ktxStorage), __FUNCTION__, "KTX storage validation failed");
119 Q_ASSERT_X(ktxMemory->isValid(), __FUNCTION__, "KTX self-validation failed");
120 QSaveFile outFile(TEST_IMAGE_KTX);
121 if (!outFile.open(QFile::WriteOnly)) {
122 throw std::runtime_error("Unable to open file");
123 }
124 auto ktxSize = ktxStorage->size();
125 outFile.resize(ktxSize);
126 auto dest = outFile.map(0, ktxSize);
127 memcpy(dest, ktxStorage->data(), ktxSize);
128 outFile.unmap(dest);
129 outFile.commit();
130 }
131
132 {
133 auto ktxFile = ktx::KTX::create(std::shared_ptr<storage::Storage>(new storage::FileStorage(TEST_IMAGE_KTX)));
134 {
135 const auto& memStorage = ktxMemory->getStorage();
136 const auto& fileStorage = ktxFile->getStorage();
137 Q_ASSERT(memStorage->size() == fileStorage->size());
138 Q_ASSERT(memStorage->data() != fileStorage->data());
139 Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size()));
140 Q_ASSERT(ktxFile->_images.size() == ktxMemory->_images.size());
141 auto imageCount = ktxFile->_images.size();
142 auto startMemory = ktxMemory->_storage->data();
143 auto startFile = ktxFile->_storage->data();
144 for (size_t i = 0; i < imageCount; ++i) {
145 auto memImages = ktxMemory->_images[i];
146 auto fileImages = ktxFile->_images[i];
147 Q_ASSERT(memImages._padding == fileImages._padding);
148 Q_ASSERT(memImages._numFaces == fileImages._numFaces);
149 Q_ASSERT(memImages._imageSize == fileImages._imageSize);
150 Q_ASSERT(memImages._faceSize == fileImages._faceSize);
151 Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces);
152 Q_ASSERT(fileImages._faceBytes.size() == fileImages._numFaces);
153 auto faceCount = fileImages._numFaces;
154 for (uint32_t face = 0; face < faceCount; ++face) {
155 auto memFace = memImages._faceBytes[face];
156 auto memOffset = memFace - startMemory;
157 auto fileFace = fileImages._faceBytes[face];
158 auto fileOffset = fileFace - startFile;
159 Q_ASSERT(memOffset % 4 == 0);
160 Q_ASSERT(memOffset == fileOffset);
161 }
162 }
163 }
164 }
165 testTexture->setKtxBacking(TEST_IMAGE_KTX.toStdString());
166 return 0;
167}
168
169#if 0
170static const QString TEST_FOLDER { "H:/ktx_cacheold" };
171//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" };
172
173//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };
174static const QString EXTENSIONS { "*.ktx" };
175
176int mainTemp(int, char**) {
177 qInstallMessageHandler(messageHandler);
178 auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
179 for (auto fileInfo : fileInfoList) {
180 qDebug() << fileInfo.filePath();
181 std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } };
182
183 if (!ktx::KTX::validate(storage)) {
184 qDebug() << "KTX invalid";
185 }
186
187 auto ktxFile = ktx::KTX::create(storage);
188 ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor();
189
190 qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs";
191 for (const auto& kv : ktxDescriptor.keyValues) {
192 qDebug() << "\t" << kv._key.c_str();
193 }
194
195 auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
196 if (offsetToMinMipKV) {
197 auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV;
198 auto minMipLevelAvailable = *data;
199 qDebug() << "\tMin mip available " << minMipLevelAvailable;
200 assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels);
201 }
202 auto storageSize = storage->size();
203 for (const auto& faceImageDesc : ktxDescriptor.images) {
204 //assert(0 == (faceImageDesc._faceSize % 4));
205 for (const auto& faceOffset : faceImageDesc._faceOffsets) {
206 assert(0 == (faceOffset % 4));
207 auto faceEndOffset = faceOffset + faceImageDesc._faceSize;
208 assert(faceEndOffset <= storageSize);
209 }
210 }
211
212 for (const auto& faceImage : ktxFile->_images) {
213 for (const ktx::Byte* faceBytes : faceImage._faceBytes) {
214 assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4));
215 }
216 }
217 }
218 return 0;
219}
220#endif
221
222#include "main.moc"
223
tests/networking/src/FileCacheTests.cpp+187−0
@@ -0,0 +1,187 @@
1//
2// ResoruceTests.cpp
3//
4// Copyright 2015 High Fidelity, Inc.
5//
6// Distributed under the Apache License, Version 2.0.
7// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8//
9
10
11#include "FileCacheTests.h"
12
13#include <FileCache.h>
14
15QTEST_GUILESS_MAIN(FileCacheTests)
16
17using namespace cache;
18
19// Limit the file size to 10 MB
20static const size_t MAX_UNUSED_SIZE { 1024 * 1024 * 10 };
21static const QByteArray TEST_DATA { 1024 * 1024, '0' };
22
23static std::string getFileKey(int i) {
24 return QString(QByteArray { 1, (char)i }.toHex()).toStdString();
25}
26
27class TestFile : public File {
28 using Parent = File;
29public:
30 TestFile(Metadata&& metadata, const std::string& filepath)
31 : Parent(std::move(metadata), filepath) {
32 }
33};
34
35class TestFileCache : public FileCache {
36 using Parent = FileCache;
37
38public:
39 TestFileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr) : Parent(dirname, ext, nullptr) {
40 initialize();
41 }
42
43 std::unique_ptr<File> createFile(Metadata&& metadata, const std::string& filepath) override {
44 qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str();
45 return std::unique_ptr<File>(new TestFile(std::move(metadata), filepath));
46 }
47};
48
49using CachePointer = std::shared_ptr<TestFileCache>;
50
51// The FileCache relies on deleteLater to clear unused files, but QTest classes don't run a conventional event loop
52// so we need to call this function to force any pending deletes to occur in the File destructor
53static void forceDeletes() {
54 while (QCoreApplication::hasPendingEvents()) {
55 QCoreApplication::sendPostedEvents();
56 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
57 QCoreApplication::processEvents();
58 }
59}
60
61size_t FileCacheTests::getCacheDirectorySize() const {
62 size_t result = 0;
63 QDir dir(_testDir.path());
64 for (const auto& file : dir.entryList({ "*.tmp" })) {
65 result += QFileInfo(dir.absoluteFilePath(file)).size();
66 }
67 return result;
68}
69
70CachePointer makeFileCache(QString& location) {
71 auto result = std::make_shared<TestFileCache>(location.toStdString(), "tmp");
72 result->setMaxSize(MAX_UNUSED_SIZE);
73 return result;
74}
75
76void FileCacheTests::initTestCase() {
77}
78
79void FileCacheTests::testUnusedFiles() {
80 auto cache = makeFileCache(_testDir.path());
81 std::list<FilePointer> inUseFiles;
82
83 {
84 for (int i = 0; i < 100; ++i) {
85 std::string key = getFileKey(i);
86 auto file = cache->writeFile(TEST_DATA.data(), TestFileCache::Metadata(key, TEST_DATA.size()));
87 inUseFiles.push_back(file);
88 forceDeletes();
89 QThread::msleep(10);
90 }
91 QCOMPARE(cache->getNumCachedFiles(), (size_t)0);
92 QCOMPARE(cache->getNumTotalFiles(), (size_t)100);
93 // Release the in-use files
94 inUseFiles.clear();
95 // Cache state is updated, but the directory state is unchanged,
96 // because the file deletes are triggered by an event loop
97 QCOMPARE(cache->getNumCachedFiles(), (size_t)10);
98 QCOMPARE(cache->getNumTotalFiles(), (size_t)10);
99 QVERIFY(getCacheDirectorySize() > MAX_UNUSED_SIZE);
100 forceDeletes();
101 QCOMPARE(cache->getNumCachedFiles(), (size_t)10);
102 QCOMPARE(cache->getNumTotalFiles(), (size_t)10);
103 QVERIFY(getCacheDirectorySize() <= MAX_UNUSED_SIZE);
104 }
105
106 // Reset the cache
107 cache = makeFileCache(_testDir.path());
108 {
109 // Test files 0 to 89 are missing, because the LRU algorithm deleted them when we released the files
110 for (int i = 0; i < 90; ++i) {
111 std::string key = getFileKey(i);
112 auto file = cache->getFile(key);
113 QVERIFY(!file.get());
114 }
115
116 // Test files 90 to 99 are present
117 for (int i = 90; i < 100; ++i) {
118 std::string key = getFileKey(i);
119 auto file = cache->getFile(key);
120 QVERIFY(file.get());
121 inUseFiles.push_back(file);
122
123 if (i == 94) {
124 // Each access touches the file, so we need to sleep here to ensure that the the last 5 files
125 // have later times for cache ejection priority, otherwise the test runs too fast to reliably
126 // differentiate
127 QThread::msleep(1000);
128 }
129 }
130
131 QCOMPARE(cache->getNumCachedFiles(), (size_t)0);
132 QCOMPARE(cache->getNumTotalFiles(), (size_t)10);
133 inUseFiles.clear();
134 QCOMPARE(cache->getNumCachedFiles(), (size_t)10);
135 QCOMPARE(cache->getNumTotalFiles(), (size_t)10);
136 }
137}
138
139size_t FileCacheTests::getFreeSpace() const {
140 return QStorageInfo(_testDir.path()).bytesFree();
141}
142
143// FIXME if something else is changing the amount of free space on the target drive concurrently with this test
144// running, then it may fail
145void FileCacheTests::testFreeSpacePreservation() {
146 QCOMPARE(getCacheDirectorySize(), MAX_UNUSED_SIZE);
147 // Set the target free space to slightly above whatever the current free space is...
148 size_t targetFreeSpace = getFreeSpace() + MAX_UNUSED_SIZE / 2;
149
150 // Reset the cache
151 auto cache = makeFileCache(_testDir.path());
152 // Setting the min free space causes it to eject the oldest files that cause the cache to exceed the minimum space
153 cache->setMinFreeSize(targetFreeSpace);
154 QVERIFY(getFreeSpace() < targetFreeSpace);
155 forceDeletes();
156 QCOMPARE(cache->getNumCachedFiles(), (size_t)5);
157 QCOMPARE(cache->getNumTotalFiles(), (size_t)5);
158 QVERIFY(getFreeSpace() >= targetFreeSpace);
159 for (int i = 0; i < 95; ++i) {
160 std::string key = getFileKey(i);
161 auto file = cache->getFile(key);
162 QVERIFY(!file.get());
163 }
164 for (int i = 95; i < 100; ++i) {
165 std::string key = getFileKey(i);
166 auto file = cache->getFile(key);
167 QVERIFY(file.get());
168 }
169}
170
171void FileCacheTests::testWipe() {
172 // Reset the cache
173 auto cache = makeFileCache(_testDir.path());
174 QCOMPARE(cache->getNumCachedFiles(), (size_t)5);
175 QCOMPARE(cache->getNumTotalFiles(), (size_t)5);
176 cache->wipe();
177 QCOMPARE(cache->getNumCachedFiles(), (size_t)0);
178 QCOMPARE(cache->getNumTotalFiles(), (size_t)0);
179 QVERIFY(getCacheDirectorySize() > 0);
180 forceDeletes();
181 QCOMPARE(getCacheDirectorySize(), (size_t)0);
182}
183
184
185void FileCacheTests::cleanupTestCase() {
186}
187
tests/networking/src/FileCacheTests.h+31−0
@@ -0,0 +1,31 @@
1//
2// ResourceTests.h
3//
4// Copyright 2015 High Fidelity, Inc.
5//
6// Distributed under the Apache License, Version 2.0.
7// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8//
9
10#ifndef hifi_ResourceTests_h
11#define hifi_ResourceTests_h
12
13#include <QtTest/QtTest>
14#include <QtCore/QTemporaryDir>
15
16class FileCacheTests : public QObject {
17 Q_OBJECT
18private slots:
19 void initTestCase();
20 void testUnusedFiles();
21 void testFreeSpacePreservation();
22 void cleanupTestCase();
23 void testWipe();
24
25private:
26 size_t getFreeSpace() const;
27 size_t getCacheDirectorySize() const;
28 QTemporaryDir _testDir;
29};
30
31#endif // hifi_ResourceTests_h
tests/qt59/CMakeLists.txt+15−0
@@ -0,0 +1,15 @@
1
2set(TARGET_NAME qt59)
3
4if (WIN32)
5 SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
6endif()
7
8# This is not a testcase -- just set it up as a regular hifi project
9setup_hifi_project(Gui)
10set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
11
12# link in the shared libraries
13link_hifi_libraries(shared networking)
14
15package_libraries_for_deployment()
tests/qt59/src/main.cpp+78−0
@@ -0,0 +1,78 @@
1//
2// Created by Bradley Austin Davis on 2017/06/06
3// Copyright 2013-2017 High Fidelity, Inc.
4//
5// Distributed under the Apache License, Version 2.0.
6// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7//
8
9#include <mutex>
10
11#include <QtCore/QCoreApplication>
12
13#include <NodeList.h>
14#include <AccountManager.h>
15#include <AddressManager.h>
16#include <MessagesClient.h>
17
18#include <BuildInfo.h>
19
20
21class Qt59TestApp : public QCoreApplication {
22 Q_OBJECT
23public:
24 Qt59TestApp(int argc, char* argv[]);
25 ~Qt59TestApp();
26
27private:
28 void finish(int exitCode);
29};
30
31
32
33Qt59TestApp::Qt59TestApp(int argc, char* argv[]) :
34 QCoreApplication(argc, argv)
35{
36
37 Setting::init();
38 DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
39 DependencyManager::set<AccountManager>([&] { return QString("Mozilla/5.0 (HighFidelityACClient)"); });
40 DependencyManager::set<AddressManager>();
41 DependencyManager::set<NodeList>(NodeType::Agent, 0);
42 auto nodeList = DependencyManager::get<NodeList>();
43 nodeList->startThread();
44 auto messagesClient = DependencyManager::set<MessagesClient>();
45 messagesClient->startThread();
46 QTimer::singleShot(1000, [this] { finish(0); });
47}
48
49Qt59TestApp::~Qt59TestApp() {
50}
51
52
53void Qt59TestApp::finish(int exitCode) {
54 auto nodeList = DependencyManager::get<NodeList>();
55
56 // send the domain a disconnect packet, force stoppage of domain-server check-ins
57 nodeList->getDomainHandler().disconnect();
58 nodeList->setIsShuttingDown(true);
59 nodeList->getPacketReceiver().setShouldDropPackets(true);
60
61 // remove the NodeList from the DependencyManager
62 DependencyManager::destroy<NodeList>();
63 QCoreApplication::exit(exitCode);
64}
65
66
67int main(int argc, char * argv[]) {
68 QCoreApplication::setApplicationName("Qt59Test");
69 QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
70 QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
71 QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
72
73 Qt59TestApp app(argc, argv);
74
75 return app.exec();
76}
77
78#include "main.moc"
tests/render-perf/CMakeLists.txt+1−1
@@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) · 9 unmodified lines
10set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")10set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
11 11
12# link in the shared libraries12# link in the shared libraries
13link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image)13link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural)
14 14
15package_libraries_for_deployment()15package_libraries_for_deployment()
16 16
tests/shaders/src/main.cpp+0−11
@@ -31,13 +31,9 @@ · 30 unmodified lines
31#include <render-utils/deferred_light_point_vert.h>31#include <render-utils/deferred_light_point_vert.h>
32#include <render-utils/deferred_light_spot_vert.h>32#include <render-utils/deferred_light_spot_vert.h>
33 33
34#include <render-utils/directional_light_frag.h>
35#include <render-utils/directional_ambient_light_frag.h>34#include <render-utils/directional_ambient_light_frag.h>
36#include <render-utils/directional_skybox_light_frag.h>35#include <render-utils/directional_skybox_light_frag.h>
37 36
38#include <render-utils/point_light_frag.h>
39#include <render-utils/spot_light_frag.h>
40
41#include <render-utils/standardTransformPNTC_vert.h>37#include <render-utils/standardTransformPNTC_vert.h>
42#include <render-utils/standardDrawTexture_frag.h>38#include <render-utils/standardDrawTexture_frag.h>
43 39
@@ -66,9 +62,6 @@ · 22 unmodified lines
66#include <entities-renderer/textured_particle_frag.h>62#include <entities-renderer/textured_particle_frag.h>
67#include <entities-renderer/textured_particle_vert.h>63#include <entities-renderer/textured_particle_vert.h>
68 64
69#include <render-utils/hit_effect_vert.h>
70#include <render-utils/hit_effect_frag.h>
71
72#include <render-utils/overlay3D_vert.h>65#include <render-utils/overlay3D_vert.h>
73#include <render-utils/overlay3D_frag.h>66#include <render-utils/overlay3D_frag.h>
74 67
@@ -155,11 +148,8 @@ void QTestWindow::draw() { · 80 unmodified lines
155 testShaderBuild(simple_vert, simple_frag);148 testShaderBuild(simple_vert, simple_frag);
156 testShaderBuild(simple_vert, simple_textured_frag);149 testShaderBuild(simple_vert, simple_textured_frag);
157 testShaderBuild(simple_vert, simple_textured_unlit_frag);150 testShaderBuild(simple_vert, simple_textured_unlit_frag);
158 testShaderBuild(deferred_light_vert, directional_light_frag);
159 testShaderBuild(deferred_light_vert, directional_ambient_light_frag);151 testShaderBuild(deferred_light_vert, directional_ambient_light_frag);
160 testShaderBuild(deferred_light_vert, directional_skybox_light_frag);152 testShaderBuild(deferred_light_vert, directional_skybox_light_frag);
161 testShaderBuild(deferred_light_point_vert, point_light_frag);
162 testShaderBuild(deferred_light_spot_vert, spot_light_frag);
163 testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag);153 testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag);
164 testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag);154 testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag);
165 155
@@ -190,7 +180,6 @@ void QTestWindow::draw() { · 24 unmodified lines
190 testShaderBuild(ambient_occlusion_vert, ambient_occlusion_frag);180 testShaderBuild(ambient_occlusion_vert, ambient_occlusion_frag);
191 testShaderBuild(ambient_occlusion_vert, occlusion_blend_frag);181 testShaderBuild(ambient_occlusion_vert, occlusion_blend_frag);
192*/182*/
193 testShaderBuild(hit_effect_vert, hit_effect_frag);
194 183
195 testShaderBuild(overlay3D_vert, overlay3D_frag);184 testShaderBuild(overlay3D_vert, overlay3D_frag);
196 185
tests/shared/src/CubicHermiteSplineTests.cpp+77−0
@@ -0,0 +1,77 @@
1//
2// CubicHermiteSplineTests.cpp
3// tests/shared/src
4//
5// Copyright 2017 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9//
10
11#include "CubicHermiteSplineTests.h"
12#include "../QTestExtensions.h"
13#include <QtCore/QDebug>
14#include "CubicHermiteSpline.h"
15
16QTEST_MAIN(CubicHermiteSplineTests)
17
18void CubicHermiteSplineTests::testCubicHermiteSplineFunctor() {
19 glm::vec3 p0(0.0f, 0.0f, 0.0f);
20 glm::vec3 m0(1.0f, 0.0f, 0.0f);
21 glm::vec3 p1(1.0f, 1.0f, 0.0f);
22 glm::vec3 m1(2.0f, 0.0f, 0.0f);
23 CubicHermiteSplineFunctor hermiteSpline(p0, m0, p1, m1);
24
25 const float EPSILON = 0.0001f;
26
27 QCOMPARE_WITH_ABS_ERROR(p0, hermiteSpline(0.0f), EPSILON);
28 QCOMPARE_WITH_ABS_ERROR(p1, hermiteSpline(1.0f), EPSILON);
29
30 // these values were computed offline.
31 const glm::vec3 oneFourth(0.203125f, 0.15625f, 0.0f);
32 const glm::vec3 oneHalf(0.375f, 0.5f, 0.0f);
33 const glm::vec3 threeFourths(0.609375f, 0.84375f, 0.0f);
34
35 QCOMPARE_WITH_ABS_ERROR(oneFourth, hermiteSpline(0.25f), EPSILON);
36 QCOMPARE_WITH_ABS_ERROR(oneHalf, hermiteSpline(0.5f), EPSILON);
37 QCOMPARE_WITH_ABS_ERROR(threeFourths, hermiteSpline(0.75f), EPSILON);
38}
39
40void CubicHermiteSplineTests::testCubicHermiteSplineFunctorWithArcLength() {
41 glm::vec3 p0(0.0f, 0.0f, 0.0f);
42 glm::vec3 m0(1.0f, 0.0f, 0.0f);
43 glm::vec3 p1(1.0f, 1.0f, 0.0f);
44 glm::vec3 m1(2.0f, 0.0f, 0.0f);
45 CubicHermiteSplineFunctorWithArcLength hermiteSpline(p0, m0, p1, m1);
46
47 const float EPSILON = 0.001f;
48
49 float arcLengths[5] = {
50 hermiteSpline.arcLength(0.0f),
51 hermiteSpline.arcLength(0.25f),
52 hermiteSpline.arcLength(0.5f),
53 hermiteSpline.arcLength(0.75f),
54 hermiteSpline.arcLength(1.0f)
55 };
56
57 // these values were computed offline
58 float referenceArcLengths[5] = {
59 0.0f,
60 0.268317f,
61 0.652788f,
62 1.07096f,
63 1.50267f
64 };
65
66 QCOMPARE_WITH_ABS_ERROR(arcLengths[0], referenceArcLengths[0], EPSILON);
67 QCOMPARE_WITH_ABS_ERROR(arcLengths[1], referenceArcLengths[1], EPSILON);
68 QCOMPARE_WITH_ABS_ERROR(arcLengths[2], referenceArcLengths[2], EPSILON);
69 QCOMPARE_WITH_ABS_ERROR(arcLengths[3], referenceArcLengths[3], EPSILON);
70 QCOMPARE_WITH_ABS_ERROR(arcLengths[4], referenceArcLengths[4], EPSILON);
71
72 QCOMPARE_WITH_ABS_ERROR(0.0f, hermiteSpline.arcLengthInverse(referenceArcLengths[0]), EPSILON);
73 QCOMPARE_WITH_ABS_ERROR(0.25f, hermiteSpline.arcLengthInverse(referenceArcLengths[1]), EPSILON);
74 QCOMPARE_WITH_ABS_ERROR(0.5f, hermiteSpline.arcLengthInverse(referenceArcLengths[2]), EPSILON);
75 QCOMPARE_WITH_ABS_ERROR(0.75f, hermiteSpline.arcLengthInverse(referenceArcLengths[3]), EPSILON);
76 QCOMPARE_WITH_ABS_ERROR(1.0f, hermiteSpline.arcLengthInverse(referenceArcLengths[4]), EPSILON);
77}
tests/shared/src/CubicHermiteSplineTests.h+23−0
@@ -0,0 +1,23 @@
1//
2// CubicHermiteSplineTests.h
3// tests/shared/src
4//
5// Copyright 2017 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9//
10
11#ifndef hifi_CubicHermiteSplineTests_h
12#define hifi_CubicHermiteSplineTests_h
13
14#include <QtTest/QtTest>
15
16class CubicHermiteSplineTests : public QObject {
17 Q_OBJECT
18private slots:
19 void testCubicHermiteSplineFunctor();
20 void testCubicHermiteSplineFunctorWithArcLength();
21};
22
23#endif // hifi_TransformTests_h
tests/shared/src/GLMHelpersTests.cpp+74−0
@@ -140,3 +140,77 @@ void GLMHelpersTests::testSimd() { · 139 unmodified lines
140 }140 }
141 qDebug() << "Done ";141 qDebug() << "Done ";
142}142}
143
144void GLMHelpersTests::testGenerateBasisVectors() {
145 { // very simple case: primary along X, secondary is linear combination of X and Y
146 glm::vec3 u(1.0f, 0.0f, 0.0f);
147 glm::vec3 v(1.0f, 1.0f, 0.0f);
148 glm::vec3 w;
149
150 generateBasisVectors(u, v, u, v, w);
151
152 QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_X, EPSILON);
153 QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_Y, EPSILON);
154 QCOMPARE_WITH_ABS_ERROR(w, Vectors::UNIT_Z, EPSILON);
155 }
156
157 { // point primary along Y instead of X
158 glm::vec3 u(0.0f, 1.0f, 0.0f);
159 glm::vec3 v(1.0f, 1.0f, 0.0f);
160 glm::vec3 w;
161
162 generateBasisVectors(u, v, u, v, w);
163
164 QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_Y, EPSILON);
165 QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_X, EPSILON);
166 QCOMPARE_WITH_ABS_ERROR(w, -Vectors::UNIT_Z, EPSILON);
167 }
168
169 { // pass bad data (both vectors along Y). The helper will guess X for secondary.
170 glm::vec3 u(0.0f, 1.0f, 0.0f);
171 glm::vec3 v(0.0f, 1.0f, 0.0f);
172 glm::vec3 w;
173
174 generateBasisVectors(u, v, u, v, w);
175
176 QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_Y, EPSILON);
177 QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_X, EPSILON);
178 QCOMPARE_WITH_ABS_ERROR(w, -Vectors::UNIT_Z, EPSILON);
179 }
180
181 { // pass bad data (both vectors along X). The helper will guess X for secondary, fail, then guess Y.
182 glm::vec3 u(1.0f, 0.0f, 0.0f);
183 glm::vec3 v(1.0f, 0.0f, 0.0f);
184 glm::vec3 w;
185
186 generateBasisVectors(u, v, u, v, w);
187
188 QCOMPARE_WITH_ABS_ERROR(u, Vectors::UNIT_X, EPSILON);
189 QCOMPARE_WITH_ABS_ERROR(v, Vectors::UNIT_Y, EPSILON);
190 QCOMPARE_WITH_ABS_ERROR(w, Vectors::UNIT_Z, EPSILON);
191 }
192
193 { // general case for arbitrary rotation
194 float angle = 1.234f;
195 glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
196 glm::quat rotation = glm::angleAxis(angle, axis);
197
198 // expected values
199 glm::vec3 x = rotation * Vectors::UNIT_X;
200 glm::vec3 y = rotation * Vectors::UNIT_Y;
201 glm::vec3 z = rotation * Vectors::UNIT_Z;
202
203 // primary is along x
204 // secondary is linear combination of x and y
205 // tertiary is unknown
206 glm::vec3 u = 1.23f * x;
207 glm::vec3 v = 2.34f * x + 3.45f * y;
208 glm::vec3 w;
209
210 generateBasisVectors(u, v, u, v, w);
211
212 QCOMPARE_WITH_ABS_ERROR(u, x, EPSILON);
213 QCOMPARE_WITH_ABS_ERROR(v, y, EPSILON);
214 QCOMPARE_WITH_ABS_ERROR(w, z, EPSILON);
215 }
216}
tests/shared/src/GLMHelpersTests.h+1−0
@@ -21,6 +21,7 @@ private slots: · 20 unmodified lines
21 void testEulerDecomposition();21 void testEulerDecomposition();
22 void testSixByteOrientationCompression();22 void testSixByteOrientationCompression();
23 void testSimd();23 void testSimd();
24 void testGenerateBasisVectors();
24};25};
25 26
26float getErrorDifference(const float& a, const float& b);27float getErrorDifference(const float& a, const float& b);
tests/shared/src/StorageTests.cpp+9−7
@@ -8,6 +8,8 @@ · 7 unmodified lines
8 8
9#include "StorageTests.h"9#include "StorageTests.h"
10 10
11#include <memory>
12
11QTEST_MAIN(StorageTests)13QTEST_MAIN(StorageTests)
12 14
13using namespace storage;15using namespace storage;
@@ -32,8 +34,8 @@ void StorageTests::testConversion() { · 18 unmodified lines
32 QFileInfo fileInfo(_testFile);34 QFileInfo fileInfo(_testFile);
33 QCOMPARE(fileInfo.exists(), false);35 QCOMPARE(fileInfo.exists(), false);
34 }36 }
35 StoragePointer storagePointer = std::make_unique<MemoryStorage>(_testData.size(), _testData.data());37 StoragePointer storagePointer = std::unique_ptr<MemoryStorage>(new MemoryStorage(_testData.size(), _testData.data()));
36 QCOMPARE(storagePointer->size(), (quint64)_testData.size());38 QCOMPARE(storagePointer->size(), _testData.size());
37 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);39 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
38 // Convert to a file40 // Convert to a file
39 storagePointer = storagePointer->toFileStorage(_testFile);41 storagePointer = storagePointer->toFileStorage(_testFile);
@@ -42,12 +44,12 @@ void StorageTests::testConversion() { · 2 unmodified lines
42 QCOMPARE(fileInfo.exists(), true);44 QCOMPARE(fileInfo.exists(), true);
43 QCOMPARE(fileInfo.size(), (qint64)_testData.size());45 QCOMPARE(fileInfo.size(), (qint64)_testData.size());
44 }46 }
45 QCOMPARE(storagePointer->size(), (quint64)_testData.size());47 QCOMPARE(storagePointer->size(), _testData.size());
46 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);48 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
47 49
48 // Convert to memory50 // Convert to memory
49 storagePointer = storagePointer->toMemoryStorage();51 storagePointer = storagePointer->toMemoryStorage();
50 QCOMPARE(storagePointer->size(), (quint64)_testData.size());52 QCOMPARE(storagePointer->size(), _testData.size());
51 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);53 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
52 {54 {
53 // ensure the file is unaffected55 // ensure the file is unaffected
@@ -58,13 +60,13 @@ void StorageTests::testConversion() { · 4 unmodified lines
58 60
59 // truncate the data as a new memory object61 // truncate the data as a new memory object
60 auto newSize = _testData.size() / 2;62 auto newSize = _testData.size() / 2;
61 storagePointer = std::make_unique<MemoryStorage>(newSize, storagePointer->data());63 storagePointer = std::unique_ptr<Storage>(new MemoryStorage(newSize, storagePointer->data()));
62 QCOMPARE(storagePointer->size(), (quint64)newSize);64 QCOMPARE(storagePointer->size(), newSize);
63 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);65 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);
64 66
65 // Convert back to file67 // Convert back to file
66 storagePointer = storagePointer->toFileStorage(_testFile);68 storagePointer = storagePointer->toFileStorage(_testFile);
67 QCOMPARE(storagePointer->size(), (quint64)newSize);69 QCOMPARE(storagePointer->size(), newSize);
68 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);70 QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);
69 {71 {
70 // ensure the file is truncated72 // ensure the file is truncated
tools/ac-client/src/ACClientApp.cpp+1−14
@@ -90,23 +90,14 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : · 89 unmodified lines
90 90
91 91
92 auto nodeList = DependencyManager::get<NodeList>();92 auto nodeList = DependencyManager::get<NodeList>();
93
94 // start the nodeThread so its event loop is running93 // start the nodeThread so its event loop is running
95 QThread* nodeThread = new QThread(this);94 nodeList->startThread();
96 nodeThread->setObjectName("NodeList Thread");
97 nodeThread->start();
98
99 // make sure the node thread is given highest priority
100 nodeThread->setPriority(QThread::TimeCriticalPriority);
101 95
102 // setup a timer for domain-server check ins96 // setup a timer for domain-server check ins
103 QTimer* domainCheckInTimer = new QTimer(nodeList.data());97 QTimer* domainCheckInTimer = new QTimer(nodeList.data());
104 connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);98 connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
105 domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);99 domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
106 100
107 // put the NodeList and datagram processing on the node thread
108 nodeList->moveToThread(nodeThread);
109
110 const DomainHandler& domainHandler = nodeList->getDomainHandler();101 const DomainHandler& domainHandler = nodeList->getDomainHandler();
111 102
112 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));103 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
@@ -239,12 +230,8 @@ void ACClientApp::finish(int exitCode) { · 126 unmodified lines
239 // tell the packet receiver we're shutting down, so it can drop packets230 // tell the packet receiver we're shutting down, so it can drop packets
240 nodeList->getPacketReceiver().setShouldDropPackets(true);231 nodeList->getPacketReceiver().setShouldDropPackets(true);
241 232
242 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
243 // remove the NodeList from the DependencyManager233 // remove the NodeList from the DependencyManager
244 DependencyManager::destroy<NodeList>();234 DependencyManager::destroy<NodeList>();
245 // ask the node thread to quit and wait until it is done
246 nodeThread->quit();
247 nodeThread->wait();
248 235
249 printFailedServers();236 printFailedServers();
250 QCoreApplication::exit(exitCode);237 QCoreApplication::exit(exitCode);
tools/atp-get/src/ATPGetApp.cpp+1−15
@@ -111,23 +111,13 @@ ATPGetApp::ATPGetApp(int argc, char* argv[]) : · 110 unmodified lines
111 111
112 112
113 auto nodeList = DependencyManager::get<NodeList>();113 auto nodeList = DependencyManager::get<NodeList>();
114 114 nodeList->startThread();
115 // start the nodeThread so its event loop is running
116 QThread* nodeThread = new QThread(this);
117 nodeThread->setObjectName("NodeList Thread");
118 nodeThread->start();
119
120 // make sure the node thread is given highest priority
121 nodeThread->setPriority(QThread::TimeCriticalPriority);
122 115
123 // setup a timer for domain-server check ins116 // setup a timer for domain-server check ins
124 QTimer* domainCheckInTimer = new QTimer(nodeList.data());117 QTimer* domainCheckInTimer = new QTimer(nodeList.data());
125 connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);118 connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
126 domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);119 domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
127 120
128 // put the NodeList and datagram processing on the node thread
129 nodeList->moveToThread(nodeThread);
130
131 const DomainHandler& domainHandler = nodeList->getDomainHandler();121 const DomainHandler& domainHandler = nodeList->getDomainHandler();
132 122
133 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));123 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
@@ -258,12 +248,8 @@ void ATPGetApp::finish(int exitCode) { · 124 unmodified lines
258 // tell the packet receiver we're shutting down, so it can drop packets248 // tell the packet receiver we're shutting down, so it can drop packets
259 nodeList->getPacketReceiver().setShouldDropPackets(true);249 nodeList->getPacketReceiver().setShouldDropPackets(true);
260 250
261 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
262 // remove the NodeList from the DependencyManager251 // remove the NodeList from the DependencyManager
263 DependencyManager::destroy<NodeList>();252 DependencyManager::destroy<NodeList>();
264 // ask the node thread to quit and wait until it is done
265 nodeThread->quit();
266 nodeThread->wait();
267 253
268 QCoreApplication::exit(exitCode);254 QCoreApplication::exit(exitCode);
269}255}
tools/oven/src/BakerCLI.cpp+64−0
@@ -0,0 +1,64 @@
1//
2// BakerCLI.cpp
3// tools/oven/src
4//
5// Created by Robbie Uvanni on 6/6/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#include <QObject>
13#include <QImageReader>
14#include <QtCore/QDebug>
15
16#include "ModelBakingLoggingCategory.h"
17#include "Oven.h"
18#include "BakerCLI.h"
19#include "FBXBaker.h"
20#include "TextureBaker.h"
21
22BakerCLI::BakerCLI(Oven* parent) : QObject(parent) {
23}
24
25void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) {
26
27 // if the URL doesn't have a scheme, assume it is a local file
28 if (inputUrl.scheme() != "http" && inputUrl.scheme() != "https" && inputUrl.scheme() != "ftp") {
29 inputUrl.setScheme("file");
30 }
31
32 static const QString MODEL_EXTENSION { ".fbx" };
33
34 // check what kind of baker we should be creating
35 bool isFBX = inputUrl.toDisplayString().endsWith(MODEL_EXTENSION, Qt::CaseInsensitive);
36 bool isSupportedImage = false;
37
38 for (QByteArray format : QImageReader::supportedImageFormats()) {
39 isSupportedImage |= inputUrl.toDisplayString().endsWith(format, Qt::CaseInsensitive);
40 }
41
42 // create our appropiate baker
43 if (isFBX) {
44 _baker = std::unique_ptr<Baker> { new FBXBaker(inputUrl, outputPath, []() -> QThread* { return qApp->getNextWorkerThread(); }) };
45 _baker->moveToThread(qApp->getFBXBakerThread());
46 } else if (isSupportedImage) {
47 _baker = std::unique_ptr<Baker> { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) };
48 _baker->moveToThread(qApp->getNextWorkerThread());
49 } else {
50 qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl;
51 QApplication::exit(1);
52 }
53
54 // invoke the bake method on the baker thread
55 QMetaObject::invokeMethod(_baker.get(), "bake");
56
57 // make sure we hear about the results of this baker when it is done
58 connect(_baker.get(), &Baker::finished, this, &BakerCLI::handleFinishedBaker);
59}
60
61void BakerCLI::handleFinishedBaker() {
62 qCDebug(model_baking) << "Finished baking file.";
63 QApplication::exit(_baker.get()->hasErrors());
64}
tools/oven/src/BakerCLI.h+34−0
@@ -0,0 +1,34 @@
1//
2// BakerCLI.h
3// tools/oven/src
4//
5// Created by Robbie Uvanni on 6/6/17.
6// Copyright 2017 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#ifndef hifi_BakerCLI_h
13#define hifi_BakerCLI_h
14
15#include <QtCore/QObject>
16
17#include "Baker.h"
18#include "Oven.h"
19
20class BakerCLI : public QObject {
21 Q_OBJECT
22
23public:
24 BakerCLI(Oven* parent);
25 void bakeFile(QUrl inputUrl, const QString outputPath);
26
27private slots:
28 void handleFinishedBaker();
29
30private:
31 std::unique_ptr<Baker> _baker;
32};
33
34#endif // hifi_BakerCLI_h
tools/oven/src/Oven.cpp+30−7
@@ -11,16 +11,20 @@ · 10 unmodified lines
11 11
12#include <QtCore/QDebug>12#include <QtCore/QDebug>
13#include <QtCore/QThread>13#include <QtCore/QThread>
14#include <QtCore/QCommandLineParser>
14 15
15#include <image/Image.h>16#include <image/Image.h>
16#include <SettingInterface.h>17#include <SettingInterface.h>
17 18
18#include "ui/OvenMainWindow.h"19#include "ui/OvenMainWindow.h"
19
20#include "Oven.h"20#include "Oven.h"
21#include "BakerCli.h"
21 22
22static const QString OUTPUT_FOLDER = "/Users/birarda/code/hifi/lod/test-oven/export";23static const QString OUTPUT_FOLDER = "/Users/birarda/code/hifi/lod/test-oven/export";
23 24
25static const QString CLI_INPUT_PARAMETER = "i";
26static const QString CLI_OUTPUT_PARAMETER = "o";
27
24Oven::Oven(int argc, char* argv[]) :28Oven::Oven(int argc, char* argv[]) :
25 QApplication(argc, argv)29 QApplication(argc, argv)
26{30{
@@ -30,24 +34,43 @@ Oven::Oven(int argc, char* argv[]) : · 3 unmodified lines
30 // init the settings interface so we can save and load settings34 // init the settings interface so we can save and load settings
31 Setting::init();35 Setting::init();
32 36
37 // parse the command line parameters
38 QCommandLineParser parser;
39
40 parser.addOptions({
41 { CLI_INPUT_PARAMETER, "Path to file that you would like to bake.", "input" },
42 { CLI_OUTPUT_PARAMETER, "Path to folder that will be used as output.", "output" }
43 });
44 parser.addHelpOption();
45 parser.process(*this);
46
33 // enable compression in image library, except for cube maps47 // enable compression in image library, except for cube maps
34 image::setColorTexturesCompressionEnabled(true);48 image::setColorTexturesCompressionEnabled(true);
35 image::setGrayscaleTexturesCompressionEnabled(true);49 image::setGrayscaleTexturesCompressionEnabled(true);
36 image::setNormalTexturesCompressionEnabled(true);50 image::setNormalTexturesCompressionEnabled(true);
37 image::setCubeTexturesCompressionEnabled(true);51 image::setCubeTexturesCompressionEnabled(true);
38 52
39 // check if we were passed any command line arguments that would tell us just to run without the GUI
40
41 // setup the GUI
42 _mainWindow = new OvenMainWindow;
43 _mainWindow->show();
44
45 // setup our worker threads53 // setup our worker threads
46 setupWorkerThreads(QThread::idealThreadCount() - 1);54 setupWorkerThreads(QThread::idealThreadCount() - 1);
47 55
48 // Autodesk's SDK means that we need a single thread for all FBX importing/exporting in the same process56 // Autodesk's SDK means that we need a single thread for all FBX importing/exporting in the same process
49 // setup the FBX Baker thread57 // setup the FBX Baker thread
50 setupFBXBakerThread();58 setupFBXBakerThread();
59
60 // check if we were passed any command line arguments that would tell us just to run without the GUI
61 if (parser.isSet(CLI_INPUT_PARAMETER) || parser.isSet(CLI_OUTPUT_PARAMETER)) {
62 if (parser.isSet(CLI_INPUT_PARAMETER) && parser.isSet(CLI_OUTPUT_PARAMETER)) {
63 BakerCLI* cli = new BakerCLI(this);
64 cli->bakeFile(parser.value(CLI_INPUT_PARAMETER), parser.value(CLI_OUTPUT_PARAMETER));
65 } else {
66 parser.showHelp();
67 QApplication::quit();
68 }
69 } else {
70 // setup the GUI
71 _mainWindow = new OvenMainWindow;
72 _mainWindow->show();
73 }
51}74}
52 75
53Oven::~Oven() {76Oven::~Oven() {
tools/oven/src/Oven.h+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
14 14
15#include <QtWidgets/QApplication>15#include <QtWidgets/QApplication>
16 16
17#include <tbb/concurrent_vector.h>17#include <TBBHelpers.h>
18 18
19#include <atomic>19#include <atomic>
20 20
unpublishedScripts/marketplace/record/assets/sounds/countdown-tick.wavrenamed from scripts/system/assets/sounds/countdown-tick.wav+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/assets/sounds/finish-recording.wavrenamed from scripts/system/assets/sounds/finish-recording.wav+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/assets/sounds/start-recording.wavrenamed from scripts/system/assets/sounds/start-recording.wav+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/html/css/edit-style.css+1262−0
@@ -0,0 +1,1262 @@
1/*
2// edit-style.css
3//
4// Created by Ryan Huffman on 13 Nov 2014
5// Copyright 2014 High Fidelity, Inc.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9*/
10
11@font-face {
12 font-family: Raleway-Regular;
13 src: url(../../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */
14 url(../../../../fonts/Raleway-Regular.ttf), /* OSX production */
15 url(../../../../interface/resources/fonts/Raleway-Regular.ttf), /* Development, running script in /HiFi/examples */
16 url(../fonts/Raleway-Regular.ttf); /* Marketplace script */
17}
18
19@font-face {
20 font-family: Raleway-Light;
21 src: url(../../../../resources/fonts/Raleway-Light.ttf),
22 url(../../../../fonts/Raleway-Light.ttf),
23 url(../../../../interface/resources/fonts/Raleway-Light.ttf),
24 url(../fonts/Raleway-Light.ttf);
25}
26
27@font-face {
28 font-family: Raleway-Bold;
29 src: url(../../../../resources/fonts/Raleway-Bold.ttf),
30 url(../../../../fonts/Raleway-Bold.ttf),
31 url(../../../../interface/resources/fonts/Raleway-Bold.ttf),
32 url(../fonts/Raleway-Bold.ttf);
33}
34
35@font-face {
36 font-family: Raleway-SemiBold;
37 src: url(../../../../resources/fonts/Raleway-SemiBold.ttf),
38 url(../../../../fonts/Raleway-SemiBold.ttf),
39 url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf),
40 url(../fonts/Raleway-SemiBold.ttf);
41}
42
43@font-face {
44 font-family: FiraSans-SemiBold;
45 src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf),
46 url(../../../../fonts/FiraSans-SemiBold.ttf),
47 url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf),
48 url(../fonts/FiraSans-SemiBold.ttf);
49}
50
51@font-face {
52 font-family: AnonymousPro-Regular;
53 src: url(../../../../resources/fonts/AnonymousPro-Regular.ttf),
54 url(../../../../fonts/AnonymousPro-Regular.ttf),
55 url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf),
56 url(../fonts/AnonymousPro-Regular.ttf);
57}
58
59@font-face {
60 font-family: HiFi-Glyphs;
61 src: url(../../../../resources/fonts/hifi-glyphs.ttf),
62 url(../../../../fonts/hifi-glyphs.ttf),
63 url(../../../../interface/resources/fonts/hifi-glyphs.ttf),
64 url(../fonts/hifi-glyphs.ttf);
65}
66
67* {
68 margin: 0;
69 padding: 0;
70}
71
72body {
73 padding: 21px 21px 21px 21px;
74
75 color: #afafaf;
76 background-color: #404040;
77 font-family: Raleway-Regular;
78 font-size: 15px;
79
80 -webkit-touch-callout: none;
81 -webkit-user-select: none;
82 -khtml-user-select: none;
83 -moz-user-select: none;
84 -ms-user-select: none;
85 user-select: none;
86
87 overflow-x: hidden;
88 overflow-y: auto;
89}
90
91table {
92 font-family: FiraSans-SemiBold;
93 font-size: 15px;
94 color: #afafaf;
95 border-collapse: collapse;
96 width: 100%;
97 border: 2px solid #575757;
98 border-radius: 7px;
99}
100
101thead {
102 font-family: Raleway-Regular;
103 font-size: 12px;
104 text-transform: uppercase;
105 background-color: #1c1c1c;
106 padding: 1px 0px;
107 border-bottom: 1px solid #575757;
108 width: 100%;
109}
110
111tbody {
112 width: 100%;
113}
114
115tfoot {
116 font-family: Raleway-Light;
117 font-size: 13px;
118 background-color: #1c1c1c;
119 border-top: 1px solid #575757;
120 width: 100%;
121}
122
123tfoot tr {
124 background-color: #1c1cff;
125}
126
127thead tr {
128 height: 26px; /* 28px with thead padding */
129}
130
131thead th {
132 height: 26px;
133 background-color: #1c1c1c;
134 border-right: 1px solid #575757;
135}
136
137thead th:last-child {
138 border: none;
139}
140
141tbody td {
142 height: 26px;
143}
144
145tfoot td {
146 height: 18px;
147 width: 100%;
148 background-color: #1c1c1c;
149 margin-left: 12px;
150}
151
152tr {
153 width: 100%;
154 cursor: pointer;
155}
156
157tr:nth-child(odd) {
158 background-color: #2e2e2e;
159}
160
161tr:nth-child(even) {
162 background-color: #1c1c1c;
163}
164
165tr:focus {
166 outline: none;
167}
168
169tr.selected {
170 color: #000000;
171 background-color: #00b4ef;
172}
173
174tr.selected + tr.selected {
175 border-top: 1px solid #2e2e2e;
176}
177
178th {
179 text-align: center;
180 word-wrap: nowrap;
181 white-space: nowrap;
182 padding-left: 12px;
183 padding-right: 12px;
184}
185
186td {
187 overflow: hidden;
188 text-overflow: ellipsis;
189 white-space: nowrap;
190 word-wrap: nowrap;
191 padding-left: 12px;
192 padding-right: 12px;
193}
194
195td.url {
196 white-space: nowrap;
197 overflow: hidden;
198}
199
200
201input[type="text"], input[type="number"], textarea {
202 margin: 0;
203 padding: 0 12px;
204 color: #afafaf;
205 background-color: #252525;
206 border: none;
207 font-family: FiraSans-SemiBold;
208 font-size: 15px;
209}
210
211textarea {
212 font-family: AnonymousPro-Regular;
213 font-size: 16px;
214 padding-top: 5px;
215 padding-bottom: 5px;
216 min-height: 64px;
217 width: 100%;
218 resize: vertical;
219}
220
221input::-webkit-input-placeholder {
222 font-style: italic;
223}
224
225input:focus, textarea:focus {
226 color: #fff;
227 background-color: #000;
228 outline: 1px solid #00b4ef;
229 outline-offset: -1px;
230}
231
232input::selection, textarea::selection {
233 color: #000000;
234 background-color: #00b4ef;
235}
236
237input.search {
238 border-radius: 14px;
239}
240
241input.search:focus {
242 outline: none;
243 box-sizing: border-box;
244 height: 26px;
245 margin-top: 1px;
246 margin-bottom: 1px;
247 box-shadow: 0 0 0px 1px #00b4ef;
248}
249
250input:disabled, textarea:disabled {
251 background-color: #383838;
252 color: #afafaf;
253}
254
255input[type="text"] {
256 height: 28px;
257 width: 100%;
258}
259
260input[type="number"] {
261 position: relative;
262 height: 28px;
263 width: 124px;
264}
265
266input[type=number] {
267 padding-right: 3px;
268}
269input[type=number]::-webkit-inner-spin-button {
270 -webkit-appearance: none;
271 opacity: 1.0;
272 display: block;
273 position: relative;
274 width: 10px;
275 height: 100%;
276 overflow: hidden;
277 font-family: hifi-glyphs;
278 font-size: 46px;
279 color: #afafaf;
280 cursor: pointer;
281 /*background-color: #000000;*/
282}
283input[type=number]::-webkit-inner-spin-button:before,
284input[type=number]::-webkit-inner-spin-button:after {
285 position:absolute;
286 left: -19px;
287 line-height: 8px;
288 text-align: center;
289}
290input[type=number]::-webkit-inner-spin-button:before {
291 content: "6";
292 top: 4px;
293}
294input[type=number]::-webkit-inner-spin-button:after {
295 content: "5";
296 bottom: 4px;
297}
298
299input[type=number].hover-up::-webkit-inner-spin-button:before,
300input[type=number].hover-down::-webkit-inner-spin-button:after {
301 color: #ffffff;
302}
303
304input.no-spin::-webkit-outer-spin-button,
305input.no-spin::-webkit-inner-spin-button {
306 display: none;
307 -webkit-appearance: none;
308 margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
309 padding-right: 12px;
310}
311
312input[type=button] {
313 font-family: Raleway-Bold;
314 font-size: 13px;
315 text-transform: uppercase;
316 vertical-align: top;
317 height: 28px;
318 min-width: 120px;
319 padding: 0px 18px;
320 margin-right: 6px;
321 border-radius: 5px;
322 border: none;
323 color: #fff;
324 background-color: #000;
325 background: linear-gradient(#343434 20%, #000 100%);
326 cursor: pointer;
327}
328
329input[type=button].glyph {
330 font-family: HiFi-Glyphs;
331 font-size: 20px;
332 text-transform: none;
333 min-width: 32px;
334 padding: 0;
335}
336
337input[type=button].red {
338 color: #fff;
339 background-color: #94132e;
340 background: linear-gradient(#d42043 20%, #94132e 100%);
341}
342input[type=button].blue {
343 color: #fff;
344 background-color: #1080b8;
345 background: linear-gradient(#00b4ef 20%, #1080b8 100%);
346}
347input[type=button].white {
348 color: #121212;
349 background-color: #afafaf;
350 background: linear-gradient(#fff 20%, #afafaf 100%);
351}
352
353input[type=button]:enabled:hover {
354 background: linear-gradient(#000, #000);
355 border: none;
356}
357input[type=button].red:enabled:hover {
358 background: linear-gradient(#d42043, #d42043);
359 border: none;
360}
361input[type=button].blue:enabled:hover {
362 background: linear-gradient(#00b4ef, #00b4ef);
363 border: none;
364}
365input[type=button].white:enabled:hover {
366 background: linear-gradient(#fff, #fff);
367 border: none;
368}
369
370input[type=button]:active {
371 background: linear-gradient(#343434, #343434);
372}
373input[type=button].red:active {
374 background: linear-gradient(#94132e, #94132e);
375}
376input[type=button].blue:active {
377 background: linear-gradient(#1080b8, #1080b8);
378}
379input[type=button].white:active {
380 background: linear-gradient(#afafaf, #afafaf);
381}
382
383input[type=button]:disabled {
384 color: #252525;
385 background: linear-gradient(#575757 20%, #252525 100%);
386}
387
388input[type=button][pressed=pressed] {
389 color: #00b4ef;
390}
391
392input[type=checkbox] {
393 display: none;
394}
395input[type=checkbox] + label {
396 padding-left: 24px;
397 background-position-y: 6px;
398 background-repeat: no-repeat;
399 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC);
400}
401input[type=checkbox]:enabled + label:hover {
402 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=);
403}
404input[type=checkbox]:checked + label {
405 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPjZK9SgNBFIXvbCaQRDQq7mIhQRPBRisJKcwLWOobaCsExEaxcEEQe0trGysfwg0EwWoDsbFIJUaIBJOwus547saNP3FlPzgzzJxzL5edFbZtG77v7wkhtrXWS9gFRQC/DZ07jnOYKJfL+8ie4n7mvyIGdhpay+VyQuK8y5dPZoHuVtbpZcLi4wjJ1x4t316R9dDgBlsSi8mGu7pJjyJFzVaH+r7iqyHSELSQzVADjS0UgjlDKUUsLzVO98+9kSLGV5qaHXhjU0GWNSxk3hCIwnsfeMNCjTArLmHeUBodoLiE+R+jxuHPUZP4elGE3teonx2S/Q7lJzOUlkYQ+A4/xzyegzNhXmJpwTMXry9IFjcoa84O0r+QXpcK1cugCLREZadyoA19Ergxwf96nKjd1KqlYqmLQ540TUNwItUmRWdu3T36AODjwgpY9xqqAAAAAElFTkSuQmCC);
406}
407input[type=checkbox]:checked + label:hover {
408 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=);
409}
410
411.icon-input input {
412 position: relative;
413 padding-left: 36px;
414}
415.icon-input span {
416 position: absolute;
417 left: 6px;
418 top: -2px;
419 font-family: hifi-glyphs;
420 font-size: 30px;
421 color: #afafaf;
422}
423.icon-input input:focus + span {
424 color: #ffffff;
425}
426
427.selectable {
428 -webkit-touch-callout: text;
429 -webkit-user-select: text;
430 -khtml-user-select: text;
431 -moz-user-select: text;
432 -ms-user-select: text;
433 user-select: text;
434 cursor: text;
435}
436
437.color-box {
438 display: inline-block;
439 width: 15pt;
440 height: 15pt;
441 border: 0.75pt solid black;
442 margin: 1.5pt;
443 cursor: pointer;
444}
445
446.color-box.highlight {
447 width: 13.5pt;
448 height: 13.5pt;
449 border: 1.5pt solid black;
450}
451
452
453.section-header, .sub-section-header, hr {
454 display: table;
455 width: 100%;
456 margin: 21px -21px 0 -21px;
457 padding: 14px 21px 0 21px;
458 font-family: Raleway-Regular;
459 font-size: 12px;
460 color: #afafaf;
461 height: 28px;
462 text-transform: uppercase;
463 outline: none;
464}
465
466.section-header {
467 position: relative;
468 background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAqCAIAAAAbNW1vAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAmSURBVChTY1BFAgzhSIDBAQmMcoYHRwIJMCgjAQZ9JMBgBQdWVgBh5XmBV5A2FQAAAABJRU5ErkJggg==) repeat-x top left;
469}
470
471.sub-section-header, .no-collapse, hr {
472 background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAjSURBVBhXY1RVVf3PgARYjIyMoEwIYHRwcEBRwQSloYCBAQCwjgPMiI7W2QAAAABJRU5ErkJggg==) repeat-x top left;
473}
474
475.section-header:first-child {
476 margin-top: -2px;
477 padding-top: 0;
478 background: none;
479 height: auto;
480}
481
482.sub-section-header {
483 margin-bottom: -10px;
484}
485
486.section-header span {
487 font-family: HiFi-Glyphs;
488 font-size: 30px;
489 float: right;
490 position: absolute;
491 top: 4px;
492 right: 13px;
493}
494
495.section-header[collapsed="true"] {
496 margin-bottom: -21px;
497}
498
499hr {
500 border: none;
501 padding-top: 2px;
502}
503
504.text-group[collapsed="true"] ~ .text-group,
505.zone-group[collapsed="true"] ~ .zone-group,
506.web-group[collapsed="true"] ~ .web-group,
507.hyperlink-group[collapsed="true"] ~ .hyperlink-group,
508.spatial-group[collapsed="true"] ~ .spatial-group,
509.physical-group[collapsed="true"] ~ .physical-group,
510.behavior-group[collapsed="true"] ~ .behavior-group,
511.model-group[collapsed="true"] ~ .model-group,
512.light-group[collapsed="true"] ~ .light-group {
513 display: none !important;
514}
515
516
517.property {
518 display: table;
519 width: 100%;
520 margin-top: 21px;
521 min-height: 28px;
522}
523
524.property.checkbox {
525 width: auto;
526}
527
528.property label, .number label {
529 display: table-cell;
530 vertical-align: middle;
531 font-family: Raleway-SemiBold;
532 font-size: 14px;
533}
534.property label .unit, .number label .unit {
535 margin-left: 8px;
536 font-family: Raleway-Light;
537 font-size: 13px;
538}
539
540.value {
541 display: block;
542 min-height: 18px;
543}
544.value label {
545 display: inline-block;
546 vertical-align: top;
547 width: 48px;
548}
549.value span {
550 font-family: FiraSans-SemiBold;
551 font-size: 15px;
552}
553
554.checkbox + .checkbox {
555 margin-top: 0;
556}
557
558.checkbox-sub-props {
559 margin-top: 18px;
560}
561
562.property .number {
563 float: left;
564}
565.property .number + .number {
566 margin-left: 10px;
567}
568
569.text label, .url label, .number label, .textarea label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label {
570 float: left;
571 margin-left: 1px;
572 margin-bottom: 3px;
573 margin-top: -2px;
574}
575
576.number > input {
577 clear: both;
578 float: left;
579}
580.number > span {
581 clear: both;
582 float: left;
583}
584.xyz > div, .pyr > div, .gen > div {
585 clear: both;
586}
587
588.dropdown {
589 position: relative;
590 margin-bottom: -17px;
591}
592
593.dropdown select {
594 clear: both;
595}
596
597.dropdown dl {
598 clear: both;
599}
600.dropdown dl {
601 font-family: FiraSans-SemiBold;
602 font-size: 15px;
603 width: 292px;
604 height: 28px;
605 padding: 0 28px 0 12px;
606 color: #afafaf;
607 background: linear-gradient(#7d7d7d 20%, #686a68 100%);
608 position: relative;
609}
610.dropdown dl[dropped="true"] {
611 color: #404040;
612 background: linear-gradient(#afafaf, #afafaf);
613}
614
615.dropdown dt {
616 height: 100%;
617 box-sizing: border-box;
618 border-right: 1px solid #121212;
619 width: 100%;
620}
621.dropdown dt:hover {
622 color: #404040;
623}
624.dropdown dt:focus {
625 outline: none;
626}
627.dropdown dt span:first-child {
628 display: inline-block;
629 position: relative;
630 top: 5px;
631}
632.dropdown dt span:last-child {
633 font-family: HiFi-Glyphs;
634 font-size: 42px;
635 float: right;
636 margin-right: -48px;
637 position: relative;
638 left: -12px;
639 top: -9px;
640}
641
642.dropdown dd {
643 position: absolute;
644 top: 28px;
645 left: 3px;
646 display: none;
647}
648.dropdown dl[dropped="true"] dd {
649 display: block;
650}
651
652.dropdown li {
653 list-style-type: none;
654 padding: 3px 0 1px 12px;
655 width: 320px;
656 height: auto;
657 font-family: FiraSans-SemiBold;
658 font-size: 15px;
659 color: #404040;
660 background-color: #afafaf
661}
662.dropdown li:hover {
663 background-color: #00b4ef;
664}
665
666.dropdown dl[disabled="disabled"], .dropdown dl[disabled="disabled"][dropped="true"] {
667 color: #252525;
668 background: linear-gradient(#575757 20%, #252525 100%);
669}
670.dropdown dl[disabled="disabled"] dd {
671 display: none;
672}
673.dropdown dl[disabled="disabled"] dt:hover {
674 color: #252525;
675}

587 more lines aren’t shown here.

unpublishedScripts/marketplace/record/html/css/record.cssrenamed from scripts/system/html/css/record.css+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/html/fonts/FiraSans-SemiBold.ttf+0−0

Binary file not shown.

unpublishedScripts/marketplace/record/html/fonts/FiraSans.license+94−0

This diff is large: its 95 lines aren’t shown here.

unpublishedScripts/marketplace/record/html/fonts/Raleway-Bold.ttf+0−0

Binary file not shown.

unpublishedScripts/marketplace/record/html/fonts/Raleway-Light.ttf+0−0

Binary file not shown.

unpublishedScripts/marketplace/record/html/fonts/Raleway-Regular.ttf+0−0

Binary file not shown.

unpublishedScripts/marketplace/record/html/fonts/Raleway-SemiBold.ttf+0−0

Binary file not shown.

unpublishedScripts/marketplace/record/html/fonts/Raleway.license+94−0

This diff is large: its 95 lines aren’t shown here.

unpublishedScripts/marketplace/record/html/fonts/hifi-glyphs.ttf+0−0

Binary file not shown.

unpublishedScripts/marketplace/record/html/img/loader-red-countdown-ring.gifrenamed from scripts/system/html/img/loader-red-countdown-ring.gif+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/html/js/record.jsrenamed from scripts/system/html/js/record.js+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/html/record.htmlrenamed from scripts/system/html/record.html+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/playRecordingAC.jsrenamed from scripts/system/playRecordingAC.js+0−0

File renamed without changes.

unpublishedScripts/marketplace/record/record.jsrenamed from scripts/system/record.js+0−0

File renamed without changes.

unpublishedScripts/marketplace/stopwatch/models/transparent-box.fbx+0−0

Binary file not shown.

unpublishedScripts/marketplace/stopwatch/spawnStopwatch.js+36−2

This diff is large: its 50 lines aren’t shown here.

unpublishedScripts/marketplace/stopwatch/stopwatchReset.js+22−0

This diff is large: its 23 lines aren’t shown here.

unpublishedScripts/marketplace/stopwatch/stopwatchServer.js+31−19

This diff is large: its 102 lines aren’t shown here.

unpublishedScripts/marketplace/stopwatch/stopwatchStartStop.jsrenamed from unpublishedScripts/marketplace/stopwatch/stopwatchClient.js+7−6

This diff is large: its 28 lines aren’t shown here.