Merge branch 'master' of https://github.com/highfidelity/hifi into amc

This commit is contained in:
Olivier Prat 2019-04-03 18:19:46 +02:00
commit eadace70f8
56 changed files with 1261 additions and 530 deletions

View file

@ -0,0 +1,10 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3383 4.13446L23.8713 5.60152C21.9374 3.46761 19.2033 2.06723 16.0691 2.06723C13.0683 2.06723 10.4009 3.40093 8.46707 5.40147L7 3.9344C9.26728 1.53375 12.5348 0 16.0691 0C19.7368 0 23.071 1.60044 25.3383 4.13446ZM21.9376 7.53584L20.4705 9.0029C19.4703 7.66921 17.8698 6.86899 16.0693 6.86899C14.4022 6.86899 12.9351 7.66921 11.8682 8.80285L10.4011 7.33578C11.8015 5.80203 13.802 4.80176 16.0693 4.80176C18.4033 4.80176 20.5372 5.86871 21.9376 7.53584ZM17.9575 30.1572C17.9575 31.1771 17.1307 32.0039 16.1108 32.0039C15.0909 32.0039 14.2642 31.1771 14.2642 30.1572C14.2642 29.1373 15.0909 28.3105 16.1108 28.3105C17.1307 28.3105 17.9575 29.1373 17.9575 30.1572ZM18.3632 11.0801H14.1597L15.0116 25.8539H17.4867L18.3632 11.0801Z" fill="#EA4C5F"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.1999 4.72587C17.5049 4.72587 18.5628 3.66795 18.5628 2.36294C18.5628 1.05792 17.5049 0 16.1999 0C14.8948 0 13.8369 1.05792 13.8369 2.36294C13.8369 3.66795 14.8948 4.72587 16.1999 4.72587ZM18.6667 11.8004V14.7337H13.7334V11.8004C13.7334 10.467 14.8667 9.40039 16.2 9.40039C17.6 9.40039 18.6667 10.467 18.6667 11.8004ZM13.7334 20.1332V17.2666H18.6667V20.1332C18.6667 21.4665 17.5333 22.5332 16.2 22.5332C14.8667 22.5332 13.7334 21.4665 13.7334 20.1332ZM23.6665 20.6V17.0667C23.6665 16.4 23.0665 15.9334 22.4665 15.9334C21.7998 15.9334 21.3332 16.4667 21.3332 17.1333V20.6C21.3332 23.0666 19.0665 25.0666 16.3332 25.0666C13.5999 25.0666 11.3333 23.0666 11.3333 20.6V17.0667C11.3333 16.4 10.8666 15.8667 10.2666 15.8667C9.59999 15.8 9 16.2667 9 16.9333V20.6C9 23.9999 11.6666 26.7999 15.1333 27.3332V29.5998H12.2666C11.6 29.5998 11.0666 30.1332 11.0666 30.7998C11.0666 31.4665 11.6 31.9998 12.2666 31.9998H20.4665C21.1332 31.9998 21.6665 31.4665 21.6665 30.7998C21.6665 30.1332 21.1332 29.5998 20.4665 29.5998H17.5332V27.3332C20.9998 26.7332 23.6665 23.9999 23.6665 20.6Z" fill="#00B4EF" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,25 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="mic-mute-a.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EA4C5F;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview18" inkscape:current-layer="svg2" inkscape:cx="25" inkscape:cy="25" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="852" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="4.72" objecttolerance="10" pagecolor="#ff0000" showgrid="false">
</sodipodi:namedview>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path id="path8" class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6l0,0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
<path id="path10" class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6l0,0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
<path id="path12" class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2L11,38.7c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
<path id="path14" class="st0" d="M23.4,40.2v3.4h-4.3c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
s-0.8-1.8-1.8-1.8H27v-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
<path id="path16" class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
C17.7,25.9,17.7,25,17.7,24.9z"/>
</g>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6441 4.13328L24.1775 5.59992C22.2442 3.46662 19.5109 2.06664 16.3776 2.06664C13.3776 2.06664 10.711 3.39995 8.77768 5.39993L7.31104 3.93328C9.57767 1.53331 12.8443 0 16.3776 0C20.0442 0 23.3775 1.59998 25.6441 4.13328ZM20.7777 9.00072L22.2444 7.53408C20.8444 5.86743 18.7111 4.80078 16.3778 4.80078C14.1111 4.80078 12.1112 5.80077 10.7112 7.33408L12.1778 8.80073C13.2445 7.66741 14.7111 6.86742 16.3778 6.86742C18.1777 6.86742 19.7777 7.66741 20.7777 9.00072ZM18.8803 12.0758V11.7496C18.8803 10.4445 17.7763 9.40039 16.4775 9.40039C15.1787 9.40039 14.0747 10.4445 14.0747 11.7496V16.2521L18.8803 12.0758ZM14.543 21.5129L12.6113 23.2103C13.4959 24.2599 14.9141 24.9311 16.4774 24.9311C19.14 24.9311 21.348 22.9735 21.348 20.559V17.1658C21.348 16.5132 21.8026 15.9912 22.452 15.9912C23.0364 15.9912 23.6209 16.448 23.6209 17.1005V20.559C23.6209 23.887 21.0233 26.6277 17.6464 27.1498V29.3684H20.5038C21.1532 29.3684 21.6727 29.8905 21.6727 30.543C21.6727 31.1956 21.1532 31.7176 20.5038 31.7176H12.5161C11.8667 31.7176 11.3471 31.1956 11.3471 30.543C11.3471 29.8905 11.8667 29.3684 12.5161 29.3684H15.3085V27.1498C13.5328 26.915 11.9589 26.0045 10.8771 24.7343L8.9443 26.4327C8.48972 26.8242 7.77537 26.759 7.38573 26.3022L7.25585 26.1717C6.8662 25.7149 6.93114 24.9971 7.38573 24.6056L23.8806 9.98853C24.3352 9.597 25.0495 9.66226 25.4392 10.119L25.5691 10.2495C25.9587 10.7716 25.8938 11.4241 25.5041 11.8809L18.8803 17.7015V20.1017C18.8803 21.4068 17.7763 22.4509 16.4775 22.4509C15.6689 22.4509 14.9744 22.0838 14.543 21.5129ZM10.5679 15.9919C11.1523 15.9919 11.6069 16.5139 11.6069 17.1664V18.4715L9.33398 20.4944V17.0359C9.39892 16.4486 9.91845 15.9266 10.5679 15.9919Z" fill="#EA4C5F"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,25 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="mic-mute-a.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EA4C5F;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview18" inkscape:current-layer="svg2" inkscape:cx="25" inkscape:cy="25" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="852" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="4.72" objecttolerance="10" pagecolor="#ff0000" showgrid="false">
</sodipodi:namedview>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path id="path8" class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6l0,0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
<path id="path10" class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6l0,0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
<path id="path12" class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2L11,38.7c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
<path id="path14" class="st0" d="M23.4,40.2v3.4h-4.3c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
s-0.8-1.8-1.8-1.8H27v-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
<path id="path16" class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
C17.7,25.9,17.7,25,17.7,24.9z"/>
</g>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6441 4.13328L24.1775 5.59992C22.2442 3.46662 19.5109 2.06664 16.3776 2.06664C13.3776 2.06664 10.711 3.39995 8.77768 5.39993L7.31104 3.93328C9.57767 1.53331 12.8443 0 16.3776 0C20.0442 0 23.3775 1.59998 25.6441 4.13328ZM20.7777 9.00072L22.2444 7.53408C20.8444 5.86743 18.7111 4.80078 16.3778 4.80078C14.1111 4.80078 12.1112 5.80077 10.7112 7.33408L12.1778 8.80073C13.2445 7.66741 14.7111 6.86742 16.3778 6.86742C18.1777 6.86742 19.7777 7.66741 20.7777 9.00072ZM18.8803 12.0758V11.7496C18.8803 10.4445 17.7763 9.40039 16.4775 9.40039C15.1787 9.40039 14.0747 10.4445 14.0747 11.7496V16.2521L18.8803 12.0758ZM14.543 21.5129L12.6113 23.2103C13.4959 24.2599 14.9141 24.9311 16.4774 24.9311C19.14 24.9311 21.348 22.9735 21.348 20.559V17.1658C21.348 16.5132 21.8026 15.9912 22.452 15.9912C23.0364 15.9912 23.6209 16.448 23.6209 17.1005V20.559C23.6209 23.887 21.0233 26.6277 17.6464 27.1498V29.3684H20.5038C21.1532 29.3684 21.6727 29.8905 21.6727 30.543C21.6727 31.1956 21.1532 31.7176 20.5038 31.7176H12.5161C11.8667 31.7176 11.3471 31.1956 11.3471 30.543C11.3471 29.8905 11.8667 29.3684 12.5161 29.3684H15.3085V27.1498C13.5328 26.915 11.9589 26.0045 10.8771 24.7343L8.9443 26.4327C8.48972 26.8242 7.77537 26.759 7.38573 26.3022L7.25585 26.1717C6.8662 25.7149 6.93114 24.9971 7.38573 24.6056L23.8806 9.98853C24.3352 9.597 25.0495 9.66226 25.4392 10.119L25.5691 10.2495C25.9587 10.7716 25.8938 11.4241 25.5041 11.8809L18.8803 17.7015V20.1017C18.8803 21.4068 17.7763 22.4509 16.4775 22.4509C15.6689 22.4509 14.9744 22.0838 14.543 21.5129ZM10.5679 15.9919C11.1523 15.9919 11.6069 16.5139 11.6069 17.1664V18.4715L9.33398 20.4944V17.0359C9.39893 16.4486 9.91845 15.9266 10.5679 15.9919Z" fill="#EA4C5F"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 40 40"
style="enable-background:new 0 0 40 40;"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="mic-mute.svg"><metadata
id="metadata6958"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6956" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1824"
inkscape:window-height="1057"
id="namedview6954"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="-40.338983"
inkscape:cy="20"
inkscape:window-x="88"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><style
type="text/css"
id="style6942">
.st0{fill:#FFFFFF;}
</style><ellipse
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path8048"
cx="20.1"
cy="20.5"
rx="15.967586"
ry="15.967585" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#feffff;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="rect8065"
width="30.1991"
height="2.9999897"
x="13.432917"
y="-1.2235159"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,70 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="mic-unmute-a.svg"><metadata
id="metadata22"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs20" /><sodipodi:namedview
pagecolor="#ff0000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="852"
inkscape:window-height="480"
id="namedview18"
showgrid="false"
inkscape:zoom="4.72"
inkscape:cx="25"
inkscape:cy="25"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><style
type="text/css"
id="style4">
.st0{fill:#FFFFFF;}
</style><g
id="Layer_2" /><g
id="Layer_1"
style="fill:#000000;fill-opacity:1"><path
class="st0"
d="M31.4,14.1l2.2-2.2c-2.1-2.5-5.3-4.1-8.8-4.1c-3.4,0-6.4,1.5-8.5,3.8c0.7,0.7,1.5,1.5,2.2,2.2 c1.6-1.7,3.8-2.9,6.3-2.9C27.5,10.9,29.9,12.1,31.4,14.1z"
id="path8"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M36.5,9l2.2-2.2C35.3,3,30.3,0.6,24.8,0.6c-5.3,0-10.2,2.3-13.6,5.9c0.7,0.7,1.5,1.5,2.2,2.2 c2.9-3,6.9-5,11.4-5C29.5,3.7,33.6,5.8,36.5,9z"
id="path10"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M28.5,22.7v-4.4c0-2-1.6-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v4.4H28.5z"
id="path12"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M21.1,26.5v4.3c0,2,1.7,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-4.3H21.1z"
id="path14"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M36,31.5c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2 c0,3.7-3.4,6.7-7.5,6.7c-4.1,0-7.5-3-7.5-6.7c0-0.4,0-4.9,0-5.3c0-1-0.7-1.8-1.6-1.8C14.9,24.3,14,25,14,26c0,0.3,0,5.4,0,5.5 c0,5.1,4,9.3,9.2,10.1l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8c0-1-0.8-1.8-1.8-1.8h-4.4 l0-3.4C32,40.7,36,36.6,36,31.5z"
id="path16"
style="fill:#000000;fill-opacity:1" /></g></svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8664 5.59992L25.3331 4.13328C23.0664 1.59998 19.7332 0 16.0665 0C12.5333 0 9.26664 1.53331 7 3.93328L8.46665 5.39993C10.4 3.39995 13.0666 2.06664 16.0665 2.06664C19.1998 2.06664 21.9331 3.46662 23.8664 5.59992ZM20.4664 8.99975L21.9331 7.5331C20.5331 5.86646 18.3998 4.7998 16.0665 4.7998C13.7999 4.7998 11.7999 5.79979 10.3999 7.3331L11.8665 8.79975C12.9332 7.66643 14.3998 6.86644 16.0665 6.86644C17.8665 6.86644 19.4664 7.66643 20.4664 8.99975ZM18.5334 11.8004V14.7337H13.6001V11.8004C13.6001 10.467 14.7334 9.40039 16.0667 9.40039C17.4667 9.40039 18.5334 10.467 18.5334 11.8004ZM13.6001 17.2666V20.1332C13.6001 21.4665 14.7334 22.5332 16.0667 22.5332C17.4 22.5332 18.5334 21.4665 18.5334 20.1332V17.2666H13.6001ZM23.5332 17.0667V20.6C23.5332 23.9999 20.8665 26.7332 17.3999 27.3332V29.5998H20.3332C20.9999 29.5998 21.5332 30.1332 21.5332 30.7998C21.5332 31.4665 20.9999 31.9998 20.3332 31.9998H12.1333C11.4667 31.9998 10.9333 31.4665 10.9333 30.7998C10.9333 30.1332 11.4667 29.5998 12.1333 29.5998H14.9999V27.3332C11.5333 26.7999 8.8667 23.9999 8.8667 20.6V16.9333C8.8667 16.2667 9.46669 15.8 10.1333 15.8667C10.7333 15.8667 11.2 16.4 11.2 17.0667V20.6C11.2 23.0666 13.4666 25.0666 16.1999 25.0666C18.9332 25.0666 21.1999 23.0666 21.1999 20.6V17.1333C21.1999 16.4667 21.6665 15.9334 22.3332 15.9334C22.9332 15.9334 23.5332 16.4 23.5332 17.0667Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,22 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M31.4,14.1l2.2-2.2c-2.1-2.5-5.3-4.1-8.8-4.1c-3.4,0-6.4,1.5-8.5,3.8c0.7,0.7,1.5,1.5,2.2,2.2
c1.6-1.7,3.8-2.9,6.3-2.9C27.5,10.9,29.9,12.1,31.4,14.1z"/>
<path class="st0" d="M36.5,9l2.2-2.2C35.3,3,30.3,0.6,24.8,0.6c-5.3,0-10.2,2.3-13.6,5.9c0.7,0.7,1.5,1.5,2.2,2.2
c2.9-3,6.9-5,11.4-5C29.5,3.7,33.6,5.8,36.5,9z"/>
<path class="st0" d="M28.5,22.7v-4.4c0-2-1.6-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v4.4H28.5z"/>
<path class="st0" d="M21.1,26.5v4.3c0,2,1.7,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-4.3H21.1z"/>
<path class="st0" d="M36,31.5c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
c0,3.7-3.4,6.7-7.5,6.7c-4.1,0-7.5-3-7.5-6.7c0-0.4,0-4.9,0-5.3c0-1-0.7-1.8-1.6-1.8C14.9,24.3,14,25,14,26c0,0.3,0,5.4,0,5.5
c0,5.1,4,9.3,9.2,10.1l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8c0-1-0.8-1.8-1.8-1.8h-4.4
l0-3.4C32,40.7,36,36.6,36,31.5z"/>
</g>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8664 5.59992L25.3331 4.13328C23.0664 1.59998 19.7332 0 16.0665 0C12.5333 0 9.26664 1.53331 7 3.93328L8.46665 5.39993C10.4 3.39995 13.0666 2.06664 16.0665 2.06664C19.1998 2.06664 21.9331 3.46662 23.8664 5.59992ZM20.4664 8.99975L21.9331 7.5331C20.5331 5.86646 18.3998 4.7998 16.0665 4.7998C13.7999 4.7998 11.7999 5.79979 10.3999 7.3331L11.8665 8.79975C12.9332 7.66643 14.3998 6.86644 16.0665 6.86644C17.8665 6.86644 19.4664 7.66643 20.4664 8.99975ZM18.5334 11.8004V14.7337H13.6001V11.8004C13.6001 10.467 14.7334 9.40039 16.0667 9.40039C17.4667 9.40039 18.5334 10.467 18.5334 11.8004ZM13.6001 17.2666V20.1332C13.6001 21.4665 14.7334 22.5332 16.0667 22.5332C17.4 22.5332 18.5334 21.4665 18.5334 20.1332V17.2666H13.6001ZM23.5332 17.0667V20.6C23.5332 23.9999 20.8665 26.7332 17.3999 27.3332V29.5998H20.3332C20.9999 29.5998 21.5332 30.1332 21.5332 30.7998C21.5332 31.4665 20.9999 31.9998 20.3332 31.9998H12.1333C11.4667 31.9998 10.9333 31.4665 10.9333 30.7998C10.9333 30.1332 11.4667 29.5998 12.1333 29.5998H14.9999V27.3332C11.5333 26.7999 8.8667 23.9999 8.8667 20.6V16.9333C8.8667 16.2667 9.46669 15.8 10.1333 15.8667C10.7333 15.8667 11.2 16.4 11.2 17.0667V20.6C11.2 23.0666 13.4666 25.0666 16.1999 25.0666C18.9332 25.0666 21.1999 23.0666 21.1999 20.6V17.1333C21.1999 16.4667 21.6665 15.9334 22.3332 15.9334C22.9332 15.9334 23.5332 16.4 23.5332 17.0667Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -59,4 +59,4 @@
d="m 27.9,20.9 c 0,0 0,-3.6 0,-3.8 0,-0.7 -0.6,-1.2 -1.3,-1.2 -0.7,0 -1.2,0.6 -1.2,1.3 0,0.2 0,3.4 0,3.7 0,2.6 -2.4,4.8 -5.3,4.8 -2.9,0 -5.3,-2.1 -5.3,-4.8 0,-0.3 0,-3.5 0,-3.8 0,-0.7 -0.5,-1.3 -1.2,-1.3 -0.7,0 -1.3,0.5 -1.3,1.2 0,0.2 0,3.9 0,3.9 0,3.6 2.9,6.6 6.6,7.2 l 0,2.4 -3.1,0 c -0.7,0 -1.3,0.6 -1.3,1.3 0,0.7 0.6,1.3 1.3,1.3 l 8.8,0 c 0.7,0 1.3,-0.6 1.3,-1.3 0,-0.7 -0.6,-1.3 -1.3,-1.3 l -3.2,0 0,-2.4 c 3.6,-0.5 6.5,-3.5 6.5,-7.2 z"
id="path6952"
inkscape:connector-curvature="0"
style="fill:#ffffff" /></svg>
style="fill:#ffffff" /></svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -7,24 +7,57 @@
//
import Hifi 1.0 as Hifi
import QtQuick 2.4
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "./hifi/audio" as HifiAudio
import TabletScriptingInterface 1.0
Item {
id: root;
objectName: "AvatarInputsBar"
property int modality: Qt.NonModal
width: audio.width;
height: audio.height;
x: 10; y: 5;
readonly property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled;
x: 10;
y: 5;
readonly property bool shouldReposition: true;
property bool hmdActive: HMD.active;
width: hmdActive ? audio.width : audioApplication.width;
height: hmdActive ? audio.height : audioApplication.height;
Timer {
id: hmdActiveCheckTimer;
interval: 500;
repeat: true;
onTriggered: {
root.hmdActive = HMD.active;
}
}
HifiAudio.MicBar {
id: audio;
visible: AvatarInputs.showAudioTools;
visible: AvatarInputs.showAudioTools && root.hmdActive;
standalone: true;
dragTarget: parent;
dragTarget: parent;
}
HifiAudio.MicBarApplication {
id: audioApplication;
visible: AvatarInputs.showAudioTools && !root.hmdActive;
standalone: true;
dragTarget: parent;
}
Component.onCompleted: {
HMD.displayModeChanged.connect(function(isHmdMode) {
root.hmdActive = isHmdMode;
});
}
BubbleIcon {
dragTarget: parent
visible: !root.hmdActive;
}
}

View file

@ -0,0 +1,100 @@
//
// Created by Bradley Austin Davis on 2015/06/19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "./hifi/audio" as HifiAudio
import TabletScriptingInterface 1.0
Rectangle {
id: bubbleRect
width: bubbleIcon.width + 10
height: bubbleIcon.height + 10
radius: 5;
property var dragTarget: null;
property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled;
function updateOpacity() {
if (ignoreRadiusEnabled) {
bubbleRect.opacity = 1.0;
} else {
bubbleRect.opacity = 0.7;
}
}
Component.onCompleted: {
updateOpacity();
}
onIgnoreRadiusEnabledChanged: {
updateOpacity();
}
color: "#00000000";
border {
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
color: "#80FFFFFF";
}
anchors {
left: dragTarget ? dragTarget.right : undefined
top: dragTarget ? dragTarget.top : undefined
}
// borders are painted over fill, so reduce the fill to fit inside the border
Rectangle {
color: "#55000000";
width: 40;
height: 40;
radius: 5;
anchors {
verticalCenter: parent.verticalCenter;
horizontalCenter: parent.horizontalCenter;
}
}
MouseArea {
id: mouseArea;
anchors.fill: parent
hoverEnabled: true;
scrollGestureEnabled: false;
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
Users.toggleIgnoreRadius();
}
drag.target: dragTarget;
onContainsMouseChanged: {
var rectOpacity = (ignoreRadiusEnabled && containsMouse) ? 1.0 : (containsMouse ? 1.0 : 0.7);
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonHover);
}
bubbleRect.opacity = rectOpacity;
}
}
Image {
id: bubbleIcon
source: "../icons/tablet-icons/bubble-i.svg";
sourceSize: Qt.size(32, 32);
smooth: true;
anchors.top: parent.top
anchors.topMargin: (parent.height - bubbleIcon.height) / 2
anchors.left: parent.left
anchors.leftMargin: (parent.width - bubbleIcon.width) / 2
}
ColorOverlay {
id: bubbleIconOverlay
anchors.fill: bubbleIcon
source: bubbleIcon
color: "#FFFFFF";
}
}

View file

@ -24,6 +24,7 @@ CheckBox {
leftPadding: 0
property int colorScheme: hifi.colorSchemes.light
property string color: hifi.colors.lightGrayText
property int fontSize: hifi.fontSizes.inputLabel
readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light
property bool isRedCheck: false
property bool isRound: false
@ -109,7 +110,7 @@ CheckBox {
contentItem: Text {
id: root
font.pixelSize: hifi.fontSizes.inputLabel
font.pixelSize: fontSize;
font.family: "Raleway"
font.weight: Font.DemiBold
text: checkBox.text

View file

@ -21,6 +21,7 @@ Item {
property int switchWidth: 70;
readonly property int switchRadius: height/2;
property string labelTextOff: "";
property int labelTextSize: hifi.fontSizes.inputLabel;
property string labelGlyphOffText: "";
property int labelGlyphOffSize: 32;
property string labelTextOn: "";
@ -89,7 +90,7 @@ Item {
RalewaySemiBold {
id: labelOff;
text: labelTextOff;
size: hifi.fontSizes.inputLabel;
size: labelTextSize;
color: originalSwitch.checked ? hifi.colors.lightGrayText : "#FFFFFF";
anchors.top: parent.top;
anchors.right: parent.right;
@ -130,7 +131,7 @@ Item {
RalewaySemiBold {
id: labelOn;
text: labelTextOn;
size: hifi.fontSizes.inputLabel;
size: labelTextSize;
color: originalSwitch.checked ? "#FFFFFF" : hifi.colors.lightGrayText;
anchors.top: parent.top;
anchors.left: parent.left;

View file

@ -0,0 +1,152 @@
//
// EditAvatarInputsBar.qml
// qml/hifi
//
// Audio setup
//
// Created by Wayne Chen on 3/20/2019
// Copyright 2019 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
//
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../windows"
Rectangle {
id: editRect
HifiConstants { id: hifi; }
color: hifi.colors.baseGray;
signal sendToScript(var message);
function emitSendToScript(message) {
sendToScript(message);
}
function fromScript(message) {
}
RalewayRegular {
id: title;
color: hifi.colors.white;
text: qsTr("Avatar Inputs Persistent UI Settings")
size: 20
font.bold: true
anchors {
top: parent.top
left: parent.left
leftMargin: (parent.width - width) / 2
}
}
HifiControlsUit.Slider {
id: xSlider
anchors {
top: title.bottom
topMargin: 50
left: parent.left
leftMargin: 20
}
label: "X OFFSET: " + value.toFixed(2);
maximumValue: 1.0
minimumValue: -1.0
stepSize: 0.05
value: -0.2
width: 300
onValueChanged: {
emitSendToScript({
"method": "reposition",
"x": value
});
}
}
HifiControlsUit.Slider {
id: ySlider
anchors {
top: xSlider.bottom
topMargin: 50
left: parent.left
leftMargin: 20
}
label: "Y OFFSET: " + value.toFixed(2);
maximumValue: 1.0
minimumValue: -1.0
stepSize: 0.05
value: -0.125
width: 300
onValueChanged: {
emitSendToScript({
"method": "reposition",
"y": value
});
}
}
HifiControlsUit.Slider {
id: zSlider
anchors {
top: ySlider.bottom
topMargin: 50
left: parent.left
leftMargin: 20
}
label: "Z OFFSET: " + value.toFixed(2);
maximumValue: 0.0
minimumValue: -1.0
stepSize: 0.05
value: -0.5
width: 300
onValueChanged: {
emitSendToScript({
"method": "reposition",
"z": value
});
}
}
HifiControlsUit.Button {
id: setVisibleButton;
text: setVisible ? "SET INVISIBLE" : "SET VISIBLE";
width: 300;
property bool setVisible: true;
anchors {
top: zSlider.bottom
topMargin: 50
left: parent.left
leftMargin: 20
}
onClicked: {
setVisible = !setVisible;
emitSendToScript({
"method": "setVisible",
"visible": setVisible
});
}
}
HifiControlsUit.Button {
id: printButton;
text: "PRINT POSITIONS";
width: 300;
anchors {
top: setVisibleButton.bottom
topMargin: 50
left: parent.left
leftMargin: 20
}
onClicked: {
emitSendToScript({
"method": "print",
});
}
}
}

View file

@ -129,6 +129,7 @@ Item {
height: 40
// Anchors
anchors.top: avatarImage.top
anchors.topMargin: avatarImage.visible ? 18 : 0;
anchors.left: avatarImage.right
anchors.leftMargin: avatarImage.visible ? 5 : 0;
anchors.rightMargin: 5;

View file

@ -31,6 +31,8 @@ Rectangle {
property string title: "Audio Settings"
property int switchHeight: 16
property int switchWidth: 40
property bool pushToTalk: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD;
property bool muted: (bar.currentIndex === 0) ? AudioScriptingInterface.mutedDesktop : AudioScriptingInterface.mutedHMD;
readonly property real verticalScrollWidth: 10
readonly property real verticalScrollShaft: 8
signal sendToScript(var message);
@ -44,7 +46,7 @@ Rectangle {
property bool isVR: AudioScriptingInterface.context === "VR"
property real rightMostInputLevelPos: 440
property real rightMostInputLevelPos: root.width
//placeholder for control sizes and paddings
//recalculates dynamically in case of UI size is changed
QtObject {
@ -103,7 +105,9 @@ Rectangle {
}
}
Component.onCompleted: enablePeakValues();
Component.onCompleted: {
enablePeakValues();
}
Flickable {
id: flickView;
@ -178,15 +182,25 @@ Rectangle {
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: "Mute microphone";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.muted;
checked: muted;
onClicked: {
if (AudioScriptingInterface.pushToTalk && !checked) {
if (pushToTalk && !checked) {
// disable push to talk if unmuting
AudioScriptingInterface.pushToTalk = false;
if (bar.currentIndex === 0) {
AudioScriptingInterface.pushToTalkDesktop = false;
}
else {
AudioScriptingInterface.pushToTalkHMD = false;
}
}
if (bar.currentIndex === 0) {
AudioScriptingInterface.mutedDesktop = checked;
}
else {
AudioScriptingInterface.mutedHMD = checked;
}
AudioScriptingInterface.muted = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
}
}
@ -198,6 +212,7 @@ Rectangle {
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: "Noise Reduction";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.noiseReduction;
onCheckedChanged: {
@ -213,7 +228,8 @@ Rectangle {
anchors.top: noiseReductionSwitch.bottom
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: qsTr("Push To Talk (T)");
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD;
onCheckedChanged: {
@ -222,13 +238,6 @@ Rectangle {
} else {
AudioScriptingInterface.pushToTalkHMD = checked;
}
checked = Qt.binding(function() {
if (bar.currentIndex === 0) {
return AudioScriptingInterface.pushToTalkDesktop;
} else {
return AudioScriptingInterface.pushToTalkHMD;
}
}); // restore binding
}
}
}
@ -245,7 +254,8 @@ Rectangle {
switchWidth: root.switchWidth;
anchors.top: parent.top
anchors.left: parent.left
labelTextOn: qsTr("Warn when muted");
labelTextOn: qsTr("Warn when muted in HMD");
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.warnWhenMuted;
onClicked: {
@ -263,6 +273,7 @@ Rectangle {
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: qsTr("Audio Level Meter");
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AvatarInputs.showAudioTools;
onCheckedChanged: {
@ -279,6 +290,7 @@ Rectangle {
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: qsTr("Stereo input");
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.isStereoInput;
onCheckedChanged: {
@ -314,7 +326,7 @@ Rectangle {
Separator {
id: secondSeparator;
anchors.top: pttTextContainer.bottom;
anchors.top: pttTextContainer.visible ? pttTextContainer.bottom : switchesContainer.bottom;
anchors.topMargin: 10;
}
@ -341,7 +353,7 @@ Rectangle {
width: margins.sizeText + margins.sizeLevel;
anchors.left: parent.left;
anchors.leftMargin: margins.sizeCheckBox;
size: 16;
size: 22;
color: hifi.colors.white;
text: qsTr("Choose input device");
}
@ -349,16 +361,17 @@ Rectangle {
ListView {
id: inputView;
width: parent.width - margins.paddings*2;
width: rightMostInputLevelPos;
anchors.top: inputDeviceHeader.bottom;
anchors.topMargin: 10;
x: margins.paddings
interactive: false;
height: contentHeight;
spacing: 4;
clip: true;
model: AudioScriptingInterface.devices.input;
delegate: Item {
width: rightMostInputLevelPos
width: rightMostInputLevelPos - margins.paddings*2
height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
margins.sizeCheckBox : checkBoxInput.implicitHeight
@ -374,6 +387,7 @@ Rectangle {
boxSize: margins.sizeCheckBox / 2
isRound: true
text: devicename
fontSize: 16;
onPressed: {
if (!checked) {
stereoInput.checked = false;
@ -407,7 +421,7 @@ Rectangle {
Separator {
id: thirdSeparator;
anchors.top: loopbackAudio.bottom;
anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputView.bottom;
anchors.topMargin: 10;
}
@ -434,7 +448,7 @@ Rectangle {
anchors.left: parent.left
anchors.leftMargin: margins.sizeCheckBox
anchors.verticalCenter: parent.verticalCenter;
size: 16;
size: 22;
color: hifi.colors.white;
text: qsTr("Choose output device");
}
@ -443,7 +457,8 @@ Rectangle {
ListView {
id: outputView
width: parent.width - margins.paddings*2
x: margins.paddings
x: margins.paddings;
interactive: false;
height: contentHeight;
anchors.top: outputDeviceHeader.bottom;
anchors.topMargin: 10;
@ -464,6 +479,7 @@ Rectangle {
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
checkable: !checked
text: devicename
fontSize: 16
onPressed: {
if (!checked) {
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
@ -526,7 +542,7 @@ Rectangle {
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: avatarGainSliderText;
text: "Avatar volume";
text: "People volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;

View file

@ -12,24 +12,26 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
Rectangle {
Item {
property var peak;
width: 70;
height: 8;
color: "transparent";
Item {
QtObject {
id: colors;
readonly property string unmuted: "#FFF";
readonly property string muted: "#E2334D";
readonly property string gutter: "#575757";
readonly property string greenStart: "#39A38F";
readonly property string greenEnd: "#1FC6A6";
readonly property string yellow: "#C0C000";
readonly property string red: colors.muted;
readonly property string fill: "#55000000";
}
Text {
id: status;
@ -79,23 +81,19 @@ Rectangle {
anchors { fill: mask }
source: mask
start: Qt.point(0, 0);
end: Qt.point(70, 0);
end: Qt.point(bar.width, 0);
gradient: Gradient {
GradientStop {
position: 0;
color: colors.greenStart;
}
GradientStop {
position: 0.8;
position: 0.5;
color: colors.greenEnd;
}
GradientStop {
position: 0.801;
color: colors.red;
}
GradientStop {
position: 1;
color: colors.red;
color: colors.yellow;
}
}
}

View file

@ -60,11 +60,11 @@ RowLayout {
}
}
// RalewayRegular {
// Layout.leftMargin: 2;
// size: 14;
// color: "white";
// font.italic: true
// text: audioLoopedBack ? qsTr("Speak in your input") : "";
// }
RalewayRegular {
Layout.leftMargin: 2;
size: 18;
color: "white";
font.italic: true
text: audioLoopedBack ? qsTr("Speak in your input") : "";
}
}

View file

@ -16,14 +16,33 @@ import stylesUit 1.0
import TabletScriptingInterface 1.0
Rectangle {
id: micBar
HifiConstants { id: hifi; }
property var muted: AudioScriptingInterface.muted;
readonly property var level: AudioScriptingInterface.inputLevel;
readonly property var clipping: AudioScriptingInterface.clipping;
property var pushToTalk: AudioScriptingInterface.pushToTalk;
property var pushingToTalk: AudioScriptingInterface.pushingToTalk;
readonly property var userSpeakingLevel: 0.4;
property bool gated: false;
Component.onCompleted: {
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
HMD.displayModeChanged.connect(function() {
muted = AudioScriptingInterface.muted;
pushToTalk = AudioScriptingInterface.pushToTalk;
});
AudioScriptingInterface.mutedChanged.connect(function() {
muted = AudioScriptingInterface.muted;
});
AudioScriptingInterface.pushToTalkChanged.connect(function() {
pushToTalk = AudioScriptingInterface.pushToTalk;
});
AudioScriptingInterface.pushingToTalkChanged.connect(function() {
pushingToTalk = AudioScriptingInterface.pushingToTalk;
});
}
property bool standalone: false;
@ -67,10 +86,10 @@ Rectangle {
hoverEnabled: true;
scrollGestureEnabled: false;
onClicked: {
if (AudioScriptingInterface.pushToTalk) {
if (pushToTalk) {
return;
}
AudioScriptingInterface.muted = !AudioScriptingInterface.muted;
AudioScriptingInterface.muted = !muted;
Tablet.playSound(TabletEnums.ButtonClick);
}
drag.target: dragTarget;
@ -84,16 +103,16 @@ Rectangle {
QtObject {
id: colors;
readonly property string unmuted: "#FFF";
readonly property string muted: "#E2334D";
readonly property string unmutedColor: "#FFF";
readonly property string mutedColor: "#E2334D";
readonly property string gutter: "#575757";
readonly property string greenStart: "#39A38F";
readonly property string greenEnd: "#1FC6A6";
readonly property string yellow: "#C0C000";
readonly property string red: colors.muted;
readonly property string red: colors.mutedColor;
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
readonly property string icon: AudioScriptingInterface.muted ? muted : unmuted;
readonly property string icon: muted ? colors.mutedColor : unmutedColor;
}
Item {
@ -113,9 +132,12 @@ Rectangle {
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg";
readonly property string clippingIcon: "../../../icons/tablet-icons/mic-clip-i.svg";
readonly property string gatedIcon: "../../../icons/tablet-icons/mic-gate-i.svg";
id: image;
source: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? pushToTalkIcon : AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
source: (pushToTalk && !pushingToTalk) ? pushToTalkIcon : muted ? mutedIcon :
clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon;
width: 30;
height: 30;
@ -138,9 +160,7 @@ Rectangle {
Item {
id: status;
readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted;
visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted;
visible: (pushToTalk && !pushingToTalk) || muted;
anchors {
left: parent.left;
@ -157,9 +177,9 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
color: parent.color;
color: colors.icon;
text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? (HMD.active ? "MUTED PTT" : "MUTED PTT-(T)") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE");
text: (pushToTalk && !pushingToTalk) ? (HMD.active ? "MUTED PTT" : "MUTED PTT-(T)") : (muted ? "MUTED" : "MUTE");
font.pointSize: 12;
}
@ -169,9 +189,9 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50;
width: pushToTalk && !pushingToTalk ? (HMD.active ? 27 : 25) : 50;
height: 4;
color: parent.color;
color: colors.icon;
}
Rectangle {
@ -180,9 +200,9 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50;
width: pushToTalk && !pushingToTalk ? (HMD.active ? 27 : 25) : 50;
height: 4;
color: parent.color;
color: colors.icon;
}
}

View file

@ -0,0 +1,255 @@
//
// MicBarApplication.qml
// qml/hifi/audio
//
// Created by Zach Pomerantz on 6/14/2017
// Copyright 2017 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
//
import QtQuick 2.5
import QtGraphicalEffects 1.0
import stylesUit 1.0
import TabletScriptingInterface 1.0
Rectangle {
id: micBar;
readonly property var level: AudioScriptingInterface.inputLevel;
readonly property var clipping: AudioScriptingInterface.clipping;
property var muted: AudioScriptingInterface.muted;
property var pushToTalk: AudioScriptingInterface.pushToTalk;
property var pushingToTalk: AudioScriptingInterface.pushingToTalk;
readonly property var userSpeakingLevel: 0.4;
property bool gated: false;
Component.onCompleted: {
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
HMD.displayModeChanged.connect(function() {
muted = AudioScriptingInterface.muted;
pushToTalk = AudioScriptingInterface.pushToTalk;
});
AudioScriptingInterface.mutedChanged.connect(function() {
muted = AudioScriptingInterface.muted;
});
AudioScriptingInterface.pushToTalkChanged.connect(function() {
pushToTalk = AudioScriptingInterface.pushToTalk;
});
}
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg";
readonly property string clippingIcon: "../../../icons/tablet-icons/mic-clip-i.svg";
readonly property string gatedIcon: "../../../icons/tablet-icons/mic-gate-i.svg";
property bool standalone: false;
property var dragTarget: null;
width: 44;
height: 44;
radius: 5;
opacity: 0.7;
onLevelChanged: {
var rectOpacity = (muted && (level >= userSpeakingLevel)) ? 1.0 : 0.7;
if (pushToTalk && !pushingToTalk) {
rectOpacity = (mouseArea.containsMouse) ? 1.0 : 0.7;
} else if (mouseArea.containsMouse && rectOpacity != 1.0) {
rectOpacity = 1.0;
}
micBar.opacity = rectOpacity;
}
color: "#00000000";
border {
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
color: colors.border;
}
// borders are painted over fill, so reduce the fill to fit inside the border
Rectangle {
color: standalone ? colors.fill : "#00000000";
width: 40;
height: 40;
radius: 5;
anchors {
verticalCenter: parent.verticalCenter;
horizontalCenter: parent.horizontalCenter;
}
}
MouseArea {
id: mouseArea;
anchors {
left: icon.left;
right: bar.right;
top: icon.top;
bottom: icon.bottom;
}
hoverEnabled: true;
scrollGestureEnabled: false;
onClicked: {
if (pushToTalk) {
return;
}
AudioScriptingInterface.muted = !muted;
Tablet.playSound(TabletEnums.ButtonClick);
muted = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
}
drag.target: dragTarget;
onContainsMouseChanged: {
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
}
QtObject {
id: colors;
readonly property string unmutedColor: "#FFF";
readonly property string gatedColor: "#00BDFF";
readonly property string mutedColor: "#E2334D";
readonly property string gutter: "#575757";
readonly property string greenStart: "#39A38F";
readonly property string greenEnd: "#1FC6A6";
readonly property string yellow: "#C0C000";
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
readonly property string icon: (muted || clipping) ? mutedColor : gated ? gatedColor : unmutedColor;
}
Item {
id: icon;
anchors {
left: parent.left;
top: parent.top;
}
width: 40;
height: 40;
Item {
Image {
id: image;
source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon :
clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon;
width: 29;
height: 32;
anchors {
left: parent.left;
top: parent.top;
topMargin: 5;
}
}
ColorOverlay {
id: imageOverlay
anchors { fill: image }
source: image;
color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : colors.icon;
}
}
}
Item {
id: status;
visible: pushToTalk || (muted && (level >= userSpeakingLevel));
anchors {
left: parent.left;
top: icon.bottom;
topMargin: 2;
}
width: parent.width;
height: statusTextMetrics.height;
TextMetrics {
id: statusTextMetrics
text: statusText.text
font: statusText.font
}
RalewaySemiBold {
id: statusText
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : (level >= userSpeakingLevel && muted) ? colors.mutedColor : colors.unmutedColor;
font.bold: true
text: pushToTalk ? (HMD.active ? "PTT" : "PTT-(T)") : (muted ? "MUTED" : "MUTE");
size: 12;
}
}
Item {
id: bar;
anchors {
right: parent.right;
rightMargin: 7;
top: parent.top
topMargin: 5
}
width: 8;
height: 32;
Rectangle { // base
id: baseBar
radius: 4;
anchors { fill: parent }
color: colors.gutter;
}
Rectangle { // mask
id: mask;
visible: (!(pushToTalk && !pushingToTalk))
height: parent.height * level;
width: parent.width;
radius: 5;
anchors {
bottom: parent.bottom;
bottomMargin: 0;
left: parent.left;
leftMargin: 0;
}
}
LinearGradient {
anchors { fill: mask }
visible: (!(pushToTalk && !pushingToTalk))
source: mask
start: Qt.point(0, 0);
end: Qt.point(0, bar.height);
rotation: 180
gradient: Gradient {
GradientStop {
position: 0.0;
color: colors.greenStart;
}
GradientStop {
position: 0.5;
color: colors.greenEnd;
}
GradientStop {
position: 1.0;
color: colors.yellow;
}
}
}
}
}

View file

@ -64,11 +64,11 @@ RowLayout {
height: 32;
}
// RalewayRegular {
// Layout.leftMargin: 2;
// size: 14;
// color: "white";
// font.italic: true
// text: isPlaying ? qsTr("Listen to your output") : "";
// }
RalewayRegular {
Layout.leftMargin: 2;
size: 18;
color: "white";
font.italic: true
text: isPlaying ? qsTr("Listen to your output") : "";
}
}

View file

@ -40,7 +40,7 @@ Item {
}
}
HifiAudio.MicBar {
HifiAudio.MicBarApplication {
anchors {
left: parent.left
leftMargin: 30

View file

@ -338,6 +338,10 @@ Setting::Handle<int> maxOctreePacketsPerSecond{"maxOctreePPS", DEFAULT_MAX_OCTRE
Setting::Handle<bool> loginDialogPoppedUp{"loginDialogPoppedUp", false};
static const QUrl AVATAR_INPUTS_BAR_QML = PathUtils::qmlUrl("AvatarInputsBar.qml");
static const QUrl MIC_BAR_APPLICATION_QML = PathUtils::qmlUrl("hifi/audio/MicBarApplication.qml");
static const QUrl BUBBLE_ICON_QML = PathUtils::qmlUrl("BubbleIcon.qml");
static const QString STANDARD_TO_ACTION_MAPPING_NAME = "Standard to Action";
static const QString NO_MOVEMENT_MAPPING_NAME = "Standard to Action (No Movement)";
static const QString NO_MOVEMENT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_nomovement.json";
@ -2372,7 +2376,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
});
auto rootItemLoadedFunctor = [webSurface, url, isTablet] {
Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString());
Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString() || url == AVATAR_INPUTS_BAR_QML.toString() ||
url == BUBBLE_ICON_QML.toString());
};
if (webSurface->getRootItem()) {
rootItemLoadedFunctor();
@ -2878,11 +2883,19 @@ void Application::initializeGL() {
}
#if !defined(DISABLE_QML)
QStringList chromiumFlags;
// Bug 21993: disable microphone and camera input
chromiumFlags << "--use-fake-device-for-media-stream";
// Disable signed distance field font rendering on ATI/AMD GPUs, due to
// https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app
std::string vendor{ (const char*)glGetString(GL_VENDOR) };
if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) {
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text"));
chromiumFlags << "--disable-distance-field-text";
}
// Ensure all Qt webengine processes launched from us have the appropriate command line flags
if (!chromiumFlags.empty()) {
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags.join(' ').toLocal8Bit());
}
#endif
@ -3291,6 +3304,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml");
offscreenUi->show(qml, "AvatarInputsBar");
#endif
_desktopRootItemCreated = true;
}
void Application::userKickConfirmation(const QUuid& nodeID) {
@ -3722,14 +3736,11 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (_firstRun.get()) {
#if !defined(Q_OS_ANDROID)
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
#endif
_firstRun.set(false);
} else {
#if !defined(Q_OS_ANDROID)
QString goingTo = "";
if (addressLookupString.isEmpty()) {
if (Menu::getInstance()->isOptionChecked(MenuOption::HomeLocation)) {
@ -3743,7 +3754,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(!goingTo.isEmpty() ? goingTo : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
sentTo = SENT_TO_PREVIOUS_LOCATION;
#endif
}
UserActivityLogger::getInstance().logAction("startup_sent_to", {
@ -8972,6 +8982,38 @@ void Application::updateLoginDialogPosition() {
}
}
void Application::createAvatarInputsBar() {
const glm::vec3 LOCAL_POSITION { 0.0, 0.0, -1.0 };
// DEFAULT_DPI / tablet scale percentage
const float DPI = 31.0f / (75.0f / 100.0f);
EntityItemProperties properties;
properties.setType(EntityTypes::Web);
properties.setName("AvatarInputsBarEntity");
properties.setSourceUrl(AVATAR_INPUTS_BAR_QML.toString());
properties.setParentID(getMyAvatar()->getSelfID());
properties.setParentJointIndex(getMyAvatar()->getJointIndex("_CAMERA_MATRIX"));
properties.setPosition(LOCAL_POSITION);
properties.setLocalRotation(Quaternions::IDENTITY);
//properties.setDimensions(LOGIN_DIMENSIONS);
properties.setPrimitiveMode(PrimitiveMode::SOLID);
properties.getGrab().setGrabbable(false);
properties.setIgnorePickIntersection(false);
properties.setAlpha(1.0f);
properties.setDPI(DPI);
properties.setVisible(true);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
_avatarInputsBarID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL);
}
void Application::destroyAvatarInputsBar() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
if (!_avatarInputsBarID.isNull()) {
entityScriptingInterface->deleteEntity(_avatarInputsBarID);
}
}
bool Application::hasRiftControllers() {
return PluginUtils::isOculusTouchControllerAvailable();
}

View file

@ -330,6 +330,9 @@ public:
void createLoginDialog();
void updateLoginDialogPosition();
void createAvatarInputsBar();
void destroyAvatarInputsBar();
// Check if a headset is connected
bool hasRiftControllers();
bool hasViveControllers();
@ -704,12 +707,14 @@ private:
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
bool _interstitialModeEnabled{ false };
bool _loginDialogPoppedUp = false;
bool _loginDialogPoppedUp{ false };
bool _desktopRootItemCreated{ false };
bool _developerMenuVisible{ false };
QString _previousAvatarSkeletonModel;
float _previousAvatarTargetScale;
CameraMode _previousCameraMode;
QUuid _loginDialogID;
QUuid _avatarInputsBarID;
LoginStateManager _loginStateManager;
quint64 _lastFaceTrackerUpdate;

View file

@ -38,6 +38,7 @@
#include <UsersScriptingInterface.h>
#include <UUID.h>
#include <shared/ConicalViewFrustum.h>
#include <ui/AvatarInputs.h>
#include "Application.h"
#include "InterfaceLogging.h"
@ -84,7 +85,6 @@ AvatarManager::AvatarManager(QObject* parent) :
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
const auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);
if (otherAvatar && _space) {
std::unique_lock<std::mutex> lock(_spaceLock);
@ -210,7 +210,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
{
// lock the hash for read to check the size
QReadLocker lock(&_hashLock);
if (_avatarHash.size() < 2 && _avatarsToFadeOut.isEmpty()) {
if (_avatarHash.size() < 2) {
return;
}
}
@ -375,19 +375,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
qApp->getMain3DScene()->enqueueTransaction(renderTransaction);
}
if (!_spaceProxiesToDelete.empty() && _space) {
std::unique_lock<std::mutex> lock(_spaceLock);
workloadTransaction.remove(_spaceProxiesToDelete);
_spaceProxiesToDelete.clear();
}
_space->enqueueTransaction(workloadTransaction);
_numAvatarsUpdated = numAvatarsUpdated;
_numAvatarsNotUpdated = numAvatarsNotUpdated;
_numHeroAvatarsUpdated = numHerosUpdated;
simulateAvatarFades(deltaTime);
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
}
@ -400,31 +393,6 @@ void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scen
}
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
if (_avatarsToFadeOut.empty()) {
return;
}
QReadLocker locker(&_hashLock);
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFadeOut.begin();
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
while (avatarItr != _avatarsToFadeOut.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
avatar->updateFadingStatus();
if (!avatar->isFading()) {
// fading to zero is such a rare event we push a unique transaction for each
if (avatar->isInScene()) {
avatar->removeFromScene(*avatarItr, scene, transaction);
}
avatarItr = _avatarsToFadeOut.erase(avatarItr);
} else {
++avatarItr;
}
}
scene->enqueueTransaction(transaction);
}
AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
auto otherAvatar = new OtherAvatar(qApp->thread());
otherAvatar->setSessionUUID(sessionUUID);
@ -452,7 +420,6 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact
transaction.objectsToRemove.push_back(mState);
}
avatar->resetDetailedMotionStates();
} else {
if (avatar->getDetailedMotionStates().size() == 0) {
avatar->createDetailedMotionStates(avatar);
@ -520,10 +487,6 @@ void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities)
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
auto avatar = std::static_pointer_cast<OtherAvatar>(removedAvatar);
{
std::unique_lock<std::mutex> lock(_spaceLock);
_spaceProxiesToDelete.push_back(avatar->getSpaceIndex());
}
AvatarHashMap::handleRemovedAvatar(avatar, removalReason);
avatar->tearDownGrabs();
@ -534,16 +497,39 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
// it might not fire until after we create a new instance for the same remote avatar, which creates a race
// on the creation of entities for that avatar instance and the deletion of entities for this instance
avatar->removeAvatarEntitiesFromTree();
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID());
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
workload::Transaction workloadTransaction;
workloadTransaction.remove(avatar->getSpaceIndex());
_space->enqueueTransaction(workloadTransaction);
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->removeFromScene(avatar, scene, transaction);
scene->enqueueTransaction(transaction);
} else if (removalReason == KillAvatarReason::AvatarDisconnected) {
// remove from node sets, if present
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
avatar->fadeOut(qApp->getMain3DScene(), removalReason);
render::Transaction transaction;
auto scene = qApp->getMain3DScene();
avatar->fadeOut(transaction, removalReason);
workload::SpacePointer space = _space;
transaction.transitionFinishedOperator(avatar->getRenderItemID(), [space, avatar]() {
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->removeFromScene(avatar, scene, transaction);
scene->enqueueTransaction(transaction);
workload::Transaction workloadTransaction;
workloadTransaction.remove(avatar->getSpaceIndex());
space->enqueueTransaction(workloadTransaction);
});
scene->enqueueTransaction(transaction);
}
_avatarsToFadeOut.push_back(removedAvatar);
}
void AvatarManager::clearOtherAvatars() {

View file

@ -220,8 +220,6 @@ private:
explicit AvatarManager(QObject* parent = 0);
explicit AvatarManager(const AvatarManager& other);
void simulateAvatarFades(float deltaTime);
AvatarSharedPointer newSharedAvatar(const QUuid& sessionUUID) override;
// called only from the AvatarHashMap thread - cannot be called while this thread holds the
@ -231,8 +229,6 @@ private:
KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
void handleTransitAnimations(AvatarTransit::Status status);
QVector<AvatarSharedPointer> _avatarsToFadeOut;
using SetOfOtherAvatars = std::set<OtherAvatarPointer>;
SetOfOtherAvatars _avatarsToChangeInPhysics;
@ -252,7 +248,6 @@ private:
mutable std::mutex _spaceLock;
workload::SpacePointer _space;
std::vector<int32_t> _spaceProxiesToDelete;
AvatarTransit::TransitConfig _transitConfig;
};

View file

@ -942,8 +942,6 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
}
handleChangedAvatarEntityData();
updateFadingStatus();
}
// As far as I know no HMD system supports a play area of a kilometer in radius.

View file

@ -356,7 +356,6 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
PROFILE_RANGE(simulation, "grabs");
applyGrabChanges();
}
updateFadingStatus();
}
void OtherAvatar::handleChangedAvatarEntityData() {

View file

@ -88,44 +88,44 @@ void Audio::setMuted(bool isMuted) {
void Audio::setMutedDesktop(bool isMuted) {
bool changed = false;
withWriteLock([&] {
if (_desktopMuted != isMuted) {
if (_mutedDesktop != isMuted) {
changed = true;
_desktopMuted = isMuted;
_mutedDesktop = isMuted;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
}
});
if (changed) {
emit mutedChanged(isMuted);
emit desktopMutedChanged(isMuted);
emit mutedDesktopChanged(isMuted);
}
}
bool Audio::getMutedDesktop() const {
return resultWithReadLock<bool>([&] {
return _desktopMuted;
return _mutedDesktop;
});
}
void Audio::setMutedHMD(bool isMuted) {
bool changed = false;
withWriteLock([&] {
if (_hmdMuted != isMuted) {
if (_mutedHMD != isMuted) {
changed = true;
_hmdMuted = isMuted;
_mutedHMD = isMuted;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
}
});
if (changed) {
emit mutedChanged(isMuted);
emit hmdMutedChanged(isMuted);
emit mutedHMDChanged(isMuted);
}
}
bool Audio::getMutedHMD() const {
return resultWithReadLock<bool>([&] {
return _hmdMuted;
return _mutedHMD;
});
}
@ -217,17 +217,17 @@ void Audio::setPTTHMD(bool enabled) {
}
void Audio::saveData() {
_desktopMutedSetting.set(getMutedDesktop());
_hmdMutedSetting.set(getMutedHMD());
_mutedDesktopSetting.set(getMutedDesktop());
_mutedHMDSetting.set(getMutedHMD());
_pttDesktopSetting.set(getPTTDesktop());
_pttHMDSetting.set(getPTTHMD());
}
void Audio::loadData() {
_desktopMuted = _desktopMutedSetting.get();
_hmdMuted = _hmdMutedSetting.get();
_pttDesktop = _pttDesktopSetting.get();
_pttHMD = _pttHMDSetting.get();
setMutedDesktop(_mutedDesktopSetting.get());
setMutedHMD(_mutedHMDSetting.get());
setPTTDesktop(_pttDesktopSetting.get());
setPTTHMD(_pttHMDSetting.get());
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false));

View file

@ -41,6 +41,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
* @hifi-assignment-client
*
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
* @property {boolean} mutedDesktop - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
* above the noise floor.
@ -68,8 +69,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged)
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged)
Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged)
Q_PROPERTY(bool mutedDesktop READ getMutedDesktop WRITE setMutedDesktop NOTIFY mutedDesktopChanged)
Q_PROPERTY(bool mutedHMD READ getMutedHMD WRITE setMutedHMD NOTIFY mutedHMDChanged)
Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged);
Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged)
Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged)
@ -287,19 +288,19 @@ signals:
/**jsdoc
* Triggered when desktop audio input is muted or unmuted.
* @function Audio.desktopMutedChanged
* @function Audio.mutedDesktopChanged
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for desktop mode, otherwise <code>false</code>.
* @returns {Signal}
*/
void desktopMutedChanged(bool isMuted);
void mutedDesktopChanged(bool isMuted);
/**jsdoc
* Triggered when HMD audio input is muted or unmuted.
* @function Audio.hmdMutedChanged
* @function Audio.mutedHMDChanged
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for HMD mode, otherwise <code>false</code>.
* @returns {Signal}
*/
void hmdMutedChanged(bool isMuted);
void mutedHMDChanged(bool isMuted);
/**
* Triggered when Push-to-Talk has been enabled or disabled.
@ -418,12 +419,12 @@ private:
bool _contextIsHMD { false };
AudioDevices* getDevices() { return &_devices; }
AudioDevices _devices;
Setting::Handle<bool> _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true };
Setting::Handle<bool> _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true };
Setting::Handle<bool> _mutedDesktopSetting{ QStringList { Audio::AUDIO, "mutedDesktop" }, true };
Setting::Handle<bool> _mutedHMDSetting{ QStringList { Audio::AUDIO, "mutedHMD" }, true };
Setting::Handle<bool> _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false };
Setting::Handle<bool> _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false };
bool _desktopMuted{ true };
bool _hmdMuted{ false };
bool _mutedDesktop{ true };
bool _mutedHMD{ false };
bool _pttDesktop{ false };
bool _pttHMD{ false };
bool _pushingToTalk{ false };

View file

@ -12,6 +12,7 @@
#include <AudioClient.h>
#include <SettingHandle.h>
#include <trackers/FaceTracker.h>
#include <UsersScriptingInterface.h>
#include "Application.h"
#include "Menu.h"
@ -30,6 +31,10 @@ AvatarInputs* AvatarInputs::getInstance() {
AvatarInputs::AvatarInputs(QObject* parent) : QObject(parent) {
_showAudioTools = showAudioToolsSetting.get();
auto nodeList = DependencyManager::get<NodeList>();
auto usersScriptingInterface = DependencyManager::get<UsersScriptingInterface>();
connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &AvatarInputs::ignoreRadiusEnabledChanged);
connect(usersScriptingInterface.data(), &UsersScriptingInterface::enteredIgnoreRadius, this, &AvatarInputs::enteredIgnoreRadiusChanged);
}
#define AI_UPDATE(name, src) \
@ -83,6 +88,10 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) {
emit showAudioToolsChanged(_showAudioTools);
}
bool AvatarInputs::getIgnoreRadiusEnabled() const {
return DependencyManager::get<NodeList>()->getIgnoreRadiusEnabled();
}
void AvatarInputs::toggleCameraMute() {
FaceTracker* faceTracker = qApp->getSelectedFaceTracker();
if (faceTracker) {

View file

@ -42,6 +42,8 @@ class AvatarInputs : public QObject {
AI_PROPERTY(bool, isHMD, false)
Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged)
Q_PROPERTY(bool ignoreRadiusEnabled READ getIgnoreRadiusEnabled NOTIFY ignoreRadiusEnabledChanged)
//Q_PROPERTY(bool enteredIgnoreRadius READ getEnteredIgnoreRadius NOTIFY enteredIgnoreRadiusChanged)
public:
static AvatarInputs* getInstance();
@ -55,7 +57,9 @@ public:
AvatarInputs(QObject* parent = nullptr);
void update();
bool showAudioTools() const { return _showAudioTools; }
bool showAudioTools() const { return _showAudioTools; }
bool getIgnoreRadiusEnabled() const;
//bool getEnteredIgnoreRadius() const;
public slots:
@ -93,6 +97,34 @@ signals:
*/
void showAudioToolsChanged(bool show);
/**jsdoc
* @function AvatarInputs.avatarEnteredIgnoreRadius
* @param {QUuid} avatarID
* @returns {Signal}
*/
void avatarEnteredIgnoreRadius(QUuid avatarID);
/**jsdoc
* @function AvatarInputs.avatarLeftIgnoreRadius
* @param {QUuid} avatarID
* @returns {Signal}
*/
void avatarLeftIgnoreRadius(QUuid avatarID);
/**jsdoc
* @function AvatarInputs.ignoreRadiusEnabledChanged
* @param {boolean} enabled
* @returns {Signal}
*/
void ignoreRadiusEnabledChanged(bool enabled);
/**jsdoc
* @function AvatarInputs.enteredIgnoreRadiusChanged
* @param {boolean} enabled
* @returns {Signal}
*/
void enteredIgnoreRadiusChanged();
protected:
/**jsdoc
@ -106,6 +138,8 @@ protected:
Q_INVOKABLE void toggleCameraMute();
private:
void onAvatarEnteredIgnoreRadius();
void onAvatarLeftIgnoreRadius();
float _trailingAudioLoudness{ 0 };
bool _showAudioTools { false };
};

View file

@ -652,9 +652,8 @@ void Avatar::fadeIn(render::ScenePointer scene) {
scene->enqueueTransaction(transaction);
}
void Avatar::fadeOut(render::ScenePointer scene, KillAvatarReason reason) {
void Avatar::fadeOut(render::Transaction& transaction, KillAvatarReason reason) {
render::Transition::Type transitionType = render::Transition::USER_LEAVE_DOMAIN;
render::Transaction transaction;
if (reason == KillAvatarReason::YourAvatarEnteredTheirBubble) {
transitionType = render::Transition::BUBBLE_ISECT_TRESPASSER;
@ -662,7 +661,6 @@ void Avatar::fadeOut(render::ScenePointer scene, KillAvatarReason reason) {
transitionType = render::Transition::BUBBLE_ISECT_OWNER;
}
fade(transaction, transitionType);
scene->enqueueTransaction(transaction);
}
void Avatar::fade(render::Transaction& transaction, render::Transition::Type type) {
@ -672,19 +670,6 @@ void Avatar::fade(render::Transaction& transaction, render::Transition::Type typ
transaction.addTransitionToItem(itemId, type, _renderItemID);
}
}
_isFading = true;
}
void Avatar::updateFadingStatus() {
if (_isFading) {
render::Transaction transaction;
transaction.queryTransitionOnItem(_renderItemID, [this](render::ItemID id, const render::Transition* transition) {
if (!transition || transition->isFinished) {
_isFading = false;
}
});
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
}
}
void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {

View file

@ -520,9 +520,7 @@ public:
bool isMoving() const { return _moving; }
void fadeIn(render::ScenePointer scene);
void fadeOut(render::ScenePointer scene, KillAvatarReason reason);
bool isFading() const { return _isFading; }
void updateFadingStatus();
void fadeOut(render::Transaction& transaction, KillAvatarReason reason);
// JSDoc is in AvatarData.h.
Q_INVOKABLE virtual float getEyeHeight() const override;
@ -727,7 +725,6 @@ protected:
bool _initialized { false };
bool _isAnimatingScale { false };
bool _mustFadeIn { false };
bool _isFading { false };
bool _reconstructSoftEntitiesJointMap { false };
float _modelScale { 1.0f };

View file

@ -1707,6 +1707,7 @@ protected:
glm::vec3 _globalBoundingBoxOffset;
AABox _defaultBubbleBox;
AABox _fitBoundingBox;
mutable ReadWriteLockable _avatarEntitiesLock;
AvatarEntityIDs _avatarEntityRemoved; // recently removed AvatarEntity ids

View file

@ -439,7 +439,6 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo
}
auto removedAvatar = _avatarHash.take(sessionUUID);
if (removedAvatar) {
removedAvatars.push_back(removedAvatar);
}

View file

@ -26,11 +26,10 @@ QUrl getBakeableModelURL(const QUrl& url) {
GLTF_EXTENSION
};
QUrl cleanURL = url.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
QString cleanURLString = cleanURL.fileName();
QString filename = url.fileName();
for (auto& extension : extensionsToBake) {
if (cleanURLString.endsWith(extension, Qt::CaseInsensitive)) {
return cleanURL;
if (filename.endsWith(extension, Qt::CaseInsensitive)) {
return url;
}
}

View file

@ -221,17 +221,15 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
// remove all entities from the scene
auto scene = _viewState->getMain3DScene();
if (scene) {
render::Transaction transaction;
for (const auto& entry : _entitiesInScene) {
const auto& renderer = entry.second;
const EntityItemPointer& entityItem = renderer->getEntity();
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
renderer->removeFromScene(scene, transaction);
fadeOutRenderable(renderer);
} else {
savedEntities[entry.first] = entry.second;
}
}
scene->enqueueTransaction(transaction);
}
_renderablesToUpdate = savedEntities;
@ -258,12 +256,10 @@ void EntityTreeRenderer::clear() {
// remove all entities from the scene
auto scene = _viewState->getMain3DScene();
if (scene) {
render::Transaction transaction;
for (const auto& entry : _entitiesInScene) {
const auto& renderer = entry.second;
renderer->removeFromScene(scene, transaction);
fadeOutRenderable(renderer);
}
scene->enqueueTransaction(transaction);
} else {
qCWarning(entitiesrenderer) << "EntitityTreeRenderer::clear(), Unexpected null scene, possibly during application shutdown";
}
@ -1016,10 +1012,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities
// here's where we remove the entity payload from the scene
render::Transaction transaction;
renderable->removeFromScene(scene, transaction);
scene->enqueueTransaction(transaction);
fadeOutRenderable(renderable);
}
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
@ -1057,13 +1050,26 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
}
}
void EntityTreeRenderer::fadeOutRenderable(const EntityRendererPointer& renderable) {
render::Transaction transaction;
auto scene = _viewState->getMain3DScene();
transaction.transitionFinishedOperator(renderable->getRenderItemID(), [scene, renderable]() {
render::Transaction transaction;
renderable->removeFromScene(scene, transaction);
scene->enqueueTransaction(transaction);
});
scene->enqueueTransaction(transaction);
}
void EntityTreeRenderer::playEntityCollisionSound(const EntityItemPointer& entity, const Collision& collision) {
assert((bool)entity);
auto renderable = renderableForEntity(entity);
if (!renderable) {
return;
if (!renderable) {
return;
}
SharedSoundPointer collisionSound = renderable->getCollisionSound();
if (!collisionSound) {
return;

View file

@ -93,6 +93,8 @@ public:
/// reloads the entity scripts, calling unload and preload
void reloadEntityScripts();
void fadeOutRenderable(const EntityRendererPointer& renderable);
// event handles which may generate entity related events
QUuid mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
@ -255,6 +257,7 @@ private:
std::unordered_map<EntityItemID, EntityRendererPointer> _renderablesToUpdate;
std::unordered_map<EntityItemID, EntityRendererPointer> _entitiesInScene;
std::unordered_map<EntityItemID, EntityItemWeakPointer> _entitiesToAdd;
// For Scene.shouldRenderEntities
QList<EntityItemID> _entityIDsLastInScene;

View file

@ -148,7 +148,7 @@ EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entit
});
}
EntityRenderer::~EntityRenderer() { }
EntityRenderer::~EntityRenderer() {}
//
// Smart payload proxy members, implementing the payload interface
@ -421,6 +421,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
if (fading) {
_isFading = Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f;
}
_prevIsTransparent = transparent;
updateModelTransformAndBound();
@ -493,4 +494,4 @@ glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const Puls
}
return result;
}
}

View file

@ -1101,13 +1101,13 @@ void EntityScriptingInterface::handleEntityScriptCallMethodPacket(QSharedPointer
void EntityScriptingInterface::onAddingEntity(EntityItem* entity) {
if (entity->isWearable()) {
QMetaObject::invokeMethod(this, "addingWearable", Q_ARG(QUuid, entity->getEntityItemID()));
QMetaObject::invokeMethod(this, "addingWearable", Q_ARG(EntityItemID, entity->getEntityItemID()));
}
}
void EntityScriptingInterface::onDeletingEntity(EntityItem* entity) {
if (entity->isWearable()) {
QMetaObject::invokeMethod(this, "deletingWearable", Q_ARG(QUuid, entity->getEntityItemID()));
QMetaObject::invokeMethod(this, "deletingWearable", Q_ARG(EntityItemID, entity->getEntityItemID()));
}
}

View file

@ -71,12 +71,12 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b
doLogAction("makeUserConnection", payload);
}
void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) {
doLogAction(newValue ? "bubbleOn" : "bubbleOff");
void UserActivityLoggerScriptingInterface::privacyShieldToggled(bool newValue) {
doLogAction(newValue ? "privacyShieldOn" : "privacyShieldOff");
}
void UserActivityLoggerScriptingInterface::bubbleActivated() {
doLogAction("bubbleActivated");
void UserActivityLoggerScriptingInterface::privacyShieldActivated() {
doLogAction("privacyShieldActivated");
}
void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) {

View file

@ -30,8 +30,8 @@ public:
Q_INVOKABLE void palAction(QString action, QString target);
Q_INVOKABLE void palOpened(float secondsOpen);
Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = "");
Q_INVOKABLE void bubbleToggled(bool newValue);
Q_INVOKABLE void bubbleActivated();
Q_INVOKABLE void privacyShieldToggled(bool newValue);
Q_INVOKABLE void privacyShieldActivated();
Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem);
Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails);

View file

@ -38,10 +38,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SendMaxTranslationDimension);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXJointOrderChange);
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SendMaxTranslationDimension);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXJointOrderChange);
case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
// ICE packets

View file

@ -266,6 +266,7 @@ enum class EntityVersion : PacketVersion {
ModelScale,
ReOrderParentIDProperties,
CertificateTypeProperty,
DisableWebMedia,
// Add new versions above here
NUM_PACKET_TYPE,
@ -327,7 +328,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
CollisionFlag,
AvatarTraitsAck,
FasterAvatarEntities,
SendMaxTranslationDimension
SendMaxTranslationDimension,
FBXJointOrderChange
};
enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -47,6 +47,10 @@ void Transaction::queryTransitionOnItem(ItemID id, TransitionQueryFunc func) {
_queriedTransitions.emplace_back(id, func);
}
void Transaction::transitionFinishedOperator(ItemID id, TransitionFinishedFunc func) {
_transitionFinishedOperators.emplace_back(id, func);
}
void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) {
_updatedItems.emplace_back(id, functor);
}
@ -75,6 +79,7 @@ void Transaction::reserve(const std::vector<Transaction>& transactionContainer)
size_t addedTransitionsCount = 0;
size_t queriedTransitionsCount = 0;
size_t reAppliedTransitionsCount = 0;
size_t transitionFinishedOperatorsCount = 0;
size_t highlightResetsCount = 0;
size_t highlightRemovesCount = 0;
size_t highlightQueriesCount = 0;
@ -85,6 +90,7 @@ void Transaction::reserve(const std::vector<Transaction>& transactionContainer)
updatedItemsCount += transaction._updatedItems.size();
resetSelectionsCount += transaction._resetSelections.size();
addedTransitionsCount += transaction._addedTransitions.size();
transitionFinishedOperatorsCount += transaction._transitionFinishedOperators.size();
queriedTransitionsCount += transaction._queriedTransitions.size();
reAppliedTransitionsCount += transaction._reAppliedTransitions.size();
highlightResetsCount += transaction._highlightResets.size();
@ -99,6 +105,7 @@ void Transaction::reserve(const std::vector<Transaction>& transactionContainer)
_addedTransitions.reserve(addedTransitionsCount);
_queriedTransitions.reserve(queriedTransitionsCount);
_reAppliedTransitions.reserve(reAppliedTransitionsCount);
_transitionFinishedOperators.reserve(transitionFinishedOperatorsCount);
_highlightResets.reserve(highlightResetsCount);
_highlightRemoves.reserve(highlightRemovesCount);
_highlightQueries.reserve(highlightQueriesCount);
@ -142,6 +149,7 @@ void Transaction::merge(Transaction&& transaction) {
moveElements(_resetSelections, transaction._resetSelections);
moveElements(_addedTransitions, transaction._addedTransitions);
moveElements(_queriedTransitions, transaction._queriedTransitions);
moveElements(_transitionFinishedOperators, transaction._transitionFinishedOperators);
moveElements(_reAppliedTransitions, transaction._reAppliedTransitions);
moveElements(_highlightResets, transaction._highlightResets);
moveElements(_highlightRemoves, transaction._highlightRemoves);
@ -156,6 +164,7 @@ void Transaction::merge(const Transaction& transaction) {
copyElements(_addedTransitions, transaction._addedTransitions);
copyElements(_queriedTransitions, transaction._queriedTransitions);
copyElements(_reAppliedTransitions, transaction._reAppliedTransitions);
copyElements(_transitionFinishedOperators, transaction._transitionFinishedOperators);
copyElements(_highlightResets, transaction._highlightResets);
copyElements(_highlightRemoves, transaction._highlightRemoves);
copyElements(_highlightQueries, transaction._highlightQueries);
@ -168,6 +177,7 @@ void Transaction::clear() {
_resetSelections.clear();
_addedTransitions.clear();
_queriedTransitions.clear();
_transitionFinishedOperators.clear();
_reAppliedTransitions.clear();
_highlightResets.clear();
_highlightRemoves.clear();
@ -271,6 +281,7 @@ void Scene::processTransactionFrame(const Transaction& transaction) {
transitionItems(transaction._addedTransitions);
reApplyTransitions(transaction._reAppliedTransitions);
queryTransitionItems(transaction._queriedTransitions);
resetTransitionFinishedOperator(transaction._transitionFinishedOperators);
// Update the numItemsAtomic counter AFTER the pending changes went through
_numAllocatedItems.exchange(maxID);
@ -394,7 +405,7 @@ void Scene::transitionItems(const Transaction::TransitionAdds& transactions) {
// Only remove if:
// transitioning to something other than none or we're transitioning to none from ELEMENT_LEAVE_DOMAIN or USER_LEAVE_DOMAIN
const auto& oldTransitionType = transitionStage->getTransition(transitionId).eventType;
if (transitionType != Transition::NONE || !(oldTransitionType == Transition::ELEMENT_LEAVE_DOMAIN || oldTransitionType == Transition::USER_LEAVE_DOMAIN)) {
if (transitionType != oldTransitionType) {
resetItemTransition(itemId);
}
}
@ -440,6 +451,23 @@ void Scene::queryTransitionItems(const Transaction::TransitionQueries& transacti
}
}
void Scene::resetTransitionFinishedOperator(const Transaction::TransitionFinishedOperators& operators) {
for (auto& finishedOperator : operators) {
auto itemId = std::get<0>(finishedOperator);
const auto& item = _items[itemId];
auto func = std::get<1>(finishedOperator);
if (item.exist() && func != nullptr) {
TransitionStage::Index transitionId = item.getTransitionId();
if (!TransitionStage::isIndexInvalid(transitionId)) {
_transitionFinishedOperatorMap[transitionId].emplace_back(func);
} else if (func) {
func();
}
}
}
}
void Scene::resetHighlights(const Transaction::HighlightResets& transactions) {
auto outlineStage = getStage<HighlightStage>(HighlightStage::getName());
if (outlineStage) {
@ -526,9 +554,18 @@ void Scene::setItemTransition(ItemID itemId, Index transitionId) {
void Scene::resetItemTransition(ItemID itemId) {
auto& item = _items[itemId];
if (!render::TransitionStage::isIndexInvalid(item.getTransitionId())) {
TransitionStage::Index transitionId = item.getTransitionId();
if (!render::TransitionStage::isIndexInvalid(transitionId)) {
auto transitionStage = getStage<TransitionStage>(TransitionStage::getName());
transitionStage->removeTransition(item.getTransitionId());
auto finishedOperators = _transitionFinishedOperatorMap[transitionId];
for (auto finishedOperator : finishedOperators) {
if (finishedOperator) {
finishedOperator();
}
}
_transitionFinishedOperatorMap.erase(transitionId);
transitionStage->removeTransition(transitionId);
setItemTransition(itemId, render::TransitionStage::INVALID_INDEX);
}
}
@ -587,4 +624,4 @@ void Scene::resetStage(const Stage::Name& name, const StagePointer& stage) {
} else {
(*found).second = stage;
}
}
}

View file

@ -32,12 +32,15 @@ class Scene;
// These changes must be expressed through the corresponding command from the Transaction
// THe Transaction is then queued on the Scene so all the pending transactions can be consolidated and processed at the time
// of updating the scene before it s rendered.
//
//
class Transaction {
friend class Scene;
public:
typedef std::function<void(ItemID, const Transition*)> TransitionQueryFunc;
typedef std::function<void()> TransitionFinishedFunc;
typedef std::function<void(HighlightStyle const*)> SelectionHighlightQueryFunc;
Transaction() {}
@ -52,6 +55,7 @@ public:
void removeTransitionFromItem(ItemID id);
void reApplyTransitionToItem(ItemID id);
void queryTransitionOnItem(ItemID id, TransitionQueryFunc func);
void transitionFinishedOperator(ItemID id, TransitionFinishedFunc func);
template <class T> void updateItem(ItemID id, std::function<void(T&)> func) {
updateItem(id, std::make_shared<UpdateFunctor<T>>(func));
@ -84,6 +88,7 @@ protected:
using Update = std::tuple<ItemID, UpdateFunctorPointer>;
using TransitionAdd = std::tuple<ItemID, Transition::Type, ItemID>;
using TransitionQuery = std::tuple<ItemID, TransitionQueryFunc>;
using TransitionFinishedOperator = std::tuple<ItemID, TransitionFinishedFunc>;
using TransitionReApply = ItemID;
using SelectionReset = Selection;
using HighlightReset = std::tuple<std::string, HighlightStyle>;
@ -95,6 +100,7 @@ protected:
using Updates = std::vector<Update>;
using TransitionAdds = std::vector<TransitionAdd>;
using TransitionQueries = std::vector<TransitionQuery>;
using TransitionFinishedOperators = std::vector<TransitionFinishedOperator>;
using TransitionReApplies = std::vector<TransitionReApply>;
using SelectionResets = std::vector<SelectionReset>;
using HighlightResets = std::vector<HighlightReset>;
@ -107,6 +113,7 @@ protected:
TransitionAdds _addedTransitions;
TransitionQueries _queriedTransitions;
TransitionReApplies _reAppliedTransitions;
TransitionFinishedOperators _transitionFinishedOperators;
SelectionResets _resetSelections;
HighlightResets _highlightResets;
HighlightRemoves _highlightRemoves;
@ -208,6 +215,7 @@ protected:
ItemIDSet _masterNonspatialSet;
void resetItems(const Transaction::Resets& transactions);
void resetTransitionFinishedOperator(const Transaction::TransitionFinishedOperators& transactions);
void removeItems(const Transaction::Removes& transactions);
void updateItems(const Transaction::Updates& transactions);
void transitionItems(const Transaction::TransitionAdds& transactions);
@ -223,6 +231,8 @@ protected:
mutable std::mutex _selectionsMutex; // mutable so it can be used in the thread safe getSelection const method
SelectionMap _selections;
std::unordered_map<int32_t, std::vector<Transaction::TransitionFinishedFunc>> _transitionFinishedOperatorMap;
void resetSelections(const Transaction::SelectionResets& transactions);
// More actions coming to selections soon:
// void removeFromSelection(const Selection& selection);

View file

@ -50,4 +50,4 @@ namespace render {
typedef std::vector<Transition::Type> TransitionTypes;
}
#endif // hifi_render_Transition_h
#endif // hifi_render_Transition_h

View file

@ -0,0 +1,138 @@
"use strict";
(function(){
var AppUi = Script.require("appUi");
var ui;
var onCreateAvatarInputsBarEntity = false;
var micBarEntity = null;
var bubbleIconEntity = null;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var AVATAR_INPUTS_EDIT_QML_SOURCE = "hifi/EditAvatarInputsBar.qml";
// DPI
var ENTITY_DPI = 60.0;
// QML NATURAL DIMENSIONS
var MIC_BAR_DIMENSIONS = Vec3.multiply(30.0 / ENTITY_DPI, {x: 0.036, y: 0.048, z: 0.3});
var BUBBLE_ICON_DIMENSIONS = Vec3.multiply(30.0 / ENTITY_DPI, {x: 0.036, y: 0.036, z: 0.3});
// ENTITY NAMES
var MIC_BAR_NAME = "AvatarInputsMicBarEntity";
var BUBBLE_ICON_NAME = "AvatarInputsBubbleIconEntity";
// CONSTANTS
var LOCAL_POSITION_X_OFFSET = -0.2;
var LOCAL_POSITION_Y_OFFSET = -0.125;
var LOCAL_POSITION_Z_OFFSET = -0.5;
function fromQml(message) {
if (message.method === "reposition") {
var micBarLocalPosition = Entities.getEntityProperties(micBarEntity).localPosition;
var bubbleIconLocalPosition = Entities.getEntityProperties(bubbleIconEntity).localPosition;
var newMicBarLocalPosition, newBubbleIconLocalPosition;
if (message.x !== undefined) {
newMicBarLocalPosition = { x: -((MIC_BAR_DIMENSIONS.x) / 2) + message.x, y: micBarLocalPosition.y, z: micBarLocalPosition.z };
newBubbleIconLocalPosition = { x: ((MIC_BAR_DIMENSIONS.x) * 1.2 / 2) + message.x, y: bubbleIconLocalPosition.y, z: bubbleIconLocalPosition.z };
} else if (message.y !== undefined) {
newMicBarLocalPosition = { x: micBarLocalPosition.x, y: message.y, z: micBarLocalPosition.z };
newBubbleIconLocalPosition = { x: bubbleIconLocalPosition.x, y: ((MIC_BAR_DIMENSIONS.y - BUBBLE_ICON_DIMENSIONS.y) / 2 + message.y), z: bubbleIconLocalPosition.z };
} else if (message.z !== undefined) {
newMicBarLocalPosition = { x: micBarLocalPosition.x, y: micBarLocalPosition.y, z: message.z };
newBubbleIconLocalPosition = { x: bubbleIconLocalPosition.x, y: bubbleIconLocalPosition.y, z: message.z };
}
var micBarProps = {
localPosition: newMicBarLocalPosition
};
var bubbleIconProps = {
localPosition: newBubbleIconLocalPosition
};
Entities.editEntity(micBarEntity, micBarProps);
Entities.editEntity(bubbleIconEntity, bubbleIconProps);
} else if (message.method === "setVisible") {
if (message.visible !== undefined) {
var props = {
visible: message.visible
};
Entities.editEntity(micBarEntity, props);
Entities.editEntity(bubbleIconEntity, props);
}
} else if (message.method === "print") {
// prints the local position into the hifi log.
var micBarLocalPosition = Entities.getEntityProperties(micBarEntity).localPosition;
var bubbleIconLocalPosition = Entities.getEntityProperties(bubbleIconEntity).localPosition;
console.log("mic bar local position is at " + JSON.stringify(micBarLocalPosition));
console.log("bubble icon local position is at " + JSON.stringify(bubbleIconLocalPosition));
}
};
function createEntities() {
if (micBarEntity != null && bubbleIconEntity != null) {
return;
}
// POSITIONS
var micBarLocalPosition = {x: (-(MIC_BAR_DIMENSIONS.x / 2)) + LOCAL_POSITION_X_OFFSET, y: LOCAL_POSITION_Y_OFFSET, z: LOCAL_POSITION_Z_OFFSET};
var bubbleIconLocalPosition = {x: (MIC_BAR_DIMENSIONS.x * 1.2 / 2) + LOCAL_POSITION_X_OFFSET, y: ((MIC_BAR_DIMENSIONS.y - BUBBLE_ICON_DIMENSIONS.y) / 2 + LOCAL_POSITION_Y_OFFSET), z: LOCAL_POSITION_Z_OFFSET};
var props = {
type: "Web",
name: MIC_BAR_NAME,
parentID: MyAvatar.SELF_ID,
parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"),
localPosition: micBarLocalPosition,
localRotation: Quat.cancelOutRollAndPitch(Quat.lookAtSimple(Camera.orientation, micBarLocalPosition)),
sourceUrl: Script.resourcesPath() + "qml/hifi/audio/MicBarApplication.qml",
// cutoff alpha for detecting transparency
alpha: 0.98,
dimensions: MIC_BAR_DIMENSIONS,
dpi: ENTITY_DPI,
drawInFront: true,
userData: {
grabbable: false
},
};
micBarEntity = Entities.addEntity(props, "local");
var props = {
type: "Web",
name: BUBBLE_ICON_NAME,
parentID: MyAvatar.SELF_ID,
parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"),
localPosition: bubbleIconLocalPosition,
localRotation: Quat.cancelOutRollAndPitch(Quat.lookAtSimple(Camera.orientation, bubbleIconLocalPosition)),
sourceUrl: Script.resourcesPath() + "qml/BubbleIcon.qml",
// cutoff alpha for detecting transparency
alpha: 0.98,
dimensions: BUBBLE_ICON_DIMENSIONS,
dpi: ENTITY_DPI,
drawInFront: true,
userData: {
grabbable: false
},
};
bubbleIconEntity = Entities.addEntity(props, "local");
tablet.loadQMLSource(AVATAR_INPUTS_EDIT_QML_SOURCE);
};
function cleanup() {
if (micBarEntity) {
Entities.deleteEntity(micBarEntity);
}
if (bubbleIconEntity) {
Entities.deleteEntity(bubbleIconEntity);
}
};
function setup() {
ui = new AppUi({
buttonName: "AVBAR",
home: Script.resourcesPath() + "qml/hifi/EditAvatarInputsBar.qml",
onMessage: fromQml,
onOpened: createEntities,
// onClosed: cleanup,
normalButton: "icons/tablet-icons/edit-i.svg",
activeButton: "icons/tablet-icons/edit-a.svg",
});
};
setup();
Script.scriptEnding.connect(cleanup);
}());

View file

@ -75,6 +75,7 @@ button.clicked.connect(onClicked);
tablet.screenChanged.connect(onScreenChanged);
Audio.mutedChanged.connect(onMuteToggled);
Audio.pushToTalkChanged.connect(onMuteToggled);
HMD.displayModeChanged.connect(onMuteToggled);
Script.scriptEnding.connect(function () {
if (onAudioScreen) {
@ -84,6 +85,7 @@ Script.scriptEnding.connect(function () {
tablet.screenChanged.disconnect(onScreenChanged);
Audio.mutedChanged.disconnect(onMuteToggled);
Audio.pushToTalkChanged.disconnect(onMuteToggled);
HMD.displayModeChanged.disconnect(onMuteToggled);
tablet.removeButton(button);
});

View file

@ -43,7 +43,7 @@
if (HMD.active) {
warningOverlayID = Overlays.addOverlay("text3d", {
name: "Muted-Warning",
localPosition: { x: 0.0, y: -0.5, z: -1.0 },
localPosition: { x: 0.0, y: -0.45, z: -1.0 },
localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 0.0, z: 0.0, w: 1.0 }),
text: warningText,
textAlpha: 1,
@ -58,20 +58,6 @@
parentID: MyAvatar.SELF_ID,
parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX")
});
} else {
var textDimensions = { x: 100, y: 50 };
warningOverlayID = Overlays.addOverlay("text", {
name: "Muted-Warning",
font: { size: 36 },
text: warningText,
x: (Window.innerWidth - textDimensions.x) / 2,
y: (Window.innerHeight - textDimensions.y),
width: textDimensions.x,
height: textDimensions.y,
textColor: { red: 226, green: 51, blue: 77 },
backgroundAlpha: 0,
visible: true
});
}
}
@ -141,4 +127,4 @@
Audio.mutedChanged.connect(startOrStopPoll);
Audio.warnWhenMutedChanged.connect(startOrStopPoll);
}()); // END LOCAL_SCOPE
}()); // END LOCAL_SCOPE

View file

@ -171,7 +171,7 @@ function goAway(fromStartup) {
if (!previousBubbleState) {
Users.toggleIgnoreRadius();
}
UserActivityLogger.bubbleToggled(Users.getIgnoreRadiusEnabled());
UserActivityLogger.privacyShieldToggled(Users.getIgnoreRadiusEnabled());
UserActivityLogger.toggledAway(true);
MyAvatar.isAway = true;
}
@ -186,7 +186,7 @@ function goActive() {
if (Users.getIgnoreRadiusEnabled() !== previousBubbleState) {
Users.toggleIgnoreRadius();
UserActivityLogger.bubbleToggled(Users.getIgnoreRadiusEnabled());
UserActivityLogger.privacyShieldToggled(Users.getIgnoreRadiusEnabled());
}
if (!Window.hasFocus()) {

View file

@ -90,7 +90,7 @@
// Called from the C++ scripting interface to show the bubble overlay
function enteredIgnoreRadius() {
createOverlays();
UserActivityLogger.bubbleActivated();
UserActivityLogger.privacyShieldActivated();
}
// Used to set the state of the bubble HUD button
@ -160,7 +160,7 @@
function onBubbleToggled(enabled, doNotLog) {
writeButtonProperties(enabled);
if (doNotLog !== true) {
UserActivityLogger.bubbleToggled(enabled);
UserActivityLogger.privacyShieldToggled(enabled);
}
if (enabled) {
createOverlays();
@ -174,7 +174,7 @@
}
// Setup the bubble button
var buttonName = "BUBBLE";
var buttonName = "SHIELD";
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
button = tablet.addButton({
icon: "icons/tablet-icons/bubble-i.svg",

View file

@ -282,6 +282,28 @@ function checkEditPermissionsAndUpdate() {
}
}
// Copies the properties in `b` into `a`. `a` will be modified.
function copyProperties(a, b) {
for (var key in b) {
a[key] = b[key];
}
return a;
}
const DEFAULT_DYNAMIC_PROPERTIES = {
dynamic: true,
damping: 0.39347,
angularDamping: 0.39347,
gravity: { x: 0, y: -9.8, z: 0 },
};
const DEFAULT_NON_DYNAMIC_PROPERTIES = {
dynamic: false,
damping: 0,
angularDamping: 0,
gravity: { x: 0, y: 0, z: 0 },
};
const DEFAULT_ENTITY_PROPERTIES = {
All: {
description: "",
@ -299,26 +321,14 @@ const DEFAULT_ENTITY_PROPERTIES = {
y: 0,
z: 0
},
damping: 0,
angularVelocity: {
x: 0,
y: 0,
z: 0
},
angularDamping: 0,
restitution: 0.5,
friction: 0.5,
density: 1000,
gravity: {
x: 0,
y: 0,
z: 0
},
acceleration: {
x: 0,
y: 0,
z: 0
},
dynamic: false,
},
Shape: {
@ -484,11 +494,6 @@ var toolBar = (function () {
dialogWindow = null,
tablet = null;
function applyProperties(originalProperties, newProperties) {
for (var key in newProperties) {
originalProperties[key] = newProperties[key];
}
}
function createNewEntity(requestedProperties) {
var dimensions = requestedProperties.dimensions ? requestedProperties.dimensions : DEFAULT_DIMENSIONS;
var position = getPositionToCreateEntity();
@ -496,17 +501,23 @@ var toolBar = (function () {
var properties = {};
applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.All);
copyProperties(properties, DEFAULT_ENTITY_PROPERTIES.All);
var type = requestedProperties.type;
if (type === "Box" || type === "Sphere") {
applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape);
copyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape);
} else {
applyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]);
copyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]);
}
// We apply the requested properties first so that they take priority over any default properties.
applyProperties(properties, requestedProperties);
copyProperties(properties, requestedProperties);
if (properties.dynamic) {
copyProperties(properties, DEFAULT_DYNAMIC_PROPERTIES);
} else {
copyProperties(properties, DEFAULT_NON_DYNAMIC_PROPERTIES);
}
if (position !== null && position !== undefined) {
@ -675,7 +686,6 @@ var toolBar = (function () {
grabbable: result.grabbable
},
dynamic: dynamic,
gravity: dynamic ? { x: 0, y: -10, z: 0 } : { x: 0, y: 0, z: 0 }
});
}
}

View file

@ -17,9 +17,10 @@ using System.Text.RegularExpressions;
class AvatarExporter : MonoBehaviour {
// update version number for every PR that changes this file, also set updated version in README file
static readonly string AVATAR_EXPORTER_VERSION = "0.4.0";
static readonly string AVATAR_EXPORTER_VERSION = "0.4.1";
static readonly float HIPS_GROUND_MIN_Y = 0.01f;
static readonly float HIPS_MIN_Y_PERCENT_OF_HEIGHT = 0.03f;
static readonly float BELOW_GROUND_THRESHOLD_PERCENT_OF_HEIGHT = -0.15f;
static readonly float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f;
static readonly int MAXIMUM_USER_BONE_COUNT = 256;
static readonly string EMPTY_WARNING_TEXT = "None";
@ -231,7 +232,8 @@ class AvatarExporter : MonoBehaviour {
HeadMapped,
HeadDescendantOfChest,
EyesMapped,
HipsNotOnGround,
HipsNotAtBottom,
ExtentsNotBelowGround,
HipsSpineChestNotCoincident,
TotalBoneCountUnderLimit,
AvatarRuleEnd,
@ -247,18 +249,26 @@ class AvatarExporter : MonoBehaviour {
class UserBoneInformation {
public string humanName; // bone name in Humanoid if it is mapped, otherwise ""
public string parentName; // parent user bone name
public BoneTreeNode boneTreeNode; // node within the user bone tree
public int mappingCount; // number of times this bone is mapped in Humanoid
public Vector3 position; // absolute position
public Quaternion rotation; // absolute rotation
public BoneTreeNode boneTreeNode;
public UserBoneInformation() {
humanName = "";
parentName = "";
boneTreeNode = new BoneTreeNode();
mappingCount = 0;
position = new Vector3();
rotation = new Quaternion();
boneTreeNode = new BoneTreeNode();
}
public UserBoneInformation(string parent, BoneTreeNode treeNode, Vector3 pos) {
humanName = "";
parentName = parent;
boneTreeNode = treeNode;
mappingCount = 0;
position = pos;
rotation = new Quaternion();
}
public bool HasHumanMapping() { return !string.IsNullOrEmpty(humanName); }
@ -266,11 +276,13 @@ class AvatarExporter : MonoBehaviour {
class BoneTreeNode {
public string boneName;
public string parentName;
public List<BoneTreeNode> children = new List<BoneTreeNode>();
public BoneTreeNode() {}
public BoneTreeNode(string name) {
public BoneTreeNode(string name, string parent) {
boneName = name;
parentName = parent;
}
}
@ -732,9 +744,11 @@ class AvatarExporter : MonoBehaviour {
// instantiate a game object of the user avatar to traverse the bone tree to gather
// bone parents and positions as well as build a bone tree, then destroy it
GameObject assetGameObject = (GameObject)Instantiate(avatarResource);
TraverseUserBoneTree(assetGameObject.transform);
DestroyImmediate(assetGameObject);
GameObject avatarGameObject = (GameObject)Instantiate(avatarResource, Vector3.zero, Quaternion.identity);
TraverseUserBoneTree(avatarGameObject.transform, userBoneTree);
Bounds bounds = AvatarUtilities.GetAvatarBounds(avatarGameObject);
float height = AvatarUtilities.GetAvatarHeight(avatarGameObject);
DestroyImmediate(avatarGameObject);
// iterate over Humanoid bones and update user bone info to increase human mapping counts for each bone
// as well as set their Humanoid name and build a Humanoid to user bone mapping
@ -753,10 +767,10 @@ class AvatarExporter : MonoBehaviour {
}
// generate the list of avatar rule failure strings for any avatar rules that are not satisfied by this avatar
SetFailedAvatarRules();
SetFailedAvatarRules(bounds, height);
}
static void TraverseUserBoneTree(Transform modelBone) {
static void TraverseUserBoneTree(Transform modelBone, BoneTreeNode boneTreeNode) {
GameObject gameObject = modelBone.gameObject;
// check if this transform is a node containing mesh, light, or camera instead of a bone
@ -770,33 +784,52 @@ class AvatarExporter : MonoBehaviour {
if (mesh) {
Material[] materials = skinnedMeshRenderer != null ? skinnedMeshRenderer.sharedMaterials : meshRenderer.sharedMaterials;
StoreMaterialData(materials);
// ensure branches within the transform hierarchy that contain meshes are removed from the user bone tree
Transform ancestorBone = modelBone;
string previousBoneName = "";
// find the name of the root child bone that this mesh is underneath
while (ancestorBone != null) {
if (ancestorBone.parent == null) {
break;
}
previousBoneName = ancestorBone.name;
ancestorBone = ancestorBone.parent;
}
// remove the bone tree node from root's children for the root child bone that has mesh children
if (!string.IsNullOrEmpty(previousBoneName)) {
foreach (BoneTreeNode rootChild in userBoneTree.children) {
if (rootChild.boneName == previousBoneName) {
userBoneTree.children.Remove(rootChild);
break;
}
}
}
} else if (!light && !camera) {
// if it is in fact a bone, add it to the bone tree as well as user bone infos list with position and parent name
UserBoneInformation userBoneInfo = new UserBoneInformation();
userBoneInfo.position = modelBone.position; // bone's absolute position
string boneName = modelBone.name;
if (modelBone.parent == null) {
// if no parent then this is actual root bone node of the user avatar, so consider it's parent as "root"
boneName = GetRootBoneName(); // ensure we use the root bone name from the skeleton list for consistency
userBoneTree = new BoneTreeNode(boneName); // initialize root of tree
userBoneInfo.parentName = "root";
userBoneInfo.boneTreeNode = userBoneTree;
boneTreeNode.boneName = boneName;
boneTreeNode.parentName = "root";
} else {
// otherwise add this bone node as a child to it's parent's children list
// if its a child of the root bone, use the root bone name from the skeleton list as the parent for consistency
string parentName = modelBone.parent.parent == null ? GetRootBoneName() : modelBone.parent.name;
BoneTreeNode boneTreeNode = new BoneTreeNode(boneName);
userBoneInfos[parentName].boneTreeNode.children.Add(boneTreeNode);
userBoneInfo.parentName = parentName;
BoneTreeNode node = new BoneTreeNode(boneName, parentName);
boneTreeNode.children.Add(node);
boneTreeNode = node;
}
Vector3 bonePosition = modelBone.position; // bone's absolute position in avatar space
UserBoneInformation userBoneInfo = new UserBoneInformation(boneTreeNode.parentName, boneTreeNode, bonePosition);
userBoneInfos.Add(boneName, userBoneInfo);
}
// recurse over transform node's children
for (int i = 0; i < modelBone.childCount; ++i) {
TraverseUserBoneTree(modelBone.GetChild(i));
TraverseUserBoneTree(modelBone.GetChild(i), boneTreeNode);
}
}
@ -840,7 +873,7 @@ class AvatarExporter : MonoBehaviour {
return "";
}
static void SetFailedAvatarRules() {
static void SetFailedAvatarRules(Bounds avatarBounds, float avatarHeight) {
failedAvatarRules.Clear();
string hipsUserBone = "";
@ -905,18 +938,29 @@ class AvatarExporter : MonoBehaviour {
break;
case AvatarRule.ChestMapped:
if (!humanoidToUserBoneMappings.TryGetValue("Chest", out chestUserBone)) {
// check to see if there is a child of Spine that we can suggest to be mapped to Chest
string spineChild = "";
// check to see if there is an unmapped child of Spine that we can suggest to be mapped to Chest
string chestMappingCandidate = "";
if (!string.IsNullOrEmpty(spineUserBone)) {
BoneTreeNode spineTreeNode = userBoneInfos[spineUserBone].boneTreeNode;
if (spineTreeNode.children.Count == 1) {
spineChild = spineTreeNode.children[0].boneName;
foreach (BoneTreeNode spineChildTreeNode in spineTreeNode.children) {
string spineChildBone = spineChildTreeNode.boneName;
if (userBoneInfos[spineChildBone].HasHumanMapping()) {
continue;
}
// a suitable candidate for Chest should have Neck/Head or Shoulder mappings in its descendants
if (IsHumanBoneInHierarchy(spineChildTreeNode, "Neck") ||
IsHumanBoneInHierarchy(spineChildTreeNode, "Head") ||
IsHumanBoneInHierarchy(spineChildTreeNode, "LeftShoulder") ||
IsHumanBoneInHierarchy(spineChildTreeNode, "RightShoulder")) {
chestMappingCandidate = spineChildBone;
break;
}
}
}
failedAvatarRules.Add(avatarRule, "There is no Chest bone mapped in Humanoid for the selected avatar.");
// if the only found child of Spine is not yet mapped then add it as a suggestion for Chest mapping
if (!string.IsNullOrEmpty(spineChild) && !userBoneInfos[spineChild].HasHumanMapping()) {
failedAvatarRules[avatarRule] += " It is suggested that you map bone " + spineChild +
if (!string.IsNullOrEmpty(chestMappingCandidate)) {
failedAvatarRules[avatarRule] += " It is suggested that you map bone " + chestMappingCandidate +
" to Chest in Humanoid.";
}
}
@ -949,15 +993,34 @@ class AvatarExporter : MonoBehaviour {
}
}
break;
case AvatarRule.HipsNotOnGround:
// ensure the absolute Y position for the bone mapped to Hips (if its mapped) is at least HIPS_GROUND_MIN_Y
case AvatarRule.HipsNotAtBottom:
// ensure that Hips is not below a proportional percentage of the avatar's height in avatar space
if (!string.IsNullOrEmpty(hipsUserBone)) {
UserBoneInformation hipsBoneInfo = userBoneInfos[hipsUserBone];
hipsPosition = hipsBoneInfo.position;
if (hipsPosition.y < HIPS_GROUND_MIN_Y) {
failedAvatarRules.Add(avatarRule, "The bone mapped to Hips in Humanoid (" + hipsUserBone +
") should not be at ground level.");
// find the lowest y position of the bones
float minBoneYPosition = float.MaxValue;
foreach (var userBoneInfo in userBoneInfos) {
Vector3 position = userBoneInfo.Value.position;
if (position.y < minBoneYPosition) {
minBoneYPosition = position.y;
}
}
// check that Hips is within a percentage of avatar's height from the lowest Y point of the avatar
float bottomYRange = HIPS_MIN_Y_PERCENT_OF_HEIGHT * avatarHeight;
if (Mathf.Abs(hipsPosition.y - minBoneYPosition) < bottomYRange) {
failedAvatarRules.Add(avatarRule, "The bone mapped to Hips in Humanoid (" + hipsUserBone +
") should not be at the bottom of the selected avatar.");
}
}
break;
case AvatarRule.ExtentsNotBelowGround:
// ensure the minimum Y extent of the model's bounds is not below a proportional threshold of avatar's height
float belowGroundThreshold = BELOW_GROUND_THRESHOLD_PERCENT_OF_HEIGHT * avatarHeight;
if (avatarBounds.min.y < belowGroundThreshold) {
failedAvatarRules.Add(avatarRule, "The bottom extents of the selected avatar go below ground level.");
}
break;
case AvatarRule.HipsSpineChestNotCoincident:
@ -989,6 +1052,23 @@ class AvatarExporter : MonoBehaviour {
}
}
static bool IsHumanBoneInHierarchy(BoneTreeNode boneTreeNode, string humanBoneName) {
UserBoneInformation userBoneInfo;
if (userBoneInfos.TryGetValue(boneTreeNode.boneName, out userBoneInfo) && userBoneInfo.humanName == humanBoneName) {
// this bone matches the human bone name being searched for
return true;
}
// recursively check downward through children bones for target human bone
foreach (BoneTreeNode childNode in boneTreeNode.children) {
if (IsHumanBoneInHierarchy(childNode, humanBoneName)) {
return true;
}
}
return false;
}
static string CheckHumanBoneMappingRule(AvatarRule avatarRule, string humanBoneName) {
string userBoneName = "";
// avatar rule fails if bone is not mapped in Humanoid
@ -999,8 +1079,8 @@ class AvatarExporter : MonoBehaviour {
return userBoneName;
}
static void CheckUserBoneDescendantOfHumanRule(AvatarRule avatarRule, string userBoneName, string descendantOfHumanName) {
if (string.IsNullOrEmpty(userBoneName)) {
static void CheckUserBoneDescendantOfHumanRule(AvatarRule avatarRule, string descendantUserBoneName, string descendantOfHumanName) {
if (string.IsNullOrEmpty(descendantUserBoneName)) {
return;
}
@ -1009,27 +1089,26 @@ class AvatarExporter : MonoBehaviour {
return;
}
string userBone = userBoneName;
string ancestorUserBone = "";
UserBoneInformation userBoneInfo = new UserBoneInformation();
string userBoneName = descendantUserBoneName;
UserBoneInformation userBoneInfo = userBoneInfos[userBoneName];
string descendantHumanName = userBoneInfo.humanName;
// iterate upward from user bone through user bone info parent names until root
// is reached or the ancestor bone name matches the target descendant of name
while (ancestorUserBone != "root") {
if (userBoneInfos.TryGetValue(userBone, out userBoneInfo)) {
ancestorUserBone = userBoneInfo.parentName;
if (ancestorUserBone == descendantOfUserBoneName) {
return;
}
userBone = ancestorUserBone;
while (userBoneName != "root") {
if (userBoneName == descendantOfUserBoneName) {
return;
}
if (userBoneInfos.TryGetValue(userBoneName, out userBoneInfo)) {
userBoneName = userBoneInfo.parentName;
} else {
break;
}
}
// avatar rule fails if no ancestor of given user bone matched the descendant of name (no early return)
failedAvatarRules.Add(avatarRule, "The bone mapped to " + userBoneInfo.humanName + " in Humanoid (" + userBoneName +
") is not a child of the bone mapped to " + descendantOfHumanName + " in Humanoid (" +
descendantOfUserBoneName + ").");
failedAvatarRules.Add(avatarRule, "The bone mapped to " + descendantHumanName + " in Humanoid (" +
descendantUserBoneName + ") is not a descendant of the bone mapped to " +
descendantOfHumanName + " in Humanoid (" + descendantOfUserBoneName + ").");
}
static void CheckAsymmetricalMappingRule(AvatarRule avatarRule, string[] mappingSuffixes, string appendage) {
@ -1296,9 +1375,8 @@ class ExportProjectWindow : EditorWindow {
const float MAX_SCALE_SLIDER = 2.0f;
const int SLIDER_SCALE_EXPONENT = 10;
const float ACTUAL_SCALE_OFFSET = 1.0f;
const float DEFAULT_AVATAR_HEIGHT = 1.755f;
const float MAXIMUM_RECOMMENDED_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f;
const float MINIMUM_RECOMMENDED_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f;
const float MAXIMUM_RECOMMENDED_HEIGHT = AvatarUtilities.DEFAULT_AVATAR_HEIGHT * 1.5f;
const float MINIMUM_RECOMMENDED_HEIGHT = AvatarUtilities.DEFAULT_AVATAR_HEIGHT * 0.25f;
const float SLIDER_DIFFERENCE_REMOVE_TEXT = 0.01f;
readonly Color COLOR_YELLOW = Color.yellow; //new Color(0.9176f, 0.8274f, 0.0f);
readonly Color COLOR_BACKGROUND = new Color(0.5f, 0.5f, 0.5f);
@ -1339,9 +1417,9 @@ class ExportProjectWindow : EditorWindow {
ShowUtility();
// if the avatar's starting height is outside of the recommended ranges, auto-adjust the scale to default height
float height = GetAvatarHeight();
float height = AvatarUtilities.GetAvatarHeight(avatarPreviewObject);
if (height < MINIMUM_RECOMMENDED_HEIGHT || height > MAXIMUM_RECOMMENDED_HEIGHT) {
float newScale = DEFAULT_AVATAR_HEIGHT / height;
float newScale = AvatarUtilities.DEFAULT_AVATAR_HEIGHT / height;
SetAvatarScale(newScale);
scaleWarningText = "Avatar's scale automatically adjusted to be within the recommended range.";
}
@ -1524,7 +1602,7 @@ class ExportProjectWindow : EditorWindow {
void UpdateScaleWarning() {
// called on any scale changes
float height = GetAvatarHeight();
float height = AvatarUtilities.GetAvatarHeight(avatarPreviewObject);
if (height < MINIMUM_RECOMMENDED_HEIGHT) {
scaleWarningText = "The height of the avatar is below the recommended minimum.";
} else if (height > MAXIMUM_RECOMMENDED_HEIGHT) {
@ -1535,23 +1613,6 @@ class ExportProjectWindow : EditorWindow {
}
}
float GetAvatarHeight() {
// height of an avatar model can be determined to be the max Y extents of the combined bounds for all its mesh renderers
if (avatarPreviewObject != null) {
Bounds bounds = new Bounds();
var meshRenderers = avatarPreviewObject.GetComponentsInChildren<MeshRenderer>();
var skinnedMeshRenderers = avatarPreviewObject.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var renderer in meshRenderers) {
bounds.Encapsulate(renderer.bounds);
}
foreach (var renderer in skinnedMeshRenderers) {
bounds.Encapsulate(renderer.bounds);
}
return bounds.max.y;
}
return 0.0f;
}
void SetAvatarScale(float actualScale) {
// set the new scale uniformly on the preview avatar's transform to show the resulting avatar size
avatarPreviewObject.transform.localScale = new Vector3(actualScale, actualScale, actualScale);
@ -1571,3 +1632,28 @@ class ExportProjectWindow : EditorWindow {
onCloseCallback();
}
}
class AvatarUtilities {
public const float DEFAULT_AVATAR_HEIGHT = 1.755f;
public static Bounds GetAvatarBounds(GameObject avatarObject) {
Bounds bounds = new Bounds();
if (avatarObject != null) {
var meshRenderers = avatarObject.GetComponentsInChildren<MeshRenderer>();
var skinnedMeshRenderers = avatarObject.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var renderer in meshRenderers) {
bounds.Encapsulate(renderer.bounds);
}
foreach (var renderer in skinnedMeshRenderers) {
bounds.Encapsulate(renderer.bounds);
}
}
return bounds;
}
public static float GetAvatarHeight(GameObject avatarObject) {
// height of an avatar model can be determined to be the max Y extents of the combined bounds for all its mesh renderers
Bounds avatarBounds = GetAvatarBounds(avatarObject);
return avatarBounds.max.y - avatarBounds.min.y;
}
}

View file

@ -1,6 +1,6 @@
High Fidelity, Inc.
Avatar Exporter
Version 0.4.0
Version 0.4.1
Note: It is recommended to use Unity versions between 2017.4.15f1 and 2018.2.12f1 for this Avatar Exporter.