mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-16 11:52:04 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into equip-via-parenting
This commit is contained in:
commit
f554e25e36
141 changed files with 3460 additions and 3235 deletions
|
@ -98,3 +98,7 @@ If you need to debug Interface, you can run interface from within Visual Studio
|
|||
* In the Solution Explorer, right click interface and click Set as StartUp Project
|
||||
* Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug
|
||||
* Now you can run and debug interface through Visual Studio
|
||||
|
||||
For better performance when running debug builds, set the environment variable ```_NO_DEBUG_HEAP``` to ```1```
|
||||
|
||||
http://preshing.com/20110717/the-windows-heap-is-slow-when-launched-from-the-debugger/
|
||||
|
|
|
@ -1172,15 +1172,15 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
pathToCopyFrom = oldDefaultPersistPath;
|
||||
}
|
||||
|
||||
QDir persistFileDirectory = QDir(persistPath).filePath("..");
|
||||
if (!persistFileDirectory.exists()) {
|
||||
qDebug() << "Creating data directory " << persistFileDirectory.absolutePath();
|
||||
persistFileDirectory.mkpath(".");
|
||||
}
|
||||
|
||||
if (shouldCopy) {
|
||||
qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistPath;
|
||||
|
||||
QDir persistFileDirectory = QDir(persistPath).filePath("..");
|
||||
|
||||
if (!persistFileDirectory.exists()) {
|
||||
qDebug() << "Creating data directory " << persistFileDirectory.path();
|
||||
persistFileDirectory.mkpath(".");
|
||||
}
|
||||
QFile::copy(pathToCopyFrom, persistPath);
|
||||
} else {
|
||||
qDebug() << "No existing persist file found";
|
||||
|
|
|
@ -28,6 +28,15 @@ macro(GENERATE_INSTALLERS)
|
|||
# include CMake module that will install compiler system libraries
|
||||
# so that we have msvcr120 and msvcp120 installed with targets
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
|
||||
|
||||
# as long as we're including sixense plugin with installer
|
||||
# we need re-distributables for VS 2011 as well
|
||||
# this should be removed if/when sixense support is pulled
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
|
||||
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll"
|
||||
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll"
|
||||
)
|
||||
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
|
||||
|
|
|
@ -82,10 +82,14 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
set(DS_EXEC_NAME "domain-server.exe")
|
||||
set(AC_EXEC_NAME "assignment-client.exe")
|
||||
|
||||
# start menu shortcuts
|
||||
set(INTERFACE_SM_SHORTCUT_NAME "High Fidelity")
|
||||
set(CONSOLE_SM_SHORTCUT_NAME "Server Console")
|
||||
|
||||
# shortcut names
|
||||
if (PRODUCTION_BUILD)
|
||||
set(INTERFACE_SHORTCUT_NAME "High Fidelity")
|
||||
set(CONSOLE_SHORTCUT_NAME "Server Console")
|
||||
else ()
|
||||
set(INTERFACE_SHORTCUT_NAME "High Fidelity - ${BUILD_VERSION}")
|
||||
set(CONSOLE_SHORTCUT_NAME "Server Console - ${BUILD_VERSION}")
|
||||
endif ()
|
||||
# check if we need to find signtool
|
||||
if (PRODUCTION_BUILD OR PR_BUILD)
|
||||
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64")
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SM_SHORTCUT_NAME@")
|
||||
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
|
||||
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
||||
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
|
||||
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SM_SHORTCUT_NAME@")
|
||||
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
|
||||
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
||||
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
||||
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
|
||||
|
|
|
@ -12,13 +12,7 @@
|
|||
|
||||
Var MUI_TEMP
|
||||
Var STARTMENU_FOLDER
|
||||
Var SV_ALLUSERS
|
||||
Var START_MENU
|
||||
Var DO_NOT_ADD_TO_PATH
|
||||
Var ADD_TO_PATH_ALL_USERS
|
||||
Var ADD_TO_PATH_CURRENT_USER
|
||||
Var INSTALL_DESKTOP
|
||||
Var IS_DEFAULT_INSTALLDIR
|
||||
|
||||
;--------------------------------
|
||||
;Include Modern UI
|
||||
|
@ -205,346 +199,15 @@ Var AR_RegFlags
|
|||
!define MUI_HEADERIMAGE_UNBITMAP "@UNINSTALLER_HEADER_IMAGE@"
|
||||
!define MUI_ABORTWARNING
|
||||
|
||||
;--------------------------------
|
||||
; path functions
|
||||
|
||||
!verbose 3
|
||||
!include "WinMessages.NSH"
|
||||
!verbose 4
|
||||
|
||||
;----------------------------------------
|
||||
; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02"
|
||||
;----------------------------------------
|
||||
!verbose 3
|
||||
!include "WinMessages.NSH"
|
||||
!verbose 4
|
||||
;====================================================
|
||||
; get_NT_environment
|
||||
; Returns: the selected environment
|
||||
; Output : head of the stack
|
||||
;====================================================
|
||||
!macro select_NT_profile UN
|
||||
Function ${UN}select_NT_profile
|
||||
StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single
|
||||
DetailPrint "Selected environment for all users"
|
||||
Push "all"
|
||||
Return
|
||||
environment_single:
|
||||
DetailPrint "Selected environment for current user only."
|
||||
Push "current"
|
||||
Return
|
||||
FunctionEnd
|
||||
!macroend
|
||||
!insertmacro select_NT_profile ""
|
||||
!insertmacro select_NT_profile "un."
|
||||
;----------------------------------------------------
|
||||
!define NT_current_env 'HKCU "Environment"'
|
||||
!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
|
||||
!ifndef WriteEnvStr_RegKey
|
||||
!ifdef ALL_USERS
|
||||
!define WriteEnvStr_RegKey \
|
||||
'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
!else
|
||||
!define WriteEnvStr_RegKey 'HKCU "Environment"'
|
||||
!endif
|
||||
!endif
|
||||
|
||||
; AddToPath - Adds the given dir to the search path.
|
||||
; Input - head of the stack
|
||||
; Note - Win9x systems requires reboot
|
||||
|
||||
Function AddToPath
|
||||
Exch $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
|
||||
# don't add if the path doesn't exist
|
||||
IfFileExists "$0\*.*" "" AddToPath_done
|
||||
|
||||
ReadEnvStr $1 PATH
|
||||
; if the path is too long for a NSIS variable NSIS will return a 0
|
||||
; length string. If we find that, then warn and skip any path
|
||||
; modification as it will trash the existing path.
|
||||
StrLen $2 $1
|
||||
IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done
|
||||
CheckPathLength_ShowPathWarning:
|
||||
Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!"
|
||||
Goto AddToPath_done
|
||||
CheckPathLength_Done:
|
||||
Push "$1;"
|
||||
Push "$0;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" "" AddToPath_done
|
||||
Push "$1;"
|
||||
Push "$0\;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" "" AddToPath_done
|
||||
GetFullPathName /SHORT $3 $0
|
||||
Push "$1;"
|
||||
Push "$3;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" "" AddToPath_done
|
||||
Push "$1;"
|
||||
Push "$3\;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" "" AddToPath_done
|
||||
|
||||
Call IsNT
|
||||
Pop $1
|
||||
StrCmp $1 1 AddToPath_NT
|
||||
; Not on NT
|
||||
StrCpy $1 $WINDIR 2
|
||||
FileOpen $1 "$1\autoexec.bat" a
|
||||
FileSeek $1 -1 END
|
||||
FileReadByte $1 $2
|
||||
IntCmp $2 26 0 +2 +2 # DOS EOF
|
||||
FileSeek $1 -1 END # write over EOF
|
||||
FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n"
|
||||
FileClose $1
|
||||
SetRebootFlag true
|
||||
Goto AddToPath_done
|
||||
|
||||
AddToPath_NT:
|
||||
StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey
|
||||
ReadRegStr $1 ${NT_current_env} "PATH"
|
||||
Goto DoTrim
|
||||
ReadAllKey:
|
||||
ReadRegStr $1 ${NT_all_env} "PATH"
|
||||
DoTrim:
|
||||
StrCmp $1 "" AddToPath_NTdoIt
|
||||
Push $1
|
||||
Call Trim
|
||||
Pop $1
|
||||
StrCpy $0 "$1;$0"
|
||||
AddToPath_NTdoIt:
|
||||
StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey
|
||||
WriteRegExpandStr ${NT_current_env} "PATH" $0
|
||||
Goto DoSend
|
||||
WriteAllKey:
|
||||
WriteRegExpandStr ${NT_all_env} "PATH" $0
|
||||
DoSend:
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
AddToPath_done:
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
|
||||
; RemoveFromPath - Remove a given dir from the path
|
||||
; Input: head of the stack
|
||||
|
||||
Function un.RemoveFromPath
|
||||
Exch $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
Push $5
|
||||
Push $6
|
||||
|
||||
IntFmt $6 "%c" 26 # DOS EOF
|
||||
|
||||
Call un.IsNT
|
||||
Pop $1
|
||||
StrCmp $1 1 unRemoveFromPath_NT
|
||||
; Not on NT
|
||||
StrCpy $1 $WINDIR 2
|
||||
FileOpen $1 "$1\autoexec.bat" r
|
||||
GetTempFileName $4
|
||||
FileOpen $2 $4 w
|
||||
GetFullPathName /SHORT $0 $0
|
||||
StrCpy $0 "SET PATH=%PATH%;$0"
|
||||
Goto unRemoveFromPath_dosLoop
|
||||
|
||||
unRemoveFromPath_dosLoop:
|
||||
FileRead $1 $3
|
||||
StrCpy $5 $3 1 -1 # read last char
|
||||
StrCmp $5 $6 0 +2 # if DOS EOF
|
||||
StrCpy $3 $3 -1 # remove DOS EOF so we can compare
|
||||
StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine
|
||||
StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine
|
||||
StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine
|
||||
StrCmp $3 "" unRemoveFromPath_dosLoopEnd
|
||||
FileWrite $2 $3
|
||||
Goto unRemoveFromPath_dosLoop
|
||||
unRemoveFromPath_dosLoopRemoveLine:
|
||||
SetRebootFlag true
|
||||
Goto unRemoveFromPath_dosLoop
|
||||
|
||||
unRemoveFromPath_dosLoopEnd:
|
||||
FileClose $2
|
||||
FileClose $1
|
||||
StrCpy $1 $WINDIR 2
|
||||
Delete "$1\autoexec.bat"
|
||||
CopyFiles /SILENT $4 "$1\autoexec.bat"
|
||||
Delete $4
|
||||
Goto unRemoveFromPath_done
|
||||
|
||||
unRemoveFromPath_NT:
|
||||
StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey
|
||||
ReadRegStr $1 ${NT_current_env} "PATH"
|
||||
Goto unDoTrim
|
||||
unReadAllKey:
|
||||
ReadRegStr $1 ${NT_all_env} "PATH"
|
||||
unDoTrim:
|
||||
StrCpy $5 $1 1 -1 # copy last char
|
||||
StrCmp $5 ";" +2 # if last char != ;
|
||||
StrCpy $1 "$1;" # append ;
|
||||
Push $1
|
||||
Push "$0;"
|
||||
Call un.StrStr ; Find `$0;` in $1
|
||||
Pop $2 ; pos of our dir
|
||||
StrCmp $2 "" unRemoveFromPath_done
|
||||
; else, it is in path
|
||||
# $0 - path to add
|
||||
# $1 - path var
|
||||
StrLen $3 "$0;"
|
||||
StrLen $4 $2
|
||||
StrCpy $5 $1 -$4 # $5 is now the part before the path to remove
|
||||
StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove
|
||||
StrCpy $3 $5$6
|
||||
|
||||
StrCpy $5 $3 1 -1 # copy last char
|
||||
StrCmp $5 ";" 0 +2 # if last char == ;
|
||||
StrCpy $3 $3 -1 # remove last char
|
||||
|
||||
StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey
|
||||
WriteRegExpandStr ${NT_current_env} "PATH" $3
|
||||
Goto unDoSend
|
||||
unWriteAllKey:
|
||||
WriteRegExpandStr ${NT_all_env} "PATH" $3
|
||||
unDoSend:
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
unRemoveFromPath_done:
|
||||
Pop $6
|
||||
Pop $5
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Uninstall sutff
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
###########################################
|
||||
# Utility Functions #
|
||||
###########################################
|
||||
|
||||
;====================================================
|
||||
; IsNT - Returns 1 if the current system is NT, 0
|
||||
; otherwise.
|
||||
; Output: head of the stack
|
||||
;====================================================
|
||||
; IsNT
|
||||
; no input
|
||||
; output, top of the stack = 1 if NT or 0 if not
|
||||
;
|
||||
; Usage:
|
||||
; Call IsNT
|
||||
; Pop $R0
|
||||
; ($R0 at this point is 1 or 0)
|
||||
|
||||
!macro IsNT un
|
||||
Function ${un}IsNT
|
||||
Push $0
|
||||
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
|
||||
StrCmp $0 "" 0 IsNT_yes
|
||||
; we are not NT.
|
||||
Pop $0
|
||||
Push 0
|
||||
Return
|
||||
|
||||
IsNT_yes:
|
||||
; NT!!!
|
||||
Pop $0
|
||||
Push 1
|
||||
FunctionEnd
|
||||
!macroend
|
||||
!insertmacro IsNT ""
|
||||
!insertmacro IsNT "un."
|
||||
|
||||
; StrStr
|
||||
; input, top of stack = string to search for
|
||||
; top of stack-1 = string to search in
|
||||
; output, top of stack (replaces with the portion of the string remaining)
|
||||
; modifies no other variables.
|
||||
;
|
||||
; Usage:
|
||||
; Push "this is a long ass string"
|
||||
; Push "ass"
|
||||
; Call StrStr
|
||||
; Pop $R0
|
||||
; ($R0 at this point is "ass string")
|
||||
|
||||
!macro StrStr un
|
||||
Function ${un}StrStr
|
||||
Exch $R1 ; st=haystack,old$R1, $R1=needle
|
||||
Exch ; st=old$R1,haystack
|
||||
Exch $R2 ; st=old$R1,old$R2, $R2=haystack
|
||||
Push $R3
|
||||
Push $R4
|
||||
Push $R5
|
||||
StrLen $R3 $R1
|
||||
StrCpy $R4 0
|
||||
; $R1=needle
|
||||
; $R2=haystack
|
||||
; $R3=len(needle)
|
||||
; $R4=cnt
|
||||
; $R5=tmp
|
||||
loop:
|
||||
StrCpy $R5 $R2 $R3 $R4
|
||||
StrCmp $R5 $R1 done
|
||||
StrCmp $R5 "" done
|
||||
IntOp $R4 $R4 + 1
|
||||
Goto loop
|
||||
done:
|
||||
StrCpy $R1 $R2 "" $R4
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Exch $R1
|
||||
FunctionEnd
|
||||
!macroend
|
||||
!insertmacro StrStr ""
|
||||
!insertmacro StrStr "un."
|
||||
|
||||
Function Trim ; Added by Pelaca
|
||||
Exch $R1
|
||||
Push $R2
|
||||
Loop:
|
||||
StrCpy $R2 "$R1" 1 -1
|
||||
StrCmp "$R2" " " RTrim
|
||||
StrCmp "$R2" "$\n" RTrim
|
||||
StrCmp "$R2" "$\r" RTrim
|
||||
StrCmp "$R2" ";" RTrim
|
||||
GoTo Done
|
||||
RTrim:
|
||||
StrCpy $R1 "$R1" -1
|
||||
Goto Loop
|
||||
Done:
|
||||
Pop $R2
|
||||
Exch $R1
|
||||
FunctionEnd
|
||||
|
||||
Function ConditionalAddToRegisty
|
||||
Pop $0
|
||||
Pop $1
|
||||
StrCmp "$0" "" ConditionalAddToRegisty_EmptyString
|
||||
WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \
|
||||
"$1" "$0"
|
||||
;MessageBox MB_OK "Set Registry: '$1' to '$0'"
|
||||
DetailPrint "Set install registry entry: '$1' to '$0'"
|
||||
|
@ -598,11 +261,10 @@ FunctionEnd
|
|||
!insertmacro MUI_PAGE_WELCOME
|
||||
|
||||
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
|
||||
Page custom InstallOptionsPage
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
;Start Menu Folder Page Configuration
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
|
||||
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
|
||||
|
@ -678,7 +340,6 @@ FunctionEnd
|
|||
;Keep these lines before any File command
|
||||
;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA)
|
||||
|
||||
ReserveFile "NSIS.InstallOptions.ini"
|
||||
ReserveFile "@POST_INSTALL_OPTIONS_PATH@"
|
||||
|
||||
;--------------------------------
|
||||
|
@ -717,7 +378,7 @@ Section "-Core installation"
|
|||
@CPACK_NSIS_FULL_INSTALL@
|
||||
|
||||
;Store installation folder
|
||||
WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
|
||||
WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
|
||||
|
||||
;Package the signed uninstaller produced by the inner loop
|
||||
!ifndef INNER
|
||||
|
@ -768,10 +429,10 @@ Section "-Core installation"
|
|||
Push "Contact"
|
||||
Push "@CPACK_NSIS_CONTACT@"
|
||||
Call ConditionalAddToRegisty
|
||||
!insertmacro INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State"
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||
|
||||
;Create shortcuts
|
||||
|
||||
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
|
||||
@CPACK_NSIS_CREATE_ICONS@
|
||||
@CPACK_NSIS_CREATE_ICONS_EXTRA@
|
||||
|
@ -799,27 +460,10 @@ Section "-Core installation"
|
|||
|
||||
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@"
|
||||
|
||||
;Read a value from an InstallOptions INI file
|
||||
!insertmacro INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
|
||||
!insertmacro INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State"
|
||||
!insertmacro INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State"
|
||||
|
||||
; Write special uninstall registry entries
|
||||
Push "StartMenu"
|
||||
Push "$STARTMENU_FOLDER"
|
||||
Call ConditionalAddToRegisty
|
||||
Push "DoNotAddToPath"
|
||||
Push "$DO_NOT_ADD_TO_PATH"
|
||||
Call ConditionalAddToRegisty
|
||||
Push "AddToPathAllUsers"
|
||||
Push "$ADD_TO_PATH_ALL_USERS"
|
||||
Call ConditionalAddToRegisty
|
||||
Push "AddToPathCurrentUser"
|
||||
Push "$ADD_TO_PATH_CURRENT_USER"
|
||||
Call ConditionalAddToRegisty
|
||||
Push "InstallToDesktop"
|
||||
Push "$INSTALL_DESKTOP"
|
||||
Call ConditionalAddToRegisty
|
||||
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
|
||||
|
@ -827,22 +471,6 @@ Section "-Core installation"
|
|||
|
||||
SectionEnd
|
||||
|
||||
Section "-Add to path"
|
||||
Push $INSTDIR\bin
|
||||
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath
|
||||
StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0
|
||||
Call AddToPath
|
||||
doNotAddToPath:
|
||||
SectionEnd
|
||||
|
||||
;--------------------------------
|
||||
; Create custom pages
|
||||
Function InstallOptionsPage
|
||||
!insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@"
|
||||
!insertmacro INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini"
|
||||
|
||||
FunctionEnd
|
||||
|
||||
; Make sure nsDialogs is included before we use it
|
||||
!include "nsdialogs.nsh"
|
||||
|
||||
|
@ -908,7 +536,6 @@ Function PostInstallOptionsPage
|
|||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED}
|
||||
|
||||
${EndIf}
|
||||
|
||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||
|
@ -941,6 +568,16 @@ Function PostInstallOptionsPage
|
|||
!insertmacro SetPostInstallOption $LaunchNowCheckbox @LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
; a PR build defaults all install options expect LaunchNowCheckbox and the settings copy to unchecked
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
${NSD_SetState} $DesktopClientCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||
${NSD_SetState} $DesktopServerCheckbox ${BST_UNCHECKED}
|
||||
${NSD_SetState} $ServerStartupCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
; push the offset
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
|
@ -993,8 +630,17 @@ Function HandlePostInstallOptions
|
|||
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
|
||||
|
||||
${If} $ServerStartupState == ${BST_CHECKED}
|
||||
; in case we added a shortcut in the global context, pull that now
|
||||
SetShellVarContext all
|
||||
Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||
|
||||
; make a startup shortcut in this user's current context
|
||||
SetShellVarContext current
|
||||
CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
|
||||
|
||||
; reset the shell var context back
|
||||
SetShellVarContext all
|
||||
|
||||
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
|
||||
|
@ -1002,6 +648,7 @@ Function HandlePostInstallOptions
|
|||
${EndIf}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
|
||||
; check if we need to copy settings/content from production for this PR build
|
||||
${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState
|
||||
|
||||
|
@ -1025,6 +672,8 @@ Function HandlePostInstallOptions
|
|||
"There was a problem copying your production content and settings to $0 for this PR build.$\r$\n$\r$\nPlease copy them manually."
|
||||
|
||||
NoError:
|
||||
|
||||
SetShellVarContext all
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
|
@ -1050,19 +699,30 @@ FunctionEnd
|
|||
!include nsProcess.nsh
|
||||
|
||||
!macro PromptForRunningApplication applicationName displayName action prompter
|
||||
${nsProcess::FindProcess} ${applicationName} $R0
|
||||
!define UniqueID ${__LINE__}
|
||||
|
||||
${If} $R0 == 0
|
||||
; the process is running, ask the user if they want us to close it
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION \
|
||||
"${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and try again."
|
||||
Abort
|
||||
${EndIf}
|
||||
Prompt_${UniqueID}:
|
||||
|
||||
${nsProcess::FindProcess} ${applicationName} $R0
|
||||
|
||||
${If} $R0 == 0
|
||||
|
||||
; the process is running, ask the user to close it
|
||||
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \
|
||||
"${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and click Retry to continue." \
|
||||
/SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0
|
||||
|
||||
; If the user decided to cancel, stop the current installer/uninstaller
|
||||
Abort
|
||||
|
||||
${EndIf}
|
||||
|
||||
!undef UniqueID
|
||||
!macroend
|
||||
|
||||
!macro CheckForRunningApplications action prompter
|
||||
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "High Fidelity client" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "Server Console" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
|
||||
!macroend
|
||||
|
@ -1109,26 +769,6 @@ Function un.onInit
|
|||
Quit
|
||||
${EndSwitch}
|
||||
|
||||
ClearErrors
|
||||
UserInfo::GetName
|
||||
IfErrors noLM
|
||||
Pop $0
|
||||
UserInfo::GetAccountType
|
||||
Pop $1
|
||||
StrCmp $1 "Admin" 0 +3
|
||||
SetShellVarContext all
|
||||
;MessageBox MB_OK 'User "$0" is in the Admin group'
|
||||
Goto done
|
||||
StrCmp $1 "Power" 0 +3
|
||||
SetShellVarContext all
|
||||
;MessageBox MB_OK 'User "$0" is in the Power Users group'
|
||||
Goto done
|
||||
|
||||
noLM:
|
||||
;Get installation folder from registry if available
|
||||
|
||||
done:
|
||||
|
||||
FunctionEnd
|
||||
|
||||
;--- Add/Remove callback functions: ---
|
||||
|
@ -1186,19 +826,12 @@ FunctionEnd
|
|||
!ifdef INNER
|
||||
Section "Uninstall"
|
||||
|
||||
ReadRegStr $START_MENU SHCTX \
|
||||
; use all users context for data/startup folders
|
||||
SetShellVarContext all
|
||||
|
||||
ReadRegStr $START_MENU HKLM \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu"
|
||||
;MessageBox MB_OK "Start menu is in: $START_MENU"
|
||||
ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath"
|
||||
ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers"
|
||||
ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser"
|
||||
;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS"
|
||||
ReadRegStr $INSTALL_DESKTOP SHCTX \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop"
|
||||
;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP "
|
||||
|
||||
@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@
|
||||
|
||||
|
@ -1217,13 +850,13 @@ Section "Uninstall"
|
|||
|
||||
;Remove the uninstaller itself.
|
||||
Delete "$INSTDIR\@UNINSTALLER_NAME@"
|
||||
DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
|
||||
;Remove the installation directory if it is empty.
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
; Remove the registry entries.
|
||||
DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
DeleteRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
|
||||
; Removes all optional components
|
||||
!insertmacro SectionList "RemoveSection_CPack"
|
||||
|
@ -1235,7 +868,12 @@ Section "Uninstall"
|
|||
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||
Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk"
|
||||
Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||
|
||||
; if it exists, delete the startup shortcut for the current user
|
||||
SetShellVarContext current
|
||||
Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||
SetShellVarContext all
|
||||
|
||||
@CPACK_NSIS_DELETE_ICONS@
|
||||
@CPACK_NSIS_DELETE_ICONS_EXTRA@
|
||||
|
||||
|
@ -1264,6 +902,8 @@ Section "Uninstall"
|
|||
; try to fix it.
|
||||
StrCpy $MUI_TEMP "$START_MENU"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||
@CPACK_NSIS_DELETE_ICONS_EXTRA@
|
||||
|
||||
;Delete empty start menu parent diretories
|
||||
|
@ -1279,24 +919,12 @@ Section "Uninstall"
|
|||
StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop
|
||||
secondStartMenuDeleteLoopDone:
|
||||
|
||||
DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
DeleteRegKey /ifempty HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
|
||||
Push $INSTDIR\bin
|
||||
StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0
|
||||
Call un.RemoveFromPath
|
||||
doNotRemoveFromPath:
|
||||
SectionEnd
|
||||
!endif
|
||||
|
||||
;--------------------------------
|
||||
; determine admin versus local install
|
||||
; Is install for "AllUsers" or "JustMe"?
|
||||
; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
|
||||
; This function is used for the very first "custom page" of the installer.
|
||||
; This custom page does not show up visibly, but it executes prior to the
|
||||
; first visible page and sets up $INSTDIR properly...
|
||||
; Choose different default installation folder based on SV_ALLUSERS...
|
||||
; "Program Files" for AllUsers, "My Documents" for JustMe...
|
||||
InstallDirRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ""
|
||||
|
||||
Function .onInit
|
||||
|
||||
|
@ -1342,48 +970,6 @@ inst:
|
|||
; Reads components status for registry
|
||||
!insertmacro SectionList "InitSection"
|
||||
|
||||
; check to see if /D has been used to change
|
||||
; the install directory by comparing it to the
|
||||
; install directory that is expected to be the
|
||||
; default
|
||||
StrCpy $IS_DEFAULT_INSTALLDIR 0
|
||||
StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2
|
||||
StrCpy $IS_DEFAULT_INSTALLDIR 1
|
||||
|
||||
StrCpy $SV_ALLUSERS "JustMe"
|
||||
; if default install dir then change the default
|
||||
; if it is installed for JustMe
|
||||
StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
|
||||
StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
|
||||
|
||||
ClearErrors
|
||||
UserInfo::GetName
|
||||
IfErrors noLM
|
||||
Pop $0
|
||||
UserInfo::GetAccountType
|
||||
Pop $1
|
||||
StrCmp $1 "Admin" 0 +4
|
||||
SetShellVarContext all
|
||||
;MessageBox MB_OK 'User "$0" is in the Admin group'
|
||||
StrCpy $SV_ALLUSERS "AllUsers"
|
||||
Goto done
|
||||
StrCmp $1 "Power" 0 +4
|
||||
SetShellVarContext all
|
||||
;MessageBox MB_OK 'User "$0" is in the Power Users group'
|
||||
StrCpy $SV_ALLUSERS "AllUsers"
|
||||
Goto done
|
||||
|
||||
noLM:
|
||||
StrCpy $SV_ALLUSERS "AllUsers"
|
||||
;Get installation folder from registry if available
|
||||
|
||||
done:
|
||||
StrCmp $SV_ALLUSERS "AllUsers" 0 +3
|
||||
StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
|
||||
StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
|
||||
|
||||
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
|
||||
!insertmacro INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
|
||||
|
||||
noOptionsPage:
|
||||
; use all users for context of data/startup folders
|
||||
SetShellVarContext all
|
||||
FunctionEnd
|
||||
|
|
|
@ -976,13 +976,160 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-header zone-section">
|
||||
<label>Zone</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<span class="label">Stage Sun Model Enabled</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-stage-sun-model-enabled">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sub-section-header zone-section keyLight-section">
|
||||
<label>KeyLight</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Light Color</div>
|
||||
<div class="value">
|
||||
<div class='color-picker' id="property-zone-key-light-color"></div>
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-zone-key-light-color-red" min="0" max="255" step="1"></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-zone-key-light-color-green" min="0" max="255" step="1"></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-zone-key-light-color-blue" min="0" max="255" step="1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Light Intensity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-key-intensity" min="0" max="10" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Light Direction</div>
|
||||
<div class="value">
|
||||
<div class="input-area">Pitch <input class="coord" type='number' id="property-zone-key-light-direction-x"></div>
|
||||
<div class="input-area">Yaw <input class="coord" type='number' id="property-zone-key-light-direction-y"></div>
|
||||
<div class="input-area">Roll <input class="coord" type='number' id="property-zone-key-light-direction-z"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Ambient Intensity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Ambient URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-key-ambient-url" class="url">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-section-header zone-section stage-section">
|
||||
<label>Stage</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Latitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-latitude" min="-90" max="90" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Longitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-longitude" min="-180" max="180" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Altitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-altitude" step="1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section stage-section property">
|
||||
<span class="label">Automatically calculate stage hour and day from location and clock.</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-stage-automatic-hour-day">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Day</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-day" min="0" max="365" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Hour</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-hour" min="0" max="24" step="0.5">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-section-header zone-section background-section">
|
||||
<label>Background</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section background-section property">
|
||||
<div class="label">Background Mode</div>
|
||||
<div class="value">
|
||||
<select name="SelectBackgroundMode" id="property-zone-background-mode">
|
||||
<option value='inherit'>Nothing</option>
|
||||
<option value='skybox'>Skybox</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sub-section-header zone-section skybox-section">
|
||||
<label>Skybox</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox Color</div>
|
||||
<div class="value">
|
||||
<div class='color-picker' id="property-zone-skybox-color"></div>
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-zone-skybox-color-red"></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-zone-skybox-color-green"></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-zone-skybox-color-blue"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-skybox-url" class="url">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-header web-section">
|
||||
<label>Web</label>
|
||||
</div>
|
||||
|
||||
<div class="web-section property">
|
||||
<div class="label">Source URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-web-source-url" class="url">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header">
|
||||
<label>Hyperlink</label>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Href</div>
|
||||
<div class="label">Href - Hifi://address</div>
|
||||
<div class="value">
|
||||
<input id="property-hyperlink-href" class="url">
|
||||
</div>
|
||||
|
@ -1311,18 +1458,6 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="section-header web-section">
|
||||
<label>Web</label>
|
||||
</div>
|
||||
|
||||
<div class="web-section property">
|
||||
<div class="label">Source URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-web-source-url" class="url">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header particle-section">
|
||||
<label>Particle</label>
|
||||
</div>
|
||||
|
@ -1438,141 +1573,6 @@
|
|||
<input class="coord" type='number' id="property-light-cutoff">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header zone-section">
|
||||
<label>Zone</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<span class="label">Stage Sun Model Enabled</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-stage-sun-model-enabled">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sub-section-header zone-section keyLight-section">
|
||||
<label>KeyLight</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Light Color</div>
|
||||
<div class="value">
|
||||
<div class='color-picker' id="property-zone-key-light-color"></div>
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-zone-key-light-color-red" min="0" max="255" step="1"></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-zone-key-light-color-green" min="0" max="255" step="1"></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-zone-key-light-color-blue" min="0" max="255" step="1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Light Intensity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-key-intensity" min="0" max="10" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Light Direction</div>
|
||||
<div class="value">
|
||||
<div class="input-area">Pitch <input class="coord" type='number' id="property-zone-key-light-direction-x"></div>
|
||||
<div class="input-area">Yaw <input class="coord" type='number' id="property-zone-key-light-direction-y"></div>
|
||||
<div class="input-area">Roll <input class="coord" type='number' id="property-zone-key-light-direction-z"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Ambient Intensity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="zone-section keyLight-section property">
|
||||
<div class="label">Ambient URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-key-ambient-url" class="url">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-section-header zone-section stage-section">
|
||||
<label>Stage</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Latitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-latitude" min="-90" max="90" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Longitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-longitude" min="-180" max="180" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Altitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-altitude" step="1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section stage-section property">
|
||||
<span class="label">Automatically calculate stage hour and day from location and clock.</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-stage-automatic-hour-day">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Day</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-day" min="0" max="365" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section stage-section property">
|
||||
<div class="label">Stage Hour</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-hour" min="0" max="24" step="0.5">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-section-header zone-section background-section">
|
||||
<label>Background</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section background-section property">
|
||||
<div class="label">Background Mode</div>
|
||||
<div class="value">
|
||||
<select name="SelectBackgroundMode" id="property-zone-background-mode">
|
||||
<option value='inherit'>Nothing</option>
|
||||
<option value='skybox'>Skybox</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sub-section-header zone-section skybox-section">
|
||||
<label>Skybox</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox Color</div>
|
||||
<div class="value">
|
||||
<div class='color-picker' id="property-zone-skybox-color"></div>
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-zone-skybox-color-red"></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-zone-skybox-color-green"></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-zone-skybox-color-blue"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-skybox-url" class="url">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
31
examples/junkyard/junkyardClientReset.js
Normal file
31
examples/junkyard/junkyardClientReset.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// junkyardClientReset.js
|
||||
// examples/junkyard
|
||||
//
|
||||
// Created by Eric Levin on 1/21/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This script resets the junkyard scene
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var IMPORT_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json";
|
||||
var PASTE_ENTITIES_LOCATION = {x: 0, y: 0, z: 0};
|
||||
reset();
|
||||
|
||||
function reset() {
|
||||
// Delete everything and re-import the junkyard arf
|
||||
var e = Entities.findEntities(MyAvatar.position, 1000);
|
||||
for (i = 0; i < e.length; i++) {
|
||||
Entities.deleteEntity(e[i]);
|
||||
}
|
||||
importAssetResourceFile();
|
||||
}
|
||||
|
||||
function importAssetResourceFile() {
|
||||
Clipboard.importEntities(IMPORT_URL);
|
||||
Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION);
|
||||
}
|
53
examples/junkyard/junkyardResetEntityScript.js
Normal file
53
examples/junkyard/junkyardResetEntityScript.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
// junkyardResetEntityScript.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by Eric Levin on 1/20/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script resets the junkyard when triggered
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include("../libraries/utils.js");
|
||||
var _this;
|
||||
var IMPORT_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json";
|
||||
var PASTE_ENTITIES_LOCATION = {x: 0, y: 0, z: 0};
|
||||
var JunkyardResetter = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
JunkyardResetter.prototype = {
|
||||
|
||||
clickReleaseOnEntity: function(entityId, mouseEvent) {
|
||||
if (!mouseEvent.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
this.reset();
|
||||
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
// Delete everything and re-import the junkyard arf
|
||||
var e = Entities.findEntities(MyAvatar.position, 1000);
|
||||
for (i = 0; i < e.length; i++) {
|
||||
// Don't delete our reset entity
|
||||
if (JSON.stringify(this.entityID) !== JSON.stringify(e[i])) {
|
||||
Entities.deleteEntity(e[i]);
|
||||
}
|
||||
}
|
||||
this.importAssetResourceFile();
|
||||
},
|
||||
|
||||
importAssetResourceFile: function() {
|
||||
Clipboard.importEntities(IMPORT_URL);
|
||||
Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION);
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
return new JunkyardResetter();
|
||||
});
|
45
examples/junkyard/junkyardResetEntitySpawner.js
Normal file
45
examples/junkyard/junkyardResetEntitySpawner.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// junkyardResetEntitySpawner.js
|
||||
// examples/junkyard
|
||||
//
|
||||
// Created by Eric Levin on 1/20/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This script spawns an entity which, when triggered, will reset the junkyard
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var orientation = Camera.getOrientation();
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
orientation.x = 0;
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
|
||||
|
||||
|
||||
var SCRIPT_URL = Script.resolvePath("junkyardResetEntityScript.js");
|
||||
var MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Blueprint%20Objects/Asylum/Asylum_Table/Asylum_Table.fbx";
|
||||
var resetEntity = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: MODEL_URL,
|
||||
position: center,
|
||||
script: SCRIPT_URL,
|
||||
dimensions: {
|
||||
x: 2.8,
|
||||
y: 1.76,
|
||||
z: 1.32
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 200
|
||||
}
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(resetEntity);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -1515,7 +1515,6 @@ SelectionDisplay = (function() {
|
|||
}
|
||||
|
||||
that.updateRotationHandles();
|
||||
that.highlightSelectable();
|
||||
|
||||
var rotation, dimensions, position, registrationPoint;
|
||||
|
||||
|
|
|
@ -236,18 +236,10 @@
|
|||
"dimensions"
|
||||
]);
|
||||
|
||||
ImageOverlay = generateOverlayClass(Overlay2D, "image", [
|
||||
"subImage", "imageURL"
|
||||
]);
|
||||
|
||||
Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [
|
||||
"url", "subImage"
|
||||
]);
|
||||
|
||||
TextOverlay = generateOverlayClass(Overlay2D, "text", [
|
||||
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
|
||||
]);
|
||||
|
||||
Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [
|
||||
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
|
||||
"rightMargin", "bottomMargin"
|
||||
|
|
|
@ -132,14 +132,15 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
this.y = y;
|
||||
this.width = 0;
|
||||
this.height = ToolBar.TITLE_BAR_HEIGHT;
|
||||
this.back = this.back = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
||||
this.backAlpha = 1.0;
|
||||
this.back = Overlays.addOverlay("rectangle", {
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
radius: 4,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
alpha: 1.0,
|
||||
backgroundAlpha: 1.0,
|
||||
alpha: this.backAlpha,
|
||||
visible: false
|
||||
});
|
||||
this.spacing = [];
|
||||
|
@ -246,10 +247,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
this.tools[tool].setAlpha(alpha);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
alpha: alpha,
|
||||
backgroundAlpha: alpha
|
||||
});
|
||||
this.backAlpha = alpha;
|
||||
Overlays.editOverlay(this.back, { alpha: alpha });
|
||||
}
|
||||
} else {
|
||||
this.tools[tool].setAlpha(alpha);
|
||||
|
@ -258,9 +257,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
|
||||
this.setBack = function(color, alpha) {
|
||||
if (color == null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
visible: false
|
||||
});
|
||||
Overlays.editOverlay(this.back, { visible: false });
|
||||
} else {
|
||||
Overlays.editOverlay(this.back, {
|
||||
width: this.width +
|
||||
|
@ -268,8 +265,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
height: this.height +
|
||||
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
visible: true,
|
||||
backgroundColor: color,
|
||||
backgroundAlpha: alpha
|
||||
color: color,
|
||||
alpha: alpha
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -339,12 +336,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
that.hover = function (enable) { // Can be overriden or extended by clients.
|
||||
that.isHovering = enable;
|
||||
if (that.back) {
|
||||
if (enable) {
|
||||
that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha');
|
||||
}
|
||||
Overlays.editOverlay(this.back, {
|
||||
visible: enable,
|
||||
backgroundAlpha: enable ? 0.5 : that.oldAlpha
|
||||
alpha: enable ? 0.5 : that.backAlpha
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
"id": "rightHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_open_right.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_open_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -157,7 +157,7 @@
|
|||
"id": "rightHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_closed_right.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_closed_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -205,7 +205,7 @@
|
|||
"id": "leftHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_open_left.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_open_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -217,7 +217,7 @@
|
|||
"id": "leftHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_closed_left.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_closed_left.fbx",
|
||||
"startFrame": 10.0,
|
||||
"endFrame": 10.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -420,7 +420,7 @@
|
|||
"id": "idleStand",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -432,7 +432,7 @@
|
|||
"id": "idleTalk",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/talk/talk.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/talk.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 801.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -457,7 +457,7 @@
|
|||
"id": "walkFwdShort",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_short_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 39.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -469,7 +469,7 @@
|
|||
"id": "walkFwdNormal",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 35.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -481,7 +481,7 @@
|
|||
"id": "walkFwdRun",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/run_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 21.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -495,7 +495,7 @@
|
|||
"id": "idleToWalkFwd",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/idle_to_walk.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle_to_walk.fbx",
|
||||
"startFrame": 1.0,
|
||||
"endFrame": 19.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -518,7 +518,7 @@
|
|||
"id": "walkBwdShort",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_short_bwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 38.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -530,7 +530,7 @@
|
|||
"id": "walkBwdNormal",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_bwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 36.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -544,7 +544,7 @@
|
|||
"id": "turnLeft",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/turn_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 28.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -556,7 +556,7 @@
|
|||
"id": "turnRight",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/turn_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 30.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -579,7 +579,7 @@
|
|||
"id": "strafeLeftShort",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_short_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 28.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -591,7 +591,7 @@
|
|||
"id": "strafeLeftNormal",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 30.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -616,7 +616,7 @@
|
|||
"id": "strafeRightShort",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_short_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 28.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -628,7 +628,7 @@
|
|||
"id": "strafeRightNormal",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 30.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -642,7 +642,7 @@
|
|||
"id": "awayIntro",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 83.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -654,7 +654,7 @@
|
|||
"id": "away",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
|
||||
"startFrame": 83.0,
|
||||
"endFrame": 84.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -666,7 +666,7 @@
|
|||
"id": "awayOutro",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
|
||||
"startFrame": 84.0,
|
||||
"endFrame": 167.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -678,7 +678,7 @@
|
|||
"id": "fly",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/fly.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/fly.fbx",
|
||||
"startFrame": 1.0,
|
||||
"endFrame": 80.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -700,7 +700,7 @@
|
|||
"id": "userAnimA",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -712,7 +712,7 @@
|
|||
"id": "userAnimB",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
|
|
|
@ -108,7 +108,7 @@ Window {
|
|||
}
|
||||
}
|
||||
|
||||
WebEngineView {
|
||||
WebView {
|
||||
id: webview
|
||||
url: "http://highfidelity.com"
|
||||
anchors.top: buttons.bottom
|
||||
|
@ -124,6 +124,12 @@ Window {
|
|||
onIconChanged: {
|
||||
console.log("New icon: " + icon)
|
||||
}
|
||||
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
storageName: "qmlUserBrowser"
|
||||
}
|
||||
|
||||
}
|
||||
} // item
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ Item {
|
|||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Velocity: " + root.velocity.toFixed(1)
|
||||
text: "Speed: " + root.speed.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
TextOverlayElement {
|
||||
id: root
|
||||
Rectangle {
|
||||
color: root.backgroundColor
|
||||
anchors.fill: parent
|
||||
Text {
|
||||
x: root.leftMargin
|
||||
y: root.topMargin
|
||||
id: text
|
||||
objectName: "textElement"
|
||||
text: root.text
|
||||
color: root.textColor
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.fontSize
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: root.lineHeight
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,6 +103,8 @@ Windows.Window {
|
|||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
var tab = tabView.getTab(index);
|
||||
tab.enabledChanged.disconnect(updateVisiblity);
|
||||
tabView.removeTab(index);
|
||||
console.log("Updating visibility based on child tab removed");
|
||||
updateVisiblity();
|
||||
|
@ -137,10 +139,7 @@ Windows.Window {
|
|||
}
|
||||
|
||||
console.log("Updating visibility based on child tab added");
|
||||
newTab.enabledChanged.connect(function() {
|
||||
console.log("Updating visibility based on child tab enabled change");
|
||||
updateVisiblity();
|
||||
})
|
||||
newTab.enabledChanged.connect(updateVisiblity)
|
||||
updateVisiblity();
|
||||
return newTab
|
||||
}
|
||||
|
|
150
interface/resources/qml/controls/ComboBox.qml
Normal file
150
interface/resources/qml/controls/ComboBox.qml
Normal file
|
@ -0,0 +1,150 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "." as VrControls
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
property alias model: comboBox.model;
|
||||
readonly property alias currentText: comboBox.currentText;
|
||||
property alias currentIndex: comboBox.currentIndex;
|
||||
implicitHeight: comboBox.height;
|
||||
focus: true
|
||||
|
||||
readonly property ComboBox control: comboBox
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
gradient: Gradient {
|
||||
GradientStop {color: control.pressed ? "#bababa" : "#fefefe" ; position: 0}
|
||||
GradientStop {color: control.pressed ? "#ccc" : "#e3e3e3" ; position: 1}
|
||||
}
|
||||
anchors.fill: parent
|
||||
border.color: control.activeFocus ? "#47b" : "#999"
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: control.activeFocus ? "#47b" : "white"
|
||||
opacity: control.hovered || control.activeFocus ? 0.1 : 0
|
||||
Behavior on opacity {NumberAnimation{ duration: 100 }}
|
||||
}
|
||||
}
|
||||
|
||||
SystemPalette { id: palette }
|
||||
|
||||
ComboBox {
|
||||
id: comboBox
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
}
|
||||
|
||||
Text {
|
||||
id: textField
|
||||
anchors { left: parent.left; leftMargin: 2; right: dropIcon.left; verticalCenter: parent.verticalCenter }
|
||||
text: comboBox.currentText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Item {
|
||||
id: dropIcon
|
||||
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
width: 20
|
||||
height: textField.height
|
||||
VrControls.FontAwesome {
|
||||
anchors.centerIn: parent; size: 16;
|
||||
text: "\uf0d7"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: toggleList();
|
||||
}
|
||||
|
||||
function toggleList() {
|
||||
if (popup.visible) {
|
||||
hideList();
|
||||
} else {
|
||||
showList();
|
||||
}
|
||||
}
|
||||
|
||||
function showList() {
|
||||
var r = desktop.mapFromItem(root, 0, 0, root.width, root.height);
|
||||
listView.currentIndex = root.currentIndex
|
||||
scrollView.x = r.x;
|
||||
scrollView.y = r.y + r.height;
|
||||
var bottom = scrollView.y + scrollView.height;
|
||||
if (bottom > desktop.height) {
|
||||
scrollView.y -= bottom - desktop.height + 8;
|
||||
}
|
||||
popup.visible = true;
|
||||
popup.forceActiveFocus();
|
||||
}
|
||||
|
||||
function hideList() {
|
||||
popup.visible = false;
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: popup
|
||||
parent: desktop
|
||||
anchors.fill: parent
|
||||
z: desktop.zLevels.menu
|
||||
visible: false
|
||||
focus: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: hideList();
|
||||
}
|
||||
|
||||
function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; }
|
||||
function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; }
|
||||
function selectCurrentItem() { root.currentIndex = listView.currentIndex; hideList(); }
|
||||
|
||||
Keys.onUpPressed: previousItem();
|
||||
Keys.onDownPressed: nextItem();
|
||||
Keys.onSpacePressed: selectCurrentItem();
|
||||
Keys.onRightPressed: selectCurrentItem();
|
||||
Keys.onReturnPressed: selectCurrentItem();
|
||||
Keys.onEscapePressed: hideList();
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
height: 480
|
||||
width: root.width
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
height: textField.height * count * 1.4
|
||||
model: root.model
|
||||
highlight: Rectangle{
|
||||
width: listView.currentItem ? listView.currentItem.width : 0
|
||||
height: listView.currentItem ? listView.currentItem.height : 0
|
||||
color: "red"
|
||||
}
|
||||
delegate: Rectangle {
|
||||
width: root.width
|
||||
height: popupText.implicitHeight * 1.4
|
||||
color: ListView.isCurrentItem ? palette.highlight : palette.base
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
id: popupText
|
||||
x: 3
|
||||
text: listView.model[index]
|
||||
}
|
||||
MouseArea {
|
||||
id: popupHover
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: true
|
||||
onEntered: listView.currentIndex = index;
|
||||
onClicked: popup.selectCurrentItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,9 @@ FocusScope {
|
|||
anchors.fill: parent;
|
||||
objectName: "desktop"
|
||||
|
||||
onHeightChanged: d.repositionAll();
|
||||
onWidthChanged: d.repositionAll();
|
||||
|
||||
// Allows QML/JS to find the desktop through the parent chain
|
||||
property bool desktopRoot: true
|
||||
|
||||
|
@ -22,7 +25,7 @@ FocusScope {
|
|||
readonly property alias zLevels: zLevels
|
||||
QtObject {
|
||||
id: zLevels;
|
||||
readonly property real normal: 0
|
||||
readonly property real normal: 1 // make windows always appear higher than QML overlays and other non-window controls.
|
||||
readonly property real top: 2000
|
||||
readonly property real modal: 4000
|
||||
readonly property real menu: 8000
|
||||
|
@ -50,10 +53,6 @@ FocusScope {
|
|||
return item;
|
||||
}
|
||||
|
||||
function isDesktop(item) {
|
||||
return item.desktopRoot;
|
||||
}
|
||||
|
||||
function isTopLevelWindow(item) {
|
||||
return item.topLevelWindow;
|
||||
}
|
||||
|
@ -147,6 +146,53 @@ FocusScope {
|
|||
var windows = getTopLevelWindows(predicate);
|
||||
fixupZOrder(windows, zBasis, targetWindow);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
|
||||
focusHack.start();
|
||||
}
|
||||
|
||||
function onWindowFocusChanged() {
|
||||
console.log("Focus item is " + offscreenWindow.activeFocusItem);
|
||||
|
||||
// FIXME this needs more testing before it can go into production
|
||||
// and I already cant produce any way to have a modal dialog lose focus
|
||||
// to a non-modal one.
|
||||
/*
|
||||
var focusedWindow = getDesktopWindow(offscreenWindow.activeFocusItem);
|
||||
|
||||
if (isModalWindow(focusedWindow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// new focused window is not modal... check if there are any modal windows
|
||||
var windows = getTopLevelWindows(isModalWindow);
|
||||
if (0 === windows.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There are modal windows present, force focus back to the top-most modal window
|
||||
windows.sort(function(a, b){ return a.z - b.z; });
|
||||
windows[windows.length - 1].focus = true;
|
||||
*/
|
||||
|
||||
// var focusedItem = offscreenWindow.activeFocusItem ;
|
||||
// if (DebugQML && focusedItem) {
|
||||
// var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height);
|
||||
// focusDebugger.x = rect.x;
|
||||
// focusDebugger.y = rect.y;
|
||||
// focusDebugger.width = rect.width
|
||||
// focusDebugger.height = rect.height
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
function repositionAll() {
|
||||
var windows = d.getTopLevelWindows();
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
reposition(windows[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function raise(item) {
|
||||
|
@ -167,7 +213,7 @@ FocusScope {
|
|||
}
|
||||
|
||||
if (setFocus) {
|
||||
focus = true;
|
||||
targetWindow.focus = true;
|
||||
}
|
||||
|
||||
reposition(targetWindow);
|
||||
|
@ -187,37 +233,36 @@ FocusScope {
|
|||
var windowRect = targetWindow.framedRect();
|
||||
var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y);
|
||||
var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height);
|
||||
var newPosition;
|
||||
if (targetWindow.x === -1 && targetWindow.y === -1) {
|
||||
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
|
||||
if (newPosition.x === -1 && newPosition.y === -1) {
|
||||
// Set initial window position
|
||||
newPosition = Utils.randomPosition(minPosition, maxPosition);
|
||||
} else {
|
||||
newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition);
|
||||
// newPosition = Utils.randomPosition(minPosition, maxPosition);
|
||||
console.log("Target has no defined position, putting in center of the screen")
|
||||
newPosition = Qt.vector2d(desktop.width / 2 - windowRect.width / 2,
|
||||
desktop.height / 2 - windowRect.height / 2);
|
||||
}
|
||||
|
||||
newPosition = Utils.clampVector(newPosition, minPosition, maxPosition);
|
||||
targetWindow.x = newPosition.x;
|
||||
targetWindow.y = newPosition.y;
|
||||
}
|
||||
|
||||
function repositionAll() {
|
||||
var windows = d.getTopLevelWindows();
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
reposition(windows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: repositionAll();
|
||||
onWidthChanged: repositionAll();
|
||||
|
||||
Component { id: messageDialogBuilder; MessageDialog { } }
|
||||
function messageBox(properties) {
|
||||
return messageDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
Component { id: queryDialogBuilder; QueryDialog { } }
|
||||
function queryBox(properties) {
|
||||
return queryDialogBuilder.createObject(desktop, properties);
|
||||
Component { id: inputDialogBuilder; QueryDialog { } }
|
||||
function inputDialog(properties) {
|
||||
return inputDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
Component { id: fileDialogBuilder; FileDialog { } }
|
||||
function fileOpenDialog(properties) {
|
||||
return fileDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
|
||||
MenuMouseHandler { id: menuPopperUpper }
|
||||
function popupMenu(point) {
|
||||
menuPopperUpper.popup(desktop, rootMenu.items, point);
|
||||
|
@ -252,39 +297,21 @@ FocusScope {
|
|||
desktop.focus = true;
|
||||
}
|
||||
|
||||
// Debugging help for figuring out focus issues
|
||||
property var offscreenWindow;
|
||||
onOffscreenWindowChanged: {
|
||||
offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
|
||||
focusHack.start();
|
||||
}
|
||||
|
||||
FocusHack { id: focusHack; }
|
||||
|
||||
function onWindowFocusChanged() {
|
||||
console.log("Focus item is " + offscreenWindow.activeFocusItem);
|
||||
var focusedItem = offscreenWindow.activeFocusItem ;
|
||||
if (DebugQML && focusedItem) {
|
||||
var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height);
|
||||
focusDebugger.x = rect.x;
|
||||
focusDebugger.y = rect.y;
|
||||
focusDebugger.width = rect.width
|
||||
focusDebugger.height = rect.height
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: focusDebugger;
|
||||
z: 9999; visible: false; color: "red"
|
||||
ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 }
|
||||
}
|
||||
|
||||
|
||||
Action {
|
||||
text: "Toggle Focus Debugger"
|
||||
shortcut: "Ctrl+Shift+F"
|
||||
enabled: DebugQML
|
||||
onTriggered: focusDebugger.visible = !focusDebugger.visible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,27 @@ import "fileDialog"
|
|||
//FIXME implement shortcuts for favorite location
|
||||
ModalWindow {
|
||||
id: root
|
||||
resizable: true
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
Settings {
|
||||
category: "FileDialog"
|
||||
property alias width: root.width
|
||||
property alias height: root.height
|
||||
property alias x: root.x
|
||||
property alias y: root.y
|
||||
}
|
||||
|
||||
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property alias caption: root.title;
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property alias dir: model.folder;
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property alias filter: selectionType.filtersString;
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property int options; // <-- FIXME unused
|
||||
|
||||
property bool selectDirectory: false;
|
||||
property bool showHidden: false;
|
||||
|
@ -19,17 +40,11 @@ ModalWindow {
|
|||
property bool multiSelect: false;
|
||||
// FIXME implement
|
||||
property bool saveDialog: false;
|
||||
property var helper: fileDialogHelper
|
||||
property alias model: fileTableView.model
|
||||
|
||||
signal selectedFile(var file);
|
||||
signal canceled();
|
||||
resizable: true
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
property var helper: fileDialogHelper
|
||||
property alias model: fileTableView.model
|
||||
property alias filterModel: selectionType.model
|
||||
property alias folder: model.folder
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
@ -120,6 +135,7 @@ ModalWindow {
|
|||
onDoubleClicked: navigateToRow(row);
|
||||
model: FolderListModel {
|
||||
id: model
|
||||
nameFilters: selectionType.currentFilter
|
||||
showDirsFirst: true
|
||||
showDotAndDotDot: false
|
||||
showFiles: !root.selectDirectory
|
||||
|
@ -157,12 +173,10 @@ ModalWindow {
|
|||
readOnly: true
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
FileTypeSelection {
|
||||
id: selectionType
|
||||
anchors { bottom: buttonRow.top; bottomMargin: 8; right: parent.right; rightMargin: 8; left: buttonRow.left }
|
||||
visible: !selectDirectory
|
||||
model: ListModel { ListElement { text: "All Files (*.*)"; filter: "*.*" } }
|
||||
// onCurrentIndexChanged: model.nameFilters = [ filterModel.get(currentIndex).filter ]
|
||||
KeyNavigation.left: fileTableView
|
||||
KeyNavigation.right: openButton
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs
|
|||
import "../controls"
|
||||
import "../styles"
|
||||
import "../windows"
|
||||
import "messageDialog"
|
||||
|
||||
// FIXME respect default button functionality
|
||||
ModalWindow {
|
||||
|
@ -126,126 +127,23 @@ ModalWindow {
|
|||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
layoutDirection: Qt.RightToLeft
|
||||
anchors { bottom: details.top; right: parent.right; margins: d.spacing * 2; bottomMargin: 0 }
|
||||
Button {
|
||||
id: okButton
|
||||
text: qsTr("OK")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Ok
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Ok)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Ok
|
||||
|
||||
}
|
||||
Button {
|
||||
id: openButton
|
||||
text: qsTr("Open")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Open
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Open)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Open
|
||||
}
|
||||
Button {
|
||||
id: saveButton
|
||||
text: qsTr("Save")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Save
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Save)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Save
|
||||
}
|
||||
Button {
|
||||
id: saveAllButton
|
||||
text: qsTr("Save All")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.SaveAll
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.SaveAll)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.SaveAll
|
||||
}
|
||||
Button {
|
||||
id: retryButton
|
||||
text: qsTr("Retry")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Retry
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Retry)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Retry
|
||||
}
|
||||
Button {
|
||||
id: ignoreButton
|
||||
text: qsTr("Ignore")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Ignore
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Ignore)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Ignore
|
||||
}
|
||||
Button {
|
||||
id: applyButton
|
||||
text: qsTr("Apply")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Apply
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Apply)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Apply
|
||||
}
|
||||
Button {
|
||||
id: yesButton
|
||||
text: qsTr("Yes")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Yes
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Yes)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Yes
|
||||
}
|
||||
Button {
|
||||
id: yesAllButton
|
||||
text: qsTr("Yes to All")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.YesToAll
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.YesToAll)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.YesToAll
|
||||
}
|
||||
Button {
|
||||
id: noButton
|
||||
text: qsTr("No")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.No
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.No)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.No
|
||||
}
|
||||
Button {
|
||||
id: noAllButton
|
||||
text: qsTr("No to All")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.NoToAll
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.NoToAll)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.NoToAll
|
||||
}
|
||||
Button {
|
||||
id: discardButton
|
||||
text: qsTr("Discard")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Discard
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Discard)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Discard
|
||||
}
|
||||
Button {
|
||||
id: resetButton
|
||||
text: qsTr("Reset")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Reset
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Reset)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Reset
|
||||
}
|
||||
Button {
|
||||
id: restoreDefaultsButton
|
||||
text: qsTr("Restore Defaults")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.RestoreDefaults
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.RestoreDefaults)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.RestoreDefaults
|
||||
}
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: qsTr("Cancel")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Cancel
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Cancel)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Cancel
|
||||
}
|
||||
Button {
|
||||
id: abortButton
|
||||
text: qsTr("Abort")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Abort
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Abort)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Abort
|
||||
}
|
||||
Button {
|
||||
id: closeButton
|
||||
text: qsTr("Close")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Close
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Close)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Close
|
||||
}
|
||||
MessageDialogButton { dialog: root; text: qsTr("OK"); button: OriginalDialogs.StandardButton.Ok; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Yes"); button: OriginalDialogs.StandardButton.Yes; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("No"); button: OriginalDialogs.StandardButton.No; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Restore Defaults"); button: OriginalDialogs.StandardButton.RestoreDefaults; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Cancel"); button: OriginalDialogs.StandardButton.Cancel; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Abort"); button: OriginalDialogs.StandardButton.Abort; }
|
||||
MessageDialogButton { dialog: root; text: qsTr("Close"); button: OriginalDialogs.StandardButton.Close; }
|
||||
Button {
|
||||
id: moreButton
|
||||
text: qsTr("Show Details...")
|
||||
|
@ -253,13 +151,7 @@ ModalWindow {
|
|||
}
|
||||
visible: detailedText && detailedText.length > 0
|
||||
}
|
||||
Button {
|
||||
id: helpButton
|
||||
text: qsTr("Help")
|
||||
focus: root.defaultButton === OriginalDialogs.StandardButton.Help
|
||||
onClicked: root.click(OriginalDialogs.StandardButton.Help)
|
||||
visible: root.buttons & OriginalDialogs.StandardButton.Help
|
||||
}
|
||||
MessageDialogButton { dialog: root; text: qsTr("Help"); button: OriginalDialogs.StandardButton.Help; }
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -328,7 +220,7 @@ ModalWindow {
|
|||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
root.click(OriginalDialogs.StandardButton.Ok)
|
||||
root.click(root.defaultButton)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,13 @@ Window {
|
|||
|
||||
sections.push(sectionBuilder.createObject(prefControls, { name: category }));
|
||||
}
|
||||
|
||||
if (sections.length) {
|
||||
sections[0].expanded = true;
|
||||
if (sections.length === 1) {
|
||||
sections[0].collapsable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flickable {
|
||||
|
|
|
@ -16,9 +16,17 @@ ModalWindow {
|
|||
signal selected(var result);
|
||||
signal canceled();
|
||||
|
||||
property alias result: textResult.text
|
||||
property var items;
|
||||
property alias label: mainTextContainer.text
|
||||
property var result;
|
||||
// FIXME not current honored
|
||||
property var current;
|
||||
|
||||
// For text boxes
|
||||
property alias placeholderText: textResult.placeholderText
|
||||
property alias text: mainTextContainer.text
|
||||
|
||||
// For combo boxes
|
||||
property bool editable: true;
|
||||
|
||||
Rectangle {
|
||||
clip: true
|
||||
|
@ -55,10 +63,20 @@ ModalWindow {
|
|||
anchors { top: mainTextContainer.bottom; bottom: buttons.top; left: parent.left; right: parent.right; margins: d.spacing }
|
||||
// FIXME make a text field type that can be bound to a history for autocompletion
|
||||
TextField {
|
||||
focus: true
|
||||
id: textResult
|
||||
focus: items ? false : true
|
||||
visible: items ? false : true
|
||||
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
}
|
||||
|
||||
VrControls.ComboBox {
|
||||
id: comboBox
|
||||
focus: items ? true : false
|
||||
visible: items ? true : false
|
||||
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
model: items ? items : []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Flow {
|
||||
|
@ -86,6 +104,7 @@ ModalWindow {
|
|||
text: qsTr("OK")
|
||||
shortcut: Qt.Key_Return
|
||||
onTriggered: {
|
||||
root.result = items ? comboBox.currentText : textResult.text
|
||||
root.selected(root.result);
|
||||
root.destroy();
|
||||
}
|
||||
|
|
|
@ -72,23 +72,6 @@ Window {
|
|||
scripts.stopAllScripts();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDialogBuilder
|
||||
FileDialog { }
|
||||
}
|
||||
|
||||
function loadFromFile() {
|
||||
var fileDialog = fileDialogBuilder.createObject(desktop, { filterModel: fileFilters });
|
||||
fileDialog.canceled.connect(function(){
|
||||
console.debug("Cancelled file open")
|
||||
})
|
||||
|
||||
fileDialog.selectedFile.connect(function(file){
|
||||
console.debug("Selected " + file)
|
||||
scripts.loadOneScript(file);
|
||||
})
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: "white"
|
||||
anchors.fill: parent
|
||||
|
@ -123,26 +106,42 @@ Window {
|
|||
}
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
clip: true
|
||||
anchors { fill: parent; margins: 0 }
|
||||
|
||||
model: runningScriptsModel
|
||||
|
||||
delegate: Rectangle {
|
||||
id: rectangle
|
||||
clip: true
|
||||
radius: 3
|
||||
anchors { left: parent.left; right: parent.right }
|
||||
|
||||
height: scriptName.height + 12
|
||||
color: index % 2 ? "#ddd" : "#eee"
|
||||
height: scriptName.height + 12 + (ListView.isCurrentItem ? scriptName.height + 6 : 0)
|
||||
color: ListView.isCurrentItem ? "#39f" :
|
||||
index % 2 ? "#ddd" : "#eee"
|
||||
|
||||
Text {
|
||||
anchors { left: parent.left; leftMargin: 4; verticalCenter: parent.verticalCenter }
|
||||
id: scriptName
|
||||
anchors { left: parent.left; leftMargin: 4; top: parent.top; topMargin:6 }
|
||||
text: name
|
||||
}
|
||||
|
||||
Text {
|
||||
id: scriptUrl
|
||||
anchors { left: scriptName.left; right: parent.right; rightMargin: 4; top: scriptName.bottom; topMargin: 6 }
|
||||
text: url
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.currentIndex = index
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenter: scriptName.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
spacing: 4
|
||||
|
@ -184,13 +183,39 @@ Window {
|
|||
anchors.bottom: filterEdit.top
|
||||
anchors.bottomMargin: 8
|
||||
anchors.right: parent.right
|
||||
|
||||
|
||||
Button {
|
||||
text: "from URL";
|
||||
onClicked: ApplicationInterface.loadScriptURLDialog();
|
||||
onClicked: fromUrlTimer.running = true;
|
||||
|
||||
// For some reason trigginer an API that enters
|
||||
// an internal event loop directly from the button clicked
|
||||
// trigger below causes the appliction to behave oddly.
|
||||
// Most likely because the button onClicked handling is never
|
||||
// completed until the function returns.
|
||||
// FIXME find a better way of handling the input dialogs that
|
||||
// doesn't trigger this.
|
||||
Timer {
|
||||
id: fromUrlTimer
|
||||
interval: 5
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: ApplicationInterface.loadScriptURLDialog();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "from Disk"
|
||||
onClicked: loadFromFile();
|
||||
onClicked: fromDiskTimer.running = true;
|
||||
|
||||
Timer {
|
||||
id: fromDiskTimer
|
||||
interval: 5
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: ApplicationInterface.loadDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import QtQuick 2.5
|
||||
|
||||
import "../../controls" as VrControls
|
||||
|
||||
VrControls.ComboBox {
|
||||
id: root
|
||||
property string filtersString: "All Files (*.*)";
|
||||
property var currentFilter: [ "*.*" ];
|
||||
|
||||
// Per http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName the string can contain
|
||||
// multiple filters separated by semicolons
|
||||
// ex: "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
|
||||
model: filtersString.split(';;');
|
||||
|
||||
enabled: model.length > 1
|
||||
|
||||
onCurrentTextChanged: {
|
||||
var globRegex = /\((.*)\)$/
|
||||
var globs = globRegex.exec(currentText);
|
||||
if (!globs[1]) {
|
||||
console.warn("Unable to parse filter " + currentText);
|
||||
return;
|
||||
}
|
||||
currentFilter = globs[1].split(" ");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
Button {
|
||||
property var dialog;
|
||||
property int button: StandardButton.NoButton;
|
||||
|
||||
focus: dialog.defaultButton === button
|
||||
onClicked: dialog.click(button)
|
||||
visible: dialog.buttons & button
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebEngine 1.1
|
||||
|
||||
import "../../windows" as Windows
|
||||
|
@ -11,9 +11,7 @@ Windows.Window {
|
|||
HifiConstants { id: hifi }
|
||||
width: 900; height: 700
|
||||
resizable: true
|
||||
anchors.centerIn: parent
|
||||
modality: Qt.ApplicationModal
|
||||
frame: Windows.ModalFrame {}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -7,6 +7,7 @@ import "."
|
|||
|
||||
Preference {
|
||||
id: root
|
||||
property bool collapsable: true
|
||||
property bool expanded: false
|
||||
property string name: "Header"
|
||||
property real spacing: 8
|
||||
|
@ -42,7 +43,10 @@ Preference {
|
|||
|
||||
VrControls.FontAwesome {
|
||||
id: toggle
|
||||
width: root.collapsable ? height : 0
|
||||
anchors { left: parent.left; top: parent.top; margins: root.spacing }
|
||||
visible: root.collapsable
|
||||
enabled: root.collapsable
|
||||
rotation: root.expanded ? 0 : -90
|
||||
text: "\uf078"
|
||||
Behavior on rotation { PropertyAnimation {} }
|
||||
|
|
124
interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
Normal file
124
interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
Normal file
|
@ -0,0 +1,124 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import "../../windows"
|
||||
import "attachments"
|
||||
|
||||
Window {
|
||||
id: root
|
||||
title: "Edit Attachments"
|
||||
objectName: "AttachmentsDialog"
|
||||
width: 600
|
||||
height: 600
|
||||
resizable: true
|
||||
// User must click OK or cancel to close the window
|
||||
closable: false
|
||||
|
||||
readonly property var originalAttachments: MyAvatar.getAttachmentsVariant();
|
||||
property var attachments: [];
|
||||
|
||||
property var settings: Settings {
|
||||
category: "AttachmentsDialog"
|
||||
property alias x: root.x
|
||||
property alias y: root.y
|
||||
property alias width: root.width
|
||||
property alias height: root.height
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < originalAttachments.length; ++i) {
|
||||
var attachment = originalAttachments[i];
|
||||
root.attachments.push(attachment);
|
||||
listView.model.append({});
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 4
|
||||
|
||||
Rectangle {
|
||||
id: attachmentsBackground
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 }
|
||||
color: "gray"
|
||||
radius: 4
|
||||
|
||||
ScrollView{
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
ListView {
|
||||
id: listView
|
||||
model: ListModel {}
|
||||
delegate: Item {
|
||||
implicitHeight: attachmentView.height + 8;
|
||||
implicitWidth: attachmentView.width;
|
||||
Attachment {
|
||||
id: attachmentView
|
||||
width: scrollView.width
|
||||
attachment: root.attachments[index]
|
||||
onDeleteAttachment: {
|
||||
attachments.splice(index, 1);
|
||||
listView.model.remove(index, 1);
|
||||
}
|
||||
onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments);
|
||||
}
|
||||
}
|
||||
onCountChanged: MyAvatar.setAttachmentsVariant(attachments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: newAttachmentButton
|
||||
anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 }
|
||||
text: "New Attachment"
|
||||
|
||||
onClicked: {
|
||||
var template = {
|
||||
modelUrl: "",
|
||||
translation: { x: 0, y: 0, z: 0 },
|
||||
rotation: { x: 0, y: 0, z: 0 },
|
||||
scale: 1,
|
||||
jointName: MyAvatar.jointNames[0],
|
||||
soft: false
|
||||
};
|
||||
attachments.push(template);
|
||||
listView.model.append({});
|
||||
MyAvatar.setAttachmentsVariant(attachments);
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
spacing: 8
|
||||
anchors { right: parent.right; bottom: parent.bottom; margins: 8 }
|
||||
Button { action: cancelAction }
|
||||
Button { action: okAction }
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction
|
||||
text: "Cancel"
|
||||
onTriggered: {
|
||||
MyAvatar.setAttachmentsVariant(originalAttachments);
|
||||
root.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: okAction
|
||||
text: "OK"
|
||||
onTriggered: {
|
||||
for (var i = 0; i < attachments.length; ++i) {
|
||||
console.log("Attachment " + i + ": " + attachments[i]);
|
||||
}
|
||||
|
||||
MyAvatar.setAttachmentsVariant(attachments);
|
||||
root.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
128
interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml
Normal file
128
interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml
Normal file
|
@ -0,0 +1,128 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.XmlListModel 2.0
|
||||
|
||||
import "../../windows"
|
||||
import "../../js/Utils.js" as Utils
|
||||
import "../models"
|
||||
|
||||
ModalWindow {
|
||||
id: root
|
||||
resizable: true
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
property var result;
|
||||
|
||||
signal selected(var modelUrl);
|
||||
signal canceled();
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "white"
|
||||
|
||||
Item {
|
||||
anchors { fill: parent; margins: 8 }
|
||||
|
||||
TextField {
|
||||
id: filterEdit
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
placeholderText: "filter"
|
||||
onTextChanged: tableView.model.filter = text
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 }
|
||||
model: S3Model{}
|
||||
onCurrentRowChanged: {
|
||||
if (currentRow == -1) {
|
||||
root.result = null;
|
||||
return;
|
||||
}
|
||||
result = model.baseUrl + "/" + model.get(tableView.currentRow).key;
|
||||
}
|
||||
itemDelegate: Component {
|
||||
Item {
|
||||
clip: true
|
||||
Text {
|
||||
x: 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
|
||||
elide: styleData.elideMode
|
||||
text: getText()
|
||||
|
||||
function getText() {
|
||||
switch(styleData.column) {
|
||||
case 1:
|
||||
return Utils.formatSize(styleData.value)
|
||||
default:
|
||||
return styleData.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "name"
|
||||
title: "Name"
|
||||
width: 200
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "size"
|
||||
title: "Size"
|
||||
width: 100
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "modified"
|
||||
title: "Last Modified"
|
||||
width: 200
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: tableView.model.status !== XmlListModel.Ready
|
||||
color: "#7fffffff"
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
width: 48; height: 48
|
||||
running: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors { right: parent.right; bottom: parent.bottom }
|
||||
Button { action: acceptAction }
|
||||
Button { action: cancelAction }
|
||||
}
|
||||
|
||||
Action {
|
||||
id: acceptAction
|
||||
text: qsTr("OK")
|
||||
enabled: root.result ? true : false
|
||||
shortcut: Qt.Key_Return
|
||||
onTriggered: {
|
||||
root.selected(root.result);
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction
|
||||
text: qsTr("Cancel")
|
||||
shortcut: Qt.Key_Escape
|
||||
onTriggered: {
|
||||
root.canceled();
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ PreferencesDialog {
|
|||
id: root
|
||||
objectName: "GeneralPreferencesDialog"
|
||||
title: "General Preferences"
|
||||
showCategories: ["Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers"]
|
||||
showCategories: ["Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Graphics"]
|
||||
property var settings: Settings {
|
||||
category: root.objectName
|
||||
property alias x: root.x
|
||||
|
|
128
interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml
Normal file
128
interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml
Normal file
|
@ -0,0 +1,128 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.XmlListModel 2.0
|
||||
|
||||
import "../../windows"
|
||||
import "../../js/Utils.js" as Utils
|
||||
import "../models"
|
||||
|
||||
ModalWindow {
|
||||
id: root
|
||||
resizable: true
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
property var result;
|
||||
|
||||
signal selected(var modelUrl);
|
||||
signal canceled();
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "white"
|
||||
|
||||
Item {
|
||||
anchors { fill: parent; margins: 8 }
|
||||
|
||||
TextField {
|
||||
id: filterEdit
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
placeholderText: "filter"
|
||||
onTextChanged: tableView.model.filter = text
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 }
|
||||
model: S3Model{}
|
||||
onCurrentRowChanged: {
|
||||
if (currentRow == -1) {
|
||||
root.result = null;
|
||||
return;
|
||||
}
|
||||
result = model.baseUrl + "/" + model.get(tableView.currentRow).key;
|
||||
}
|
||||
itemDelegate: Component {
|
||||
Item {
|
||||
clip: true
|
||||
Text {
|
||||
x: 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
|
||||
elide: styleData.elideMode
|
||||
text: getText()
|
||||
|
||||
function getText() {
|
||||
switch(styleData.column) {
|
||||
case 1:
|
||||
return Utils.formatSize(styleData.value)
|
||||
default:
|
||||
return styleData.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "name"
|
||||
title: "Name"
|
||||
width: 200
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "size"
|
||||
title: "Size"
|
||||
width: 100
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "modified"
|
||||
title: "Last Modified"
|
||||
width: 200
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: tableView.model.status !== XmlListModel.Ready
|
||||
color: "#7fffffff"
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
width: 48; height: 48
|
||||
running: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors { right: parent.right; bottom: parent.bottom }
|
||||
Button { action: acceptAction }
|
||||
Button { action: cancelAction }
|
||||
}
|
||||
|
||||
Action {
|
||||
id: acceptAction
|
||||
text: qsTr("OK")
|
||||
enabled: root.result ? true : false
|
||||
shortcut: Qt.Key_Return
|
||||
onTriggered: {
|
||||
root.selected(root.result);
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction
|
||||
text: qsTr("Cancel")
|
||||
shortcut: Qt.Key_Escape
|
||||
onTriggered: {
|
||||
root.canceled();
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
165
interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
Normal file
165
interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
Normal file
|
@ -0,0 +1,165 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "../../../windows"
|
||||
import "../../../controls" as VrControls
|
||||
import "."
|
||||
import ".."
|
||||
|
||||
Item {
|
||||
height: column.height + 2 * 8
|
||||
|
||||
property var attachment;
|
||||
|
||||
signal deleteAttachment(var attachment);
|
||||
signal updateAttachment();
|
||||
property bool completed: false;
|
||||
|
||||
Rectangle { color: "white"; anchors.fill: parent; radius: 4 }
|
||||
|
||||
Component.onCompleted: {
|
||||
completed = true;
|
||||
}
|
||||
|
||||
Column {
|
||||
y: 8
|
||||
id: column
|
||||
anchors { left: parent.left; right: parent.right; margins: 8 }
|
||||
spacing: 8
|
||||
|
||||
Item {
|
||||
height: modelChooserButton.height
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter }
|
||||
TextField {
|
||||
id: modelUrl;
|
||||
height: jointChooser.height;
|
||||
anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left }
|
||||
text: attachment ? attachment.modelUrl : ""
|
||||
onTextChanged: {
|
||||
if (completed && attachment && attachment.modelUrl !== text) {
|
||||
attachment.modelUrl = text;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
id: modelChooserButton;
|
||||
text: "Choose";
|
||||
anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter }
|
||||
Component {
|
||||
id: modelBrowserBuiler;
|
||||
ModelBrowserDialog {}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var browser = modelBrowserBuiler.createObject(desktop);
|
||||
browser.selected.connect(function(newModelUrl){
|
||||
modelUrl.text = newModelUrl;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: jointChooser.height
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
Text {
|
||||
id: jointLabel;
|
||||
width: 80;
|
||||
text: "Joint:";
|
||||
anchors.verticalCenter: jointChooser.verticalCenter;
|
||||
}
|
||||
VrControls.ComboBox {
|
||||
id: jointChooser;
|
||||
anchors { left: jointLabel.right; leftMargin: 8; right: parent.right }
|
||||
model: MyAvatar.jointNames
|
||||
currentIndex: attachment ? model.indexOf(attachment.jointName) : -1
|
||||
onCurrentIndexChanged: {
|
||||
if (completed && attachment && currentIndex != -1 && currentText && currentText !== attachment.jointName) {
|
||||
attachment.jointName = currentText;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: translation.height
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
Text { id: translationLabel; width: 80; text: "Translation:"; anchors.verticalCenter: translation.verticalCenter; }
|
||||
Translation {
|
||||
id: translation;
|
||||
anchors { left: translationLabel.right; leftMargin: 8; right: parent.right }
|
||||
vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0};
|
||||
onValueChanged: {
|
||||
if (completed && attachment) {
|
||||
attachment.translation = vector;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: rotation.height
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
Text { id: rotationLabel; width: 80; text: "Rotation:"; anchors.verticalCenter: rotation.verticalCenter; }
|
||||
Rotation {
|
||||
id: rotation;
|
||||
anchors { left: rotationLabel.right; leftMargin: 8; right: parent.right }
|
||||
vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0};
|
||||
onValueChanged: {
|
||||
if (completed && attachment) {
|
||||
attachment.rotation = vector;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: scaleSpinner.height
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
Text { id: scaleLabel; width: 80; text: "Scale:"; anchors.verticalCenter: scale.verticalCenter; }
|
||||
SpinBox {
|
||||
id: scaleSpinner;
|
||||
anchors { left: scaleLabel.right; leftMargin: 8; right: parent.right }
|
||||
decimals: 1;
|
||||
minimumValue: 0.1
|
||||
maximumValue: 10
|
||||
stepSize: 0.1;
|
||||
value: attachment ? attachment.scale : 1.0
|
||||
onValueChanged: {
|
||||
if (completed && attachment && attachment.scale !== value) {
|
||||
attachment.scale = value;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: soft.height
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
Text { id: softLabel; width: 80; text: "Is soft:"; anchors.verticalCenter: soft.verticalCenter; }
|
||||
CheckBox {
|
||||
id: soft;
|
||||
anchors { left: softLabel.right; leftMargin: 8; right: parent.right }
|
||||
checked: attachment ? attachment.soft : false
|
||||
onCheckedChanged: {
|
||||
if (completed && attachment && attachment.soft !== checked) {
|
||||
attachment.soft = checked;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
text: "Delete"
|
||||
onClicked: deleteAttachment(root.attachment);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import "."
|
||||
|
||||
Vector3 {
|
||||
decimals: 1;
|
||||
stepSize: 1;
|
||||
maximumValue: 180
|
||||
minimumValue: -180
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import "."
|
||||
|
||||
Vector3 {
|
||||
decimals: 2;
|
||||
stepSize: 0.01;
|
||||
maximumValue: 10
|
||||
minimumValue: -10
|
||||
}
|
||||
|
71
interface/resources/qml/hifi/dialogs/attachments/Vector3.qml
Normal file
71
interface/resources/qml/hifi/dialogs/attachments/Vector3.qml
Normal file
|
@ -0,0 +1,71 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitHeight: xspinner.height
|
||||
readonly property real spacing: 8
|
||||
property real spinboxWidth: (width / 3) - spacing
|
||||
property var vector;
|
||||
property real decimals: 0
|
||||
property real stepSize: 1
|
||||
property real maximumValue: 99
|
||||
property real minimumValue: 0
|
||||
|
||||
signal valueChanged();
|
||||
|
||||
SpinBox {
|
||||
id: xspinner
|
||||
width: root.spinboxWidth
|
||||
anchors { left: parent.left }
|
||||
value: root.vector.x
|
||||
|
||||
decimals: root.decimals
|
||||
stepSize: root.stepSize
|
||||
maximumValue: root.maximumValue
|
||||
minimumValue: root.minimumValue
|
||||
onValueChanged: {
|
||||
if (value !== vector.x) {
|
||||
vector.x = value
|
||||
root.valueChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: yspinner
|
||||
width: root.spinboxWidth
|
||||
anchors { horizontalCenter: parent.horizontalCenter }
|
||||
value: root.vector.y
|
||||
|
||||
decimals: root.decimals
|
||||
stepSize: root.stepSize
|
||||
maximumValue: root.maximumValue
|
||||
minimumValue: root.minimumValue
|
||||
onValueChanged: {
|
||||
if (value !== vector.y) {
|
||||
vector.y = value
|
||||
root.valueChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: zspinner
|
||||
width: root.spinboxWidth
|
||||
anchors { right: parent.right; }
|
||||
value: root.vector.z
|
||||
|
||||
decimals: root.decimals
|
||||
stepSize: root.stepSize
|
||||
maximumValue: root.maximumValue
|
||||
minimumValue: root.minimumValue
|
||||
onValueChanged: {
|
||||
if (value !== vector.z) {
|
||||
vector.z = value
|
||||
root.valueChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
41
interface/resources/qml/hifi/models/S3Model.qml
Normal file
41
interface/resources/qml/hifi/models/S3Model.qml
Normal file
|
@ -0,0 +1,41 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.XmlListModel 2.0
|
||||
|
||||
//http://s3.amazonaws.com/hifi-public?prefix=models/attachments
|
||||
/*
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Contents>
|
||||
<Key>models/attachments/guitar.fst</Key>
|
||||
<LastModified>2015-11-10T00:28:22.000Z</LastModified>
|
||||
<ETag>"236c00c4802ba9c2605cabd5601d138e"</ETag>
|
||||
<Size>2992</Size>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
</Contents>
|
||||
</ListBucketResult>
|
||||
*/
|
||||
|
||||
// FIXME how to deal with truncated results? Store the marker?
|
||||
XmlListModel {
|
||||
id: xmlModel
|
||||
property string prefix: "models/attachments/"
|
||||
property string extension: "fst"
|
||||
property string filter;
|
||||
|
||||
readonly property string realPrefix: prefix.match('.*/$') ? prefix : (prefix + "/")
|
||||
readonly property string nameRegex: realPrefix + (filter ? (".*" + filter) : "") + ".*\." + extension
|
||||
readonly property string nameQuery: "Key/substring-before(substring-after(string(), '" + prefix + "'), '." + extension + "')"
|
||||
readonly property string baseUrl: "http://s3.amazonaws.com/hifi-public"
|
||||
|
||||
// FIXME need to urlencode prefix?
|
||||
source: baseUrl + "?prefix=" + realPrefix
|
||||
query: "/ListBucketResult/Contents[matches(Key, '" + nameRegex + "')]"
|
||||
namespaceDeclarations: "declare default element namespace 'http://s3.amazonaws.com/doc/2006-03-01/';"
|
||||
|
||||
XmlRole { name: "name"; query: nameQuery }
|
||||
XmlRole { name: "size"; query: "Size/string()" }
|
||||
XmlRole { name: "tag"; query: "ETag/string()" }
|
||||
XmlRole { name: "modified"; query: "LastModified/string()" }
|
||||
XmlRole { name: "key"; query: "Key/string()" }
|
||||
}
|
||||
|
78
interface/resources/qml/hifi/overlays/ImageOverlay.qml
Normal file
78
interface/resources/qml/hifi/overlays/ImageOverlay.qml
Normal file
|
@ -0,0 +1,78 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
import "."
|
||||
|
||||
Overlay {
|
||||
id: root
|
||||
|
||||
Image {
|
||||
id: image
|
||||
property bool scaleFix: true;
|
||||
property real xOffset: 0
|
||||
property real yOffset: 0
|
||||
property real imageScale: 1.0
|
||||
property var resizer: Timer {
|
||||
interval: 50
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
var targetAspect = root.width / root.height;
|
||||
var sourceAspect = image.sourceSize.width / image.sourceSize.height;
|
||||
if (sourceAspect <= targetAspect) {
|
||||
if (root.width === image.sourceSize.width) {
|
||||
return;
|
||||
}
|
||||
image.imageScale = root.width / image.sourceSize.width;
|
||||
} else if (sourceAspect > targetAspect){
|
||||
if (root.height === image.sourceSize.height) {
|
||||
return;
|
||||
}
|
||||
image.imageScale = root.height / image.sourceSize.height;
|
||||
}
|
||||
image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale);
|
||||
}
|
||||
}
|
||||
x: -1 * xOffset * imageScale
|
||||
y: -1 * yOffset * imageScale
|
||||
|
||||
onSourceSizeChanged: {
|
||||
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
|
||||
scaleFix = false;
|
||||
resizer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateSubImage(subImage) {
|
||||
var keys = Object.keys(subImage);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i];
|
||||
var value = subImage[key];
|
||||
switch (key) {
|
||||
case "x": image.xOffset = value; break;
|
||||
case "y": image.yOffset = value; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updatePropertiesFromScript(properties) {
|
||||
var keys = Object.keys(properties);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i];
|
||||
var value = properties[key];
|
||||
switch (key) {
|
||||
case "height": root.height = value; break;
|
||||
case "width": root.width = value; break;
|
||||
case "x": root.x = value; break;
|
||||
case "y": root.y = value; break;
|
||||
case "visible": root.visible = value; break;
|
||||
case "alpha": root.opacity = value; break;
|
||||
case "imageURL": image.source = value; break;
|
||||
case "subImage": updateSubImage(value); break;
|
||||
default: console.log("OVERLAY Unhandled image property " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
interface/resources/qml/hifi/overlays/Overlay.qml
Normal file
30
interface/resources/qml/hifi/overlays/Overlay.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
Item {
|
||||
id: root
|
||||
clip: true
|
||||
|
||||
property int dumpDepth: 0;
|
||||
|
||||
|
||||
function dumpObject(object) {
|
||||
var keys = Object.keys(object);
|
||||
var tabsString = "";
|
||||
for (var j = 0; j < dumpDepth; ++j) {
|
||||
tabsString = tabsString + "\t";
|
||||
}
|
||||
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i];
|
||||
var value = object[key];
|
||||
console.log(tabsString + "OVERLAY Key " + key + " (" + typeof(value) + "): " + value);
|
||||
if (typeof(value) === "object") {
|
||||
++dumpDepth;
|
||||
dumpObject(value)
|
||||
--dumpDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
interface/resources/qml/hifi/overlays/RectangleOverlay.qml
Normal file
38
interface/resources/qml/hifi/overlays/RectangleOverlay.qml
Normal file
|
@ -0,0 +1,38 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
import "."
|
||||
|
||||
Overlay {
|
||||
id: root
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
}
|
||||
|
||||
function updatePropertiesFromScript(properties) {
|
||||
var keys = Object.keys(properties);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i];
|
||||
var value = properties[key];
|
||||
console.log("OVERLAY rectangle property " + key + " set to value " + value);
|
||||
switch (key) {
|
||||
case "height": root.height = value; break;
|
||||
case "width": root.width = value; break;
|
||||
case "x": root.x = value; break;
|
||||
case "y": root.y = value; break;
|
||||
case "visible": root.visible = value; break;
|
||||
case "alpha": rectangle.color.a = value; break;
|
||||
case "color": rectangle.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.color.a); break;
|
||||
case "borderAlpha": rectangle.border.color.a = value; break;
|
||||
case "borderColor": rectangle.border.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.border.color.a); break;
|
||||
case "borderWidth": rectangle.border.width = value; break;
|
||||
case "radius": rectangle.radius = value; break;
|
||||
default: console.log("OVERLAY Unhandled rectangle property " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
54
interface/resources/qml/hifi/overlays/TextOverlay.qml
Normal file
54
interface/resources/qml/hifi/overlays/TextOverlay.qml
Normal file
|
@ -0,0 +1,54 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
import "."
|
||||
|
||||
Overlay {
|
||||
id: root
|
||||
clip: true
|
||||
Rectangle {
|
||||
id: background;
|
||||
anchors.fill: parent
|
||||
color: "#B2000000"
|
||||
|
||||
Text {
|
||||
id: textField;
|
||||
anchors { fill: parent; bottomMargin: textField.anchors.topMargin; rightMargin: textField.anchors.leftMargin; }
|
||||
objectName: "textElement"
|
||||
color: "white"
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.family: "Helvetica"
|
||||
font.pixelSize: 18
|
||||
lineHeight: 18
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updatePropertiesFromScript(properties) {
|
||||
var keys = Object.keys(properties);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i];
|
||||
var value = properties[key];
|
||||
switch (key) {
|
||||
case "height": root.height = value; break;
|
||||
case "width": root.width = value; break;
|
||||
case "x": root.x = value; break;
|
||||
case "y": root.y = value; break;
|
||||
case "visible": root.visible = value; break;
|
||||
case "alpha": textField.color.a = value; break;
|
||||
case "margin": textField.anchors.margins = value; break;
|
||||
case "leftMargin": textField.anchors.leftMargin = value; break;
|
||||
case "topMargin": textField.anchors.topMargin = value; break;
|
||||
case "color": // fall through
|
||||
case "textColor": textField.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, textField.color.a); break;
|
||||
case "text": textField.text = value; break;
|
||||
case "backgroundAlpha": background.color = Qt.rgba(background.color.r, background.color.g, background.color.b, value); break;
|
||||
case "backgroundColor": background.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, background.color.a); break;
|
||||
case "font": textField.font.pixelSize = value.size; break;
|
||||
case "lineHeight": textField.lineHeight = value; break;
|
||||
default:
|
||||
console.log("OVERLAY text unhandled property " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,3 +14,17 @@ function randomPosition(min, max) {
|
|||
Math.random() * (max.x - min.x),
|
||||
Math.random() * (max.y - min.y));
|
||||
}
|
||||
|
||||
function formatSize(size) {
|
||||
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
|
||||
var suffixIndex = 0
|
||||
while ((size / 1024.0) > 1.1) {
|
||||
size /= 1024.0;
|
||||
++suffixIndex;
|
||||
}
|
||||
|
||||
size = Math.round(size*1000)/1000;
|
||||
size = size.toLocaleString()
|
||||
|
||||
return size + " " + suffixes[suffixIndex];
|
||||
}
|
||||
|
|
|
@ -20,12 +20,8 @@ Frame {
|
|||
|
||||
Row {
|
||||
id: controlsRow
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: iconSize
|
||||
anchors.topMargin: iconSize / 2
|
||||
anchors { right: parent.right; top: parent.top; rightMargin: iconSize; topMargin: iconSize / 2; }
|
||||
spacing: iconSize / 4
|
||||
|
||||
FontAwesome {
|
||||
visible: false
|
||||
text: "\uf08d"
|
||||
|
@ -54,6 +50,18 @@ Frame {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: titleText
|
||||
anchors { left: parent.left; leftMargin: iconSize; right: controlsRow.left; rightMargin: iconSize; top: parent.top; topMargin: iconSize / 2; }
|
||||
text: window.title
|
||||
elide: Text.ElideRight
|
||||
font.bold: true
|
||||
color: window.focus ? "white" : "gray"
|
||||
style: Text.Outline;
|
||||
styleColor: "black"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ Item {
|
|||
id: debugZ
|
||||
visible: DebugQML
|
||||
text: window ? "Z: " + window.z : ""
|
||||
y: -height
|
||||
y: window.height + 4
|
||||
}
|
||||
|
||||
function deltaSize(dx, dy) {
|
||||
|
|
|
@ -13,5 +13,16 @@ Frame {
|
|||
color: "#7f7f7f7f";
|
||||
radius: 3;
|
||||
}
|
||||
|
||||
Text {
|
||||
y: -implicitHeight - iconSize / 2
|
||||
text: window.title
|
||||
elide: Text.ElideRight
|
||||
font.bold: true
|
||||
color: window.focus ? "white" : "gray"
|
||||
style: Text.Outline;
|
||||
styleColor: "black"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,6 @@
|
|||
#include <RenderShadowTask.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <RenderScriptingInterface.h>
|
||||
#include <SceneScriptingInterface.h>
|
||||
#include <RecordingScriptingInterface.h>
|
||||
#include <ScriptCache.h>
|
||||
|
@ -363,7 +362,6 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
#endif
|
||||
DependencyManager::set<DiscoverabilityManager>();
|
||||
DependencyManager::set<SceneScriptingInterface>();
|
||||
DependencyManager::set<RenderScriptingInterface>();
|
||||
DependencyManager::set<OffscreenUi>();
|
||||
DependencyManager::set<AutoUpdater>();
|
||||
DependencyManager::set<PathUtils>();
|
||||
|
@ -408,6 +406,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_entityClipboard(new EntityTree()),
|
||||
_lastQueriedTime(usecTimestampNow()),
|
||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
|
||||
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_scaleMirror(1.0f),
|
||||
_rotateMirror(0.0f),
|
||||
|
@ -675,12 +674,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_offscreenContext->makeCurrent();
|
||||
initializeGL();
|
||||
|
||||
// Start rendering
|
||||
render::CullFunctor cullFunctor = LODManager::shouldRender;
|
||||
_renderEngine->addTask(make_shared<RenderShadowTask>(cullFunctor));
|
||||
_renderEngine->addTask(make_shared<RenderDeferredTask>(cullFunctor));
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// Tell our entity edit sender about our known jurisdictions
|
||||
|
@ -1151,9 +1144,17 @@ void Application::initializeGL() {
|
|||
initDisplay();
|
||||
qCDebug(interfaceapp, "Initialized Display.");
|
||||
|
||||
// Set up the render engine
|
||||
render::CullFunctor cullFunctor = LODManager::shouldRender;
|
||||
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
|
||||
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", cullFunctor);
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
// TODO: Load a cached config file
|
||||
|
||||
// The UI can't be created until the primary OpenGL
|
||||
// context is created, because it needs to share
|
||||
// texture resources
|
||||
// Needs to happen AFTER the render engine initialization to access its configuration
|
||||
initializeUi();
|
||||
qCDebug(interfaceapp, "Initialized Offscreen UI.");
|
||||
_offscreenContext->makeCurrent();
|
||||
|
@ -1186,6 +1187,7 @@ void Application::initializeUi() {
|
|||
UpdateDialog::registerType();
|
||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_offscreenContext->getContext());
|
||||
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||
|
@ -1250,7 +1252,7 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
|
||||
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||
|
@ -1842,6 +1844,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_X:
|
||||
if (isShifted && isMeta) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->getRootContext()->engine()->clearComponentCache();
|
||||
OffscreenUi::information("Debugging", "Component cache cleared");
|
||||
// placeholder for dialogs being converted to QML.
|
||||
}
|
||||
break;
|
||||
|
@ -3765,29 +3770,13 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
{
|
||||
PerformanceTimer perfTimer("EngineRun");
|
||||
|
||||
auto renderInterface = DependencyManager::get<RenderScriptingInterface>();
|
||||
auto renderContext = renderInterface->getRenderContext();
|
||||
|
||||
renderArgs->_viewFrustum = getDisplayViewFrustum();
|
||||
renderContext.setArgs(renderArgs);
|
||||
|
||||
bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||
bool shadowStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugShadows);
|
||||
bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||
bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned);
|
||||
renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus, shadowStatus);
|
||||
|
||||
_renderEngine->setRenderContext(renderContext);
|
||||
_renderEngine->getRenderContext()->args = renderArgs;
|
||||
|
||||
// Before the deferred pass, let's try to use the render engine
|
||||
myAvatar->startRenderRun();
|
||||
_renderEngine->run();
|
||||
myAvatar->endRenderRun();
|
||||
|
||||
auto engineContext = _renderEngine->getRenderContext();
|
||||
renderInterface->setItemCounts(engineContext->getItemsConfig());
|
||||
renderInterface->setJobGPUTimes(engineContext->getAmbientOcclusion().gpuTime);
|
||||
|
||||
}
|
||||
|
||||
activeRenderingThread = nullptr;
|
||||
|
@ -4196,7 +4185,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0);
|
||||
|
||||
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
}
|
||||
|
@ -4513,14 +4502,24 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject
|
|||
}
|
||||
|
||||
void Application::loadDialog() {
|
||||
// To be migratd to QML
|
||||
QString fileNameString = QFileDialog::getOpenFileName(
|
||||
_glWidget, tr("Open Script"), "", tr("JavaScript Files (*.js)"));
|
||||
if (!fileNameString.isEmpty()) {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString fileNameString = OffscreenUi::getOpenFileName(
|
||||
_glWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)"));
|
||||
if (!fileNameString.isEmpty() && QFile(fileNameString).exists()) {
|
||||
setPreviousScriptLocation(QFileInfo(fileNameString).absolutePath());
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(fileNameString, true, false, false, true); // Don't load from cache
|
||||
}
|
||||
}
|
||||
|
||||
QString Application::getPreviousScriptLocation() {
|
||||
QString result = _previousScriptLocation.get();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Application::setPreviousScriptLocation(const QString& location) {
|
||||
_previousScriptLocation.set(location);
|
||||
}
|
||||
|
||||
void Application::loadScriptURLDialog() {
|
||||
auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL");
|
||||
if (!newScript.isEmpty()) {
|
||||
|
|
|
@ -103,6 +103,9 @@ public:
|
|||
|
||||
void postLambdaEvent(std::function<void()> f) override;
|
||||
|
||||
QString getPreviousScriptLocation();
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
|
||||
void initializeGL();
|
||||
void initializeUi();
|
||||
void paintGL();
|
||||
|
@ -431,6 +434,7 @@ private:
|
|||
Camera _mirrorCamera; // Cammera for mirror view
|
||||
QRect _mirrorViewRect;
|
||||
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
Setting::Handle<float> _fieldOfView;
|
||||
|
||||
float _scaleMirror;
|
||||
|
|
|
@ -20,12 +20,14 @@
|
|||
|
||||
#include <AddressManager.h>
|
||||
#include <Application.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#include "Bookmarks.h"
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
Bookmarks::Bookmarks() {
|
||||
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME;
|
||||
|
@ -104,18 +106,13 @@ void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
|||
}
|
||||
|
||||
void Bookmarks::bookmarkLocation() {
|
||||
QInputDialog bookmarkLocationDialog(qApp->getWindow());
|
||||
bookmarkLocationDialog.setWindowTitle("Bookmark Location");
|
||||
bookmarkLocationDialog.setLabelText("Name:");
|
||||
bookmarkLocationDialog.setInputMode(QInputDialog::TextInput);
|
||||
bookmarkLocationDialog.resize(400, 200);
|
||||
|
||||
if (bookmarkLocationDialog.exec() == QDialog::Rejected) {
|
||||
bool ok = false;
|
||||
auto bookmarkName = OffscreenUi::getText(nullptr, "Bookmark Location", "Name:", QLineEdit::Normal, QString(), &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString bookmarkName = bookmarkLocationDialog.textValue().trimmed();
|
||||
bookmarkName = bookmarkName.replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -125,13 +122,14 @@ void Bookmarks::bookmarkLocation() {
|
|||
|
||||
Menu* menubar = Menu::getInstance();
|
||||
if (contains(bookmarkName)) {
|
||||
QMessageBox duplicateBookmarkMessage;
|
||||
duplicateBookmarkMessage.setIcon(QMessageBox::Warning);
|
||||
duplicateBookmarkMessage.setText("The bookmark name you entered already exists in your list.");
|
||||
duplicateBookmarkMessage.setInformativeText("Would you like to overwrite it?");
|
||||
duplicateBookmarkMessage.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
duplicateBookmarkMessage.setDefaultButton(QMessageBox::Yes);
|
||||
if (duplicateBookmarkMessage.exec() == QMessageBox::No) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto duplicateBookmarkMessage = offscreenUi->createMessageBox(QMessageBox::Warning, "Duplicate Bookmark",
|
||||
"The bookmark name you entered already exists in your list.",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
|
||||
|
||||
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
|
||||
if (result != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
removeLocationFromMenu(menubar, bookmarkName);
|
||||
|
@ -157,19 +155,13 @@ void Bookmarks::deleteBookmark() {
|
|||
bookmarkList.append(menuItems[i]->text());
|
||||
}
|
||||
|
||||
QInputDialog deleteBookmarkDialog(qApp->getWindow());
|
||||
deleteBookmarkDialog.setWindowTitle("Delete Bookmark");
|
||||
deleteBookmarkDialog.setLabelText("Select the bookmark to delete");
|
||||
deleteBookmarkDialog.resize(400, 400);
|
||||
deleteBookmarkDialog.setOption(QInputDialog::UseListViewForComboBoxItems);
|
||||
deleteBookmarkDialog.setComboBoxItems(bookmarkList);
|
||||
deleteBookmarkDialog.setOkButtonText("Delete");
|
||||
|
||||
if (deleteBookmarkDialog.exec() == QDialog::Rejected) {
|
||||
bool ok = false;
|
||||
auto bookmarkName = OffscreenUi::getItem(nullptr, "Delete Bookmark", "Select the bookmark to delete", bookmarkList, 0, false, &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString bookmarkName = deleteBookmarkDialog.textValue().trimmed();
|
||||
bookmarkName = bookmarkName.trimmed();
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "devices/Faceshift.h"
|
||||
#include "input-plugins/SpacemouseManager.h"
|
||||
#include "MainWindow.h"
|
||||
#include "render/DrawStatus.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "ui/AssetUploadDialogFactory.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
|
@ -70,8 +71,8 @@ Menu::Menu() {
|
|||
}
|
||||
|
||||
// File > Update -- FIXME: needs implementation
|
||||
auto updateAction = addActionToQMenuAndActionHash(fileMenu, "Update");
|
||||
updateAction->setDisabled(true);
|
||||
auto action = addActionToQMenuAndActionHash(fileMenu, "Update");
|
||||
action->setDisabled(true);
|
||||
|
||||
// File > Help
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
|
||||
|
@ -166,8 +167,11 @@ Menu::Menu() {
|
|||
QObject* avatar = avatarManager->getMyAvatar();
|
||||
|
||||
// Avatar > Attachments...
|
||||
addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments, 0,
|
||||
dialogsManager.data(), SLOT(editAttachments()));
|
||||
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
DependencyManager::get<OffscreenUi>()->show(QString("hifi/dialogs/AttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
});
|
||||
|
||||
|
||||
// Avatar > Size
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
|
@ -285,7 +289,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
// Settings > General...
|
||||
auto action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
DependencyManager::get<OffscreenUi>()->toggle(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), "GeneralPreferencesDialog");
|
||||
});
|
||||
|
@ -333,9 +337,6 @@ Menu::Menu() {
|
|||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugShadows);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true);
|
||||
|
||||
// Developer > Render > Ambient Light
|
||||
|
@ -479,6 +480,10 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false,
|
||||
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
||||
avatar, SLOT(setEnableInverseKinematics(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
|
@ -587,7 +592,11 @@ Menu::Menu() {
|
|||
|
||||
// Developer > Physics >>>
|
||||
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned);
|
||||
{
|
||||
auto drawStatusConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<render::DrawStatus>();
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned,
|
||||
0, false, drawStatusConfig, SLOT(setShowNetwork(bool)));
|
||||
}
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
|
||||
|
||||
// Developer > Display Crash Options
|
||||
|
|
|
@ -152,7 +152,6 @@ namespace MenuOption {
|
|||
const QString AnimDebugDrawAnimPose = "Debug Draw Animation";
|
||||
const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose";
|
||||
const QString AnimDebugDrawPosition= "Debug Draw Position";
|
||||
const QString Antialiasing = "Antialiasing";
|
||||
const QString AssetMigration = "ATP Asset Migration";
|
||||
const QString Attachments = "Attachments...";
|
||||
const QString AudioNetworkStats = "Audio Network Stats";
|
||||
|
@ -186,8 +185,6 @@ namespace MenuOption {
|
|||
const QString CopyPath = "Copy Path to Clipboard";
|
||||
const QString CoupleEyelids = "Couple Eyelids";
|
||||
const QString CrashInterface = "Crash Interface";
|
||||
const QString DebugShadows = "Shadows";
|
||||
const QString DebugAmbientOcclusion = "Ambient Occlusion";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DeleteBookmark = "Delete Bookmark...";
|
||||
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||
|
@ -208,6 +205,7 @@ namespace MenuOption {
|
|||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
|
@ -247,8 +245,8 @@ namespace MenuOption {
|
|||
const QString OutputMenu = "Display";
|
||||
const QString PackageModel = "Package Model...";
|
||||
const QString Pair = "Pair";
|
||||
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
|
||||
const QString PhysicsShowHulls = "Draw Collision Hulls";
|
||||
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
|
||||
const QString PipelineWarnings = "Log Render Pipeline Warnings";
|
||||
const QString Preferences = "General...";
|
||||
const QString Quit = "Quit";
|
||||
|
@ -302,6 +300,7 @@ namespace MenuOption {
|
|||
const QString UploadAsset = "Upload File to Asset Server";
|
||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||
const QString UseCamera = "Use Camera";
|
||||
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
|
||||
const QString VelocityFilter = "Velocity Filter";
|
||||
const QString VisibleToEveryone = "Everyone";
|
||||
const QString VisibleToFriends = "Friends";
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <TextRenderer3D.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <AnimDebugDraw.h>
|
||||
#include <AnimClip.h>
|
||||
#include <recording/Deck.h>
|
||||
#include <recording/Recorder.h>
|
||||
#include <recording/Clip.h>
|
||||
|
@ -49,6 +50,8 @@
|
|||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "DebugDraw.h"
|
||||
#include "EntityEditPacketSender.h"
|
||||
#include "MovingEntitiesOperator.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -362,6 +365,37 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
// consider updating our billboard
|
||||
maybeUpdateBillboard();
|
||||
|
||||
locationChanged();
|
||||
// if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server.
|
||||
EntityTreeRenderer* entityTreeRenderer = qApp->getEntities();
|
||||
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
||||
if (entityTree) {
|
||||
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
|
||||
MovingEntitiesOperator moveOperator(entityTree);
|
||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||
// if the queryBox has changed, tell the entity-server
|
||||
if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||
bool success;
|
||||
AACube newCube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
moveOperator.addEntityToMoveList(entity, newCube);
|
||||
}
|
||||
if (packetSender) {
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
properties.setQueryAACubeDirty();
|
||||
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties);
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
}
|
||||
}
|
||||
});
|
||||
// also update the position of children in our local octree
|
||||
if (moveOperator.hasMovingEntities()) {
|
||||
PerformanceTimer perfTimer("recurseTreeWithOperator");
|
||||
entityTree->recurseTreeWithOperator(&moveOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
|
||||
|
@ -647,6 +681,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
|||
_skeletonModel.setVisibleInScene(isEnabled, scene);
|
||||
}
|
||||
|
||||
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
|
||||
AnimClip::usePreAndPostPoseFromAnim = isEnabled;
|
||||
reset(true);
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
||||
_rig->setEnableInverseKinematics(isEnabled);
|
||||
}
|
||||
|
||||
void MyAvatar::loadData() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
|
@ -1042,6 +1085,8 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
|||
nextAttitude(position, orientation);
|
||||
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
||||
|
||||
setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
|
||||
|
||||
// now that physics has adjusted our position, we can update attachements.
|
||||
Avatar::simulateAttachments(deltaTime);
|
||||
}
|
||||
|
|
|
@ -260,6 +260,8 @@ public slots:
|
|||
void setEnableDebugDrawPosition(bool isEnabled);
|
||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||
void setEnableInverseKinematics(bool isEnabled);
|
||||
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
||||
|
||||
glm::vec3 getPositionForAudio();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QSharedMemory>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <gl/OpenGLVersionChecker.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AddressManager.h"
|
||||
|
@ -83,6 +84,17 @@ int main(int argc, const char* argv[]) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// Check OpenGL version.
|
||||
// This is done separately from the main Application so that start-up and shut-down logic within the main Application is
|
||||
// not made more complicated than it already is.
|
||||
{
|
||||
OpenGLVersionChecker openGLVersionChecker(argc, const_cast<char**>(argv));
|
||||
if (!openGLVersionChecker.isValidVersion()) {
|
||||
qCDebug(interfaceapp, "Early exit due to OpenGL version.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
QElapsedTimer startupTime;
|
||||
startupTime.start();
|
||||
|
||||
|
@ -96,6 +108,7 @@ int main(int argc, const char* argv[]) {
|
|||
usecTimestampNowForceClockSkew(clockSkew);
|
||||
qCDebug(interfaceapp, "clockSkewOption=%s clockSkew=%d", clockSkewOption, clockSkew);
|
||||
}
|
||||
|
||||
// Oculus initialization MUST PRECEDE OpenGL context creation.
|
||||
// The nature of the Application constructor means this has to be either here,
|
||||
// or in the main window ctor, before GL startup.
|
||||
|
|
|
@ -1,239 +0,0 @@
|
|||
//
|
||||
// AttachmentsDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/4/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <avatar/MyAvatar.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "AttachmentsDialog.h"
|
||||
#include "ModelsBrowser.h"
|
||||
|
||||
AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
||||
QDialog(parent) {
|
||||
|
||||
setWindowTitle("Edit Attachments");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
setLayout(layout);
|
||||
|
||||
QScrollArea* area = new QScrollArea();
|
||||
layout->addWidget(area);
|
||||
area->setWidgetResizable(true);
|
||||
QWidget* container = new QWidget();
|
||||
container->setLayout(_attachments = new QVBoxLayout());
|
||||
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||
area->setWidget(container);
|
||||
_attachments->addStretch(1);
|
||||
|
||||
foreach (const AttachmentData& data, DependencyManager::get<AvatarManager>()->getMyAvatar()->getAttachmentData()) {
|
||||
addAttachment(data);
|
||||
}
|
||||
|
||||
QPushButton* newAttachment = new QPushButton("New Attachment");
|
||||
connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
|
||||
layout->addWidget(newAttachment);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
layout->addWidget(buttons);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
|
||||
_ok = buttons->button(QDialogButtonBox::Ok);
|
||||
|
||||
setMinimumSize(600, 600);
|
||||
}
|
||||
|
||||
void AttachmentsDialog::setVisible(bool visible) {
|
||||
QDialog::setVisible(visible);
|
||||
|
||||
// un-default the OK button
|
||||
if (visible) {
|
||||
_ok->setDefault(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AttachmentsDialog::updateAttachmentData() {
|
||||
QVector<AttachmentData> data;
|
||||
for (int i = 0; i < _attachments->count() - 1; i++) {
|
||||
data.append(static_cast<AttachmentPanel*>(_attachments->itemAt(i)->widget())->getAttachmentData());
|
||||
}
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->setAttachmentData(data);
|
||||
}
|
||||
|
||||
void AttachmentsDialog::addAttachment(const AttachmentData& data) {
|
||||
_attachments->insertWidget(_attachments->count() - 1, new AttachmentPanel(this, data));
|
||||
}
|
||||
|
||||
static QDoubleSpinBox* createTranslationBox(AttachmentPanel* panel, float value) {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||
box->setSingleStep(0.01);
|
||||
box->setMinimum(-FLT_MAX);
|
||||
box->setMaximum(FLT_MAX);
|
||||
box->setValue(value);
|
||||
panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||
return box;
|
||||
}
|
||||
|
||||
static QDoubleSpinBox* createRotationBox(AttachmentPanel* panel, float value) {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||
box->setMinimum(-180.0);
|
||||
box->setMaximum(180.0);
|
||||
box->setWrapping(true);
|
||||
box->setValue(value);
|
||||
panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||
return box;
|
||||
}
|
||||
|
||||
AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) :
|
||||
_dialog(dialog),
|
||||
_applying(false) {
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
|
||||
QFormLayout* layout = new QFormLayout();
|
||||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
setLayout(layout);
|
||||
|
||||
QHBoxLayout* urlBox = new QHBoxLayout();
|
||||
layout->addRow("Model URL:", urlBox);
|
||||
urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
|
||||
_modelURL->setText(data.modelURL.toString());
|
||||
connect(_modelURL, SIGNAL(editingFinished()), SLOT(modelURLChanged()));
|
||||
QPushButton* chooseURL = new QPushButton("Choose");
|
||||
urlBox->addWidget(chooseURL);
|
||||
connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
|
||||
|
||||
layout->addRow("Joint:", _jointName = new QComboBox());
|
||||
QSharedPointer<NetworkGeometry> geometry = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSkeletonModel().getGeometry();
|
||||
if (geometry && geometry->isLoaded()) {
|
||||
foreach (const FBXJoint& joint, geometry->getFBXGeometry().joints) {
|
||||
_jointName->addItem(joint.name);
|
||||
}
|
||||
}
|
||||
_jointName->setCurrentText(data.jointName);
|
||||
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
|
||||
|
||||
QHBoxLayout* translationBox = new QHBoxLayout();
|
||||
translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
|
||||
translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y));
|
||||
translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z));
|
||||
layout->addRow("Translation:", translationBox);
|
||||
|
||||
QHBoxLayout* rotationBox = new QHBoxLayout();
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
|
||||
rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x));
|
||||
rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y));
|
||||
rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z));
|
||||
layout->addRow("Rotation:", rotationBox);
|
||||
|
||||
layout->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||
_scale->setSingleStep(0.01);
|
||||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setValue(data.scale);
|
||||
connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||
|
||||
layout->addRow("Is Soft:", _isSoft = new QCheckBox());
|
||||
_isSoft->setChecked(data.isSoft);
|
||||
connect(_isSoft, SIGNAL(stateChanged(int)), SLOT(updateAttachmentData()));
|
||||
|
||||
QPushButton* remove = new QPushButton("Delete");
|
||||
layout->addRow(remove);
|
||||
connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
||||
dialog->connect(remove, SIGNAL(clicked(bool)), SLOT(updateAttachmentData()), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
AttachmentData AttachmentPanel::getAttachmentData() const {
|
||||
AttachmentData data;
|
||||
data.modelURL = _modelURL->text();
|
||||
data.jointName = _jointName->currentText();
|
||||
data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value());
|
||||
data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
|
||||
data.scale = _scale->value();
|
||||
data.isSoft = _isSoft->isChecked();
|
||||
return data;
|
||||
}
|
||||
|
||||
void AttachmentPanel::chooseModelURL() {
|
||||
ModelsBrowser modelBrowser(FSTReader::ATTACHMENT_MODEL, this);
|
||||
connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&)));
|
||||
modelBrowser.browse();
|
||||
}
|
||||
|
||||
void AttachmentPanel::setModelURL(const QString& url) {
|
||||
_modelURL->setText(url);
|
||||
modelURLChanged();
|
||||
}
|
||||
|
||||
void AttachmentPanel::modelURLChanged() {
|
||||
// check for saved attachment data
|
||||
if (_modelURL->text().isEmpty()) {
|
||||
_dialog->updateAttachmentData();
|
||||
return;
|
||||
}
|
||||
AttachmentData attachment = DependencyManager::get<AvatarManager>()->getMyAvatar()->loadAttachmentData(_modelURL->text());
|
||||
if (attachment.isValid()) {
|
||||
_applying = true;
|
||||
_jointName->setCurrentText(attachment.jointName);
|
||||
applyAttachmentData(attachment);
|
||||
}
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
||||
void AttachmentPanel::jointNameChanged() {
|
||||
if (_applying) {
|
||||
return;
|
||||
}
|
||||
// check for saved attachment data specific to this joint
|
||||
if (_modelURL->text().isEmpty()) {
|
||||
_dialog->updateAttachmentData();
|
||||
return;
|
||||
}
|
||||
AttachmentData attachment = DependencyManager::get<AvatarManager>()->getMyAvatar()->loadAttachmentData(
|
||||
_modelURL->text(), _jointName->currentText());
|
||||
if (attachment.isValid()) {
|
||||
applyAttachmentData(attachment);
|
||||
}
|
||||
updateAttachmentData();
|
||||
}
|
||||
|
||||
void AttachmentPanel::updateAttachmentData() {
|
||||
if (_applying) {
|
||||
return;
|
||||
}
|
||||
// save the attachment data under the model URL (if any)
|
||||
if (!_modelURL->text().isEmpty()) {
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->saveAttachmentData(getAttachmentData());
|
||||
}
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
||||
void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) {
|
||||
_applying = true;
|
||||
_translationX->setValue(attachment.translation.x);
|
||||
_translationY->setValue(attachment.translation.y);
|
||||
_translationZ->setValue(attachment.translation.z);
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(attachment.rotation));
|
||||
_rotationX->setValue(eulers.x);
|
||||
_rotationY->setValue(eulers.y);
|
||||
_rotationZ->setValue(eulers.z);
|
||||
_scale->setValue(attachment.scale);
|
||||
_isSoft->setChecked(attachment.isSoft);
|
||||
_applying = false;
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
//
|
||||
// AttachmentsDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/4/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AttachmentsDialog_h
|
||||
#define hifi_AttachmentsDialog_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFrame>
|
||||
|
||||
#include <AvatarData.h>
|
||||
|
||||
class QComboBox;
|
||||
class QDoubleSpinner;
|
||||
class QLineEdit;
|
||||
class QVBoxLayout;
|
||||
|
||||
/// Allows users to edit the avatar attachments.
|
||||
class AttachmentsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AttachmentsDialog(QWidget* parent = nullptr);
|
||||
|
||||
virtual void setVisible(bool visible);
|
||||
|
||||
public slots:
|
||||
|
||||
void updateAttachmentData();
|
||||
|
||||
private slots:
|
||||
|
||||
void addAttachment(const AttachmentData& data = AttachmentData());
|
||||
|
||||
private:
|
||||
|
||||
QVBoxLayout* _attachments;
|
||||
QPushButton* _ok;
|
||||
};
|
||||
|
||||
/// A panel controlling a single attachment.
|
||||
class AttachmentPanel : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
||||
|
||||
AttachmentData getAttachmentData() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void chooseModelURL();
|
||||
void setModelURL(const QString& url);
|
||||
void modelURLChanged();
|
||||
void jointNameChanged();
|
||||
void updateAttachmentData();
|
||||
|
||||
private:
|
||||
|
||||
void applyAttachmentData(const AttachmentData& attachment);
|
||||
|
||||
AttachmentsDialog* _dialog;
|
||||
QLineEdit* _modelURL;
|
||||
QComboBox* _jointName;
|
||||
QDoubleSpinBox* _translationX;
|
||||
QDoubleSpinBox* _translationY;
|
||||
QDoubleSpinBox* _translationZ;
|
||||
QDoubleSpinBox* _rotationX;
|
||||
QDoubleSpinBox* _rotationY;
|
||||
QDoubleSpinBox* _rotationZ;
|
||||
QDoubleSpinBox* _scale;
|
||||
QCheckBox* _isSoft;
|
||||
bool _applying;
|
||||
};
|
||||
|
||||
#endif // hifi_AttachmentsDialog_h
|
|
@ -19,7 +19,6 @@
|
|||
#include <PathUtils.h>
|
||||
|
||||
#include "AddressBarDialog.h"
|
||||
#include "AttachmentsDialog.h"
|
||||
#include "BandwidthDialog.h"
|
||||
#include "CachesSizeDialog.h"
|
||||
#include "DiskCacheEditor.h"
|
||||
|
@ -91,15 +90,6 @@ void DialogsManager::cachesSizeDialog() {
|
|||
_cachesSizeDialog->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::editAttachments() {
|
||||
if (!_attachmentsDialog) {
|
||||
maybeCreateDialog(_attachmentsDialog);
|
||||
_attachmentsDialog->show();
|
||||
} else {
|
||||
_attachmentsDialog->close();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsManager::audioStatsDetails() {
|
||||
if (! _audioStatsDialog) {
|
||||
_audioStatsDialog = new AudioStatsDialog(qApp->getWindow());
|
||||
|
|
|
@ -48,7 +48,6 @@ public slots:
|
|||
void showLoginDialog();
|
||||
void octreeStatsDetails();
|
||||
void cachesSizeDialog();
|
||||
void editAttachments();
|
||||
void audioStatsDetails();
|
||||
void bandwidthDetails();
|
||||
void lodTools();
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include "Snapshot.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
#include "AntialiasingEffect.h"
|
||||
#include "RenderShadowTask.h"
|
||||
|
||||
void setupPreferences() {
|
||||
auto preferences = DependencyManager::get<Preferences>();
|
||||
|
||||
|
@ -310,4 +314,29 @@ void setupPreferences() {
|
|||
preference->setStep(1);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
static const QString RENDER("Graphics");
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
{
|
||||
auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled<AmbientOcclusionEffect>(); };
|
||||
auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled<AmbientOcclusionEffect>(enable); };
|
||||
auto preference = new CheckPreference(RENDER, "Ambient Occlusion", getter, setter);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled<Antialiasing>(); };
|
||||
auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled<Antialiasing>(enable); };
|
||||
auto preference = new CheckPreference(RENDER, "Antialiasing", getter, setter);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled<RenderShadowTask>(); };
|
||||
auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled<RenderShadowTask>(enable); };
|
||||
auto preference = new CheckPreference(RENDER, "Shadows", getter, setter);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,10 +191,10 @@ bool ScriptEditorWidget::save() {
|
|||
bool ScriptEditorWidget::saveAs() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"),
|
||||
scriptEngines->getPreviousScriptLocation(),
|
||||
qApp->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!fileName.isEmpty()) {
|
||||
scriptEngines->setPreviousScriptLocation(fileName);
|
||||
qApp->setPreviousScriptLocation(fileName);
|
||||
return saveFile(fileName);
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -87,12 +87,11 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {
|
|||
}
|
||||
|
||||
void ScriptEditorWindow::loadScriptClicked() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"),
|
||||
scriptEngines->getPreviousScriptLocation(),
|
||||
qApp->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!scriptName.isEmpty()) {
|
||||
scriptEngines->setPreviousScriptLocation(scriptName);
|
||||
qApp->setPreviousScriptLocation(scriptName);
|
||||
addScriptEditorWidget("loading...")->loadFile(scriptName);
|
||||
updateButtons();
|
||||
}
|
||||
|
@ -100,7 +99,7 @@ void ScriptEditorWindow::loadScriptClicked() {
|
|||
|
||||
void ScriptEditorWindow::loadMenuAboutToShow() {
|
||||
_loadMenu->clear();
|
||||
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();;
|
||||
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();
|
||||
if (runningScripts.count() > 0) {
|
||||
QSignalMapper* signalMapper = new QSignalMapper(this);
|
||||
foreach (const QString& runningScript, runningScripts) {
|
||||
|
|
|
@ -162,7 +162,7 @@ void Stats::updateStats(bool force) {
|
|||
MyAvatar* myAvatar = avatarManager->getMyAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
|
||||
STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f);
|
||||
STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.1f);
|
||||
STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f);
|
||||
if (_expanded || force) {
|
||||
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||
|
|
|
@ -47,7 +47,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, entitiesPing, 0)
|
||||
STATS_PROPERTY(int, assetPing, 0)
|
||||
STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) )
|
||||
STATS_PROPERTY(float, velocity, 0)
|
||||
STATS_PROPERTY(float, speed, 0)
|
||||
STATS_PROPERTY(float, yaw, 0)
|
||||
STATS_PROPERTY(int, avatarMixerInKbps, 0)
|
||||
STATS_PROPERTY(int, avatarMixerInPps, 0)
|
||||
|
@ -138,7 +138,7 @@ signals:
|
|||
void entitiesPingChanged();
|
||||
void assetPingChanged();
|
||||
void positionChanged();
|
||||
void velocityChanged();
|
||||
void speedChanged();
|
||||
void yawChanged();
|
||||
void avatarMixerInKbpsChanged();
|
||||
void avatarMixerInPpsChanged();
|
||||
|
|
|
@ -17,159 +17,15 @@
|
|||
|
||||
|
||||
QString const ImageOverlay::TYPE = "image";
|
||||
QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml"));
|
||||
|
||||
ImageOverlay::ImageOverlay() :
|
||||
_imageURL(),
|
||||
_renderImage(false),
|
||||
_wantClipFromImage(false)
|
||||
{
|
||||
}
|
||||
ImageOverlay::ImageOverlay()
|
||||
: QmlOverlay(URL) { }
|
||||
|
||||
ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) :
|
||||
Overlay2D(imageOverlay),
|
||||
_imageURL(imageOverlay->_imageURL),
|
||||
_textureImage(imageOverlay->_textureImage),
|
||||
_texture(imageOverlay->_texture),
|
||||
_fromImage(imageOverlay->_fromImage),
|
||||
_renderImage(imageOverlay->_renderImage),
|
||||
_wantClipFromImage(imageOverlay->_wantClipFromImage)
|
||||
{
|
||||
}
|
||||
QmlOverlay(URL, imageOverlay) { }
|
||||
|
||||
// TODO: handle setting image multiple times, how do we manage releasing the bound texture?
|
||||
void ImageOverlay::setImageURL(const QUrl& url) {
|
||||
_imageURL = url;
|
||||
if (url.isEmpty()) {
|
||||
_isLoaded = true;
|
||||
_renderImage = false;
|
||||
_texture.clear();
|
||||
} else {
|
||||
_isLoaded = false;
|
||||
_renderImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageOverlay::render(RenderArgs* args) {
|
||||
if (!_isLoaded && _renderImage) {
|
||||
_isLoaded = true;
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(_imageURL);
|
||||
}
|
||||
// If we are not visible or loaded, return. If we are trying to render an
|
||||
// image but the texture hasn't loaded, return.
|
||||
if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
if (_renderImage) {
|
||||
batch.setResourceTexture(0, _texture->getGPUTexture());
|
||||
} else {
|
||||
batch.setResourceTexture(0, args->_whiteTexture);
|
||||
}
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
xColor color = getColor();
|
||||
float alpha = getAlpha();
|
||||
glm::vec4 quadColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
int left = _bounds.left();
|
||||
int right = _bounds.right() + 1;
|
||||
int top = _bounds.top();
|
||||
int bottom = _bounds.bottom() + 1;
|
||||
|
||||
glm::vec2 topLeft(left, top);
|
||||
glm::vec2 bottomRight(right, bottom);
|
||||
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
// if for some reason our image is not over 0 width or height, don't attempt to render the image
|
||||
if (_renderImage) {
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
if (imageWidth > 0 && imageHeight > 0) {
|
||||
QRect fromImage;
|
||||
if (_wantClipFromImage) {
|
||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
||||
|
||||
fromImage.setX(scaleX * _fromImage.x());
|
||||
fromImage.setY(scaleY * _fromImage.y());
|
||||
fromImage.setWidth(scaleX * _fromImage.width());
|
||||
fromImage.setHeight(scaleY * _fromImage.height());
|
||||
}
|
||||
else {
|
||||
fromImage.setX(0);
|
||||
fromImage.setY(0);
|
||||
fromImage.setWidth(imageWidth);
|
||||
fromImage.setHeight(imageHeight);
|
||||
}
|
||||
|
||||
float x = fromImage.x() / imageWidth;
|
||||
float y = fromImage.y() / imageHeight;
|
||||
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
|
||||
float h = fromImage.height() / imageHeight;
|
||||
|
||||
glm::vec2 texCoordTopLeft(x, y);
|
||||
glm::vec2 texCoordBottomRight(x + w, y + h);
|
||||
glm::vec4 texcoordRect(texCoordTopLeft, w, h);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
|
||||
}
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageOverlay::setProperties(const QScriptValue& properties) {
|
||||
Overlay2D::setProperties(properties);
|
||||
|
||||
QScriptValue subImageBounds = properties.property("subImage");
|
||||
if (subImageBounds.isValid()) {
|
||||
QRect oldSubImageRect = _fromImage;
|
||||
QRect subImageRect = _fromImage;
|
||||
if (subImageBounds.property("x").isValid()) {
|
||||
subImageRect.setX(subImageBounds.property("x").toVariant().toInt());
|
||||
} else {
|
||||
subImageRect.setX(oldSubImageRect.x());
|
||||
}
|
||||
if (subImageBounds.property("y").isValid()) {
|
||||
subImageRect.setY(subImageBounds.property("y").toVariant().toInt());
|
||||
} else {
|
||||
subImageRect.setY(oldSubImageRect.y());
|
||||
}
|
||||
if (subImageBounds.property("width").isValid()) {
|
||||
subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt());
|
||||
} else {
|
||||
subImageRect.setWidth(oldSubImageRect.width());
|
||||
}
|
||||
if (subImageBounds.property("height").isValid()) {
|
||||
subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt());
|
||||
} else {
|
||||
subImageRect.setHeight(oldSubImageRect.height());
|
||||
}
|
||||
setClipFromSource(subImageRect);
|
||||
}
|
||||
|
||||
QScriptValue imageURL = properties.property("imageURL");
|
||||
if (imageURL.isValid()) {
|
||||
setImageURL(imageURL.toVariant().toString());
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue ImageOverlay::getProperty(const QString& property) {
|
||||
if (property == "subImage") {
|
||||
return qRectToScriptValue(_scriptEngine, _fromImage);
|
||||
}
|
||||
if (property == "imageURL") {
|
||||
return _imageURL.toString();
|
||||
}
|
||||
|
||||
return Overlay2D::getProperty(property);
|
||||
}
|
||||
ImageOverlay* ImageOverlay::createClone() const {
|
||||
return new ImageOverlay(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,48 +11,22 @@
|
|||
#ifndef hifi_ImageOverlay_h
|
||||
#define hifi_ImageOverlay_h
|
||||
|
||||
// include this before QGLWidget, which includes an earlier version of OpenGL
|
||||
#include <QImage>
|
||||
#include <QRect>
|
||||
#include <QUrl>
|
||||
|
||||
#include <TextureCache.h>
|
||||
#include "QmlOverlay.h"
|
||||
|
||||
#include "Overlay2D.h"
|
||||
|
||||
class ImageOverlay : public Overlay2D {
|
||||
class ImageOverlay : public QmlOverlay {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
static QUrl const URL;
|
||||
|
||||
ImageOverlay();
|
||||
ImageOverlay(const ImageOverlay* imageOverlay);
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
// getters
|
||||
const QRect& getClipFromSource() const { return _fromImage; }
|
||||
const QUrl& getImageURL() const { return _imageURL; }
|
||||
|
||||
// setters
|
||||
void setClipFromSource(const QRect& bounds) { _fromImage = bounds; _wantClipFromImage = true; }
|
||||
void setImageURL(const QUrl& url);
|
||||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
virtual ImageOverlay* createClone() const;
|
||||
|
||||
private:
|
||||
|
||||
QUrl _imageURL;
|
||||
QImage _textureImage;
|
||||
|
||||
NetworkTexturePointer _texture;
|
||||
QRect _fromImage; // where from in the image to sample
|
||||
bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle
|
||||
bool _wantClipFromImage;
|
||||
virtual ImageOverlay* createClone() const override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
virtual void render(RenderArgs* args) = 0;
|
||||
|
||||
virtual AABox getBounds() const = 0;
|
||||
virtual bool supportsGetProperty() const { return true; }
|
||||
|
||||
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "Sphere3DOverlay.h"
|
||||
#include "Grid3DOverlay.h"
|
||||
#include "TextOverlay.h"
|
||||
#include "RectangleOverlay.h"
|
||||
#include "Text3DOverlay.h"
|
||||
#include "Web3DOverlay.h"
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
@ -175,6 +176,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
|
|||
thisOverlay = std::make_shared<ModelOverlay>();
|
||||
} else if (type == Web3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Web3DOverlay>();
|
||||
} else if (type == RectangleOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<RectangleOverlay>();
|
||||
}
|
||||
|
||||
if (thisOverlay) {
|
||||
|
@ -373,7 +376,7 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop
|
|||
OverlayPropertyResult result;
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
QReadLocker lock(&_lock);
|
||||
if (thisOverlay) {
|
||||
if (thisOverlay && thisOverlay->supportsGetProperty()) {
|
||||
result.value = thisOverlay->getProperty(property);
|
||||
}
|
||||
return result;
|
||||
|
|
74
interface/src/ui/overlays/QmlOverlay.cpp
Normal file
74
interface/src/ui/overlays/QmlOverlay.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/01/27
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "QmlOverlay.h"
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextureCache.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "text/FontFamilies.h"
|
||||
|
||||
QmlOverlay::QmlOverlay(const QUrl& url) {
|
||||
buildQmlElement(url);
|
||||
}
|
||||
|
||||
QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay)
|
||||
: Overlay2D(textOverlay) {
|
||||
buildQmlElement(url);
|
||||
}
|
||||
|
||||
void QmlOverlay::buildQmlElement(const QUrl& url) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->returnFromUiThread([=] {
|
||||
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
|
||||
_qmlElement = dynamic_cast<QQuickItem*>(object);
|
||||
});
|
||||
while (!_qmlElement) {
|
||||
qApp->processEvents();
|
||||
}
|
||||
return QVariant();
|
||||
});
|
||||
}
|
||||
|
||||
QmlOverlay::~QmlOverlay() {
|
||||
if (_qmlElement) {
|
||||
_qmlElement->deleteLater();
|
||||
_qmlElement = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void QmlOverlay::setProperties(const QScriptValue& properties) {
|
||||
Overlay2D::setProperties(properties);
|
||||
auto bounds = _bounds;
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
_qmlElement->setX(bounds.left());
|
||||
_qmlElement->setY(bounds.top());
|
||||
_qmlElement->setWidth(bounds.width());
|
||||
_qmlElement->setHeight(bounds.height());
|
||||
});
|
||||
QMetaObject::invokeMethod(_qmlElement, "updatePropertiesFromScript", Q_ARG(QVariant, properties.toVariant()));
|
||||
}
|
||||
|
||||
void QmlOverlay::render(RenderArgs* args) {
|
||||
if (!_qmlElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_visible != _qmlElement->isVisible()) {
|
||||
_qmlElement->setVisible(_visible);
|
||||
}
|
||||
}
|
41
interface/src/ui/overlays/QmlOverlay.h
Normal file
41
interface/src/ui/overlays/QmlOverlay.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/01/27
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_QmlOverlay_h
|
||||
#define hifi_QmlOverlay_h
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include "Overlay2D.h"
|
||||
|
||||
class QQuickItem;
|
||||
|
||||
class QmlOverlay : public Overlay2D {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QmlOverlay(const QUrl& url);
|
||||
QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay);
|
||||
~QmlOverlay();
|
||||
|
||||
// Cannot fetch properties from QML based overlays due to race conditions
|
||||
bool supportsGetProperty() const override { return false; }
|
||||
|
||||
void setProperties(const QScriptValue& properties) override;
|
||||
void render(RenderArgs* args) override;
|
||||
|
||||
private:
|
||||
void buildQmlElement(const QUrl& url);
|
||||
|
||||
protected:
|
||||
QQuickItem* _qmlElement{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_QmlOverlay_h
|
21
interface/src/ui/overlays/RectangleOverlay.cpp
Normal file
21
interface/src/ui/overlays/RectangleOverlay.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/01/27
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "RectangleOverlay.h"
|
||||
|
||||
QString const RectangleOverlay::TYPE = "rectangle";
|
||||
QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml"));
|
||||
|
||||
RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {}
|
||||
|
||||
RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay)
|
||||
: QmlOverlay(URL, rectangleOverlay) { }
|
||||
|
||||
RectangleOverlay* RectangleOverlay::createClone() const {
|
||||
return new RectangleOverlay(this);
|
||||
}
|
27
interface/src/ui/overlays/RectangleOverlay.h
Normal file
27
interface/src/ui/overlays/RectangleOverlay.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/01/27
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RectangleOverlay_h
|
||||
#define hifi_RectangleOverlay_h
|
||||
|
||||
#include "QmlOverlay.h"
|
||||
|
||||
class RectangleOverlay : public QmlOverlay {
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
static QUrl const URL;
|
||||
|
||||
RectangleOverlay();
|
||||
RectangleOverlay(const RectangleOverlay* RectangleOverlay);
|
||||
|
||||
virtual RectangleOverlay* createClone() const;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_RectangleOverlay_h
|
|
@ -24,216 +24,21 @@
|
|||
#include "Application.h"
|
||||
#include "text/FontFamilies.h"
|
||||
|
||||
#define TEXT_OVERLAY_PROPERTY(type, name, initialValue) \
|
||||
Q_PROPERTY(type name READ name WRITE set##name NOTIFY name##Changed) \
|
||||
public: \
|
||||
type name() { return _##name; }; \
|
||||
void set##name(const type& name) { \
|
||||
if (name != _##name) { \
|
||||
_##name = name; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
} \
|
||||
private: \
|
||||
type _##name{ initialValue };
|
||||
|
||||
|
||||
class TextOverlayElement : public QQuickItem {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
private:
|
||||
TEXT_OVERLAY_PROPERTY(QString, text, "")
|
||||
TEXT_OVERLAY_PROPERTY(QString, fontFamily, SANS_FONT_FAMILY)
|
||||
TEXT_OVERLAY_PROPERTY(QString, textColor, "#ffffffff")
|
||||
TEXT_OVERLAY_PROPERTY(QString, backgroundColor, "#B2000000")
|
||||
TEXT_OVERLAY_PROPERTY(qreal, fontSize, 18)
|
||||
TEXT_OVERLAY_PROPERTY(qreal, lineHeight, 18)
|
||||
TEXT_OVERLAY_PROPERTY(qreal, leftMargin, 0)
|
||||
TEXT_OVERLAY_PROPERTY(qreal, topMargin, 0)
|
||||
|
||||
public:
|
||||
TextOverlayElement(QQuickItem* parent = nullptr) : QQuickItem(parent) {
|
||||
}
|
||||
|
||||
signals:
|
||||
void textChanged();
|
||||
void fontFamilyChanged();
|
||||
void fontSizeChanged();
|
||||
void lineHeightChanged();
|
||||
void leftMarginChanged();
|
||||
void topMarginChanged();
|
||||
void textColorChanged();
|
||||
void backgroundColorChanged();
|
||||
};
|
||||
|
||||
HIFI_QML_DEF(TextOverlayElement)
|
||||
|
||||
QString toQmlColor(const glm::vec4& v) {
|
||||
QString templat("#%1%2%3%4");
|
||||
return templat.
|
||||
arg((int)(v.a * 255), 2, 16, QChar('0')).
|
||||
arg((int)(v.r * 255), 2, 16, QChar('0')).
|
||||
arg((int)(v.g * 255), 2, 16, QChar('0')).
|
||||
arg((int)(v.b * 255), 2, 16, QChar('0'));
|
||||
}
|
||||
|
||||
QString const TextOverlay::TYPE = "text";
|
||||
QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml"));
|
||||
|
||||
TextOverlay::TextOverlay() :
|
||||
_backgroundColor(DEFAULT_BACKGROUND_COLOR),
|
||||
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),
|
||||
_leftMargin(DEFAULT_MARGIN),
|
||||
_topMargin(DEFAULT_MARGIN),
|
||||
_fontSize(DEFAULT_FONTSIZE)
|
||||
{
|
||||
qApp->postLambdaEvent([=] {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
TextOverlayElement::registerType();
|
||||
});
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
TextOverlayElement::show([=](QQmlContext* context, QObject* object) {
|
||||
_qmlElement = static_cast<TextOverlayElement*>(object);
|
||||
});
|
||||
});
|
||||
while (!_qmlElement) {
|
||||
QThread::msleep(1);
|
||||
}
|
||||
TextOverlay::TextOverlay() : QmlOverlay(URL) { }
|
||||
|
||||
TextOverlay::TextOverlay(const TextOverlay* textOverlay)
|
||||
: QmlOverlay(URL, textOverlay) {
|
||||
}
|
||||
|
||||
TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
|
||||
Overlay2D(textOverlay),
|
||||
_text(textOverlay->_text),
|
||||
_backgroundColor(textOverlay->_backgroundColor),
|
||||
_backgroundAlpha(textOverlay->_backgroundAlpha),
|
||||
_leftMargin(textOverlay->_leftMargin),
|
||||
_topMargin(textOverlay->_topMargin),
|
||||
_fontSize(textOverlay->_fontSize)
|
||||
{
|
||||
qApp->postLambdaEvent([=] {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
TextOverlayElement::show([this](QQmlContext* context, QObject* object) {
|
||||
_qmlElement = static_cast<TextOverlayElement*>(object);
|
||||
});
|
||||
});
|
||||
while (!_qmlElement) {
|
||||
QThread::msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
TextOverlay::~TextOverlay() {
|
||||
if (_qmlElement) {
|
||||
_qmlElement->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
xColor TextOverlay::getBackgroundColor() {
|
||||
if (_colorPulse == 0.0f) {
|
||||
return _backgroundColor;
|
||||
}
|
||||
|
||||
float pulseLevel = updatePulse();
|
||||
xColor result = _backgroundColor;
|
||||
if (_colorPulse < 0.0f) {
|
||||
result.red *= (1.0f - pulseLevel);
|
||||
result.green *= (1.0f - pulseLevel);
|
||||
result.blue *= (1.0f - pulseLevel);
|
||||
} else {
|
||||
result.red *= pulseLevel;
|
||||
result.green *= pulseLevel;
|
||||
result.blue *= pulseLevel;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextOverlay::render(RenderArgs* args) {
|
||||
if (!_qmlElement) {
|
||||
return;
|
||||
}
|
||||
if (_visible != _qmlElement->isVisible()) {
|
||||
_qmlElement->setVisible(_visible);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TextOverlay::setProperties(const QScriptValue& properties) {
|
||||
Overlay2D::setProperties(properties);
|
||||
_qmlElement->setX(_bounds.left());
|
||||
_qmlElement->setY(_bounds.top());
|
||||
_qmlElement->setWidth(_bounds.width());
|
||||
_qmlElement->setHeight(_bounds.height());
|
||||
_qmlElement->settextColor(toQmlColor(vec4(toGlm(_color), _alpha)));
|
||||
QScriptValue font = properties.property("font");
|
||||
if (font.isObject()) {
|
||||
if (font.property("size").isValid()) {
|
||||
setFontSize(font.property("size").toInt32());
|
||||
}
|
||||
QFont font(_qmlElement->fontFamily());
|
||||
font.setPixelSize(_qmlElement->fontSize());
|
||||
QFontMetrics fm(font);
|
||||
_qmlElement->setlineHeight(fm.lineSpacing() * 1.2);
|
||||
}
|
||||
|
||||
QScriptValue text = properties.property("text");
|
||||
if (text.isValid()) {
|
||||
setText(text.toVariant().toString());
|
||||
}
|
||||
|
||||
QScriptValue backgroundColor = properties.property("backgroundColor");
|
||||
if (backgroundColor.isValid()) {
|
||||
QScriptValue red = backgroundColor.property("red");
|
||||
QScriptValue green = backgroundColor.property("green");
|
||||
QScriptValue blue = backgroundColor.property("blue");
|
||||
if (red.isValid() && green.isValid() && blue.isValid()) {
|
||||
_backgroundColor.red = red.toVariant().toInt();
|
||||
_backgroundColor.green = green.toVariant().toInt();
|
||||
_backgroundColor.blue = blue.toVariant().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.property("backgroundAlpha").isValid()) {
|
||||
_backgroundAlpha = properties.property("backgroundAlpha").toVariant().toFloat();
|
||||
}
|
||||
_qmlElement->setbackgroundColor(toQmlColor(vec4(toGlm(_backgroundColor), _backgroundAlpha)));
|
||||
|
||||
if (properties.property("leftMargin").isValid()) {
|
||||
setLeftMargin(properties.property("leftMargin").toVariant().toInt());
|
||||
}
|
||||
|
||||
if (properties.property("topMargin").isValid()) {
|
||||
setTopMargin(properties.property("topMargin").toVariant().toInt());
|
||||
}
|
||||
}
|
||||
TextOverlay::~TextOverlay() { }
|
||||
|
||||
TextOverlay* TextOverlay::createClone() const {
|
||||
return new TextOverlay(this);
|
||||
}
|
||||
|
||||
QScriptValue TextOverlay::getProperty(const QString& property) {
|
||||
if (property == "font") {
|
||||
QScriptValue font = _scriptEngine->newObject();
|
||||
font.setProperty("size", _fontSize);
|
||||
return font;
|
||||
}
|
||||
if (property == "text") {
|
||||
return _text;
|
||||
}
|
||||
if (property == "backgroundColor") {
|
||||
return xColorToScriptValue(_scriptEngine, _backgroundColor);
|
||||
}
|
||||
if (property == "backgroundAlpha") {
|
||||
return _backgroundAlpha;
|
||||
}
|
||||
if (property == "leftMargin") {
|
||||
return _leftMargin;
|
||||
}
|
||||
if (property == "topMargin") {
|
||||
return _topMargin;
|
||||
}
|
||||
|
||||
return Overlay2D::getProperty(property);
|
||||
}
|
||||
|
||||
QSizeF TextOverlay::textSize(const QString& text) const {
|
||||
int lines = 1;
|
||||
foreach(QChar c, text) {
|
||||
|
@ -241,31 +46,15 @@ QSizeF TextOverlay::textSize(const QString& text) const {
|
|||
++lines;
|
||||
}
|
||||
}
|
||||
QFont font(_qmlElement->fontFamily());
|
||||
font.setPixelSize(_qmlElement->fontSize());
|
||||
QFont font(SANS_FONT_FAMILY);
|
||||
font.setPixelSize(18);
|
||||
QFontMetrics fm(font);
|
||||
QSizeF result = QSizeF(fm.width(text), _qmlElement->lineHeight() * lines);
|
||||
QSizeF result = QSizeF(fm.width(text), 18 * lines);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextOverlay::setFontSize(int fontSize) {
|
||||
_fontSize = fontSize;
|
||||
_qmlElement->setfontSize(fontSize);
|
||||
}
|
||||
|
||||
void TextOverlay::setText(const QString& text) {
|
||||
_text = text;
|
||||
_qmlElement->settext(text);
|
||||
}
|
||||
|
||||
void TextOverlay::setLeftMargin(int margin) {
|
||||
_leftMargin = margin;
|
||||
_qmlElement->setleftMargin(margin);
|
||||
}
|
||||
|
||||
void TextOverlay::setTopMargin(int margin) {
|
||||
_topMargin = margin;
|
||||
_qmlElement->settopMargin(margin);
|
||||
}
|
||||
|
||||
#include "TextOverlay.moc"
|
||||
void TextOverlay::setTopMargin(float margin) {}
|
||||
void TextOverlay::setLeftMargin(float margin) {}
|
||||
void TextOverlay::setFontSize(float size) {}
|
||||
void TextOverlay::setText(const QString& text) {}
|
||||
|
|
|
@ -11,59 +11,27 @@
|
|||
#ifndef hifi_TextOverlay_h
|
||||
#define hifi_TextOverlay_h
|
||||
|
||||
#include <QString>
|
||||
#include "QmlOverlay.h"
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Overlay2D.h"
|
||||
|
||||
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
|
||||
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
|
||||
const int DEFAULT_MARGIN = 10;
|
||||
const int DEFAULT_FONTSIZE = 12;
|
||||
const int DEFAULT_FONT_WEIGHT = 50;
|
||||
|
||||
class TextOverlayElement;
|
||||
|
||||
class TextOverlay : public Overlay2D {
|
||||
Q_OBJECT
|
||||
|
||||
class TextOverlay : public QmlOverlay {
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
QString getType() const override { return TYPE; }
|
||||
static QUrl const URL;
|
||||
|
||||
|
||||
TextOverlay();
|
||||
TextOverlay(const TextOverlay* textOverlay);
|
||||
~TextOverlay();
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
// getters
|
||||
const QString& getText() const { return _text; }
|
||||
int getLeftMargin() const { return _leftMargin; }
|
||||
int getTopMargin() const { return _topMargin; }
|
||||
xColor getBackgroundColor();
|
||||
float getBackgroundAlpha() const { return _backgroundAlpha; }
|
||||
|
||||
// setters
|
||||
void setTopMargin(float margin);
|
||||
void setLeftMargin(float margin);
|
||||
void setFontSize(float size);
|
||||
void setText(const QString& text);
|
||||
void setLeftMargin(int margin);
|
||||
void setTopMargin(int margin);
|
||||
void setFontSize(int fontSize);
|
||||
|
||||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual TextOverlay* createClone() const;
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
TextOverlay* createClone() const;
|
||||
QSizeF textSize(const QString& text) const; // Pixels
|
||||
|
||||
private:
|
||||
TextOverlayElement* _qmlElement{ nullptr };
|
||||
QString _text;
|
||||
xColor _backgroundColor;
|
||||
float _backgroundAlpha;
|
||||
int _leftMargin;
|
||||
int _topMargin;
|
||||
int _fontSize;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
bool AnimClip::usePreAndPostPoseFromAnim = false;
|
||||
|
||||
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
|
||||
AnimNode(AnimNode::Type::Clip, id),
|
||||
_startFrame(startFrame),
|
||||
|
@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
|
||||
for (int frame = 0; frame < frameCount; frame++) {
|
||||
|
||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||
|
||||
// init all joints in animation to default pose
|
||||
// this will give us a resonable result for bones in the model skeleton but not in the animation.
|
||||
_anim[frame].reserve(skeletonJointCount);
|
||||
|
@ -119,36 +123,41 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
||||
int skeletonJoint = jointMap[animJoint];
|
||||
|
||||
const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint];
|
||||
const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint];
|
||||
|
||||
// skip joints that are in the animation but not in the skeleton.
|
||||
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
||||
|
||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
|
||||
#ifdef USE_PRE_ROT_FROM_ANIM
|
||||
// TODO: This is the correct way to apply the pre rotations from maya, however
|
||||
// the current animation set has incorrect preRotations for the left wrist and thumb
|
||||
// so it looks wrong if we enable this code.
|
||||
glm::quat preRotation = animSkeleton.getPreRotation(animJoint);
|
||||
#else
|
||||
// TODO: This is the legacy approach, this does not work when animations and models do not
|
||||
// have the same set of pre rotations. For example when mixing maya models with blender animations.
|
||||
glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot;
|
||||
#endif
|
||||
const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
|
||||
AnimPose preRot, postRot;
|
||||
if (usePreAndPostPoseFromAnim) {
|
||||
preRot = animSkeleton.getPreRotationPose(animJoint);
|
||||
postRot = animSkeleton.getPostRotationPose(animJoint);
|
||||
} else {
|
||||
// In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations.
|
||||
preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3());
|
||||
postRot = AnimPose::identity;
|
||||
}
|
||||
|
||||
// used to adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// cancel out scale
|
||||
preRot.scale = glm::vec3(1.0f);
|
||||
postRot.scale = glm::vec3(1.0f);
|
||||
|
||||
AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3());
|
||||
|
||||
// adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// will be adjusted when played on a skeleton with short limbs.
|
||||
float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans));
|
||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
|
||||
const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
|
||||
float boneLengthScale = 1.0f;
|
||||
const float EPSILON = 0.0001f;
|
||||
if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) {
|
||||
boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans);
|
||||
}
|
||||
|
||||
AnimPose& pose = _anim[frame][skeletonJoint];
|
||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans));
|
||||
|
||||
// rotation in fbxAnimationFrame is a delta from its preRotation.
|
||||
pose.rot = preRotation * fbxAnimFrame.rotations[animJoint];
|
||||
|
||||
// translation in fbxAnimationFrame is not a delta.
|
||||
// convert it into a delta by subtracting from the first frame.
|
||||
const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint];
|
||||
pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans);
|
||||
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ class AnimClip : public AnimNode {
|
|||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
static bool usePreAndPostPoseFromAnim;
|
||||
|
||||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
||||
virtual ~AnimClip() override;
|
||||
|
||||
|
|
|
@ -153,7 +153,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
continue;
|
||||
}
|
||||
|
||||
// cache tip absolute transform
|
||||
int tipIndex = target.getIndex();
|
||||
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
||||
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
|
||||
|
@ -165,12 +164,30 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
continue;
|
||||
}
|
||||
|
||||
glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
|
||||
glm::quat tipRotation = absolutePoses[tipIndex].rot;
|
||||
// cache tip's absolute orientation
|
||||
glm::quat tipOrientation = absolutePoses[tipIndex].rot;
|
||||
|
||||
// cache tip's parent's absolute rotation so we can recompute the tip's parent-relative
|
||||
// as we proceed walking down the joint chain
|
||||
glm::quat tipParentRotation = absolutePoses[pivotIndex].rot;
|
||||
// also cache tip's parent's absolute orientation so we can recompute
|
||||
// the tip's parent-relative as we proceed up the chain
|
||||
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot;
|
||||
|
||||
if (targetType == IKTarget::Type::HmdHead) {
|
||||
// rotate tip directly to target orientation
|
||||
tipOrientation = target.getRotation();
|
||||
|
||||
// enforce tip's constraint
|
||||
RotationConstraint* constraint = getConstraint(tipIndex);
|
||||
if (constraint) {
|
||||
glm::quat tipRelativeRotation = glm::normalize(tipOrientation * glm::inverse(tipParentOrientation));
|
||||
bool constrained = constraint->apply(tipRelativeRotation);
|
||||
if (constrained) {
|
||||
tipOrientation = glm::normalize(tipRelativeRotation * tipParentOrientation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache tip absolute position
|
||||
glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
|
||||
|
||||
// descend toward root, pivoting each joint to get tip closer to target
|
||||
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
|
||||
|
@ -201,9 +218,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
deltaRotation = glm::angleAxis(angle, axis);
|
||||
|
||||
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
|
||||
// new rotation and its target. This is the final parent-relative rotation that the tip joint have
|
||||
// make to achieve its target rotation.
|
||||
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation();
|
||||
// new orientation and its target. This is the final parent-relative orientation that the tip joint have
|
||||
// make to achieve its target orientation.
|
||||
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentOrientation) * target.getRotation();
|
||||
|
||||
// enforce tip's constraint
|
||||
RotationConstraint* constraint = getConstraint(tipIndex);
|
||||
|
@ -212,7 +229,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
if (constrained) {
|
||||
// The tip's final parent-relative rotation would violate its constraint
|
||||
// so we try to pre-twist this pivot to compensate.
|
||||
glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation;
|
||||
glm::quat constrainedTipRotation = deltaRotation * tipParentOrientation * tipRelativeRotation;
|
||||
glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation);
|
||||
glm::quat swingPart;
|
||||
glm::quat twistPart;
|
||||
|
@ -227,9 +244,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
} else if (targetType == IKTarget::Type::HmdHead) {
|
||||
// An HmdHead target slaves the orientation of the end-effector by distributing rotation
|
||||
// deltas up the hierarchy. Its target position is enforced later by shifting the hips.
|
||||
deltaRotation = target.getRotation() * glm::inverse(tipRotation);
|
||||
deltaRotation = target.getRotation() * glm::inverse(tipOrientation);
|
||||
float dotSign = copysignf(1.0f, deltaRotation.w);
|
||||
const float ANGLE_DISTRIBUTION_FACTOR = 0.15f;
|
||||
const float ANGLE_DISTRIBUTION_FACTOR = 0.45f;
|
||||
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * deltaRotation, ANGLE_DISTRIBUTION_FACTOR));
|
||||
}
|
||||
|
||||
|
@ -245,12 +262,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
if (constraint) {
|
||||
bool constrained = constraint->apply(newRot);
|
||||
if (constrained) {
|
||||
// the constraint will modify the movement of the tip so we have to compute the modified
|
||||
// model-frame deltaRotation
|
||||
// the constraint will modify the local rotation of the tip so we must
|
||||
// compute the corresponding model-frame deltaRotation
|
||||
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
|
||||
deltaRotation = absolutePoses[pivotsParentIndex].rot *
|
||||
newRot *
|
||||
glm::inverse(absolutePoses[pivotIndex].rot);
|
||||
newRot * glm::inverse(absolutePoses[pivotIndex].rot);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,8 +280,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
|
||||
// keep track of tip's new transform as we descend towards root
|
||||
tipPosition = jointPosition + deltaRotation * leverArm;
|
||||
tipRotation = glm::normalize(deltaRotation * tipRotation);
|
||||
tipParentRotation = glm::normalize(deltaRotation * tipParentRotation);
|
||||
tipOrientation = glm::normalize(deltaRotation * tipOrientation);
|
||||
tipParentOrientation = glm::normalize(deltaRotation * tipParentOrientation);
|
||||
|
||||
pivotIndex = pivotsParentIndex;
|
||||
pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
|
||||
|
@ -360,13 +376,26 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
++constraintItr;
|
||||
}
|
||||
} else {
|
||||
// shift the hips according to the offset from the previous frame
|
||||
// shift the everything according to the _hipsOffset from the previous frame
|
||||
float offsetLength = glm::length(_hipsOffset);
|
||||
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
||||
if (offsetLength > MIN_HIPS_OFFSET_LENGTH) {
|
||||
// but only if offset is long enough
|
||||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset;
|
||||
if (_hipsParentIndex == -1) {
|
||||
// the hips are the root so _hipsOffset is in the correct frame
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset;
|
||||
} else {
|
||||
// the hips are NOT the root so we need to transform _hipsOffset into hips local-frame
|
||||
glm::quat hipsFrameRotation = _relativePoses[_hipsParentIndex].rot;
|
||||
int index = _skeleton->getParentIndex(_hipsParentIndex);
|
||||
while (index != -1) {
|
||||
hipsFrameRotation *= _relativePoses[index].rot;
|
||||
index = _skeleton->getParentIndex(index);
|
||||
}
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans
|
||||
+ glm::inverse(glm::normalize(hipsFrameRotation)) * (scaleFactor * _hipsOffset);
|
||||
}
|
||||
}
|
||||
|
||||
solveWithCyclicCoordinateDescent(targets);
|
||||
|
@ -605,7 +634,7 @@ void AnimInverseKinematics::initConstraints() {
|
|||
} else if (baseName.startsWith("Spine", Qt::CaseInsensitive)) {
|
||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||
const float MAX_SPINE_TWIST = PI / 8.0f;
|
||||
const float MAX_SPINE_TWIST = PI / 12.0f;
|
||||
stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST);
|
||||
|
||||
std::vector<float> minDots;
|
||||
|
@ -629,11 +658,11 @@ void AnimInverseKinematics::initConstraints() {
|
|||
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
|
||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||
const float MAX_NECK_TWIST = PI / 4.0f;
|
||||
const float MAX_NECK_TWIST = PI / 9.0f;
|
||||
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
|
||||
|
||||
std::vector<float> minDots;
|
||||
const float MAX_NECK_SWING = PI / 3.0f;
|
||||
const float MAX_NECK_SWING = PI / 8.0f;
|
||||
minDots.push_back(cosf(MAX_NECK_SWING));
|
||||
stConstraint->setSwingLimits(minDots);
|
||||
|
||||
|
@ -641,11 +670,11 @@ void AnimInverseKinematics::initConstraints() {
|
|||
} else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) {
|
||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||
const float MAX_HEAD_TWIST = PI / 4.0f;
|
||||
const float MAX_HEAD_TWIST = PI / 9.0f;
|
||||
stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST);
|
||||
|
||||
std::vector<float> minDots;
|
||||
const float MAX_HEAD_SWING = PI / 4.0f;
|
||||
const float MAX_HEAD_SWING = PI / 10.0f;
|
||||
minDots.push_back(cosf(MAX_HEAD_SWING));
|
||||
stConstraint->setSwingLimits(minDots);
|
||||
|
||||
|
@ -759,9 +788,13 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
|||
initConstraints();
|
||||
_headIndex = _skeleton->nameToJointIndex("Head");
|
||||
_hipsIndex = _skeleton->nameToJointIndex("Hips");
|
||||
|
||||
// also cache the _hipsParentIndex for later
|
||||
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
|
||||
} else {
|
||||
clearConstraints();
|
||||
_headIndex = -1;
|
||||
_hipsIndex = -1;
|
||||
_hipsParentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,13 +79,14 @@ protected:
|
|||
AnimPoseVec _relativePoses; // current relative poses
|
||||
|
||||
// experimental data for moving hips during IK
|
||||
int _headIndex = -1;
|
||||
int _hipsIndex = -1;
|
||||
glm::vec3 _hipsOffset = Vectors::ZERO;
|
||||
glm::vec3 _hipsOffset { Vectors::ZERO };
|
||||
int _headIndex { -1 };
|
||||
int _hipsIndex { -1 };
|
||||
int _hipsParentIndex { -1 };
|
||||
|
||||
// _maxTargetIndex is tracked to help optimize the recalculation of absolute poses
|
||||
// during the the cyclic coordinate descent algorithm
|
||||
int _maxTargetIndex = 0;
|
||||
int _maxTargetIndex { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_AnimInverseKinematics_h
|
||||
|
|
|
@ -59,8 +59,14 @@ const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const {
|
|||
return _absoluteDefaultPoses[jointIndex];
|
||||
}
|
||||
|
||||
const glm::quat AnimSkeleton::getPreRotation(int jointIndex) const {
|
||||
return _joints[jointIndex].preRotation;
|
||||
// get pre multiplied transform which should include FBX pre potations
|
||||
const AnimPose& AnimSkeleton::getPreRotationPose(int jointIndex) const {
|
||||
return _relativePreRotationPoses[jointIndex];
|
||||
}
|
||||
|
||||
// get post multiplied transform which might include FBX offset transformations
|
||||
const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
|
||||
return _relativePostRotationPoses[jointIndex];
|
||||
}
|
||||
|
||||
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
||||
|
@ -99,13 +105,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
|
|||
// build a chache of default poses
|
||||
_absoluteDefaultPoses.reserve(joints.size());
|
||||
_relativeDefaultPoses.reserve(joints.size());
|
||||
_relativePreRotationPoses.reserve(joints.size());
|
||||
_relativePostRotationPoses.reserve(joints.size());
|
||||
|
||||
// iterate over FBXJoints and extract the bind pose information.
|
||||
for (int i = 0; i < (int)joints.size(); i++) {
|
||||
|
||||
// build pre and post transforms
|
||||
glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation);
|
||||
glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform;
|
||||
_relativePreRotationPoses.push_back(AnimPose(preRotationTransform));
|
||||
_relativePostRotationPoses.push_back(AnimPose(postRotationTransform));
|
||||
|
||||
// build relative and absolute default poses
|
||||
glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation);
|
||||
glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform;
|
||||
glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform;
|
||||
AnimPose relDefaultPose(relDefaultMat);
|
||||
_relativeDefaultPoses.push_back(relDefaultPose);
|
||||
int parentIndex = getParentIndex(i);
|
||||
|
|
|
@ -42,8 +42,11 @@ public:
|
|||
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
|
||||
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
|
||||
|
||||
// get pre-rotation aka Maya's joint orientation.
|
||||
const glm::quat getPreRotation(int jointIndex) const;
|
||||
// get pre transform which should include FBX pre potations
|
||||
const AnimPose& getPreRotationPose(int jointIndex) const;
|
||||
|
||||
// get post transform which might include FBX offset transformations
|
||||
const AnimPose& getPostRotationPose(int jointIndex) const;
|
||||
|
||||
int getParentIndex(int jointIndex) const;
|
||||
|
||||
|
@ -64,6 +67,8 @@ protected:
|
|||
AnimPoseVec _relativeBindPoses;
|
||||
AnimPoseVec _relativeDefaultPoses;
|
||||
AnimPoseVec _absoluteDefaultPoses;
|
||||
AnimPoseVec _relativePreRotationPoses;
|
||||
AnimPoseVec _relativePostRotationPoses;
|
||||
|
||||
// no copies
|
||||
AnimSkeleton(const AnimSkeleton&) = delete;
|
||||
|
|
|
@ -18,7 +18,7 @@ const float AnimationLoop::MAXIMUM_POSSIBLE_FRAME = 100000.0f;
|
|||
|
||||
AnimationLoop::AnimationLoop() :
|
||||
_fps(30.0f),
|
||||
_loop(false),
|
||||
_loop(true),
|
||||
_hold(false),
|
||||
_startAutomatically(false),
|
||||
_firstFrame(0.0f),
|
||||
|
|
|
@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::setEnableInverseKinematics(bool enable) {
|
||||
_enableInverseKinematics = enable;
|
||||
}
|
||||
|
||||
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
|
||||
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
||||
return _absoluteDefaultPoses[index];
|
||||
|
@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
}
|
||||
|
||||
t += deltaTime;
|
||||
|
||||
if (_enableInverseKinematics) {
|
||||
_animVars.set("ikOverlayAlpha", 1.0f);
|
||||
} else {
|
||||
_animVars.set("ikOverlayAlpha", 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
_lastFront = front;
|
||||
|
|
|
@ -208,6 +208,8 @@ public:
|
|||
|
||||
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
|
||||
|
||||
void setEnableInverseKinematics(bool enable);
|
||||
|
||||
protected:
|
||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||
void updateAnimationStateHandlers();
|
||||
|
@ -290,6 +292,8 @@ public:
|
|||
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
||||
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
||||
|
||||
bool _enableInverseKinematics { true };
|
||||
|
||||
private:
|
||||
QMap<int, StateHandler> _stateHandlers;
|
||||
int _nextStateHandlerId { 0 };
|
||||
|
|
|
@ -29,7 +29,10 @@ namespace AudioConstants {
|
|||
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
|
||||
const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
|
||||
/ (float)AudioConstants::SAMPLE_RATE) * 1000.0f;
|
||||
const unsigned int NETWORK_FRAME_USECS = (unsigned int)floorf(NETWORK_FRAME_MSECS * 1000.0f);
|
||||
|
||||
// be careful with overflows when using this constant
|
||||
const int NETWORK_FRAME_USECS = static_cast<int>(NETWORK_FRAME_MSECS * 1000.0f);
|
||||
|
||||
const int MIN_SAMPLE_VALUE = std::numeric_limits<AudioSample>::min();
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<AudioSample>::max();
|
||||
}
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
//
|
||||
// AudioEditBuffer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 8/29/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AudioEditBuffer_h
|
||||
#define hifi_AudioEditBuffer_h
|
||||
|
||||
template< typename T >
|
||||
class AudioEditBuffer : public AudioFrameBuffer<T> {
|
||||
|
||||
public:
|
||||
|
||||
AudioEditBuffer();
|
||||
AudioEditBuffer(const uint32_t channelCount, const uint32_t frameCount);
|
||||
~AudioEditBuffer();
|
||||
|
||||
bool getZeroCrossing(uint32_t start, bool direction, float32_t epsilon, uint32_t& zero);
|
||||
|
||||
void linearFade(uint32_t start, uint32_t stop, bool increasing);
|
||||
void exponentialFade(uint32_t start, uint32_t stop, bool increasing);
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
AudioEditBuffer<T>::AudioEditBuffer() :
|
||||
AudioFrameBuffer<T>() {
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
AudioEditBuffer<T>::AudioEditBuffer(const uint32_t channelCount, const uint32_t frameCount) :
|
||||
AudioFrameBuffer<T>(channelCount, frameCount) {
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
AudioEditBuffer<T>::~AudioEditBuffer() {
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool AudioEditBuffer<T>::getZeroCrossing(uint32_t start, bool direction, float32_t epsilon, uint32_t& zero) {
|
||||
|
||||
zero = this->_frameCount;
|
||||
|
||||
if (direction) { // scan from the left
|
||||
if (start < this->_frameCount) {
|
||||
for (uint32_t i = start; i < this->_frameCount; ++i) {
|
||||
for (uint32_t j = 0; j < this->_channelCount; ++j) {
|
||||
if (this->_frameBuffer[j][i] >= -epsilon && this->_frameBuffer[j][i] <= epsilon) {
|
||||
zero = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // scan from the right
|
||||
if (start != 0 && start < this->_frameCount) {
|
||||
for (uint32_t i = start; i != 0; --i) {
|
||||
for (uint32_t j = 0; j < this->_channelCount; ++j) {
|
||||
if (this->_frameBuffer[j][i] >= -epsilon && this->_frameBuffer[j][i] <= epsilon) {
|
||||
zero = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline void AudioEditBuffer<T>::linearFade(uint32_t start, uint32_t stop, bool increasing) {
|
||||
|
||||
if (start >= stop || start > this->_frameCount || stop > this->_frameCount ) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t count = stop - start;
|
||||
float32_t delta;
|
||||
float32_t gain;
|
||||
|
||||
if (increasing) { // 0.0 to 1.0f in delta increments
|
||||
delta = 1.0f / (float32_t)count;
|
||||
gain = 0.0f;
|
||||
} else { // 1.0f to 0.0f in delta increments
|
||||
delta = -1.0f / (float32_t)count;
|
||||
gain = 1.0f;
|
||||
}
|
||||
|
||||
for (uint32_t i = start; i < stop; ++i) {
|
||||
for (uint32_t j = 0; j < this->_channelCount; ++j) {
|
||||
this->_frameBuffer[j][i] *= gain;
|
||||
}
|
||||
gain += delta;
|
||||
}
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline void AudioEditBuffer<T>::exponentialFade(uint32_t start, uint32_t stop, bool increasing) {
|
||||
// TBD
|
||||
}
|
||||
|
||||
typedef AudioEditBuffer< float32_t > AudioEditBufferFloat32;
|
||||
typedef AudioEditBuffer< int32_t > AudioEditBufferSInt32;
|
||||
|
||||
#endif // hifi_AudioEditBuffer_h
|
||||
|
|
@ -158,11 +158,10 @@ bool AudioInjector::injectLocally() {
|
|||
}
|
||||
|
||||
const uchar MAX_INJECTOR_VOLUME = 0xFF;
|
||||
static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0;
|
||||
static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1;
|
||||
|
||||
uint64_t AudioInjector::injectNextFrame() {
|
||||
static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1;
|
||||
static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
|
||||
|
||||
int64_t AudioInjector::injectNextFrame() {
|
||||
if (_state == AudioInjector::State::Finished) {
|
||||
qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning.";
|
||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||
|
@ -182,6 +181,15 @@ uint64_t AudioInjector::injectNextFrame() {
|
|||
// make sure we actually have samples downloaded to inject
|
||||
if (_audioData.size()) {
|
||||
|
||||
int sampleSize = (_options.stereo ? 2 : 1) * sizeof(AudioConstants::AudioSample);
|
||||
auto numSamples = static_cast<int>(_audioData.size() / sampleSize);
|
||||
auto targetSize = numSamples * sampleSize;
|
||||
if (targetSize != _audioData.size()) {
|
||||
qDebug() << "Resizing audio that doesn't end at multiple of sample size, resizing from "
|
||||
<< _audioData.size() << " to " << targetSize;
|
||||
_audioData.resize(targetSize);
|
||||
}
|
||||
|
||||
_outgoingSequenceNumber = 0;
|
||||
_nextFrame = 0;
|
||||
|
||||
|
@ -206,7 +214,7 @@ uint64_t AudioInjector::injectNextFrame() {
|
|||
audioPacketStream << _options.stereo;
|
||||
|
||||
// pack the flag for loopback
|
||||
uchar loopbackFlag = (uchar) true;
|
||||
uchar loopbackFlag = (uchar)true;
|
||||
audioPacketStream << loopbackFlag;
|
||||
|
||||
// pack the position for injected audio
|
||||
|
@ -238,16 +246,19 @@ uint64_t AudioInjector::injectNextFrame() {
|
|||
}
|
||||
}
|
||||
|
||||
int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
|
||||
_audioData.size() - _currentSendOffset);
|
||||
int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||
if (!_options.loop) {
|
||||
// If we aren't looping, let's make sure we don't read past the end
|
||||
totalBytesLeftToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset);
|
||||
}
|
||||
|
||||
// Measure the loudness of this frame
|
||||
_loudness = 0.0f;
|
||||
for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) {
|
||||
_loudness += abs(*reinterpret_cast<int16_t*>(_audioData.data() + _currentSendOffset + i)) /
|
||||
(AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
|
||||
for (int i = 0; i < totalBytesLeftToCopy; i += sizeof(int16_t)) {
|
||||
_loudness += abs(*reinterpret_cast<int16_t*>(_audioData.data() + ((_currentSendOffset + i) % _audioData.size()))) /
|
||||
(AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
|
||||
}
|
||||
_loudness /= (float)(bytesToCopy / sizeof(int16_t));
|
||||
_loudness /= (float)(totalBytesLeftToCopy/ sizeof(int16_t));
|
||||
|
||||
_currentPacket->seek(0);
|
||||
|
||||
|
@ -264,8 +275,16 @@ uint64_t AudioInjector::injectNextFrame() {
|
|||
|
||||
_currentPacket->seek(audioDataOffset);
|
||||
|
||||
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
||||
_currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy);
|
||||
while (totalBytesLeftToCopy > 0) {
|
||||
int bytesToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset);
|
||||
|
||||
_currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy);
|
||||
_currentSendOffset += bytesToCopy;
|
||||
totalBytesLeftToCopy -= bytesToCopy;
|
||||
if (_options.loop && _currentSendOffset >= _audioData.size()) {
|
||||
_currentSendOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// set the correct size used for this packet
|
||||
_currentPacket->setPayloadSize(_currentPacket->pos());
|
||||
|
@ -280,33 +299,32 @@ uint64_t AudioInjector::injectNextFrame() {
|
|||
_outgoingSequenceNumber++;
|
||||
}
|
||||
|
||||
_currentSendOffset += bytesToCopy;
|
||||
|
||||
if (_currentSendOffset >= _audioData.size()) {
|
||||
// we're at the end of the audio data to send
|
||||
if (_options.loop) {
|
||||
// we were asked to loop, set our send offset to 0
|
||||
_currentSendOffset = 0;
|
||||
} else {
|
||||
// we weren't to loop, say that we're done now
|
||||
finish();
|
||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||
}
|
||||
if (_currentSendOffset >= _audioData.size() && !_options.loop) {
|
||||
finish();
|
||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||
}
|
||||
|
||||
if (_currentSendOffset == bytesToCopy) {
|
||||
if (!_hasSentFirstFrame) {
|
||||
_hasSentFirstFrame = true;
|
||||
// ask AudioInjectorManager to call us right away again to
|
||||
// immediately send the first two frames so the mixer can start using the audio right away
|
||||
return NEXT_FRAME_DELTA_IMMEDIATELY;
|
||||
} else {
|
||||
uint64_t untilNextFrame = (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000;
|
||||
if (untilNextFrame >= 10 * AudioConstants::NETWORK_FRAME_USECS) {
|
||||
qDebug() << "AudioInjector returning delta to next frame that is very large -" << untilNextFrame;
|
||||
qDebug() << "Next Frame is " << _nextFrame << "frameTimer nsecsElapsed is" << _frameTimer->nsecsElapsed();
|
||||
}
|
||||
return untilNextFrame;
|
||||
}
|
||||
|
||||
const int MAX_ALLOWED_FRAMES_TO_FALL_BEHIND = 7;
|
||||
int64_t currentTime = _frameTimer->nsecsElapsed() / 1000;
|
||||
auto currentFrameBasedOnElapsedTime = currentTime / AudioConstants::NETWORK_FRAME_USECS;
|
||||
|
||||
if (currentFrameBasedOnElapsedTime - _nextFrame > MAX_ALLOWED_FRAMES_TO_FALL_BEHIND) {
|
||||
// If we are falling behind by more frames than our threshold, let's skip the frames ahead
|
||||
qDebug() << this << "injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames";
|
||||
_nextFrame = currentFrameBasedOnElapsedTime;
|
||||
_currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData.size();
|
||||
}
|
||||
|
||||
int64_t playNextFrameAt = ++_nextFrame * AudioConstants::NETWORK_FRAME_USECS;
|
||||
|
||||
return std::max(INT64_C(0), playNextFrameAt - currentTime);
|
||||
}
|
||||
|
||||
void AudioInjector::stop() {
|
||||
|
|
|
@ -84,21 +84,22 @@ private slots:
|
|||
|
||||
private:
|
||||
void setupInjection();
|
||||
uint64_t injectNextFrame();
|
||||
int64_t injectNextFrame();
|
||||
bool injectLocally();
|
||||
|
||||
QByteArray _audioData;
|
||||
AudioInjectorOptions _options;
|
||||
State _state { State::NotFinished };
|
||||
bool _hasSetup = false;
|
||||
bool _shouldStop = false;
|
||||
float _loudness = 0.0f;
|
||||
int _currentSendOffset = 0;
|
||||
bool _hasSentFirstFrame { false };
|
||||
bool _hasSetup { false };
|
||||
bool _shouldStop { false };
|
||||
float _loudness { 0.0f };
|
||||
int _currentSendOffset { 0 };
|
||||
std::unique_ptr<NLPacket> _currentPacket { nullptr };
|
||||
AbstractAudioInterface* _localAudioInterface { nullptr };
|
||||
AudioInjectorLocalBuffer* _localBuffer { nullptr };
|
||||
|
||||
int _nextFrame { 0 };
|
||||
int64_t _nextFrame { 0 };
|
||||
std::unique_ptr<QElapsedTimer> _frameTimer { nullptr };
|
||||
quint16 _outgoingSequenceNumber { 0 };
|
||||
|
||||
|
|
|
@ -87,8 +87,8 @@ void AudioInjectorManager::run() {
|
|||
if (!injector.isNull()) {
|
||||
// this is an injector that's ready to go, have it send a frame now
|
||||
auto nextCallDelta = injector->injectNextFrame();
|
||||
|
||||
if (nextCallDelta > 0 && !injector->isFinished()) {
|
||||
|
||||
if (nextCallDelta >= 0 && !injector->isFinished()) {
|
||||
// re-enqueue the injector with the correct timing
|
||||
_injectors.emplace(usecTimestampNow() + nextCallDelta, injector);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "AudioRingBuffer.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioEditBuffer.h"
|
||||
#include "AudioLogging.h"
|
||||
#include "Sound.h"
|
||||
|
||||
|
@ -69,7 +68,6 @@ void Sound::downloadFinished(const QByteArray& data) {
|
|||
|
||||
interpretAsWav(rawAudioByteArray, outputAudioByteArray);
|
||||
downSample(outputAudioByteArray);
|
||||
trimFrames();
|
||||
} else if (fileName.endsWith(RAW_EXTENSION)) {
|
||||
// check if this was a stereo raw file
|
||||
// since it's raw the only way for us to know that is if the file was called .stereo.raw
|
||||
|
@ -80,7 +78,6 @@ void Sound::downloadFinished(const QByteArray& data) {
|
|||
|
||||
// Process as RAW file
|
||||
downSample(rawAudioByteArray);
|
||||
trimFrames();
|
||||
} else {
|
||||
qCDebug(audio) << "Unknown sound file type";
|
||||
}
|
||||
|
@ -98,11 +95,23 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
|||
|
||||
int numSourceSamples = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
|
||||
|
||||
int numDestinationBytes = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
|
||||
if (_isStereo && numSourceSamples % 2 != 0) {
|
||||
numDestinationBytes += sizeof(AudioConstants::AudioSample);
|
||||
if (_isStereo && numSourceSamples % 2 != 0){
|
||||
// in the unlikely case that we have stereo audio but we seem to be missing a sample
|
||||
// (the sample for one channel is missing in a set of interleaved samples)
|
||||
// then drop the odd sample
|
||||
--numSourceSamples;
|
||||
}
|
||||
|
||||
int numDestinationSamples = numSourceSamples / 2.0f;
|
||||
|
||||
if (_isStereo && numDestinationSamples % 2 != 0) {
|
||||
// if this is stereo we need to make sure we produce stereo output
|
||||
// which means we should have an even number of output samples
|
||||
numDestinationSamples += 1;
|
||||
}
|
||||
|
||||
int numDestinationBytes = numDestinationSamples * sizeof(AudioConstants::AudioSample);
|
||||
|
||||
_byteArray.resize(numDestinationBytes);
|
||||
|
||||
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
|
||||
|
@ -129,26 +138,6 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
|||
}
|
||||
}
|
||||
|
||||
void Sound::trimFrames() {
|
||||
|
||||
const uint32_t inputFrameCount = _byteArray.size() / sizeof(int16_t);
|
||||
const uint32_t trimCount = 1024; // number of leading and trailing frames to trim
|
||||
|
||||
if (inputFrameCount <= (2 * trimCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t* inputFrameData = (int16_t*)_byteArray.data();
|
||||
|
||||
AudioEditBufferFloat32 editBuffer(1, inputFrameCount);
|
||||
editBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/);
|
||||
|
||||
editBuffer.linearFade(0, trimCount, true);
|
||||
editBuffer.linearFade(inputFrameCount - trimCount, inputFrameCount, false);
|
||||
|
||||
editBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
|
||||
}
|
||||
|
||||
//
|
||||
// Format description from https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
||||
//
|
||||
|
|
|
@ -38,7 +38,6 @@ private:
|
|||
bool _isStereo;
|
||||
bool _isReady;
|
||||
|
||||
void trimFrames();
|
||||
void downSample(const QByteArray& rawAudioByteArray);
|
||||
void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <QVariantGLM.h>
|
||||
#include <Transform.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
|
|||
assert(false);
|
||||
return glm::vec3();
|
||||
}
|
||||
|
||||
QVariant AttachmentData::toVariant() const {
|
||||
QVariantMap result;
|
||||
result["modelUrl"] = modelURL;
|
||||
result["jointName"] = jointName;
|
||||
result["translation"] = glmToQMap(translation);
|
||||
result["rotation"] = glmToQMap(glm::degrees(safeEulerAngles(rotation)));
|
||||
result["scale"] = scale;
|
||||
result["soft"] = isSoft;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 variantToVec3(const QVariant& var) {
|
||||
auto map = var.toMap();
|
||||
glm::vec3 result;
|
||||
result.x = map["x"].toFloat();
|
||||
result.y = map["y"].toFloat();
|
||||
result.z = map["z"].toFloat();
|
||||
return result;
|
||||
}
|
||||
|
||||
void AttachmentData::fromVariant(const QVariant& variant) {
|
||||
auto map = variant.toMap();
|
||||
if (map.contains("modelUrl")) {
|
||||
auto urlString = map["modelUrl"].toString();
|
||||
modelURL = urlString;
|
||||
}
|
||||
if (map.contains("jointName")) {
|
||||
jointName = map["jointName"].toString();
|
||||
}
|
||||
if (map.contains("translation")) {
|
||||
translation = variantToVec3(map["translation"]);
|
||||
}
|
||||
if (map.contains("rotation")) {
|
||||
rotation = glm::quat(glm::radians(variantToVec3(map["rotation"])));
|
||||
}
|
||||
if (map.contains("scale")) {
|
||||
scale = map["scale"].toFloat();
|
||||
}
|
||||
if (map.contains("soft")) {
|
||||
isSoft = map["soft"].toBool();
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList AvatarData::getAttachmentsVariant() const {
|
||||
QVariantList result;
|
||||
for (const auto& attachment : getAttachmentData()) {
|
||||
result.append(attachment.toVariant());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
|
||||
QVector<AttachmentData> newAttachments;
|
||||
newAttachments.reserve(variant.size());
|
||||
for (const auto& attachmentVar : variant) {
|
||||
AttachmentData attachment;
|
||||
attachment.fromVariant(attachmentVar);
|
||||
newAttachments.append(attachment);
|
||||
}
|
||||
setAttachmentData(newAttachments);
|
||||
}
|
||||
|
|
|
@ -277,6 +277,9 @@ public:
|
|||
|
||||
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
|
||||
|
||||
Q_INVOKABLE QVariantList getAttachmentsVariant() const;
|
||||
Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant);
|
||||
|
||||
void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
||||
|
||||
// key state
|
||||
|
@ -448,6 +451,9 @@ public:
|
|||
|
||||
QJsonObject toJson() const;
|
||||
void fromJson(const QJsonObject& json);
|
||||
|
||||
QVariant toVariant() const;
|
||||
void fromVariant(const QVariant& variant);
|
||||
};
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
||||
|
|
|
@ -763,7 +763,7 @@ void EntityItem::adjustEditPacketForClockSkew(QByteArray& buffer, int clockSkew)
|
|||
// lastEdited
|
||||
quint64 lastEditedInLocalTime;
|
||||
memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
|
||||
quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
|
||||
quint64 lastEditedInServerTime = lastEditedInLocalTime > 0 ? lastEditedInLocalTime + clockSkew : 0;
|
||||
memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities, "EntityItem::adjustEditPacketForClockSkew()...");
|
||||
|
@ -812,6 +812,14 @@ void EntityItem::setMass(float mass) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::setHref(QString value) {
|
||||
auto href = value.toLower();
|
||||
if (! (value.toLower().startsWith("hifi://")) ) {
|
||||
return;
|
||||
}
|
||||
_href = value;
|
||||
}
|
||||
|
||||
void EntityItem::simulate(const quint64& now) {
|
||||
if (_lastSimulated == 0) {
|
||||
_lastSimulated = now;
|
||||
|
|
|
@ -172,7 +172,7 @@ public:
|
|||
|
||||
// Hyperlink related getters and setters
|
||||
QString getHref() const { return _href; }
|
||||
void setHref(QString value) { _href = value; }
|
||||
void setHref(QString value);
|
||||
|
||||
QString getDescription() const { return _description; }
|
||||
void setDescription(QString value) { _description = value; }
|
||||
|
|
|
@ -321,6 +321,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
QScriptValue properties = engine->newObject();
|
||||
EntityItemProperties defaultEntityProperties;
|
||||
|
||||
if (_created == UNKNOWN_CREATED_TIME) {
|
||||
// No entity properties can have been set so return without setting any default, zero property values.
|
||||
return properties;
|
||||
}
|
||||
|
||||
if (_idSet) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(id, _id.toString());
|
||||
}
|
||||
|
|
|
@ -252,6 +252,8 @@ public:
|
|||
|
||||
void setLinePointsDirty() {_linePointsChanged = true; }
|
||||
|
||||
void setQueryAACubeDirty() { _queryAACubeChanged = true; }
|
||||
|
||||
void setCreated(QDateTime& v);
|
||||
|
||||
bool hasTerseUpdateChanges() const;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define GPU_LEGACY 0
|
||||
#define GPU_CORE_41 410
|
||||
#define GPU_CORE_43 430
|
||||
#define GPU_CORE_MINIMUM GPU_CORE_41
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
|
|
|
@ -338,6 +338,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
|||
_updateTimer.start();
|
||||
|
||||
_qmlComponent = new QQmlComponent(_qmlEngine);
|
||||
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
||||
|
|
61
libraries/gl/src/gl/OpenGLVersionChecker.cpp
Normal file
61
libraries/gl/src/gl/OpenGLVersionChecker.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// OpenGLVersionChecker.cpp
|
||||
// libraries/gl/src/gl
|
||||
//
|
||||
// Created by David Rowe on 28 Jan 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "OpenGLVersionChecker.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "Config.h"
|
||||
#include "GLWidget.h"
|
||||
|
||||
OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) :
|
||||
QApplication(argc, argv)
|
||||
{
|
||||
}
|
||||
|
||||
bool OpenGLVersionChecker::isValidVersion() {
|
||||
bool valid = true;
|
||||
|
||||
// Retrieve OpenGL version
|
||||
GLWidget* glWidget = new GLWidget();
|
||||
glWidget->initializeGL();
|
||||
QString glVersion = QString((const char*)glGetString(GL_VERSION));
|
||||
delete glWidget;
|
||||
|
||||
// Compare against minimum
|
||||
// The GL_VERSION string begins with a version number in one of these forms:
|
||||
// - major_number.minor_number
|
||||
// - major_number.minor_number.release_number
|
||||
// Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml
|
||||
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
|
||||
int majorNumber = versionParts[0].toInt();
|
||||
int minorNumber = versionParts[1].toInt();
|
||||
int minimumMajorNumber = GPU_CORE_MINIMUM / 100;
|
||||
int minimumMinorNumber = (GPU_CORE_MINIMUM - minimumMajorNumber * 100) / 10;
|
||||
valid = (majorNumber > minimumMajorNumber
|
||||
|| (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber));
|
||||
|
||||
// Prompt user if below minimum
|
||||
if (!valid) {
|
||||
QMessageBox messageBox;
|
||||
messageBox.setWindowTitle("OpenGL Version Too Low");
|
||||
messageBox.setIcon(QMessageBox::Warning);
|
||||
messageBox.setText(QString().sprintf("Your OpenGL version of %i.%i is lower than the minimum of %i.%i.",
|
||||
majorNumber, minorNumber, minimumMajorNumber, minimumMinorNumber));
|
||||
messageBox.setInformativeText("Press OK to exit; Ignore to continue.");
|
||||
messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Ignore);
|
||||
messageBox.setDefaultButton(QMessageBox::Ok);
|
||||
valid = messageBox.exec() == QMessageBox::Ignore;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
25
libraries/gl/src/gl/OpenGLVersionChecker.h
Normal file
25
libraries/gl/src/gl/OpenGLVersionChecker.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// OpenGLVersionChecker.h
|
||||
// libraries/gl/src/gl
|
||||
//
|
||||
// Created by David Rowe on 28 Jan 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_OpenGLVersionChecker_h
|
||||
#define hifi_OpenGLVersionChecker_h
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
class OpenGLVersionChecker : public QApplication {
|
||||
|
||||
public:
|
||||
OpenGLVersionChecker(int& argc, char** argv);
|
||||
|
||||
static bool isValidVersion();
|
||||
};
|
||||
|
||||
#endif // hifi_OpenGLVersionChecker_h
|
|
@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p
|
|||
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
|
||||
QSharedMemory sharedMem(key);
|
||||
if (!sharedMem.attach(QSharedMemory::ReadOnly)) {
|
||||
qWarning() << "Could not attach to shared memory at key" << key;
|
||||
qCWarning(networking) << "Could not attach to shared memory at key" << key;
|
||||
return false;
|
||||
} else {
|
||||
sharedMem.lock();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue