mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 07:22:19 +02:00
merge from master
This commit is contained in:
commit
4500ed17e7
174 changed files with 7706 additions and 5586 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -85,4 +85,7 @@ npm-debug.log
|
|||
android/app/src/main/assets
|
||||
|
||||
# Resource binary file
|
||||
interface/compiledResources
|
||||
interface/compiledResources
|
||||
|
||||
# GPUCache
|
||||
interface/resources/GPUCache/*
|
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME native-lib)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared networking gl gpu image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
|
||||
link_hifi_libraries(shared task networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
|
||||
target_opengl()
|
||||
target_bullet()
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
auto nodeID = node->getUUID();
|
||||
|
||||
// compute the node's max relative volume
|
||||
float nodeVolume;
|
||||
float nodeVolume = 0.0f;
|
||||
for (auto& streamPair : nodeData->getAudioStreams()) {
|
||||
auto nodeStream = streamPair.second;
|
||||
|
||||
|
@ -193,10 +193,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
}
|
||||
|
||||
// max-heapify the nodes by relative volume
|
||||
throttledNodes.push_back(std::make_pair(nodeVolume, node));
|
||||
if (!throttledNodes.empty()) {
|
||||
std::push_heap(throttledNodes.begin(), throttledNodes.end());
|
||||
}
|
||||
throttledNodes.push_back({ nodeVolume, node });
|
||||
std::push_heap(throttledNodes.begin(), throttledNodes.end());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME interface)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js)
|
||||
add_custom_target(qml SOURCES ${QML_SRC})
|
||||
add_custom_target(qmls SOURCES ${QML_SRC})
|
||||
GroupSources("resources/qml")
|
||||
|
||||
function(JOIN VALUES GLUE OUTPUT)
|
||||
|
@ -204,13 +204,14 @@ endif()
|
|||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(
|
||||
shared octree ktx gpu gl procedural graphics render
|
||||
shared task octree ktx gpu gl procedural graphics render
|
||||
pointers
|
||||
recording fbx networking model-networking entities avatars trackers
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer avatars-renderer ui auto-updater midi
|
||||
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
||||
controllers plugins image trackers
|
||||
ui-plugins display-plugins input-plugins
|
||||
workload
|
||||
${PLATFORM_GL_BACKEND}
|
||||
)
|
||||
|
||||
|
|
Binary file not shown.
BIN
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot
Normal file
BIN
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot
Normal file
Binary file not shown.
148
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.svg
Normal file
148
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.svg
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by Fontastic.me</metadata>
|
||||
<defs>
|
||||
<font id="hifi-glyphs" horiz-adv-x="512">
|
||||
<font-face font-family="hifi-glyphs" units-per-em="512" ascent="480" descent="-32"/>
|
||||
<missing-glyph horiz-adv-x="512" />
|
||||
|
||||
<glyph glyph-name="hmd" unicode="b" d="M381 139l-70 0c-18 0-30 17-40 33-4 6-11 16-15 18-4-2-10-12-15-18-11-15-23-33-43-33l-67 0c-53 0-97 42-97 95l0 45c0 53 44 96 97 96l250 0c53 0 96-43 96-96l0-45c0-52-43-95-96-95z m-125 77c16 0 26-15 36-30 5-7 15-22 19-22l70 0c39 0 71 32 71 70l0 45c0 39-32 70-71 70l-250 0c-39 0-71-31-71-70l0-45c0-38 32-70 71-70l67 0c6 0 16 14 22 23 10 14 20 29 35 29 1 0 1 0 1 0z"/>
|
||||
<glyph glyph-name="2d-screen" unicode="c" d="M395 386l-276 0c-33 0-60-28-60-61l0-116c0-33 27-62 60-62l127 0 0-27-80 0c-8 0-14-5-14-13 0-8 7-13 14-13l186 0c7 0 13 5 13 13 0 8-6 13-13 13l-81 0 0 27 124 0c33 0 60 29 60 62l0 116c0 33-27 61-60 61z m32-177c0-18-14-33-32-33l-134 0c-1 0-1 0-2 0-1 0-2 0-2 0l-138 0c-18 0-32 15-32 33l0 116c0 18 14 33 32 33l276 0c18 0 32-15 32-33z"/>
|
||||
<glyph glyph-name="keyboard" unicode="d" d="M375 250l-26 0 0 25 26 0z m-35 0l-27 0 0 25 27 0z m-36 0l-26 0 0 25 26 0z m-36 0l-26 0 0 25 26 0z m-35 0l-27 0 0 25 27 0z m-36 0l-26 0 0 25 26 0z m-36 0l-26 0 0 25 26 0z m225-1l17 0c7 0 13 6 13 13 0 8-6 14-13 14l-18 0m-262 0l-17 0c-7 0-13-6-13-14 0-7 6-13 13-13l18 0m251 39l-31 0 0 25 31 0z m-41 0l-32 0 0 25 32 0z m-42 0l-32 0 0 25 32 0z m-42 0l-32 0 0 25 32 0z m-42 0l-31 0 0 25 31 0z m-41 0l-32 0 0 25 32 0z m219 0l17 0c7 0 13 6 13 13 0 7-6 13-13 13l-18 0m-262 0l-17 0c-7 0-13-6-13-13 0-7 6-13 13-13l18 0m287-124l-315 0c-32 0-59 27-59 60l0 77c0 33 27 60 59 60l315 0c33 0 60-27 60-60l0-77c0-33-27-60-60-60z m-315 171c-18 0-33-15-33-34l0-77c0-19 15-34 33-34l315 0c19 0 34 16 34 34l0 77c0 19-15 34-34 34z m249-99l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m251-25l17 0c7 0 13 6 13 13 0 7-6 13-13 13l-18 0m-262 0l-17 0c-7 0-13-6-13-13 0-7 6-13 13-13l18 0"/>
|
||||
<glyph glyph-name="hand-controllers" unicode="e" d="M141 268l-3 0c-7 0-13 5-13 13 0 7 6 13 13 13l3 0c7 0 13-6 13-13 0-8-6-13-13-13z m28 26l-3 0c-7 0-13 6-13 13 0 7 6 13 13 13l3 0c7 0 13-6 13-13 0-7-6-13-13-13z m-12-169l-11 0c-31 0-50 23-50 60l-10 143c0 34 27 61 60 61l11 0c33 0 60-27 60-60l0-1-10-144c0-36-20-59-50-59z m-11 238c-19 0-34-15-34-34l10-143c0-14 3-35 24-35l11 0c21 0 24 21 24 34l10 144c-1 19-16 34-34 34z m203-69c-7 0-13 5-13 13l0 2c0 8 6 13 13 13 7 0 13-5 13-13l0-2c0-7-6-13-13-13z m27-29c-8 0-13 6-13 13l0 3c0 7 5 13 13 13 7 0 13-6 13-13l0-3c0-7-6-13-13-13z m-11-140l-10 0c-31 0-51 23-51 60l-9 143c0 34 27 61 60 61l10 0c33 0 60-27 60-60l0-1-9-144c0-36-20-59-51-59z m-10 238c-19 0-34-15-34-34l9-143c0-14 4-35 25-35l11 0c21 0 24 21 24 34l9 144c0 19-15 34-33 34z"/>
|
||||
<glyph glyph-name="headphones-mic" unicode="f" d="M419 348l-22 0c-3 48-42 83-89 83l-105 0c-47 0-86-35-89-83l-20 0c-25 0-45-19-45-44l0-71c0-25 20-45 45-45l19 0c1-27 14-50 33-66-3-17 5-35 20-45l41-25c7-4 15-7 23-7 3 0 6 1 10 2 11 2 21 9 27 19 13 21 6 48-14 60l-41 26c-10 6-21 8-33 5-8-2-15-6-20-11-12 11-20 27-20 45l0 152c0 34 29 62 64 62l105 0c35 0 64-28 64-62l0-152c0-1-1-3-1-3l48 0c25 0 46 20 46 45l0 71c0 25-21 44-46 44z m-306-134l-19 0c-10 0-19 9-19 19l0 71c0 10 9 19 19 19l19 0z m61-90c3 4 7 6 11 7 2 1 3 1 4 1 4 0 7-1 9-3l41-25c8-5 11-16 6-24-3-4-7-7-11-8-5-1-10 0-14 2l-40 25c-8 6-11 16-6 25z m264 109c0-10-8-19-19-19l-22 0 0 109 22 0c11 0 19-9 19-19z"/>
|
||||
<glyph glyph-name="gamepad" unicode="g" d="M107 136c-10 0-20 3-29 10-46 37-8 131-4 141l1 1c1 4 3 7 5 11 16 30 37 73 81 73l182 0c51 0 71-47 87-85 5-10 44-101 4-138-28-26-67-1-102 22-21 13-42 26-56 26l-39 0c-13 0-33-13-53-27-25-16-52-34-77-34z m-10 141c-10-24-30-90-3-112 17-13 47 7 76 26 24 16 47 31 67 31l40 0c20 0 44-15 68-30 28-18 59-37 72-25 23 22 0 89-10 110-17 42-31 70-64 70l-182 0c-29 0-45-33-59-60-2-3-3-7-5-10z m247-36l-3 0c-7 0-13 6-13 13 0 7 6 13 13 13l3 0c7 0 13-6 13-13 0-7-6-13-13-13z m0 55l-3 0c-7 0-13 6-13 13 0 7 6 13 13 13l3 0c7 0 13-6 13-13 0-7-6-13-13-13z m-29-29l-2 0c-8 0-13 6-13 13 0 8 5 13 13 13l2 0c8 0 13-5 13-13 0-7-5-13-13-13z m57 0l-3 0c-7 0-13 6-13 13 0 8 6 13 13 13l3 0c7 0 13-5 13-13 0-7-6-13-13-13z m-172 26l-12 0 0 13c0 7-6 13-13 13-7 0-13-6-13-13l0-13-13 0c-7 0-13-6-13-13 0-7 6-13 13-13l13 0 0-12c0-8 6-14 13-14 7 0 13 6 13 14l0 12 12 0c8 0 13 6 13 13 0 7-5 13-13 13z"/>
|
||||
<glyph glyph-name="headphones" unicode="h" d="M141 168l0 152c0 35 28 65 63 65l106 0c34 0 62-30 62-65l0-151c0-1 1-3 0-4l48 0c25 0 47 21 47 46l0 70c0 25-22 45-47 45l-21 0c-4 47-42 83-89 83l-106 0c-47 0-86-36-89-83l-19 0c-25 0-45-20-45-45l0-70c0-25 20-46 45-46l45 0z m-63 43l0 70c0 11 7 19 18 19l19 0 0-108-19 0c-11 0-18 8-18 19z m362 0c0-11-9-19-20-19l-20 0 0 108 20 0c11 0 20-8 20-19z"/>
|
||||
<glyph glyph-name="mic" unicode="i" d="M318 370c0 33-26 59-59 59l-6 0c-33 0-59-26-59-59l0-105c0-33 26-59 59-59l6 0c33 0 59 26 59 59z m-25-103c0-19-15-34-34-34l-7 0c-19 0-34 15-34 34l0 104c0 19 15 34 34 34l7 0c19 0 34-15 34-34z m82 8c0 7-6 13-12 13-7 0-13-6-13-13 0-51-42-93-93-93-52 0-93 42-93 93 0 7-6 13-13 13-7 0-12-6-12-13 0-60 46-110 104-117l0-34-80 0c-8 0-14-6-14-14 0-7 6-13 14-13l186 0c7 0 13 6 13 13 0 8-6 14-13 14l-80 0 0 34c60 6 106 56 106 117z"/>
|
||||
<glyph glyph-name="upload" unicode="j" d="M330 193l-83 86-84-86 52 0 0-141 61 23 0 118z m-12 247c-39 0-76-15-105-41-23-21-40-49-47-80-9 3-19 4-29 4-53 0-97-43-97-97 0-54 44-98 97-98 1 0 19 0 45 0l0 29c-26 0-44 0-45 0-37 0-68 31-68 69 0 37 31 68 68 68 12 0 23-3 34-9l19-11 2 23c3 31 18 59 41 81 23 21 54 33 85 33 70 0 127-57 127-127 0-70-57-127-127-127 0 0-5 0-10 0l0-29c5 0 10 0 10 0 86 0 156 70 156 156 0 86-70 156-156 156z"/>
|
||||
<glyph glyph-name="script" unicode="k" d="M283 80l-150 0c-30 0-56 15-73 44-13 21-17 42-17 42l-3 15 91 0 0 252 315 0 0-238c1-7 5-58-21-87-13-14-29-21-50-21-42 0-63 23-73 41-6 11-9 21-10 29l-220 0c2-6 5-13 9-20 13-21 31-32 52-32l150 0c7 0 13-6 13-13 0-6-6-12-13-12z m-127 101l158 0 1-12c0 0 1-15 9-30 10-18 27-28 51-28 13 0 23 5 31 13 10 11 14 29 15 42 1 15 0 27 0 27l0 1 0 214-265 0z m225 168l-185 0c-8 0-14 6-14 13 0 8 6 14 14 14l185 0c8 0 14-6 14-14 0-7-6-13-14-13z m0-61l-185 0c-8 0-14 7-14 14 0 8 6 14 14 14l185 0c8 0 14-6 14-14 0-7-6-14-14-14z m0-60l-185 0c-8 0-14 6-14 14 0 7 6 13 14 13l185 0c8 0 14-6 14-13 0-8-6-14-14-14z"/>
|
||||
<glyph glyph-name="text" unicode="l" d="M220 134l-81 232c-1 2-3 4-6 4l-10 0c-3 0-5-2-6-4l-83-233c-1-2 0-5 1-7 1-1 3-3 6-3l16 0c3 0 5 3 6 5l27 79 74 0 27-79c1-2 4-5 7-5l16 0c2 0 4 2 5 4 2 1 2 4 1 7z m-120 102l26 73c1 2 1 3 2 5 0-2 1-3 2-4l24-74z m252 60c-10 12-25 18-44 18-17 0-35-5-53-14-3-2-4-6-3-9l5-14c1-2 2-3 4-4 2-1 4 0 6 1 14 8 28 12 41 12 11 0 19-3 23-10 5-7 8-18 8-33l0-4-23-1c-25 0-44-6-58-16-14-11-21-26-21-45 0-17 4-31 14-40 10-10 23-16 40-16 12 0 23 3 32 8 6 4 11 8 17 14l1-13c1-4 4-7 7-7l10 0c4 0 8 4 8 8l0 115c0 23-5 39-14 50z m-87-119c0 11 4 19 11 24 8 5 22 9 42 10l19 1 0-10c0-17-4-30-12-39-8-9-19-13-33-13-9 0-16 2-20 7-5 4-7 11-7 20z m186-105c-10 0-15 8-15 18 0 22 0 298 0 320 0 10 5 19 15 19 0 0 0 0 0 0 10 0 15-8 15-18 1-22 1-299 1-321 0-10-6-18-16-18 0 0 0 0 0 0z"/>
|
||||
<glyph glyph-name="cube" unicode="m" d="M452 421l-263 26c-3 0-6 0-8-2l0 0-126-88c-1-1-1-1-1-1-3-3-4-7-4-10l0-263c0-7 5-12 12-13l262-26c0 0 1 0 1 0 3 0 5 1 8 2l126 89c0 0 0 0 0 1 3 2 4 6 4 9l0 263c0 7-5 12-11 13z m-22-274l-2-1c0 0 0 0 0-1 0 0 0 0 0 0l-89-62 0 230 98 69 0-222c-1-6-2-9-7-13z m-227 272l211-21-92-65-222 22 86 61c1 0 1 0 1 0 0 0 0 0 0 0 3 2 7 4 11 4 2 0 4-1 5-1z m110-347l-237 23 0 236 237-23z"/>
|
||||
<glyph glyph-name="sphere" unicode="n" d="M418 415c-91 91-237 91-327 0-44-43-68-101-68-163 0-62 24-120 68-164 45-45 104-67 163-67 59 0 119 22 164 67 43 44 68 102 68 164-1 62-25 120-68 163z m-305-22c30 30 67 49 106 55-8-11-14-26-20-44-13-41-20-96-20-154 0-3 0-7 0-10 6-1 12-2 19-3 2 0 4 0 6 0 0 4 0 8 0 13 0 117 30 193 52 200 1 0 1 1 1 1 51 0 100-20 138-58 37-37 58-85 59-137-5-22-82-53-200-53-116 0-192 29-200 52 1 52 22 101 59 138z m283-283c-40-39-92-59-144-58-14 9-30 43-40 97l-25 1c3-19 7-39 12-54 5-16 11-30 17-40-38 7-74 25-103 54-30 30-49 67-56 107 11-7 25-13 42-18 42-14 97-21 155-21 58 0 113 7 154 21 18 5 32 12 43 19-7-41-26-78-55-108z"/>
|
||||
<glyph glyph-name="zone" unicode="o" d="M381 372c-3 7-9 11-17 11l-72 0 0-34 30 0-163-163 0 36-36 0 0-79 1 0c0-2 1-4 2-6 2-7 9-11 16-11l83 0 0 35-41 0 162 161 0-25 35 0 0 62c1 4 1 9 0 13z m-43-298c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-21-22-21-12 0-22 9-22 21z m-71 0c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-21-21-21-12 0-22 9-22 21z m-72 0c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-21-22-21-12 0-22 9-22 21z m-71 0c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-21-21-21-12 0-22 9-22 21z m-50-21c-6 0-11 2-15 6-4 4-7 10-7 15 0 6 3 12 7 16 4 4 9 6 15 6 6 0 11-2 15-6 4-4 7-10 7-16 0-5-2-11-7-15-4-4-9-6-15-6z m-22 93c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-22-22-22-12 0-22 10-22 22z m0 71c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-21-22-21-12 0-22 9-22 21z m0 72c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-22-22-22-12 0-22 10-22 22z m0 71c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-21-22-21-12 0-22 9-22 21z m286 72c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-22-22-22-12 0-22 10-22 22z m-71 0c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-22-21-22-12 0-22 10-22 22z m-72 0c0 12 10 22 22 22 12 0 22-10 22-22 0-12-10-22-22-22-12 0-22 10-22 22z m-71 0c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-22-21-22-12 0-22 10-22 22z m308-22c-6 0-12 3-16 7-4 4-6 9-6 15 0 6 2 11 6 15 4 4 10 7 16 7 5 0 11-3 15-7 4-4 6-9 6-15 0-6-2-11-6-15-4-4-10-7-15-7z m-22-264c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-22-21-22-12 0-22 10-22 22z m0 71c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-21-21-21-12 0-22 9-22 21z m0 72c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-22-21-22-12 0-22 10-22 22z m0 71c0 12 10 22 22 22 12 0 21-10 21-22 0-12-9-21-21-21-12 0-22 9-22 21z"/>
|
||||
<glyph glyph-name="light" unicode="p" d="M298 259c0-21-18-38-39-38-21 0-38 17-38 38 0 22 17 39 38 39 21 0 39-17 39-39z m-39-109c-60 0-109 49-109 109 0 61 49 110 109 110 61 0 110-49 110-110 0-60-50-109-110-109z m0 190c-44 0-80-36-80-81 0-44 36-80 80-80 45 0 81 36 81 80 0 45-36 81-81 81z m155-97c-8 0-14 7-14 15 0 8 6 14 14 14 17 0 35 0 52 0 0 0 0 0 0 0 8 0 14-6 14-14 0-8-6-14-14-14-17 0-35 0-52-1 0 0 0 0 0 0z m-308 0c-17 1-35 1-52 1-8 0-14 6-14 14 0 8 7 14 14 14 0 0 0 0 1 0 17 0 34 0 51 0 8 0 14-7 14-14 0-8-6-15-14-15z m263 109c-3 0-7 2-10 5-5 5-5 14 0 20 12 12 24 24 36 36 6 6 15 6 21 1 5-6 5-15 0-21-12-12-25-24-37-36-3-3-6-5-10-5z m-255-254c-3 0-7 2-10 5-5 5-5 14 0 20 13 12 25 24 37 36 6 5 15 5 20 0 6-6 6-15 0-20-12-13-24-25-37-37-2-2-6-4-10-4z m146 300c-8 0-14 6-14 14 0 17 0 34 0 52 0 7 6 14 14 14 0 0 0 0 0 0 8 0 14-6 14-14 0-18 1-35 1-52 0-8-7-14-15-14z m0-360c0 0 0 0 0 0-8 0-14 6-14 14 0 17 0 35 0 52 0 8 6 14 14 14 0 0 0 0 0 0 8 0 15-7 15-14 0-18-1-35-1-52 0-8-6-14-14-14z m-109 315c-3 0-7 1-10 4-12 12-24 24-36 36-6 6-6 15-1 20 6 6 15 6 21 0 12-12 24-24 36-36 6-6 6-15 0-20-2-3-6-4-10-4z m254-255c-3 0-7 1-10 4-12 12-24 25-36 37-5 6-5 15 0 20 6 6 15 6 20 0 13-12 25-25 37-37 5-5 5-14-1-20-2-3-6-4-10-4z"/>
|
||||
<glyph glyph-name="web" unicode="q" d="M438 390c0 8-6 15-14 15l-333 0c-8 0-15-7-15-15l0-298c0-8 7-14 15-14l333 0c8 0 14 6 14 14z m-219-8l172 0c8 0 15-7 15-16 0-9-7-16-15-16l-172 0c-8 0-15 7-15 16 0 9 7 16 15 16z m-47 1c9 0 16-7 16-16 0-10-7-17-16-17-10 0-17 7-17 17 0 9 7 16 17 16z m-51 0c9 0 17-7 17-16 0-10-8-17-17-17-9 0-17 7-17 17 0 9 8 16 17 16z m291-276l-308 0 0 219 308 0z m-250 84l-15 0-20 60 13 0 14-45 15 45 13 0 15-45 14 45 13 0-20-60-14 0-14 41z m137 25l-46 0c0-5 2-8 6-11 3-2 8-4 12-4 8 0 13 3 17 7l7-8c-6-6-14-9-25-9-8 0-15 2-21 8-6 5-9 13-9 22 0 9 3 17 9 22 6 6 13 9 21 9 8 0 15-3 21-8 6-5 8-11 8-20z m-46 9l34 0c0 5-2 9-5 12-3 3-7 4-11 4-5 0-9-1-13-4-3-3-5-7-5-12z m101 27c8 0 14-3 20-9 6-5 9-12 9-22 0-9-3-16-9-22-5-6-12-8-19-8-8 0-15 3-21 9l0-9-12 0 0 83 12 0 0-34c5 8 12 12 20 12z m-20-31c0-6 2-10 5-14 4-4 8-5 13-5 5 0 9 1 13 5 3 4 5 8 5 14 0 6-2 10-5 14-4 4-8 6-13 6-5 0-9-2-13-6-3-4-5-8-5-14z"/>
|
||||
<glyph glyph-name="web-2" unicode="r" d="M438 390c0 8-6 15-14 15l-333 0c-8 0-15-7-15-15l0-298c0-8 7-14 15-14l333 0c8 0 14 6 14 14z m-219-8l172 0c8 0 15-7 15-16 0-9-7-16-15-16l-172 0c-8 0-15 7-15 16 0 9 7 16 15 16z m-47 1c9 0 16-7 16-16 0-10-7-17-16-17-10 0-17 7-17 17 0 9 7 16 17 16z m-51 0c9 0 17-7 17-16 0-10-8-17-17-17-9 0-17 7-17 17 0 9 8 16 17 16z m291-276l-308 0 0 219 308 0z"/>
|
||||
<glyph glyph-name="edit" unicode="s" d="M196 214c-27-27-54-54-81-81-6 6-13 13-19 19 27 27 54 54 81 81l-22 21c-31-31-61-62-92-92-7-7-11-13-12-22-3-25-7-50-11-76 3 0 4 0 6 0 24 5 48 10 72 15 5 1 10 4 13 7 32 32 64 64 96 96z m126 207c10 10 21 21 33 32 4 4 10 4 14 0 19-19 38-38 57-57 4-5 4-10 0-15-11-11-22-22-34-33-23 24-46 48-70 73z m23-181c-5-1-8 0-11 3-8 8-15 15-23 23 18 18 37 37 55 55 2 2 4 4 4 4-24 25-47 49-71 74-2-2-3-4-5-6-18-18-37-37-55-55-34 34-67 67-101 101-2 2-5 5-8 7-18 14-42 12-57-5-15-17-14-42 2-58 50-51 101-101 151-151 17-16 33-33 50-49 2-3 3-5 2-8-2-8-4-15-4-23-4-48 27-90 73-102 20-5 39-4 58 4-1 2-3 3-5 4-14 14-28 28-42 42-10 11-11 26-2 36 8 8 16 16 24 24 11 10 24 10 35 0 2-1 4-4 6-6 15-14 29-28 43-42 0 0 1 0 2 1 1 8 3 16 4 24 7 69-59 123-125 103z m-243 162c-7 0-14 6-14 14 0 8 6 14 14 14 8 0 15-6 15-14 0-7-7-14-15-14z m198-46c6-6 13-12 19-19-13-13-26-26-39-39-7 6-13 12-20 19 14 13 27 26 40 39z"/>
|
||||
<glyph glyph-name="market" unicode="t" d="M88 370c3 0 7 0 10 0 6 0 11-1 15-2 9-2 16-8 20-16 3-5 4-10 6-15 2-6 3-13 5-19 3-10 5-19 8-27 2-6 3-13 5-19 3-7 5-15 7-23 3-9 6-19 9-30 0-2 1-4 1-6 2-8 5-17 9-24 3-7 8-12 13-15 6-3 13-5 22-6 2 0 5 0 7 0l21 0 57 0c25 0 50 0 75 0 12 0 25 0 37 0 1 0 1 0 2 0 6 0 13 0 17 2 5 3 7 9 9 16l3 10c0 0 0 0 0 0 0 2 1 3 1 3 5 20 11 40 17 60 1 4 2 9 4 13 2 8 4 16 7 24 2 7 4 16 0 22-3 4-9 5-14 5-6 0-188-2-284-4l-4 0-5 22c0 3-1 6-2 9 0 3-1 5-1 7-1 2-1 3-1 4-1 4-2 7-3 11-2 6-6 12-11 17-5 5-11 8-17 9-3 1-6 1-10 1-5 1-9 1-14 1-19 0-38 0-59 0-1 0-3 0-4 0-3 0-7 0-10-1-3 0-6-2-8-4-3-5-4-11-2-17 2-5 6-6 9-7 4-1 8-1 13-1 5 0 10 0 16 0l16 0c3 0 5 0 8 0z m353-78l-26-92-6-4-2 0c-2 0-21 0-55 0-50 0-118-1-125-1l-1-1 0 0c-6 0-12 2-16 6-4 4-6 10-8 16-3 12-6 25-10 39-1 4-2 8-3 12-2 4-3 8-4 13l-4 12z m-194-164c-9 0-15-2-20-7-5-5-8-11-8-20 0-8 3-16 8-21 5-5 13-8 20-8 17 0 30 12 30 28 0 8-3 15-9 20-5 6-12 8-21 8z m139 0c-9 0-16-2-21-6-5-6-8-13-8-21 0-9 3-16 8-21 5-6 12-8 21-8 16 0 29 13 29 29 0 8-3 14-8 20-5 5-13 7-21 7z"/>
|
||||
<glyph glyph-name="directory" unicode="u" d="M432 451l-99-38c-2 0-3-1-4-1-3 1-5 2-8 2l-116 38c-8 3-15 6-30-1l-91-35c-17-5-32-16-32-31l0-303c0-15 14-27 32-27l99 38c3 1 6 2 9 4 1-1 3-2 4-2 3-1 5-2 7-3 0 0 1 0 1-1 0 0 1 0 1 0l116-38 0 0c8-1 12-1 21 3l90 34c13 6 32 16 32 31l0 304c0 14-14 26-32 26z m-351-371c-1 0-1 1-1 2l0 303c0 2 5 6 14 9l1 0 1 1 89 34c1-1 0-1 0-2l0-303c0-1-3-4-16-10z m356 42c-1-2-5-5-17-11l-90-34c0 1-1 1-1 2l0 304c0 1 5 5 14 8l1 0 1 1 91 34c0 0 1-1 1-1z"/>
|
||||
<glyph glyph-name="menu" unicode="v" d="M257 22c-60 0-119 22-164 67-44 44-68 102-68 164 0 62 24 120 68 163 90 91 237 91 327 0 44-43 68-101 68-163 0-62-24-120-68-164-45-45-104-67-163-67z m0 431c-52 0-103-20-142-59-38-38-58-88-58-141 0-54 20-104 58-142 78-78 205-78 283 0 38 38 59 88 59 142 0 53-21 103-59 141-39 39-90 59-141 59z m101-133l-203 0c-8 0-15 7-15 15 0 8 7 15 15 15l203 0c8 0 14-7 14-15 0-8-6-15-14-15z m0-84l-203 0c-8 0-15 7-15 15 0 8 7 15 15 15l203 0c8 0 14-7 14-15 0-8-6-15-14-15z m0-81l-203 0c-8 0-15 7-15 15 0 8 7 14 15 14l203 0c8 0 14-6 14-14 0-8-6-15-14-15z"/>
|
||||
<glyph glyph-name="close" unicode="w" d="M258 19c-59 0-118 23-163 68-44 43-68 101-68 163 0 62 24 120 68 164 90 90 237 90 327 0 44-44 68-102 68-164 0-62-24-120-68-163-45-45-104-68-164-68z m0 431c-51 0-102-19-141-58-38-38-59-88-59-142 0-53 21-103 59-141 78-78 205-78 283 0 38 38 58 88 58 141 0 54-20 104-58 142-39 39-90 58-142 58z m25-200l67 67c7 7 7 18 0 25-7 7-18 7-25 0l-67-67-66 67c-7 7-18 7-25 0-7-7-7-18 0-25l66-67-66-66c-7-7-7-18 0-25 7-7 18-7 25 0l66 66 67-66c7-7 18-7 25 0 7 7 7 18 0 25z"/>
|
||||
<glyph glyph-name="close-inverted" unicode="x" d="M400 388c-39 39-90 59-142 59-51 0-102-20-141-59-38-37-59-88-59-141 0-53 21-104 59-141 78-78 205-78 283 0 38 37 58 88 58 141 0 53-20 104-58 141z m-45-208c8-9 8-22 0-30-8-8-21-8-29 0l-68 67-67-67c-8-8-21-8-29 0-9 8-9 21 0 30l67 67-67 67c-9 8-9 22 0 30 8 8 21 8 29 0l67-68 68 68c8 8 21 8 29 0 8-9 8-22 0-30l-67-67z"/>
|
||||
<glyph glyph-name="pin" unicode="y" d="M304 144c1 16-2 31-8 45l97 115 25-8c14-4 29 2 36 14 8 13 6 29-5 39l-116 116c-10 11-26 13-39 5-12-8-18-23-13-37l8-25-115-102c-33 11-72 2-98-24-12-12-12-33 0-45l65-65-83-82c-6-7-6-17 0-23 6-6 16-6 23 0l82 83 66-66c13-13 33-13 45 0 17 17 28 38 30 60z m-205 115c21 22 55 26 81 9l145 130-14 45 116-116-45 13-126-149c25-28 18-62-4-85z"/>
|
||||
<glyph glyph-name="pin-inverted" unicode="z" d="M297 160c2 14 0 27-6 39l92 106 21-5c13-4 26 2 33 13 8 12 6 26-3 35l-98 99c-9 8-24 10-35 2-11-7-17-21-13-33l6-22-106-96c-30 9-64 0-88-24-12-12-13-30-2-41l55-55-77-76c-5-6-6-15-1-20 6-6 15-5 20 1l77 76 56-56c11-11 29-10 41 2 15 15 25 35 28 55z"/>
|
||||
<glyph glyph-name="resize-handle" unicode="A" d="M262 175l70 71c7 7 18 7 25 0 7-7 7-18 0-25l-70-71c-7-7-18-7-25 0-7 7-7 18 0 25m-101 0l175 175c7 7 18 7 25 0 7-7 7-18 0-25l-175-175c-7-7-18-7-25 0-7 7-7 18 0 25"/>
|
||||
<glyph glyph-name="diclosure-expand" unicode="B" d="M239 187c-3 0-6 1-8 3-5 5-5 12 0 17l42 42-43 44c-5 4-5 12 0 17 4 4 12 4 17 0l60-61-60-59c-2-2-5-3-8-3z"/>
|
||||
<glyph glyph-name="reload-small" unicode="a" d="M334 253c-9 0-18-7-18-16-3-27-25-47-52-47-29 0-52 24-52 52 0 11 2 27 14 38 6 5 14 9 24 12-5-7-4-16 2-22 3-3 8-4 12-4 5 0 9 1 13 5l28 28c1 2 3 4 3 7 3 6 1 13-3 18l-29 29c-3 3-7 5-12 5-5 0-9-2-12-5-4-3-5-8-5-12 0-5 1-9 5-13l0 0c-20-3-37-11-50-23-16-16-25-38-25-63 0-47 39-86 86-86 45 0 82 33 87 78 1 9-6 18-16 19z"/>
|
||||
<glyph glyph-name="close-small" unicode="C" d="M291 259l43 44c8 7 8 19 0 27-7 7-19 7-26 0l-44-44-44 44c-7 7-19 7-26 0-8-8-8-20 0-27l43-44-43-43c-8-8-8-20 0-27 7-7 19-7 26 0l44 44 44-44c7-7 19-7 26 0 8 8 8 19 0 27z"/>
|
||||
<glyph glyph-name="backward" unicode="E" d="M292 349c-5 3-12 3-18-1l-94-71c-4-3-7-8-7-13 0-5 2-10 6-14l95-80c3-2 7-4 11-4 2 0 5 1 7 2 6 3 10 9 10 15l0 151c0 6-4 12-10 15"/>
|
||||
<glyph glyph-name="reload" unicode="F" d="M365 261c-9 1-17-5-18-15-4-45-43-80-89-80-49 0-89 40-89 89 0 19 4 45 25 65 16 15 39 23 68 25l-15-16c-6-6-6-17 0-24 4-3 8-4 12-4 4 0 9 1 12 5l43 44c2 2 3 4 4 6 2 6 1 13-4 18l-44 44c-6 6-17 6-23 0-7-7-7-17 0-24l15-15c-38-2-69-14-91-35-23-21-36-53-36-88 0-68 55-123 123-123 64 0 116 47 122 110 1 9-5 18-15 18"/>
|
||||
<glyph glyph-name="minimize" unicode="I" d="M154 282l198 0c10 0 18-8 18-18 0-10-8-18-18-18l-198 0c-10 0-18 8-18 18 0 10 8 18 18 18"/>
|
||||
<glyph glyph-name="maximize" unicode="J" d="M157 244l77 0 0-75c0-9 8-17 17-17 9 0 17 8 17 17l0 75 75 0c10 0 17 8 17 17 0 10-7 18-17 18l-75 0 0 76c0 10-8 17-17 17-9 0-17-7-17-17l0-76-77 0c-10 0-17-8-17-18 0-9 8-17 17-17z"/>
|
||||
<glyph glyph-name="maximize-inverted" unicode="K" d="M251 434c-96 0-173-78-173-173 0-96 77-173 173-173 95 0 173 77 173 173 0 95-78 173-173 173z m93-190l-77 0 0-76c0-10-7-17-16-17-9 0-16 7-16 17l0 76-77 0c-10 0-17 8-17 17 0 9 7 17 17 17l77 0 0 76c0 9 7 17 16 17 9 0 16-8 16-17l0-76 77 0c9 0 17-8 17-17 0-9-8-17-17-17z"/>
|
||||
<glyph glyph-name="disclosure-button-expand" unicode="L" d="M264 202l-60 59c-4 5-4 12 0 17 5 4 13 4 17 0l43-43 43 44c4 4 12 4 17 0 4-5 4-13 0-17z m90-79l-188 0c-16 0-29 13-29 29l0 188c0 16 13 29 29 29l188 0c16 0 29-13 29-29l0-188c0-16-13-29-29-29z m-188 222c-3 0-5-2-5-5l0-188c0-3 2-5 5-5l188 0c3 0 5 2 5 5l0 188c0 3-2 5-5 5z"/>
|
||||
<glyph glyph-name="disclosure-button-collapse" unicode="M" d="M264 290l-60-59c-4-5-4-12 0-17 5-4 13-4 17 0l43 43 43-44c4-4 12-4 17 0 4 5 4 13 0 17z m119 50l0-188c0-16-13-29-29-29l-188 0c-16 0-29 13-29 29l0 188c0 16 13 29 29 29l188 0c16 0 29-13 29-29z m-29-193c3 0 5 2 5 5l0 188c0 3-2 5-5 5l-188 0c-3 0-5-2-5-5l0-188c0-3 2-5 5-5z"/>
|
||||
<glyph glyph-name="script-stop" unicode="N" d="M298 79l-145 0c-29 0-54 14-71 42-13 20-17 41-17 41l-3 16 267 0 1-13c0 0 1-15 9-29 9-17 26-26 48-26 13 0 23 4 30 12 16 18 16 54 15 66l0 1 0 205-270 0c-7 0-13 7-13 14 0 7 6 14 13 14l294 0 0-232c1-7 5-57-20-86-13-13-29-20-49-20-41 0-61 22-71 41-6 9-9 18-10 27l-211 0c2-7 5-12 9-18 12-20 29-31 49-31l145 0c8 0 13-5 13-12 0-7-5-12-13-12z m95 258l-112 0c-7 0-14 7-14 14 0 8 7 15 14 15l112 0c8 0 14-7 14-15 0-8-6-14-14-14z m0-58l-143 0c-7 0-14 6-14 14 0 8 7 14 14 14l143 0c8 0 14-6 14-14 0-8-6-14-14-14z m0-58l-112 0c-7 0-14 6-14 14 0 8 7 14 14 14l112 0c8 0 14-6 14-14 0-8-6-14-14-14z m-204 72l42 42c7 7 7 19 0 26-7 7-18 7-25 0l-43-42-42 42c-7 7-18 7-26 0-7-7-7-19 0-26l43-42-43-42c-7-7-7-19 0-26 8-7 19-7 26 0l42 42 43-42c7-7 18-7 25 0 7 7 7 19 0 26z"/>
|
||||
<glyph glyph-name="script-reload" unicode="O" d="M236 295c-10 1-18-6-19-15-2-26-24-45-50-45-28 0-50 22-50 50 0 10 2 25 14 36 5 6 13 10 23 12-4-7-4-15 2-21 3-3 7-5 12-5 4 0 9 2 12 5l27 28c1 2 3 4 3 6 3 7 2 14-3 18l-28 28c-3 3-7 5-11 5-5 0-9-2-12-5-3-3-5-7-5-12 0-4 2-9 5-12l1-1c-20-2-37-10-49-22-16-14-25-36-25-60 0-46 38-84 84-84 43 0 79 33 83 76 1 9-5 17-14 18z m62-216l-145 0c-30 0-55 15-72 43-12 20-16 40-16 41l-3 15 267 0 0-12c0 0 1-15 9-29 10-18 26-26 49-26 13 0 22 4 29 11 16 19 16 55 14 67l0 1 0 207-215 0c-7 0-12 5-12 12 0 8 5 13 12 13l241 0 0-231c1-7 5-57-21-86-12-13-28-20-48-20-41 0-62 22-72 40-5 10-8 20-10 27l-210 0c2-5 5-11 8-17 13-20 29-29 50-29l145 0c7 0 13-6 13-14 0-7-6-13-13-13z m95 259l-139 0c-8 0-14 6-14 14 0 8 6 14 14 14l139 0c7 0 14-6 14-14 0-8-7-14-14-14z m0-58l-103 0c-7 0-14 6-14 14 0 8 7 14 14 14l103 0c7 0 14-6 14-14 0-8-7-14-14-14z m0-58l-111 0c-7 0-14 6-14 14 0 8 7 14 14 14l111 0c7 0 14-6 14-14 0-8-7-14-14-14z"/>
|
||||
<glyph glyph-name="script-run" unicode="P" d="M209 309l-79 60c-5 3-11 4-16 1-4-2-8-7-8-13l0-126c0-6 4-11 9-14 2-1 4-1 6-1 3 0 7 1 9 3l80 68c3 3 5 7 5 11 0 5-2 9-6 11m89-232l-145 0c-30 0-55 16-72 44-12 20-16 40-16 41l-3 16 267 0 0-13c0 0 1-14 9-29 10-17 26-26 49-26 13 0 22 4 29 12 16 18 16 54 14 66l0 1 0 206-269 0c-7 0-13 6-13 13 0 7 6 13 13 13l295 0 0-230c1-8 5-57-21-86-12-14-28-21-48-21-41 0-62 23-72 41-5 10-8 19-10 28l-210 0c2-7 5-12 8-18 13-20 29-31 50-31l145 0c7 0 13-7 13-14 0-7-6-13-13-13z m95 261l-180 0c-7 0-14 6-14 14 0 8 7 14 14 14l180 0c7 0 14-6 14-14 0-8-7-14-14-14z m0-59l-135 0c-8 0-14 7-14 15 0 7 6 14 14 14l135 0c7 0 14-7 14-14 0-8-7-15-14-15z m0-58l-180 0c-7 0-14 7-14 14 0 8 7 15 14 15l180 0c7 0 14-7 14-15 0-8-7-14-14-14z"/>
|
||||
<glyph glyph-name="script-new" unicode="Q" d="M298 80l-145 0c-30 0-55 15-72 43-12 20-16 40-16 41l-3 16 267 0 0-12c0-1 1-15 9-29 10-18 26-27 49-27 13 0 22 4 29 12 16 18 16 54 14 66l0 1 0 206-269 0c-7 0-13 6-13 13 0 7 6 13 13 13l295 0 0-230c1-8 5-57-21-86-12-14-28-21-48-21-41 0-62 23-72 41-5 10-8 19-10 28l-210 0c2-7 5-12 8-18 13-20 29-30 50-30l145 0c7 0 13-7 13-14 0-7-6-13-13-13z m95 260l-180 0c-7 0-14 6-14 14 0 8 7 14 14 14l180 0c7 0 14-6 14-14 0-8-7-14-14-14z m0-59l-135 0c-8 0-14 7-14 15 0 7 6 14 14 14l135 0c7 0 14-7 14-14 0-8-7-15-14-15z m0-58l-180 0c-7 0-14 7-14 14 0 8 7 15 14 15l180 0c7 0 14-7 14-15 0-7-7-14-14-14z m-250 90l0 53c0 9-7 16-16 16-9 0-16-7-16-16l0-53-54 0c-9 0-16-8-16-17 0-9 8-17 16-17l54 0 0-53c0-9 7-16 16-16 9 0 16 8 16 17l0 52 54 0c9 0 16 8 16 17 0 9-8 16-17 16z"/>
|
||||
<glyph glyph-name="hifi-forum" unicode="2" d="M265 410c-83 0-150-68-150-150 0-24 6-47 16-67l-27-79 80 20c23-16 51-25 81-25 83 0 150 68 150 150 0 83-67 151-150 151z m38-248c-9 0-17 7-17 17 0 7 4 14 12 16l0 46-74 33 0-56c7-2 12-8 12-16 0-10-7-18-17-18-10 0-19 8-19 18 0 7 6 13 10 16l0 111c-4 2-10 9-10 16 0 10 9 17 19 17 9 0 17-7 17-17 0-8-5-14-12-16l0-41 74-33 0 51c-8 3-12 9-12 16 0 10 7 18 17 18 10 0 17-8 17-18 0-7-5-14-12-16l0-110c7-3 12-9 12-17 0-10-7-17-17-17z"/>
|
||||
<glyph glyph-name="hifi-logo-small" unicode="S" d="M374 374c-32 32-74 49-119 49-46 0-88-17-120-49-32-32-49-74-49-119 0-45 17-87 49-118 32-32 74-49 119-49 45 0 88 17 119 49 32 32 49 73 49 118 1 45-17 87-48 119z m-17-221c-28-28-65-43-103-43-39 0-75 15-103 43-27 27-42 64-42 102 0 39 15 75 42 103 28 28 64 43 103 43 38 0 75-15 103-43 27-28 42-64 42-103 0-39-15-76-42-102z m-145 47c-5 0-9 3-9 6l0 126c0 3 4 7 9 7 6 0 10-4 10-7l0-125c0-4-4-7-10-7z m0 118c-5 0-10 2-14 6-7 7-7 20 0 27 5 5 9 6 14 6 6 0 11-2 14-6 4-4 5-8 5-14 0-5-2-10-5-13-4-4-8-6-14-6z m0-144c-5 0-10 2-14 5-4 5-5 9-5 14 0 6 2 11 5 14 5 4 9 5 14 5 6 0 11-2 14-5 4-4 5-8 5-14 0-5-2-10-5-14-4-3-8-5-14-5z m85 2c-6 0-10 3-10 7l0 121c0 4 4 7 10 7 5 0 9-3 9-7l0-121c0-5-4-7-9-7z m0 120c-6 0-11 2-14 5-8 8-8 20 0 28 4 4 8 5 14 5 5 0 10-2 14-5 4-4 5-9 5-14 0-5-2-11-5-14-4-3-9-5-14-5z m0-144c-6 0-11 2-14 5-8 7-8 20 0 28 4 4 8 5 14 5 5 0 10-2 14-5 4-5 5-9 5-14 0-5-2-11-5-14-4-3-9-5-14-5z m1 73l-85 40 1 18 86-40z"/>
|
||||
<glyph glyph-name="avatar-1" unicode="T" d="M293 71c-1 0-1 0-1 0-14-1-14-1-16 12-6 43-11 86-16 128-1 2-1 4-1 6-3 0-6 0-9 0-2-10-3-20-4-31-4-36-8-72-12-109-1-7-2-8-9-8-2 0-5 0-7 0 0 74 1 181 1 254-1-1-33 0-44 1-15 0-62 1-79 1-8 0-14 3-18 10-1 2-2 4-4 7 8 0 15 0 22 1 35 1 99 8 100 12 14 11 23 10 36 10 15 0 31 0 46-1 11 0 24 3 37-10 20-10 81-10 123-11 0 0 1 0 1 0-4-12-12-17-25-17-29-1-77-3-127-1 2-73 4-181 6-254z m-32 371c16-6 14-20 13-32 0-5-1-10-1-14-2-11-10-18-20-18-11 0-19 8-20 18-1 6-1 13-2 20-1 7 3 13 11 15 10 3 10 3 19 11z"/>
|
||||
<glyph glyph-name="placemark" unicode="U" d="M134 98c31-32 73-49 119-49l1 0c45 0 86 16 117 47 31 30 48 71 48 114 1 46-16 88-46 120-9 9-20 17-31 24-3-7-6-15-10-22 5-4 11-8 16-13 15-14 28-32 33-46l1-1c0-1 0-2-1-3l-1 0c-12-7-25-10-38-12-2-1-4-1-7-2l-1 0c0 0-1 0-2 1 0 0-1 1-1 1l0 1c-3 15-6 31-12 46l-7-15c4-11 6-21 8-32l0-1c0-1 0-1 0-2-1-1-1-1-2-1l-1 0-55-3-4 0c0 0-1 0-1 0-1 1-1 2-1 2l0 74c-5 9-8 17-11 26 0-1 0-1 0-1l-1 0 0-97c0 0 0-1 0-2 0-1-1-2-2-2l-1 0c0 0-1 0-1 0l-51 3c-1 0-2 0-2 1-1 1-1 2 0 2l0 1c5 27 13 58 35 83 6 6 12 13 20 16 0 0 1 0 1 0-3 7-6 15-9 22-37-4-71-20-99-47-30-30-47-70-48-113-1-46 16-88 47-120z m198 72c0 4 1 7 1 10l0 0c0 7 1 14 1 21 0 9 0 18 0 27 0 3 0 6 0 9l0 1c-1 1-1 6 5 7 11 3 23 5 34 8l1 0c3 1 7 2 11 2l1 1c1 0 3-1 3-2l0-1c0-1 1-1 1-2 1-3 1-5 2-8 3-14 4-27 3-40-2-12-8-21-18-25-8-3-17-5-25-8-3 0-7-1-10-2-1-1-3-1-4-1-1 0-1 0-2 0l-1-1c0 0 0 0 0 0-1 0-2 1-2 1-1 1-1 1-1 2z m2-17c12 4 23 7 35 10l14 4c1 0 2 0 2-1 1 0 1-2 1-2l-1-2c-7-18-17-34-30-47-13-13-28-24-46-31l-2-1c0 0-1 0-1 0-1 0-1 0-2 0 0 1-1 2-1 3l1 1c0 1 0 1 0 1 0 1 0 2 1 3 6 9 15 24 22 55 1 3 2 6 7 7z m-78 83c0 1 1 3 2 3l3 0 41 2 13 1c0 0 1 0 1 0l1 0c0 0 0-1 1-1l1 0c1 0 2-1 2-3 0-1 0-3 0-5 0-2 0-4 0-6l0-5c1-7 1-15 0-22l0-4c0-5 0-11-1-17 0-1 0-3 0-5 0-1 0-3 0-4 0-2-1-7-7-8-10 0-20-1-30-2l-2 0c-3-1-8-1-12-1-5-1-7-1-8-1-1 0-1 0-2 0l-2 1c-1 1-1 2-1 3l0 74z m0-92c0 1 1 3 2 3l3 0 51 3c1 0 2 0 2-1 1 0 1-1 1-2l0-1c0 0 0-1 0-1 0 0 0 0-1-1 0 0 0-1 0-1l0-1c0-1-1-3-1-4-1-2-2-5-3-7l-1-3c-3-9-7-18-11-27-4-8-9-14-14-19-5-6-13-9-23-10l-3 0c-1 0-1 0-2 1 0 0 0 1 0 2l0 69z m-52-59c-1-1-1-2-2-2 0 0-1 1-1 1l-2 1c-27 6-64 40-78 79l-1 1c0 1 0 2 1 3 1 1 2 1 3 1l13-4c12-4 24-7 36-11 3-1 6-2 7-7 5-24 12-42 23-57l1-2c1-1 1-3 0-3z m40-10c0-1-1-1-1-2-1 0-2 0-2 0 0 0-1 0-1 0-1 0-14 4-20 11-15 19-23 41-29 61 0 0 0 0 0 1l0 1c0 1 0 1 1 2 0 1 1 1 2 1l1 0 29-2 17-1c2-1 3-2 3-3l0-69z m-21 165l18-2c2 0 3-1 3-2l0-75c0-1-1-2-1-2-1-1-2-1-2-1 0 0 0 0-1 0 0 0-14 2-21 2-8 1-17 2-26 3-4 0-6 4-6 7-2 18-3 35-3 47 0 5-1 22-1 22 0 1 1 2 1 2 1 1 1 1 2 1z m-98 32l1 1c12 31 42 59 76 71l3 1c1 0 2 0 3-1 0-1 0-2 0-3l-2-3c-18-25-25-53-31-79l0-1c0-1 0-2-1-2 0 0-1-1-1-1-1 0-1 0-1 0l-1 1c-5 1-11 3-17 4-8 2-18 4-27 8l0 1c-2 0-2 2-2 3z m-8-18l1 1c0 1 1 2 3 2l49-13c1 0 2-1 2-3l2-71c0-1 0-2-1-2-1-1-1-1-2-1 0 0 0 0 0 0l-1 0c-1 0-2 1-3 1l0 0c-2 0-4 1-7 1-16 4-29 9-39 17-6 4-9 8-9 15-1 19 1 37 5 53z m178 42c-8 16-14 31-21 46-8 17-17 35-24 52-14 31 3 65 36 71 28 5 56-16 59-44 0-9-1-18-5-26-14-32-29-64-44-97 0 0-1-1-1-2z m27 117c0 15-12 28-27 28-15 0-27-12-28-27 0-16 13-28 28-28 15 0 27 12 27 27z"/>
|
||||
<glyph glyph-name="box" unicode="V" d="M318 74l126 89 15-22-126-88z m-137 101l0-99 27 0 0 96z m145-125c-1 0-1 0-2 0l-262 26c-7 1-12 7-12 13l0 263c0 4 1 7 4 10 3 2 7 4 10 3l263-26c7 0 12-6 12-13l0-262c0-4-2-8-4-10-3-3-6-4-9-4z m-250 51l236-23 0 236-236 23z m377 326l-263 26c-3 1-7-1-10-3-3-3-4-6-4-10l0-21c3 2 7 3 11 3 0 0 0 0 1 0 3 2 7 4 11 4 2 0 3 0 5-1l234-23 0-236c0-7-3-10-10-14 1-2 1-5 1-7 1-2 0-3 0-5l21-2c1 0 1 0 2 0 3 0 6 1 8 4 3 2 5 6 5 10l0 262c0 7-5 13-12 13z m-397-64l125 88 16-22-126-88z m262-26l126 88 15-22-126-88z m-256-123l2 27 263-26-3-26z m146 37l0 91-27 0 0-88c9-1 18-2 27-3z"/>
|
||||
<glyph glyph-name="community" unicode="0" d="M50 175c-4 0-8 2-11 6-4 6-2 14 4 18l24 16 69 48 89-64c6-4 7-13 3-19-5-6-13-7-19-3l-74 53-53-37-24-16c-3-1-5-2-8-2z m130-10l-44 32-47-32-22-14 0-63 135 0 0 60z m120 10c-4 0-9 2-11 6-4 6-3 14 3 18l25 16 68 48 89-64c6-4 7-13 3-19-4-6-13-7-19-3l-73 53-54-37-24-16c-2-1-5-2-7-2z m129-10l-46 32-45-32-22-14 0-63 135 0 0 60z m-256 202c-4 0-9 2-11 5-4 7-3 15 4 19l24 16 68 48 89-65c6-4 7-12 3-18-4-6-12-7-18-3l-74 53-53-37-25-16c-2-2-5-2-7-2z m129-11l-46 32-45-31-22-15 0-62 135 0 0 60z"/>
|
||||
<glyph glyph-name="grab-handle" unicode="X" d="M280 318c0 10 9 19 20 19 10 0 19-9 19-19 0-11-9-20-19-20-11 0-20 9-20 20z m-46-20c-5 0-10 2-14 6-3 4-5 8-5 14 0 5 2 10 5 13 4 4 9 6 14 6 5 0 10-2 13-6 4-3 6-8 6-13 0-5-2-10-6-14-3-4-8-6-13-6z m46-42c0 10 9 19 20 19 10 0 19-9 19-19 0-11-9-20-19-20-11 0-20 9-20 20z m-46-20c-5 0-10 3-14 6-3 4-5 9-5 14 0 5 2 10 5 13 4 4 9 6 14 6 5 0 10-2 13-6 4-3 6-8 6-13 0-5-2-10-6-14-3-3-8-6-13-6z m46-42c0 10 9 19 20 19 10 0 19-9 19-19 0-11-9-19-19-19-11 0-20 8-20 19z m-46-19c-5 0-10 2-14 5-3 4-5 9-5 14 0 5 2 10 5 13 4 4 9 6 14 6 5 0 10-2 13-6 4-3 6-8 6-13 0-5-2-10-6-14-3-3-8-5-13-5z"/>
|
||||
<glyph glyph-name="search" unicode="Y" d="M277 185c-48 0-88 40-88 88 0 49 40 89 88 89 49 0 89-40 89-89 0-48-40-88-89-88z m0 159c-38 0-70-32-70-71 0-38 32-70 70-70 39 0 71 32 71 70 0 39-32 71-71 71z m-112-205c-2 0-4 1-6 2-4 4-4 9 0 13l61 64c3 4 9 4 12 0 4-3 4-9 1-12l-61-64c-2-2-5-3-7-3z"/>
|
||||
<glyph glyph-name="disclosure-collapse" unicode="Z" d="M264 198l-60 59c-4 5-4 12 0 17 5 4 13 4 17 0l43-43 43 44c4 4 12 4 17 0 4-5 4-13 0-17z"/>
|
||||
<glyph glyph-name="script-upload" unicode="R" d="M260 91l-117 0c-29 0-54 15-71 43-13 20-17 40-17 41l-3 16 157 0c7 0 13-5 13-12 0-7-6-12-13-12l-124 0c2-8 5-12 9-18 12-20 29-31 49-31l117 0c7 0 13-6 13-14 0-7-6-13-13-13z m117 7c-7 0-12 6-12 13 0 7 5 13 12 13 13 0 23 4 30 11 16 19 16 55 14 67l1 1 0 0 0 206-257 0 0-226c0-7-5-13-12-13-8 0-13 6-13 13l0 251 306 0 0-230c1-7 5-57-20-86-13-13-28-20-49-20z m6 253l-179 0c-8 0-14 6-14 14 0 8 6 14 14 14l179 0c8 0 14-6 14-14 0-8-6-14-14-14z m0-58l-179 0c-8 0-14 6-14 14 0 8 6 14 14 14l179 0c8 0 14-6 14-14 0-8-6-14-14-14z m0-58l-16 0c-8 0-14 6-14 14 0 8 6 14 14 14l16 0c8 0 14-6 14-14 0-8-6-14-14-14z m-110 0l-69 0c-8 0-14 6-14 14 0 8 6 14 14 14l69 0c8 0 14-6 14-14 0-8-6-14-14-14z m127-71l-80 83-81-83 49 0 0-136 62 22 0 114z"/>
|
||||
<glyph glyph-name="code" unicode="W" d="M92 242l74 73c6 6 15 6 21 0 6-5 6-15 0-21l-53-52 54-54c6-6 6-16 0-21-5-6-15-6-21 0z m347 0l-74 73c-5 6-15 6-21 0-5-5-5-15 0-21l53-52-54-54c-6-6-6-16 0-21 6-6 15-6 21 0z m-223-137c-1 0-2 0-4 1-5 2-8 8-6 14l98 254c3 6 9 8 15 6 5-2 8-8 6-14l-99-254c-1-4-5-7-10-7z"/>
|
||||
<glyph glyph-name="avatar" unicode="<" d="M256 88c-93 0-169 75-169 168 0 93 76 169 169 169 93 0 169-76 169-169 0-93-76-168-169-168z m0 316c-81 0-148-66-148-148 0-81 67-147 148-147 81 0 148 66 148 147 0 82-67 148-148 148z m97-90l-1 1c-3 3-7 4-10 4-1 0-61-9-86-9-1 0-1 0-2 0-25 0-87 10-87 10-5 0-10-2-13-6l-1-2c-2-3-2-7-1-10 1-4 3-6 6-8 12-5 49-20 60-22 2 0 5 0 6-7 1-8-3-46-7-65-5-17-13-40-13-41-2-6 1-13 7-15l8-3c3-1 6-1 9 1 3 1 5 4 6 7l21 65 20-67c1-3 3-6 6-7 2-1 4-1 5-1 2 0 3 0 5 0l7 3c5 2 8 8 7 14 0 0-6 24-11 44-3 12-4 30-5 45 0 9-1 16-2 22 0 1 0 4 5 5 0 0 1 0 2 0l55 22c4 2 6 5 7 9 1 4 0 8-3 11z m-68 37c0-16-13-29-29-29-16 0-29 13-29 29 0 16 13 29 29 29 16 0 29-13 29-29z"/>
|
||||
<glyph glyph-name="arrows-h" unicode=":" d="M512 256c0-5-2-9-5-13l-74-73c-3-4-7-5-12-5-5 0-10 1-13 5-4 4-6 8-6 13l0 36-292 0 0-36c0-5-2-9-6-13-3-4-8-5-13-5-5 0-9 1-12 5l-74 73c-3 4-5 8-5 13 0 5 2 9 5 13l74 73c3 4 7 5 12 5 5 0 10-1 13-5 4-4 6-8 6-13l0-36 292 0 0 36c0 5 2 9 6 13 3 4 8 5 13 5 5 0 9-1 12-5l74-73c3-4 5-8 5-13z"/>
|
||||
<glyph glyph-name="arrows-v" unicode=";" d="M347 421c0-5-1-10-5-13-4-4-8-6-13-6l-36 0 0-292 36 0c5 0 9-2 13-6 4-3 5-8 5-13 0-5-1-9-5-12l-73-74c-4-3-8-5-13-5-5 0-9 2-13 5l-73 74c-4 3-5 7-5 12 0 5 1 10 5 13 4 4 8 6 13 6l36 0 0 292-36 0c-5 0-9 2-13 6-4 3-5 8-5 13 0 5 1 9 5 12l73 74c4 3 8 5 13 5 5 0 9-2 13-5l73-74c4-3 5-7 5-12z"/>
|
||||
<glyph glyph-name="arrows" unicode="`" d="M512 256c0-5-2-9-5-13l-74-73c-3-4-7-5-12-5-5 0-10 1-13 5-4 4-6 8-6 13l0 36-109 0 0-109 36 0c5 0 9-2 13-6 4-3 5-8 5-13 0-5-1-9-5-12l-73-74c-4-3-8-5-13-5-5 0-9 2-13 5l-73 74c-4 3-5 7-5 12 0 5 1 10 5 13 4 4 8 6 13 6l36 0 0 109-109 0 0-36c0-5-2-9-6-13-3-4-8-5-13-5-5 0-9 1-12 5l-74 73c-3 4-5 8-5 13 0 5 2 9 5 13l74 73c3 4 7 5 12 5 5 0 10-1 13-5 4-4 6-8 6-13l0-36 109 0 0 109-36 0c-5 0-9 2-13 6-4 3-5 8-5 13 0 5 1 9 5 12l73 74c4 3 8 5 13 5 5 0 9-2 13-5l73-74c4-3 5-7 5-12 0-5-1-10-5-13-4-4-8-6-13-6l-36 0 0-109 109 0 0 36c0 5 2 9 6 13 3 4 8 5 13 5 5 0 9-1 12-5l74-73c3-4 5-8 5-13z"/>
|
||||
<glyph glyph-name="compress" unicode="!" d="M256 238l0-128c0-5-2-10-5-13-4-4-8-6-13-6-5 0-10 2-13 6l-41 41-95-95c-2-2-4-3-7-3-2 0-4 1-6 3l-33 33c-2 2-3 4-3 6 0 3 1 5 3 7l95 95-41 41c-4 3-6 8-6 13 0 5 2 9 6 13 3 3 8 5 13 5l128 0c5 0 9-2 13-5 3-4 5-8 5-13z m216 192c0-3-1-5-3-7l-95-95 41-41c4-3 6-8 6-13 0-5-2-9-6-13-3-3-8-5-13-5l-128 0c-5 0-9 2-13 5-3 4-5 8-5 13l0 128c0 5 2 10 5 13 4 4 8 6 13 6 5 0 10-2 13-6l41-41 95 95c2 2 4 3 7 3 2 0 4-1 6-3l33-33c2-2 3-4 3-6z"/>
|
||||
<glyph glyph-name="expand" unicode=""" d="M252 210c0-2-1-4-3-6l-94-95 41-41c3-4 5-8 5-13 0-5-2-9-5-13-4-4-8-5-13-5l-128 0c-5 0-9 1-13 5-4 4-5 8-5 13l0 128c0 5 1 9 5 13 4 3 8 5 13 5 5 0 9-2 13-5l41-41 95 94c2 2 4 3 6 3 3 0 5-1 7-3l32-32c2-2 3-4 3-7z m223 247l0-128c0-5-1-9-5-13-4-3-8-5-13-5-5 0-9 2-13 5l-41 41-95-94c-2-2-4-3-6-3-3 0-5 1-7 3l-32 32c-2 2-3 4-3 7 0 2 1 4 3 6l94 95-41 41c-3 4-5 8-5 13 0 5 2 9 5 13 4 4 8 5 13 5l128 0c5 0 9-1 13-5 4-4 5-8 5-13z"/>
|
||||
<glyph glyph-name="placemark-1" unicode="#" d="M475 213l0 176c-32-17-61-26-87-26-16 0-29 3-41 10-19 9-37 16-53 21-16 6-33 8-51 8-33 0-71-12-115-36l0-171c47 21 88 32 124 32 10 0 20-1 29-2 10-1 19-4 28-7 10-4 17-7 22-9 6-3 13-6 24-12l8-4c8-4 18-6 29-6 23 0 50 9 83 26z m-384 226c0-7-1-13-5-18-3-6-7-10-13-14l0-361c0-3-1-5-2-7-2-2-4-2-7-2l-18 0c-3 0-5 0-7 2-2 2-2 4-2 7l0 361c-6 4-10 8-14 14-3 5-5 11-5 18 0 10 4 19 11 26 7 7 16 10 26 10 10 0 19-3 26-10 7-7 10-16 10-26z m421-18l0-218c0-8-3-13-10-17-2-1-4-2-5-2-41-22-77-33-105-33-17 0-32 3-45 10l-8 4c-13 6-22 10-29 13-6 3-15 6-26 9-10 2-21 4-32 4-20 0-42-5-68-13-25-8-47-18-65-29-3-2-6-3-9-3-3 0-6 1-9 3-7 3-10 9-10 16l0 212c0 6 3 11 9 15 7 4 14 8 23 12 8 5 19 9 32 15 14 6 28 11 44 14 15 4 30 6 44 6 21 0 41-3 60-9 18-6 38-14 60-25 7-3 15-5 25-5 23 0 53 11 89 32 4 2 7 4 8 5 6 3 12 3 18-1 6-4 9-9 9-15z"/>
|
||||
<glyph glyph-name="circle" unicode="$" d="M366 238c0 35-13 65-38 90-25 25-55 38-90 38-36 0-66-13-91-38-25-25-37-55-37-90 0-36 12-66 37-91 25-25 55-37 91-37 35 0 65 12 90 37 25 25 38 55 38 91z m36 0c0-23-4-44-13-64-8-20-20-38-35-53-15-14-32-26-52-35-21-9-42-13-64-13-23 0-44 4-64 13-20 9-38 21-53 35-14 15-26 33-35 53-9 20-13 41-13 64 0 22 4 43 13 64 9 20 21 37 35 52 15 15 33 27 53 35 20 9 41 13 64 13 22 0 43-4 64-13 20-8 37-20 52-35 15-15 27-32 35-52 9-21 13-42 13-64z"/>
|
||||
<glyph glyph-name="hand-pointer" unicode="9" d="M183 475c-10 0-19-3-26-10-7-7-11-16-11-26l0-256-43 58c-8 10-18 15-30 15-10 0-19-4-26-11-7-7-10-16-10-26 0-8 2-15 7-22l110-146c7-10 17-14 29-14l205 0c4 0 8 1 11 3 4 3 6 6 7 10l26 105c5 19 7 37 7 56l0 62c0 8-3 14-8 20-5 6-12 9-20 9-7 0-14-3-19-8-5-6-8-12-8-20l-9 0 0 18c0 9-3 17-9 23-6 6-14 10-23 10-9 0-16-4-23-10-6-6-9-14-9-22l0-19-9 0 0 26c0 10-4 19-11 27-7 8-16 11-26 11-10 0-19-3-26-10-7-8-10-16-10-26l0-28-10 0 0 163c0 11-3 20-10 27-7 8-16 11-26 11z m0 37c20 0 38-7 52-22 14-15 21-32 21-53l0-63c4 1 7 1 9 1 19 0 35-7 50-20 9 4 18 6 28 6 21 0 39-8 52-25 6 2 11 2 16 2 18 0 33-6 46-19 12-13 18-28 18-46l0-62c0-22-2-44-8-64l-26-106c-3-12-9-22-19-29-10-8-21-12-34-12l-205 0c-12 0-22 3-33 8-10 5-19 12-26 21l-109 146c-10 13-15 28-15 44 0 20 7 38 21 52 14 14 32 22 52 22 13 0 25-4 37-10l0 156c0 20 7 37 21 52 14 14 32 21 52 21z m36-402l0 109-9 0 0-109z m74 0l0 109-10 0 0-109z m73 0l0 109-9 0 0-109z"/>
|
||||
<glyph glyph-name="plus-square-o" unicode="%" d="M384 283l0-18c0-3-1-5-3-6-1-2-3-3-6-3l-101 0 0-101c0-2-1-4-2-6-2-2-4-3-7-3l-18 0c-3 0-5 1-7 3-1 2-2 4-2 6l0 101-101 0c-3 0-5 1-6 3-2 1-3 3-3 6l0 18c0 3 1 5 3 7 1 2 3 3 6 3l101 0 0 100c0 3 1 5 2 7 2 1 4 2 7 2l18 0c3 0 5-1 7-2 1-2 2-4 2-7l0-100 101 0c3 0 5-1 6-3 2-2 3-4 3-7z m37-128l0 238c0 13-5 23-14 32-9 9-20 14-32 14l-238 0c-12 0-23-5-32-14-9-9-14-19-14-32l0-238c0-12 5-23 14-32 9-9 20-13 32-13l238 0c12 0 23 4 32 13 9 9 14 20 14 32z m36 238l0-238c0-22-8-42-24-58-16-16-35-24-58-24l-238 0c-23 0-42 8-58 24-16 16-24 36-24 58l0 238c0 23 8 42 24 58 16 16 35 24 58 24l238 0c23 0 42-8 58-24 16-16 24-35 24-58z"/>
|
||||
<glyph glyph-name="square" unicode="'" d="M375 439l-238 0c-12 0-23-5-32-14-9-9-14-19-14-32l0-238c0-12 5-23 14-32 9-9 20-13 32-13l238 0c12 0 23 4 32 13 9 9 14 20 14 32l0 238c0 13-5 23-14 32-9 9-20 14-32 14z m82-46l0-238c0-22-8-42-24-58-16-16-35-24-58-24l-238 0c-23 0-42 8-58 24-16 16-24 36-24 58l0 238c0 23 8 42 24 58 16 16 35 24 58 24l238 0c23 0 42-8 58-24 16-16 24-35 24-58z"/>
|
||||
<glyph glyph-name="align-center" unicode="8" d="M416 434l-320 0c-10 0-17-7-17-17l0-16c0-10 7-17 17-17l320 0c10 0 17 7 17 17l0 16c0 10-7 17-17 17z m-26-135l0 16c0 10-8 17-17 17l-234 0c-9 0-17-7-17-17l0-16c0-10 8-17 17-17l234 0c9 0 17 7 17 17z m8-171l-284 0c-10 0-18-7-18-17l0-16c0-10 8-17 18-17l284 0c10 0 18 7 18 17l0 0 0 16 0 0c0 10-8 17-18 17z m-25 85c0 10-8 17-18 17l-198 0c-10 0-18-7-18-17l0-16c0-10 8-17 18-17l198 0c10 0 18 7 18 17 0 0 0 0 0 0z"/>
|
||||
<glyph glyph-name="align-justify" unicode=")" d="M416 433l-320 0c-10 0-17-8-17-17l0-16c0-10 7-18 17-18l320 0c10 0 17 8 17 18l0 16c0 9-7 17-17 17z m0-101l-320 0c-10 0-17-8-17-17l0-16c0-10 7-18 17-18l320 0c10 0 17 8 17 18l0 16c0 9-7 17-17 17z m0-101l-320 0c-10 0-17-8-17-18l0-16c0-9 7-17 17-17l320 0c10 0 17 8 17 17l0 16c0 10-7 18-17 18z m0-101l-320 0c-10 0-17-8-17-18l0-16c0-9 7-17 17-17l320 0c10 0 17 8 17 17l0 16c0 10-7 18-17 18z"/>
|
||||
<glyph glyph-name="align-left" unicode="*" d="M416 434l-320 0c-10 0-17-7-17-17l0-16c0-10 7-17 17-17l320 0c10 0 17 7 17 17l0 16c0 10-7 17-17 17z m-320-152l234 0c9 0 17 7 17 17l0 16c0 10-8 18-17 18l-234 0c-10 0-17-8-17-18l0-16c0-10 7-17 17-17z m285-154l-285 0c-10 0-17-7-17-17l0-16c0-10 7-17 17-17l285 0c10 0 17 7 17 17l0 0 0 16 0 0c0 10-7 17-17 17z m-285 52l199 0c9 0 17 7 17 17 0 0 0 0 0 0l0 16c0 10-8 17-17 17l-199 0c-10 0-17-7-17-17l0-16c0-10 7-17 17-17z"/>
|
||||
<glyph glyph-name="align-right" unicode="^" d="M416 434l-320 0c-10 0-17-7-17-17l0-16c0-10 7-17 17-17l320 0c10 0 17 7 17 17l0 16c0 10-7 17-17 17z m0-102l-234 0c-9 0-17-7-17-17l0-16c0-10 8-17 17-17l234 0c10 0 17 7 17 17l0 16c0 10-7 17-17 17z m0-204l-285 0c-10 0-17-7-17-17l0-16c0-10 7-17 17-17l285 0c9 0 17 7 17 17l0 0 0 16 0 0c0 10-8 17-17 17z m17 85c0 10-7 17-17 17l-199 0c-9 0-17-7-17-17l0-16c0-10 8-17 17-17l199 0c10 0 17 7 17 17 0 0 0 0 0 0z"/>
|
||||
<glyph glyph-name="bars" unicode="7" d="M475 128l0-37c0-5-1-9-5-12-4-4-8-6-13-6l-402 0c-5 0-9 2-13 6-4 3-5 7-5 12l0 37c0 5 1 9 5 13 4 3 8 5 13 5l402 0c5 0 9-2 13-5 4-4 5-8 5-13z m0 146l0-36c0-5-1-10-5-13-4-4-8-6-13-6l-402 0c-5 0-9 2-13 6-4 3-5 8-5 13l0 36c0 5 1 10 5 13 4 4 8 6 13 6l402 0c5 0 9-2 13-6 4-3 5-8 5-13z m0 147l0-37c0-5-1-9-5-13-4-3-8-5-13-5l-402 0c-5 0-9 2-13 5-4 4-5 8-5 13l0 37c0 5 1 9 5 12 4 4 8 6 13 6l402 0c5 0 9-2 13-6 4-3 5-7 5-12z"/>
|
||||
<glyph glyph-name="circle-slash" unicode="," d="M256 416c-88 0-160-72-160-160 0-88 72-160 160-160 88 0 160 72 160 160 0 88-72 160-160 160z m0-64c14 0 27-3 39-8l-127-127c-5 12-8 25-8 39 0 53 43 96 96 96z m0-192c-14 0-27 3-39 9l127 126c5-12 8-25 8-39 0-53-43-96-96-96z"/>
|
||||
<glyph glyph-name="sync" unicode="(" d="M392 275c6-41-7-84-39-115-47-47-119-52-173-17l38 36-138 19 19-134 42 40c76-55 183-50 251 18 40 39 58 91 56 142z m-233 77c47 46 119 52 173 17l-38-36 138-19-19 134-42-40c-76 55-183 50-251-18-40-39-58-91-56-142l56-11c-6 41 7 84 39 115z"/>
|
||||
<glyph glyph-name="key" unicode="-" d="M479 282c-1 2-1 4-3 6-2 2-5 3-7 3l-202 0c-13 51-60 89-115 89-65 0-119-54-119-119 0-66 54-119 119-119 59 0 107 43 117 99l45 0 0-65 0 0c0-5 5-9 10-9 0 0 0 0 0 0l0 0 31 0 0 0c5 0 10 4 10 9l0 0 0 65 26 0 0-100 0 0c0-5 4-9 9-9 0 0 0 0 0 0l32 0c0 0 0 0 0 0 5 0 9 4 10 9l0 0 0 100 27 0 0 0c2 0 5 0 7 2 2 2 3 5 3 7l0 0 0 32 0 0z m-327-90c-37 0-68 30-68 68 0 38 31 68 68 68 38 0 68-30 68-68 0-38-30-68-68-68z"/>
|
||||
<glyph glyph-name="link" unicode="." d="M202 136c5 5 10 7 17 7 7 0 13-2 19-7 10-11 10-23 0-36 0 0-22-20-22-20-19-19-42-29-68-29-26 0-49 10-68 29-19 19-29 42-29 67 0 27 10 50 29 69 0 0 76 76 76 76 24 23 48 36 73 39 26 3 47-4 66-22 5-5 8-11 8-18 0-7-3-13-8-19-12-11-24-11-36 0-17 17-40 11-68-17 0 0-75-75-75-75-9-9-14-20-14-33 0-13 5-23 14-31 9-9 19-14 32-14 13 0 23 5 32 14 0 0 22 20 22 20m230 294c19-19 29-42 29-68 0-26-10-49-29-68 0 0-81-81-81-81-25-25-51-37-77-37-21 0-40 9-57 26-5 5-7 10-7 17 0 7 2 13 7 19 5 4 11 7 18 7 7 0 13-3 18-7 17-17 38-13 62 12 0 0 81 80 81 80 10 9 15 20 15 32 0 13-5 24-15 32-8 9-17 14-28 16-11 2-22-2-31-11 0 0-26-25-26-25-5-5-11-7-18-7-7 0-13 2-18 7-11 11-11 23 0 36 0 0 26 25 26 25 18 19 40 27 65 26 25-1 47-11 66-31"/>
|
||||
<glyph glyph-name="location" unicode="/" d="M256 512c-88 0-160-72-160-160 0-89 80-208 160-352 80 144 160 263 160 352 0 88-71 160-160 160z m0-224c-35 0-64 28-64 64 0 35 29 64 64 64 36 0 64-29 64-64 0-36-28-64-64-64z"/>
|
||||
<glyph glyph-name="carat-r" unicode="3" d="M304 249l-55-43 0 86z"/>
|
||||
<glyph glyph-name="carat-l" unicode="4" d="M225 250l55 43 0-87z"/>
|
||||
<glyph glyph-name="folder-lg" unicode=">" d="M203 385l38-51 185 0 0-206-333 0 0 257 110 0m0 30l-110 0c-17 0-30-14-30-30l0-257c0-17 13-30 30-30l333 0c17 0 30 13 30 30l0 206c0 16-13 30-30 30l-170 0-29 39c-6 7-15 12-24 12z"/>
|
||||
<glyph glyph-name="folder-sm" unicode="?" d="M226 324l20-27 98 0 0-109-176 0 0 136 58 0m0 24l-58 0c-13 0-24-11-24-24l0-136c0-13 11-24 24-24l176 0c13 0 24 11 24 24l0 109c0 13-11 24-24 24l-86 0-12 17c-5 6-12 10-20 10z"/>
|
||||
<glyph glyph-name="level-up" unicode="1" d="M331 274c-9 9-21 14-37 14l-68 0 32 32c5 4 5 12 0 17-2 2-5 3-8 3-3 0-6-1-9-3l-59-60 60-60c5-4 13-4 17 0 5 5 5 12 0 17l-31 31 66 0c9 0 16-2 19-7 6-6 5-15 5-16l0 0 0-75c0-6 5-12 12-12 6 0 11 6 11 12l0 73c0 5 1 21-10 34z"/>
|
||||
<glyph glyph-name="info" unicode="[" d="M267 218c-1-7 0-10 7-10l8 0-3-15c-7-3-13-4-18-4-11 0-22 6-19 24l8 56c-3 0-7 1-11 2l2 17 36 0z m15 94c-1-7-8-11-15-11-8 0-14 6-13 14 1 7 8 11 15 11 8 0 14-5 13-14z m65 29c-23 23-53 35-85 35-33 0-63-12-86-35-23-23-35-53-35-85 0-33 12-63 35-85 23-23 53-35 85-35 33 0 63 12 85 35 23 23 35 53 35 85 1 31-11 61-34 85z m18-85c0-28-11-55-31-73-19-20-45-31-73-31-28 0-54 11-73 31-20 19-31 45-31 73 0 27 11 53 31 73 19 20 45 31 73 31 28 0 54-11 73-31 20-20 31-46 31-73z"/>
|
||||
<glyph glyph-name="question" unicode="]" d="M360 346c1 0 3-1 3-3l0-181c0-2-2-3-3-3l-182 0c-1 0-3 1-3 3l0 181c0 2 2 3 3 3l182 0m0 17l-182 0c-11 0-20-9-20-20l0-181c0-11 9-20 20-20l182 0c11 0 20 9 20 20l0 181c0 11-9 20-20 20z m-133-55c20 7 29 9 47 9 28 0 40-10 40-34l0-5c0-17-6-24-17-28-8-3-16-5-25-8l0-17-20 0-3 30c12 3 22 7 30 9 8 3 11 7 11 13l0 4c0 13-4 16-18 16-10 0-16-1-24-4l-2-12-19 0z m21-110c0 9 5 13 14 13 10 0 15-4 15-13 0-9-5-13-15-13-9 0-14 4-14 13z"/>
|
||||
<glyph glyph-name="alert" unicode="+" d="M267 369l120-207-240 0 120 207m0 18c-6 0-12-3-16-9l-119-207c-4-6-4-12 0-18 3-6 9-9 15-9l240 0c6 0 12 3 15 9 4 6 4 12 0 18l-119 207c-4 6-10 9-16 9z m-15-195c0 9 5 13 15 13 9 0 14-4 14-13 0-9-5-13-14-13-10 0-15 4-15 13z m28 96l-6-67-15 0-6 67 0 21 27 0z"/>
|
||||
<glyph glyph-name="home" unicode="_" d="M265 376l133-122-49 0 0-112-169 0 0 112-49 0 134 122m0 20c-5 0-10-2-14-5l-134-122c-6-5-8-14-5-22 3-8 11-13 19-13l29 0 0-92c0-11 9-20 20-20l169 0c11 0 20 9 20 20l0 92 29 0c9 0 16 5 19 13 3 8 1 17-5 22l-134 122c-4 3-9 5-13 5z"/>
|
||||
<glyph glyph-name="error" unicode="=" d="M258 143c-29 0-58 11-80 33-21 22-33 50-33 80 0 30 12 59 33 80 44 44 116 44 160 0 22-21 34-50 34-80 0-30-12-58-34-80-22-22-51-33-80-33z m0 211c-25 0-50-9-69-29-18-18-29-43-29-69 0-26 11-50 29-69 38-38 100-38 139 0 18 19 28 43 28 69 0 26-10 51-28 69-20 20-45 29-70 29z m13-98l32 33c4 3 4 9 0 12-3 3-9 3-12 0l-33-33-32 33c-4 3-9 3-12 0-4-3-4-9 0-12l32-33-32-32c-4-4-4-9 0-13 3-3 8-3 12 0l32 33 33-33c3-3 9-3 12 0 4 4 4 9 0 13z"/>
|
||||
<glyph glyph-name="settings" unicode="@" d="M352 276c-3 0-6-1-8-2-1 0-1 0-1 0-3 0-6-1-8-2-2 9-6 17-11 25 3 1 6 2 8 4 0 0 0 0 0 0 3 1 5 3 7 5 8 8 8 20 0 28-7 7-20 7-27-1-2-2-4-4-5-7 0 0 0 0 0 0-2-2-4-5-4-8-8 5-16 9-25 11 1 2 2 5 2 8 0 0 0 1 0 1 1 2 2 5 2 8 0 11-9 19-20 19-10 0-19-8-19-19 0-3 1-6 2-8 0-1 0-1 0-1 0-3 1-6 2-8-9-2-18-6-25-11-1 3-2 6-5 8 0 0 0 0 0 0-1 3-2 5-4 7-8 8-20 8-28 0-7-7-7-20 0-27 2-2 5-4 7-5 1 0 1 0 1 0 2-2 4-4 7-4-5-8-8-16-10-25-3 1-6 2-9 2 0 0 0 0 0 0-2 1-5 2-8 2-11 0-20-9-20-20 0-10 9-19 20-19 3 0 6 0 8 2 0 0 0-1 0-1 4 0 6 1 9 3 2-9 5-18 10-25-2-1-5-2-7-5 0 0 0 0 0 0-3-1-6-2-8-4-7-8-7-20 0-28 4-4 9-5 14-5 5 0 10 1 14 5 2 2 4 5 4 7 1 1 1 1 1 1 2 2 3 4 4 7 8-5 16-8 25-10-1-3-2-6-2-9 0 0 0 0 0 0-1-2-2-5-2-8 0-11 9-20 20-20 10 0 19 9 19 20 0 3-1 6-2 8 0 0 0 0 0 0 0 3-1 6-2 9 9 2 17 5 25 10 1-3 2-5 4-7 1 0 1 0 1-1 1-2 2-5 4-7 4-4 9-5 14-5 5 0 10 1 14 5 7 8 7 20 0 28-2 2-5 3-7 4-1 0-1 0-1 0-2 3-4 4-7 5 5 7 8 16 10 25 3-1 6-2 9-2 0 0 0 0 0 0 2-1 5-2 8-2 11 0 19 9 19 19 0 0 0 0 0 0 0 0 0 1 0 1 0 10-9 19-19 19z m-90-61c-22 0-41 19-41 41 0 23 19 41 41 41 23 0 41-18 41-41 0-22-18-41-41-41z"/>
|
||||
<glyph glyph-name="trash" unicode="{" d="M201 302l0-165c0-3-1-5-2-6-2-2-4-3-7-3l-18 0c-3 0-5 1-7 3-2 1-2 3-2 6l0 165c0 2 0 5 2 6 2 2 4 3 7 3l18 0c3 0 5-1 7-3 1-1 2-4 2-6z m73 0l0-165c0-3-1-5-2-6-2-2-4-3-7-3l-18 0c-3 0-5 1-7 3-1 1-2 3-2 6l0 165c0 2 1 5 2 6 2 2 4 3 7 3l18 0c3 0 5-1 7-3 1-1 2-4 2-6z m73 0l0-165c0-3 0-5-2-6-2-2-4-3-7-3l-18 0c-3 0-5 1-7 3-1 1-2 3-2 6l0 165c0 2 1 5 2 6 2 2 4 3 7 3l18 0c3 0 5-1 7-3 2-1 2-4 2-6z m37-207l0 271-256 0 0-271c0-4 1-8 2-12 1-3 3-6 4-7 2-2 3-3 3-3l238 0c0 0 1 1 3 3 1 1 3 4 4 7 1 4 2 8 2 12z m-192 307l128 0-14 34c-1 1-3 2-5 3l-90 0c-2-1-4-2-5-3z m265-9l0-18c0-3-1-5-2-7-2-1-4-2-7-2l-27 0 0-271c0-16-5-30-14-41-9-12-20-17-32-17l-238 0c-12 0-23 5-32 16-9 11-14 25-14 41l0 272-27 0c-3 0-5 1-7 2-1 2-2 4-2 7l0 18c0 3 1 5 2 7 2 1 4 2 7 2l88 0 20 48c3 7 8 13 16 18 7 5 15 7 22 7l92 0c7 0 15-2 22-7 8-5 13-11 16-18l20-48 88 0c3 0 5-1 7-2 1-2 2-4 2-7z"/>
|
||||
<glyph glyph-name="object-group" unicode="" d="M549 402l-37 0 0-292 37 0 0-110-110 0 0 37-366 0 0-37-110 0 0 110 37 0 0 292-37 0 0 110 110 0 0-37 366 0 0 37 110 0z m-74 73l0-36 37 0 0 36z m-475 0l0-36 37 0 0 36z m37-438l0 36-37 0 0-36z m402 36l0 37 36 0 0 292-36 0 0 37-366 0 0-37-36 0 0-292 36 0 0-37z m73-36l0 36-37 0 0-36z m-183 292l110 0 0-219-256 0 0 73-110 0 0 219 256 0z m-219-110l183 0 0 147-183 0z m292-73l0 147-73 0 0-110-110 0 0-37z"/>
|
||||
<glyph glyph-name="cm" unicode="}" d="M202 207c-5 0-10 1-14 4-4 2-8 5-11 10-2 4-4 9-6 15-1 6-2 12-2 20 0 7 1 13 2 19 2 6 4 11 6 16 3 4 7 7 11 10 4 2 9 3 14 3 5 0 9-1 12-2 4-2 6-4 8-6l-8-11c-1 1-3 3-5 4-2 1-4 2-7 2-3 0-5-1-7-3-3-2-4-4-6-7-1-3-2-7-3-11-1-5-1-9-1-14 0-5 0-10 1-14 1-4 2-8 3-11 2-3 3-5 6-7 2-2 5-3 8-3 3 0 5 1 7 2 2 1 4 2 5 4l7-12c-5-5-11-8-20-8z m55 92c4 3 9 5 15 5 4 0 6 0 9-2 2-1 4-2 6-4 1-2 3-4 4-6 1-2 1-4 2-7l0 0c1 2 2 5 4 7 1 2 3 4 5 6 2 2 4 3 7 4 2 1 5 2 8 2 5 0 9-1 12-3 3-2 6-5 7-8 2-4 3-7 4-12 0-4 1-9 1-13l0-58-16 0 0 58c0 3 0 5 0 8-1 3-1 5-2 7-1 2-2 4-4 6-2 1-4 2-7 2-2 0-5-1-7-3-2-1-4-3-5-5-2-3-3-6-4-9-1-4-1-7-1-12l0-52-15 0 0 58c0 3 0 5-1 8 0 3-1 5-1 7-1 3-3 4-4 6-2 1-4 2-7 2-3 0-5-1-7-2-2-2-4-4-6-6-1-3-2-6-3-9-1-3-1-7-1-11l0-53-15 0 0 68c0 4-1 7-1 12 0 4 0 8 0 11l15 0c0-3 0-6 0-9 1-3 1-5 1-7l0 0c1 6 4 10 7 14z"/>
|
||||
<glyph glyph-name="msvg" unicode="~" d="M228 299c4 3 8 5 15 5 3 0 6 0 8-2 2-1 4-2 6-4 2-2 3-4 4-6 1-2 2-4 2-7l1 0c0 2 2 5 3 7 1 2 3 4 5 6 2 2 4 3 7 4 2 1 5 2 9 2 4 0 8-1 12-3 3-2 5-5 7-8 1-4 2-7 3-12 1-4 1-9 1-13l0-58-16 0 0 58c0 3 0 5 0 8 0 3-1 5-2 7-1 2-2 4-4 6-1 1-4 2-7 2-2 0-5-1-7-3-2-1-4-3-5-5-2-3-3-6-4-9 0-4-1-7-1-12l0-52-15 0 0 58c0 3 0 5 0 8-1 3-1 5-2 7-1 3-2 4-4 6-2 1-4 2-7 2-2 0-5-1-7-2-2-2-4-4-5-6-2-3-3-6-4-9-1-3-1-7-1-11l0-53-15 0 0 68c0 4 0 7 0 12 0 4-1 8-1 11l15 0c0-3 0-6 1-9 0-3 0-5 0-7l0 0c1 6 4 10 8 14z"/>
|
||||
<glyph glyph-name="deg" unicode="\" d="M199 210l0 14-1 0c-1-4-4-9-7-12-3-3-8-5-14-5-5 0-9 1-12 4-4 2-7 6-9 10-3 4-5 9-6 15-1 6-2 13-2 20 0 7 1 13 2 19 1 6 3 11 5 16 3 4 6 7 10 10 3 2 7 3 12 3 5 0 10-1 13-4 4-3 7-7 8-12l0 0 0 59 16 0 0-137z m0 45c0 5-1 10-1 14-1 5-2 8-4 11-1 3-3 6-5 8-2 2-5 2-8 2-3 0-5 0-8-2-2-2-3-5-5-8-1-3-2-6-3-11-1-4-1-9-1-14 0-4 0-9 1-13 1-4 2-8 3-11 2-3 3-6 5-8 3-1 5-2 8-2 3 0 6 1 8 2 2 2 4 5 5 8 2 3 3 7 4 11 0 4 1 9 1 13z m49-3c0-5 1-9 1-13 1-3 2-7 4-9 1-3 3-5 5-7 3-2 5-2 8-2 5 0 8 1 10 3 3 3 5 6 6 9l12-6c-3-6-6-11-11-15-4-3-10-5-17-5-10 0-19 4-24 13-6 8-9 20-9 35 0 8 0 14 2 20 2 6 4 11 7 16 3 4 6 7 10 10 4 2 9 3 14 3 5 0 10-1 14-3 4-3 7-6 9-10 3-4 5-9 6-14 1-6 1-12 1-18l0-7-48 0z m33 12c0 8-2 14-4 19-3 5-6 7-12 7-3 0-6 0-8-2-2-2-3-4-5-7-1-3-2-6-3-9 0-3-1-6-1-8z m79 37l15 0 0-91c0-6-1-12-2-17-1-5-3-10-6-14-2-4-6-7-10-9-5-2-10-3-16-3-7 0-12 1-18 4-5 2-10 6-13 10l9 11c3-3 6-6 10-8 3-2 8-3 12-3 4 0 7 0 9 2 3 1 5 3 6 6 1 3 2 6 3 9 1 4 1 8 1 12l0 14-1 0c-1-5-3-9-7-12-3-3-7-5-14-5-4 0-8 1-12 4-4 2-7 6-9 10-3 4-4 9-6 15-1 6-2 13-2 20 0 7 1 13 2 19 1 6 3 11 6 15 2 5 5 8 9 10 3 3 8 4 12 4 6 0 10-2 14-5 3-3 6-6 7-11l1 0 0 13z m0-45c0 4 0 9-1 13-1 5-2 8-3 11-2 4-4 6-6 8-2 2-5 3-8 3-3 0-5-1-7-3-2-2-4-4-5-8-2-3-3-6-4-11 0-4-1-9-1-13 0-5 1-10 1-14 1-4 2-8 4-11 1-3 3-6 5-8 2-1 4-2 7-2 3 0 6 1 8 2 2 2 4 5 6 8 1 3 2 7 3 11 1 4 1 9 1 14z"/>
|
||||
<glyph glyph-name="px" unicode="|" d="M206 301l0-14 1 0c1 5 3 9 7 12 3 3 8 5 14 5 4 0 9-1 12-4 4-2 7-6 9-10 3-4 5-9 6-15 1-6 2-13 2-20 0-7-1-13-2-19-1-6-3-11-6-15-2-5-5-8-9-10-3-3-7-4-12-4-5 0-10 1-14 5-3 3-6 7-7 11l0 0 0-56-16 0 0 134z m0-45c0-5 1-10 1-14 1-4 2-8 4-11 1-3 3-6 5-8 2-1 5-2 8-2 3 0 5 1 7 2 3 2 4 5 6 8 1 3 2 7 3 11 0 4 1 9 1 14 0 4-1 9-1 13-1 5-2 8-3 11-2 3-3 6-6 8-2 2-4 2-7 2-3 0-6 0-8-2-2-2-4-5-5-8-2-3-3-6-4-11 0-4-1-9-1-13z m86 2l-23 43 17 0 15-32 13 32 17 0-22-43 24-48-17 0-16 36-16-36-17 0z"/>
|
||||
<glyph glyph-name="m-sq" unicode="" d="M204 299c4 3 8 5 15 5 3 0 6 0 8-2 2-1 4-2 6-4 2-2 3-4 4-6 1-2 2-4 2-7l1 0c0 2 2 5 3 7 1 2 3 4 5 6 2 2 4 3 7 4 2 1 5 2 9 2 4 0 8-1 12-3 3-2 5-5 7-8 1-4 2-7 3-12 1-4 1-9 1-13l0-58-16 0 0 58c0 3 0 5 0 8 0 3-1 5-2 7-1 2-2 4-4 6-1 1-4 2-7 2-2 0-5-1-7-3-2-1-4-3-5-5-2-3-3-6-4-9 0-4-1-7-1-12l0-52-15 0 0 58c0 3 0 5 0 8-1 3-1 5-2 7-1 3-2 4-4 6-2 1-4 2-7 2-2 0-5-1-7-2-2-2-4-4-5-6-2-3-3-6-4-9-1-3-1-7-1-11l0-53-15 0 0 68c0 4 0 7 0 12 0 4-1 8-1 11l15 0c0-3 0-6 1-9 0-3 0-5 0-7l0 0c1 6 4 10 8 14z m129 44c0-2-1-4-1-5-1-2-1-4-2-6-1-1-2-3-3-5-2-2-3-3-4-5l-14-20 23 0 0-7-32 0 0 8 18 24c2 3 3 6 4 8 2 3 2 5 2 8 0 3-1 5-2 7-1 3-3 4-6 4-2 0-4-1-6-3-2-2-3-4-3-7l-8 1c1 5 3 9 6 12 3 2 7 4 12 4 2 0 4-1 6-2 2-1 4-2 5-3 2-2 3-4 3-6 1-2 2-4 2-7z"/>
|
||||
<glyph glyph-name="m-cubed" unicode="" d="M204 299c4 3 8 5 15 5 3 0 6 0 8-2 2-1 4-2 6-4 2-2 3-4 4-6 1-2 2-4 2-7l1 0c0 2 2 5 3 7 1 2 3 4 5 6 2 2 4 3 7 4 2 1 5 2 9 2 4 0 8-1 12-3 3-2 5-5 7-8 1-4 2-7 3-12 1-4 1-9 1-13l0-58-16 0 0 58c0 3 0 5 0 8 0 3-1 5-2 7-1 2-2 4-4 6-1 1-4 2-7 2-2 0-5-1-7-3-2-1-4-3-5-5-2-3-3-6-4-9 0-4-1-7-1-12l0-52-15 0 0 58c0 3 0 5 0 8-1 3-1 5-2 7-1 3-2 4-4 6-2 1-4 2-7 2-2 0-5-1-7-2-2-2-4-4-5-6-2-3-3-6-4-9-1-3-1-7-1-11l0-53-15 0 0 68c0 4 0 7 0 12 0 4-1 8-1 11l15 0c0-3 0-6 1-9 0-3 0-5 0-7l0 0c1 6 4 10 8 14z m130 14c0-3-1-5-2-7 0-3-2-5-3-7-2-1-3-3-6-4-2-1-4-1-7-1-5 0-8 1-11 3-4 2-6 6-7 10l7 2c1-2 2-4 4-6 2-1 4-2 7-2 1 0 3 0 4 1 1 1 2 2 3 3 1 1 2 2 2 4 1 1 1 3 1 4 0 4-1 7-3 9-3 2-5 3-9 3l-2 0 0 7 2 0c3 0 6 1 8 3 1 2 2 5 2 8 0 2 0 3 0 4 0 1-1 2-2 3 0 1-1 2-2 3-1 1-3 1-4 1-2 0-4-1-5-2-2-1-3-2-4-4l-7 2c1 2 2 3 3 5 1 1 3 2 4 3 1 1 3 2 4 2 2 1 4 1 5 1 3 0 5-1 7-1 2-1 3-2 5-4 1-1 2-3 3-5 1-2 1-4 1-7 0-2 0-3 0-5-1-2-1-3-2-4-1-2-2-3-3-4-1-1-3-1-4-2l0 0c3-1 6-3 8-6 2-2 3-6 3-10z"/>
|
||||
<glyph glyph-name="acceleration" unicode="" d="M207 350c3 2 8 3 14 3 3 0 5 0 8-1 2 0 4-1 5-2 2-1 3-2 4-4 1-1 2-3 2-4l0 0c1 1 2 3 3 4 2 1 3 3 5 4 2 1 4 2 7 2 2 1 5 1 8 1 5 0 8 0 11-1 3-2 5-3 7-5 1-2 2-5 3-7 1-3 1-6 1-8l0-35-15 0 0 35c0 1 0 3 0 4 0 2-1 4-2 5-1 1-2 2-3 3-2 1-4 1-7 1-2 0-4 0-6-1-2-1-4-2-6-4-1-1-2-3-3-5-1-2-1-4-1-7l0-31-14 0 0 35c0 1-1 3-1 4 0 2-1 3-1 5-1 1-2 2-4 3-2 1-4 1-7 1-2 0-4 0-6-1-2-1-4-2-5-4-2-1-3-3-3-5-1-2-2-4-2-7l0-32-14 0 0 41c0 3 0 5 0 8 0 2 0 4-1 6l14 0c0-1 1-3 1-5 0-2 0-3 0-4l0 0c2 3 4 6 8 8z m112-92c0-2 0-4-1-5 0-1-1-3-2-4 0-2-1-3-2-4-1-2-2-3-3-5l-11-15 18 0 0-6-25 0 0 6 14 20c2 2 3 4 4 6 1 2 1 4 1 6 0 3 0 5-1 6-1 2-3 3-5 3-2 0-4-1-5-2-1-2-2-3-2-6l-7 1c1 4 3 7 5 9 2 2 5 3 9 3 2 0 4 0 6-1 1 0 3-1 4-3 1-1 2-2 2-4 1-2 1-3 1-5z m-112-18c-1 1-2 2-3 3-1 1-3 1-5 1-1 0-3 0-4-2-1-1-2-3-2-5 0-2 1-3 2-4 1-1 3-2 6-3 1-1 2-1 3-2 2-1 3-2 4-3 1-1 2-2 2-4 1-1 1-3 1-5 0-2 0-4-1-6-1-2-2-3-3-5-1-1-3-2-4-2-2-1-4-1-6-1-3 0-5 0-7 1-3 1-4 3-6 5l5 5c1-2 2-3 3-4 2 0 3-1 5-1 2 0 4 1 5 2 1 2 2 3 2 6 0 1 0 2-1 3 0 1-1 2-2 2 0 1-1 1-2 2-1 0-2 1-3 1-2 1-3 1-4 2-1 0-2 1-3 2-1 1-1 2-2 4 0 1-1 3-1 5 0 2 1 4 1 5 1 2 2 3 3 5 1 1 3 2 4 2 2 1 4 1 6 1 2 0 5 0 7-1 2-1 3-2 5-4z m18-16c0-2 0-4 0-6 1-2 1-4 2-5 1-1 2-3 3-3 1-1 3-2 4-2 2 0 4 1 5 2 1 2 2 3 3 5l6-3c-2-3-3-6-6-8-2-1-5-2-8-2-5 0-10 2-13 6-3 4-4 10-4 18 0 4 0 7 1 10 1 3 2 6 4 8 1 2 3 3 5 5 2 1 4 1 7 1 2 0 5 0 7-1 2-2 3-3 5-5 1-2 2-5 2-7 1-3 1-6 1-9l0-4-24 0z m16 6c0 4 0 7-2 10-1 2-3 4-6 4-1 0-3-1-4-2-1-1-2-2-2-3-1-2-1-3-2-5 0-1 0-2 0-4l16 0z m31-28c-3 0-5 0-7 1-2 2-4 3-5 5-2 3-3 5-3 8-1 3-1 6-1 10 0 4 0 7 1 10 0 3 1 5 3 8 1 2 3 3 5 5 2 1 5 1 7 1 3 0 5 0 6-1 2-1 3-1 4-2l-4-6c0 1-1 1-2 2-1 0-2 1-4 1-1 0-2-1-3-2-1-1-2-2-3-3-1-2-1-4-2-6 0-2 0-4 0-7 0-2 0-5 0-7 1-2 1-4 2-5 1-2 2-3 3-4 1-1 2-1 4-1 1 0 3 0 4 1 1 0 1 1 2 2l4-6c-3-3-6-4-11-4z m17 74l-107 0c-2 0-4 2-4 4 0 2 2 4 4 4l107 0c2 0 4-2 4-4 0-2-2-4-4-4z"/>
|
||||
<glyph glyph-name="particles" unicode="" d="M332 229c0 12 10 23 23 23 13 0 23-11 23-23 0-13-10-24-23-24-13 0-23 11-23 24z m-54-68c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z m-62-18c0 13 11 23 24 23 12 0 23-10 23-23 0-13-11-23-23-23-13 0-24 10-24 23z m-46 60c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z m81-138c-5 3-8 9-9 15-1 6 1 12 4 17 4 5 9 8 15 9 6 1 13 0 18-4 5-4 8-9 9-15 1-6-1-13-4-18-4-4-9-8-16-9-6-1-12 1-17 5z m-183 201c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z m111 46c0 13 10 24 23 24 13 0 23-11 23-24 0-12-10-23-23-23-13 0-23 11-23 23z m71-94c0 13 11 23 24 23 12 0 23-10 23-23 0-13-11-23-23-23-13 0-24 10-24 23z m163 52c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-12 0-23 10-23 23z m-111 38c0 13 11 23 23 23 13 0 24-10 24-23 0-13-11-23-24-23-12 0-23 10-23 23z m-170 90c0 13 11 24 24 24 12 0 23-11 23-24 0-12-11-23-23-23-13 0-24 11-24 23z m235-23c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z"/>
|
||||
<glyph glyph-name="voxels" unicode="" d="M434 379l-85 49c-4 2-10 2-14 0l-77-45-79 46c-4 2-10 2-14 0l-85-49c-4-3-7-7-7-12l0-98c0-5 3-10 7-13l78-45 0-89c0-5 3-10 7-12l85-49c2-2 5-2 7-2 2 0 5 0 7 2l85 49c4 2 7 7 7 12l0 88 78 45c5 3 7 7 7 12l0 99c0 5-2 9-7 12z m-21-88l-59 34 0 68 59-35z m-69-55l-73 42 0 80 59 35 0-68-29-17c-6-3-8-11-4-16 2-4 6-6 10-6 2 0 4 0 6 1l29 17 60-34z m-75-57l0 66 59-34 0-66z m-26 113l-59 34 0 68 59-34z m-142 68l59 34 0-68-31-18c-6-3-8-11-5-17 2-3 7-6 11-6 2 0 4 1 6 2l31 18 59-34-60-34-70 41z m156-270l-71 41 0 81 59 34 0-67-28-16c-6-3-8-11-5-17 3-3 7-6 11-6 2 0 4 1 6 2l28 16 59-34z"/>
|
||||
<glyph glyph-name="lock" unicode="" d="M389 233l0 62c0 68-55 124-123 124-69 0-124-56-124-124l0-62c-24-4-44-26-44-52l0-74c0-29 24-52 52-52l230 0c29 0 53 23 53 52l0 74c0 26-18 48-44 52z m-123 129c37 0 67-30 67-67l0-61-135 0 0 61c0 37 31 67 68 67z"/>
|
||||
<glyph glyph-name="visible" unicode="" d="M258 116c-55 0-106 17-147 51-31 25-47 51-47 52-4 7-4 16 1 23 2 4 66 98 195 96 133-3 192-93 195-97 4-6 4-15 0-22 0-1-15-27-46-53-29-23-79-50-151-50 0 0 0 0 0 0z m-148 113c7-7 17-18 30-29 34-27 73-40 118-40 0 0 0 0 0 0 47 0 88 13 122 40 13 10 23 21 29 29-7 7-16 16-30 26-34 25-74 38-119 38-81 2-130-42-150-64z m-27 1z m227-4c0-25-21-46-47-46-26 0-47 21-47 46 0 26 21 47 47 47 26 0 47-21 47-47z"/>
|
||||
<glyph glyph-name="model" unicode="" d="M494 395c-2 5-8 8-13 7l-90-16 45 72c3 5 2 11-1 15-4 4-10 5-15 3l-213-98c-15 5-72 27-111 43 0 0-1 0-1 0 0 0 0 0 0 0 0 0-1 0-1 1 0 0-1 0-1 0 0 0-1 0-1 0 0 0 0 0 0 0-1 0-1 0-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0 0 0-1-1 0-1 0-2 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0-1 0-1-1 0-2 0-3-1 0 0 0 0 0 0 0-1-1-1-1-1 0 0 0 0 0 0 0 0 0-1 0-1-1 0-1 0-1 0 0 0 0 0 0-1 0 0 0 0-1-1l-27-52-33-40c-3-4-3-10 0-15 2-3 6-5 10-5 1 0 2 0 4 1l50 17 40 2-26-51c-3-4-2-9 1-13 1-1 26-30 52-58 15-17 28-30 38-40 6-6 11-11 15-14l-16-61-46-18c-6-3-9-10-6-16 2-5 6-8 11-8 1 0 3 1 4 1l45 18 17-15c2-3 5-4 8-4 4 0 7 2 9 5 5 5 4 12-1 17l-17 15 16 61 76-90c2-2 6-4 9-4 1 0 2 0 3 0l85 23c5 2 8 6 9 11 0 5-2 9-7 12l-136 72 45 91 178 123c5 3 6 9 4 15z m-200-117l-122 55 41 21 181 83z m-148 73l-24 33c16-6 39-15 54-21z m-59-6l-9 13 15 29 27-38 2-2z m36-77l23 44c18-45 35-91 47-121-19 20-45 49-70 77z m194-194l-57 68 105-55z m-94 101c-5 14-42 104-30 77 0-2-21 49-28 66l121-59-48-95z m108 120l43 63 70 16z"/>
|
||||
<glyph glyph-name="forward" unicode="D" d="M330 278l-95 70c-5 4-12 5-18 2-5-3-9-9-9-16l0-150c0-7 4-13 10-16 2-1 5-2 7-2 4 0 8 2 11 5l95 79c4 4 6 9 6 14-1 5-3 10-7 14"/>
|
||||
<glyph glyph-name="avatar-2" unicode="" d="M256 88c-93 0-169 75-169 168 0 93 76 169 169 169 93 0 169-76 169-169 0-93-76-168-169-168z m0 316c-81 0-148-66-148-148 0-81 67-147 148-147 81 0 148 66 148 147 0 82-67 148-148 148z m97-90l-1 1c-3 3-7 4-10 4-1 0-61-9-86-9-1 0-1 0-2 0-25 0-87 10-87 10-5 0-10-2-13-6l-1-2c-2-3-2-7-1-10 1-4 3-6 6-8 12-5 49-20 60-22 2 0 5 0 6-7 1-8-3-46-7-65-5-17-13-40-13-41-2-6 1-13 7-15l8-3c3-1 6-1 9 1 3 1 5 4 6 7l21 65 20-67c1-3 3-6 6-7 2-1 4-1 5-1 2 0 3 0 5 0l7 3c5 2 8 8 7 14 0 0-6 24-11 44-3 12-4 30-5 45 0 9-1 16-2 22 0 1 0 4 5 5 0 0 1 0 2 0l55 22c4 2 6 5 7 9 1 4 0 8-3 11z m-68 37c0-16-13-29-29-29-16 0-29 13-29 29 0 16 13 29 29 29 16 0 29-13 29-29z"/>
|
||||
<glyph glyph-name="arrow-dn" unicode="5" d="M258 219l-43 55 86 0z"/>
|
||||
<glyph glyph-name="arrow-up" unicode="6" d="M258 283l43-55-86 0z"/>
|
||||
<glyph glyph-name="time" unicode="" d="M256 390c-73 0-132-59-132-132 0-73 59-132 132-132 73 0 132 59 132 132 0 73-59 132-132 132z m60-162l1 0-64 0c-2 0-4 0-6 1-8 2-15 10-15 19l0 0 2 92c0 11 9 20 19 20 11 0 20-9 20-20l-1-72 44 0c11 0 20-9 20-20 0-10-9-20-20-20z"/>
|
||||
<glyph glyph-name="transparency" unicode="" d="M349 349c1 0 3-1 3-3l0-181c0-2-2-3-3-3l-182 0c-1 0-3 1-3 3l0 181c0 2 2 3 3 3l182 0m0 17l-182 0c-11 0-20-9-20-20l0-181c0-11 9-20 20-20l182 0c11 0 20 9 20 20l0 181c0 11-9 20-20 20z m-187-108l96 0 0-96-96 0z m94 94l96 0 0-95-96 0z"/>
|
||||
<glyph glyph-name="unmuted" unicode="G" d="M298 255c-1-8-2-17-3-25-2-7-4-15-6-22-2-6-1-11 3-14 7-6 16-3 19 6 8 23 12 47 9 71-1 14-4 27-9 40-3 7-12 10-18 5-5-3-7-8-4-14 5-15 8-31 9-47m-35 81c0 12-5 20-16 24-7 3-19 2-27-7-11-12-22-24-34-37-1-1-3-1-5-2-4 0-7 0-11 0-3 0-5 0-8 0-15 0-26-10-26-23 0-28 0-42 0-71 0-11 10-22 21-23 4 0 9 0 14 0 3-1 7-1 10-1 2 0 4-1 5-2 10-11 22-23 33-36 6-6 12-9 20-9 3 0 5 1 8 2 11 3 16 12 16 24 0 22 0 39 0 59l0 43c0 19 0 59 0 59m-26-3l0-155-1 1c-3 3-5 5-7 7l-8 9c-8 9-16 17-23 25-2 2-4 2-6 2-4 1-9 1-13 1-2 0-4 0-7 0l-9 0 0 65 2 0c3 0 5 0 8 0 6 0 12 0 18 0 0 0 0 0 0 0 3 0 5 1 8 4 10 10 20 21 30 32z m140-78c0 3-1 6-1 10-1 7-1 14-2 22-3 15-7 31-14 47-2 4-5 7-9 8-4 1-8 0-11-3-5-4-6-10-4-16 9-22 13-42 14-64 1-24-4-49-14-72-1-4-2-8 0-11 1-3 4-6 7-7 2-1 4-1 5-1 6 0 10 3 13 9 10 25 15 51 16 78z"/>
|
||||
<glyph glyph-name="user" unicode="" d="M257 406c-83 0-151-68-151-151 0-83 68-150 151-150 83 0 150 67 150 150 0 83-67 151-150 151z m0-282c-73 0-132 59-132 131 0 73 59 132 132 132 73 0 132-59 132-132 0-72-59-131-132-131z m45 179c0-24-19-43-42-43-24 0-43 19-43 43 0 23 19 42 43 42 23 0 42-19 42-42z m-10-71l-62 0c-25 0-53-12-53-37l0-18c26-23 50-32 77-32 32 0 69 14 85 33l0 17c0 25-22 37-47 37z"/>
|
||||
<glyph glyph-name="edit-pencil" unicode="" d="M341 403c9 9 18 19 28 28 4 4 9 3 12-1 17-16 33-32 50-49 3-4 4-8 0-12-10-10-20-19-29-29-20 21-41 42-61 63z m-25-23c-1-2-2-4-3-5-9-9-17-17-26-26-49-49-153-152-175-175-5-5-9-11-10-19-3-22-6-43-10-66 3 1 4 1 6 1 20 4 41 8 62 13 4 1 8 3 11 6 21 21 109 108 163 162 13 14 27 28 41 42 2 1 3 3 4 3-21 21-41 42-63 64z m-158-231c-6 6-12 12-17 17 21 21 137 137 177 177 5-5 11-11 16-17-7-7-14-14-21-22-50-50-138-137-155-155z"/>
|
||||
<glyph glyph-name="muted" unicode="H" d="M377 274l-57-57c-5-5-13-5-18 0-5 5-5 13 0 18l57 57c5 5 13 5 18 0 5-5 5-13 0-18m-18-57l-57 57c-5 5-5 13 0 18 5 5 13 5 18 0l57-57c5-5 5-13 0-18-5-5-13-5-18 0m-95 120c0 12-6 21-17 25-8 3-20 3-29-7-11-12-23-25-35-37-1-2-3-2-4-3-4 0-8 0-12 0-3 0-6 0-8 0-15 0-27-11-27-24 0-29 0-42 0-73 0-11 10-22 21-23 5-1 10-1 15-1 4 0 7 0 11 0 1 0 3-1 4-2 11-12 23-24 35-37 5-7 12-10 20-10 3 0 6 1 9 2 11 4 17 13 17 25 0 22 0 40 0 60l0 45c0 20 0 60 0 60m-28-3l0-159-1 1c-3 3-5 5-7 8l-9 9c-8 9-16 17-24 26-1 1-3 2-5 2-5 0-9 0-14 0-2 0-5 0-7 0l-10 0 0 67 3 0c3 0 5 0 8 0 6 0 12 0 19 0 0 0 0 0 0 0 3 0 5 1 8 4 10 11 20 22 31 34z"/>
|
||||
<glyph glyph-name="vol-0" unicode="" d="M109 352c-2-2-5-4-8-4-7 0-13 0-18 0-2 0-4 0-7 0-2 0-4 0-6 0-23 0-41-16-41-37 0-44 0-65 0-113 1-17 15-33 32-35 8-1 16-1 23-1l1 0c5 0 11 0 16 0 3 0 6-2 8-4 15-15 35-37 54-58 8-9 18-14 30-14 4 0 8 1 12 2 16 6 23 19 23 39l0 162c0 31 2 94 2 95 0 19-9 31-25 37-11 5-29 4-42-10-20-23-37-42-54-59z m66 14l15 18 0-259-4 7c-2 2-4 4-6 6-1 2-3 4-5 6l-13 14c-3 3-6 6-9 10-9 10-18 20-27 30-2 2-6 3-8 3-6 0-13 0-21 0l-29 0 0 106 7 0c3 0 5 0 8 0 1 0 2 0 4 0 5 0 11 0 16 0 4 0 9 0 13 0l1 0c4 0 7 2 11 6 7 8 14 16 21 23 8 10 17 20 26 30z"/>
|
||||
<glyph glyph-name="vol-1" unicode="" d="M109 352c-2-2-5-4-8-4-7 0-13 0-18 0-2 0-4 0-7 0-2 0-4 0-6 0-23 0-41-16-41-37 0-44 0-65 0-113 1-17 15-33 32-35 8-1 16-1 23-1l1 0c5 0 11 0 16 0 3 0 6-2 8-4 15-15 35-37 54-58 8-9 18-14 30-14 4 0 8 1 12 2 16 6 23 19 23 39l0 162c0 31 2 94 2 95 0 19-9 31-25 37-11 5-29 4-42-10-20-23-37-42-54-59z m66 14l15 18 0-259-4 7c-2 2-4 4-6 6-1 2-3 4-5 6l-13 14c-3 3-6 6-9 10-9 10-18 20-27 30-2 2-6 3-8 3-6 0-13 0-21 0l-29 0 0 106 7 0c3 0 5 0 8 0 1 0 2 0 4 0 5 0 11 0 16 0 4 0 9 0 13 0l1 0c4 0 7 2 11 6 7 8 14 16 21 23 8 10 17 20 26 30z m89-110c0 32-15 58-34 58-19 0-34-26-34-58 0-32 15-58 34-58 19 0 34 26 34 58z"/>
|
||||
<glyph glyph-name="vol-2" unicode="" d="M304 254c-2-13-3-26-5-39-2-12-6-24-10-36-3-10-1-17 5-22 12-9 26-5 31 9 14 37 19 75 15 115-3 21-8 43-16 63-4 12-18 16-28 9-8-5-10-13-7-24 9-24 14-49 15-75m-195 98c-2-2-5-4-8-4-7 0-13 0-18 0-2 0-4 0-7 0-2 0-4 0-6 0-23 0-41-16-41-37 0-44 0-65 0-113 1-17 15-33 32-35 8-1 16-1 23-1l1 0c5 0 11 0 16 0 3 0 6-2 8-4 15-15 35-37 54-58 8-9 18-14 30-14 4 0 8 1 12 2 16 6 23 19 23 39l0 162c0 31 2 94 2 95 0 19-9 31-25 37-11 5-29 4-42-10-20-23-37-42-54-59z m66 14l15 18 0-259-4 7c-2 2-4 4-6 6-1 2-3 4-5 6l-13 14c-3 3-6 6-9 10-9 10-18 20-27 30-2 2-6 3-8 3-6 0-13 0-21 0l-29 0 0 106 7 0c3 0 5 0 8 0 1 0 2 0 4 0 5 0 11 0 16 0 4 0 9 0 13 0l1 0c4 0 7 2 11 6 7 8 14 16 21 23 8 10 17 20 26 30z m89-110c0 32-15 58-34 58-19 0-34-26-34-58 0-32 15-58 34-58 19 0 34 26 34 58z"/>
|
||||
<glyph glyph-name="vol-3" unicode="" d="M304 254c-2-13-3-26-5-39-2-12-6-24-10-36-3-10-1-17 5-22 12-9 26-5 31 9 14 37 19 75 15 115-3 21-8 43-16 63-4 12-18 16-28 9-8-5-10-13-7-24 9-24 14-49 15-75m-195 98c-2-2-5-4-8-4-7 0-13 0-18 0-2 0-4 0-7 0-2 0-4 0-6 0-23 0-41-16-41-37 0-44 0-65 0-113 1-17 15-33 32-35 8-1 16-1 23-1l1 0c5 0 11 0 16 0 3 0 6-2 8-4 15-15 35-37 54-58 8-9 18-14 30-14 4 0 8 1 12 2 16 6 23 19 23 39l0 162c0 31 2 94 2 95 0 19-9 31-25 37-11 5-29 4-42-10-20-23-37-42-54-59z m66 14l15 18 0-259-4 7c-2 2-4 4-6 6-1 2-3 4-5 6l-13 14c-3 3-6 6-9 10-9 10-18 20-27 30-2 2-6 3-8 3-6 0-13 0-21 0l-29 0 0 106 7 0c3 0 5 0 8 0 1 0 2 0 4 0 5 0 11 0 16 0 4 0 9 0 13 0l1 0c4 0 7 2 11 6 7 8 14 16 21 23 8 10 17 20 26 30z m245-112c-1 6-1 11-2 16-1 11-2 23-3 35-4 25-12 51-22 76-3 6-8 11-14 12-7 2-13 1-19-4-8-6-10-15-5-26 13-34 21-67 21-102 1-39-6-78-22-116-2-6-3-13 0-18 2-5 6-9 12-11 2-1 5-2 8-2 8 0 16 6 20 16 16 39 25 81 26 124z m-156 2c0 32-15 58-34 58-19 0-34-26-34-58 0-32 15-58 34-58 19 0 34 26 34 58z"/>
|
||||
<glyph glyph-name="vol-4" unicode="" d="M304 254c-2-13-3-26-5-39-2-12-6-24-10-36-3-10-1-17 5-22 12-9 26-5 31 9 14 37 19 75 15 115-3 21-8 43-16 63-4 12-18 16-28 9-8-5-10-13-7-24 9-24 14-49 15-75m-195 98c-2-2-5-4-8-4-7 0-13 0-18 0-2 0-4 0-7 0-2 0-4 0-6 0-23 0-41-16-41-37 0-44 0-65 0-113 1-17 15-33 32-35 8-1 16-1 23-1l1 0c5 0 11 0 16 0 3 0 6-2 8-4 15-15 35-37 54-58 8-9 18-14 30-14 4 0 8 1 12 2 16 6 23 19 23 39l0 162c0 31 2 94 2 95 0 19-9 31-25 37-11 5-29 4-42-10-20-23-37-42-54-59z m66 14l15 18 0-259-4 7c-2 2-4 4-6 6-1 2-3 4-5 6l-13 14c-3 3-6 6-9 10-9 10-18 20-27 30-2 2-6 3-8 3-6 0-13 0-21 0l-29 0 0 106 7 0c3 0 5 0 8 0 1 0 2 0 4 0 5 0 11 0 16 0 4 0 9 0 13 0l1 0c4 0 7 2 11 6 7 8 14 16 21 23 8 10 17 20 26 30z m245-112c-1 6-1 11-2 16-1 11-2 23-3 35-4 25-12 51-22 76-3 6-8 11-14 12-7 2-13 1-19-4-8-6-10-15-5-26 13-34 21-67 21-102 1-39-6-78-22-116-2-6-3-13 0-18 2-5 6-9 12-11 2-1 5-2 8-2 8 0 16 6 20 16 16 39 25 81 26 124z m-156 2c0 32-15 58-34 58-19 0-34-26-34-58 0-32 15-58 34-58 19 0 34 26 34 58z m173-186c-4 0-7 1-10 3-10 5-13 18-8 27 45 79 61 208-4 322-6 9-3 22 7 27 10 6 22 2 27-7 74-127 55-273 5-362-4-6-10-10-17-10z"/>
|
||||
<glyph glyph-name="vol-x-0" unicode="" d="M209 196l-57 58 56 57c9 9 9 22 0 31-8 8-22 8-30-1l-57-57-57 58c-8 8-22 8-30-1-9-8-9-22 0-30l57-58-53-58c-9-9-9-22 0-31 8-8 22-8 30 0l54 58 57-57c8-8 22-8 30 0 9 9 8 23 0 31z"/>
|
||||
<glyph glyph-name="vol-x-1" unicode="" d="M248 305l-48-48 48-50c10 10 16 28 16 49 0 21-6 39-16 49z m-39-109l-57 58 56 57c9 9 9 22 0 31-8 8-22 8-30-1l-57-57-57 58c-8 8-22 8-30-1-9-8-9-22 0-30l57-58-53-58c-9-9-9-22 0-31 8-8 22-8 30 0l54 58 57-57c8-8 22-8 30 0 9 9 8 23 0 31z"/>
|
||||
<glyph glyph-name="vol-x-2" unicode="" d="M304 254c-2-13-3-26-5-39-2-12-6-24-10-36-3-10-1-17 5-22 12-9 26-5 31 9 14 37 19 75 15 115-3 21-8 43-16 63-4 12-18 16-28 9-8-5-10-13-7-24 9-24 14-49 15-75m-56 51l-48-48 48-50c10 10 16 28 16 49 0 21-6 39-16 49z m-39-109l-57 58 56 57c9 9 9 22 0 31-8 8-22 8-30-1l-57-57-57 58c-8 8-22 8-30-1-9-8-9-22 0-30l57-58-53-58c-9-9-9-22 0-31 8-8 22-8 30 0l54 58 57-57c8-8 22-8 30 0 9 9 8 23 0 31z"/>
|
||||
<glyph glyph-name="vol-x-3" unicode="" d="M304 254c-2-13-3-26-5-39-2-12-6-24-10-36-3-10-1-17 5-22 12-9 26-5 31 9 14 37 19 75 15 115-3 21-8 43-16 63-4 12-18 16-28 9-8-5-10-13-7-24 9-24 14-49 15-75m116 0c-1 6-1 11-2 16-1 11-2 23-3 35-4 25-12 51-22 76-3 6-8 11-14 12-7 2-13 1-19-4-8-6-10-15-5-26 13-34 21-67 21-102 1-39-6-78-22-116-2-6-3-13 0-18 2-5 6-9 12-11 2-1 5-2 8-2 8 0 16 6 20 16 16 39 25 81 26 124z m-172 51l-48-48 48-50c10 10 16 28 16 49 0 21-6 39-16 49z m-39-109l-57 58 56 57c9 9 9 22 0 31-8 8-22 8-30-1l-57-57-57 58c-8 8-22 8-30-1-9-8-9-22 0-30l57-58-53-58c-9-9-9-22 0-31 8-8 22-8 30 0l54 58 57-57c8-8 22-8 30 0 9 9 8 23 0 31z"/>
|
||||
<glyph glyph-name="vol-x-4" unicode="" d="M304 254c-2-13-3-26-5-39-2-12-6-24-10-36-3-10-1-17 5-22 12-9 26-5 31 9 14 37 19 75 15 115-3 21-8 43-16 63-4 12-18 16-28 9-8-5-10-13-7-24 9-24 14-49 15-75m116 0c-1 6-1 11-2 16-1 11-2 23-3 35-4 25-12 51-22 76-3 6-8 11-14 12-7 2-13 1-19-4-8-6-10-15-5-26 13-34 21-67 21-102 1-39-6-78-22-116-2-6-3-13 0-18 2-5 6-9 12-11 2-1 5-2 8-2 8 0 16 6 20 16 16 39 25 81 26 124z m17-184c-4 0-7 1-10 3-10 5-13 18-8 27 45 79 61 208-4 322-6 9-3 22 7 27 10 6 22 2 27-7 74-127 55-273 5-362-4-6-10-10-17-10z m-189 235l-48-48 48-50c10 10 16 28 16 49 0 21-6 39-16 49z m-39-109l-57 58 56 57c9 9 9 22 0 31-8 8-22 8-30-1l-57-57-57 58c-8 8-22 8-30-1-9-8-9-22 0-30l57-58-53-58c-9-9-9-22 0-31 8-8 22-8 30 0l54 58 57-57c8-8 22-8 30 0 9 9 8 23 0 31z"/>
|
||||
<glyph glyph-name="share-ext" unicode="" d="M135 133c0 71 0 127 0 198 51 0 85 0 136 0 1 0 1-1 1-1-4-3-7-7-11-10-4-4-8-10-13-12-5-2-11 0-17 0-28 0-39 0-67 0-2 0-3 0-5 0 0-56 0-96 0-151 55 0 94 0 149 0 0 2 0 3 0 5 0 32 0 50 0 82 0 3 1 5 3 7 7 7 14 13 20 20 0-51 0-87 0-138-71 0-125 0-196 0z m202 222c-21 0-42 0-64 0 0 8 0 16 0 24 35 0 71 0 106 0 0-36 0-71 0-107-8 0-15 0-23 0 0 22 0 43 0 65-49-49-97-97-145-145-6 7-12 12-17 17 48 48 96 97 144 145 0 0 0 1-1 1z"/>
|
||||
<glyph glyph-name="ellipsis" unicode="" d="M174 232c-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24 0-14-11-24-24-24z m78 0c-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24 0-14-11-24-24-24z m78 0c-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24 0-14-11-24-24-24z"/>
|
||||
<glyph glyph-name="check" unicode="" d="M256 426c95 0 172-77 171-173 0-93-77-169-171-169-95 0-171 77-171 173 0 93 78 169 171 169z m-23-192c-2 3-3 5-4 6-13 13-26 26-39 39-10 11-26 12-36 2-10-10-10-25 1-36 20-21 40-41 60-61 11-10 26-10 36 0 36 35 71 71 106 107 4 3 7 9 8 14 2 10-3 21-13 26-10 4-21 3-29-5-28-29-57-57-85-86-2-1-3-3-5-6z"/>
|
||||
<glyph glyph-name="sliders" unicode="&" d="M185 371l-35 0c-9 0-17-8-17-16 0-9 8-16 17-16l35 0z m190 0l-99 0 0-32 99 0c9 0 17 7 17 16 0 8-8 16-17 16m-163-195l-62 0c-9 0-17-8-17-17 0-9 8-16 17-16l62 0z m163 0l-71 0 0-33 71 0c9 0 17 7 17 16 0 9-8 17-17 17m-63 95l-162 0c-9 0-17-6-17-15 0-9 8-16 17-16l162 0z m-63 120c2 0 4-1 6-4 2-1 2-4 2-8l0-48c0-4 0-7-2-8-2-3-4-4-6-4l-36 0c-2 0-4 1-6 4-2 2-2 5-2 8l0 48c0 4 0 6 2 8 2 3 4 4 6 4z m126-100c2 0 4-1 6-4 2-2 2-5 2-8l0-48c0-2-1-5-2-8-2-2-4-4-6-4l-34 0c-2 0-4 2-6 4-2 2-3 5-3 8l0 48c0 4 1 7 3 8 2 3 4 4 6 4z m-100-96c2 0 5-1 7-3 2-3 2-6 2-9l0-48c0-2 0-5-2-8-2-3-4-4-7-4l-36 0c-3 0-5 1-6 4-2 2-3 5-3 8l0 48c0 4 1 6 3 9 1 2 4 3 6 3z"/>
|
||||
<glyph glyph-name="polyline" unicode="" d="M150 90c1 0 3 0 5-1 4-1 8 0 12 2 3 1 6 5 7 9 1 3 0 8-2 11-2 3-5 6-9 7-2 1-4 1-5 1-4 1-8 1-12-1-3-2-6-6-7-9-1-4-1-8 2-12 2-3 5-6 9-7z m-55 36c1-2 2-3 3-5 1 0 1-1 2-1 0-1 1-2 2-2 3-3 6-5 10-5 2 0 4 1 6 2 2 0 4 1 5 3 2 3 4 6 4 10 0 4-1 8-4 11-1 1-3 2-4 4 1-1 2-2 3-3-1 0-1 1-1 1-2 3-6 6-9 7-2 1-4 1-6 0-2 0-4 0-6-2-3-2-6-5-7-9-1-4 0-7 2-11z m-15 64c0-3 0-6 0-8 0-4 1-8 4-11 3-3 7-5 11-4 8 0 15 6 15 15 0 2 0 5 0 8 0 4-2 8-5 10-2 3-6 5-10 5-9-1-15-7-15-15z m22 87c-5-10-9-21-12-32-2-8 3-17 10-19 9-1 16 3 19 11 2 8 5 17 9 25 3 7 2 16-6 20-6 4-17 2-20-5z m59 82c-12-12-23-24-33-38-5-6-2-16 5-20 8-5 16-1 20 5 8 11 19 22 29 32 6 5 6 15 0 21-6 6-15 5-21 0z m104-63c-19-8-25-31-20-50 4-21 23-33 43-34 21-2 40 10 52 26 12 18 14 41 8 62-7 20-21 37-40 47-19 9-42 11-63 4-39-12-65-49-69-89-3-38 15-77 45-100 33-27 78-34 119-23 80 23 131 113 109 193-10 37-37 66-72 81-40 16-88 11-128-3-10-3-21-8-31-13-7-3-9-13-5-20 4-7 13-9 20-5 32 15 68 24 104 20 15-1 30-6 42-14 3-1 6-3 9-6 2-1-2 2 0 0 1 0 2-1 2-2 2-1 4-2 5-4 3-2 5-5 7-7 0 0 4-5 2-3 1-1 2-3 3-4 2-3 4-6 6-9 1-2 2-4 2-6 1 0 1-1 1-2-1 3 1-1 1-1 3-8 5-16 6-25 0 2 0-1 0-1 1-1 1-2 1-3 0-2 0-4 0-6 0-5 0-10 0-15-1-7-2-14-4-23-4-16-10-29-20-43-1-2-2-4-4-5 0-1-1-3 0-1-1-1-1-2-2-2-2-3-5-7-8-9-2-3-5-5-8-8-1-1-3-2-4-4 0 0-2-2-1 0-1-1-2-2-3-3-14-10-26-15-41-19-9-2-14-3-23-3-9-1-17 0-23 1-16 3-31 9-43 19-1 1-5 4-7 7-3 2-6 5-8 8 0 1-2 3-1 1 0 0 0 1-1 2-1 1-2 3-3 5-2 3-4 6-6 10 0 1-2 5-1 3-1 1-2 3-2 5-2 4-3 8-3 12-1 1-1 3-1 5-1 2 0-2 0 0 0 1 0 2-1 3 0 4 0 8 0 12 0 2 1 4 1 6 0 3 0 1 0 0 0 1 0 2 0 3 1 4 2 8 3 11 1 2 1 4 2 5 0 0 1 3 0 2 0-2 1 1 1 1 2 4 4 7 6 11 1 1 2 2 3 4-1-2 0 0 0 0 1 1 2 2 2 2 3 3 6 6 9 9 0 0 2 1 0 0 1 1 2 1 3 2 1 1 3 2 5 3 3 2 7 4 10 5 8 3 14 4 22 4 2 0 5 0 7-1-2 1 2 0 3 0 2-1 4-1 5-2 1 0 2-1 3-1-2 1 1 0 1-1 4-1 7-3 10-5-1 1 2-2 3-2 1-2 3-4 4-5 1-1 2-2 2-3 0 1-1 2 1 0 1-2 2-3 3-5 1-2 2-3 3-5 1-3 0-2 0-1 0-1 1-3 1-4 1-2 1-4 2-5 0-1 0-2 0-4 0 2 1 0 1 0 0-3 0-5 0-7 0-1 0-2 0-2 0-1 0-1 0 0 0-1 0-2-1-2 0-2 0-4-1-6 0-1-1-2-1-3-1-2 1 2 0 0-1-2-2-3-3-5 0-1-1-1-1-2-3-4 1 1-1-1-1-1-2-3-3-4-1 0-5-4-3-2-2-1-3-2-5-3 0 0-1-1-2-1-1-1 2 0-1-1-2 0-4-1-6-1 3 1-1 0-2 0-1 0-3 0-4 0 3 0 0 0 0 0-1 1-3 1-4 1 0 0-3 1-1 1 2-1 0 0 0 0-2 1-4 2-5 3 2-2-1 1-2 2 2-2-1 1-1 2-2 2 0 1 0 0 0 1-1 2-1 2 0 1 0 2 0 2-1 2 0-2 0 0 0 1-1 3-1 4 0 1 1 2 1 3 0-3 0-1 0 0 0 1 0 2 1 3 0 0 1 3 0 1-1-2 1 2 2 2-2-2 0 1 1 2-2-2 2 0 3 1 7 3 9 14 5 20-5 8-13 10-21 6z"/>
|
||||
<glyph glyph-name="source" unicode="" d="M397 222c-40 0-73-28-76-67-41 9-57 54-59 60-16 47-46 69-90 69-4 0-7 0-10-1l-3 0c-1 0-1 0-2 0l-2-2c-7-4-27-13-42-13-27 0-49 22-49 49 0 26 22 48 49 48 27 0 49-22 49-48 0-3 0-8 0-8 0-1 0-1 0-2 1-1 2-1 2-1l181-1c1 0 2 1 2 1 1 1 1 2 1 2 0 0 0 6 0 9 0 26 22 48 49 48 27 0 49-22 49-48 0-27-22-49-49-49-11 0-21 4-30 10 0 1-1 1-1 1-1 0-1 0-1 0-1 0-1-1-2-1l-14-19c0-1-1-1-1-2 1-1 1-1 1-2 14-10 31-16 48-16 43 0 78 35 78 77 0 43-35 77-78 77-41 0-74-26-77-65l-131 0c-3 39-36 66-76 66-43 0-78-35-78-77 0-43 35-77 78-77 21 0 45 9 54 15 2 0 3 0 4 0 31 0 51-14 63-49 1-3 26-79 101-79l0 0 12 0c1 0 2 1 2 1 1 1 1 1 1 2 0 0-1 16-1 18 0 26 21 45 48 45 27 0 49-21 49-48 0-26-22-48-49-48-11 0-21 3-30 10 0 0-1 0-1 0 0 0-1 0-1 0 0 0-1 0-2-1l-14-18c0-1-1-2 0-2 0-1 0-2 1-2 13-10 30-16 47-16 43 0 78 35 78 77 0 43-35 77-78 77z"/>
|
||||
<glyph glyph-name="playback-play" unicode="" d="M128 416l256-160-256-160z"/>
|
||||
<glyph glyph-name="stop-square" unicode="" d="M384 128l-256 0 0 256 256 0z"/>
|
||||
<glyph glyph-name="avatar-t-pose" unicode="" d="M274 70c-1 0-1 0-1 0-12-1-14 2-14 12 0 0 0 133 0 135-3 0-6 0-9 0-2-10 1-140 1-140-1-7-2-8-9-8-2 0-5 0-7 0-8 109-16 188-16 188 0 24 0 46 0 66-1-1-33 0-44 1-15 0-62 1-79 1-8 0-14 3-18 10-1 2-2 4-4 6 8 1 15 1 22 1 35 2 99 9 100 13 14 10 23 10 36 10 15 0 31 0 46-1 11-1 24 3 37-10 20-10 81-11 123-11 0 0 1 0 1-1-4-11-12-17-25-16-29-1-77-3-127-1 1-20 1-42 2-66 0 0-6-59-15-189z m-13 372c16-6 14-20 13-32 0-5-1-10-1-14-2-11-10-18-20-18-11 0-19 8-20 18-1 6-1 13-2 20-1 7 3 13 11 15 10 3 10 3 19 11z"/>
|
||||
<glyph glyph-name="check-1" unicode="" d="M233 234c-2 3-3 5-4 6-13 13-26 26-39 39-10 11-26 12-36 2-10-10-10-25 1-36 20-21 40-41 60-61 11-10 26-10 36 0 36 35 71 71 106 107 4 3 7 9 8 14 2 10-3 21-13 26-10 4-21 3-29-5-28-29-57-57-85-86-2-1-3-3-5-6z"/>
|
||||
<glyph glyph-name="exchange" unicode="" d="M315 344c0 8 0 15 0 22 0 7 0 13 1 20 0 2 2 5 3 6 3 1 6 0 8-1 1 0 2-1 2-1 21-21 41-42 62-62 5-5 5-8 0-13-21-21-42-42-62-63-5-4-11-4-13 1-1 1-1 3-1 5 0 11 0 22 0 33 0 2 0 3 0 6-2 0-4 0-5 0-61 0-122 0-183 0-9 0-10 1-10 10 0 10 0 19 0 29 0 6 2 8 8 9 1 0 2 0 4 0 60 0 120 0 180 0 2-1 4-1 6-1z m-116-121c2 0 4 0 6 0 61 0 122 0 182 0 9 0 11-1 11-10 0-10 0-19 0-29 0-6-3-8-9-9-1 0-3 0-4 0-60 0-120 0-180 0-2 0-4 0-6 0 0-2 0-3 0-5 0-11 0-23 0-34 0-4-1-6-4-8-4-1-6 0-9 2-21 22-42 43-64 64-4 4-4 7 1 12 21 21 42 42 63 63 3 3 6 4 9 2 3-1 4-4 4-7 0-12 0-24 0-35 0-2 0-3 0-6z"/>
|
||||
<glyph glyph-name="hfc" unicode="" d="M370 142c-12-23-30-42-51-55-12-8-25-14-39-18-38-12-79-7-115 12-35 19-61 51-72 90-12 38-7 79 12 114 12 23 29 42 50 55 7 5 14 8 22 12l1-229c10-6 22-11 34-14l-1 251c13 2 31 2 50 2l1-254c12 2 24 6 34 11l0 244c37 0 72-1 84-1l7 33c-15 0-52 1-91 1l0 57 166 1 0 34-201-1 1-91c-19 0-37-1-51-2l-1 93-34-1 1-98c-44-15-80-45-102-86-24-44-29-94-15-141 14-47 46-86 89-110 44-23 94-29 141-15 17 6 33 13 48 22 26 17 47 40 62 68 8 14 13 28 17 44-11 2-22 4-33 7-3-12-8-24-14-35z"/>
|
||||
<glyph glyph-name="home-1" unicode="" d="M155 273c-5 0-10 2-14 7-5 7-3 17 5 23l29 19 84 59 109-79c8-5 9-15 4-22-5-8-16-9-23-4l-90 65-65-46-30-19c-3-2-6-3-9-3z m84-78c0 12 10 21 21 21 11 0 21-10 21-21l0-60 75 0 0 89-32 24-64 46-68-46-33-21 0-92 80 0 0 60z"/>
|
||||
<glyph glyph-name="private-key" unicode="" d="M238 263c-30 43-16 96 20 121 34 25 82 21 112-8 30-31 33-79 8-114-25-34-77-48-120-18-21-21-41-41-61-61 2-3 5-6 7-8 6-6 12-12 18-18 3-3 3-5 0-7-3-3-6-6-9-9-3-3-5-3-7 0-5 5-9 9-14 14-5-5-9-10-14-15 4-4 9-9 14-14 2-2 2-4 0-6-4-3-7-7-10-10-3-2-4-2-6 0-14 14-28 28-42 42-4 3-2 5 0 7 33 33 67 66 100 99 1 2 3 3 4 5z m131 51c0 34-26 61-61 61-33 0-60-27-61-60 0-35 27-61 62-62 33 0 60 28 60 61z"/>
|
||||
<glyph glyph-name="security-pic" unicode="" d="M365 406l-212 0c-18 0-33-14-33-32l0-91c9 5 18 11 27 15l0 76c0 3 3 5 6 5l212 0c3 0 5-2 5-5l0-212c0-3-2-6-5-6l-106 0 0-27 106 0c18 0 32 15 32 33l0 212c0 18-14 32-32 32z m-153-209l0 16c0 28-23 51-51 51-28 0-51-23-51-51l0-16c-10-2-18-11-18-22l0-39c0-12 9-22 21-22l95 0c12 0 21 10 21 22l0 39c1 11-7 20-17 22z m-51 44c15 0 27-12 27-28l0-16-55 0 0 16c0 16 13 28 28 28z m183 49l-66 49-83-50c7-4 14-10 19-17l62 37 68-50 0 31z m-114-44l114 0 0-26-114 0z"/>
|
||||
<glyph glyph-name="wallet" unicode="" d="M400 400c-3 10-8 19-15 24-7 5-15 8-24 7l-227 0c-19 0-35-15-35-34l0-58c-19-2-34-18-34-37l0-95c0-19 15-35 34-37l0-53c0-19 16-34 35-34l228 0c20 0 30 16 38 31l0 1c1 2 21 53 21 139 0 83-19 140-21 146z m-309-193l0 95c0 6 5 11 11 11l86 0c6 0 11-5 11-11l0-95c0-6-5-11-11-11l-86 0c-6 0-11 5-11 11z m285-82c-8-16-11-16-14-16l-228 0c-5 0-9 4-9 8l0 53 63 0c21 0 37 17 37 37l0 95c0 20-16 37-37 37l-63 0 0 58c0 4 4 8 9 8l228 0c3 0 10 1 13-13l0 0 0-1c1 0 20-55 20-137 0-77-17-124-19-129z"/>
|
||||
<glyph glyph-name="send" unicode="" d="M391 376c4-4 4-10 2-16-7-21-14-42-22-63-21-63-43-125-65-188-1-4-4-7-6-10-8-6-17-4-22 5-15 28-30 56-44 85-1 1 0 3 0 4 18 21 35 43 53 64 5 6 10 12 15 18 4 6 5 10 1 14-4 4-8 3-14-1-18-15-36-30-54-44-9-8-19-16-28-24-1-1-3-1-4 0-29 14-57 29-85 44-6 3-8 8-8 14 1 7 5 11 11 13 36 13 72 25 107 37 49 17 97 34 145 51 7 2 13 2 18-3z"/>
|
||||
<glyph glyph-name="password" unicode="" d="M104 267l0 41 22 0 0-41 35 20 11-19-35-20 35-20-11-20-35 20 0-40-22 0 0 40-35-20-11 20 35 20-35 20 11 19z m136 0l0 41 23 0 0-41 35 20 11-19-35-20 35-20-11-20-35 20 0-40-23 0 0 40-35-20-11 20 35 20-35 20 11 19z m137 0l0 41 23 0 0-41 34 20 12-19-35-20 35-20-12-20-35 20 0-40-22 0 0 40-35-20-11 20 35 20-35 20 11 19z"/>
|
||||
<glyph glyph-name="rez" unicode="" d="M373 321c-2 5-6 8-11 8l-49 8 55 61c4 4 5 9 3 14-2 5-7 8-12 8 0 0 0 0 0 0l-114-1c-5-1-10-4-12-9l-54-136c-1-4-1-8 1-11 2-4 6-6 9-7l38-5-54-136c-2-6 0-13 6-16 2-1 4-2 7-2 3 0 7 2 10 5l175 206c3 4 3 9 2 13z"/>
|
||||
<glyph glyph-name="keyboard-collapse" unicode="" d="M373 249l-26 0 0 25 26 0z m-35 0l-27 0 0 25 27 0z m-36 0l-27 0 0 25 27 0z m-36 0l-26 0 0 25 26 0z m-35 0l-27 0 0 25 27 0z m-36 0l-27 0 0 25 27 0z m-36 0l-26 0 0 25 26 0z m224-1l18 0c7 0 13 6 13 13 0 7-6 13-13 13l-18 0m-262 0l-17 0c-7 0-13-6-13-13 0-7 6-13 13-13l17 0m252 39l-31 0 0 25 31 0z m-42 0l-31 0 0 25 31 0z m-41 0l-32 0 0 25 32 0z m-42 0l-32 0 0 25 32 0z m-42 0l-32 0 0 25 32 0z m-41 0l-32 0 0 25 32 0z m218-1l18 0c7 0 13 6 13 13 0 8-6 14-13 14l-18 0m-262 0l-17 0c-7 0-13-6-13-14 0-7 6-13 13-13l17 0m288-124l-315 0c-33 0-59 28-59 61l0 76c0 34 26 61 59 61l315 0c33 0 59-27 59-61l0-76c1-33-26-61-59-61z m-315 172c-18 0-33-16-33-34l0-77c0-19 15-34 33-34l315 0c18 0 33 15 33 34l0 77c0 19-15 34-33 34z m248-99l32 0 0-25-32 0z m-41 0l31 0 0-25-31 0z m-42 0l31 0 0-25-31 0z m-43 0l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m-41 0l31 0 0-25-31 0z m250-26l18 0c7 0 13 6 13 14 0 7-6 13-13 13l-18 0m-262 0l-17 0c-7 0-13-6-13-13 0-8 6-13 13-13l17 0m81-82l50-50 53 54-107 0"/>
|
||||
<glyph glyph-name="image" unicode="" d="M257 428c52 0 104 0 156 0 24 0 37-13 37-37 1-90 1-179 0-269 0-25-13-38-39-38-103 0-207 0-311 0-26 0-39 13-39 40 0 88 0 176 0 263 0 28 13 41 41 41 51 0 103 0 155 0z m167-263c0 7 0 10 0 14 0 69 0 138 0 206 0 17 0 17-17 17-101 0-202 0-303 0-16 0-17-1-17-17 0-58 0-115 0-173 0-3 0-7 0-12 8 3 14 6 19 9 17 8 30 7 44-6 5-5 10-10 15-15 5-7 11-8 19-4 40 21 81 41 121 61 19 10 31 8 46-7 9-9 18-18 27-27 15-15 29-29 46-46z m-328-54c7 0 11-1 15-1 98 0 197 0 296 0 6 0 14 2 16 6 5 7 0 14-6 20-27 26-54 53-80 80-8 9-15 9-26 4-67-35-135-68-203-102-3-2-6-4-12-7z m-8 26c21 10 40 20 63 31-8 7-14 12-20 17-2 2-7 3-10 3-30-9-36-17-34-48 0 0 0-1 1-3z m134 169c1-25-21-46-46-46-25-1-46 20-47 46 0 25 21 46 47 47 25 0 46-21 46-47z m-46 22c-12 0-22-9-22-21 0-13 9-22 21-23 13 0 23 9 23 22 0 13-10 22-22 22z"/>
|
||||
</font></defs></svg>
|
After Width: | Height: | Size: 78 KiB |
BIN
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf
Normal file
BIN
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf
Normal file
Binary file not shown.
BIN
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.woff
Normal file
BIN
interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.woff
Normal file
Binary file not shown.
|
@ -12,7 +12,7 @@
|
|||
<body>
|
||||
<div class="container">
|
||||
<h1>HiFi Glyphs</h1>
|
||||
<p class="small">This font was created in<a href="http://highfidelity.com/">High Fidelity</a></p>
|
||||
<p class="small">This font was created for use in<a href="http://highfidelity.io/">High Fidelity</a></p>
|
||||
<h2>CSS mapping</h2>
|
||||
<ul class="glyphs css-mapping">
|
||||
<li>
|
||||
|
@ -520,8 +520,52 @@
|
|||
<input type="text" readonly="readonly" value="avatar-t-pose">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-check-2-01"></div>
|
||||
<input type="text" readonly="readonly" value="check-2-01">
|
||||
<div class="icon icon-check-1"></div>
|
||||
<input type="text" readonly="readonly" value="check-1">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-exchange"></div>
|
||||
<input type="text" readonly="readonly" value="exchange">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-hfc"></div>
|
||||
<input type="text" readonly="readonly" value="hfc">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-home-1"></div>
|
||||
<input type="text" readonly="readonly" value="home-1">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-private-key"></div>
|
||||
<input type="text" readonly="readonly" value="private-key">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-security-pic"></div>
|
||||
<input type="text" readonly="readonly" value="security-pic">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-wallet"></div>
|
||||
<input type="text" readonly="readonly" value="wallet">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-send"></div>
|
||||
<input type="text" readonly="readonly" value="send">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-password"></div>
|
||||
<input type="text" readonly="readonly" value="password">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-rez"></div>
|
||||
<input type="text" readonly="readonly" value="rez">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-keyboard-collapse"></div>
|
||||
<input type="text" readonly="readonly" value="keyboard-collapse">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-image"></div>
|
||||
<input type="text" readonly="readonly" value="image">
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Character mapping</h2>
|
||||
|
@ -1034,6 +1078,50 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe020;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe021;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe022;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe023;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe024;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe026;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe027;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe028;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe029;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe025;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe02b;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe02a;">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>(function() {
|
||||
|
|
|
@ -416,6 +416,39 @@
|
|||
.icon-avatar-t-pose:before {
|
||||
content: "\e01f";
|
||||
}
|
||||
.icon-check-2-01:before {
|
||||
.icon-check-1:before {
|
||||
content: "\e020";
|
||||
}
|
||||
.icon-exchange:before {
|
||||
content: "\e021";
|
||||
}
|
||||
.icon-hfc:before {
|
||||
content: "\e022";
|
||||
}
|
||||
.icon-home-1:before {
|
||||
content: "\e023";
|
||||
}
|
||||
.icon-private-key:before {
|
||||
content: "\e024";
|
||||
}
|
||||
.icon-security-pic:before {
|
||||
content: "\e026";
|
||||
}
|
||||
.icon-wallet:before {
|
||||
content: "\e027";
|
||||
}
|
||||
.icon-send:before {
|
||||
content: "\e028";
|
||||
}
|
||||
.icon-password:before {
|
||||
content: "\e029";
|
||||
}
|
||||
.icon-rez:before {
|
||||
content: "\e025";
|
||||
}
|
||||
.icon-keyboard-collapse:before {
|
||||
content: "\e02b";
|
||||
}
|
||||
.icon-image:before {
|
||||
content: "\e02a";
|
||||
}
|
||||
|
|
23
interface/resources/icons/create-icons/image.svg
Normal file
23
interface/resources/icons/create-icons/image.svg
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M256.5,83.7c52.1,0,104.2-0.1,156.3,0.1c23.7,0.1,37.6,13.5,37.6,37.2c0.2,89.6,0.3,179.2,0,268.7
|
||||
c-0.1,25.5-13.6,38.2-39,38.3c-103.8,0.1-207.5,0.1-311.3,0c-25.8,0-38.8-13.5-38.8-39.7c0-87.9,0-175.9,0-263.8
|
||||
c0-27.7,12.8-40.7,40.2-40.8C153.2,83.6,204.8,83.7,256.5,83.7z M423.7,347c0.4-6.9,0.7-10.5,0.7-14.1c0-68.8,0-137.5,0-206.3
|
||||
c0-16.2-0.6-16.7-17-16.7c-101.3,0-202.5,0-303.8,0c-15.9,0-16.9,1-16.9,17.2c0,57.5,0,115,0,172.5c0,3.5,0.3,7.1,0.6,12.4
|
||||
c7.3-3.5,13.2-6.3,19.1-9.2c16.7-8.1,29.8-6.4,43.6,6c5.2,4.7,10.3,9.8,14.7,15.3c5.6,6.9,10.8,7.8,19.1,3.5
|
||||
c40.3-20.8,81-40.9,121.6-61.1c18.3-9.1,30.6-7.1,45.3,7c9.3,9,18.3,18.2,27.4,27.4C392.7,315.5,407.2,330.2,423.7,347z
|
||||
M95.6,400.9c7.3,0.4,11.2,0.8,15.1,0.8c98.7,0,197.5,0.1,296.2-0.2c5.6,0,14-1.4,16.2-5.1c4.5-7.4-0.3-14.4-6.5-20.5
|
||||
c-26.8-26.2-53.5-52.6-79.5-79.6c-8.5-8.9-15.4-9.4-26.1-4c-67.5,34.3-135.3,67.9-203,101.8C104.9,395.7,101.8,397.5,95.6,400.9z
|
||||
M88.2,375.1c20.6-10.3,40.2-20.1,62.4-31.3c-7.6-6.7-13.2-12.2-19.5-16.9c-2.6-1.9-7-3.3-9.9-2.5c-30,8.1-36,16.8-34,47.6
|
||||
C87.1,372.4,87.4,372.8,88.2,375.1z"/>
|
||||
<path class="st0" d="M222.2,205.5c0.3,25.1-20.8,46.5-46,46.8c-25.1,0.3-46.5-20.8-46.8-46.1c-0.3-25.1,20.8-46.5,46.1-46.8
|
||||
C200.5,159.2,221.9,180.2,222.2,205.5z M176,184.1c-12.3-0.1-21.7,9-21.9,21.3c-0.2,12.3,8.8,21.8,21,22.2
|
||||
c12.8,0.4,22.5-9.1,22.5-21.9C197.5,193.4,188.3,184.2,176,184.1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
BIN
interface/resources/meshes/images/default-image-model.fbx
Normal file
BIN
interface/resources/meshes/images/default-image-model.fbx
Normal file
Binary file not shown.
|
@ -143,7 +143,7 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i];
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
|
||||
if (selectedItems > 1) {
|
||||
return false;
|
||||
|
@ -181,92 +181,103 @@ Windows.ScrollingWindow {
|
|||
return;
|
||||
}
|
||||
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
|
||||
var textures = JSON.stringify({ "tex.picture": defaultURL});
|
||||
var shapeType = "box";
|
||||
var dynamic = false;
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, position, gravity);
|
||||
} else {
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = desktop.customInputDialog({
|
||||
textInput: {
|
||||
label: "Model URL",
|
||||
text: defaultURL
|
||||
},
|
||||
comboBox: {
|
||||
label: "Automatic Collisions",
|
||||
index: SHAPE_TYPE_DEFAULT,
|
||||
items: SHAPE_TYPES
|
||||
},
|
||||
checkBox: {
|
||||
label: "Dynamic",
|
||||
checked: DYNAMIC_DEFAULT,
|
||||
disableForItems: [
|
||||
SHAPE_TYPE_STATIC_MESH
|
||||
],
|
||||
checkStateOnDisable: false,
|
||||
warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors"
|
||||
}
|
||||
});
|
||||
|
||||
prompt.selected.connect(function (jsonResult) {
|
||||
if (jsonResult) {
|
||||
var result = JSON.parse(jsonResult);
|
||||
var url = result.textInput.trim();
|
||||
var shapeType;
|
||||
switch (result.comboBox) {
|
||||
case SHAPE_TYPE_SIMPLE_HULL:
|
||||
shapeType = "simple-hull";
|
||||
break;
|
||||
case SHAPE_TYPE_SIMPLE_COMPOUND:
|
||||
shapeType = "simple-compound";
|
||||
break;
|
||||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = desktop.customInputDialog({
|
||||
textInput: {
|
||||
label: "Model URL",
|
||||
text: defaultURL
|
||||
},
|
||||
comboBox: {
|
||||
label: "Automatic Collisions",
|
||||
index: SHAPE_TYPE_DEFAULT,
|
||||
items: SHAPE_TYPES
|
||||
},
|
||||
checkBox: {
|
||||
label: "Dynamic",
|
||||
checked: DYNAMIC_DEFAULT,
|
||||
disableForItems: [
|
||||
SHAPE_TYPE_STATIC_MESH
|
||||
],
|
||||
checkStateOnDisable: false,
|
||||
warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors"
|
||||
}
|
||||
});
|
||||
|
||||
var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT;
|
||||
if (shapeType === "static-mesh" && dynamic) {
|
||||
// The prompt should prevent this case
|
||||
print("Error: model cannot be both static mesh and dynamic. This should never happen.");
|
||||
} else if (url) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity;
|
||||
if (dynamic) {
|
||||
// Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a
|
||||
// different scripting engine from QTScript.
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10);
|
||||
} else {
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
prompt.selected.connect(function (jsonResult) {
|
||||
if (jsonResult) {
|
||||
var result = JSON.parse(jsonResult);
|
||||
var url = result.textInput.trim();
|
||||
var shapeType;
|
||||
switch (result.comboBox) {
|
||||
case SHAPE_TYPE_SIMPLE_HULL:
|
||||
shapeType = "simple-hull";
|
||||
break;
|
||||
case SHAPE_TYPE_SIMPLE_COMPOUND:
|
||||
shapeType = "simple-compound";
|
||||
break;
|
||||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
}
|
||||
|
||||
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
|
||||
var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT;
|
||||
if (shapeType === "static-mesh" && dynamic) {
|
||||
// The prompt should prevent this case
|
||||
print("Error: model cannot be both static mesh and dynamic. This should never happen.");
|
||||
} else if (url) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity;
|
||||
if (dynamic) {
|
||||
// Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a
|
||||
// different scripting engine from QTScript.
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10);
|
||||
} else {
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
}
|
||||
|
||||
// Entities.addEntity doesn't work from QML, so we use this.
|
||||
Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity);
|
||||
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
|
||||
|
||||
// Entities.addEntity doesn't work from QML, so we use this.
|
||||
Entities.addModelEntity(name, url, "", shapeType, dynamic, addPosition, gravity);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function copyURLToClipboard(index) {
|
||||
|
|
|
@ -19,21 +19,31 @@ import "../../../controls-uit" as HifiControlsUit
|
|||
import "../../../controls" as HifiControls
|
||||
import "../wallet" as HifiWallet
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property string marketplaceUrl;
|
||||
property string certificateId;
|
||||
property string marketplaceUrl: "";
|
||||
property string entityId: "";
|
||||
property string certificateId: "";
|
||||
property string itemName: "--";
|
||||
property string itemOwner: "--";
|
||||
property string itemEdition: "--";
|
||||
property string dateOfPurchase: "--";
|
||||
property string itemCost: "--";
|
||||
property string certTitleTextColor: hifi.colors.darkGray;
|
||||
property string certTextColor: hifi.colors.white;
|
||||
property string infoTextColor: hifi.colors.blueAccent;
|
||||
// 0 means replace none
|
||||
// 4 means replace all but "Item Edition"
|
||||
// 5 means replace all 5 replaceable fields
|
||||
property int certInfoReplaceMode: 5;
|
||||
property bool isLightbox: false;
|
||||
property bool isMyCert: false;
|
||||
property bool isCertificateInvalid: false;
|
||||
property bool useGoldCert: true;
|
||||
property bool certificateInfoPending: true;
|
||||
property int certificateStatus: 0;
|
||||
property bool certificateStatusPending: true;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
Connections {
|
||||
|
@ -45,72 +55,135 @@ Rectangle {
|
|||
} else {
|
||||
root.marketplaceUrl = result.data.marketplace_item_url;
|
||||
root.isMyCert = result.isMyCert ? result.isMyCert : false;
|
||||
root.itemOwner = root.isCertificateInvalid ? "--" : (root.isMyCert ? Account.username :
|
||||
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022");
|
||||
root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" :
|
||||
(result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run));
|
||||
root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000);
|
||||
root.itemName = result.data.marketplace_item_name;
|
||||
|
||||
if (root.certInfoReplaceMode > 3) {
|
||||
root.itemName = result.data.marketplace_item_name;
|
||||
// "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc
|
||||
root.itemOwner = root.isMyCert ? Account.username :
|
||||
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
||||
root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed";
|
||||
root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed";
|
||||
}
|
||||
if (root.certInfoReplaceMode > 4) {
|
||||
root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
|
||||
}
|
||||
|
||||
if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
|
||||
if (root.isMyCert) {
|
||||
errorText.text = "This item is an uncertified copy of an item you purchased.";
|
||||
} else {
|
||||
errorText.text = "The person who placed this item doesn't own it.";
|
||||
}
|
||||
}
|
||||
|
||||
if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") {
|
||||
titleBarText.text = "Invalid Certificate";
|
||||
titleBarText.color = hifi.colors.redHighlight;
|
||||
root.useGoldCert = false;
|
||||
root.certTitleTextColor = hifi.colors.redHighlight;
|
||||
root.certTextColor = hifi.colors.redHighlight;
|
||||
root.infoTextColor = hifi.colors.redHighlight;
|
||||
titleBarText.text = "Certificate\nNo Longer Valid";
|
||||
popText.text = "";
|
||||
showInMarketplaceButton.visible = false;
|
||||
// "Edition" text previously set above in this function
|
||||
// "Owner" text previously set above in this function
|
||||
// "Purchase Date" text previously set above in this function
|
||||
// "Purchase Price" text previously set above in this function
|
||||
if (result.data.invalid_reason) {
|
||||
errorText.text = result.data.invalid_reason;
|
||||
}
|
||||
} else if (result.data.transfer_status[0] === "pending") {
|
||||
root.useGoldCert = false;
|
||||
root.certTitleTextColor = hifi.colors.redHighlight;
|
||||
root.certTextColor = hifi.colors.redHighlight;
|
||||
root.infoTextColor = hifi.colors.redHighlight;
|
||||
titleBarText.text = "Certificate Pending";
|
||||
popText.text = "";
|
||||
showInMarketplaceButton.visible = true;
|
||||
// "Edition" text previously set above in this function
|
||||
// "Owner" text previously set above in this function
|
||||
// "Purchase Date" text previously set above in this function
|
||||
// "Purchase Price" text previously set above in this function
|
||||
errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " +
|
||||
"this entity will be cleaned up by the domain.";
|
||||
errorText.color = hifi.colors.baseGray;
|
||||
}
|
||||
}
|
||||
root.certificateInfoPending = false;
|
||||
}
|
||||
|
||||
onUpdateCertificateStatus: {
|
||||
if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
|
||||
// NOP
|
||||
} else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
|
||||
root.isCertificateInvalid = true;
|
||||
errorText.text = "Verification of this certificate timed out.";
|
||||
errorText.color = hifi.colors.redHighlight;
|
||||
} else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
|
||||
root.isCertificateInvalid = true;
|
||||
titleBarText.text = "Invalid Certificate";
|
||||
titleBarText.color = hifi.colors.redHighlight;
|
||||
|
||||
popText.text = "";
|
||||
root.itemOwner = "";
|
||||
dateOfPurchaseHeader.text = "";
|
||||
root.dateOfPurchase = "";
|
||||
root.itemEdition = "Uncertified Copy";
|
||||
|
||||
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
|
||||
errorText.color = hifi.colors.baseGray;
|
||||
} else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
|
||||
root.isCertificateInvalid = true;
|
||||
titleBarText.text = "Invalid Certificate";
|
||||
titleBarText.color = hifi.colors.redHighlight;
|
||||
|
||||
popText.text = "";
|
||||
root.itemOwner = "";
|
||||
dateOfPurchaseHeader.text = "";
|
||||
root.dateOfPurchase = "";
|
||||
root.itemEdition = "Uncertified Copy";
|
||||
|
||||
errorText.text = "The avatar who rezzed this item doesn't own it.";
|
||||
errorText.color = hifi.colors.baseGray;
|
||||
} else {
|
||||
console.log("Unknown certificate status received from ledger signal!");
|
||||
}
|
||||
updateCertificateStatus(certStatus);
|
||||
}
|
||||
}
|
||||
|
||||
onCertificateIdChanged: {
|
||||
if (certificateId !== "") {
|
||||
Commerce.certificateInfo(certificateId);
|
||||
function updateCertificateStatus(status) {
|
||||
root.certificateStatus = status;
|
||||
if (root.certificateStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
|
||||
root.useGoldCert = true;
|
||||
root.certTitleTextColor = hifi.colors.darkGray;
|
||||
root.certTextColor = hifi.colors.white;
|
||||
root.infoTextColor = hifi.colors.blueAccent;
|
||||
titleBarText.text = "Certificate";
|
||||
popText.text = "PROOF OF PROVENANCE";
|
||||
showInMarketplaceButton.visible = true;
|
||||
root.certInfoReplaceMode = 5;
|
||||
// "Item Name" text will be set in "onCertificateInfoResult()"
|
||||
// "Edition" text will be set in "onCertificateInfoResult()"
|
||||
// "Owner" text will be set in "onCertificateInfoResult()"
|
||||
// "Purchase Date" text will be set in "onCertificateInfoResult()"
|
||||
// "Purchase Price" text will be set in "onCertificateInfoResult()"
|
||||
errorText.text = "";
|
||||
} else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
|
||||
root.useGoldCert = false;
|
||||
root.certTitleTextColor = hifi.colors.redHighlight;
|
||||
root.certTextColor = hifi.colors.redHighlight;
|
||||
root.infoTextColor = hifi.colors.redHighlight;
|
||||
titleBarText.text = "Request Timed Out";
|
||||
popText.text = "";
|
||||
showInMarketplaceButton.visible = false;
|
||||
root.certInfoReplaceMode = 0;
|
||||
root.itemName = "";
|
||||
root.itemEdition = "";
|
||||
root.itemOwner = "";
|
||||
root.dateOfPurchase = "";
|
||||
root.itemCost = "";
|
||||
errorText.text = "Your request to inspect this item timed out. Please try again later.";
|
||||
} else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
|
||||
root.useGoldCert = false;
|
||||
root.certTitleTextColor = hifi.colors.redHighlight;
|
||||
root.certTextColor = hifi.colors.redHighlight;
|
||||
root.infoTextColor = hifi.colors.redHighlight;
|
||||
titleBarText.text = "Certificate\nNo Longer Valid";
|
||||
popText.text = "";
|
||||
showInMarketplaceButton.visible = true;
|
||||
root.certInfoReplaceMode = 5;
|
||||
// "Item Name" text will be set in "onCertificateInfoResult()"
|
||||
// "Edition" text will be set in "onCertificateInfoResult()"
|
||||
// "Owner" text will be set in "onCertificateInfoResult()"
|
||||
// "Purchase Date" text will be set in "onCertificateInfoResult()"
|
||||
// "Purchase Price" text will be set in "onCertificateInfoResult()"
|
||||
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
|
||||
} else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
|
||||
root.useGoldCert = false;
|
||||
root.certTitleTextColor = hifi.colors.redHighlight;
|
||||
root.certTextColor = hifi.colors.redHighlight;
|
||||
root.infoTextColor = hifi.colors.redHighlight;
|
||||
titleBarText.text = "Invalid Certificate";
|
||||
popText.text = "";
|
||||
showInMarketplaceButton.visible = true;
|
||||
root.certInfoReplaceMode = 4;
|
||||
// "Item Name" text will be set in "onCertificateInfoResult()"
|
||||
root.itemEdition = "Uncertified Copy"
|
||||
// "Owner" text will be set in "onCertificateInfoResult()"
|
||||
// "Purchase Date" text will be set in "onCertificateInfoResult()"
|
||||
// "Purchase Price" text will be set in "onCertificateInfoResult()"
|
||||
// "Error Text" text will be set in "onCertificateInfoResult()"
|
||||
} else {
|
||||
console.log("Unknown certificate status received from ledger signal!");
|
||||
}
|
||||
|
||||
root.certificateStatusPending = false;
|
||||
// We've gotten cert status - we are GO on getting the cert info
|
||||
Commerce.certificateInfo(root.certificateId);
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
|
@ -122,9 +195,35 @@ Rectangle {
|
|||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Image {
|
||||
Rectangle {
|
||||
id: loadingOverlay;
|
||||
z: 998;
|
||||
|
||||
visible: root.certificateInfoPending || root.certificateStatusPending;
|
||||
anchors.fill: parent;
|
||||
source: "images/cert-bg.jpg";
|
||||
color: Qt.rgba(0.0, 0.0, 0.0, 0.7);
|
||||
|
||||
// This object is always used in a popup or full-screen Wallet section.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup/section.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
}
|
||||
|
||||
AnimatedImage {
|
||||
source: "../common/images/loader.gif"
|
||||
width: 96;
|
||||
height: width;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backgroundImage;
|
||||
anchors.fill: parent;
|
||||
source: root.useGoldCert ? "images/cert-bg-gold-split.png" : "images/nocert-bg-split.png";
|
||||
}
|
||||
|
||||
// Title text
|
||||
|
@ -137,16 +236,17 @@ Rectangle {
|
|||
anchors.top: parent.top;
|
||||
anchors.topMargin: 40;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 45;
|
||||
anchors.leftMargin: 36;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 8;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: root.certTitleTextColor;
|
||||
wrapMode: Text.WordWrap;
|
||||
}
|
||||
// Title text
|
||||
RalewayRegular {
|
||||
id: popText;
|
||||
text: "Proof of Provenance";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
|
@ -154,9 +254,39 @@ Rectangle {
|
|||
anchors.topMargin: 4;
|
||||
anchors.left: titleBarText.left;
|
||||
anchors.right: titleBarText.right;
|
||||
height: paintedHeight;
|
||||
height: text === "" ? 0 : paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: root.certTitleTextColor;
|
||||
}
|
||||
|
||||
// "Close" button
|
||||
HiFiGlyphs {
|
||||
z: 999;
|
||||
id: closeGlyphButton;
|
||||
text: hifi.glyphs.close;
|
||||
color: hifi.colors.white;
|
||||
size: 26;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 10;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 10;
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: true;
|
||||
onEntered: {
|
||||
parent.text = hifi.glyphs.closeInverted;
|
||||
}
|
||||
onExited: {
|
||||
parent.text = hifi.glyphs.close;
|
||||
}
|
||||
onClicked: {
|
||||
if (root.isLightbox) {
|
||||
root.visible = false;
|
||||
} else {
|
||||
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -164,11 +294,13 @@ Rectangle {
|
|||
//
|
||||
Item {
|
||||
id: certificateContainer;
|
||||
anchors.top: popText.bottom;
|
||||
anchors.topMargin: 30;
|
||||
anchors.bottom: buttonsContainer.top;
|
||||
anchors.top: titleBarText.top;
|
||||
anchors.topMargin: 110;
|
||||
anchors.bottom: infoContainer.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: titleBarText.anchors.leftMargin;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 24;
|
||||
|
||||
RalewayRegular {
|
||||
id: itemNameHeader;
|
||||
|
@ -178,9 +310,7 @@ Rectangle {
|
|||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 45;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
|
@ -197,79 +327,30 @@ Rectangle {
|
|||
anchors.right: itemNameHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
color: root.certTextColor;
|
||||
elide: Text.ElideRight;
|
||||
MouseArea {
|
||||
enabled: showInMarketplaceButton.visible;
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: enabled;
|
||||
onClicked: {
|
||||
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
|
||||
}
|
||||
onEntered: itemName.color = hifi.colors.blueHighlight;
|
||||
onExited: itemName.color = hifi.colors.white;
|
||||
onExited: itemName.color = root.certTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: ownedByHeader;
|
||||
text: "OWNER";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: itemName.bottom;
|
||||
anchors.topMargin: 28;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 45;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: ownedBy;
|
||||
text: root.itemOwner;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Anchors
|
||||
anchors.top: ownedByHeader.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: ownedByHeader.left;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
AnonymousProRegular {
|
||||
id: isMyCertText;
|
||||
visible: root.isMyCert && !root.isCertificateInvalid;
|
||||
text: "(Private)";
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: ownedBy.top;
|
||||
anchors.topMargin: 4;
|
||||
anchors.bottom: ownedBy.bottom;
|
||||
anchors.left: ownedBy.right;
|
||||
anchors.leftMargin: 6;
|
||||
anchors.right: ownedByHeader.right;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
elide: Text.ElideRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: editionHeader;
|
||||
text: "EDITION";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: ownedBy.bottom;
|
||||
anchors.top: itemName.bottom;
|
||||
anchors.topMargin: 28;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 45;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
|
@ -286,21 +367,117 @@ Rectangle {
|
|||
anchors.right: editionHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
color: root.certTextColor;
|
||||
}
|
||||
|
||||
// "Show In Marketplace" button
|
||||
HifiControlsUit.Button {
|
||||
id: showInMarketplaceButton;
|
||||
enabled: root.marketplaceUrl;
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 48;
|
||||
anchors.right: parent.right;
|
||||
width: 200;
|
||||
height: 40;
|
||||
text: "View In Market"
|
||||
onClicked: {
|
||||
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// "CERTIFICATE" END
|
||||
//
|
||||
|
||||
//
|
||||
// "INFO CONTAINER" START
|
||||
//
|
||||
Item {
|
||||
id: infoContainer;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: titleBarText.anchors.leftMargin;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 24;
|
||||
height: root.useGoldCert ? 220 : 372;
|
||||
|
||||
RalewayRegular {
|
||||
id: errorText;
|
||||
visible: !root.useGoldCert;
|
||||
// Text size
|
||||
size: 20;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 36;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 116;
|
||||
// Style
|
||||
wrapMode: Text.WordWrap;
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: ownedByHeader;
|
||||
text: "OWNER";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: errorText.visible ? errorText.bottom : parent.top;
|
||||
anchors.topMargin: 28;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: ownedBy;
|
||||
text: root.itemOwner;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Anchors
|
||||
anchors.top: ownedByHeader.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: ownedByHeader.left;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: root.infoTextColor;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
AnonymousProRegular {
|
||||
id: isMyCertText;
|
||||
visible: root.isMyCert && ownedBy.text !== "--" && ownedBy.text !== "";
|
||||
text: "(Private)";
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: ownedBy.top;
|
||||
anchors.topMargin: 4;
|
||||
anchors.bottom: ownedBy.bottom;
|
||||
anchors.left: ownedBy.right;
|
||||
anchors.leftMargin: 6;
|
||||
anchors.right: ownedByHeader.right;
|
||||
// Style
|
||||
color: root.infoTextColor;
|
||||
elide: Text.ElideRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: dateOfPurchaseHeader;
|
||||
text: "DATE OF PURCHASE";
|
||||
text: "PURCHASE DATE";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: edition.bottom;
|
||||
anchors.top: ownedBy.bottom;
|
||||
anchors.topMargin: 28;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 45;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
anchors.right: parent.horizontalCenter;
|
||||
anchors.rightMargin: 8;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
|
@ -317,73 +494,58 @@ Rectangle {
|
|||
anchors.right: dateOfPurchaseHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
color: root.infoTextColor;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: errorText;
|
||||
id: priceHeader;
|
||||
text: "PURCHASE PRICE";
|
||||
// Text size
|
||||
size: 20;
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: dateOfPurchase.bottom;
|
||||
anchors.topMargin: 36;
|
||||
anchors.left: dateOfPurchase.left;
|
||||
anchors.right: dateOfPurchase.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
// Style
|
||||
wrapMode: Text.WordWrap;
|
||||
color: hifi.colors.redHighlight;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
}
|
||||
//
|
||||
// "CERTIFICATE" END
|
||||
//
|
||||
|
||||
Item {
|
||||
id: buttonsContainer;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 30;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 50;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.noneBorderlessWhite;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
width: parent.width/2 - 50;
|
||||
height: 50;
|
||||
text: "close";
|
||||
onClicked: {
|
||||
if (root.isLightbox) {
|
||||
root.visible = false;
|
||||
} else {
|
||||
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Show In Marketplace" button
|
||||
HifiControlsUit.Button {
|
||||
id: showInMarketplaceButton;
|
||||
enabled: root.marketplaceUrl;
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
anchors.top: parent.top;
|
||||
anchors.top: ownedBy.bottom;
|
||||
anchors.topMargin: 28;
|
||||
anchors.left: parent.horizontalCenter;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
width: parent.width/2 - 50;
|
||||
height: 50;
|
||||
text: "View In Market"
|
||||
onClicked: {
|
||||
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
|
||||
}
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
HiFiGlyphs {
|
||||
id: hfcGlyph;
|
||||
visible: priceText.text !== "Undisclosed" && priceText.text !== "";
|
||||
text: hifi.glyphs.hfc;
|
||||
// Size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: priceHeader.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: priceHeader.left;
|
||||
width: visible ? paintedWidth + 6 : 0;
|
||||
height: 40;
|
||||
// Style
|
||||
color: root.infoTextColor;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
}
|
||||
AnonymousProRegular {
|
||||
id: priceText;
|
||||
text: root.itemCost;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: priceHeader.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: hfcGlyph.right;
|
||||
anchors.right: priceHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: root.infoTextColor;
|
||||
}
|
||||
}
|
||||
//
|
||||
// "INFO CONTAINER" END
|
||||
//
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
|
@ -404,19 +566,17 @@ Rectangle {
|
|||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
case 'inspectionCertificate_setCertificateId':
|
||||
resetCert(false);
|
||||
root.certificateId = message.certificateId;
|
||||
if (message.entityId === "") {
|
||||
updateCertificateStatus(1); // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
|
||||
} else {
|
||||
root.entityId = message.entityId;
|
||||
sendToScript({method: 'inspectionCertificate_requestOwnershipVerification', entity: root.entityId});
|
||||
}
|
||||
break;
|
||||
case 'inspectionCertificate_resetCert':
|
||||
titleBarText.text = "Certificate";
|
||||
popText.text = "PROOF OF PURCHASE";
|
||||
root.certificateId = "";
|
||||
root.itemName = "--";
|
||||
root.itemOwner = "--";
|
||||
root.itemEdition = "--";
|
||||
root.dateOfPurchase = "--";
|
||||
root.marketplaceUrl = "";
|
||||
root.isMyCert = false;
|
||||
errorText.text = "";
|
||||
resetCert(true);
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
|
@ -424,7 +584,34 @@ Rectangle {
|
|||
}
|
||||
signal sendToScript(var message);
|
||||
|
||||
function resetCert(alsoResetCertID) {
|
||||
if (alsoResetCertID) {
|
||||
root.entityId = "";
|
||||
root.certificateId = "";
|
||||
}
|
||||
root.certInfoReplaceMode = 5;
|
||||
root.certificateInfoPending = true;
|
||||
root.certificateStatusPending = true;
|
||||
root.useGoldCert = true;
|
||||
root.certTitleTextColor = hifi.colors.darkGray;
|
||||
root.certTextColor = hifi.colors.white;
|
||||
root.infoTextColor = hifi.colors.blueAccent;
|
||||
titleBarText.text = "Certificate";
|
||||
popText.text = "";
|
||||
root.itemName = "--";
|
||||
root.itemOwner = "--";
|
||||
root.itemEdition = "--";
|
||||
root.dateOfPurchase = "--";
|
||||
root.marketplaceUrl = "";
|
||||
root.itemCost = "--";
|
||||
root.isMyCert = false;
|
||||
errorText.text = "";
|
||||
}
|
||||
|
||||
function getFormattedDate(timestamp) {
|
||||
if (timestamp === "--") {
|
||||
return "--";
|
||||
}
|
||||
function addLeadingZero(n) {
|
||||
return n < 10 ? '0' + n : '' + n;
|
||||
}
|
||||
|
@ -449,7 +636,7 @@ Rectangle {
|
|||
|
||||
var min = addLeadingZero(a.getMinutes());
|
||||
var sec = addLeadingZero(a.getSeconds());
|
||||
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
|
||||
return year + '-' + month + '-' + day + ' ' + drawnHour + ':' + min + amOrPm;
|
||||
}
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 185 KiB |
Binary file not shown.
Before Width: | Height: | Size: 62 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -1115,7 +1115,7 @@ Item {
|
|||
|
||||
AnimatedImage {
|
||||
id: sendingMoneyImage;
|
||||
source: "./images/loader.gif"
|
||||
source: "../../common/images/loader.gif"
|
||||
width: 96;
|
||||
height: width;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
|
@ -182,92 +182,103 @@ Rectangle {
|
|||
return;
|
||||
}
|
||||
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
|
||||
var textures = JSON.stringify({ "tex.picture": defaultURL});
|
||||
var shapeType = "box";
|
||||
var dynamic = false;
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, position, gravity);
|
||||
} else {
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
var SHAPE_TYPES = [];ww
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = tabletRoot.customInputDialog({
|
||||
textInput: {
|
||||
label: "Model URL",
|
||||
text: defaultURL
|
||||
},
|
||||
comboBox: {
|
||||
label: "Automatic Collisions",
|
||||
index: SHAPE_TYPE_DEFAULT,
|
||||
items: SHAPE_TYPES
|
||||
},
|
||||
checkBox: {
|
||||
label: "Dynamic",
|
||||
checked: DYNAMIC_DEFAULT,
|
||||
disableForItems: [
|
||||
SHAPE_TYPE_STATIC_MESH
|
||||
],
|
||||
checkStateOnDisable: false,
|
||||
warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors"
|
||||
}
|
||||
});
|
||||
|
||||
prompt.selected.connect(function (jsonResult) {
|
||||
if (jsonResult) {
|
||||
var result = JSON.parse(jsonResult);
|
||||
var url = result.textInput.trim();
|
||||
var shapeType;
|
||||
switch (result.comboBox) {
|
||||
case SHAPE_TYPE_SIMPLE_HULL:
|
||||
shapeType = "simple-hull";
|
||||
break;
|
||||
case SHAPE_TYPE_SIMPLE_COMPOUND:
|
||||
shapeType = "simple-compound";
|
||||
break;
|
||||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = tabletRoot.customInputDialog({
|
||||
textInput: {
|
||||
label: "Model URL",
|
||||
text: defaultURL
|
||||
},
|
||||
comboBox: {
|
||||
label: "Automatic Collisions",
|
||||
index: SHAPE_TYPE_DEFAULT,
|
||||
items: SHAPE_TYPES
|
||||
},
|
||||
checkBox: {
|
||||
label: "Dynamic",
|
||||
checked: DYNAMIC_DEFAULT,
|
||||
disableForItems: [
|
||||
SHAPE_TYPE_STATIC_MESH
|
||||
],
|
||||
checkStateOnDisable: false,
|
||||
warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors"
|
||||
}
|
||||
});
|
||||
|
||||
var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT;
|
||||
if (shapeType === "static-mesh" && dynamic) {
|
||||
// The prompt should prevent this case
|
||||
print("Error: model cannot be both static mesh and dynamic. This should never happen.");
|
||||
} else if (url) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity;
|
||||
if (dynamic) {
|
||||
// Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a
|
||||
// different scripting engine from QTScript.
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10);
|
||||
} else {
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
prompt.selected.connect(function (jsonResult) {
|
||||
if (jsonResult) {
|
||||
var result = JSON.parse(jsonResult);
|
||||
var url = result.textInput.trim();
|
||||
var shapeType;
|
||||
switch (result.comboBox) {
|
||||
case SHAPE_TYPE_SIMPLE_HULL:
|
||||
shapeType = "simple-hull";
|
||||
break;
|
||||
case SHAPE_TYPE_SIMPLE_COMPOUND:
|
||||
shapeType = "simple-compound";
|
||||
break;
|
||||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
}
|
||||
|
||||
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
|
||||
var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT;
|
||||
if (shapeType === "static-mesh" && dynamic) {
|
||||
// The prompt should prevent this case
|
||||
print("Error: model cannot be both static mesh and dynamic. This should never happen.");
|
||||
} else if (url) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity;
|
||||
if (dynamic) {
|
||||
// Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a
|
||||
// different scripting engine from QTScript.
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10);
|
||||
} else {
|
||||
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
}
|
||||
|
||||
// Entities.addEntity doesn't work from QML, so we use this.
|
||||
Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity);
|
||||
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
|
||||
|
||||
// Entities.addEntity doesn't work from QML, so we use this.
|
||||
Entities.addModelEntity(name, url, "", shapeType, dynamic, addPosition, gravity);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function copyURLToClipboard(index) {
|
||||
|
|
|
@ -101,6 +101,17 @@ TabView {
|
|||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/image.svg"
|
||||
text: "IMAGE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newImageButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/25-web-1-01.svg"
|
||||
text: "WEB"
|
||||
|
|
|
@ -106,7 +106,7 @@ Rectangle {
|
|||
height: 400
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
/*Text {
|
||||
id: text3
|
||||
text: qsTr("Material Mode")
|
||||
color: "#ffffff"
|
||||
|
@ -122,7 +122,7 @@ Rectangle {
|
|||
z: 100
|
||||
transformOrigin: Item.Center
|
||||
model: materialArray
|
||||
}
|
||||
}*/
|
||||
|
||||
Row {
|
||||
id: row3
|
||||
|
@ -142,7 +142,7 @@ Rectangle {
|
|||
method: "newMaterialDialogAdd",
|
||||
params: {
|
||||
textInput: materialURL.text,
|
||||
comboBox: materialMode.currentIndex
|
||||
//comboBox: materialMode.currentIndex
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
BIN
interface/resources/qml/js/Utils.jsc
Normal file
BIN
interface/resources/qml/js/Utils.jsc
Normal file
Binary file not shown.
|
@ -67,7 +67,7 @@ QPushButton#revealLogButton {
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
QPushButton#showAllButton {
|
||||
QPushButton#allLogsButton {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background-color: #333333;
|
||||
color: #BBBBBB;
|
||||
|
@ -112,4 +112,11 @@ QComboBox::drop-down {
|
|||
QComboBox::down-arrow {
|
||||
image: url(:/styles/filter.png);
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
QLabel#messageCount {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #3d3d3d;
|
||||
font-size: 11px;
|
||||
}
|
|
@ -335,15 +335,17 @@ static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16;
|
|||
// we will never drop below the 'min' value
|
||||
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1;
|
||||
|
||||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||
static const QString SVO_EXTENSION = ".svo";
|
||||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||
static const QString JPG_EXTENSION = ".jpg";
|
||||
static const QString PNG_EXTENSION = ".png";
|
||||
static const QString SVO_EXTENSION = ".svo";
|
||||
static const QString SVO_JSON_EXTENSION = ".svo.json";
|
||||
static const QString JSON_GZ_EXTENSION = ".json.gz";
|
||||
static const QString JSON_EXTENSION = ".json";
|
||||
static const QString JS_EXTENSION = ".js";
|
||||
static const QString FST_EXTENSION = ".fst";
|
||||
static const QString FBX_EXTENSION = ".fbx";
|
||||
static const QString OBJ_EXTENSION = ".obj";
|
||||
static const QString JS_EXTENSION = ".js";
|
||||
static const QString FST_EXTENSION = ".fst";
|
||||
static const QString FBX_EXTENSION = ".fbx";
|
||||
static const QString OBJ_EXTENSION = ".obj";
|
||||
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
||||
static const QString WEB_VIEW_TAG = "noDownload=true";
|
||||
static const QString ZIP_EXTENSION = ".zip";
|
||||
|
@ -382,7 +384,9 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
|
|||
{ JS_EXTENSION, &Application::askToLoadScript },
|
||||
{ FST_EXTENSION, &Application::askToSetAvatarUrl },
|
||||
{ JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent },
|
||||
{ ZIP_EXTENSION, &Application::importFromZIP }
|
||||
{ ZIP_EXTENSION, &Application::importFromZIP },
|
||||
{ JPG_EXTENSION, &Application::importImage },
|
||||
{ PNG_EXTENSION, &Application::importImage }
|
||||
};
|
||||
|
||||
class DeadlockWatchdogThread : public QThread {
|
||||
|
@ -803,6 +807,8 @@ std::shared_ptr<Cube3DOverlay> _keyboardFocusHighlight{ nullptr };
|
|||
OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID };
|
||||
|
||||
|
||||
OffscreenGLCanvas* _qmlShareContext { nullptr };
|
||||
|
||||
// FIXME hack access to the internal share context for the Chromium helper
|
||||
// Normally we'd want to use QWebEngine::initialize(), but we can't because
|
||||
// our primary context is a QGLWidget, which can't easily be initialized to share
|
||||
|
@ -2301,18 +2307,37 @@ void Application::initializeGL() {
|
|||
_isGLInitialized = true;
|
||||
}
|
||||
|
||||
// Build a shared canvas / context for the Chromium processes
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
// Chromium rendering uses some GL functions that prevent nSight from capturing
|
||||
// frames, so we only create the shared context if nsight is NOT active.
|
||||
if (!nsightActive()) {
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->setObjectName("ChromiumShareContext");
|
||||
_chromiumShareContext->create(_glWidget->qglContext());
|
||||
_chromiumShareContext->makeCurrent();
|
||||
if (!_chromiumShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make chromium shared context current");
|
||||
}
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
} else {
|
||||
qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Build a shared canvas / context for the QML rendering
|
||||
_glWidget->makeCurrent();
|
||||
_qmlShareContext = new OffscreenGLCanvas();
|
||||
_qmlShareContext->setObjectName("QmlShareContext");
|
||||
_qmlShareContext->create(_glWidget->qglContext());
|
||||
if (!_qmlShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make QML shared context current");
|
||||
}
|
||||
OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext());
|
||||
_qmlShareContext->doneCurrent();
|
||||
|
||||
_glWidget->makeCurrent();
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK,
|
||||
|
@ -2348,20 +2373,23 @@ void Application::initializeGL() {
|
|||
if (!_offscreenContext->makeCurrent()) {
|
||||
qFatal("Unable to make offscreen context current");
|
||||
}
|
||||
_offscreenContext->doneCurrent();
|
||||
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
|
||||
|
||||
// The UI can't be created until the primary OpenGL
|
||||
// context is created, because it needs to share
|
||||
// texture resources
|
||||
// Needs to happen AFTER the render engine initialization to access its configuration
|
||||
if (!_offscreenContext->makeCurrent()) {
|
||||
qFatal("Unable to make offscreen context current");
|
||||
}
|
||||
|
||||
initializeUi();
|
||||
qCDebug(interfaceapp, "Initialized Offscreen UI.");
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
// Needs to happen AFTER the QML UI initialization
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
if (!_offscreenContext->makeCurrent()) {
|
||||
qFatal("Unable to make offscreen context current");
|
||||
}
|
||||
init();
|
||||
qCDebug(interfaceapp, "init() complete.");
|
||||
|
||||
|
@ -2372,7 +2400,6 @@ void Application::initializeGL() {
|
|||
|
||||
_idleLoopStdev.reset();
|
||||
|
||||
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
|
||||
|
||||
// Restore the primary GL content for the main thread
|
||||
if (!_offscreenContext->makeCurrent()) {
|
||||
|
@ -2438,29 +2465,70 @@ void Application::initializeUi() {
|
|||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->getTablet(SYSTEM_TABLET);
|
||||
}
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
DeadlockWatchdogThread::pause();
|
||||
offscreenUi->create();
|
||||
DeadlockWatchdogThread::resume();
|
||||
|
||||
auto surfaceContext = offscreenUi->getSurfaceContext();
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
connect(offscreenUi.data(), &hifi::qml::OffscreenSurface::rootContextCreated,
|
||||
this, &Application::onDesktopRootContextCreated);
|
||||
connect(offscreenUi.data(), &hifi::qml::OffscreenSurface::rootItemCreated,
|
||||
this, &Application::onDesktopRootItemCreated);
|
||||
|
||||
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
|
||||
// support the window management and scripting proxies for VR use
|
||||
offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml"));
|
||||
DeadlockWatchdogThread::withPause([&] {
|
||||
offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml"));
|
||||
});
|
||||
// FIXME either expose so that dialogs can set this themselves or
|
||||
// do better detection in the offscreen UI of what has focus
|
||||
offscreenUi->setNavigationFocused(false);
|
||||
|
||||
auto engine = surfaceContext->engine();
|
||||
connect(engine, &QQmlEngine::quit, [] {
|
||||
qApp->quit();
|
||||
});
|
||||
|
||||
|
||||
setupPreferences();
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||
QPointF result = pt;
|
||||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
if (displayPlugin->isHmd()) {
|
||||
getApplicationCompositor().handleRealMouseMoveEvent(false);
|
||||
auto resultVec = getApplicationCompositor().getReticlePosition();
|
||||
result = QPointF(resultVec.x, resultVec.y);
|
||||
}
|
||||
return result.toPoint();
|
||||
});
|
||||
offscreenUi->resume();
|
||||
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
|
||||
resizeGL();
|
||||
});
|
||||
|
||||
// This will set up the input plugins UI
|
||||
_activeInputPlugins.clear();
|
||||
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
|
||||
if (KeyboardMouseDevice::NAME == inputPlugin->getName()) {
|
||||
_keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
|
||||
}
|
||||
if (TouchscreenDevice::NAME == inputPlugin->getName()) {
|
||||
_touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
|
||||
}
|
||||
}
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] {
|
||||
if (isHMDMode()) {
|
||||
showCursor(compositorHelper->getAllowMouseCapture() ?
|
||||
Cursor::Manager::lookupIcon(_preferredCursor.get()) :
|
||||
Cursor::Icon::SYSTEM);
|
||||
}
|
||||
});
|
||||
|
||||
// Pre-create a couple of Web3D overlays to speed up tablet UI
|
||||
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
||||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
}
|
||||
|
||||
|
||||
void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
||||
auto engine = surfaceContext->engine();
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
@ -2542,48 +2610,12 @@ void Application::initializeUi() {
|
|||
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
|
||||
}
|
||||
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||
QPointF result = pt;
|
||||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
if (displayPlugin->isHmd()) {
|
||||
getApplicationCompositor().handleRealMouseMoveEvent(false);
|
||||
auto resultVec = getApplicationCompositor().getReticlePosition();
|
||||
result = QPointF(resultVec.x, resultVec.y);
|
||||
}
|
||||
return result.toPoint();
|
||||
});
|
||||
offscreenUi->resume();
|
||||
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
|
||||
resizeGL();
|
||||
});
|
||||
|
||||
// This will set up the input plugins UI
|
||||
_activeInputPlugins.clear();
|
||||
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
|
||||
if (KeyboardMouseDevice::NAME == inputPlugin->getName()) {
|
||||
_keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
|
||||
}
|
||||
if (TouchscreenDevice::NAME == inputPlugin->getName()) {
|
||||
_touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
|
||||
}
|
||||
}
|
||||
_window->setMenuBar(new Menu());
|
||||
}
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] {
|
||||
if (isHMDMode()) {
|
||||
showCursor(compositorHelper->getAllowMouseCapture() ?
|
||||
Cursor::Manager::lookupIcon(_preferredCursor.get()) :
|
||||
Cursor::Icon::SYSTEM);
|
||||
}
|
||||
});
|
||||
|
||||
// Pre-create a couple of Web3D overlays to speed up tablet UI
|
||||
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
||||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
||||
Stats::show();
|
||||
AvatarInputs::show();
|
||||
}
|
||||
|
||||
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||
|
@ -2822,6 +2854,8 @@ void Application::resizeGL() {
|
|||
QMutexLocker viewLocker(&_viewMutex);
|
||||
_myCamera.loadViewFrustum(_viewFrustum);
|
||||
}
|
||||
|
||||
DependencyManager::get<OffscreenUi>()->resize(fromGlm(displayPlugin->getRecommendedUiSize()));
|
||||
}
|
||||
|
||||
void Application::handleSandboxStatus(QNetworkReply* reply) {
|
||||
|
@ -2937,6 +2971,14 @@ bool Application::importFromZIP(const QString& filePath) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Application::importImage(const QString& urlString) {
|
||||
qCDebug(interfaceapp) << "An image file has been dropped in";
|
||||
QString filepath(urlString);
|
||||
filepath.remove("file:///");
|
||||
addAssetToWorld(filepath, "", false, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
void Application::onPresent(quint32 frameCount) {
|
||||
bool expected = false;
|
||||
|
@ -4030,7 +4072,7 @@ void Application::idle() {
|
|||
// Bit of a hack since there's no device pixel ratio change event I can find.
|
||||
if (offscreenUi->size() != fromGlm(uiSize)) {
|
||||
qCDebug(interfaceapp) << "Device pixel ratio changed, triggering resize to " << uiSize;
|
||||
offscreenUi->resize(fromGlm(uiSize), true);
|
||||
offscreenUi->resize(fromGlm(uiSize));
|
||||
_offscreenContext->makeCurrent();
|
||||
}
|
||||
}
|
||||
|
@ -6529,7 +6571,8 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q
|
|||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||
} else {
|
||||
// to prevent files that aren't models from being loaded into world automatically
|
||||
if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) {
|
||||
if (filePath.endsWith(OBJ_EXTENSION) || filePath.endsWith(FBX_EXTENSION) ||
|
||||
filePath.endsWith(JPG_EXTENSION) || filePath.endsWith(PNG_EXTENSION)) {
|
||||
addAssetToWorldAddEntity(filePath, mapping);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Zipped contents are not supported entity files";
|
||||
|
@ -6546,8 +6589,17 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) {
|
|||
EntityItemProperties properties;
|
||||
properties.setType(EntityTypes::Model);
|
||||
properties.setName(mapping.right(mapping.length() - 1));
|
||||
properties.setModelURL("atp:" + mapping);
|
||||
properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND);
|
||||
if (filePath.endsWith(PNG_EXTENSION) || filePath.endsWith(JPG_EXTENSION)) {
|
||||
QJsonObject textures {
|
||||
{"tex.picture", QString("atp:" + mapping) }
|
||||
};
|
||||
properties.setModelURL("https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx");
|
||||
properties.setTextures(QJsonDocument(textures).toJson(QJsonDocument::Compact));
|
||||
properties.setShapeType(SHAPE_TYPE_BOX);
|
||||
} else {
|
||||
properties.setModelURL("atp:" + mapping);
|
||||
properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND);
|
||||
}
|
||||
properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar.
|
||||
properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions.
|
||||
glm::vec3 positionOffset = getMyAvatar()->getWorldOrientation() * (getMyAvatar()->getSensorToWorldScale() * glm::vec3(0.0f, 0.0f, -2.0f));
|
||||
|
@ -6971,10 +7023,10 @@ void Application::loadAvatarBrowser() const {
|
|||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
|
||||
// Get a screenshot and save it
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename);
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated) {
|
||||
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
|
@ -6986,9 +7038,9 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
});
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCameraSnapshot() {
|
||||
postLambdaEvent([this] {
|
||||
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
|
||||
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
|
||||
postLambdaEvent([filename, this] {
|
||||
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename);
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
|
||||
});
|
||||
}
|
||||
|
@ -7362,9 +7414,7 @@ void Application::updateDisplayMode() {
|
|||
action->setChecked(true);
|
||||
}
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
|
||||
_offscreenContext->makeCurrent();
|
||||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection);
|
||||
|
|
|
@ -266,8 +266,10 @@ public:
|
|||
|
||||
float getGameLoopRate() const { return _gameLoopCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void takeSecondaryCameraSnapshot();
|
||||
// Note that takeSnapshot has a default value, as this method is used internally.
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
|
||||
void takeSecondaryCameraSnapshot(const QString& filename);
|
||||
|
||||
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
|
||||
|
||||
graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
|
@ -389,6 +391,8 @@ public slots:
|
|||
void setPreferredCursor(const QString& cursor);
|
||||
|
||||
private slots:
|
||||
void onDesktopRootItemCreated(QQuickItem* qmlContext);
|
||||
void onDesktopRootContextCreated(QQmlContext* qmlContext);
|
||||
void showDesktop();
|
||||
void clearDomainOctreeDetails();
|
||||
void clearDomainAvatars();
|
||||
|
@ -469,6 +473,7 @@ private:
|
|||
bool importJSONFromURL(const QString& urlString);
|
||||
bool importSVOFromURL(const QString& urlString);
|
||||
bool importFromZIP(const QString& filePath);
|
||||
bool importImage(const QString& urlString);
|
||||
|
||||
bool nearbyEntitiesAreReadyForPhysics();
|
||||
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
|
||||
|
|
|
@ -121,13 +121,13 @@ void AvatarBookmarks::addBookmark() {
|
|||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap *bookmark = new QVariantMap;
|
||||
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
QVariantMap bookmark;
|
||||
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -144,13 +144,13 @@ void AvatarEntitiesBookmarks::addBookmark() {
|
|||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
QVariantMap *bookmark = new QVariantMap;
|
||||
bookmark->insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION);
|
||||
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark->insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
|
||||
QVariantMap bookmark;
|
||||
bookmark.insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION);
|
||||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
|
||||
using RenderArgsPointer = std::shared_ptr<RenderArgs>;
|
||||
|
||||
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
|
||||
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
|
|
@ -959,6 +959,18 @@ void MyAvatar::restoreRoleAnimation(const QString& role) {
|
|||
_skeletonModel->getRig().restoreRoleAnimation(role);
|
||||
}
|
||||
|
||||
void MyAvatar::saveAvatarUrl() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
if (qApp->getSaveAvatarOverrideUrl() || !qApp->getAvatarOverrideUrl().isValid() ) {
|
||||
settings.setValue("fullAvatarURL",
|
||||
_fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ?
|
||||
"" :
|
||||
_fullAvatarURLFromPreferences.toString());
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void MyAvatar::saveData() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
|
@ -1452,6 +1464,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE);
|
||||
_headBoneSet.clear();
|
||||
_cauterizationNeedsUpdate = true;
|
||||
saveAvatarUrl();
|
||||
emit skeletonChanged();
|
||||
|
||||
}
|
||||
|
|
|
@ -646,6 +646,7 @@ private:
|
|||
|
||||
void simulate(float deltaTime);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
void saveAvatarUrl();
|
||||
virtual void render(RenderArgs* renderArgs) override;
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "LimitlessConnection.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <src/InterfaceLogging.h>
|
||||
#include <src/ui/AvatarInputs.h>
|
||||
#include "LimitlessConnection.h"
|
||||
#include "LimitlessVoiceRecognitionScriptingInterface.h"
|
||||
|
||||
LimitlessConnection::LimitlessConnection() :
|
||||
|
|
|
@ -430,12 +430,12 @@ bool WindowScriptingInterface::setDisplayTexture(const QString& name) {
|
|||
return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false.
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
qApp->takeSnapshot(notify, includeAnimated, aspectRatio);
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||
qApp->takeSnapshot(notify, includeAnimated, aspectRatio, filename);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSecondaryCameraSnapshot() {
|
||||
qApp->takeSecondaryCameraSnapshot();
|
||||
void WindowScriptingInterface::takeSecondaryCameraSnapshot(const QString& filename) {
|
||||
qApp->takeSecondaryCameraSnapshot(filename);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& href) {
|
||||
|
|
|
@ -334,6 +334,8 @@ public slots:
|
|||
* @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is <code>0</code> the
|
||||
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
|
||||
* dimensions is adjusted in order to match the aspect ratio.
|
||||
* @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ",jpg".
|
||||
* otherwise, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'
|
||||
* @example <caption>Using the snapshot function and signals.</caption>
|
||||
* function onStillSnapshotTaken(path, notify) {
|
||||
* print("Still snapshot taken: " + path);
|
||||
|
@ -355,15 +357,19 @@ public slots:
|
|||
* var notify = true;
|
||||
* var animated = true;
|
||||
* var aspect = 1920 / 1080;
|
||||
* Window.takeSnapshot(notify, animated, aspect);
|
||||
* var filename = QString();
|
||||
* Window.takeSnapshot(notify, animated, aspect, filename);
|
||||
*/
|
||||
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
|
||||
* @function Window.takeSecondaryCameraSnapshot
|
||||
* @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ".jpg"
|
||||
*
|
||||
* var filename = QString();
|
||||
*/
|
||||
void takeSecondaryCameraSnapshot();
|
||||
void takeSecondaryCameraSnapshot(const QString& filename = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that
|
||||
|
|
|
@ -25,7 +25,6 @@ Setting::Handle<bool> showAudioToolsSetting { QStringList { "AvatarInputs", "sho
|
|||
AvatarInputs* AvatarInputs::getInstance() {
|
||||
if (!INSTANCE) {
|
||||
AvatarInputs::registerType();
|
||||
AvatarInputs::show();
|
||||
Q_ASSERT(INSTANCE);
|
||||
}
|
||||
return INSTANCE;
|
||||
|
|
|
@ -29,8 +29,8 @@ const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
|
|||
const int SEARCH_TEXT_WIDTH = 240;
|
||||
const int TIME_STAMP_LENGTH = 16;
|
||||
const int FONT_WEIGHT = 75;
|
||||
const QColor HIGHLIGHT_COLOR = QColor("#3366CC");
|
||||
const QColor BOLD_COLOR = QColor("#445c8c");
|
||||
const QColor HIGHLIGHT_COLOR = QColor("#00B4EF");
|
||||
const QColor BOLD_COLOR = QColor("#1080B8");
|
||||
const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]";
|
||||
|
||||
BaseLogDialog::BaseLogDialog(QWidget* parent) : QDialog(parent, Qt::Window) {
|
||||
|
@ -182,6 +182,7 @@ void BaseLogDialog::updateSelection() {
|
|||
Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
|
||||
boldFormat.setFontWeight(FONT_WEIGHT);
|
||||
boldFormat.setForeground(BOLD_COLOR);
|
||||
keywordFormat.setFontWeight(FONT_WEIGHT);
|
||||
keywordFormat.setForeground(HIGHLIGHT_COLOR);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QLabel>
|
||||
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
|
||||
const int REVEAL_BUTTON_WIDTH = 122;
|
||||
const int CLEAR_FILTER_BUTTON_WIDTH = 80;
|
||||
const int ALL_LOGS_BUTTON_WIDTH = 90;
|
||||
const int MARGIN_LEFT = 25;
|
||||
const int DEBUG_CHECKBOX_WIDTH = 70;
|
||||
const int INFO_CHECKBOX_WIDTH = 65;
|
||||
|
@ -142,6 +143,11 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
|
|||
_filterDropdown->addItem("qml");
|
||||
connect(_filterDropdown, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &LogDialog::handleFilterDropdownChanged);
|
||||
|
||||
_leftPad += COMBOBOX_WIDTH + MARGIN_LEFT + MARGIN_LEFT;
|
||||
_messageCount = new QLabel("", this);
|
||||
_messageCount->setObjectName("messageCount");
|
||||
_messageCount->show();
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
|
@ -149,12 +155,13 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
|
|||
_extraDebuggingBox->show();
|
||||
connect(_extraDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleExtraDebuggingCheckbox);
|
||||
|
||||
_clearFilterButton = new QPushButton("Clear Filters", this);
|
||||
_allLogsButton = new QPushButton("All Messages", this);
|
||||
// set object name for css styling
|
||||
_clearFilterButton->setObjectName("showAllButton");
|
||||
_clearFilterButton->show();
|
||||
connect(_clearFilterButton, &QPushButton::clicked, this, &LogDialog::handleClearFilterButton);
|
||||
handleClearFilterButton();
|
||||
|
||||
_allLogsButton->setObjectName("allLogsButton");
|
||||
_allLogsButton->show();
|
||||
connect(_allLogsButton, &QPushButton::clicked, this, &LogDialog::handleAllLogsButton);
|
||||
handleAllLogsButton();
|
||||
|
||||
auto windowGeometry = _windowGeometry.get();
|
||||
if (windowGeometry.isValid()) {
|
||||
|
@ -168,11 +175,15 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
|
|||
ELEMENT_MARGIN,
|
||||
REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_clearFilterButton->setGeometry(width() - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH,
|
||||
_allLogsButton->setGeometry(width() - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH,
|
||||
THIRD_ROW,
|
||||
CLEAR_FILTER_BUTTON_WIDTH,
|
||||
ALL_LOGS_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH,
|
||||
_extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_messageCount->setGeometry(_leftPad,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
|
@ -187,13 +198,13 @@ void LogDialog::handleRevealButton() {
|
|||
_logger->locateLog();
|
||||
}
|
||||
|
||||
void LogDialog::handleClearFilterButton() {
|
||||
void LogDialog::handleAllLogsButton() {
|
||||
_logger->setExtraDebugging(false);
|
||||
_extraDebuggingBox->setCheckState(Qt::Unchecked);
|
||||
_logger->setDebugPrint(false);
|
||||
_debugPrintBox->setCheckState(Qt::Unchecked);
|
||||
_logger->setInfoPrint(false);
|
||||
_infoPrintBox->setCheckState(Qt::Unchecked);
|
||||
_logger->setDebugPrint(true);
|
||||
_debugPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setInfoPrint(true);
|
||||
_infoPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setCriticalPrint(true);
|
||||
_criticalPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setWarningPrint(true);
|
||||
|
@ -270,40 +281,67 @@ void LogDialog::appendLogLine(QString logLine) {
|
|||
if (logLine.contains(DEBUG_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->debugPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
} else if (logLine.contains(INFO_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->infoPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
} else if (logLine.contains(CRITICAL_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->criticalPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
} else if (logLine.contains(WARNING_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->warningPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
} else if (logLine.contains(SUPPRESS_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->suppressPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
} else if (logLine.contains(FATAL_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->fatalPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
} else {
|
||||
if (_logger->unknownPrint()) {
|
||||
if (_logger->unknownPrint() && logLine.trimmed() != "") {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_count++;
|
||||
updateMessageCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LogDialog::printLogFile() {
|
||||
_count = 0;
|
||||
_logTextBox->clear();
|
||||
QString log = getCurrentLog();
|
||||
QStringList logList = log.split('\n');
|
||||
for (const auto& message : logList) {
|
||||
appendLogLine(message);
|
||||
}
|
||||
updateMessageCount();
|
||||
}
|
||||
|
||||
void LogDialog::updateMessageCount() {
|
||||
_countLabel = QString::number(_count);
|
||||
if (_count != 1) {
|
||||
_countLabel.append(" log messages");
|
||||
}
|
||||
else {
|
||||
_countLabel.append(" log message");
|
||||
}
|
||||
_messageCount->setText(_countLabel);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QResizeEvent;
|
||||
class AbstractLoggerInterface;
|
||||
|
||||
|
@ -41,19 +42,21 @@ private slots:
|
|||
void handleFatalPrintBox(int);
|
||||
void handleUnknownPrintBox(int);
|
||||
void handleFilterDropdownChanged(int);
|
||||
void handleClearFilterButton();
|
||||
void handleAllLogsButton();
|
||||
void printLogFile();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
QString getCurrentLog() override;
|
||||
void printLogFile();
|
||||
void updateMessageCount();
|
||||
|
||||
|
||||
private:
|
||||
QCheckBox* _extraDebuggingBox;
|
||||
QPushButton* _revealLogButton;
|
||||
QPushButton* _clearFilterButton;
|
||||
QPushButton* _allLogsButton;
|
||||
QCheckBox* _debugPrintBox;
|
||||
QCheckBox* _infoPrintBox;
|
||||
QCheckBox* _criticalPrintBox;
|
||||
|
@ -62,10 +65,12 @@ private:
|
|||
QCheckBox* _fatalPrintBox;
|
||||
QCheckBox* _unknownPrintBox;
|
||||
QComboBox* _filterDropdown;
|
||||
QLabel* _messageCount;
|
||||
QString _filterSelection;
|
||||
|
||||
QString _countLabel;
|
||||
AbstractLoggerInterface* _logger;
|
||||
Setting::Handle<QRect> _windowGeometry;
|
||||
int _count = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_LogDialog_h
|
||||
|
|
|
@ -73,9 +73,9 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
|||
return data;
|
||||
}
|
||||
|
||||
QString Snapshot::saveSnapshot(QImage image) {
|
||||
QString Snapshot::saveSnapshot(QImage image, const QString& filename) {
|
||||
|
||||
QFile* snapshotFile = savedFileForSnapshot(image, false);
|
||||
QFile* snapshotFile = savedFileForSnapshot(image, false, filename);
|
||||
|
||||
// we don't need the snapshot file, so close it, grab its filename and delete it
|
||||
snapshotFile->close();
|
||||
|
@ -92,7 +92,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
|
|||
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));
|
||||
}
|
||||
|
||||
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
||||
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
|
||||
|
||||
// adding URL to snapshot
|
||||
QUrl currentURL = DependencyManager::get<AddressManager>()->currentShareableAddress();
|
||||
|
@ -104,7 +104,15 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
|||
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
QString filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
|
||||
// If user has requested specific filename then use it, else create the filename
|
||||
// 'jpg" is appended, as the image is saved in jpg format. This is the case for all snapshots
|
||||
// (see definition of FILENAME_PATH_FORMAT)
|
||||
QString filename;
|
||||
if (!userSelectedFilename.isNull()) {
|
||||
filename = userSelectedFilename + ".jpg";
|
||||
} else {
|
||||
filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
|
||||
}
|
||||
|
||||
const int IMAGE_QUALITY = 100;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class Snapshot : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
public:
|
||||
static QString saveSnapshot(QImage image);
|
||||
static QString saveSnapshot(QImage image, const QString& filename);
|
||||
static QTemporaryFile* saveTempSnapshot(QImage image);
|
||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||
|
||||
|
@ -51,7 +51,7 @@ public slots:
|
|||
Q_INVOKABLE QString getSnapshotsLocation();
|
||||
Q_INVOKABLE void setSnapshotsLocation(const QString& location);
|
||||
private:
|
||||
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
|
||||
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString());
|
||||
};
|
||||
|
||||
#endif // hifi_Snapshot_h
|
||||
|
|
|
@ -48,7 +48,6 @@ QString getTextureMemoryPressureModeString();
|
|||
Stats* Stats::getInstance() {
|
||||
if (!INSTANCE) {
|
||||
Stats::registerType();
|
||||
Stats::show();
|
||||
Q_ASSERT(INSTANCE);
|
||||
}
|
||||
return INSTANCE;
|
||||
|
|
|
@ -274,82 +274,88 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
if (entityProperties.getClientOnly()) {
|
||||
if (entityProperties.verifyStaticCertificateProperties()) {
|
||||
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
|
||||
if (entityProperties.verifyStaticCertificateProperties()) {
|
||||
if (entityProperties.getClientOnly()) {
|
||||
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
|
||||
|
||||
if (entityServer) {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = entityProperties.getCertificateID();
|
||||
networkRequest.setUrl(requestURL);
|
||||
if (entityServer) {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = entityProperties.getCertificateID();
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = NULL;
|
||||
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
QNetworkReply* networkReply = NULL;
|
||||
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
|
||||
qCDebug(entities) << "invalid_reason not empty";
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
|
||||
qCDebug(entities) << "'transfer_status' is 'failed'";
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
|
||||
qCDebug(entities) << "'transfer_status' is 'pending'";
|
||||
} else {
|
||||
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
|
||||
|
||||
QByteArray certID = entityProperties.getCertificateID().toUtf8();
|
||||
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
|
||||
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
|
||||
|
||||
int certIDByteArraySize = certID.length();
|
||||
int textByteArraySize = text.length();
|
||||
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
|
||||
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(textByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
|
||||
challengeOwnershipPacket->write(certID);
|
||||
challengeOwnershipPacket->write(text);
|
||||
challengeOwnershipPacket->write(nodeToChallengeByteArray);
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
|
||||
|
||||
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
|
||||
return;
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
|
||||
qCDebug(entities) << "invalid_reason not empty";
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
|
||||
qCDebug(entities) << "'transfer_status' is 'failed'";
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
|
||||
qCDebug(entities) << "'transfer_status' is 'pending'";
|
||||
} else {
|
||||
startChallengeOwnershipTimer();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
|
||||
"More info:" << networkReply->readAll();
|
||||
}
|
||||
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
qCWarning(context_overlay) << "Couldn't get Entity Server!";
|
||||
}
|
||||
QByteArray certID = entityProperties.getCertificateID().toUtf8();
|
||||
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
|
||||
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
|
||||
|
||||
int certIDByteArraySize = certID.length();
|
||||
int textByteArraySize = text.length();
|
||||
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
|
||||
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(textByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
|
||||
challengeOwnershipPacket->write(certID);
|
||||
challengeOwnershipPacket->write(text);
|
||||
challengeOwnershipPacket->write(nodeToChallengeByteArray);
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
|
||||
|
||||
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
|
||||
return;
|
||||
} else {
|
||||
startChallengeOwnershipTimer();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
|
||||
"More info:" << networkReply->readAll();
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
qCWarning(context_overlay) << "Couldn't get Entity Server!";
|
||||
}
|
||||
} else {
|
||||
// We don't currently verify ownership of entities that aren't Avatar Entities,
|
||||
// so they always pass Ownership Verification. It's necessary to emit this signal
|
||||
// so that the Inspection Certificate can continue its information-grabbing process.
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
_challengeOwnershipTimeoutTimer.stop();
|
||||
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
|
||||
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
|
||||
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
|
||||
}
|
||||
} else {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
_challengeOwnershipTimeoutTimer.stop();
|
||||
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
|
||||
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,12 +363,10 @@ static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspection
|
|||
void ContextOverlayInterface::openInspectionCertificate() {
|
||||
// lets open the tablet to the inspection certificate QML
|
||||
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
|
||||
setLastInspectedEntity(_currentEntityWithContextOverlay);
|
||||
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
|
||||
_hmdScriptingInterface->openTablet();
|
||||
|
||||
setLastInspectedEntity(_currentEntityWithContextOverlay);
|
||||
requestOwnershipVerification(_lastInspectedEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
bool getEnabled() { return _enabled; }
|
||||
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
|
||||
void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; }
|
||||
void requestOwnershipVerification(const QUuid& entityID);
|
||||
Q_INVOKABLE void requestOwnershipVerification(const QUuid& entityID);
|
||||
EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; }
|
||||
|
||||
signals:
|
||||
|
|
|
@ -134,7 +134,11 @@ void Web3DOverlay::destroyWebSurface() {
|
|||
|
||||
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, _webSurface);
|
||||
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
// FIXME prevents crash on shutdown, but we shoudln't have to do this check
|
||||
if (offscreenCache) {
|
||||
offscreenCache->release(QML, _webSurface);
|
||||
}
|
||||
_webSurface.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1244,7 +1244,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
||||
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
|
||||
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
|
||||
if (!leftArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
if (!leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
|
@ -1291,7 +1291,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
|
||||
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
|
||||
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
|
||||
if (!rightArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
if (!rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
|
|
|
@ -13,5 +13,6 @@ include_hifi_library_headers(entities-renderer)
|
|||
include_hifi_library_headers(audio)
|
||||
include_hifi_library_headers(entities)
|
||||
include_hifi_library_headers(octree)
|
||||
include_hifi_library_headers(task)
|
||||
|
||||
target_bullet()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(TARGET_NAME entities-renderer)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils)
|
||||
setup_hifi_library(Network Script)
|
||||
link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image ui pointers)
|
||||
link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gl)
|
||||
include_hifi_library_headers(ktx)
|
||||
|
@ -13,6 +13,7 @@ include_hifi_library_headers(fbx)
|
|||
include_hifi_library_headers(entities)
|
||||
include_hifi_library_headers(avatars)
|
||||
include_hifi_library_headers(controllers)
|
||||
include_hifi_library_headers(task)
|
||||
|
||||
target_bullet()
|
||||
target_polyvox()
|
||||
|
|
|
@ -221,19 +221,17 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
|||
});
|
||||
};
|
||||
|
||||
{
|
||||
// FIXME use the surface cache instead of explicit creation
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
||||
_webSurface->create();
|
||||
}
|
||||
|
||||
// FIXME use the surface cache instead of explicit creation
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
|
||||
_webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
|
||||
// Let us interact with the keyboard
|
||||
_webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [this](QQmlContext* surfaceContext) {
|
||||
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
|
||||
surfaceContext->setContextProperty("desktop", QVariant());
|
||||
// Let us interact with the keyboard
|
||||
surfaceContext->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
});
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
loadSourceURL();
|
||||
_webSurface->resume();
|
||||
|
|
|
@ -299,7 +299,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
}
|
||||
}
|
||||
|
||||
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType,
|
||||
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType,
|
||||
bool dynamic, const glm::vec3& position, const glm::vec3& gravity) {
|
||||
_activityTracking.addedEntityCount++;
|
||||
|
||||
|
@ -311,6 +311,9 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin
|
|||
properties.setDynamic(dynamic);
|
||||
properties.setPosition(position);
|
||||
properties.setGravity(gravity);
|
||||
if (!textures.isEmpty()) {
|
||||
properties.setTextures(textures);
|
||||
}
|
||||
return addEntity(properties);
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ public slots:
|
|||
|
||||
/// temporary method until addEntity can be used from QJSEngine
|
||||
/// Deliberately not adding jsdoc, only used internally.
|
||||
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic,
|
||||
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic,
|
||||
const glm::vec3& position, const glm::vec3& gravity);
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -913,18 +913,25 @@ void EntityTree::findEntities(RecurseOctreeOperation& elementFilter,
|
|||
recurseTreeWithOperation(elementFilter, nullptr);
|
||||
}
|
||||
|
||||
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) {
|
||||
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) const {
|
||||
EntityItemID entityID(id);
|
||||
return findEntityByEntityItemID(entityID);
|
||||
}
|
||||
|
||||
EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /*const*/ {
|
||||
EntityItemPointer foundEntity = NULL;
|
||||
EntityTreeElementPointer containingElement = getContainingElement(entityID);
|
||||
if (containingElement) {
|
||||
foundEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) const {
|
||||
EntityItemPointer foundEntity = nullptr;
|
||||
{
|
||||
QReadLocker locker(&_entityMapLock);
|
||||
foundEntity = _entityMap.value(entityID);
|
||||
}
|
||||
if (foundEntity && !foundEntity->getElement()) {
|
||||
// special case to maintain legacy behavior:
|
||||
// if the entity is in the map but not in the tree
|
||||
// then pretend the entity doesn't exist
|
||||
return EntityItemPointer(nullptr);
|
||||
} else {
|
||||
return foundEntity;
|
||||
}
|
||||
return foundEntity;
|
||||
}
|
||||
|
||||
void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties) {
|
||||
|
|
|
@ -132,9 +132,9 @@ public:
|
|||
/// \param position point of query in world-frame (meters)
|
||||
/// \param targetRadius radius of query (meters)
|
||||
EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius);
|
||||
EntityItemPointer findEntityByID(const QUuid& id);
|
||||
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID);
|
||||
virtual SpatiallyNestablePointer findByID(const QUuid& id) override { return findEntityByID(id); }
|
||||
EntityItemPointer findEntityByID(const QUuid& id) const;
|
||||
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const;
|
||||
virtual SpatiallyNestablePointer findByID(const QUuid& id) const override { return findEntityByID(id); }
|
||||
|
||||
EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID
|
||||
|
||||
|
|
|
@ -180,6 +180,8 @@ public:
|
|||
float emissiveIntensity{ 1.0f };
|
||||
float ambientFactor{ 1.0f };
|
||||
|
||||
float bumpMultiplier { 1.0f }; // TODO: to be implemented
|
||||
|
||||
QString materialID;
|
||||
QString name;
|
||||
QString shadingModel;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "OBJReader.h"
|
||||
|
||||
#include <ctype.h> // .obj files are not locale-specific. The C/ASCII charset applies.
|
||||
#include <sstream>
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QIODevice>
|
||||
|
@ -35,6 +36,11 @@ QHash<QString, float> COMMENT_SCALE_HINTS = {{"This file uses centimeters as uni
|
|||
|
||||
const QString SMART_DEFAULT_MATERIAL_NAME = "High Fidelity smart default material name";
|
||||
|
||||
const float ILLUMINATION_MODEL_MIN_OPACITY = 0.1f;
|
||||
const float ILLUMINATION_MODEL_APPLY_SHININESS = 0.5f;
|
||||
const float ILLUMINATION_MODEL_APPLY_ROUGHNESS = 1.0f;
|
||||
const float ILLUMINATION_MODEL_APPLY_NON_METALLIC = 0.0f;
|
||||
|
||||
namespace {
|
||||
template<class T>
|
||||
T& checked_at(QVector<T>& vector, int i) {
|
||||
|
@ -70,6 +76,7 @@ int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) {
|
|||
}
|
||||
switch (ch) {
|
||||
case '#': {
|
||||
_datum = "";
|
||||
_comment = _device->readLine(); // stash comment for a future call to getComment
|
||||
return COMMENT_TOKEN;
|
||||
}
|
||||
|
@ -256,7 +263,14 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
default:
|
||||
materials[matName] = currentMaterial;
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename;
|
||||
qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel <<
|
||||
" shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity <<
|
||||
" diffuse color:" << currentMaterial.diffuseColor << " specular color:" <<
|
||||
currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor <<
|
||||
" diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" <<
|
||||
currentMaterial.specularTextureFilename << " emissive texture:" <<
|
||||
currentMaterial.emissiveTextureFilename << " bump texture:" <<
|
||||
currentMaterial.bumpTextureFilename;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -272,20 +286,46 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName;
|
||||
#endif
|
||||
currentMaterial.diffuseTextureFilename = "";
|
||||
currentMaterial.emissiveTextureFilename = "";
|
||||
currentMaterial.specularTextureFilename = "";
|
||||
currentMaterial.bumpTextureFilename = "";
|
||||
} else if (token == "Ns") {
|
||||
currentMaterial.shininess = tokenizer.getFloat();
|
||||
} else if ((token == "d") || (token == "Tr")) {
|
||||
} else if (token == "Ni") {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << tokenizer.getFloat();
|
||||
#else
|
||||
tokenizer.getFloat();
|
||||
#endif
|
||||
} else if (token == "d") {
|
||||
currentMaterial.opacity = tokenizer.getFloat();
|
||||
} else if (token == "Tr") {
|
||||
currentMaterial.opacity = 1.0f - tokenizer.getFloat();
|
||||
} else if (token == "illum") {
|
||||
currentMaterial.illuminationModel = tokenizer.getFloat();
|
||||
} else if (token == "Tf") {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << tokenizer.getVec3();
|
||||
#else
|
||||
tokenizer.getVec3();
|
||||
#endif
|
||||
} else if (token == "Ka") {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();
|
||||
qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();;
|
||||
#else
|
||||
tokenizer.getVec3();
|
||||
#endif
|
||||
} else if (token == "Kd") {
|
||||
currentMaterial.diffuseColor = tokenizer.getVec3();
|
||||
} else if (token == "Ke") {
|
||||
currentMaterial.emissiveColor = tokenizer.getVec3();
|
||||
} else if (token == "Ks") {
|
||||
currentMaterial.specularColor = tokenizer.getVec3();
|
||||
} else if ((token == "map_Kd") || (token == "map_Ks")) {
|
||||
QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8();
|
||||
} else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) {
|
||||
const QByteArray textureLine = tokenizer.getLineAsDatum();
|
||||
QByteArray filename;
|
||||
OBJMaterialTextureOptions textureOptions;
|
||||
parseTextureLine(textureLine, filename, textureOptions);
|
||||
if (filename.endsWith(".tga")) {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url;
|
||||
|
@ -294,11 +334,104 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
}
|
||||
if (token == "map_Kd") {
|
||||
currentMaterial.diffuseTextureFilename = filename;
|
||||
} else if( token == "map_Ks" ) {
|
||||
} else if (token == "map_Ke") {
|
||||
currentMaterial.emissiveTextureFilename = filename;
|
||||
} else if (token == "map_Ks" ) {
|
||||
currentMaterial.specularTextureFilename = filename;
|
||||
} else if ((token == "map_bump") || (token == "bump")) {
|
||||
currentMaterial.bumpTextureFilename = filename;
|
||||
currentMaterial.bumpTextureOptions = textureOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions) {
|
||||
// Texture options reference http://paulbourke.net/dataformats/mtl/
|
||||
// and https://wikivisually.com/wiki/Material_Template_Library
|
||||
|
||||
std::istringstream iss(textureLine.toStdString());
|
||||
const std::vector<std::string> parser(std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>());
|
||||
|
||||
uint i = 0;
|
||||
while (i < parser.size()) {
|
||||
if (i + 1 < parser.size() && parser[i][0] == '-') {
|
||||
const std::string& option = parser[i++];
|
||||
if (option == "-blendu" || option == "-blendv") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& onoff = parser[i++];
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str();
|
||||
#endif
|
||||
} else if (option == "-bm") {
|
||||
const std::string& bm = parser[i++];
|
||||
textureOptions.bumpMultiplier = std::stof(bm);
|
||||
} else if (option == "-boost") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& boost = parser[i++];
|
||||
float boostFloat = std::stof(boost);
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << boost.c_str();
|
||||
#endif
|
||||
} else if (option == "-cc") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& onoff = parser[i++];
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str();
|
||||
#endif
|
||||
} else if (option == "-clamp") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& onoff = parser[i++];
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str();
|
||||
#endif
|
||||
} else if (option == "-imfchan") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& imfchan = parser[i++];
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << imfchan.c_str();
|
||||
#endif
|
||||
} else if (option == "-mm") {
|
||||
if (i + 1 < parser.size()) {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& mmBase = parser[i++];
|
||||
const std::string& mmGain = parser[i++];
|
||||
float mmBaseFloat = std::stof(mmBase);
|
||||
float mmGainFloat = std::stof(mmGain);
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << mmBase.c_str() << mmGain.c_str();
|
||||
#endif
|
||||
}
|
||||
} else if (option == "-o" || option == "-s" || option == "-t") {
|
||||
if (i + 2 < parser.size()) {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& u = parser[i++];
|
||||
const std::string& v = parser[i++];
|
||||
const std::string& w = parser[i++];
|
||||
float uFloat = std::stof(u);
|
||||
float vFloat = std::stof(v);
|
||||
float wFloat = std::stof(w);
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << u.c_str() << v.c_str() << w.c_str();
|
||||
#endif
|
||||
}
|
||||
} else if (option == "-texres") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& texres = parser[i++];
|
||||
float texresFloat = std::stof(texres);
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << texres.c_str();
|
||||
#endif
|
||||
} else if (option == "-type") {
|
||||
#ifdef WANT_DEBUG
|
||||
const std::string& type = parser[i++];
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << type.c_str();
|
||||
#endif
|
||||
} else if (option[0] == '-') {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring unsupported texture option" << option.c_str();
|
||||
#endif
|
||||
}
|
||||
} else { // assume filename at end when no more options
|
||||
std::string filenameString = parser[i++];
|
||||
while (i < parser.size()) { // filename has space in it
|
||||
filenameString += " " + parser[i++];
|
||||
}
|
||||
filename = filenameString.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<bool, QByteArray> requestData(QUrl& url) {
|
||||
|
@ -745,7 +878,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
|||
}
|
||||
geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor,
|
||||
objMaterial.specularColor,
|
||||
glm::vec3(0.0f),
|
||||
objMaterial.emissiveColor,
|
||||
objMaterial.shininess,
|
||||
objMaterial.opacity);
|
||||
FBXMaterial& fbxMaterial = geometry.materials[materialID];
|
||||
|
@ -759,17 +892,88 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
|||
if (!objMaterial.specularTextureFilename.isEmpty()) {
|
||||
fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename;
|
||||
}
|
||||
if (!objMaterial.emissiveTextureFilename.isEmpty()) {
|
||||
fbxMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename;
|
||||
}
|
||||
if (!objMaterial.bumpTextureFilename.isEmpty()) {
|
||||
fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename;
|
||||
fbxMaterial.normalTexture.isBumpmap = true;
|
||||
fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier;
|
||||
}
|
||||
|
||||
modelMaterial->setEmissive(fbxMaterial.emissiveColor);
|
||||
modelMaterial->setAlbedo(fbxMaterial.diffuseColor);
|
||||
modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor));
|
||||
modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess));
|
||||
|
||||
if (fbxMaterial.opacity <= 0.0f) {
|
||||
modelMaterial->setOpacity(1.0f);
|
||||
} else {
|
||||
modelMaterial->setOpacity(fbxMaterial.opacity);
|
||||
bool applyTransparency = false;
|
||||
bool applyShininess = false;
|
||||
bool applyRoughness = false;
|
||||
bool applyNonMetallic = false;
|
||||
bool fresnelOn = false;
|
||||
|
||||
// Illumination model reference http://paulbourke.net/dataformats/mtl/
|
||||
switch (objMaterial.illuminationModel) {
|
||||
case 0: // Color on and Ambient off
|
||||
// We don't support ambient = do nothing?
|
||||
break;
|
||||
case 1: // Color on and Ambient on
|
||||
// We don't support ambient = do nothing?
|
||||
break;
|
||||
case 2: // Highlight on
|
||||
// Change specular intensity = do nothing for now?
|
||||
break;
|
||||
case 3: // Reflection on and Ray trace on
|
||||
applyShininess = true;
|
||||
break;
|
||||
case 4: // Transparency: Glass on and Reflection: Ray trace on
|
||||
applyTransparency = true;
|
||||
applyShininess = true;
|
||||
break;
|
||||
case 5: // Reflection: Fresnel on and Ray trace on
|
||||
applyShininess = true;
|
||||
fresnelOn = true;
|
||||
break;
|
||||
case 6: // Transparency: Refraction on and Reflection: Fresnel off and Ray trace on
|
||||
applyTransparency = true;
|
||||
applyNonMetallic = true;
|
||||
applyShininess = true;
|
||||
break;
|
||||
case 7: // Transparency: Refraction on and Reflection: Fresnel on and Ray trace on
|
||||
applyTransparency = true;
|
||||
applyNonMetallic = true;
|
||||
applyShininess = true;
|
||||
fresnelOn = true;
|
||||
break;
|
||||
case 8: // Reflection on and Ray trace off
|
||||
applyShininess = true;
|
||||
break;
|
||||
case 9: // Transparency: Glass on and Reflection: Ray trace off
|
||||
applyTransparency = true;
|
||||
applyNonMetallic = true;
|
||||
applyRoughness = true;
|
||||
break;
|
||||
case 10: // Casts shadows onto invisible surfaces
|
||||
// Do nothing?
|
||||
break;
|
||||
}
|
||||
|
||||
if (applyTransparency) {
|
||||
fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY);
|
||||
}
|
||||
if (applyShininess) {
|
||||
modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS);
|
||||
} else if (applyRoughness) {
|
||||
modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_ROUGHNESS);
|
||||
}
|
||||
if (applyNonMetallic) {
|
||||
modelMaterial->setMetallic(ILLUMINATION_MODEL_APPLY_NON_METALLIC);
|
||||
}
|
||||
if (fresnelOn) {
|
||||
modelMaterial->setFresnel(glm::vec3(1.0f));
|
||||
}
|
||||
|
||||
modelMaterial->setOpacity(fbxMaterial.opacity);
|
||||
}
|
||||
|
||||
return geometryPtr;
|
||||
|
|
|
@ -48,6 +48,11 @@ private:
|
|||
void addFrom(const OBJFace* face, int index);
|
||||
};
|
||||
|
||||
class OBJMaterialTextureOptions {
|
||||
public:
|
||||
float bumpMultiplier { 1.0f };
|
||||
}
|
||||
;
|
||||
// Materials and references to material names can come in any order, and different mesh parts can refer to the same material.
|
||||
// Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files.
|
||||
class OBJMaterial {
|
||||
|
@ -56,11 +61,16 @@ public:
|
|||
float opacity;
|
||||
glm::vec3 diffuseColor;
|
||||
glm::vec3 specularColor;
|
||||
glm::vec3 emissiveColor;
|
||||
QByteArray diffuseTextureFilename;
|
||||
QByteArray specularTextureFilename;
|
||||
QByteArray emissiveTextureFilename;
|
||||
QByteArray bumpTextureFilename;
|
||||
OBJMaterialTextureOptions bumpTextureOptions;
|
||||
int illuminationModel;
|
||||
bool used { false };
|
||||
bool userSpecifiesUV { false };
|
||||
OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {}
|
||||
OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), illuminationModel(-1) {}
|
||||
};
|
||||
|
||||
class OBJReader: public QObject { // QObject so we can make network requests.
|
||||
|
@ -84,6 +94,7 @@ private:
|
|||
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry,
|
||||
float& scaleGuess, bool combineParts);
|
||||
void parseMaterialLibrary(QIODevice* device);
|
||||
void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions);
|
||||
bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format.
|
||||
|
||||
int _partCounter { 0 };
|
||||
|
|
|
@ -81,6 +81,10 @@ bool isRenderThread() {
|
|||
return QThread::currentThread() == RENDER_THREAD;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define USE_GLES 1
|
||||
#endif
|
||||
|
||||
namespace gl {
|
||||
void withSavedContext(const std::function<void()>& f) {
|
||||
// Save the original GL context, because creating a QML surface will create a new context
|
||||
|
@ -91,4 +95,47 @@ namespace gl {
|
|||
savedContext->makeCurrent(savedSurface);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkGLError(const char* name) {
|
||||
GLenum error = glGetError();
|
||||
if (!error) {
|
||||
return false;
|
||||
}
|
||||
switch (error) {
|
||||
case GL_INVALID_ENUM:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
|
||||
break;
|
||||
#if !defined(USE_GLES)
|
||||
case GL_STACK_UNDERFLOW:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow.";
|
||||
break;
|
||||
case GL_STACK_OVERFLOW:
|
||||
qCWarning(glLogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow.";
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool checkGLErrorDebug(const char* name) {
|
||||
#ifdef DEBUG
|
||||
return checkGLError(name);
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <functional>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "GLLogging.h"
|
||||
|
||||
// 16 bits of depth precision
|
||||
#define DEFAULT_GL_DEPTH_BUFFER_BITS 16
|
||||
// 8 bits of stencil buffer (typically you really only need 1 bit for functionality
|
||||
|
@ -43,6 +45,13 @@ bool isRenderThread();
|
|||
|
||||
namespace gl {
|
||||
void withSavedContext(const std::function<void()>& f);
|
||||
}
|
||||
|
||||
bool checkGLError(const char* name);
|
||||
|
||||
bool checkGLErrorDebug(const char* name);
|
||||
|
||||
} // namespace gl
|
||||
|
||||
#define CHECK_GL_ERROR() ::gl::checkGLErrorDebug(__FUNCTION__)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
#include "GLShared.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <fstream>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <GPUIdent.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <fstream>
|
||||
|
||||
Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl")
|
||||
Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl")
|
||||
|
@ -21,47 +22,6 @@ Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail")
|
|||
|
||||
namespace gpu { namespace gl {
|
||||
|
||||
bool checkGLError(const char* name) {
|
||||
GLenum error = glGetError();
|
||||
if (!error) {
|
||||
return false;
|
||||
} else {
|
||||
switch (error) {
|
||||
case GL_INVALID_ENUM:
|
||||
qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
|
||||
break;
|
||||
case GL_STACK_UNDERFLOW:
|
||||
qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow.";
|
||||
break;
|
||||
case GL_STACK_OVERFLOW:
|
||||
qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow.";
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkGLErrorDebug(const char* name) {
|
||||
#ifdef DEBUG
|
||||
return checkGLError(name);
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
gpu::Size getFreeDedicatedMemory() {
|
||||
Size result { 0 };
|
||||
static bool nvidiaMemorySupported { true };
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define hifi_gpu_GLShared_h
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include <gpu/Format.h>
|
||||
#include <gpu/Context.h>
|
||||
|
@ -114,9 +115,6 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
|
|||
GL_INT_2_10_10_10_REV,
|
||||
};
|
||||
|
||||
bool checkGLError(const char* name = nullptr);
|
||||
bool checkGLErrorDebug(const char* name = nullptr);
|
||||
|
||||
class GLBackend;
|
||||
|
||||
template <typename GPUType>
|
||||
|
@ -141,11 +139,8 @@ class GLShader;
|
|||
class GLTexture;
|
||||
struct ShaderObject;
|
||||
|
||||
|
||||
} } // namespace gpu::gl
|
||||
|
||||
#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -413,7 +413,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui
|
|||
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
|
||||
sourceMip._offset = bufferOffset;
|
||||
bufferOffset += sourceMip._size;
|
||||
gpu::gl::checkGLError();
|
||||
::gl::checkGLError(__FUNCTION__);
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
|
@ -458,7 +458,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui
|
|||
#endif
|
||||
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
|
||||
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
|
||||
gpu::gl::checkGLError();
|
||||
::gl::checkGLError(__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
#define hifi_gpu_GLShared_h
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
|
||||
#include <gpu/Forward.h>
|
||||
#include <gpu/Format.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail)
|
||||
|
@ -143,8 +146,6 @@ struct ShaderObject;
|
|||
|
||||
} } // namespace gpu::gl
|
||||
|
||||
#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) {
|
|||
samplerDesc._wrapModeU = Sampler::WRAP_BORDER;
|
||||
samplerDesc._wrapModeV = Sampler::WRAP_BORDER;
|
||||
samplerDesc._filter = Sampler::FILTER_MIN_MAG_LINEAR;
|
||||
samplerDesc._comparisonFunc = LESS_EQUAL;
|
||||
samplerDesc._comparisonFunc = LESS;
|
||||
|
||||
depthTexture->setSampler(Sampler(samplerDesc));
|
||||
framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
|
||||
|
|
|
@ -666,7 +666,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
|
|||
}
|
||||
|
||||
emit locationChangeRequired(newPosition, orientationChanged,
|
||||
LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation,
|
||||
trigger == LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation,
|
||||
shouldFace
|
||||
);
|
||||
|
||||
|
|
|
@ -989,7 +989,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
|
|||
}
|
||||
|
||||
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
|
||||
QWriteLocker{ &_avatarGainMapLock };
|
||||
QWriteLocker lock{ &_avatarGainMapLock };
|
||||
_avatarGainMap[nodeID] = gain;
|
||||
|
||||
} else {
|
||||
|
@ -1001,7 +1001,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
|
|||
}
|
||||
|
||||
float NodeList::getAvatarGain(const QUuid& nodeID) {
|
||||
QReadLocker{ &_avatarGainMapLock };
|
||||
QReadLocker lock{ &_avatarGainMapLock };
|
||||
auto it = _avatarGainMap.find(nodeID);
|
||||
if (it != _avatarGainMap.cend()) {
|
||||
return it->second;
|
||||
|
|
|
@ -31,7 +31,6 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::MaterialEntities);
|
||||
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -64,8 +64,14 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc
|
|||
return voxelSizeScale / powf(2.0f, renderLevel);
|
||||
}
|
||||
|
||||
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
const float maxScale = (float)TREE_SCALE;
|
||||
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO;
|
||||
return atan(maxScale / visibleDistanceAtMaxScale);
|
||||
}
|
||||
|
||||
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
// Smallest visible element is 1cm
|
||||
const float smallestSize = 0.01f;
|
||||
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ float calculateRenderAccuracy(const glm::vec3& position,
|
|||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
|
||||
|
||||
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
|
||||
// MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
|
||||
const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians
|
||||
|
|
|
@ -214,9 +214,10 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
|||
}
|
||||
|
||||
void PluginManager::disableDisplayPlugin(const QString& name) {
|
||||
std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){
|
||||
auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){
|
||||
return plugin->getName() == name;
|
||||
});
|
||||
_displayPlugins.erase(it, _displayPlugins.end());
|
||||
}
|
||||
|
||||
|
||||
|
|
6
libraries/qml/CMakeLists.txt
Normal file
6
libraries/qml/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(TARGET_NAME qml)
|
||||
setup_hifi_library(Multimedia Network Qml Quick WebChannel WebSockets ${PLATFORM_QT_COMPONENTS})
|
||||
link_hifi_libraries(shared networking gl)
|
||||
|
||||
# Required for some low level GL interaction in the OffscreenQMLSurface
|
||||
target_opengl()
|
11
libraries/qml/src/qml/Logging.cpp
Normal file
11
libraries/qml/src/qml/Logging.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2016/03/01
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(qmlLogging, "hifi.qml")
|
16
libraries/qml/src/qml/Logging.h
Normal file
16
libraries/qml/src/qml/Logging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2018/01/04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Controllers_Logging_h
|
||||
#define hifi_Controllers_Logging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(qmlLogging)
|
||||
|
||||
#endif
|
355
libraries/qml/src/qml/OffscreenSurface.cpp
Normal file
355
libraries/qml/src/qml/OffscreenSurface.cpp
Normal file
|
@ -0,0 +1,355 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015-05-13
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "OffscreenSurface.h"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtQml/QtQml>
|
||||
#include <QtQml/QQmlEngine>
|
||||
#include <QtQml/QQmlComponent>
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickRenderControl>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "impl/SharedObject.h"
|
||||
#include "impl/TextureCache.h"
|
||||
|
||||
using namespace hifi::qml;
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {
|
||||
return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension));
|
||||
}
|
||||
|
||||
static QSize clampSize(const QSize& qsize, uint32_t maxDimension) {
|
||||
return fromGlm(clampSize(toGlm(qsize), maxDimension));
|
||||
}
|
||||
|
||||
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
|
||||
|
||||
void OffscreenSurface::initializeEngine(QQmlEngine* engine) {
|
||||
}
|
||||
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
size_t OffscreenSurface::getUsedTextureMemory() {
|
||||
return SharedObject::getTextureCache().getUsedTextureMemory();
|
||||
}
|
||||
|
||||
void OffscreenSurface::setSharedContext(QOpenGLContext* sharedContext) {
|
||||
SharedObject::setSharedContext(sharedContext);
|
||||
}
|
||||
|
||||
std::function<void(uint32_t, void*)> OffscreenSurface::getDiscardLambda() {
|
||||
return [](uint32_t texture, void* fence) {
|
||||
SharedObject::getTextureCache().releaseTexture({ texture, static_cast<GLsync>(fence) });
|
||||
};
|
||||
}
|
||||
|
||||
OffscreenSurface::OffscreenSurface()
|
||||
: _sharedObject(new impl::SharedObject()) {
|
||||
}
|
||||
|
||||
OffscreenSurface::~OffscreenSurface() {
|
||||
disconnect(qApp);
|
||||
_sharedObject->destroy();
|
||||
}
|
||||
|
||||
bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) {
|
||||
if (!_sharedObject) {
|
||||
return false;
|
||||
}
|
||||
hifi::qml::impl::TextureAndFence typedTextureAndFence;
|
||||
bool result = _sharedObject->fetchTexture(typedTextureAndFence);
|
||||
textureAndFence = typedTextureAndFence;
|
||||
return result;
|
||||
}
|
||||
|
||||
void OffscreenSurface::resize(const QSize& newSize_) {
|
||||
const uint32_t MAX_OFFSCREEN_DIMENSION = 4096;
|
||||
_sharedObject->setSize(clampSize(newSize_, MAX_OFFSCREEN_DIMENSION));
|
||||
}
|
||||
|
||||
QQuickItem* OffscreenSurface::getRootItem() {
|
||||
return _sharedObject->getRootItem();
|
||||
}
|
||||
|
||||
void OffscreenSurface::clearCache() {
|
||||
_sharedObject->getContext()->engine()->clearComponentCache();
|
||||
}
|
||||
|
||||
QPointF OffscreenSurface::mapToVirtualScreen(const QPointF& originalPoint) {
|
||||
return _mouseTranslator(originalPoint);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
//
|
||||
// Event handling customization
|
||||
//
|
||||
|
||||
bool OffscreenSurface::filterEnabled(QObject* originalDestination, QEvent* event) const {
|
||||
if (!_sharedObject || _sharedObject->getWindow() == originalDestination) {
|
||||
return false;
|
||||
}
|
||||
// Only intercept events while we're in an active state
|
||||
if (_sharedObject->isPaused()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) {
|
||||
if (!filterEnabled(originalDestination, event)) {
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
// Don't intercept our own events, or we enter an infinite recursion
|
||||
{
|
||||
auto rootItem = _sharedObject->getRootItem();
|
||||
auto quickWindow = _sharedObject->getWindow();
|
||||
QObject* recurseTest = originalDestination;
|
||||
while (recurseTest) {
|
||||
Q_ASSERT(recurseTest != rootItem && recurseTest != quickWindow);
|
||||
recurseTest = recurseTest->parent();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease: {
|
||||
event->ignore();
|
||||
if (QCoreApplication::sendEvent(_sharedObject->getWindow(), event)) {
|
||||
return event->isAccepted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::Wheel: {
|
||||
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
|
||||
QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos());
|
||||
QWheelEvent mappedEvent(transformedPos, wheelEvent->delta(), wheelEvent->buttons(), wheelEvent->modifiers(),
|
||||
wheelEvent->orientation());
|
||||
mappedEvent.ignore();
|
||||
if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) {
|
||||
return mappedEvent.isAccepted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseMove: {
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos());
|
||||
QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->screenPos(), mouseEvent->button(),
|
||||
mouseEvent->buttons(), mouseEvent->modifiers());
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
// TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install
|
||||
// need to investigate into why this crash is happening.
|
||||
//_qmlContext->setContextProperty("lastMousePosition", transformedPos);
|
||||
}
|
||||
mappedEvent.ignore();
|
||||
if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) {
|
||||
return mappedEvent.isAccepted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OffscreenSurface::pause() {
|
||||
_sharedObject->pause();
|
||||
}
|
||||
|
||||
void OffscreenSurface::resume() {
|
||||
_sharedObject->resume();
|
||||
}
|
||||
|
||||
bool OffscreenSurface::isPaused() const {
|
||||
return _sharedObject->isPaused();
|
||||
}
|
||||
|
||||
void OffscreenSurface::setProxyWindow(QWindow* window) {
|
||||
_sharedObject->setProxyWindow(window);
|
||||
}
|
||||
|
||||
QObject* OffscreenSurface::getEventHandler() {
|
||||
return getWindow();
|
||||
}
|
||||
|
||||
QQuickWindow* OffscreenSurface::getWindow() {
|
||||
return _sharedObject->getWindow();
|
||||
}
|
||||
|
||||
QSize OffscreenSurface::size() const {
|
||||
return _sharedObject->getSize();
|
||||
}
|
||||
|
||||
QQmlContext* OffscreenSurface::getSurfaceContext() {
|
||||
return _sharedObject->getContext();
|
||||
}
|
||||
|
||||
void OffscreenSurface::setMaxFps(uint8_t maxFps) {
|
||||
_sharedObject->setMaxFps(maxFps);
|
||||
}
|
||||
|
||||
void OffscreenSurface::load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) {
|
||||
loadInternal(qmlSource, false, parent, [callback](QQmlContext* context, QQuickItem* newItem) {
|
||||
QJSValue(callback).call(QJSValueList() << context->engine()->newQObject(newItem));
|
||||
});
|
||||
}
|
||||
|
||||
void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback) {
|
||||
loadInternal(qmlSource, createNewContext, nullptr, callback);
|
||||
}
|
||||
|
||||
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
|
||||
load(qmlSource, true, callback);
|
||||
}
|
||||
|
||||
void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
|
||||
load(qmlSource, false, callback);
|
||||
}
|
||||
|
||||
void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback) {
|
||||
return load(QUrl(qmlSourceFile), callback);
|
||||
}
|
||||
|
||||
void OffscreenSurface::loadInternal(const QUrl& qmlSource,
|
||||
bool createNewContext,
|
||||
QQuickItem* parent,
|
||||
const QmlContextObjectCallback& callback) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qFatal("Called load on a non-surface thread");
|
||||
}
|
||||
// Synchronous loading may take a while; restart the deadlock timer
|
||||
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
||||
|
||||
if (!getRootItem()) {
|
||||
_sharedObject->create(this);
|
||||
}
|
||||
|
||||
QUrl finalQmlSource = qmlSource;
|
||||
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
|
||||
finalQmlSource = getSurfaceContext()->resolvedUrl(qmlSource);
|
||||
}
|
||||
|
||||
if (!getRootItem()) {
|
||||
_sharedObject->setObjectName(finalQmlSource.toString());
|
||||
}
|
||||
|
||||
auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext);
|
||||
auto qmlComponent = new QQmlComponent(getSurfaceContext()->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
|
||||
if (qmlComponent->isLoading()) {
|
||||
connect(qmlComponent, &QQmlComponent::statusChanged, this,
|
||||
[=](QQmlComponent::Status) { finishQmlLoad(qmlComponent, targetContext, parent, callback); });
|
||||
return;
|
||||
}
|
||||
|
||||
finishQmlLoad(qmlComponent, targetContext, parent, callback);
|
||||
}
|
||||
|
||||
void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent,
|
||||
QQmlContext* qmlContext,
|
||||
QQuickItem* parent,
|
||||
const QmlContextObjectCallback& callback) {
|
||||
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
qCWarning(qmlLogging) << error.url() << error.line() << error;
|
||||
}
|
||||
qmlComponent->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* newObject = qmlComponent->beginCreate(qmlContext);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
qCWarning(qmlLogging) << error.url() << error.line() << error;
|
||||
}
|
||||
if (!getRootItem()) {
|
||||
qFatal("Unable to finish loading QML root");
|
||||
}
|
||||
qmlComponent->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newObject) {
|
||||
if (!getRootItem()) {
|
||||
qFatal("Could not load object as root item");
|
||||
return;
|
||||
}
|
||||
qCWarning(qmlLogging) << "Unable to load QML item";
|
||||
return;
|
||||
}
|
||||
|
||||
qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
|
||||
// All quick items should be focusable
|
||||
QQuickItem* newItem = qobject_cast<QQuickItem*>(newObject);
|
||||
if (newItem) {
|
||||
// Make sure we make items focusable (critical for
|
||||
// supporting keyboard shortcuts)
|
||||
newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
|
||||
}
|
||||
|
||||
bool rootCreated = getRootItem() != nullptr;
|
||||
|
||||
// Make sure we will call callback for this codepath
|
||||
// Call this before qmlComponent->completeCreate() otherwise ghost window appears
|
||||
// If we already have a root, just set a couple of flags and the ancestry
|
||||
if (rootCreated) {
|
||||
callback(qmlContext, newItem);
|
||||
if (!parent) {
|
||||
parent = getRootItem();
|
||||
}
|
||||
// Allow child windows to be destroyed from JS
|
||||
QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership);
|
||||
newObject->setParent(parent);
|
||||
newItem->setParentItem(parent);
|
||||
} else {
|
||||
// The root item is ready. Associate it with the window.
|
||||
_sharedObject->setRootItem(newItem);
|
||||
}
|
||||
|
||||
qmlComponent->completeCreate();
|
||||
qmlComponent->deleteLater();
|
||||
|
||||
onItemCreated(qmlContext, newItem);
|
||||
connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant)));
|
||||
|
||||
if (!rootCreated) {
|
||||
onRootCreated();
|
||||
emit rootItemCreated(newItem);
|
||||
// Call this callback after rootitem is set, otherwise VrMenu wont work
|
||||
callback(qmlContext, newItem);
|
||||
}
|
||||
}
|
||||
|
||||
QQmlContext* OffscreenSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {
|
||||
QQmlContext* targetContext = parent ? QQmlEngine::contextForObject(parent) : getSurfaceContext();
|
||||
if (!targetContext) {
|
||||
targetContext = getSurfaceContext();
|
||||
}
|
||||
|
||||
if (getRootItem() && forceNewContext) {
|
||||
targetContext = new QQmlContext(targetContext, targetContext->engine());
|
||||
}
|
||||
|
||||
return targetContext;
|
||||
}
|
127
libraries/qml/src/qml/OffscreenSurface.h
Normal file
127
libraries/qml/src/qml/OffscreenSurface.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015-04-04
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_qml_OffscreenSurface_h
|
||||
#define hifi_qml_OffscreenSurface_h
|
||||
|
||||
#include <atomic>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QSize>
|
||||
#include <QtCore/QPointF>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <QtQml/QJSValue>
|
||||
|
||||
class QWindow;
|
||||
class QOpenGLContext;
|
||||
class QQmlContext;
|
||||
class QQmlEngine;
|
||||
class QQmlComponent;
|
||||
class QQuickWindow;
|
||||
class QQuickItem;
|
||||
class OffscreenQmlSharedObject;
|
||||
|
||||
namespace hifi { namespace qml {
|
||||
|
||||
namespace impl {
|
||||
class SharedObject;
|
||||
}
|
||||
|
||||
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
|
||||
|
||||
class OffscreenSurface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK;
|
||||
|
||||
using TextureAndFence = std::pair<uint32_t, void*>;
|
||||
using MouseTranslator = std::function<QPoint(const QPointF&)>;
|
||||
|
||||
static void setSharedContext(QOpenGLContext* context);
|
||||
|
||||
OffscreenSurface();
|
||||
virtual ~OffscreenSurface();
|
||||
|
||||
QSize size() const;
|
||||
virtual void resize(const QSize& size);
|
||||
void clearCache();
|
||||
|
||||
void setMaxFps(uint8_t maxFps);
|
||||
// Optional values for event handling
|
||||
void setProxyWindow(QWindow* window);
|
||||
void setMouseTranslator(const MouseTranslator& mouseTranslator) { _mouseTranslator = mouseTranslator; }
|
||||
|
||||
void pause();
|
||||
void resume();
|
||||
bool isPaused() const;
|
||||
|
||||
QQuickItem* getRootItem();
|
||||
QQuickWindow* getWindow();
|
||||
QObject* getEventHandler();
|
||||
QQmlContext* getSurfaceContext();
|
||||
|
||||
// Checks to see if a new texture is available. If one is, the function returns true and
|
||||
// textureAndFence will be populated with the texture ID and a fence which will be signalled
|
||||
// when the texture is safe to read.
|
||||
// Returns false if no new texture is available
|
||||
bool fetchTexture(TextureAndFence& textureAndFence);
|
||||
|
||||
static std::function<void(uint32_t, void*)> getDiscardLambda();
|
||||
static size_t getUsedTextureMemory();
|
||||
QPointF mapToVirtualScreen(const QPointF& originalPoint);
|
||||
|
||||
// For use from QML/JS
|
||||
Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
|
||||
|
||||
// For use from C++
|
||||
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
||||
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
||||
Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
||||
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
||||
|
||||
public slots:
|
||||
virtual void onFocusObjectChanged(QObject* newFocus) {}
|
||||
|
||||
signals:
|
||||
void rootContextCreated(QQmlContext* rootContext);
|
||||
void rootItemCreated(QQuickItem* rootContext);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* originalDestination, QEvent* event) override;
|
||||
bool filterEnabled(QObject* originalDestination, QEvent* event) const;
|
||||
|
||||
virtual void initializeEngine(QQmlEngine* engine);
|
||||
virtual void loadInternal(const QUrl& qmlSource,
|
||||
bool createNewContext,
|
||||
QQuickItem* parent,
|
||||
const QmlContextObjectCallback& callback) final;
|
||||
virtual void finishQmlLoad(QQmlComponent* qmlComponent,
|
||||
QQmlContext* qmlContext,
|
||||
QQuickItem* parent,
|
||||
const QmlContextObjectCallback& onQmlLoadedCallback) final;
|
||||
|
||||
virtual void onRootCreated() {}
|
||||
virtual void onItemCreated(QQmlContext* context, QQuickItem* newItem) {}
|
||||
virtual void onRootContextCreated(QQmlContext* qmlContext) {}
|
||||
|
||||
virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext);
|
||||
private:
|
||||
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
|
||||
friend class hifi::qml::impl::SharedObject;
|
||||
impl::SharedObject* const _sharedObject;
|
||||
};
|
||||
|
||||
}} // namespace hifi::qml
|
||||
|
||||
#endif
|
12
libraries/qml/src/qml/impl/Profiling.cpp
Normal file
12
libraries/qml/src/qml/impl/Profiling.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Profiling.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml")
|
||||
Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")
|
13
libraries/qml/src/qml/impl/Profiling.h
Normal file
13
libraries/qml/src/qml/impl/Profiling.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <Profile.h>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render_qml)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render_qml_gl)
|
29
libraries/qml/src/qml/impl/RenderControl.cpp
Normal file
29
libraries/qml/src/qml/impl/RenderControl.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "RenderControl.h"
|
||||
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
RenderControl::RenderControl(QObject* parent) : QQuickRenderControl(parent) {
|
||||
|
||||
}
|
||||
|
||||
void RenderControl::setRenderWindow(QWindow* renderWindow) {
|
||||
_renderWindow = renderWindow;
|
||||
}
|
||||
|
||||
QWindow* RenderControl::renderWindow(QPoint* offset) {
|
||||
if (nullptr == _renderWindow) {
|
||||
return QQuickRenderControl::renderWindow(offset);
|
||||
}
|
||||
if (nullptr != offset) {
|
||||
offset->rx() = offset->ry() = 0;
|
||||
}
|
||||
return _renderWindow;
|
||||
}
|
||||
|
26
libraries/qml/src/qml/impl/RenderControl.h
Normal file
26
libraries/qml/src/qml/impl/RenderControl.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtQuick/QQuickRenderControl>
|
||||
|
||||
namespace hifi { namespace qml { namespace impl {
|
||||
|
||||
class RenderControl : public QQuickRenderControl {
|
||||
public:
|
||||
RenderControl(QObject* parent = Q_NULLPTR);
|
||||
void setRenderWindow(QWindow* renderWindow);
|
||||
|
||||
protected:
|
||||
QWindow* renderWindow(QPoint* offset) override;
|
||||
|
||||
private:
|
||||
QWindow* _renderWindow{ nullptr };
|
||||
};
|
||||
|
||||
}}} // namespace hifi::qml::impl
|
169
libraries/qml/src/qml/impl/RenderEventHandler.cpp
Normal file
169
libraries/qml/src/qml/impl/RenderEventHandler.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "RenderEventHandler.h"
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include "Profiling.h"
|
||||
#include "SharedObject.h"
|
||||
#include "TextureCache.h"
|
||||
#include "RenderControl.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
bool RenderEventHandler::event(QEvent* e) {
|
||||
switch (static_cast<OffscreenEvent::Type>(e->type())) {
|
||||
case OffscreenEvent::Render:
|
||||
onRender();
|
||||
return true;
|
||||
|
||||
case OffscreenEvent::Initialize:
|
||||
onInitalize();
|
||||
return true;
|
||||
|
||||
case OffscreenEvent::Quit:
|
||||
onQuit();
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
RenderEventHandler::RenderEventHandler(SharedObject* shared, QThread* targetThread)
|
||||
: _shared(shared) {
|
||||
// Create the GL canvas in the same thread as the share canvas
|
||||
if (!_canvas.create(SharedObject::getSharedContext())) {
|
||||
qFatal("Unable to create new offscreen GL context");
|
||||
}
|
||||
|
||||
moveToThread(targetThread);
|
||||
_canvas.moveToThreadWithContext(targetThread);
|
||||
}
|
||||
|
||||
void RenderEventHandler::onInitalize() {
|
||||
if (_shared->isQuit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_canvas.makeCurrent()) {
|
||||
qFatal("Unable to make QML rendering context current on render thread");
|
||||
}
|
||||
_shared->initializeRenderControl(_canvas.getContext());
|
||||
}
|
||||
|
||||
void RenderEventHandler::resize() {
|
||||
PROFILE_RANGE(render_qml_gl, __FUNCTION__);
|
||||
auto targetSize = _shared->getSize();
|
||||
if (_currentSize != targetSize) {
|
||||
auto& offscreenTextures = SharedObject::getTextureCache();
|
||||
// Release hold on the textures of the old size
|
||||
if (_currentSize != QSize()) {
|
||||
_shared->releaseTextureAndFence();
|
||||
offscreenTextures.releaseSize(_currentSize);
|
||||
}
|
||||
|
||||
_currentSize = targetSize;
|
||||
|
||||
// Acquire the new texture size
|
||||
if (_currentSize != QSize()) {
|
||||
qCDebug(qmlLogging) << "Upating offscreen textures to " << _currentSize.width() << " x " << _currentSize.height();
|
||||
offscreenTextures.acquireSize(_currentSize);
|
||||
if (_depthStencil) {
|
||||
glDeleteRenderbuffers(1, &_depthStencil);
|
||||
_depthStencil = 0;
|
||||
}
|
||||
glGenRenderbuffers(1, &_depthStencil);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _currentSize.width(), _currentSize.height());
|
||||
if (!_fbo) {
|
||||
glGenFramebuffers(1, &_fbo);
|
||||
}
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
|
||||
glViewport(0, 0, _currentSize.width(), _currentSize.height());
|
||||
glScissor(0, 0, _currentSize.width(), _currentSize.height());
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderEventHandler::onRender() {
|
||||
if (_shared->isQuit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_canvas.getContext() != QOpenGLContextWrapper::currentContext()) {
|
||||
qFatal("QML rendering context not current on render thread");
|
||||
}
|
||||
|
||||
PROFILE_RANGE(render_qml_gl, __FUNCTION__);
|
||||
|
||||
if (!_shared->preRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
resize();
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render_qml_gl, "render");
|
||||
GLuint texture = SharedObject::getTextureCache().acquireTexture(_currentSize);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
|
||||
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
|
||||
if (nsightActive()) {
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
} else {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
_shared->_quickWindow->setRenderTarget(_fbo, _currentSize);
|
||||
_shared->_renderControl->render();
|
||||
}
|
||||
_shared->_lastRenderTime = usecTimestampNow();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_shared->updateTextureAndFence({ texture, fence });
|
||||
// Fence will be used in another thread / context, so a flush is required
|
||||
_shared->_quickWindow->resetOpenGLState();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderEventHandler::onQuit() {
|
||||
if (_canvas.getContext() != QOpenGLContextWrapper::currentContext()) {
|
||||
qFatal("QML rendering context not current on render thread");
|
||||
}
|
||||
|
||||
if (_depthStencil) {
|
||||
glDeleteRenderbuffers(1, &_depthStencil);
|
||||
_depthStencil = 0;
|
||||
}
|
||||
|
||||
if (_fbo) {
|
||||
glDeleteFramebuffers(1, &_fbo);
|
||||
_fbo = 0;
|
||||
}
|
||||
|
||||
_shared->shutdownRendering(_canvas, _currentSize);
|
||||
// Release the reference to the shared object. This will allow it to
|
||||
// be destroyed (should happen on it's own thread).
|
||||
_shared->deleteLater();
|
||||
|
||||
deleteLater();
|
||||
|
||||
QThread::currentThread()->quit();
|
||||
}
|
56
libraries/qml/src/qml/impl/RenderEventHandler.h
Normal file
56
libraries/qml/src/qml/impl/RenderEventHandler.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtGui/qevent.h>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
|
||||
namespace hifi { namespace qml { namespace impl {
|
||||
|
||||
class SharedObject;
|
||||
|
||||
class OffscreenEvent : public QEvent {
|
||||
public:
|
||||
enum Type {
|
||||
Initialize = QEvent::User + 1,
|
||||
Render,
|
||||
Quit
|
||||
};
|
||||
|
||||
OffscreenEvent(Type type) : QEvent(static_cast<QEvent::Type>(type)) {}
|
||||
};
|
||||
|
||||
/* The render event handler lives on the QML rendering thread for a given surface
|
||||
* (each surface has a dedicated rendering thread) and handles events of type
|
||||
* OffscreenEvent to do one time initialization or destruction, and to actually
|
||||
* perform the render.
|
||||
*/
|
||||
class RenderEventHandler : public QObject {
|
||||
public:
|
||||
RenderEventHandler(SharedObject* shared, QThread* targetThread);
|
||||
|
||||
private:
|
||||
bool event(QEvent* e) override;
|
||||
void onInitalize();
|
||||
void resize();
|
||||
void onRender();
|
||||
void onQuit();
|
||||
|
||||
SharedObject* const _shared;
|
||||
OffscreenGLCanvas _canvas;
|
||||
QSize _currentSize;
|
||||
|
||||
uint32_t _fbo{ 0 };
|
||||
uint32_t _depthStencil{ 0 };
|
||||
};
|
||||
|
||||
}}} // namespace hifi::qml::impl
|
448
libraries/qml/src/qml/impl/SharedObject.cpp
Normal file
448
libraries/qml/src/qml/impl/SharedObject.cpp
Normal file
|
@ -0,0 +1,448 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "SharedObject.h"
|
||||
|
||||
#include <QtCore/qlogging.h>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQml/QQmlEngine>
|
||||
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include "../OffscreenSurface.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
#include "Profiling.h"
|
||||
#include "RenderControl.h"
|
||||
#include "RenderEventHandler.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
// Time between receiving a request to render the offscreen UI actually triggering
|
||||
// the render. Could possibly be increased depending on the framerate we expect to
|
||||
// achieve.
|
||||
// This has the effect of capping the framerate at 200
|
||||
static const int MIN_TIMER_MS = 5;
|
||||
|
||||
using namespace hifi::qml;
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
TextureCache offscreenTextures;
|
||||
|
||||
TextureCache& SharedObject::getTextureCache() {
|
||||
return offscreenTextures;
|
||||
}
|
||||
|
||||
#define OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY "com.highfidelity.qml.gl.sharedContext"
|
||||
void SharedObject::setSharedContext(QOpenGLContext* sharedContext) {
|
||||
qApp->setProperty(OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY, QVariant::fromValue<void*>(sharedContext));
|
||||
if (QOpenGLContextWrapper::currentContext() != sharedContext) {
|
||||
qFatal("The shared context must be the current context when setting");
|
||||
}
|
||||
}
|
||||
|
||||
QOpenGLContext* SharedObject::getSharedContext() {
|
||||
return static_cast<QOpenGLContext*>(qApp->property(OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY).value<void*>());
|
||||
}
|
||||
|
||||
SharedObject::SharedObject() {
|
||||
// Create render control
|
||||
_renderControl = new RenderControl();
|
||||
|
||||
// Create a QQuickWindow that is associated with our render control.
|
||||
// This window never gets created or shown, meaning that it will never get an underlying native (platform) window.
|
||||
// NOTE: Must be created on the main thread so that OffscreenQmlSurface can send it events
|
||||
// NOTE: Must be created on the rendering thread or it will refuse to render,
|
||||
// so we wait until after its ctor to move object/context to this thread.
|
||||
QQuickWindow::setDefaultAlphaBuffer(true);
|
||||
_quickWindow = new QQuickWindow(_renderControl);
|
||||
_quickWindow->setColor(QColor(255, 255, 255, 0));
|
||||
_quickWindow->setClearBeforeRendering(true);
|
||||
|
||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit);
|
||||
}
|
||||
|
||||
SharedObject::~SharedObject() {
|
||||
if (_quickWindow) {
|
||||
_quickWindow->destroy();
|
||||
_quickWindow = nullptr;
|
||||
}
|
||||
|
||||
if (_renderControl) {
|
||||
_renderControl->deleteLater();
|
||||
_renderControl = nullptr;
|
||||
}
|
||||
|
||||
if (_renderThread) {
|
||||
_renderThread->quit();
|
||||
_renderThread->deleteLater();
|
||||
}
|
||||
|
||||
if (_rootItem) {
|
||||
_rootItem->deleteLater();
|
||||
_rootItem = nullptr;
|
||||
}
|
||||
|
||||
releaseEngine(_qmlContext->engine());
|
||||
}
|
||||
|
||||
void SharedObject::create(OffscreenSurface* surface) {
|
||||
if (_rootItem) {
|
||||
qFatal("QML surface root item already set");
|
||||
}
|
||||
|
||||
QObject::connect(_quickWindow, &QQuickWindow::focusObjectChanged, surface, &OffscreenSurface::onFocusObjectChanged);
|
||||
|
||||
// Create a QML engine.
|
||||
auto qmlEngine = acquireEngine(surface);
|
||||
_qmlContext = new QQmlContext(qmlEngine->rootContext(), qmlEngine);
|
||||
surface->onRootContextCreated(_qmlContext);
|
||||
emit surface->rootContextCreated(_qmlContext);
|
||||
|
||||
if (!qmlEngine->incubationController()) {
|
||||
qmlEngine->setIncubationController(_quickWindow->incubationController());
|
||||
}
|
||||
_qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(_quickWindow));
|
||||
}
|
||||
|
||||
void SharedObject::setRootItem(QQuickItem* rootItem) {
|
||||
_rootItem = rootItem;
|
||||
_rootItem->setSize(_quickWindow->size());
|
||||
|
||||
// Create the render thread
|
||||
_renderThread = new QThread();
|
||||
_renderThread->setObjectName(objectName());
|
||||
_renderThread->start();
|
||||
|
||||
// Create event handler for the render thread
|
||||
_renderObject = new RenderEventHandler(this, _renderThread);
|
||||
QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize));
|
||||
|
||||
QObject::connect(_renderControl, &QQuickRenderControl::renderRequested, this, &SharedObject::requestRender);
|
||||
QObject::connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &SharedObject::requestRenderSync);
|
||||
}
|
||||
|
||||
void SharedObject::destroy() {
|
||||
if (_quit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_rootItem) {
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_paused = true;
|
||||
if (_renderTimer) {
|
||||
QObject::disconnect(_renderTimer);
|
||||
_renderTimer->deleteLater();
|
||||
}
|
||||
|
||||
QObject::disconnect(_renderControl);
|
||||
QObject::disconnect(qApp);
|
||||
|
||||
QMutexLocker lock(&_mutex);
|
||||
_quit = true;
|
||||
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit));
|
||||
}
|
||||
|
||||
|
||||
#define SINGLE_QML_ENGINE 0
|
||||
|
||||
|
||||
#if SINGLE_QML_ENGINE
|
||||
static QQmlEngine* globalEngine{ nullptr };
|
||||
static size_t globalEngineRefCount{ 0 };
|
||||
#endif
|
||||
|
||||
QQmlEngine* SharedObject::acquireEngine(OffscreenSurface* surface) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
|
||||
QQmlEngine* result = nullptr;
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
qCWarning(qmlLogging) << "Cannot acquire QML engine on any thread but the main thread";
|
||||
}
|
||||
|
||||
#if SINGLE_QML_ENGINE
|
||||
if (!globalEngine) {
|
||||
Q_ASSERT(0 == globalEngineRefCount);
|
||||
globalEngine = new QQmlEngine();
|
||||
surface->initializeQmlEngine(result);
|
||||
++globalEngineRefCount;
|
||||
}
|
||||
result = globalEngine;
|
||||
#else
|
||||
result = new QQmlEngine();
|
||||
surface->initializeEngine(result);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SharedObject::releaseEngine(QQmlEngine* engine) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
#if SINGLE_QML_ENGINE
|
||||
Q_ASSERT(0 != globalEngineRefCount);
|
||||
if (0 == --globalEngineRefCount) {
|
||||
globalEngine->deleteLater();
|
||||
globalEngine = nullptr;
|
||||
}
|
||||
#else
|
||||
engine->deleteLater();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SharedObject::event(QEvent* e) {
|
||||
switch (static_cast<OffscreenEvent::Type>(e->type())) {
|
||||
case OffscreenEvent::Initialize:
|
||||
onInitialize();
|
||||
return true;
|
||||
|
||||
case OffscreenEvent::Render:
|
||||
onRender();
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
// Called by the render event handler, from the render thread
|
||||
void SharedObject::initializeRenderControl(QOpenGLContext* context) {
|
||||
if (context->shareContext() != getSharedContext()) {
|
||||
qFatal("QML rendering context has no share context");
|
||||
}
|
||||
|
||||
if (!nsightActive()) {
|
||||
_renderControl->initialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
void SharedObject::releaseTextureAndFence() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
// If the most recent texture was unused, we can directly recycle it
|
||||
if (_latestTextureAndFence.first) {
|
||||
offscreenTextures.releaseTexture(_latestTextureAndFence);
|
||||
_latestTextureAndFence = TextureAndFence{ 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
void SharedObject::setRenderTarget(uint32_t fbo, const QSize& size) {
|
||||
_quickWindow->setRenderTarget(fbo, size);
|
||||
}
|
||||
|
||||
QSize SharedObject::getSize() const {
|
||||
QMutexLocker locker(&_mutex);
|
||||
return _size;
|
||||
}
|
||||
|
||||
void SharedObject::setSize(const QSize& size) {
|
||||
if (getSize() == size) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
_size = size;
|
||||
}
|
||||
|
||||
qCDebug(qmlLogging) << "Offscreen UI resizing to " << size.width() << "x" << size.height();
|
||||
_quickWindow->setGeometry(QRect(QPoint(), size));
|
||||
_quickWindow->contentItem()->setSize(size);
|
||||
|
||||
if (_rootItem) {
|
||||
_qmlContext->setContextProperty("surfaceSize", size);
|
||||
_rootItem->setSize(size);
|
||||
}
|
||||
|
||||
requestRenderSync();
|
||||
}
|
||||
|
||||
bool SharedObject::preRender() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
if (_paused) {
|
||||
if (_syncRequested) {
|
||||
wake();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_syncRequested) {
|
||||
bool syncResult = true;
|
||||
if (!nsightActive()) {
|
||||
PROFILE_RANGE(render_qml_gl, "sync")
|
||||
syncResult = _renderControl->sync();
|
||||
}
|
||||
wake();
|
||||
if (!syncResult) {
|
||||
return false;
|
||||
}
|
||||
_syncRequested = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SharedObject::shutdownRendering(OffscreenGLCanvas& canvas, const QSize& size) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (size != QSize(0, 0)) {
|
||||
offscreenTextures.releaseSize(size);
|
||||
}
|
||||
_renderControl->invalidate();
|
||||
canvas.doneCurrent();
|
||||
wake();
|
||||
}
|
||||
|
||||
bool SharedObject::isQuit() {
|
||||
QMutexLocker locker(&_mutex);
|
||||
return _quit;
|
||||
}
|
||||
|
||||
void SharedObject::requestRender() {
|
||||
// Don't queue multiple renders
|
||||
if (_renderRequested) {
|
||||
return;
|
||||
}
|
||||
_renderRequested = true;
|
||||
}
|
||||
|
||||
void SharedObject::requestRenderSync() {
|
||||
if (_quit) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
_syncRequested = true;
|
||||
}
|
||||
|
||||
requestRender();
|
||||
}
|
||||
|
||||
bool SharedObject::fetchTexture(TextureAndFence& textureAndFence) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (0 == _latestTextureAndFence.first) {
|
||||
return false;
|
||||
}
|
||||
textureAndFence = { 0, 0 };
|
||||
std::swap(textureAndFence, _latestTextureAndFence);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SharedObject::setProxyWindow(QWindow* window) {
|
||||
_proxyWindow = window;
|
||||
_renderControl->setRenderWindow(window);
|
||||
}
|
||||
|
||||
void SharedObject::wait() {
|
||||
_cond.wait(&_mutex);
|
||||
}
|
||||
|
||||
void SharedObject::wake() {
|
||||
_cond.wakeOne();
|
||||
}
|
||||
|
||||
void SharedObject::onInitialize() {
|
||||
// Associate root item with the window.
|
||||
_rootItem->setParentItem(_quickWindow->contentItem());
|
||||
_renderControl->prepareThread(_renderThread);
|
||||
|
||||
// Set up the render thread
|
||||
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Initialize));
|
||||
|
||||
requestRender();
|
||||
|
||||
// Set up timer to trigger renders
|
||||
_renderTimer = new QTimer(this);
|
||||
QObject::connect(_renderTimer, &QTimer::timeout, this, &SharedObject::onTimer);
|
||||
|
||||
_renderTimer->setTimerType(Qt::PreciseTimer);
|
||||
_renderTimer->setInterval(MIN_TIMER_MS); // 5ms, Qt::PreciseTimer required
|
||||
_renderTimer->start();
|
||||
}
|
||||
|
||||
void SharedObject::onRender() {
|
||||
PROFILE_RANGE(render_qml, __FUNCTION__);
|
||||
if (_quit) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker lock(&_mutex);
|
||||
if (_syncRequested) {
|
||||
lock.unlock();
|
||||
_renderControl->polishItems();
|
||||
lock.relock();
|
||||
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Render));
|
||||
// sync and render request, main and render threads must be synchronized
|
||||
wait();
|
||||
} else {
|
||||
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Render));
|
||||
}
|
||||
_renderRequested = false;
|
||||
}
|
||||
|
||||
void SharedObject::onTimer() {
|
||||
offscreenTextures.report();
|
||||
if (!_renderRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
// Don't queue more than one frame at a time
|
||||
if (0 != _latestTextureAndFence.first) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto minRenderInterval = USECS_PER_SECOND / _maxFps;
|
||||
auto lastInterval = usecTimestampNow() - _lastRenderTime;
|
||||
// Don't exceed the framerate limit
|
||||
if (lastInterval < minRenderInterval) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Render));
|
||||
}
|
||||
|
||||
void SharedObject::onAboutToQuit() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
void SharedObject::updateTextureAndFence(const TextureAndFence& newTextureAndFence) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
// If the most recent texture was unused, we can directly recycle it
|
||||
if (_latestTextureAndFence.first) {
|
||||
offscreenTextures.releaseTexture(_latestTextureAndFence);
|
||||
_latestTextureAndFence = { 0, 0 };
|
||||
}
|
||||
|
||||
_latestTextureAndFence = newTextureAndFence;
|
||||
}
|
||||
|
||||
void SharedObject::pause() {
|
||||
_paused = true;
|
||||
}
|
||||
|
||||
void SharedObject::resume() {
|
||||
_paused = false;
|
||||
requestRender();
|
||||
}
|
||||
|
||||
bool SharedObject::isPaused() const {
|
||||
return _paused;
|
||||
}
|
119
libraries/qml/src/qml/impl/SharedObject.h
Normal file
119
libraries/qml/src/qml/impl/SharedObject.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QWaitCondition>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QSize>
|
||||
|
||||
#include "TextureCache.h"
|
||||
|
||||
|
||||
class QWindow;
|
||||
class QTimer;
|
||||
class QQuickWindow;
|
||||
class QQuickItem;
|
||||
class QOpenGLContext;
|
||||
class QQmlEngine;
|
||||
class QQmlContext;
|
||||
class OffscreenGLCanvas;
|
||||
|
||||
namespace hifi { namespace qml {
|
||||
|
||||
class OffscreenSurface;
|
||||
|
||||
namespace impl {
|
||||
|
||||
class RenderControl;
|
||||
class RenderEventHandler;
|
||||
|
||||
class SharedObject : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
friend class RenderEventHandler;
|
||||
|
||||
public:
|
||||
static void setSharedContext(QOpenGLContext* context);
|
||||
static QOpenGLContext* getSharedContext();
|
||||
static TextureCache& getTextureCache();
|
||||
|
||||
SharedObject();
|
||||
virtual ~SharedObject();
|
||||
|
||||
void create(OffscreenSurface* surface);
|
||||
void setRootItem(QQuickItem* rootItem);
|
||||
void destroy();
|
||||
bool isQuit();
|
||||
|
||||
QSize getSize() const;
|
||||
void setSize(const QSize& size);
|
||||
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
|
||||
|
||||
QQuickWindow* getWindow() { return _quickWindow; }
|
||||
QQuickItem* getRootItem() { return _rootItem; }
|
||||
QQmlContext* getContext() { return _qmlContext; }
|
||||
void setProxyWindow(QWindow* window);
|
||||
|
||||
void pause();
|
||||
void resume();
|
||||
bool isPaused() const;
|
||||
bool fetchTexture(TextureAndFence& textureAndFence);
|
||||
|
||||
|
||||
private:
|
||||
bool event(QEvent* e) override;
|
||||
|
||||
bool preRender();
|
||||
void shutdownRendering(OffscreenGLCanvas& canvas, const QSize& size);
|
||||
// Called by the render event handler, from the render thread
|
||||
void initializeRenderControl(QOpenGLContext* context);
|
||||
void releaseTextureAndFence();
|
||||
void setRenderTarget(uint32_t fbo, const QSize& size);
|
||||
|
||||
QQmlEngine* acquireEngine(OffscreenSurface* surface);
|
||||
void releaseEngine(QQmlEngine* engine);
|
||||
|
||||
void requestRender();
|
||||
void requestRenderSync();
|
||||
void wait();
|
||||
void wake();
|
||||
void onInitialize();
|
||||
void onRender();
|
||||
void onTimer();
|
||||
void onAboutToQuit();
|
||||
void updateTextureAndFence(const TextureAndFence& newTextureAndFence);
|
||||
|
||||
// Texture management
|
||||
TextureAndFence _latestTextureAndFence{ 0, 0 };
|
||||
RenderControl* _renderControl{ nullptr };
|
||||
RenderEventHandler* _renderObject{ nullptr };
|
||||
QQuickWindow* _quickWindow{ nullptr };
|
||||
QWindow* _proxyWindow{ nullptr };
|
||||
QQuickItem* _item{ nullptr };
|
||||
QQuickItem* _rootItem{ nullptr };
|
||||
QQmlContext* _qmlContext{ nullptr };
|
||||
QTimer* _renderTimer{ nullptr };
|
||||
QThread* _renderThread{ nullptr };
|
||||
QWaitCondition _cond;
|
||||
mutable QMutex _mutex;
|
||||
|
||||
uint64_t _lastRenderTime{ 0 };
|
||||
QSize _size{ 100, 100 };
|
||||
uint8_t _maxFps{ 60 };
|
||||
|
||||
bool _renderRequested{ false };
|
||||
bool _syncRequested{ false };
|
||||
bool _quit{ false };
|
||||
bool _paused{ false };
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
}} // namespace hifi::qml
|
152
libraries/qml/src/qml/impl/TextureCache.cpp
Normal file
152
libraries/qml/src/qml/impl/TextureCache.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include "TextureCache.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <gl/Config.h>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "Profiling.h"
|
||||
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define USE_GLES 1
|
||||
#endif
|
||||
|
||||
uint64_t uvec2ToUint64(const QSize& size) {
|
||||
uint64_t result = size.width();
|
||||
result <<= 32;
|
||||
result |= size.height();
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureCache::acquireSize(const QSize& size) {
|
||||
auto sizeKey = uvec2ToUint64(size);
|
||||
Lock lock(_mutex);
|
||||
auto& textureSet = _textures[sizeKey];
|
||||
++textureSet.clientCount;
|
||||
}
|
||||
|
||||
void TextureCache::releaseSize(const QSize& size) {
|
||||
auto sizeKey = uvec2ToUint64(size);
|
||||
ValueList texturesToDelete;
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
assert(_textures.count(sizeKey));
|
||||
auto& textureSet = _textures[sizeKey];
|
||||
if (0 == --textureSet.clientCount) {
|
||||
texturesToDelete.swap(textureSet.returnedTextures);
|
||||
_textures.erase(sizeKey);
|
||||
}
|
||||
}
|
||||
for (const auto& textureAndFence : texturesToDelete) {
|
||||
destroy(textureAndFence);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TextureCache::acquireTexture(const QSize& size) {
|
||||
Lock lock(_mutex);
|
||||
recycle();
|
||||
|
||||
|
||||
++_activeTextureCount;
|
||||
auto sizeKey = uvec2ToUint64(size);
|
||||
assert(_textures.count(sizeKey));
|
||||
auto& textureSet = _textures[sizeKey];
|
||||
if (!textureSet.returnedTextures.empty()) {
|
||||
auto textureAndFence = textureSet.returnedTextures.front();
|
||||
textureSet.returnedTextures.pop_front();
|
||||
glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)textureAndFence.second);
|
||||
return textureAndFence.first;
|
||||
}
|
||||
return createTexture(size);
|
||||
}
|
||||
|
||||
void TextureCache::releaseTexture(const Value& textureAndFence) {
|
||||
--_activeTextureCount;
|
||||
Lock lock(_mutex);
|
||||
_returnedTextures.push_back(textureAndFence);
|
||||
}
|
||||
|
||||
void TextureCache::report() {
|
||||
if (randFloat() < 0.01f) {
|
||||
PROFILE_COUNTER(render_qml_gl, "offscreenTextures",
|
||||
{
|
||||
{ "total", QVariant::fromValue(_allTextureCount.load()) },
|
||||
{ "active", QVariant::fromValue(_activeTextureCount.load()) },
|
||||
});
|
||||
PROFILE_COUNTER(render_qml_gl, "offscreenTextureMemory", { { "value", QVariant::fromValue(_totalTextureUsage) } });
|
||||
}
|
||||
}
|
||||
|
||||
size_t TextureCache::getUsedTextureMemory() {
|
||||
return _totalTextureUsage;
|
||||
}
|
||||
|
||||
size_t TextureCache::getMemoryForSize(const QSize& size) {
|
||||
// Base size + mips
|
||||
return static_cast<size_t>(((size.width() * size.height()) << 2) * 1.33f);
|
||||
}
|
||||
|
||||
void TextureCache::destroyTexture(uint32_t texture) {
|
||||
--_allTextureCount;
|
||||
auto size = _textureSizes[texture];
|
||||
assert(getMemoryForSize(size) <= _totalTextureUsage);
|
||||
_totalTextureUsage -= getMemoryForSize(size);
|
||||
_textureSizes.erase(texture);
|
||||
// FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context.
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
void TextureCache::destroy(const Value& textureAndFence) {
|
||||
const auto& fence = textureAndFence.second;
|
||||
// FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context.
|
||||
glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)fence);
|
||||
destroyTexture(textureAndFence.first);
|
||||
}
|
||||
|
||||
uint32_t TextureCache::createTexture(const QSize& size) {
|
||||
// Need a new texture
|
||||
uint32_t newTexture;
|
||||
glGenTextures(1, &newTexture);
|
||||
++_allTextureCount;
|
||||
_textureSizes[newTexture] = size;
|
||||
_totalTextureUsage += getMemoryForSize(size);
|
||||
glBindTexture(GL_TEXTURE_2D, newTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
|
||||
#if !defined(USE_GLES)
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f);
|
||||
#endif
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.width(), size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
return newTexture;
|
||||
}
|
||||
|
||||
void TextureCache::recycle() {
|
||||
// First handle any global returns
|
||||
ValueList returnedTextures;
|
||||
returnedTextures.swap(_returnedTextures);
|
||||
|
||||
for (auto textureAndFence : returnedTextures) {
|
||||
GLuint texture = textureAndFence.first;
|
||||
QSize size = _textureSizes[texture];
|
||||
auto sizeKey = uvec2ToUint64(size);
|
||||
// Textures can be returned after all surfaces of the given size have been destroyed,
|
||||
// in which case we just destroy the texture
|
||||
if (!_textures.count(sizeKey)) {
|
||||
destroy(textureAndFence);
|
||||
continue;
|
||||
}
|
||||
_textures[sizeKey].returnedTextures.push_back(textureAndFence);
|
||||
}
|
||||
}
|
75
libraries/qml/src/qml/impl/TextureCache.h
Normal file
75
libraries/qml/src/qml/impl/TextureCache.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018-01-04
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_QmlTextureCache_h
|
||||
#define hifi_QmlTextureCache_h
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
|
||||
#include <QtCore/QSize>
|
||||
|
||||
namespace hifi { namespace qml { namespace impl {
|
||||
|
||||
class TextureAndFence : public std::pair<uint32_t, void*> {
|
||||
using Parent = std::pair<uint32_t, void*>;
|
||||
public:
|
||||
TextureAndFence() : Parent(0, 0) {}
|
||||
TextureAndFence(uint32_t texture, void* sync) : Parent(texture, sync) {};
|
||||
};
|
||||
|
||||
|
||||
class TextureCache {
|
||||
public:
|
||||
using Value = TextureAndFence;
|
||||
using ValueList = std::list<Value>;
|
||||
using Size = uint64_t;
|
||||
|
||||
struct TextureSet {
|
||||
Size textureSize;
|
||||
// The number of surfaces with this size
|
||||
size_t clientCount{ 0 };
|
||||
ValueList returnedTextures;
|
||||
};
|
||||
|
||||
|
||||
void releaseSize(const QSize& size);
|
||||
void acquireSize(const QSize& size);
|
||||
uint32_t acquireTexture(const QSize& size);
|
||||
void releaseTexture(const Value& textureAndFence);
|
||||
|
||||
// For debugging
|
||||
void report();
|
||||
size_t getUsedTextureMemory();
|
||||
private:
|
||||
static size_t getMemoryForSize(const QSize& size);
|
||||
|
||||
uint32_t createTexture(const QSize& size);
|
||||
void destroyTexture(uint32_t texture);
|
||||
|
||||
void destroy(const Value& textureAndFence);
|
||||
void recycle();
|
||||
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
std::atomic<int> _allTextureCount;
|
||||
std::atomic<int> _activeTextureCount;
|
||||
std::unordered_map<Size, TextureSet> _textures;
|
||||
std::unordered_map<uint32_t, QSize> _textureSizes;
|
||||
Mutex _mutex;
|
||||
std::list<Value> _returnedTextures;
|
||||
size_t _totalTextureUsage{ 0 };
|
||||
};
|
||||
|
||||
}}} // namespace hifi::qml::impl
|
||||
|
||||
#endif
|
|
@ -187,14 +187,14 @@ void Deck::processFrames() {
|
|||
|
||||
void Deck::removeClip(const ClipConstPointer& clip) {
|
||||
Locker lock(_mutex);
|
||||
std::remove_if(_clips.begin(), _clips.end(), [&](const Clip::ConstPointer& testClip)->bool {
|
||||
_clips.remove_if([&](const Clip::ConstPointer& testClip)->bool {
|
||||
return (clip == testClip);
|
||||
});
|
||||
}
|
||||
|
||||
void Deck::removeClip(const QString& clipName) {
|
||||
Locker lock(_mutex);
|
||||
std::remove_if(_clips.begin(), _clips.end(), [&](const Clip::ConstPointer& clip)->bool {
|
||||
_clips.remove_if([&](const Clip::ConstPointer& clip)->bool {
|
||||
return (clip->getName() == clipName);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ AUTOSCRIBE_SHADER_LIB(gpu graphics render)
|
|||
# pull in the resources.qrc file
|
||||
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
|
||||
setup_hifi_library(Gui Network Qml Quick Script)
|
||||
link_hifi_libraries(shared ktx gpu graphics model-networking render animation fbx image procedural)
|
||||
link_hifi_libraries(shared task ktx gpu graphics model-networking render animation fbx image procedural)
|
||||
include_hifi_library_headers(audio)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(octree)
|
||||
include_hifi_library_headers(audio)
|
||||
|
||||
if (NOT ANDROID)
|
||||
target_nsight()
|
||||
|
|
|
@ -749,8 +749,6 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
|
|||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
{
|
||||
PROFILE_RANGE(render, "Process Default Skybox");
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
|
||||
QFileSelector fileSelector;
|
||||
fileSelector.setExtraSelectors(FileUtils::getFileSelectors());
|
||||
auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx");
|
||||
|
|
|
@ -297,19 +297,20 @@ float FadeConfig::getEdgeWidth() const {
|
|||
return sqrtf(events[editedCategory].edgeWidth);
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeInnerColorR(float value) {
|
||||
events[editedCategory].edgeInnerColor.r = value;
|
||||
void FadeConfig::setEdgeInnerColor(const QColor& value) {
|
||||
events[editedCategory].edgeInnerColor.r = value.redF();
|
||||
events[editedCategory].edgeInnerColor.g = value.greenF();
|
||||
events[editedCategory].edgeInnerColor.b = value.blueF();
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeInnerColorG(float value) {
|
||||
events[editedCategory].edgeInnerColor.g = value;
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeInnerColorB(float value) {
|
||||
events[editedCategory].edgeInnerColor.b = value;
|
||||
emit dirty();
|
||||
QColor FadeConfig::getEdgeInnerColor() const {
|
||||
QColor color;
|
||||
color.setRedF(events[editedCategory].edgeInnerColor.r);
|
||||
color.setGreenF(events[editedCategory].edgeInnerColor.g);
|
||||
color.setBlueF(events[editedCategory].edgeInnerColor.b);
|
||||
color.setAlphaF(1.0f);
|
||||
return color;
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeInnerIntensity(float value) {
|
||||
|
@ -317,19 +318,20 @@ void FadeConfig::setEdgeInnerIntensity(float value) {
|
|||
emit dirty();
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeOuterColorR(float value) {
|
||||
events[editedCategory].edgeOuterColor.r = value;
|
||||
void FadeConfig::setEdgeOuterColor(const QColor& value) {
|
||||
events[editedCategory].edgeOuterColor.r = value.redF();
|
||||
events[editedCategory].edgeOuterColor.g = value.greenF();
|
||||
events[editedCategory].edgeOuterColor.b = value.blueF();
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeOuterColorG(float value) {
|
||||
events[editedCategory].edgeOuterColor.g = value;
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeOuterColorB(float value) {
|
||||
events[editedCategory].edgeOuterColor.b = value;
|
||||
emit dirty();
|
||||
QColor FadeConfig::getEdgeOuterColor() const {
|
||||
QColor color;
|
||||
color.setRedF(events[editedCategory].edgeOuterColor.r);
|
||||
color.setGreenF(events[editedCategory].edgeOuterColor.g);
|
||||
color.setBlueF(events[editedCategory].edgeOuterColor.b);
|
||||
color.setAlphaF(1.0f);
|
||||
return color;
|
||||
}
|
||||
|
||||
void FadeConfig::setEdgeOuterIntensity(float value) {
|
||||
|
@ -352,13 +354,13 @@ QString FadeConfig::eventNames[FADE_CATEGORY_COUNT] = {
|
|||
};
|
||||
|
||||
void FadeConfig::save() const {
|
||||
// Save will only work if the HIFI_USE_SOURCE_TREE_RESOURCES environment variable is set
|
||||
assert(editedCategory < FADE_CATEGORY_COUNT);
|
||||
QJsonObject lProperties;
|
||||
const QString configFile = "config/" + eventNames[editedCategory] + ".json";
|
||||
QUrl path(PathUtils::resourcesPath() + configFile);
|
||||
QFile file(path.toString());
|
||||
const QString configFilePath = PathUtils::resourcesPath() + "config/" + eventNames[editedCategory] + ".json";
|
||||
QFile file(configFilePath);
|
||||
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
qWarning() << "Fade event configuration file " << path << " cannot be opened";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " cannot be opened";
|
||||
}
|
||||
else {
|
||||
const auto& event = events[editedCategory];
|
||||
|
@ -381,15 +383,13 @@ void FadeConfig::save() const {
|
|||
}
|
||||
|
||||
void FadeConfig::load() {
|
||||
const QString configFile = "config/" + eventNames[editedCategory] + ".json";
|
||||
|
||||
QUrl path(PathUtils::resourcesPath() + configFile);
|
||||
QFile file(path.toString());
|
||||
const QString configFilePath = PathUtils::resourcesPath() + "config/" + eventNames[editedCategory] + ".json";
|
||||
QFile file(configFilePath);
|
||||
if (!file.exists()) {
|
||||
qWarning() << "Fade event configuration file " << path << " does not exist";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " does not exist";
|
||||
}
|
||||
else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qWarning() << "Fade event configuration file " << path << " cannot be opened";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " cannot be opened";
|
||||
}
|
||||
else {
|
||||
QString fileData = file.readAll();
|
||||
|
@ -401,14 +401,14 @@ void FadeConfig::load() {
|
|||
QJsonValue value;
|
||||
auto& event = events[editedCategory];
|
||||
|
||||
qCDebug(renderlogging) << "Fade event configuration file" << path << "loaded";
|
||||
qCDebug(renderlogging) << "Fade event configuration file" << configFilePath << "loaded";
|
||||
|
||||
value = jsonObject["edgeInnerColor"];
|
||||
if (value.isArray()) {
|
||||
QJsonArray data = value.toArray();
|
||||
|
||||
if (data.size() < 4) {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeInnerColor' field. Expected array of size 4";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'edgeInnerColor' field. Expected array of size 4";
|
||||
}
|
||||
else {
|
||||
event.edgeInnerColor.r = (float)data.at(0).toDouble();
|
||||
|
@ -418,7 +418,7 @@ void FadeConfig::load() {
|
|||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeInnerColor' field. Expected array of size 4";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'edgeInnerColor' field. Expected array of size 4";
|
||||
}
|
||||
|
||||
value = jsonObject["edgeOuterColor"];
|
||||
|
@ -426,7 +426,7 @@ void FadeConfig::load() {
|
|||
QJsonArray data = value.toArray();
|
||||
|
||||
if (data.size() < 4) {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeOuterColor' field. Expected array of size 4";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'edgeOuterColor' field. Expected array of size 4";
|
||||
}
|
||||
else {
|
||||
event.edgeOuterColor.r = (float)data.at(0).toDouble();
|
||||
|
@ -436,7 +436,7 @@ void FadeConfig::load() {
|
|||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeOuterColor' field. Expected array of size 4";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'edgeOuterColor' field. Expected array of size 4";
|
||||
}
|
||||
|
||||
value = jsonObject["noiseSize"];
|
||||
|
@ -444,7 +444,7 @@ void FadeConfig::load() {
|
|||
QJsonArray data = value.toArray();
|
||||
|
||||
if (data.size() < 3) {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSize' field. Expected array of size 3";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'noiseSize' field. Expected array of size 3";
|
||||
}
|
||||
else {
|
||||
event.noiseSize.x = (float)data.at(0).toDouble();
|
||||
|
@ -453,7 +453,7 @@ void FadeConfig::load() {
|
|||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSize' field. Expected array of size 3";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'noiseSize' field. Expected array of size 3";
|
||||
}
|
||||
|
||||
value = jsonObject["noiseSpeed"];
|
||||
|
@ -461,7 +461,7 @@ void FadeConfig::load() {
|
|||
QJsonArray data = value.toArray();
|
||||
|
||||
if (data.size() < 3) {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSpeed' field. Expected array of size 3";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'noiseSpeed' field. Expected array of size 3";
|
||||
}
|
||||
else {
|
||||
event.noiseSpeed.x = (float)data.at(0).toDouble();
|
||||
|
@ -470,7 +470,7 @@ void FadeConfig::load() {
|
|||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSpeed' field. Expected array of size 3";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'noiseSpeed' field. Expected array of size 3";
|
||||
}
|
||||
|
||||
value = jsonObject["baseSize"];
|
||||
|
@ -478,7 +478,7 @@ void FadeConfig::load() {
|
|||
QJsonArray data = value.toArray();
|
||||
|
||||
if (data.size() < 3) {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseSize' field. Expected array of size 3";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'baseSize' field. Expected array of size 3";
|
||||
}
|
||||
else {
|
||||
event.baseSize.x = (float)data.at(0).toDouble();
|
||||
|
@ -487,7 +487,7 @@ void FadeConfig::load() {
|
|||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseSize' field. Expected array of size 3";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'baseSize' field. Expected array of size 3";
|
||||
}
|
||||
|
||||
value = jsonObject["noiseLevel"];
|
||||
|
@ -495,7 +495,7 @@ void FadeConfig::load() {
|
|||
event.noiseLevel = (float)value.toDouble();
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseLevel' field. Expected float value";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'noiseLevel' field. Expected float value";
|
||||
}
|
||||
|
||||
value = jsonObject["baseLevel"];
|
||||
|
@ -503,7 +503,7 @@ void FadeConfig::load() {
|
|||
event.baseLevel = (float)value.toDouble();
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseLevel' field. Expected float value";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'baseLevel' field. Expected float value";
|
||||
}
|
||||
|
||||
value = jsonObject["duration"];
|
||||
|
@ -511,7 +511,7 @@ void FadeConfig::load() {
|
|||
event.duration = (float)value.toDouble();
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'duration' field. Expected float value";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'duration' field. Expected float value";
|
||||
}
|
||||
|
||||
value = jsonObject["edgeWidth"];
|
||||
|
@ -519,7 +519,7 @@ void FadeConfig::load() {
|
|||
event.edgeWidth = std::min(1.f, std::max(0.f, (float)value.toDouble()));
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeWidth' field. Expected float value";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'edgeWidth' field. Expected float value";
|
||||
}
|
||||
|
||||
value = jsonObject["timing"];
|
||||
|
@ -527,7 +527,7 @@ void FadeConfig::load() {
|
|||
event.timing = std::max(0, std::min(TIMING_COUNT - 1, value.toInt()));
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'timing' field. Expected integer value";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'timing' field. Expected integer value";
|
||||
}
|
||||
|
||||
value = jsonObject["isInverted"];
|
||||
|
@ -535,13 +535,13 @@ void FadeConfig::load() {
|
|||
event.isInverted = value.toBool();
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file " << path << " contains an invalid 'isInverted' field. Expected boolean value";
|
||||
qWarning() << "Fade event configuration file " << configFilePath << " contains an invalid 'isInverted' field. Expected boolean value";
|
||||
}
|
||||
|
||||
emit dirty();
|
||||
}
|
||||
else {
|
||||
qWarning() << "Fade event configuration file" << path << "failed to load:" <<
|
||||
qWarning() << "Fade event configuration file" << configFilePath << "failed to load:" <<
|
||||
error.errorString() << "at offset" << error.offset;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,13 +56,9 @@ class FadeConfig : public render::Job::Config {
|
|||
Q_PROPERTY(float noiseSizeZ READ getNoiseSizeZ WRITE setNoiseSizeZ NOTIFY dirty)
|
||||
Q_PROPERTY(float noiseLevel READ getNoiseLevel WRITE setNoiseLevel NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeWidth READ getEdgeWidth WRITE setEdgeWidth NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeInnerColorR READ getEdgeInnerColorR WRITE setEdgeInnerColorR NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeInnerColorG READ getEdgeInnerColorG WRITE setEdgeInnerColorG NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeInnerColorB READ getEdgeInnerColorB WRITE setEdgeInnerColorB NOTIFY dirty)
|
||||
Q_PROPERTY(QColor edgeInnerColor READ getEdgeInnerColor WRITE setEdgeInnerColor NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeInnerIntensity READ getEdgeInnerIntensity WRITE setEdgeInnerIntensity NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeOuterColorR READ getEdgeOuterColorR WRITE setEdgeOuterColorR NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeOuterColorG READ getEdgeOuterColorG WRITE setEdgeOuterColorG NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeOuterColorB READ getEdgeOuterColorB WRITE setEdgeOuterColorB NOTIFY dirty)
|
||||
Q_PROPERTY(QColor edgeOuterColor READ getEdgeOuterColor WRITE setEdgeOuterColor NOTIFY dirty)
|
||||
Q_PROPERTY(float edgeOuterIntensity READ getEdgeOuterIntensity WRITE setEdgeOuterIntensity NOTIFY dirty)
|
||||
Q_PROPERTY(int timing READ getTiming WRITE setTiming NOTIFY dirty)
|
||||
Q_PROPERTY(float noiseSpeedX READ getNoiseSpeedX WRITE setNoiseSpeedX NOTIFY dirty)
|
||||
|
@ -129,26 +125,14 @@ public:
|
|||
void setEdgeWidth(float value);
|
||||
float getEdgeWidth() const;
|
||||
|
||||
void setEdgeInnerColorR(float value);
|
||||
float getEdgeInnerColorR() const { return events[editedCategory].edgeInnerColor.r; }
|
||||
|
||||
void setEdgeInnerColorG(float value);
|
||||
float getEdgeInnerColorG() const { return events[editedCategory].edgeInnerColor.g; }
|
||||
|
||||
void setEdgeInnerColorB(float value);
|
||||
float getEdgeInnerColorB() const { return events[editedCategory].edgeInnerColor.b; }
|
||||
void setEdgeInnerColor(const QColor& value);
|
||||
QColor getEdgeInnerColor() const;
|
||||
|
||||
void setEdgeInnerIntensity(float value);
|
||||
float getEdgeInnerIntensity() const { return events[editedCategory].edgeInnerColor.a; }
|
||||
|
||||
void setEdgeOuterColorR(float value);
|
||||
float getEdgeOuterColorR() const { return events[editedCategory].edgeOuterColor.r; }
|
||||
|
||||
void setEdgeOuterColorG(float value);
|
||||
float getEdgeOuterColorG() const { return events[editedCategory].edgeOuterColor.g; }
|
||||
|
||||
void setEdgeOuterColorB(float value);
|
||||
float getEdgeOuterColorB() const { return events[editedCategory].edgeOuterColor.b; }
|
||||
void setEdgeOuterColor(const QColor& value);
|
||||
QColor getEdgeOuterColor() const;
|
||||
|
||||
void setEdgeOuterIntensity(float value);
|
||||
float getEdgeOuterIntensity() const { return events[editedCategory].edgeOuterColor.a; }
|
||||
|
|
|
@ -75,10 +75,10 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie
|
|||
) {
|
||||
|
||||
// Rotate surface normal and eye direction
|
||||
vec3 ambientSpaceSurfaceNormal = (ambient.transform * vec4(surface.normal, 0.0)).xyz;
|
||||
vec3 ambientSpaceSurfaceEyeDir = (ambient.transform * vec4(surface.eyeDir, 0.0)).xyz;
|
||||
vec3 ambientSpaceSurfaceNormal = (ambient.transform * vec4(surface.normal, 0.0)).xyz;
|
||||
vec3 ambientSpaceSurfaceEyeDir = (ambient.transform * vec4(surface.eyeDir, 0.0)).xyz;
|
||||
<@if supportScattering@>
|
||||
vec3 ambientSpaceLowNormalCurvature = (ambient.transform * lowNormalCurvature).xyz;
|
||||
vec3 ambientSpaceLowNormal = (ambient.transform * vec4(lowNormalCurvature.xyz, 0.0)).xyz;
|
||||
<@endif@>
|
||||
|
||||
vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, 1.0-surface.roughness);
|
||||
|
@ -99,7 +99,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie
|
|||
obscurance = min(obscurance, ambientOcclusion);
|
||||
|
||||
// Diffuse from ambient
|
||||
diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceLowNormalCurvature).xyz;
|
||||
diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceLowNormal).xyz;
|
||||
|
||||
// Scattering ambient specular is the same as non scattering for now
|
||||
// TODO: we should use the same specular answer as for direct lighting
|
||||
|
|
|
@ -23,8 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
|
|||
0.5, 0.5, 0.5, 1.0 };
|
||||
const int LightStage::Shadow::MAP_SIZE = 1024;
|
||||
|
||||
static const auto MAX_BIAS = 0.006f;
|
||||
|
||||
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
|
||||
LightStage::LightStage() {
|
||||
|
@ -63,7 +61,7 @@ LightStage::LightStage() {
|
|||
|
||||
LightStage::Shadow::Schema::Schema() {
|
||||
ShadowTransform defaultTransform;
|
||||
defaultTransform.bias = MAX_BIAS;
|
||||
defaultTransform.fixedBias = 0.005f;
|
||||
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
||||
invMapSize = 1.0f / MAP_SIZE;
|
||||
cascadeCount = 1;
|
||||
|
@ -214,13 +212,10 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
|||
cascade._frustum->setOrientation(orientation);
|
||||
cascade._frustum->setPosition(position);
|
||||
}
|
||||
// Update the buffer
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f);
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||
float nearDepth, float farDepth) {
|
||||
float nearDepth, float farDepth, float fixedBias, float slopeBias) {
|
||||
assert(nearDepth < farDepth);
|
||||
assert(cascadeIndex < _cascades.size());
|
||||
|
||||
|
@ -269,12 +264,10 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
|
|||
|
||||
// Update the buffer
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
// Adapt shadow bias to shadow resolution with a totally empirical formula
|
||||
const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y));
|
||||
const auto REFERENCE_TEXEL_DENSITY = 7.5f;
|
||||
const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim;
|
||||
schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity);
|
||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
schemaCascade.fixedBias = fixedBias;
|
||||
schemaCascade.slopeBias = slopeBias;
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
||||
|
@ -285,7 +278,9 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View
|
|||
|
||||
*cascade._frustum = shadowFrustum;
|
||||
// Update the buffer
|
||||
_schemaBuffer.edit<Schema>().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
void setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f, float fixedBias = 0.005f, float slopeBias = 0.005f);
|
||||
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||
|
||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||
|
@ -213,6 +213,7 @@ protected:
|
|||
Index _sunOffLightId;
|
||||
|
||||
Index _defaultLightId;
|
||||
|
||||
};
|
||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||
|
||||
|
|
|
@ -200,8 +200,10 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
});
|
||||
}
|
||||
|
||||
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) {
|
||||
cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; };
|
||||
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cameraCullFunctor, uint8_t tagBits, uint8_t tagMask) {
|
||||
::CullFunctor shadowCullFunctor = [this](const RenderArgs* args, const AABox& bounds) {
|
||||
return _cullFunctor(args, bounds);
|
||||
};
|
||||
|
||||
// Prepare the ShapePipeline
|
||||
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
|
||||
|
@ -213,26 +215,54 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
initZPassPipelines(*shapePlumber, state);
|
||||
}
|
||||
|
||||
task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
const auto queryResolution = setupOutput.getN<RenderShadowSetup::Outputs>(2);
|
||||
// Fetch and cull the items from the scene
|
||||
static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
|
||||
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
|
||||
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
|
||||
const auto shadowItems = task.addJob<FetchSpatialSelection>("FetchShadowSelection", selectionInputs);
|
||||
|
||||
// Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
|
||||
// frustum culling or this will make shadow casters out of the camera frustum disappear.
|
||||
const auto cameraFrustum = setupOutput.getN<RenderShadowSetup::Outputs>(2);
|
||||
const auto applyFunctorInputs = ApplyCullFunctorOnItemBounds::Inputs(shadowItems, cameraFrustum).asVarying();
|
||||
const auto culledShadowItems = task.addJob<ApplyCullFunctorOnItemBounds>("ShadowCullCamera", applyFunctorInputs, cameraCullFunctor);
|
||||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadow", culledShadowItems);
|
||||
const auto sortedShapes = task.addJob<DepthSortShapes>("DepthSortShadow", sortedPipelines, true);
|
||||
|
||||
render::Varying cascadeFrustums[SHADOW_CASCADE_MAX_COUNT] = {
|
||||
ViewFrustumPointer(),
|
||||
ViewFrustumPointer(),
|
||||
ViewFrustumPointer(),
|
||||
ViewFrustumPointer()
|
||||
};
|
||||
|
||||
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
||||
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>("ShadowCascadeSetup", i, tagBits, tagMask);
|
||||
const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
char jobName[64];
|
||||
sprintf(jobName, "ShadowCascadeSetup%d", i);
|
||||
const auto cascadeSetupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i, _cullFunctor, tagBits, tagMask);
|
||||
const auto shadowFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
|
||||
auto antiFrustum = render::Varying(ViewFrustumPointer());
|
||||
cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
if (i > 1) {
|
||||
antiFrustum = cascadeFrustums[i - 2];
|
||||
}
|
||||
|
||||
// CPU jobs:
|
||||
// Fetch and cull the items from the scene
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
|
||||
const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying();
|
||||
const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW);
|
||||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
|
||||
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
|
||||
// CPU jobs: finer grained culling
|
||||
const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
|
||||
const auto culledShadowItemsAndBounds = task.addJob<CullShapeBounds>("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", setupOutput);
|
||||
sprintf(jobName, "RenderShadowMap%d", i);
|
||||
task.addJob<RenderShadowMap>(jobName, culledShadowItemsAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowFilter);
|
||||
}
|
||||
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
|
||||
}
|
||||
|
||||
void RenderShadowTask::configure(const Config& configuration) {
|
||||
|
@ -241,15 +271,107 @@ void RenderShadowTask::configure(const Config& configuration) {
|
|||
// Task::configure(configuration);
|
||||
}
|
||||
|
||||
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
||||
RenderShadowSetup::RenderShadowSetup() :
|
||||
_cameraFrustum{ std::make_shared<ViewFrustum>() },
|
||||
_coarseShadowFrustum{ std::make_shared<ViewFrustum>() } {
|
||||
|
||||
}
|
||||
|
||||
void RenderShadowSetup::configure(const Config& configuration) {
|
||||
setConstantBias(0, configuration.constantBias0);
|
||||
setConstantBias(1, configuration.constantBias1);
|
||||
setConstantBias(2, configuration.constantBias2);
|
||||
setConstantBias(3, configuration.constantBias3);
|
||||
setSlopeBias(0, configuration.slopeBias0);
|
||||
setSlopeBias(1, configuration.slopeBias1);
|
||||
setSlopeBias(2, configuration.slopeBias2);
|
||||
setSlopeBias(3, configuration.slopeBias3);
|
||||
}
|
||||
|
||||
void RenderShadowSetup::setConstantBias(int cascadeIndex, float value) {
|
||||
_bias[cascadeIndex]._constant = value * value * value * 0.004f;
|
||||
}
|
||||
|
||||
void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) {
|
||||
_bias[cascadeIndex]._slope = value * value * value * 0.01f;
|
||||
}
|
||||
|
||||
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
// Cache old render args
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
output.edit0() = args->_renderMode;
|
||||
output.edit1() = glm::ivec2(0, 0);
|
||||
// Save main camera frustum
|
||||
*_cameraFrustum = args->getViewFrustum();
|
||||
output.edit2() = _cameraFrustum;
|
||||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow) {
|
||||
globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
|
||||
auto& firstCascade = globalShadow->getCascade(0);
|
||||
auto& firstCascadeFrustum = firstCascade.getFrustum();
|
||||
unsigned int cascadeIndex;
|
||||
|
||||
// Adjust each cascade frustum
|
||||
for (cascadeIndex = 0; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) {
|
||||
auto& bias = _bias[cascadeIndex];
|
||||
globalShadow->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(),
|
||||
SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR,
|
||||
bias._constant, bias._slope);
|
||||
}
|
||||
|
||||
// Now adjust coarse frustum bounds
|
||||
auto frustumPosition = firstCascadeFrustum->getPosition();
|
||||
auto farTopLeft = firstCascadeFrustum->getFarTopLeft() - frustumPosition;
|
||||
auto farBottomRight = firstCascadeFrustum->getFarBottomRight() - frustumPosition;
|
||||
|
||||
auto left = glm::dot(farTopLeft, firstCascadeFrustum->getRight());
|
||||
auto right = glm::dot(farBottomRight, firstCascadeFrustum->getRight());
|
||||
auto top = glm::dot(farTopLeft, firstCascadeFrustum->getUp());
|
||||
auto bottom = glm::dot(farBottomRight, firstCascadeFrustum->getUp());
|
||||
auto near = firstCascadeFrustum->getNearClip();
|
||||
auto far = firstCascadeFrustum->getFarClip();
|
||||
|
||||
for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) {
|
||||
auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum();
|
||||
|
||||
farTopLeft = cascadeFrustum->getFarTopLeft() - frustumPosition;
|
||||
farBottomRight = cascadeFrustum->getFarBottomRight() - frustumPosition;
|
||||
|
||||
auto cascadeLeft = glm::dot(farTopLeft, cascadeFrustum->getRight());
|
||||
auto cascadeRight = glm::dot(farBottomRight, cascadeFrustum->getRight());
|
||||
auto cascadeTop = glm::dot(farTopLeft, cascadeFrustum->getUp());
|
||||
auto cascadeBottom = glm::dot(farBottomRight, cascadeFrustum->getUp());
|
||||
auto cascadeNear = cascadeFrustum->getNearClip();
|
||||
auto cascadeFar = cascadeFrustum->getFarClip();
|
||||
left = glm::min(left, cascadeLeft);
|
||||
right = glm::max(right, cascadeRight);
|
||||
bottom = glm::min(bottom, cascadeBottom);
|
||||
top = glm::max(top, cascadeTop);
|
||||
near = glm::min(near, cascadeNear);
|
||||
far = glm::max(far, cascadeFar);
|
||||
}
|
||||
|
||||
_coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition());
|
||||
_coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation());
|
||||
_coarseShadowFrustum->setProjection(glm::ortho<float>(left, right, bottom, top, near, far));
|
||||
_coarseShadowFrustum->calculate();
|
||||
|
||||
// Push frustum for further culling and selection
|
||||
args->pushViewFrustum(*_coarseShadowFrustum);
|
||||
|
||||
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||
|
||||
// We want for the octree query enough resolution to catch the details in the lowest cascade. So compute
|
||||
// the desired resolution for the first cascade frustum and extrapolate it to the coarse frustum.
|
||||
glm::ivec2 queryResolution = firstCascade.framebuffer->getSize();
|
||||
queryResolution.x = int(queryResolution.x * _coarseShadowFrustum->getWidth() / firstCascadeFrustum->getWidth());
|
||||
queryResolution.y = int(queryResolution.y * _coarseShadowFrustum->getHeight() / firstCascadeFrustum->getHeight());
|
||||
output.edit1() = queryResolution;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,39 +381,44 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
|||
// Cache old render args
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
output.edit0() = args->_renderMode;
|
||||
output.edit2() = args->_sizeScale;
|
||||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
||||
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
|
||||
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
|
||||
// Set the keylight render args
|
||||
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
||||
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||
if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) {
|
||||
const float shadowSizeScale = 1e16f;
|
||||
// Set the size scale to a ridiculously high value to prevent small object culling which assumes
|
||||
// the view frustum is a perspective projection. But this isn't the case for the sun which
|
||||
// is an orthographic projection.
|
||||
args->_sizeScale = shadowSizeScale;
|
||||
}
|
||||
auto& cascade = globalShadow->getCascade(_cascadeIndex);
|
||||
auto& cascadeFrustum = cascade.getFrustum();
|
||||
args->pushViewFrustum(*cascadeFrustum);
|
||||
auto texelSize = glm::min(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x;
|
||||
// Set the cull threshold to 24 shadow texels. This is totally arbitrary
|
||||
const auto minTexelCount = 24.0f;
|
||||
// TODO : maybe adapt that with LOD management system?
|
||||
texelSize *= minTexelCount;
|
||||
_cullFunctor._minSquareSize = texelSize * texelSize;
|
||||
|
||||
output.edit1() = cascadeFrustum;
|
||||
} else {
|
||||
output.edit1() = ItemFilter::Builder::nothing();
|
||||
output.edit0() = ItemFilter::Builder::nothing();
|
||||
output.edit1() = ViewFrustumPointer();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) {
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.selectsNothing()) {
|
||||
args->popViewFrustum();
|
||||
}
|
||||
assert(args->hasViewFrustum());
|
||||
}
|
||||
|
||||
void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
|
||||
args->popViewFrustum();
|
||||
}
|
||||
assert(args->hasViewFrustum());
|
||||
// Reset the render args
|
||||
args->_renderMode = input.get0();
|
||||
args->_sizeScale = input.get2();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include <render/CullTask.h>
|
||||
|
||||
#include "Shadows_shared.slh"
|
||||
|
||||
class ViewFrustum;
|
||||
|
||||
class RenderShadowMap {
|
||||
|
@ -48,40 +50,101 @@ public:
|
|||
using JobModel = render::Task::Model<RenderShadowTask, Config>;
|
||||
|
||||
RenderShadowTask() {}
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cameraCullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00);
|
||||
|
||||
void configure(const Config& configuration);
|
||||
|
||||
struct CullFunctor {
|
||||
float _minSquareSize{ 0.0f };
|
||||
|
||||
bool operator()(const RenderArgs* args, const AABox& bounds) const {
|
||||
// Cull only objects that are too small relatively to shadow frustum
|
||||
const auto boundsSquareRadius = glm::dot(bounds.getDimensions(), bounds.getDimensions());
|
||||
return boundsSquareRadius > _minSquareSize;
|
||||
}
|
||||
};
|
||||
|
||||
CullFunctor _cullFunctor;
|
||||
|
||||
};
|
||||
|
||||
class RenderShadowSetupConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float constantBias0 MEMBER constantBias0 NOTIFY dirty)
|
||||
Q_PROPERTY(float constantBias1 MEMBER constantBias1 NOTIFY dirty)
|
||||
Q_PROPERTY(float constantBias2 MEMBER constantBias2 NOTIFY dirty)
|
||||
Q_PROPERTY(float constantBias3 MEMBER constantBias3 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias0 MEMBER slopeBias0 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias1 MEMBER slopeBias1 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias2 MEMBER slopeBias2 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias3 MEMBER slopeBias3 NOTIFY dirty)
|
||||
public:
|
||||
|
||||
float constantBias0{ 0.15f };
|
||||
float constantBias1{ 0.15f };
|
||||
float constantBias2{ 0.175f };
|
||||
float constantBias3{ 0.2f };
|
||||
float slopeBias0{ 0.6f };
|
||||
float slopeBias1{ 0.6f };
|
||||
float slopeBias2{ 0.7f };
|
||||
float slopeBias3{ 0.82f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class RenderShadowSetup {
|
||||
public:
|
||||
using JobModel = render::Job::Model<RenderShadowSetup>;
|
||||
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, glm::ivec2, ViewFrustumPointer>;
|
||||
using Config = RenderShadowSetupConfig;
|
||||
using JobModel = render::Job::ModelO<RenderShadowSetup, Outputs, Config>;
|
||||
|
||||
RenderShadowSetup() {}
|
||||
void run(const render::RenderContextPointer& renderContext);
|
||||
RenderShadowSetup();
|
||||
void configure(const Config& configuration);
|
||||
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
||||
|
||||
private:
|
||||
|
||||
ViewFrustumPointer _cameraFrustum;
|
||||
ViewFrustumPointer _coarseShadowFrustum;
|
||||
struct {
|
||||
float _constant;
|
||||
float _slope;
|
||||
} _bias[SHADOW_CASCADE_MAX_COUNT];
|
||||
|
||||
void setConstantBias(int cascadeIndex, float value);
|
||||
void setSlopeBias(int cascadeIndex, float value);
|
||||
};
|
||||
|
||||
class RenderShadowCascadeSetup {
|
||||
public:
|
||||
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>;
|
||||
using Outputs = render::VaryingSet2<render::ItemFilter, ViewFrustumPointer>;
|
||||
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
|
||||
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : _cascadeIndex{ cascadeIndex }, _tagBits(tagBits), _tagMask(tagMask) {}
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) :
|
||||
_cascadeIndex{ cascadeIndex }, _cullFunctor{ cullFunctor }, _tagBits(tagBits), _tagMask(tagMask) {}
|
||||
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
||||
|
||||
private:
|
||||
|
||||
unsigned int _cascadeIndex;
|
||||
RenderShadowTask::CullFunctor& _cullFunctor;
|
||||
uint8_t _tagBits{ 0x00 };
|
||||
uint8_t _tagMask{ 0x00 };
|
||||
};
|
||||
|
||||
class RenderShadowCascadeTeardown {
|
||||
public:
|
||||
using Input = RenderShadowCascadeSetup::Outputs;
|
||||
using Input = render::ItemFilter;
|
||||
using JobModel = render::Job::ModelI<RenderShadowCascadeTeardown, Input>;
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
};
|
||||
|
||||
class RenderShadowTeardown {
|
||||
public:
|
||||
using Input = RenderShadowSetup::Outputs;
|
||||
using JobModel = render::Job::ModelI<RenderShadowTeardown, Input>;
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
};
|
||||
|
||||
#endif // hifi_RenderShadowTask_h
|
||||
|
|
|
@ -17,18 +17,9 @@
|
|||
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits, uint8_t tagMask) {
|
||||
// auto items = input.get<Input>();
|
||||
|
||||
// Shadows use an orthographic projection because they are linked to sunlights
|
||||
// but the cullFunctor passed is probably tailored for perspective projection and culls too much.
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) {
|
||||
// Cull only objects that are too small relatively to shadow frustum
|
||||
auto& frustum = args->getViewFrustum();
|
||||
auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth());
|
||||
const auto boundsRadius = bounds.getDimensions().length();
|
||||
const auto relativeBoundRadius = boundsRadius / frustumSize;
|
||||
const auto threshold = 1e-3f;
|
||||
return relativeBoundRadius > threshold;
|
||||
return true;
|
||||
}, tagBits, tagMask);
|
||||
// Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling
|
||||
// is performed, then casters not in the view frustum will be removed, which is not what we wish.
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, tagBits, tagMask);
|
||||
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
|
|
@ -79,31 +79,26 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
|
|||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) +
|
||||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3])
|
||||
);
|
||||
|
||||
return shadowAttenuation;
|
||||
}
|
||||
|
||||
float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) {
|
||||
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
|
||||
// If a point is not in the map, do not attenuate
|
||||
return 1.0;
|
||||
}
|
||||
// Multiply bias if we are at a grazing angle with light
|
||||
float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal));
|
||||
float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor);
|
||||
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) {
|
||||
float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL;
|
||||
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
||||
}
|
||||
|
||||
float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) {
|
||||
float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) {
|
||||
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
||||
vec4 cascadeShadowCoords[2];
|
||||
vec4 cascadeShadowCoords[2] = { vec4(0), vec4(0) };
|
||||
ivec2 cascadeIndices;
|
||||
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
||||
|
||||
// Adjust bias if we are at a grazing angle with light
|
||||
float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0, 1);
|
||||
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]);
|
||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], oneMinusNdotL);
|
||||
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]);
|
||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], oneMinusNdotL);
|
||||
}
|
||||
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||
// Falloff to max distance
|
||||
|
|
|
@ -37,14 +37,15 @@ float getShadowScale() {
|
|||
return shadow.invMapSize;
|
||||
}
|
||||
|
||||
float getShadowBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].bias;
|
||||
float getShadowFixedBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].fixedBias;
|
||||
}
|
||||
|
||||
vec3 getShadowDirInViewSpace() {
|
||||
return shadow.lightDirInViewSpace;
|
||||
float getShadowSlopeBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].slopeBias;
|
||||
}
|
||||
|
||||
|
||||
// Compute the texture coordinates from world coordinates
|
||||
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
||||
vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position;
|
||||
|
@ -52,8 +53,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
|||
}
|
||||
|
||||
bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) {
|
||||
bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0));
|
||||
bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1));
|
||||
bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0));
|
||||
bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1));
|
||||
return all(greaterThanZero) && all(lessThanOne);
|
||||
}
|
||||
|
||||
|
@ -81,10 +82,10 @@ float evalShadowCascadeWeight(vec4 cascadeTexCoords) {
|
|||
float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) {
|
||||
cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]);
|
||||
cascadeIndices.y = cascadeIndices.x+1;
|
||||
if (cascadeIndices.x < (getShadowCascadeCount()-1)) {
|
||||
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
|
||||
if (firstCascadeWeight<1.0 && cascadeIndices.x < (getShadowCascadeCount()-1)) {
|
||||
cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]);
|
||||
|
||||
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
|
||||
float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]);
|
||||
// Returns the mix amount between first and second cascade.
|
||||
return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight);
|
||||
|
|
|
@ -11,16 +11,14 @@
|
|||
|
||||
struct ShadowTransform {
|
||||
MAT4 reprojection;
|
||||
|
||||
float bias;
|
||||
float fixedBias;
|
||||
float slopeBias;
|
||||
float _padding1;
|
||||
float _padding2;
|
||||
float _padding3;
|
||||
};
|
||||
|
||||
struct ShadowParameters {
|
||||
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
||||
VEC3 lightDirInViewSpace;
|
||||
int cascadeCount;
|
||||
float invMapSize;
|
||||
float invCascadeBlendWidth;
|
||||
|
|
|
@ -88,4 +88,4 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -28,7 +28,9 @@ void main(void) {
|
|||
|
||||
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
||||
vec4 worldPos = getViewInverse() * viewPos;
|
||||
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal);
|
||||
Light shadowLight = getKeyLight();
|
||||
vec3 worldLightDirection = getLightDirection(shadowLight);
|
||||
float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal);
|
||||
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
discard;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue