mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-12 17:38:58 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into animate-iff-visible
This commit is contained in:
commit
fa40e347ee
54 changed files with 1537 additions and 1175 deletions
|
@ -1172,15 +1172,15 @@ void OctreeServer::domainSettingsRequestComplete() {
|
||||||
pathToCopyFrom = oldDefaultPersistPath;
|
pathToCopyFrom = oldDefaultPersistPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDir persistFileDirectory = QDir(persistPath).filePath("..");
|
||||||
|
if (!persistFileDirectory.exists()) {
|
||||||
|
qDebug() << "Creating data directory " << persistFileDirectory.absolutePath();
|
||||||
|
persistFileDirectory.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldCopy) {
|
if (shouldCopy) {
|
||||||
qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistPath;
|
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);
|
QFile::copy(pathToCopyFrom, persistPath);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "No existing persist file found";
|
qDebug() << "No existing persist file found";
|
||||||
|
|
|
@ -28,6 +28,15 @@ macro(GENERATE_INSTALLERS)
|
||||||
# include CMake module that will install compiler system libraries
|
# include CMake module that will install compiler system libraries
|
||||||
# so that we have msvcr120 and msvcp120 installed with targets
|
# so that we have msvcr120 and msvcp120 installed with targets
|
||||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
|
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)
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
|
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(DS_EXEC_NAME "domain-server.exe")
|
||||||
set(AC_EXEC_NAME "assignment-client.exe")
|
set(AC_EXEC_NAME "assignment-client.exe")
|
||||||
|
|
||||||
# start menu shortcuts
|
# shortcut names
|
||||||
set(INTERFACE_SM_SHORTCUT_NAME "High Fidelity")
|
if (PRODUCTION_BUILD)
|
||||||
set(CONSOLE_SM_SHORTCUT_NAME "Server Console")
|
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
|
# check if we need to find signtool
|
||||||
if (PRODUCTION_BUILD OR PR_BUILD)
|
if (PRODUCTION_BUILD OR PR_BUILD)
|
||||||
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64")
|
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
|
# 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(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
||||||
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
|
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(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
||||||
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
||||||
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
|
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
|
||||||
|
|
|
@ -12,13 +12,7 @@
|
||||||
|
|
||||||
Var MUI_TEMP
|
Var MUI_TEMP
|
||||||
Var STARTMENU_FOLDER
|
Var STARTMENU_FOLDER
|
||||||
Var SV_ALLUSERS
|
|
||||||
Var START_MENU
|
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
|
;Include Modern UI
|
||||||
|
@ -205,346 +199,15 @@ Var AR_RegFlags
|
||||||
!define MUI_HEADERIMAGE_UNBITMAP "@UNINSTALLER_HEADER_IMAGE@"
|
!define MUI_HEADERIMAGE_UNBITMAP "@UNINSTALLER_HEADER_IMAGE@"
|
||||||
!define MUI_ABORTWARNING
|
!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 #
|
# 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
|
Function ConditionalAddToRegisty
|
||||||
Pop $0
|
Pop $0
|
||||||
Pop $1
|
Pop $1
|
||||||
StrCmp "$0" "" ConditionalAddToRegisty_EmptyString
|
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"
|
"$1" "$0"
|
||||||
;MessageBox MB_OK "Set Registry: '$1' to '$0'"
|
;MessageBox MB_OK "Set Registry: '$1' to '$0'"
|
||||||
DetailPrint "Set install registry entry: '$1' to '$0'"
|
DetailPrint "Set install registry entry: '$1' to '$0'"
|
||||||
|
@ -598,11 +261,10 @@ FunctionEnd
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
|
||||||
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
|
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
|
||||||
Page custom InstallOptionsPage
|
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
|
||||||
;Start Menu Folder Page Configuration
|
;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_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
|
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
|
||||||
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
|
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
|
||||||
|
@ -678,7 +340,6 @@ FunctionEnd
|
||||||
;Keep these lines before any File command
|
;Keep these lines before any File command
|
||||||
;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA)
|
;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA)
|
||||||
|
|
||||||
ReserveFile "NSIS.InstallOptions.ini"
|
|
||||||
ReserveFile "@POST_INSTALL_OPTIONS_PATH@"
|
ReserveFile "@POST_INSTALL_OPTIONS_PATH@"
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
@ -717,7 +378,7 @@ Section "-Core installation"
|
||||||
@CPACK_NSIS_FULL_INSTALL@
|
@CPACK_NSIS_FULL_INSTALL@
|
||||||
|
|
||||||
;Store installation folder
|
;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
|
;Package the signed uninstaller produced by the inner loop
|
||||||
!ifndef INNER
|
!ifndef INNER
|
||||||
|
@ -768,10 +429,10 @@ Section "-Core installation"
|
||||||
Push "Contact"
|
Push "Contact"
|
||||||
Push "@CPACK_NSIS_CONTACT@"
|
Push "@CPACK_NSIS_CONTACT@"
|
||||||
Call ConditionalAddToRegisty
|
Call ConditionalAddToRegisty
|
||||||
!insertmacro INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State"
|
|
||||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||||
|
|
||||||
;Create shortcuts
|
;Create shortcuts
|
||||||
|
|
||||||
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
|
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
|
||||||
@CPACK_NSIS_CREATE_ICONS@
|
@CPACK_NSIS_CREATE_ICONS@
|
||||||
@CPACK_NSIS_CREATE_ICONS_EXTRA@
|
@CPACK_NSIS_CREATE_ICONS_EXTRA@
|
||||||
|
@ -799,27 +460,10 @@ Section "-Core installation"
|
||||||
|
|
||||||
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@"
|
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
|
; Write special uninstall registry entries
|
||||||
Push "StartMenu"
|
Push "StartMenu"
|
||||||
Push "$STARTMENU_FOLDER"
|
Push "$STARTMENU_FOLDER"
|
||||||
Call ConditionalAddToRegisty
|
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
|
!insertmacro MUI_STARTMENU_WRITE_END
|
||||||
|
|
||||||
|
@ -827,22 +471,6 @@ Section "-Core installation"
|
||||||
|
|
||||||
SectionEnd
|
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
|
; Make sure nsDialogs is included before we use it
|
||||||
!include "nsdialogs.nsh"
|
!include "nsdialogs.nsh"
|
||||||
|
|
||||||
|
@ -908,7 +536,6 @@ Function PostInstallOptionsPage
|
||||||
|
|
||||||
; set the checkbox state depending on what is present in the registry
|
; set the checkbox state depending on what is present in the registry
|
||||||
!insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED}
|
!insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED}
|
||||||
|
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||||
|
@ -941,6 +568,16 @@ Function PostInstallOptionsPage
|
||||||
!insertmacro SetPostInstallOption $LaunchNowCheckbox @LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
!insertmacro SetPostInstallOption $LaunchNowCheckbox @LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||||
|
|
||||||
${If} @PR_BUILD@ == 1
|
${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
|
; push the offset
|
||||||
IntOp $CurrentOffset $CurrentOffset + 15
|
IntOp $CurrentOffset $CurrentOffset + 15
|
||||||
|
|
||||||
|
@ -993,8 +630,17 @@ Function HandlePostInstallOptions
|
||||||
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
|
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
|
||||||
|
|
||||||
${If} $ServerStartupState == ${BST_CHECKED}
|
${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@"
|
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
|
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
|
||||||
${Else}
|
${Else}
|
||||||
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
|
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
|
||||||
|
@ -1002,6 +648,7 @@ Function HandlePostInstallOptions
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
${If} @PR_BUILD@ == 1
|
${If} @PR_BUILD@ == 1
|
||||||
|
|
||||||
; check if we need to copy settings/content from production for this PR build
|
; check if we need to copy settings/content from production for this PR build
|
||||||
${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState
|
${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."
|
"There was a problem copying your production content and settings to $0 for this PR build.$\r$\n$\r$\nPlease copy them manually."
|
||||||
|
|
||||||
NoError:
|
NoError:
|
||||||
|
|
||||||
|
SetShellVarContext all
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
@ -1050,19 +699,30 @@ FunctionEnd
|
||||||
!include nsProcess.nsh
|
!include nsProcess.nsh
|
||||||
|
|
||||||
!macro PromptForRunningApplication applicationName displayName action prompter
|
!macro PromptForRunningApplication applicationName displayName action prompter
|
||||||
|
!define UniqueID ${__LINE__}
|
||||||
|
|
||||||
|
Prompt_${UniqueID}:
|
||||||
|
|
||||||
${nsProcess::FindProcess} ${applicationName} $R0
|
${nsProcess::FindProcess} ${applicationName} $R0
|
||||||
|
|
||||||
${If} $R0 == 0
|
${If} $R0 == 0
|
||||||
; the process is running, ask the user if they want us to close it
|
|
||||||
MessageBox MB_OK|MB_ICONEXCLAMATION \
|
; the process is running, ask the user to close it
|
||||||
"${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and try again."
|
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
|
Abort
|
||||||
|
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
!undef UniqueID
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro CheckForRunningApplications action prompter
|
!macro CheckForRunningApplications action prompter
|
||||||
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "High Fidelity client" ${action} ${prompter}
|
||||||
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "Server Console" ${action} ${prompter}
|
||||||
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
|
||||||
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
|
||||||
!macroend
|
!macroend
|
||||||
|
@ -1109,26 +769,6 @@ Function un.onInit
|
||||||
Quit
|
Quit
|
||||||
${EndSwitch}
|
${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
|
FunctionEnd
|
||||||
|
|
||||||
;--- Add/Remove callback functions: ---
|
;--- Add/Remove callback functions: ---
|
||||||
|
@ -1186,19 +826,12 @@ FunctionEnd
|
||||||
!ifdef INNER
|
!ifdef INNER
|
||||||
Section "Uninstall"
|
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"
|
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu"
|
||||||
;MessageBox MB_OK "Start menu is in: $START_MENU"
|
;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@
|
@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@
|
||||||
|
|
||||||
|
@ -1217,13 +850,13 @@ Section "Uninstall"
|
||||||
|
|
||||||
;Remove the uninstaller itself.
|
;Remove the uninstaller itself.
|
||||||
Delete "$INSTDIR\@UNINSTALLER_NAME@"
|
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.
|
;Remove the installation directory if it is empty.
|
||||||
RMDir "$INSTDIR"
|
RMDir "$INSTDIR"
|
||||||
|
|
||||||
; Remove the registry entries.
|
; 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
|
; Removes all optional components
|
||||||
!insertmacro SectionList "RemoveSection_CPack"
|
!insertmacro SectionList "RemoveSection_CPack"
|
||||||
|
@ -1235,7 +868,12 @@ Section "Uninstall"
|
||||||
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||||
Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk"
|
Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk"
|
||||||
Delete "$DESKTOP\@CONSOLE_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"
|
Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||||
|
SetShellVarContext all
|
||||||
|
|
||||||
@CPACK_NSIS_DELETE_ICONS@
|
@CPACK_NSIS_DELETE_ICONS@
|
||||||
@CPACK_NSIS_DELETE_ICONS_EXTRA@
|
@CPACK_NSIS_DELETE_ICONS_EXTRA@
|
||||||
|
|
||||||
|
@ -1264,6 +902,8 @@ Section "Uninstall"
|
||||||
; try to fix it.
|
; try to fix it.
|
||||||
StrCpy $MUI_TEMP "$START_MENU"
|
StrCpy $MUI_TEMP "$START_MENU"
|
||||||
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
|
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@
|
@CPACK_NSIS_DELETE_ICONS_EXTRA@
|
||||||
|
|
||||||
;Delete empty start menu parent diretories
|
;Delete empty start menu parent diretories
|
||||||
|
@ -1279,24 +919,12 @@ Section "Uninstall"
|
||||||
StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop
|
StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop
|
||||||
secondStartMenuDeleteLoopDone:
|
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
|
SectionEnd
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
;--------------------------------
|
InstallDirRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ""
|
||||||
; 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...
|
|
||||||
|
|
||||||
Function .onInit
|
Function .onInit
|
||||||
|
|
||||||
|
@ -1342,48 +970,6 @@ inst:
|
||||||
; Reads components status for registry
|
; Reads components status for registry
|
||||||
!insertmacro SectionList "InitSection"
|
!insertmacro SectionList "InitSection"
|
||||||
|
|
||||||
; check to see if /D has been used to change
|
; use all users for context of data/startup folders
|
||||||
; 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
|
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:
|
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
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);
|
|
@ -103,6 +103,8 @@ Windows.Window {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var tab = tabView.getTab(index);
|
||||||
|
tab.enabledChanged.disconnect(updateVisiblity);
|
||||||
tabView.removeTab(index);
|
tabView.removeTab(index);
|
||||||
console.log("Updating visibility based on child tab removed");
|
console.log("Updating visibility based on child tab removed");
|
||||||
updateVisiblity();
|
updateVisiblity();
|
||||||
|
@ -137,10 +139,7 @@ Windows.Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Updating visibility based on child tab added");
|
console.log("Updating visibility based on child tab added");
|
||||||
newTab.enabledChanged.connect(function() {
|
newTab.enabledChanged.connect(updateVisiblity)
|
||||||
console.log("Updating visibility based on child tab enabled change");
|
|
||||||
updateVisiblity();
|
|
||||||
})
|
|
||||||
updateVisiblity();
|
updateVisiblity();
|
||||||
return newTab
|
return newTab
|
||||||
}
|
}
|
||||||
|
|
148
interface/resources/qml/controls/ComboBox.qml
Normal file
148
interface/resources/qml/controls/ComboBox.qml
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
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
|
||||||
|
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: textView.height * count
|
||||||
|
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;
|
anchors.fill: parent;
|
||||||
objectName: "desktop"
|
objectName: "desktop"
|
||||||
|
|
||||||
|
onHeightChanged: d.repositionAll();
|
||||||
|
onWidthChanged: d.repositionAll();
|
||||||
|
|
||||||
// Allows QML/JS to find the desktop through the parent chain
|
// Allows QML/JS to find the desktop through the parent chain
|
||||||
property bool desktopRoot: true
|
property bool desktopRoot: true
|
||||||
|
|
||||||
|
@ -50,10 +53,6 @@ FocusScope {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDesktop(item) {
|
|
||||||
return item.desktopRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTopLevelWindow(item) {
|
function isTopLevelWindow(item) {
|
||||||
return item.topLevelWindow;
|
return item.topLevelWindow;
|
||||||
}
|
}
|
||||||
|
@ -147,6 +146,53 @@ FocusScope {
|
||||||
var windows = getTopLevelWindows(predicate);
|
var windows = getTopLevelWindows(predicate);
|
||||||
fixupZOrder(windows, zBasis, targetWindow);
|
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) {
|
function raise(item) {
|
||||||
|
@ -167,7 +213,7 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setFocus) {
|
if (setFocus) {
|
||||||
focus = true;
|
targetWindow.focus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
reposition(targetWindow);
|
reposition(targetWindow);
|
||||||
|
@ -187,27 +233,20 @@ FocusScope {
|
||||||
var windowRect = targetWindow.framedRect();
|
var windowRect = targetWindow.framedRect();
|
||||||
var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y);
|
var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y);
|
||||||
var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height);
|
var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height);
|
||||||
var newPosition;
|
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
|
||||||
if (targetWindow.x === -1 && targetWindow.y === -1) {
|
if (newPosition.x === -1 && newPosition.y === -1) {
|
||||||
// Set initial window position
|
// Set initial window position
|
||||||
newPosition = Utils.randomPosition(minPosition, maxPosition);
|
// newPosition = Utils.randomPosition(minPosition, maxPosition);
|
||||||
} else {
|
console.log("Target has no defined position, putting in center of the screen")
|
||||||
newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition);
|
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.x = newPosition.x;
|
||||||
targetWindow.y = newPosition.y;
|
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 { } }
|
Component { id: messageDialogBuilder; MessageDialog { } }
|
||||||
function messageBox(properties) {
|
function messageBox(properties) {
|
||||||
return messageDialogBuilder.createObject(desktop, properties);
|
return messageDialogBuilder.createObject(desktop, properties);
|
||||||
|
@ -252,27 +291,8 @@ FocusScope {
|
||||||
desktop.focus = true;
|
desktop.focus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugging help for figuring out focus issues
|
|
||||||
property var offscreenWindow;
|
|
||||||
onOffscreenWindowChanged: {
|
|
||||||
offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
|
|
||||||
focusHack.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
FocusHack { id: focusHack; }
|
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 {
|
Rectangle {
|
||||||
id: focusDebugger;
|
id: focusDebugger;
|
||||||
z: 9999; visible: false; color: "red"
|
z: 9999; visible: false; color: "red"
|
||||||
|
@ -285,6 +305,7 @@ FocusScope {
|
||||||
enabled: DebugQML
|
enabled: DebugQML
|
||||||
onTriggered: focusDebugger.visible = !focusDebugger.visible
|
onTriggered: focusDebugger.visible = !focusDebugger.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -184,9 +184,28 @@ Window {
|
||||||
anchors.bottom: filterEdit.top
|
anchors.bottom: filterEdit.top
|
||||||
anchors.bottomMargin: 8
|
anchors.bottomMargin: 8
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
// 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: asyncAction
|
||||||
|
interval: 50
|
||||||
|
repeat: false
|
||||||
|
running: false
|
||||||
|
onTriggered: ApplicationInterface.loadScriptURLDialog();
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "from URL";
|
text: "from URL";
|
||||||
onClicked: ApplicationInterface.loadScriptURLDialog();
|
onClicked: {
|
||||||
|
focus = false;
|
||||||
|
asyncAction.running = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: "from Disk"
|
text: "from Disk"
|
||||||
|
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
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()" }
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,17 @@ function randomPosition(min, max) {
|
||||||
Math.random() * (max.x - min.x),
|
Math.random() * (max.x - min.x),
|
||||||
Math.random() * (max.y - min.y));
|
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];
|
||||||
|
}
|
||||||
|
|
|
@ -1186,6 +1186,7 @@ void Application::initializeUi() {
|
||||||
UpdateDialog::registerType();
|
UpdateDialog::registerType();
|
||||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||||
|
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
offscreenUi->create(_offscreenContext->getContext());
|
offscreenUi->create(_offscreenContext->getContext());
|
||||||
offscreenUi->setProxyWindow(_window->windowHandle());
|
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||||
|
@ -1380,10 +1381,12 @@ void Application::paintGL() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 boomOffset;
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("CameraUpdates");
|
PerformanceTimer perfTimer("CameraUpdates");
|
||||||
|
|
||||||
auto myAvatar = getMyAvatar();
|
auto myAvatar = getMyAvatar();
|
||||||
|
boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT;
|
||||||
|
|
||||||
myAvatar->startCapture();
|
myAvatar->startCapture();
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
|
@ -1411,18 +1414,16 @@ void Application::paintGL() {
|
||||||
if (isHMDMode()) {
|
if (isHMDMode()) {
|
||||||
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||||
_myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat)));
|
_myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat)));
|
||||||
auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f));
|
_myCamera.setPosition(extractTranslation(hmdWorldMat) +
|
||||||
_myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset);
|
myAvatar->getOrientation() * boomOffset);
|
||||||
} else {
|
} else {
|
||||||
_myCamera.setRotation(myAvatar->getHead()->getOrientation());
|
_myCamera.setRotation(myAvatar->getHead()->getOrientation());
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||||
+ _myCamera.getRotation()
|
+ _myCamera.getRotation() * boomOffset);
|
||||||
* (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
|
|
||||||
} else {
|
} else {
|
||||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||||
+ myAvatar->getOrientation()
|
+ myAvatar->getOrientation() * boomOffset);
|
||||||
* (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
|
@ -1488,6 +1489,7 @@ void Application::paintGL() {
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
||||||
PerformanceTimer perfTimer("mainRender");
|
PerformanceTimer perfTimer("mainRender");
|
||||||
|
renderArgs._boomOffset = boomOffset;
|
||||||
// Viewport is assigned to the size of the framebuffer
|
// Viewport is assigned to the size of the framebuffer
|
||||||
renderArgs._viewport = ivec4(0, 0, size.width(), size.height());
|
renderArgs._viewport = ivec4(0, 0, size.width(), size.height());
|
||||||
if (displayPlugin->isStereo()) {
|
if (displayPlugin->isStereo()) {
|
||||||
|
@ -1841,6 +1843,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
|
|
||||||
case Qt::Key_X:
|
case Qt::Key_X:
|
||||||
if (isShifted && isMeta) {
|
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.
|
// placeholder for dialogs being converted to QML.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -70,8 +70,8 @@ Menu::Menu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// File > Update -- FIXME: needs implementation
|
// File > Update -- FIXME: needs implementation
|
||||||
auto updateAction = addActionToQMenuAndActionHash(fileMenu, "Update");
|
auto action = addActionToQMenuAndActionHash(fileMenu, "Update");
|
||||||
updateAction->setDisabled(true);
|
action->setDisabled(true);
|
||||||
|
|
||||||
// File > Help
|
// File > Help
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
|
||||||
|
@ -166,8 +166,11 @@ Menu::Menu() {
|
||||||
QObject* avatar = avatarManager->getMyAvatar();
|
QObject* avatar = avatarManager->getMyAvatar();
|
||||||
|
|
||||||
// Avatar > Attachments...
|
// Avatar > Attachments...
|
||||||
addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments, 0,
|
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
|
||||||
dialogsManager.data(), SLOT(editAttachments()));
|
connect(action, &QAction::triggered, [] {
|
||||||
|
DependencyManager::get<OffscreenUi>()->show(QString("hifi/dialogs/AttachmentsDialog.qml"), "AttachmentsDialog");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Avatar > Size
|
// Avatar > Size
|
||||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||||
|
@ -285,7 +288,7 @@ Menu::Menu() {
|
||||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||||
|
|
||||||
// Settings > General...
|
// 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, [] {
|
connect(action, &QAction::triggered, [] {
|
||||||
DependencyManager::get<OffscreenUi>()->toggle(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), "GeneralPreferencesDialog");
|
DependencyManager::get<OffscreenUi>()->toggle(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), "GeneralPreferencesDialog");
|
||||||
});
|
});
|
||||||
|
@ -479,6 +482,10 @@ Menu::Menu() {
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
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,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
|
||||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||||
|
|
|
@ -208,6 +208,7 @@ namespace MenuOption {
|
||||||
const QString EchoServerAudio = "Echo Server Audio";
|
const QString EchoServerAudio = "Echo Server Audio";
|
||||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||||
const QString EnableCharacterController = "Enable avatar collisions";
|
const QString EnableCharacterController = "Enable avatar collisions";
|
||||||
|
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
|
||||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||||
|
@ -302,6 +303,7 @@ namespace MenuOption {
|
||||||
const QString UploadAsset = "Upload File to Asset Server";
|
const QString UploadAsset = "Upload File to Asset Server";
|
||||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||||
const QString UseCamera = "Use Camera";
|
const QString UseCamera = "Use Camera";
|
||||||
|
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
|
||||||
const QString VelocityFilter = "Velocity Filter";
|
const QString VelocityFilter = "Velocity Filter";
|
||||||
const QString VisibleToEveryone = "Everyone";
|
const QString VisibleToEveryone = "Everyone";
|
||||||
const QString VisibleToFriends = "Friends";
|
const QString VisibleToFriends = "Friends";
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <TextRenderer3D.h>
|
#include <TextRenderer3D.h>
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
#include <AnimDebugDraw.h>
|
#include <AnimDebugDraw.h>
|
||||||
|
#include <AnimClip.h>
|
||||||
#include <recording/Deck.h>
|
#include <recording/Deck.h>
|
||||||
#include <recording/Recorder.h>
|
#include <recording/Recorder.h>
|
||||||
#include <recording/Clip.h>
|
#include <recording/Clip.h>
|
||||||
|
@ -49,6 +50,8 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
#include "DebugDraw.h"
|
#include "DebugDraw.h"
|
||||||
|
#include "EntityEditPacketSender.h"
|
||||||
|
#include "MovingEntitiesOperator.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -362,6 +365,37 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
|
|
||||||
// consider updating our billboard
|
// consider updating our billboard
|
||||||
maybeUpdateBillboard();
|
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 {
|
glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
|
||||||
|
@ -647,6 +681,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||||
_skeletonModel.setVisibleInScene(isEnabled, scene);
|
_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() {
|
void MyAvatar::loadData() {
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.beginGroup("Avatar");
|
settings.beginGroup("Avatar");
|
||||||
|
|
|
@ -260,6 +260,8 @@ public slots:
|
||||||
void setEnableDebugDrawPosition(bool isEnabled);
|
void setEnableDebugDrawPosition(bool isEnabled);
|
||||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||||
void setEnableMeshVisible(bool isEnabled);
|
void setEnableMeshVisible(bool isEnabled);
|
||||||
|
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||||
|
void setEnableInverseKinematics(bool isEnabled);
|
||||||
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
||||||
|
|
||||||
glm::vec3 getPositionForAudio();
|
glm::vec3 getPositionForAudio();
|
||||||
|
|
|
@ -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 <PathUtils.h>
|
||||||
|
|
||||||
#include "AddressBarDialog.h"
|
#include "AddressBarDialog.h"
|
||||||
#include "AttachmentsDialog.h"
|
|
||||||
#include "BandwidthDialog.h"
|
#include "BandwidthDialog.h"
|
||||||
#include "CachesSizeDialog.h"
|
#include "CachesSizeDialog.h"
|
||||||
#include "DiskCacheEditor.h"
|
#include "DiskCacheEditor.h"
|
||||||
|
@ -91,15 +90,6 @@ void DialogsManager::cachesSizeDialog() {
|
||||||
_cachesSizeDialog->raise();
|
_cachesSizeDialog->raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsManager::editAttachments() {
|
|
||||||
if (!_attachmentsDialog) {
|
|
||||||
maybeCreateDialog(_attachmentsDialog);
|
|
||||||
_attachmentsDialog->show();
|
|
||||||
} else {
|
|
||||||
_attachmentsDialog->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogsManager::audioStatsDetails() {
|
void DialogsManager::audioStatsDetails() {
|
||||||
if (! _audioStatsDialog) {
|
if (! _audioStatsDialog) {
|
||||||
_audioStatsDialog = new AudioStatsDialog(qApp->getWindow());
|
_audioStatsDialog = new AudioStatsDialog(qApp->getWindow());
|
||||||
|
|
|
@ -48,7 +48,6 @@ public slots:
|
||||||
void showLoginDialog();
|
void showLoginDialog();
|
||||||
void octreeStatsDetails();
|
void octreeStatsDetails();
|
||||||
void cachesSizeDialog();
|
void cachesSizeDialog();
|
||||||
void editAttachments();
|
|
||||||
void audioStatsDetails();
|
void audioStatsDetails();
|
||||||
void bandwidthDetails();
|
void bandwidthDetails();
|
||||||
void lodTools();
|
void lodTools();
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
|
bool AnimClip::usePreAndPostPoseFromAnim = false;
|
||||||
|
|
||||||
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
|
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
|
||||||
AnimNode(AnimNode::Type::Clip, id),
|
AnimNode(AnimNode::Type::Clip, id),
|
||||||
_startFrame(startFrame),
|
_startFrame(startFrame),
|
||||||
|
@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() {
|
||||||
|
|
||||||
for (int frame = 0; frame < frameCount; frame++) {
|
for (int frame = 0; frame < frameCount; frame++) {
|
||||||
|
|
||||||
|
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||||
|
|
||||||
// init all joints in animation to default pose
|
// 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.
|
// this will give us a resonable result for bones in the model skeleton but not in the animation.
|
||||||
_anim[frame].reserve(skeletonJointCount);
|
_anim[frame].reserve(skeletonJointCount);
|
||||||
|
@ -119,36 +123,41 @@ void AnimClip::copyFromNetworkAnim() {
|
||||||
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
||||||
int skeletonJoint = jointMap[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.
|
// skip joints that are in the animation but not in the skeleton.
|
||||||
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
||||||
|
|
||||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
|
AnimPose preRot, postRot;
|
||||||
#ifdef USE_PRE_ROT_FROM_ANIM
|
if (usePreAndPostPoseFromAnim) {
|
||||||
// TODO: This is the correct way to apply the pre rotations from maya, however
|
preRot = animSkeleton.getPreRotationPose(animJoint);
|
||||||
// the current animation set has incorrect preRotations for the left wrist and thumb
|
postRot = animSkeleton.getPostRotationPose(animJoint);
|
||||||
// so it looks wrong if we enable this code.
|
} else {
|
||||||
glm::quat preRotation = animSkeleton.getPreRotation(animJoint);
|
// In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations.
|
||||||
#else
|
preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3());
|
||||||
// TODO: This is the legacy approach, this does not work when animations and models do not
|
postRot = AnimPose::identity;
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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.
|
// 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];
|
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans));
|
||||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
|
||||||
|
|
||||||
// rotation in fbxAnimationFrame is a delta from its preRotation.
|
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ class AnimClip : public AnimNode {
|
||||||
public:
|
public:
|
||||||
friend class AnimTests;
|
friend class AnimTests;
|
||||||
|
|
||||||
|
static bool usePreAndPostPoseFromAnim;
|
||||||
|
|
||||||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
||||||
virtual ~AnimClip() override;
|
virtual ~AnimClip() override;
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache tip absolute transform
|
|
||||||
int tipIndex = target.getIndex();
|
int tipIndex = target.getIndex();
|
||||||
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
||||||
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
|
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
|
||||||
|
@ -165,12 +164,30 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
|
// cache tip's absolute orientation
|
||||||
glm::quat tipRotation = absolutePoses[tipIndex].rot;
|
glm::quat tipOrientation = absolutePoses[tipIndex].rot;
|
||||||
|
|
||||||
// cache tip's parent's absolute rotation so we can recompute the tip's parent-relative
|
// also cache tip's parent's absolute orientation so we can recompute
|
||||||
// as we proceed walking down the joint chain
|
// the tip's parent-relative as we proceed up the chain
|
||||||
glm::quat tipParentRotation = absolutePoses[pivotIndex].rot;
|
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
|
// descend toward root, pivoting each joint to get tip closer to target
|
||||||
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
|
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
|
||||||
|
@ -201,9 +218,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
||||||
deltaRotation = glm::angleAxis(angle, axis);
|
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
|
// 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
|
// new orientation and its target. This is the final parent-relative orientation that the tip joint have
|
||||||
// make to achieve its target rotation.
|
// make to achieve its target orientation.
|
||||||
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation();
|
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentOrientation) * target.getRotation();
|
||||||
|
|
||||||
// enforce tip's constraint
|
// enforce tip's constraint
|
||||||
RotationConstraint* constraint = getConstraint(tipIndex);
|
RotationConstraint* constraint = getConstraint(tipIndex);
|
||||||
|
@ -212,7 +229,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
||||||
if (constrained) {
|
if (constrained) {
|
||||||
// The tip's final parent-relative rotation would violate its constraint
|
// The tip's final parent-relative rotation would violate its constraint
|
||||||
// so we try to pre-twist this pivot to compensate.
|
// 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 missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation);
|
||||||
glm::quat swingPart;
|
glm::quat swingPart;
|
||||||
glm::quat twistPart;
|
glm::quat twistPart;
|
||||||
|
@ -227,9 +244,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
||||||
} else if (targetType == IKTarget::Type::HmdHead) {
|
} else if (targetType == IKTarget::Type::HmdHead) {
|
||||||
// An HmdHead target slaves the orientation of the end-effector by distributing rotation
|
// 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.
|
// 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);
|
float dotSign = copysignf(1.0f, deltaRotation.w);
|
||||||
const float ANGLE_DISTRIBUTION_FACTOR = 0.15f;
|
const float ANGLE_DISTRIBUTION_FACTOR = 0.35f;
|
||||||
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * deltaRotation, ANGLE_DISTRIBUTION_FACTOR));
|
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) {
|
if (constraint) {
|
||||||
bool constrained = constraint->apply(newRot);
|
bool constrained = constraint->apply(newRot);
|
||||||
if (constrained) {
|
if (constrained) {
|
||||||
// the constraint will modify the movement of the tip so we have to compute the modified
|
// the constraint will modify the local rotation of the tip so we must
|
||||||
// model-frame deltaRotation
|
// compute the corresponding model-frame deltaRotation
|
||||||
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
|
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
|
||||||
deltaRotation = absolutePoses[pivotsParentIndex].rot *
|
deltaRotation = absolutePoses[pivotsParentIndex].rot *
|
||||||
newRot *
|
newRot * glm::inverse(absolutePoses[pivotIndex].rot);
|
||||||
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
|
// keep track of tip's new transform as we descend towards root
|
||||||
tipPosition = jointPosition + deltaRotation * leverArm;
|
tipPosition = jointPosition + deltaRotation * leverArm;
|
||||||
tipRotation = glm::normalize(deltaRotation * tipRotation);
|
tipOrientation = glm::normalize(deltaRotation * tipOrientation);
|
||||||
tipParentRotation = glm::normalize(deltaRotation * tipParentRotation);
|
tipParentOrientation = glm::normalize(deltaRotation * tipParentOrientation);
|
||||||
|
|
||||||
pivotIndex = pivotsParentIndex;
|
pivotIndex = pivotsParentIndex;
|
||||||
pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
|
pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
|
||||||
|
@ -629,11 +645,11 @@ void AnimInverseKinematics::initConstraints() {
|
||||||
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
|
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
|
||||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||||
const float MAX_NECK_TWIST = PI / 4.0f;
|
const float MAX_NECK_TWIST = PI / 6.0f;
|
||||||
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
|
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
|
||||||
|
|
||||||
std::vector<float> minDots;
|
std::vector<float> minDots;
|
||||||
const float MAX_NECK_SWING = PI / 3.0f;
|
const float MAX_NECK_SWING = PI / 4.0f;
|
||||||
minDots.push_back(cosf(MAX_NECK_SWING));
|
minDots.push_back(cosf(MAX_NECK_SWING));
|
||||||
stConstraint->setSwingLimits(minDots);
|
stConstraint->setSwingLimits(minDots);
|
||||||
|
|
||||||
|
@ -641,11 +657,11 @@ void AnimInverseKinematics::initConstraints() {
|
||||||
} else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) {
|
} else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) {
|
||||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||||
const float MAX_HEAD_TWIST = PI / 4.0f;
|
const float MAX_HEAD_TWIST = PI / 8.0f;
|
||||||
stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST);
|
stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST);
|
||||||
|
|
||||||
std::vector<float> minDots;
|
std::vector<float> minDots;
|
||||||
const float MAX_HEAD_SWING = PI / 4.0f;
|
const float MAX_HEAD_SWING = PI / 6.0f;
|
||||||
minDots.push_back(cosf(MAX_HEAD_SWING));
|
minDots.push_back(cosf(MAX_HEAD_SWING));
|
||||||
stConstraint->setSwingLimits(minDots);
|
stConstraint->setSwingLimits(minDots);
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,14 @@ const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const {
|
||||||
return _absoluteDefaultPoses[jointIndex];
|
return _absoluteDefaultPoses[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const glm::quat AnimSkeleton::getPreRotation(int jointIndex) const {
|
// get pre multiplied transform which should include FBX pre potations
|
||||||
return _joints[jointIndex].preRotation;
|
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 {
|
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
||||||
|
@ -99,13 +105,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
|
||||||
// build a chache of default poses
|
// build a chache of default poses
|
||||||
_absoluteDefaultPoses.reserve(joints.size());
|
_absoluteDefaultPoses.reserve(joints.size());
|
||||||
_relativeDefaultPoses.reserve(joints.size());
|
_relativeDefaultPoses.reserve(joints.size());
|
||||||
|
_relativePreRotationPoses.reserve(joints.size());
|
||||||
|
_relativePostRotationPoses.reserve(joints.size());
|
||||||
|
|
||||||
// iterate over FBXJoints and extract the bind pose information.
|
// iterate over FBXJoints and extract the bind pose information.
|
||||||
for (int i = 0; i < (int)joints.size(); i++) {
|
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
|
// 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) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform;
|
||||||
glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform;
|
|
||||||
AnimPose relDefaultPose(relDefaultMat);
|
AnimPose relDefaultPose(relDefaultMat);
|
||||||
_relativeDefaultPoses.push_back(relDefaultPose);
|
_relativeDefaultPoses.push_back(relDefaultPose);
|
||||||
int parentIndex = getParentIndex(i);
|
int parentIndex = getParentIndex(i);
|
||||||
|
|
|
@ -42,8 +42,11 @@ public:
|
||||||
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
|
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
|
||||||
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
|
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
|
||||||
|
|
||||||
// get pre-rotation aka Maya's joint orientation.
|
// get pre transform which should include FBX pre potations
|
||||||
const glm::quat getPreRotation(int jointIndex) const;
|
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;
|
int getParentIndex(int jointIndex) const;
|
||||||
|
|
||||||
|
@ -64,6 +67,8 @@ protected:
|
||||||
AnimPoseVec _relativeBindPoses;
|
AnimPoseVec _relativeBindPoses;
|
||||||
AnimPoseVec _relativeDefaultPoses;
|
AnimPoseVec _relativeDefaultPoses;
|
||||||
AnimPoseVec _absoluteDefaultPoses;
|
AnimPoseVec _absoluteDefaultPoses;
|
||||||
|
AnimPoseVec _relativePreRotationPoses;
|
||||||
|
AnimPoseVec _relativePostRotationPoses;
|
||||||
|
|
||||||
// no copies
|
// no copies
|
||||||
AnimSkeleton(const AnimSkeleton&) = delete;
|
AnimSkeleton(const AnimSkeleton&) = delete;
|
||||||
|
|
|
@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Rig::setEnableInverseKinematics(bool enable) {
|
||||||
|
_enableInverseKinematics = enable;
|
||||||
|
}
|
||||||
|
|
||||||
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
|
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
|
||||||
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
||||||
return _absoluteDefaultPoses[index];
|
return _absoluteDefaultPoses[index];
|
||||||
|
@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
}
|
}
|
||||||
|
|
||||||
t += deltaTime;
|
t += deltaTime;
|
||||||
|
|
||||||
|
if (_enableInverseKinematics) {
|
||||||
|
_animVars.set("ikOverlayAlpha", 1.0f);
|
||||||
|
} else {
|
||||||
|
_animVars.set("ikOverlayAlpha", 0.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastFront = front;
|
_lastFront = front;
|
||||||
|
|
|
@ -208,6 +208,8 @@ public:
|
||||||
|
|
||||||
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
|
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
|
||||||
|
|
||||||
|
void setEnableInverseKinematics(bool enable);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||||
void updateAnimationStateHandlers();
|
void updateAnimationStateHandlers();
|
||||||
|
@ -290,6 +292,8 @@ public:
|
||||||
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
||||||
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
||||||
|
|
||||||
|
bool _enableInverseKinematics { true };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<int, StateHandler> _stateHandlers;
|
QMap<int, StateHandler> _stateHandlers;
|
||||||
int _nextStateHandlerId { 0 };
|
int _nextStateHandlerId { 0 };
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace AudioConstants {
|
||||||
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
|
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
|
const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
|
||||||
/ (float)AudioConstants::SAMPLE_RATE) * 1000.0f;
|
/ (float)AudioConstants::SAMPLE_RATE) * 1000.0f;
|
||||||
const unsigned int NETWORK_FRAME_USECS = (unsigned int)floorf(NETWORK_FRAME_MSECS * 1000.0f);
|
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 MIN_SAMPLE_VALUE = std::numeric_limits<AudioSample>::min();
|
||||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<AudioSample>::max();
|
const int MAX_SAMPLE_VALUE = std::numeric_limits<AudioSample>::max();
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,11 +158,10 @@ bool AudioInjector::injectLocally() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const uchar MAX_INJECTOR_VOLUME = 0xFF;
|
const uchar MAX_INJECTOR_VOLUME = 0xFF;
|
||||||
static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0;
|
static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1;
|
||||||
static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1;
|
static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
|
||||||
|
|
||||||
uint64_t AudioInjector::injectNextFrame() {
|
|
||||||
|
|
||||||
|
int64_t AudioInjector::injectNextFrame() {
|
||||||
if (_state == AudioInjector::State::Finished) {
|
if (_state == AudioInjector::State::Finished) {
|
||||||
qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning.";
|
qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning.";
|
||||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||||
|
@ -238,16 +237,19 @@ uint64_t AudioInjector::injectNextFrame() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
|
int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||||
_audioData.size() - _currentSendOffset);
|
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
|
// Measure the loudness of this frame
|
||||||
_loudness = 0.0f;
|
_loudness = 0.0f;
|
||||||
for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) {
|
for (int i = 0; i < totalBytesLeftToCopy; i += sizeof(int16_t)) {
|
||||||
_loudness += abs(*reinterpret_cast<int16_t*>(_audioData.data() + _currentSendOffset + i)) /
|
_loudness += abs(*reinterpret_cast<int16_t*>(_audioData.data() + ((_currentSendOffset + i) % _audioData.size()))) /
|
||||||
(AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
|
(AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
|
||||||
}
|
}
|
||||||
_loudness /= (float)(bytesToCopy / sizeof(int16_t));
|
_loudness /= (float)(totalBytesLeftToCopy/ sizeof(int16_t));
|
||||||
|
|
||||||
_currentPacket->seek(0);
|
_currentPacket->seek(0);
|
||||||
|
|
||||||
|
@ -264,8 +266,16 @@ uint64_t AudioInjector::injectNextFrame() {
|
||||||
|
|
||||||
_currentPacket->seek(audioDataOffset);
|
_currentPacket->seek(audioDataOffset);
|
||||||
|
|
||||||
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
while (totalBytesLeftToCopy > 0) {
|
||||||
|
int bytesToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset);
|
||||||
|
|
||||||
_currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy);
|
_currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy);
|
||||||
|
_currentSendOffset += bytesToCopy;
|
||||||
|
totalBytesLeftToCopy -= bytesToCopy;
|
||||||
|
if (_currentSendOffset >= _audioData.size()) {
|
||||||
|
_currentSendOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the correct size used for this packet
|
// set the correct size used for this packet
|
||||||
_currentPacket->setPayloadSize(_currentPacket->pos());
|
_currentPacket->setPayloadSize(_currentPacket->pos());
|
||||||
|
@ -280,28 +290,30 @@ uint64_t AudioInjector::injectNextFrame() {
|
||||||
_outgoingSequenceNumber++;
|
_outgoingSequenceNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentSendOffset += bytesToCopy;
|
if (_currentSendOffset >= _audioData.size() && !_options.loop) {
|
||||||
|
|
||||||
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();
|
finish();
|
||||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentSendOffset == bytesToCopy) {
|
if (!_hasSentFirstFrame) {
|
||||||
|
_hasSentFirstFrame = true;
|
||||||
// ask AudioInjectorManager to call us right away again to
|
// 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
|
// immediately send the first two frames so the mixer can start using the audio right away
|
||||||
return NEXT_FRAME_DELTA_IMMEDIATELY;
|
return NEXT_FRAME_DELTA_IMMEDIATELY;
|
||||||
} else {
|
|
||||||
return (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() << "AudioInjector::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() {
|
void AudioInjector::stop() {
|
||||||
|
|
|
@ -84,16 +84,17 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupInjection();
|
void setupInjection();
|
||||||
uint64_t injectNextFrame();
|
int64_t injectNextFrame();
|
||||||
bool injectLocally();
|
bool injectLocally();
|
||||||
|
|
||||||
QByteArray _audioData;
|
QByteArray _audioData;
|
||||||
AudioInjectorOptions _options;
|
AudioInjectorOptions _options;
|
||||||
State _state { State::NotFinished };
|
State _state { State::NotFinished };
|
||||||
bool _hasSetup = false;
|
bool _hasSentFirstFrame { false };
|
||||||
bool _shouldStop = false;
|
bool _hasSetup { false };
|
||||||
float _loudness = 0.0f;
|
bool _shouldStop { false };
|
||||||
int _currentSendOffset = 0;
|
float _loudness { 0.0f };
|
||||||
|
int _currentSendOffset { 0 };
|
||||||
std::unique_ptr<NLPacket> _currentPacket { nullptr };
|
std::unique_ptr<NLPacket> _currentPacket { nullptr };
|
||||||
AbstractAudioInterface* _localAudioInterface { nullptr };
|
AbstractAudioInterface* _localAudioInterface { nullptr };
|
||||||
AudioInjectorLocalBuffer* _localBuffer { nullptr };
|
AudioInjectorLocalBuffer* _localBuffer { nullptr };
|
||||||
|
|
|
@ -88,7 +88,7 @@ void AudioInjectorManager::run() {
|
||||||
// this is an injector that's ready to go, have it send a frame now
|
// this is an injector that's ready to go, have it send a frame now
|
||||||
auto nextCallDelta = injector->injectNextFrame();
|
auto nextCallDelta = injector->injectNextFrame();
|
||||||
|
|
||||||
if (nextCallDelta > 0 && !injector->isFinished()) {
|
if (nextCallDelta >= 0 && !injector->isFinished()) {
|
||||||
// re-enqueue the injector with the correct timing
|
// re-enqueue the injector with the correct timing
|
||||||
_injectors.emplace(usecTimestampNow() + nextCallDelta, injector);
|
_injectors.emplace(usecTimestampNow() + nextCallDelta, injector);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
|
|
||||||
|
#include <QVariantGLM.h>
|
||||||
#include <Transform.h>
|
#include <Transform.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
|
||||||
assert(false);
|
assert(false);
|
||||||
return glm::vec3();
|
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 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; }
|
void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
||||||
|
|
||||||
// key state
|
// key state
|
||||||
|
@ -448,6 +451,9 @@ public:
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
void fromJson(const QJsonObject& json);
|
void fromJson(const QJsonObject& json);
|
||||||
|
|
||||||
|
QVariant toVariant() const;
|
||||||
|
void fromVariant(const QVariant& variant);
|
||||||
};
|
};
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
||||||
|
|
|
@ -252,6 +252,8 @@ public:
|
||||||
|
|
||||||
void setLinePointsDirty() {_linePointsChanged = true; }
|
void setLinePointsDirty() {_linePointsChanged = true; }
|
||||||
|
|
||||||
|
void setQueryAACubeDirty() { _queryAACubeChanged = true; }
|
||||||
|
|
||||||
void setCreated(QDateTime& v);
|
void setCreated(QDateTime& v);
|
||||||
|
|
||||||
bool hasTerseUpdateChanges() const;
|
bool hasTerseUpdateChanges() const;
|
||||||
|
|
|
@ -338,6 +338,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||||
_updateTimer.start();
|
_updateTimer.start();
|
||||||
|
|
||||||
_qmlComponent = new QQmlComponent(_qmlEngine);
|
_qmlComponent = new QQmlComponent(_qmlEngine);
|
||||||
|
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
||||||
|
|
|
@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p
|
||||||
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
|
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
|
||||||
QSharedMemory sharedMem(key);
|
QSharedMemory sharedMem(key);
|
||||||
if (!sharedMem.attach(QSharedMemory::ReadOnly)) {
|
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;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
sharedMem.lock();
|
sharedMem.lock();
|
||||||
|
|
|
@ -144,7 +144,12 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
||||||
return MOTION_TYPE_STATIC;
|
return MOTION_TYPE_STATIC;
|
||||||
}
|
}
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
|
|
||||||
if (_entity->getDynamic()) {
|
if (_entity->getDynamic()) {
|
||||||
|
if (!_entity->getParentID().isNull()) {
|
||||||
|
// if something would have been dynamic but is a child of something else, force it to be kinematic, instead.
|
||||||
|
return MOTION_TYPE_KINEMATIC;
|
||||||
|
}
|
||||||
return MOTION_TYPE_DYNAMIC;
|
return MOTION_TYPE_DYNAMIC;
|
||||||
}
|
}
|
||||||
return (_entity->isMoving() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC;
|
return (_entity->isMoving() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC;
|
||||||
|
|
|
@ -146,9 +146,9 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render
|
||||||
RenderArgs::RenderMode mode = args->_renderMode;
|
RenderArgs::RenderMode mode = args->_renderMode;
|
||||||
|
|
||||||
auto nearClip = viewFrustum->getNearClip();
|
auto nearClip = viewFrustum->getNearClip();
|
||||||
const int SHADOW_NEAR_DEPTH = -2;
|
float nearDepth = -args->_boomOffset.z;
|
||||||
const int SHADOW_FAR_DEPTH = 20;
|
const int SHADOW_FAR_DEPTH = 20;
|
||||||
globalLight->shadow.setKeylightFrustum(viewFrustum, nearClip + SHADOW_NEAR_DEPTH, nearClip + SHADOW_FAR_DEPTH);
|
globalLight->shadow.setKeylightFrustum(viewFrustum, nearDepth, nearClip + SHADOW_FAR_DEPTH);
|
||||||
|
|
||||||
// Set the keylight render args
|
// Set the keylight render args
|
||||||
args->_viewFrustum = globalLight->shadow.getFrustum().get();
|
args->_viewFrustum = globalLight->shadow.getFrustum().get();
|
||||||
|
|
|
@ -105,7 +105,8 @@ public:
|
||||||
std::shared_ptr<render::ShapePipeline> _pipeline = nullptr;
|
std::shared_ptr<render::ShapePipeline> _pipeline = nullptr;
|
||||||
OctreeRenderer* _renderer = nullptr;
|
OctreeRenderer* _renderer = nullptr;
|
||||||
ViewFrustum* _viewFrustum = nullptr;
|
ViewFrustum* _viewFrustum = nullptr;
|
||||||
glm::ivec4 _viewport{ 0, 0, 1, 1 };
|
glm::ivec4 _viewport{ 0.0f, 0.0f, 1.0f, 1.0f };
|
||||||
|
glm::vec3 _boomOffset{ 0.0f, 0.0f, 1.0f };
|
||||||
float _sizeScale = 1.0f;
|
float _sizeScale = 1.0f;
|
||||||
int _boundaryLevelAdjust = 0;
|
int _boundaryLevelAdjust = 0;
|
||||||
RenderMode _renderMode = DEFAULT_RENDER_MODE;
|
RenderMode _renderMode = DEFAULT_RENDER_MODE;
|
||||||
|
|
|
@ -150,8 +150,10 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
~ModalDialogListener() {
|
~ModalDialogListener() {
|
||||||
|
if (_dialog) {
|
||||||
disconnect(_dialog);
|
disconnect(_dialog);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual QVariant waitForResult() {
|
virtual QVariant waitForResult() {
|
||||||
while (!_finished) {
|
while (!_finished) {
|
||||||
|
@ -164,10 +166,11 @@ protected slots:
|
||||||
void onDestroyed() {
|
void onDestroyed() {
|
||||||
_finished = true;
|
_finished = true;
|
||||||
disconnect(_dialog);
|
disconnect(_dialog);
|
||||||
|
_dialog = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QQuickItem* const _dialog;
|
QQuickItem* _dialog;
|
||||||
bool _finished { false };
|
bool _finished { false };
|
||||||
QVariant _result;
|
QVariant _result;
|
||||||
};
|
};
|
||||||
|
@ -372,6 +375,7 @@ void OffscreenUi::createDesktop(const QUrl& url) {
|
||||||
qDebug() << "Desktop already created";
|
qDebug() << "Desktop already created";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
getRootContext()->setContextProperty("DebugQML", QVariant(true));
|
getRootContext()->setContextProperty("DebugQML", QVariant(true));
|
||||||
#else
|
#else
|
||||||
|
@ -382,9 +386,6 @@ void OffscreenUi::createDesktop(const QUrl& url) {
|
||||||
Q_ASSERT(_desktop);
|
Q_ASSERT(_desktop);
|
||||||
getRootContext()->setContextProperty("desktop", _desktop);
|
getRootContext()->setContextProperty("desktop", _desktop);
|
||||||
|
|
||||||
// Enable focus debugging
|
|
||||||
_desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
|
||||||
|
|
||||||
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
||||||
|
|
||||||
new VrMenu(this);
|
new VrMenu(this);
|
||||||
|
|
|
@ -142,7 +142,6 @@ void VrMenu::addAction(QMenu* menu, QAction* action) {
|
||||||
Q_ASSERT(menuQml);
|
Q_ASSERT(menuQml);
|
||||||
QQuickMenuItem* returnedValue { nullptr };
|
QQuickMenuItem* returnedValue { nullptr };
|
||||||
|
|
||||||
qDebug() << menuQml;
|
|
||||||
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,
|
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,
|
||||||
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
|
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
|
||||||
Q_ARG(QString, action->text()));
|
Q_ARG(QString, action->text()));
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
|
||||||
** Contact: http://www.qt-project.org/legal
|
|
||||||
**
|
|
||||||
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
|
|
||||||
**
|
|
||||||
** $QT_BEGIN_LICENSE:BSD$
|
|
||||||
** You may use this file under the terms of the BSD license as follows:
|
|
||||||
**
|
|
||||||
** "Redistribution and use in source and binary forms, with or without
|
|
||||||
** modification, are permitted provided that the following conditions are
|
|
||||||
** met:
|
|
||||||
** * Redistributions of source code must retain the above copyright
|
|
||||||
** notice, this list of conditions and the following disclaimer.
|
|
||||||
** * Redistributions in binary form must reproduce the above copyright
|
|
||||||
** notice, this list of conditions and the following disclaimer in
|
|
||||||
** the documentation and/or other materials provided with the
|
|
||||||
** distribution.
|
|
||||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
|
||||||
** of its contributors may be used to endorse or promote products derived
|
|
||||||
** from this software without specific prior written permission.
|
|
||||||
**
|
|
||||||
**
|
|
||||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
|
||||||
**
|
|
||||||
** $QT_END_LICENSE$
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
.pragma library
|
|
||||||
|
|
||||||
var margin = 2
|
|
||||||
var tabPosition = Qt.TopEdge
|
|
||||||
var label = ""
|
|
|
@ -7,6 +7,7 @@ import "../../../interface/resources/qml"
|
||||||
import "../../../interface/resources/qml/windows"
|
import "../../../interface/resources/qml/windows"
|
||||||
import "../../../interface/resources/qml/dialogs"
|
import "../../../interface/resources/qml/dialogs"
|
||||||
import "../../../interface/resources/qml/hifi"
|
import "../../../interface/resources/qml/hifi"
|
||||||
|
import "../../../interface/resources/qml/hifi/dialogs"
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: appWindow
|
id: appWindow
|
||||||
|
@ -196,4 +197,15 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
text: "Open Browser"
|
||||||
|
shortcut: "Ctrl+Shift+X"
|
||||||
|
onTriggered: {
|
||||||
|
builder.createObject(desktop);
|
||||||
|
}
|
||||||
|
property var builder: Component {
|
||||||
|
ModelBrowserDialog{}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,108 +7,24 @@ CONFIG += c++11
|
||||||
SOURCES += src/main.cpp \
|
SOURCES += src/main.cpp \
|
||||||
../../libraries/ui/src/FileDialogHelper.cpp
|
../../libraries/ui/src/FileDialogHelper.cpp
|
||||||
|
|
||||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
|
||||||
QML_IMPORT_PATH =
|
|
||||||
|
|
||||||
DISTFILES += \
|
|
||||||
qml/UI.js \
|
|
||||||
qml/main.qml \
|
|
||||||
qml/Palettes.qml \
|
|
||||||
qml/StubMenu.qml \
|
|
||||||
qml/Stubs.qml \
|
|
||||||
qml/TestControllers.qml \
|
|
||||||
qml/TestDialog.qml \
|
|
||||||
qml/TestMenu.qml \
|
|
||||||
qml/TestRoot.qml \
|
|
||||||
qml/controlDemo/ButtonPage.qml \
|
|
||||||
qml/controlDemo/InputPage.qml \
|
|
||||||
qml/controlDemo/main.qml \
|
|
||||||
qml/controlDemo/ProgressPage.qml \
|
|
||||||
../../interface/resources/qml/controller/hydra/HydraButtons.qml \
|
|
||||||
../../interface/resources/qml/controller/hydra/HydraStick.qml \
|
|
||||||
../../interface/resources/qml/controller/xbox/DPad.qml \
|
|
||||||
../../interface/resources/qml/controller/xbox/LeftAnalogStick.qml \
|
|
||||||
../../interface/resources/qml/controller/xbox/RightAnalogStick.qml \
|
|
||||||
../../interface/resources/qml/controller/xbox/XboxButtons.qml \
|
|
||||||
../../interface/resources/qml/controller/AnalogButton.qml \
|
|
||||||
../../interface/resources/qml/controller/AnalogStick.qml \
|
|
||||||
../../interface/resources/qml/controller/Hydra.qml \
|
|
||||||
../../interface/resources/qml/controller/Standard.qml \
|
|
||||||
../../interface/resources/qml/controller/ToggleButton.qml \
|
|
||||||
../../interface/resources/qml/controller/Xbox.qml \
|
|
||||||
../../interface/resources/qml/controls/Button.qml \
|
|
||||||
../../interface/resources/qml/controls/ButtonAwesome.qml \
|
|
||||||
../../interface/resources/qml/controls/CheckBox.qml \
|
|
||||||
../../interface/resources/qml/controls/FontAwesome.qml \
|
|
||||||
../../interface/resources/qml/controls/Player.qml \
|
|
||||||
../../interface/resources/qml/controls/Slider.qml \
|
|
||||||
../../interface/resources/qml/controls/Spacer.qml \
|
|
||||||
../../interface/resources/qml/controls/SpinBox.qml \
|
|
||||||
../../interface/resources/qml/controls/Text.qml \
|
|
||||||
../../interface/resources/qml/controls/TextAndSlider.qml \
|
|
||||||
../../interface/resources/qml/controls/TextAndSpinBox.qml \
|
|
||||||
../../interface/resources/qml/controls/TextArea.qml \
|
|
||||||
../../interface/resources/qml/controls/TextEdit.qml \
|
|
||||||
../../interface/resources/qml/controls/TextHeader.qml \
|
|
||||||
../../interface/resources/qml/controls/TextInput.qml \
|
|
||||||
../../interface/resources/qml/controls/TextInputAndButton.qml \
|
|
||||||
../../interface/resources/qml/controls/WebView.qml \
|
|
||||||
../../interface/resources/qml/dialogs/FileDialog.qml \
|
|
||||||
../../interface/resources/qml/dialogs/MessageDialog.qml \
|
|
||||||
../../interface/resources/qml/dialogs/RunningScripts.qml \
|
|
||||||
../../interface/resources/qml/hifi/MenuOption.qml \
|
|
||||||
../../interface/resources/qml/styles/Border.qml \
|
|
||||||
../../interface/resources/qml/styles/HifiConstants.qml \
|
|
||||||
../../interface/resources/qml/windows/DefaultFrame.qml \
|
|
||||||
../../interface/resources/qml/windows/Fadable.qml \
|
|
||||||
../../interface/resources/qml/windows/Frame.qml \
|
|
||||||
../../interface/resources/qml/windows/ModalFrame.qml \
|
|
||||||
../../interface/resources/qml/windows/Window.qml \
|
|
||||||
../../interface/resources/qml/AddressBarDialog.qml \
|
|
||||||
../../interface/resources/qml/AvatarInputs.qml \
|
|
||||||
../../interface/resources/qml/Browser.qml \
|
|
||||||
../../interface/resources/qml/InfoView.qml \
|
|
||||||
../../interface/resources/qml/LoginDialog.qml \
|
|
||||||
../../interface/resources/qml/QmlWebWindow.qml \
|
|
||||||
../../interface/resources/qml/QmlWindow.qml \
|
|
||||||
../../interface/resources/qml/Root.qml \
|
|
||||||
../../interface/resources/qml/ScrollingGraph.qml \
|
|
||||||
../../interface/resources/qml/Stats.qml \
|
|
||||||
../../interface/resources/qml/TextOverlayElement.qml \
|
|
||||||
../../interface/resources/qml/Tooltip.qml \
|
|
||||||
../../interface/resources/qml/ToolWindow.qml \
|
|
||||||
../../interface/resources/qml/UpdateDialog.qml \
|
|
||||||
../../interface/resources/qml/VrMenu.qml \
|
|
||||||
../../interface/resources/qml/VrMenuItem.qml \
|
|
||||||
../../interface/resources/qml/VrMenuView.qml \
|
|
||||||
../../interface/resources/qml/WebEntity.qml \
|
|
||||||
../../interface/resources/qml/desktop/Desktop.qml \
|
|
||||||
../../interface/resources/qml/hifi/Desktop.qml \
|
|
||||||
../../interface/resources/qml/menus/MenuMouseHandler.qml \
|
|
||||||
../../interface/resources/qml/menus/VrMenuItem.qml \
|
|
||||||
../../interface/resources/qml/menus/VrMenuView.qml \
|
|
||||||
../../interface/resources/qml/windows/ModalWindow.qml \
|
|
||||||
../../interface/resources/qml/desktop/FocusHack.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/PreferencesDialog.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/Section.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Browsable.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Section.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Editable.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Slider.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Preference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/CheckBox.qml \
|
|
||||||
../../interface/resources/qml/dialogs/fileDialog/FileTableView.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Avatar.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/AvatarBrowser.qml \
|
|
||||||
../../interface/resources/qml/dialogs/QueryDialog.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/Button.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/AvatarPreference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/BrowsablePreference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/ButtonPreference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/CheckBoxPreference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/EditablePreference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/SliderPreference.qml \
|
|
||||||
../../interface/resources/qml/hifi/dialogs/preferences/SpinBoxPreference.qml
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
../../libraries/ui/src/FileDialogHelper.h
|
../../libraries/ui/src/FileDialogHelper.h
|
||||||
|
|
||||||
|
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||||
|
QML_IMPORT_PATH = ../../interface/resources/qml
|
||||||
|
|
||||||
|
|
||||||
|
DISTFILES += \
|
||||||
|
qml/*.qml \
|
||||||
|
../../interface/resources/qml/*.qml \
|
||||||
|
../../interface/resources/qml/controls/*.qml \
|
||||||
|
../../interface/resources/qml/dialogs/*.qml \
|
||||||
|
../../interface/resources/qml/dialogs/fileDialog/*.qml \
|
||||||
|
../../interface/resources/qml/desktop/*.qml \
|
||||||
|
../../interface/resources/qml/menus/*.qml \
|
||||||
|
../../interface/resources/qml/styles/*.qml \
|
||||||
|
../../interface/resources/qml/windows/*.qml \
|
||||||
|
../../interface/resources/qml/hifi/*.qml \
|
||||||
|
../../interface/resources/qml/hifi/dialogs/*.qml \
|
||||||
|
../../interface/resources/qml/hifi/dialogs/preferences/*.qml
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue