Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / System / View</title>
<g id="Icons-/-System-/-View" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M11.9842667,6 C14.2018891,6 17.5404669,8 22,12 C17.5404669,16 14.2018891,18 11.9842667,18 C9.76664426,18 6.43855537,16 2,12 C6.43855537,8 9.76664426,6 11.9842667,6 Z M12,7.5 C9.51471863,7.5 7.5,9.51471863 7.5,12 C7.5,14.4852814 9.51471863,16.5 12,16.5 C14.4852814,16.5 16.5,14.4852814 16.5,12 C16.5,9.51471863 14.4852814,7.5 12,7.5 Z M12,9 C13.6568542,9 15,10.3431458 15,12 C15,13.6568542 13.6568542,15 12,15 C10.3431458,15 9,13.6568542 9,12 C9,10.3431458 10.3431458,9 12,9 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 953 B

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4fa1646134c32e57a5ea1e7c5e32325be158c089317397638e90b61127cbb856
size 223
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf016edacd48e20dd5841e7dda10ba8811195307d55544a6278819253d75fa1e
size 224
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8e49d78ef9dbbc1c41a1e4cd7f8b5d32bcd3fb65782d5fe5cca1dd0145e0dd2e
size 347
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #444444;">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>Icons / Toolbar / Ground</title>
<desc>Created with Sketch.</desc>
<g id="Icons-/-Toolbar-/-Ground" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M2,19 L5,6 L19,6 L22,19 L2,19 Z M5,14.667 L4.462,17 L8.051,17 L8.255,15 L5,15 L5,14.667 Z M19,14.667 L19,15 L15.699,15 L15.9,17 L19.538,17 L19,14.667 Z M13.699,15 L10.255,15 L10.051,17 L13.9,17 L13.699,15 Z M5.269,13.5 L8.407,13.5 L8.611,11.5 L5.731,11.5 L5.269,13.5 Z M18.269,11.5 L15.348,11.5 L15.549,13.5 L18.731,13.5 L18.269,11.5 Z M13.348,11.5 L10.611,11.5 L10.407,13.5 L13.549,13.5 L13.348,11.5 Z M6.077,10 L8.763,10 L8.967,8 L6.538,8 L6.077,10 Z M17.462,8 L10.967,8 L10.763,10 L13.198,10 L13,8.02631406 L15,8.02631406 L15.198,10 L17.923,10 L17.462,8 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Editor / Material Variant</title>
<g id="Icons-/-Editor-/-Material-Variant" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<rect id="Rectangle" fill="#EEEEEE" fill-rule="nonzero" x="16" y="15.9999948" width="6" height="6"></rect>
<path d="M13.2003238,2.05728467 L13.3206336,2.08541409 C17.9843907,2.69389766 21.611637,6.4842271 21.9707625,11.2247926 C21.9900321,11.3980031 22,11.5711198 22,11.7433304 L21.997,11.815 L22,12 C22,12.6856005 21.9319119,13.3545263 21.802092,14.0004211 L14,14 L14.0004211,21.802092 C13.4430737,21.9141145 12.8685772,21.980171 12.2810157,21.9961774 L12,22 C6.45454545,22 2,17.5454545 2,12 C2,6.45454545 6.45454545,2 12,2 C12.1802332,2 12.359314,2.00470542 12.537127,2.01400079 C12.719362,1.98546889 12.9435559,2.00149401 13.2003238,2.05728467 Z M11.999,15.636 L9.54545455,15.6363636 C10.0721003,18.3573668 11.1072317,20.0613988 11.9144212,20.1756828 L11.999,20.181 L11.999,15.636 Z M7.72727273,15.6363636 L4.63636364,15.6363636 C5.54545455,17.4545455 7.09090909,18.8181818 8.90909091,19.6363636 C8.36363636,18.5454545 8,17.0909091 7.72727273,15.6363636 Z M11.999,10.181 L9.36363636,10.1818182 C9.27272727,10.7272727 9.27272727,11.3636364 9.27272727,12 C9.27272727,12.5090909 9.27272727,13.0181818 9.31927273,13.4807273 L9.36363636,13.8181818 L11.999,13.818 L11.999,10.181 Z M7.45454545,10.1818182 L4,10.1818182 C3.90909091,10.7272727 3.81818182,11.3636364 3.81818182,12 C3.81818182,12.5090909 3.87636364,13.0181818 3.94618182,13.4807273 L4,13.8181818 L7.54545455,13.8181818 C7.47272727,13.3090909 7.45818182,12.8581818 7.45527273,12.3723636 L7.45454545,12 L7.45454545,10.1818182 Z M11.9990157,3.81818265 C11.2091528,3.8195194 10.1655566,5.42953176 9.60376892,8.07613162 L9.54545455,8.36363636 L11.999,8.363 L11.9990157,3.81818265 Z M9,4.36363636 C7.1969697,5.13636364 5.71829405,6.3956229 4.79372428,8.0648304 L4.63636364,8.36363636 L7.72727273,8.36363636 C8,6.90909091 8.45454545,5.45454545 9,4.36363636 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Editor / Base Material</title>
<g id="Icons-/-Editor-/-Base-Material" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<g id="community" transform="translate(2.000000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M10,-5.86197757e-14 C4.45454545,-5.86197757e-14 -8.52651283e-14,4.45454545 -8.52651283e-14,10 C-8.52651283e-14,15.5454545 4.45454545,20 10,20 C15.5454545,20 20,15.5454545 20,10 C20,4.45454545 15.5454545,-5.86197757e-14 10,-5.86197757e-14 Z M17.3636364,6.36363636 L14.2727273,6.36363636 C14,4.90909091 13.6363636,3.45454545 13.0909091,2.36363636 C14.9090909,3.18181818 16.4545455,4.54545455 17.3636364,6.36363636 Z M12.7272727,10 C12.7272727,10.6363636 12.7272727,11.2727273 12.6363636,11.8181818 L7.36363636,11.8181818 C7.27272727,11.2727273 7.27272727,10.6363636 7.27272727,10 C7.27272727,9.36363636 7.27272727,8.72727273 7.36363636,8.18181818 L12.6363636,8.18181818 C12.7272727,8.72727273 12.7272727,9.36363636 12.7272727,10 Z M10,18.1818182 C9.18181818,18.1818182 8.09090909,16.4545455 7.54545455,13.6363636 L12.3636364,13.6363636 C11.9090909,16.4545455 10.8181818,18.1818182 10,18.1818182 Z M7.54545455,6.36363636 C8.09090909,3.54545455 9.18181818,1.81818182 10,1.81818182 C10.8181818,1.81818182 11.9090909,3.54545455 12.4545455,6.36363636 L7.54545455,6.36363636 Z M7,2.36363636 C6.45454545,3.45454545 6,4.90909091 5.72727273,6.36363636 L2.63636364,6.36363636 C3.54545455,4.54545455 5.09090909,3.18181818 7,2.36363636 Z M2,8.18181818 L5.45454545,8.18181818 C5.45454545,8.81818182 5.45454545,9.36363636 5.45454545,10 C5.45454545,10.6363636 5.45454545,11.1818182 5.54545455,11.8181818 L2,11.8181818 C1.90909091,11.2727273 1.81818182,10.6363636 1.81818182,10 C1.81818182,9.36363636 1.90909091,8.72727273 2,8.18181818 Z M2.63636364,13.6363636 L5.72727273,13.6363636 C6,15.0909091 6.36363636,16.5454545 6.90909091,17.6363636 C5.09090909,16.8181818 3.54545455,15.4545455 2.63636364,13.6363636 Z M13,17.6363636 C13.5454545,16.5454545 14,15.1818182 14.1818182,13.6363636 L17.2727273,13.6363636 C16.4545455,15.4545455 14.9090909,16.8181818 13,17.6363636 Z M18,11.8181818 L14.5454545,11.8181818 C14.6363636,11.1818182 14.6363636,10.6363636 14.6363636,10 C14.6363636,9.36363636 14.6363636,8.81818182 14.5454545,8.18181818 L18,8.18181818 C18.0909091,8.72727273 18.1818182,9.36363636 18.1818182,10 C18.1818182,10.6363636 18.0909091,11.2727273 18,11.8181818 Z" id="Shape"></path>
</g>
<path d="M22,20.8342395 C22,18.1069667 22,17.2661779 22,11.7433304 C22,6.22048287 22,5.379694 22,2.65242127 C22,-0.0748514543 12,6.28878491 12,11.7433304 C12,17.1978758 22,23.5615122 22,20.8342395 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero" transform="translate(17.000000, 11.743330) scale(-1, 1) translate(-17.000000, -11.743330) "></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Editor / Mesh</title>
<g id="Icons-/-Editor-/-Mesh" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<g id="community" transform="translate(1.900000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M10.25,0 C4.56590909,0 0,4.45454545 0,10 C0,15.5454545 4.56590909,20 10.25,20 C15.9340909,20 20.5,15.5454545 20.5,10 C20.5,4.45454545 15.9340909,0 10.25,0 Z M17.7977273,6.36363636 L14.6295455,6.36363636 C14.35,4.90909091 13.9772727,3.45454545 13.4181818,2.36363636 C15.2818182,3.18181818 16.8659091,4.54545455 17.7977273,6.36363636 Z M13.0454545,10 C13.0454545,10.6363636 13.0454545,11.2727273 12.9522727,11.8181818 L7.54772727,11.8181818 C7.45454545,11.2727273 7.45454545,10.6363636 7.45454545,10 C7.45454545,9.36363636 7.45454545,8.72727273 7.54772727,8.18181818 L12.9522727,8.18181818 C13.0454545,8.72727273 13.0454545,9.36363636 13.0454545,10 Z M10.25,18.1818182 C9.41136364,18.1818182 8.29318182,16.4545455 7.73409091,13.6363636 L12.6727273,13.6363636 C12.2068182,16.4545455 11.0886364,18.1818182 10.25,18.1818182 Z M7.73409091,6.36363636 C8.29318182,3.54545455 9.41136364,1.81818182 10.25,1.81818182 C11.0886364,1.81818182 12.2068182,3.54545455 12.7659091,6.36363636 L7.73409091,6.36363636 Z M7.175,2.36363636 C6.61590909,3.45454545 6.15,4.90909091 5.87045455,6.36363636 L2.70227273,6.36363636 C3.63409091,4.54545455 5.21818182,3.18181818 7.175,2.36363636 Z M2.05,8.18181818 L5.59090909,8.18181818 C5.59090909,8.81818182 5.59090909,9.36363636 5.59090909,10 C5.59090909,10.6363636 5.59090909,11.1818182 5.68409091,11.8181818 L2.05,11.8181818 C1.95681818,11.2727273 1.86363636,10.6363636 1.86363636,10 C1.86363636,9.36363636 1.95681818,8.72727273 2.05,8.18181818 Z M2.70227273,13.6363636 L5.87045455,13.6363636 C6.15,15.0909091 6.52272727,16.5454545 7.08181818,17.6363636 C5.21818182,16.8181818 3.63409091,15.4545455 2.70227273,13.6363636 Z M13.325,17.6363636 C13.8840909,16.5454545 14.35,15.1818182 14.5363636,13.6363636 L17.7045455,13.6363636 C16.8659091,15.4545455 15.2818182,16.8181818 13.325,17.6363636 Z M18.45,11.8181818 L14.9090909,11.8181818 C15.0022727,11.1818182 15.0022727,10.6363636 15.0022727,10 C15.0022727,9.36363636 15.0022727,8.81818182 14.9090909,8.18181818 L18.45,8.18181818 C18.5431818,8.72727273 18.6363636,9.36363636 18.6363636,10 C18.6363636,10.6363636 18.5431818,11.2727273 18.45,11.8181818 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:36c55e5e6cfdb9b0e387a6b0a7de6888cde6372d5d00aa2f918c9873a888fd83
size 302
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ab737fbb49b456dd9d0cb92f6f5e6c70d6bfd67871a6a1fed2907dc602bd70c2
size 358
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4de0f4a102e5c4990d5d23e22b5cdd16532192f3a125758b608cd8c731278898
size 355
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d0b6f7c69112d124eac1123ffa840268c16148fef9ded3a67f84d3ad8d73eb96
size 212
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b795fb88a6f301d7ecc9ce75141bdcf9e6dca25043730661aa4d1f09b055d6f
size 222
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:88fbce753cdd9381a814e3b2ec5d16df7cc26f2a37ad30b7010be71ae9183510
size 214
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #444444;">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>Icons / Toolbar / Shadow</title>
<desc>Created with Sketch.</desc>
<g id="Icons-/-Toolbar-/-Shadow" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6.99979618,18.810136 L9.18779618,20.999136 L7,21 L6.99979618,18.810136 Z M17,3 L17,7.312 L20.9997962,11.310136 L20.9997962,13.189136 L17,9.19 L17,11.311 L20.9997962,15.310136 L20.9997962,17.689136 L17,13.69 L17,15.812 L20.9997962,19.811136 L21,21 L19.8097962,20.999136 L15.81,17 L13.689,17 L17.6887962,20.999136 L15.3097962,20.999136 L11.31,17 L9.188,17 L13.1877962,20.999136 L11.3097962,20.999136 L7.311,17 L3,17 L3,3 L17,3 Z M21,7 L20.9997962,9.18913601 L18.8087962,6.99913601 L21,7 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1023 B

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Editor / Image</title>
<g id="Icons-/-Editor-/-Image" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<g id="picture" transform="translate(3.000000, 3.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M14.550959,11.6 L12,8.6 L11,9.6 L8,6.6 L3.46109347,11.6 L3.46109347,14.5401062 L14.550959,14.5401062 L14.550959,11.6 Z M13.5,3 C14.3284271,3 15,3.67157288 15,4.5 C15,5.32842712 14.3284271,6 13.5,6 C12.6715729,6 12,5.32842712 12,4.5 C12,3.67157288 12.6715729,3 13.5,3 Z M0,1.77635684e-15 L0,18 L18,18 L18,1.77635684e-15 L0,1.77635684e-15 Z M16,16 L2,16 L2,2 L16,2 L16,16 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 946 B

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2a963be55d5e0a339a9ec137972c8f3455a1cf9d7f4b3b9664ce338bcb0c39bf
size 227
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #444444;">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>Icons / Toolbar / Tone Mapping</title>
<desc>Created with Sketch.</desc>
<g id="Icons-/-Toolbar-/-Tone-Mapping" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#444444" x="0" y="0" width="24" height="24"></rect>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M21,3 L21,21 L3,21 L3,3 L21,3 Z M19,5 L5,5 L5,19 L19,19 L19,5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
<path d="M4.5,19.5 C7.10977381,19.3752859 9.2645953,18.2113806 10.9644645,16.0082842 C13.5142682,12.7036395 12.2106423,9.58363419 14.3305826,7 C15.7438762,5.27757721 17.4670153,4.44424388 19.5,4.5" id="Line-2" stroke="#FFFFFF" stroke-width="2" stroke-linecap="square"></path>
<rect id="Rectangle" fill="#FFFFFF" x="5" y="3" width="16" height="2"></rect>
<rect id="Rectangle" fill="#FFFFFF" x="19" y="3" width="2" height="16"></rect>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:74278b5d4f4786a2a49f575c22b2afc5bac150e5e8ea3b2ef50ca5b37bdfc9e1
size 341
@@ -0,0 +1,341 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <QApplication>
#include <QMenu>
#include <QInputDialog>
#include <QMessageBox>
#include <QFileDialog>
#include <QDesktopServices>
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
#include <AtomToolsFramework/Util/Util.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Source/Window/MaterialBrowserInteractions.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
namespace MaterialEditor
{
MaterialBrowserInteractions::MaterialBrowserInteractions()
{
using namespace AzToolsFramework::AssetBrowser;
AssetBrowserInteractionNotificationBus::Handler::BusConnect();
}
MaterialBrowserInteractions::~MaterialBrowserInteractions()
{
AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
}
void MaterialBrowserInteractions::AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>& entries)
{
AssetBrowserEntry* entry = entries.empty() ? nullptr : entries.front();
if (!entry)
{
return;
}
m_caller = caller;
QObject::connect(m_caller, &QObject::destroyed, [this]()
{
m_caller = nullptr;
});
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
{
const auto source = azalias_cast<const SourceAssetBrowserEntry*>(entry);
if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), MaterialExtension))
{
AddContextMenuActionsForMaterialSource(caller, menu, source);
}
else if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), MaterialTypeExtension))
{
AddContextMenuActionsForMaterialTypeSource(caller, menu, source);
}
else
{
AddContextMenuActionsForOtherSource(caller, menu, source);
}
}
else if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
{
const auto folder = azalias_cast<const FolderAssetBrowserEntry*>(entry);
AddContextMenuActionsForFolder(caller, menu, folder);
}
}
void MaterialBrowserInteractions::AddContextMenuActionsForMaterialTypeSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
{
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
{
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
});
menu->addSeparator();
menu->addAction("Create Material...", [entry]()
{
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CreateDocumentFromFile,
entry->GetFullPath(), AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
});
AddPerforceMenuActions(caller, menu, entry);
}
void MaterialBrowserInteractions::AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
{
menu->addAction("Open", [entry]()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(entry->GetFullPath().c_str()));
});
menu->addAction("Duplicate...", [entry, caller]()
{
const QFileInfo duplicateFileInfo(AtomToolsFramework::GetDuplicationFileInfo(entry->GetFullPath().c_str()));
if (!duplicateFileInfo.absoluteFilePath().isEmpty())
{
if (QFile::copy(entry->GetFullPath().c_str(), duplicateFileInfo.absoluteFilePath()))
{
QFile::setPermissions(duplicateFileInfo.absoluteFilePath(), QFile::ReadOther | QFile::WriteOther);
// Auto add file to source control
AzToolsFramework::SourceControlCommandBus::Broadcast(&AzToolsFramework::SourceControlCommandBus::Events::RequestEdit,
duplicateFileInfo.absoluteFilePath().toUtf8().constData(), true, [](bool, const AzToolsFramework::SourceControlFileInfo&) {});
}
}
});
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
{
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
});
AddPerforceMenuActions(caller, menu, entry);
}
void MaterialBrowserInteractions::AddContextMenuActionsForMaterialSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
{
menu->addAction("Open", [entry]()
{
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, entry->GetFullPath());
});
menu->addAction("Duplicate...", [entry, caller]()
{
const QFileInfo duplicateFileInfo(AtomToolsFramework::GetDuplicationFileInfo(entry->GetFullPath().c_str()));
if (!duplicateFileInfo.absoluteFilePath().isEmpty())
{
if (QFile::copy(entry->GetFullPath().c_str(), duplicateFileInfo.absoluteFilePath()))
{
QFile::setPermissions(duplicateFileInfo.absoluteFilePath(), QFile::ReadOther | QFile::WriteOther);
// Auto add file to source control
AzToolsFramework::SourceControlCommandBus::Broadcast(&AzToolsFramework::SourceControlCommandBus::Events::RequestEdit,
duplicateFileInfo.absoluteFilePath().toUtf8().constData(), true, [](bool, const AzToolsFramework::SourceControlFileInfo&) {});
}
}
});
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
{
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
});
menu->addSeparator();
menu->addAction("Create Child Material...", [entry]()
{
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CreateDocumentFromFile,
entry->GetFullPath(), AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
});
menu->addSeparator();
QAction* openParentAction = menu->addAction("Open Parent Material", [entry]()
{
AZ_UNUSED(entry);
// ToDo
});
openParentAction->setEnabled(false);
AddPerforceMenuActions(caller, menu, entry);
}
void MaterialBrowserInteractions::AddContextMenuActionsForFolder(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry* entry)
{
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
{
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
});
QAction* createFolderAction = menu->addAction(QObject::tr("Create new sub folder..."));
QObject::connect(createFolderAction, &QAction::triggered, caller, [caller, entry]()
{
bool ok;
QString newFolderName = QInputDialog::getText(caller, "Enter new folder name", "name:", QLineEdit::Normal, "NewFolder", &ok);
if (ok)
{
if (newFolderName.isEmpty())
{
QMessageBox msgBox(QMessageBox::Icon::Critical, "Error", "Folder name can't be empty", QMessageBox::Ok, caller);
msgBox.exec();
}
else
{
AZStd::string newFolderPath;
AzFramework::StringFunc::Path::Join(entry->GetFullPath().c_str(), newFolderName.toUtf8().constData(), newFolderPath);
QDir dir(newFolderPath.c_str());
if (dir.exists())
{
QMessageBox::critical(caller, "Error", "Folder with this name already exists");
return;
}
auto result = dir.mkdir(newFolderPath.c_str());
if (!result)
{
AZ_Error("MaterialBrowser", false, "Failed to make new folder");
return;
}
}
}
});
}
void MaterialBrowserInteractions::AddPerforceMenuActions([[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
using namespace AzToolsFramework;
bool isActive = false;
SourceControlConnectionRequestBus::BroadcastResult(isActive, &SourceControlConnectionRequests::IsActive);
if (isActive)
{
menu->addSeparator();
AZStd::string path = entry->GetFullPath();
AzFramework::StringFunc::Path::Normalize(path);
QMenu* sourceControlMenu = menu->addMenu("Source Control");
// Update the enabled state of source control menu actions only if menu is shown
QMenu::connect(sourceControlMenu, &QMenu::aboutToShow, [this, path]()
{
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::GetFileInfo, path.c_str(),
[this](bool success, const SourceControlFileInfo& info) { UpdateSourceControlActions(success, info); });
});
// add get latest action
m_getLatestAction = sourceControlMenu->addAction("Get Latest", [path, this]()
{
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestLatest, path.c_str(),
[](bool, const SourceControlFileInfo&) {});
});
QObject::connect(m_getLatestAction, &QObject::destroyed, [this]()
{
m_getLatestAction = nullptr;
});
m_getLatestAction->setEnabled(false);
// add add action
m_addAction = sourceControlMenu->addAction("Add", [path]()
{
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestEdit, path.c_str(), true,
[path](bool, const SourceControlFileInfo&)
{
SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
});
});
QObject::connect(m_addAction, &QObject::destroyed, [this]()
{
m_addAction = nullptr;
});
m_addAction->setEnabled(false);
// add checkout action
m_checkOutAction = sourceControlMenu->addAction("Check Out", [path]()
{
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestEdit, path.c_str(), true,
[path](bool, const SourceControlFileInfo&)
{
SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
});
});
QObject::connect(m_checkOutAction, &QObject::destroyed, [this]()
{
m_checkOutAction = nullptr;
});
m_checkOutAction->setEnabled(false);
// add undo checkout action
m_undoCheckOutAction = sourceControlMenu->addAction("Undo Check Out", [path]()
{
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestRevert, path.c_str(),
[path](bool, const SourceControlFileInfo&)
{
SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
});
});
QObject::connect(m_undoCheckOutAction, &QObject::destroyed, [this]()
{
m_undoCheckOutAction = nullptr;
});
m_undoCheckOutAction->setEnabled(false);
}
}
void MaterialBrowserInteractions::UpdateSourceControlActions(bool success, AzToolsFramework::SourceControlFileInfo info)
{
if (!success && m_caller)
{
QMessageBox::critical(m_caller, "Error", "Source control operation failed.");
}
if (m_getLatestAction)
{
m_getLatestAction->setEnabled(info.IsManaged() && info.HasFlag(AzToolsFramework::SCF_OutOfDate));
}
if (m_addAction)
{
m_addAction->setEnabled(!info.IsManaged());
}
if (m_checkOutAction)
{
m_checkOutAction->setEnabled(info.IsManaged() && info.IsReadOnly() && !info.IsLockedByOther());
}
if (m_undoCheckOutAction)
{
m_undoCheckOutAction->setEnabled(info.IsManaged() && !info.IsReadOnly());
}
}
} // namespace MaterialEditor
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
class QWidget;
class QMenu;
class QAction;
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntry;
class SourceAssetBrowserEntry;
class FolderAssetBrowserEntry;
}
}
namespace MaterialEditor
{
class MaterialBrowserInteractions
: public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(MaterialBrowserInteractions, AZ::SystemAllocator, 0);
MaterialBrowserInteractions();
~MaterialBrowserInteractions();
private:
//! AssetBrowserInteractionNotificationBus::Handler overrides...
void AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>& entries) override;
void AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
void AddContextMenuActionsForMaterialSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
void AddContextMenuActionsForMaterialTypeSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
void AddContextMenuActionsForFolder(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry* entry);
void AddPerforceMenuActions(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
void UpdateSourceControlActions(bool success, AzToolsFramework::SourceControlFileInfo info);
QWidget* m_caller = nullptr;
QAction* m_addAction = nullptr;
QAction* m_checkOutAction = nullptr;
QAction* m_undoCheckOutAction = nullptr;
QAction* m_getLatestAction = nullptr;
};
} // namespace MaterialEditor
@@ -0,0 +1,249 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Atom/Document/MaterialDocumentRequestBus.h>
#include <Source/Window/MaterialBrowserWidget.h>
#include <Source/Window/ui_MaterialBrowserWidget.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QDesktopServices>
#include <QUrl>
#include <QMessageBox>
#include <QMenu>
#include <QAction>
#include <QCursor>
#include <QPushButton>
#include <QList>
#include <QByteArray>
AZ_POP_DISABLE_WARNING
namespace MaterialEditor
{
MaterialBrowserWidget::MaterialBrowserWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::MaterialBrowserWidget)
{
using namespace AzToolsFramework::AssetBrowser;
m_ui->setupUi(this);
m_ui->m_searchWidget->Setup(true, true);
m_ui->m_searchWidget->SetFilterState("", AZ::RPI::StreamingImageAsset::Group, true);
m_ui->m_searchWidget->SetFilterState("", AZ::RPI::MaterialAsset::Group, true);
m_ui->m_searchWidget->setMinimumSize(QSize(150, 0));
m_ui->m_viewOptionButton->setIcon(QIcon(":/Icons/View.svg"));
m_ui->m_splitter->setSizes(QList<int>() << 400 << 200);
m_ui->m_splitter->setStretchFactor(0, 1);
connect(m_ui->m_viewOptionButton, &QPushButton::clicked, this, &MaterialBrowserWidget::OpenOptionsMenu);
// Get the asset browser model
AssetBrowserModel* assetBrowserModel = nullptr;
AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel);
AZ_Assert(assetBrowserModel, "Failed to get file browser model");
// Hook up the data set to the tree view
m_filterModel = aznew AssetBrowserFilterModel(this);
m_filterModel->setSourceModel(assetBrowserModel);
m_filterModel->SetFilter(CreateFilter());
m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel);
m_ui->m_assetBrowserTreeViewWidget->SetShowSourceControlIcons(true);
m_ui->m_assetBrowserTreeViewWidget->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
// Maintains the tree expansion state between runs
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel, &AssetBrowserFilterModel::filterUpdatedSlot);
connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, [this]()
{
const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
constexpr bool selectFirstFilteredIndex = true;
m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
});
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::activated, this, &MaterialBrowserWidget::OpenSelectedEntries);
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, [this]() {
const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
if (!selectedAssets.empty())
{
m_ui->m_previewerFrame->Display(selectedAssets.front());
}
else
{
m_ui->m_previewerFrame->Clear();
}
});
AssetBrowserModelNotificationBus::Handler::BusConnect();
MaterialDocumentNotificationBus::Handler::BusConnect();
}
MaterialBrowserWidget::~MaterialBrowserWidget()
{
// Maintains the tree expansion state between runs
m_ui->m_assetBrowserTreeViewWidget->SaveState();
MaterialDocumentNotificationBus::Handler::BusDisconnect();
AssetBrowserModelNotificationBus::Handler::BusDisconnect();
}
AzToolsFramework::AssetBrowser::FilterConstType MaterialBrowserWidget::CreateFilter() const
{
using namespace AzToolsFramework::AssetBrowser;
QSharedPointer<EntryTypeFilter> sourceFilter(new EntryTypeFilter);
sourceFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Source);
QSharedPointer<EntryTypeFilter> folderFilter(new EntryTypeFilter);
folderFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Folder);
QSharedPointer<CompositeFilter> sourceOrFolderFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::OR));
sourceOrFolderFilter->AddFilter(sourceFilter);
sourceOrFolderFilter->AddFilter(folderFilter);
QSharedPointer<CompositeFilter> finalFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::AND));
finalFilter->AddFilter(sourceOrFolderFilter);
finalFilter->AddFilter(m_ui->m_searchWidget->GetFilter());
return finalFilter;
}
void MaterialBrowserWidget::OpenSelectedEntries()
{
const AZStd::vector<AssetBrowserEntry*> entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
const int multiSelectPromptThreshold = 10;
if (entries.size() >= multiSelectPromptThreshold)
{
if (QMessageBox::question(
QApplication::activeWindow(),
QString("Attemptng to open %1 files").arg(entries.size()),
"Would you like to open anyway?",
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
{
return;
}
}
for (const AssetBrowserEntry* entry : entries)
{
const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
if (!sourceEntry)
{
const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry);
if (productEntry)
{
sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(productEntry->GetParent());
}
}
if (sourceEntry)
{
if (AzFramework::StringFunc::Path::IsExtension(sourceEntry->GetFullPath().c_str(), MaterialExtension))
{
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, sourceEntry->GetFullPath());
}
else if (AzFramework::StringFunc::Path::IsExtension(sourceEntry->GetFullPath().c_str(), MaterialTypeExtension))
{
//ignore MaterialTypeExtension
}
else
{
QDesktopServices::openUrl(QUrl::fromLocalFile(sourceEntry->GetFullPath().c_str()));
}
}
}
}
void MaterialBrowserWidget::EntryAdded(const AssetBrowserEntry* entry)
{
if (m_pathToSelect.empty())
{
return;
}
const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
if (!sourceEntry)
{
const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry);
if (productEntry)
{
sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(productEntry->GetParent());
}
}
if (sourceEntry)
{
AZStd::string sourcePath = sourceEntry->GetFullPath();
AzFramework::StringFunc::Path::Normalize(sourcePath);
if (m_pathToSelect == sourcePath)
{
m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect);
m_pathToSelect.clear();
}
}
}
void MaterialBrowserWidget::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZStd::string absolutePath;
MaterialDocumentRequestBus::EventResult(absolutePath, documentId, &MaterialDocumentRequestBus::Events::GetAbsolutePath);
if (!absolutePath.empty())
{
m_pathToSelect = absolutePath;
AzFramework::StringFunc::Path::Normalize(m_pathToSelect);
m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect);
}
}
void MaterialBrowserWidget::OpenOptionsMenu()
{
QMenu menu;
QAction* action = new QAction("Show Asset Preview", this);
action->setCheckable(true);
action->setChecked(m_ui->m_previewerFrame->isVisible());
connect(action, &QAction::triggered, [this]() {
bool isPreviewFrameVisible = m_ui->m_previewerFrame->isVisible();
m_ui->m_previewerFrame->setVisible(!isPreviewFrameVisible);
if (isPreviewFrameVisible)
{
m_materialBrowserState = m_ui->m_splitter->saveState();
m_ui->m_splitter->setSizes(QList({ 1, 0 }));
}
else
{
m_ui->m_splitter->restoreState(m_materialBrowserState);
}
});
menu.addAction(action);
menu.exec(QCursor::pos());
}
} // namespace MaterialEditor
#include <Source/Window/moc_MaterialBrowserWidget.cpp>
@@ -0,0 +1,81 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <Atom/Document/MaterialDocumentNotificationBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWidget>
#include <QByteArray>
AZ_POP_DISABLE_WARNING
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserFilterModel;
class CompositeFilter;
class AssetBrowserEntry;
class ProductAssetBrowserEntry;
class SourceAssetBrowserEntry;
}
}
namespace Ui
{
class MaterialBrowserWidget;
}
namespace MaterialEditor
{
//! Provides a tree view of all available materials and other assets exposed by the MaterialEditor.
class MaterialBrowserWidget
: public QWidget
, public AzToolsFramework::AssetBrowser::AssetBrowserModelNotificationBus::Handler
, public MaterialDocumentNotificationBus::Handler
{
Q_OBJECT
public:
MaterialBrowserWidget(QWidget* parent = nullptr);
~MaterialBrowserWidget();
private:
AzToolsFramework::AssetBrowser::FilterConstType CreateFilter() const;
void OpenSelectedEntries();
QScopedPointer<Ui::MaterialBrowserWidget> m_ui;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_filterModel = nullptr;
//! if new asset is being created with this path it will automatically be selected
AZStd::string m_pathToSelect;
// AssetBrowserModelNotificationBus::Handler implementation
void EntryAdded(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) override;
// MaterialDocumentNotificationBus::Handler implementation
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OpenOptionsMenu();
QByteArray m_materialBrowserState;
};
} // namespace MaterialEditor
@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MaterialBrowserWidget</class>
<widget class="QWidget" name="MaterialBrowserWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>691</width>
<height>554</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>400</height>
</size>
</property>
<property name="windowTitle">
<string>Asset Browser</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="m_headerLayout">
<item alignment="Qt::AlignTop">
<widget class="AzToolsFramework::AssetBrowser::SearchWidget" name="m_searchWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item alignment="Qt::AlignTop">
<widget class="QPushButton" name="m_viewOptionButton">
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="m_splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="m_leftLayout" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">vertical-align: top</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="previewWidgetWrapper">
<layout class="QVBoxLayout" name="m_rightLayout">
<item>
<widget class="AzToolsFramework::AssetBrowser::PreviewerFrame" name="m_previewerFrame">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::AssetBrowser::SearchWidget</class>
<extends>QWidget</extends>
<header>AzToolsFramework/AssetBrowser/Search/SearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::AssetBrowserTreeView</class>
<extends>QTreeView</extends>
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h</header>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::PreviewerFrame</class>
<extends>QFrame</extends>
<header>AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,23 @@
<RCC>
<qresource prefix="/">
<file>Icons/material.svg</file>
<file>Icons/materialtype.svg</file>
<file>Icons/mesh.svg</file>
<file>Icons/texture.svg</file>
<file>Icons/add.png</file>
<file>Icons/addnew.png</file>
<file>Icons/down.png</file>
<file>Icons/up.png</file>
<file>Icons/refresh.png</file>
<file>Icons/remove.png</file>
<file>Icons/save.png</file>
<file>Icons/save_active.png</file>
<file>Icons/save_disabled.png</file>
<file>Icons/save_normal.png</file>
<file>Icons/texture_edit.png</file>
<file>Icons/grid.svg</file>
<file>Icons/shadow.svg</file>
<file>Icons/toneMapping.svg</file>
<file>Icons/View.svg</file>
</qresource>
</RCC>
@@ -0,0 +1,827 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
#include <AtomToolsFramework/Util/Util.h>
#include <Atom/Document/MaterialDocumentRequestBus.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Atom/Window/MaterialEditorWindowNotificationBus.h>
#include <Atom/Viewport/MaterialViewportRequestBus.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Viewport/MaterialViewportWidget.h>
#include <Viewport/MaterialViewportWidget.h>
#include <Window/MaterialEditorWindow.h>
#include <Window/MaterialTypesLibrary.h>
#include <Window/PerformanceMonitor/PerformanceMonitorWidget.h>
#include <Window/MaterialBrowserWidget.h>
#include <Window/MaterialInspector/MaterialInspector.h>
#include <Window/ViewportSettingsInspector/ViewportSettingsInspector.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QCloseEvent>
#include <QVariant>
#include <QFileDialog>
#include <QWindow>
#include <QVBoxLayout>
AZ_POP_DISABLE_WARNING
namespace MaterialEditor
{
MaterialEditorWindow::MaterialEditorWindow(QWidget* parent /* = 0 */)
: AzQtComponents::DockMainWindow(parent)
{
QApplication::setWindowIcon(QIcon(":/Icons/materialtype.svg"));
AZ::Name apiName = AZ::RHI::Factory::Get().GetName();
if (!apiName.IsEmpty())
{
QString title = QString{ "Material Editor (%1)" }.arg(apiName.GetCStr());
setWindowTitle(title);
}
else
{
AZ_Assert(false, "Render API name not found");
setWindowTitle("Material Editor");
}
m_advancedDockManager = new AzQtComponents::FancyDocking(this);
setDockNestingEnabled(true);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
m_menuBar = new QMenuBar(this);
setMenuBar(m_menuBar);
m_toolBar = new MaterialEditorToolBar(this);
addToolBar(m_toolBar);
m_centralWidget = new QWidget(this);
m_tabWidget = new AzQtComponents::TabWidget(m_centralWidget);
m_tabWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
m_tabWidget->setContentsMargins(0, 0, 0, 0);
m_materialViewport = new MaterialViewportWidget(m_centralWidget);
m_materialViewport->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
QVBoxLayout* vl = new QVBoxLayout(m_centralWidget);
vl->setMargin(0);
vl->setContentsMargins(0, 0, 0, 0);
vl->addWidget(m_tabWidget);
vl->addWidget(m_materialViewport);
m_centralWidget->setLayout(vl);
setCentralWidget(m_centralWidget);
m_statusBar = new StatusBarWidget(this);
this->statusBar()->addPermanentWidget(m_statusBar, 1);
SetupMenu();
SetupTabs();
AddDockWidget("Asset Browser", new MaterialBrowserWidget, Qt::BottomDockWidgetArea, Qt::Vertical);
AddDockWidget("Inspector", new MaterialInspector, Qt::RightDockWidgetArea, Qt::Horizontal);
AddDockWidget("Viewport Settings", new ViewportSettingsInspector, Qt::LeftDockWidgetArea, Qt::Horizontal);
AddDockWidget("Performance Monitor", new PerformanceMonitorWidget, Qt::RightDockWidgetArea, Qt::Horizontal);
AddDockWidget("Python Terminal", new AzToolsFramework::CScriptTermDialog, Qt::BottomDockWidgetArea, Qt::Horizontal);
SetDockWidgetVisible("Viewport Settings", false);
SetDockWidgetVisible("Performance Monitor", false);
SetDockWidgetVisible("Python Terminal", false);
MaterialEditorWindowRequestBus::Handler::BusConnect();
MaterialDocumentNotificationBus::Handler::BusConnect();
OnDocumentOpened(AZ::Uuid::CreateNull());
}
MaterialEditorWindow::~MaterialEditorWindow()
{
MaterialDocumentNotificationBus::Handler::BusDisconnect();
MaterialEditorWindowRequestBus::Handler::BusDisconnect();
}
bool MaterialEditorWindow::AddDockWidget(const AZStd::string& name, QWidget* widget, uint32_t area, uint32_t orientation)
{
auto dockWidgetItr = m_dockWidgets.find(name);
if (dockWidgetItr != m_dockWidgets.end() || !widget)
{
return false;
}
auto dockWidget = new AzQtComponents::StyledDockWidget(name.c_str());
dockWidget->setObjectName(name.c_str());
dockWidget->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
widget->setParent(dockWidget);
widget->setMinimumSize(QSize(300, 300));
dockWidget->setWidget(widget);
addDockWidget(aznumeric_cast<Qt::DockWidgetArea>(area), dockWidget);
resizeDocks({ dockWidget }, { 400 }, aznumeric_cast<Qt::Orientation>(orientation));
m_dockWidgets[name] = dockWidget;
return true;
}
void MaterialEditorWindow::RemoveDockWidget(const AZStd::string& name)
{
auto dockWidgetItr = m_dockWidgets.find(name);
if (dockWidgetItr != m_dockWidgets.end())
{
delete dockWidgetItr->second;
m_dockWidgets.erase(dockWidgetItr);
}
}
void MaterialEditorWindow::SetDockWidgetVisible(const AZStd::string& name, bool visible)
{
auto dockWidgetItr = m_dockWidgets.find(name);
if (dockWidgetItr != m_dockWidgets.end())
{
dockWidgetItr->second->setVisible(visible);
}
}
bool MaterialEditorWindow::IsDockWidgetVisible(const AZStd::string& name) const
{
auto dockWidgetItr = m_dockWidgets.find(name);
if (dockWidgetItr != m_dockWidgets.end())
{
return dockWidgetItr->second->isVisible();
}
return false;
}
AZStd::vector<AZStd::string> MaterialEditorWindow::GetDockWidgetNames() const
{
AZStd::vector<AZStd::string> names;
names.reserve(m_dockWidgets.size());
for (const auto& dockWidgetPair : m_dockWidgets)
{
names.push_back(dockWidgetPair.first);
}
return names;
}
void MaterialEditorWindow::ResizeViewportRenderTarget(uint32_t width, uint32_t height)
{
QSize requestedViewportSize = QSize(width, height) / devicePixelRatioF();
QSize currentViewportSize = m_materialViewport->size();
QSize offset = requestedViewportSize - currentViewportSize;
QSize requestedWindowSize = size() + offset;
resize(requestedWindowSize);
AZ_Assert(m_materialViewport->size() == requestedViewportSize,
"Resizing the window did not give the expected viewport size. Requested %d x %d but got %d x %d.",
requestedViewportSize.width(), requestedViewportSize.height(),
m_materialViewport->size().width(), m_materialViewport->size().height());
QSize newDeviceSize = m_materialViewport->size();
AZ_Warning("Material Editor", newDeviceSize.width() == width && newDeviceSize.height() == height,
"Resizing the window did not give the expected frame size. Requested %d x %d but got %d x %d.",
width, height,
newDeviceSize.width(), newDeviceSize.height());
}
void MaterialEditorWindow::LockViewportRenderTargetSize(uint32_t width, uint32_t height)
{
m_materialViewport->LockRenderTargetSize(width, height);
}
void MaterialEditorWindow::UnlockViewportRenderTargetSize()
{
m_materialViewport->UnlockRenderTargetSize();
}
void MaterialEditorWindow::closeEvent(QCloseEvent* closeEvent)
{
bool didClose = true;
MaterialDocumentSystemRequestBus::BroadcastResult(didClose, &MaterialDocumentSystemRequestBus::Events::CloseAllDocuments);
if (!didClose)
{
closeEvent->ignore();
return;
}
MaterialEditorWindowNotificationBus::Broadcast(&MaterialEditorWindowNotifications::OnMaterialEditorWindowClosing);
}
void MaterialEditorWindow::OnDocumentOpened(const AZ::Uuid& documentId)
{
bool isOpen = false;
MaterialDocumentRequestBus::EventResult(isOpen, documentId, &MaterialDocumentRequestBus::Events::IsOpen);
bool isSavable = false;
MaterialDocumentRequestBus::EventResult(isSavable, documentId, &MaterialDocumentRequestBus::Events::IsSavable);
bool canUndo = false;
MaterialDocumentRequestBus::EventResult(canUndo, documentId, &MaterialDocumentRequestBus::Events::CanUndo);
bool canRedo = false;
MaterialDocumentRequestBus::EventResult(canRedo, documentId, &MaterialDocumentRequestBus::Events::CanRedo);
// Update UI to display the new document
AddTabForDocumentId(documentId);
UpdateTabForDocumentId(documentId);
const bool hasTabs = m_tabWidget->count() > 0;
// Update menu options
m_actionNewFromOther->setEnabled(true);
m_actionOpen->setEnabled(true);
m_actionOpenRecent->setEnabled(false);
m_actionClose->setEnabled(hasTabs);
m_actionCloseAll->setEnabled(hasTabs);
m_actionCloseOthers->setEnabled(hasTabs);
m_actionSave->setEnabled(isOpen && isSavable);
m_actionSaveAsCopy->setEnabled(isOpen && isSavable);
m_actionSaveAsChild->setEnabled(isOpen);
m_actionSaveAll->setEnabled(hasTabs);
m_actionExit->setEnabled(true);
m_actionUndo->setEnabled(canUndo);
m_actionRedo->setEnabled(canRedo);
m_actionPreferences->setEnabled(false);
m_actionAssetBrowser->setEnabled(true);
m_actionInspector->setEnabled(true);
m_actionConsole->setEnabled(false);
m_actionPythonTerminal->setEnabled(true);
m_actionPerfMonitor->setEnabled(true);
m_actionViewportSettings->setEnabled(true);
m_actionPreviousTab->setEnabled(m_tabWidget->count() > 1);
m_actionNextTab->setEnabled(m_tabWidget->count() > 1);
m_actionHelp->setEnabled(false);
m_actionAbout->setEnabled(false);
activateWindow();
raise();
const QString documentPath = GetDocumentPath(documentId);
if (!documentPath.isEmpty())
{
m_statusBar->UpdateStatusInfo(QString("Material opened: %1").arg(documentPath));
}
}
void MaterialEditorWindow::OnDocumentClosed(const AZ::Uuid& documentId)
{
RemoveTabForDocumentId(documentId);
const QString documentPath = GetDocumentPath(documentId);
m_statusBar->UpdateStatusInfo(QString("Material closed: %1").arg(documentPath));
}
void MaterialEditorWindow::OnDocumentModified(const AZ::Uuid& documentId)
{
UpdateTabForDocumentId(documentId);
}
void MaterialEditorWindow::OnDocumentUndoStateChanged(const AZ::Uuid& documentId)
{
if (documentId == GetDocumentIdFromTab(m_tabWidget->currentIndex()))
{
bool canUndo = false;
MaterialDocumentRequestBus::EventResult(canUndo, documentId, &MaterialDocumentRequestBus::Events::CanUndo);
bool canRedo = false;
MaterialDocumentRequestBus::EventResult(canRedo, documentId, &MaterialDocumentRequestBus::Events::CanRedo);
m_actionUndo->setEnabled(canUndo);
m_actionRedo->setEnabled(canRedo);
}
}
void MaterialEditorWindow::OnDocumentSaved(const AZ::Uuid& documentId)
{
UpdateTabForDocumentId(documentId);
const QString documentPath = GetDocumentPath(documentId);
m_statusBar->UpdateStatusInfo(QString("Material saved: %1").arg(documentPath));
}
void MaterialEditorWindow::SetupMenu()
{
// Generating the main menu manually because it's easier and we will have some dynamic or data driven entries
m_menuFile = m_menuBar->addMenu("&File");
SetupMenu_New();
m_actionOpen = m_menuFile->addAction("&Open...", [this]() {
const AZStd::vector<AZ::Data::AssetType> assetTypes = { azrtti_typeid<AZ::RPI::MaterialAsset>() };
const AZStd::string filePath = AtomToolsFramework::GetOpenFileInfo(assetTypes).absoluteFilePath().toUtf8().constData();
if (!filePath.empty())
{
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, filePath);
}
}, QKeySequence::Open);
m_actionOpenRecent = m_menuFile->addAction("Open &Recent");
m_menuFile->addSeparator();
m_actionSave = m_menuFile->addAction("&Save", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
bool result = false;
MaterialDocumentSystemRequestBus::BroadcastResult(result, &MaterialDocumentSystemRequestBus::Events::SaveDocument, documentId);
if (!result)
{
const QString documentPath = GetDocumentPath(documentId);
m_statusBar->UpdateStatusError(QString("Failed to save material: %1").arg(documentPath));
}
}, QKeySequence::Save);
m_actionSaveAsCopy = m_menuFile->addAction("Save &As...", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
const QString documentPath = GetDocumentPath(documentId);
bool result = false;
MaterialDocumentSystemRequestBus::BroadcastResult(result, &MaterialDocumentSystemRequestBus::Events::SaveDocumentAsCopy,
documentId, AtomToolsFramework::GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
if (!result)
{
m_statusBar->UpdateStatusError(QString("Failed to save material: %1").arg(documentPath));
}
}, QKeySequence::SaveAs);
m_actionSaveAsChild = m_menuFile->addAction("Save As &Child...", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
const QString documentPath = GetDocumentPath(documentId);
bool result = false;
MaterialDocumentSystemRequestBus::BroadcastResult(result, &MaterialDocumentSystemRequestBus::Events::SaveDocumentAsChild,
documentId, AtomToolsFramework::GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
if (!result)
{
m_statusBar->UpdateStatusError(QString("Failed to save material: %1").arg(documentPath));
}
});
m_actionSaveAll = m_menuFile->addAction("Save A&ll", [this]() {
bool result = false;
MaterialDocumentSystemRequestBus::BroadcastResult(result, &MaterialDocumentSystemRequestBus::Events::SaveAllDocuments);
if (!result)
{
m_statusBar->UpdateStatusError(QString("Failed to save materials."));
}
});
m_menuFile->addSeparator();
m_actionClose = m_menuFile->addAction("&Close", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CloseDocument, documentId);
}, QKeySequence::Close);
m_actionCloseAll = m_menuFile->addAction("Close All", [this]() {
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CloseAllDocuments);
});
m_actionCloseOthers = m_menuFile->addAction("Close Others", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
});
m_menuFile->addSeparator();
auto presetMenu = m_menuFile->addMenu("Presets");
presetMenu->addAction("New Model Preset", [this]() {
AZ::Render::ModelPresetPtr preset;
MaterialViewportRequestBus::BroadcastResult(preset, &MaterialViewportRequestBus::Events::AddModelPreset, AZ::Render::ModelPreset());
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::SelectModelPreset, preset);
});
presetMenu->addAction("Save Model Preset", [this]() {
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled.modelpreset.azasset").absoluteFilePath();
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::SaveModelPresetSelection,
AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
});
presetMenu->addSeparator();
presetMenu->addAction("New Lighting Preset", [this]() {
AZ::Render::LightingPresetPtr preset;
MaterialViewportRequestBus::BroadcastResult(preset, &MaterialViewportRequestBus::Events::AddLightingPreset, AZ::Render::LightingPreset());
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::SelectLightingPreset, preset);
});
presetMenu->addAction("Save Lighting Preset", [this]() {
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled.lightingpreset.azasset").absoluteFilePath();
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::SaveLightingPresetSelection,
AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
});
presetMenu->addSeparator();
presetMenu->addAction("Reload Presets", [this]() {
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::ReloadContent);
}, QKeySequence::Refresh);
m_menuFile->addSeparator();
m_menuFile->addAction("Run &Python...", [this]() {
const QString script = QFileDialog::getOpenFileName(this, "Run Script", QString(), QString("*.py"));
if (!script.isEmpty())
{
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData());
}
});
m_menuFile->addSeparator();
m_actionExit = m_menuFile->addAction("E&xit", [this]() {
close();
}, QKeySequence::Quit);
m_menuEdit = m_menuBar->addMenu("&Edit");
m_actionUndo = m_menuEdit->addAction("&Undo", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
bool result = false;
MaterialDocumentRequestBus::EventResult(result, documentId, &MaterialDocumentRequestBus::Events::Undo);
if (!result)
{
const QString documentPath = GetDocumentPath(documentId);
m_statusBar->UpdateStatusError(QString("Failed to perform Undo in material: %1").arg(documentPath));
}
}, QKeySequence::Undo);
m_actionRedo = m_menuEdit->addAction("&Redo", [this]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
bool result = false;
MaterialDocumentRequestBus::EventResult(result, documentId, &MaterialDocumentRequestBus::Events::Redo);
if (!result)
{
const QString documentPath = GetDocumentPath(documentId);
m_statusBar->UpdateStatusError(QString("Failed to perform Undo in material: %1").arg(documentPath));
}
}, QKeySequence::Redo);
m_menuEdit->addSeparator();
m_actionPreferences = m_menuEdit->addAction("&Preferences...", [this]() {
}, QKeySequence::Preferences);
m_actionPreferences->setEnabled(false);
m_menuView = m_menuBar->addMenu("&View");
m_actionAssetBrowser = m_menuView->addAction("&Asset Browser", [this]() {
const AZStd::string label = "Asset Browser";
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
});
m_actionInspector = m_menuView->addAction("&Inspector", [this]() {
const AZStd::string label = "Inspector";
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
});
m_actionConsole = m_menuView->addAction("&Console", [this]() {
});
m_actionPythonTerminal = m_menuView->addAction("Python &Terminal", [this]() {
const AZStd::string label = "Python Terminal";
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
});
m_actionPerfMonitor = m_menuView->addAction("Performance &Monitor", [this]() {
const AZStd::string label = "Performance Monitor";
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
});
m_actionViewportSettings = m_menuView->addAction("Viewport Settings", [this]() {
const AZStd::string label = "Viewport Settings";
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
});
m_menuView->addSeparator();
m_actionPreviousTab = m_menuView->addAction("&Previous Tab", [this]() {
SelectPreviousTab();
}, Qt::CTRL | Qt::SHIFT | Qt::Key_Tab); //QKeySequence::PreviousChild is mapped incorrectly in Qt
m_actionNextTab = m_menuView->addAction("&Next Tab", [this]() {
SelectNextTab();
}, Qt::CTRL | Qt::Key_Tab); //QKeySequence::NextChild works as expected but mirroring Previous
m_menuHelp = m_menuBar->addMenu("&Help");
m_actionHelp = m_menuHelp->addAction("&Help...", [this]() {
});
m_actionAbout = m_menuHelp->addAction("&About...", [this]() {
});
}
void MaterialEditorWindow::SetupMenu_New()
{
m_menuNew = m_menuFile->addMenu("&New");
// Enumerate and load all the relevant config files in the project.
// (The files are stored in a temporary list instead of processed in the callback because deep operations inside
// AssetCatalogRequestBus::EnumerateAssets can lead to deadlocked)
AZStd::list<AZ::Data::AssetInfo> materialTypeAssetInfoList;
AZ::Data::AssetCatalogRequests::AssetEnumerationCB enumerateCB = [&materialTypeAssetInfoList]([[maybe_unused]] const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info)
{
if (AzFramework::StringFunc::EndsWith(info.m_relativePath.c_str(), ".materialtypes.json"))
{
materialTypeAssetInfoList.push_front(info);
}
};
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, enumerateCB, nullptr);
AZStd::vector<MaterialTypeEntry> materialTypeEntries;
for (const auto& info : materialTypeAssetInfoList)
{
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(info.m_assetId);
if (!sourcePath.empty())
{
MaterialTypesLibrary library;
if (AZ::RPI::JsonUtils::LoadObjectFromFile(sourcePath, library))
{
for (const auto& entry : library.m_entries)
{
materialTypeEntries.push_back(entry);
}
AZ_TracePrintf("Material Editor", "Loaded materialType library: %s.\n", info.m_relativePath.c_str());
}
}
}
const char* engineRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
AZ_Assert(engineRoot, "Engine Root not initialized");
// Generate the menu actions for creating new materials from types
for (const auto& materialTypeEntry : materialTypeEntries)
{
AZStd::string absoluteMaterialTypePath;
AZ::StringFunc::Path::Join(engineRoot, materialTypeEntry.m_filePath.c_str(), absoluteMaterialTypePath);
m_menuNew->addAction(materialTypeEntry.m_displayName.c_str(), [this, absoluteMaterialTypePath]() {
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CreateDocumentFromFile,
absoluteMaterialTypePath, AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
});
}
m_menuNew->addSeparator();
// Add the final entry that allows selection of any source file
m_actionNewFromOther = m_menuNew->addAction("Other &Type...", [this]() {
// [GFX TODO] This is functional but UI is not as designed
// This will open the parent material or type then save it as a child material
// if the operation fails the material document is closed/destroyed
const AZStd::vector<AZ::Data::AssetType> assetTypes = {
azrtti_typeid<AZ::RPI::MaterialTypeAsset>(),
};
const QString& sourcePath = AtomToolsFramework::GetOpenFileInfo(assetTypes).absoluteFilePath();
if (!sourcePath.isEmpty())
{
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
const QString& targetPath = AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath();
if (!targetPath.isEmpty())
{
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CreateDocumentFromFile,
sourcePath.toUtf8().constData(), targetPath.toUtf8().constData());
}
}
}, QKeySequence::New);
}
void MaterialEditorWindow::SetupTabs()
{
// The tab bar should only be visible if it has active documents
m_tabWidget->setVisible(false);
m_tabWidget->setTabBarAutoHide(false);
m_tabWidget->setMovable(true);
m_tabWidget->setTabsClosable(true);
m_tabWidget->setUsesScrollButtons(true);
// Add context menu for right-clicking on tabs
m_tabWidget->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
connect(m_tabWidget, &QWidget::customContextMenuRequested, this, [this]() {
OpenTabContextMenu();
});
// This signal will be triggered whenever a tab is added, removed, selected, clicked, dragged
// When the last tab is removed tabIndex will be -1 and the document ID will be null
// This should automatically clear the active document
connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int tabIndex) {
const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex);
MaterialDocumentNotificationBus::Broadcast(&MaterialDocumentNotificationBus::Events::OnDocumentOpened, documentId);
});
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int tabIndex) {
const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex);
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CloseDocument, documentId);
});
}
void MaterialEditorWindow::AddTabForDocumentId(const AZ::Uuid& documentId)
{
bool isOpen = false;
MaterialDocumentRequestBus::EventResult(isOpen, documentId, &MaterialDocumentRequestBus::Events::IsOpen);
if (documentId.IsNull() || !isOpen)
{
return;
}
// Blocking signals from the tab bar so the currentChanged signal is not sent while a document is already being opened.
// This prevents the OnDocumentOpened notification from being sent recursively.
const QSignalBlocker blocker(m_tabWidget);
// If a tab for this document already exists then select it instead of creating a new one
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
{
if (documentId == GetDocumentIdFromTab(tabIndex))
{
m_tabWidget->setCurrentIndex(tabIndex);
m_tabWidget->repaint();
return;
}
}
// Create a new tab for the document ID and assign it's label to the file name of the document.
AZStd::string absolutePath;
MaterialDocumentRequestBus::EventResult(absolutePath, documentId, &MaterialDocumentRequestBus::Events::GetAbsolutePath);
AZStd::string filename;
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
// The tab widget requires a dummy page per tab
QWidget* placeHolderWidget = new QWidget(m_centralWidget);
placeHolderWidget->setContentsMargins(0, 0, 0, 0);
placeHolderWidget->resize(0, 0);
placeHolderWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
const int tabIndex = m_tabWidget->addTab(placeHolderWidget, filename.c_str());
// The user can manually reorder tabs which will invalidate any association by index.
// We need to store the document ID with the tab using the tab instead of a separate mapping.
m_tabWidget->tabBar()->setTabData(tabIndex, QVariant(documentId.ToString<QString>()));
m_tabWidget->setTabToolTip(tabIndex, absolutePath.c_str());
m_tabWidget->setCurrentIndex(tabIndex);
m_tabWidget->setVisible(true);
m_tabWidget->repaint();
}
void MaterialEditorWindow::RemoveTabForDocumentId(const AZ::Uuid& documentId)
{
// We are not blocking signals here because we want closing tabs to close the associated document
// and automatically select the next document.
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
{
if (documentId == GetDocumentIdFromTab(tabIndex))
{
m_tabWidget->removeTab(tabIndex);
m_tabWidget->setVisible(m_tabWidget->count() > 0);
m_tabWidget->repaint();
break;
}
}
}
void MaterialEditorWindow::UpdateTabForDocumentId(const AZ::Uuid& documentId)
{
// Whenever a document is opened, saved, or modified we need to update the tab label
if (!documentId.IsNull())
{
// Because tab order and indexes can change from user interactions, we cannot store a map
// between a tab index and document ID.
// We must iterate over all of the tabs to find the one associated with this document.
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
{
if (documentId == GetDocumentIdFromTab(tabIndex))
{
AZStd::string absolutePath;
MaterialDocumentRequestBus::EventResult(absolutePath, documentId, &MaterialDocumentRequestBus::Events::GetAbsolutePath);
AZStd::string filename;
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
bool isModified = false;
MaterialDocumentRequestBus::EventResult(isModified, documentId, &MaterialDocumentRequestBus::Events::IsModified);
// We use an asterisk appended to the file name to denote modified document
if (isModified)
{
filename += " *";
}
m_tabWidget->setTabText(tabIndex, filename.c_str());
m_tabWidget->setTabToolTip(tabIndex, absolutePath.c_str());
m_tabWidget->repaint();
break;
}
}
}
}
QString MaterialEditorWindow::GetDocumentPath(const AZ::Uuid& documentId) const
{
AZStd::string absolutePath;
MaterialDocumentRequestBus::EventResult(absolutePath, documentId, &MaterialDocumentRequestBus::Handler::GetAbsolutePath);
return absolutePath.c_str();
}
AZ::Uuid MaterialEditorWindow::GetDocumentIdFromTab(const int tabIndex) const
{
const QVariant tabData = m_tabWidget->tabBar()->tabData(tabIndex);
if (!tabData.isNull())
{
// We need to be able to convert between a UUID and a string to store and retrieve a document ID from the tab bar
const QString documentIdString = tabData.toString();
const QByteArray documentIdBytes = documentIdString.toUtf8();
const AZ::Uuid documentId(documentIdBytes.data(), documentIdBytes.size());
return documentId;
}
return AZ::Uuid::CreateNull();
}
void MaterialEditorWindow::OpenTabContextMenu()
{
const QTabBar* tabBar = m_tabWidget->tabBar();
const QPoint position = tabBar->mapFromGlobal(QCursor::pos());
const int clickedTabIndex = tabBar->tabAt(position);
const int currentTabIndex = tabBar->currentIndex();
if (clickedTabIndex >= 0)
{
QMenu tabMenu;
const QString selectActionName = (currentTabIndex == clickedTabIndex) ? "Select in Browser" : "Select";
tabMenu.addAction(selectActionName, [this, clickedTabIndex]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
MaterialDocumentNotificationBus::Broadcast(&MaterialDocumentNotificationBus::Events::OnDocumentOpened, documentId);
});
tabMenu.addAction("Close", [this, clickedTabIndex]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CloseDocument, documentId);
});
auto closeOthersAction = tabMenu.addAction("Close Others", [this, clickedTabIndex]() {
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
});
closeOthersAction->setEnabled(tabBar->count() > 1);
tabMenu.exec(QCursor::pos());
}
}
void MaterialEditorWindow::SelectPreviousTab()
{
if (m_tabWidget->count() > 1)
{
// Adding count to wrap around when index <= 0
m_tabWidget->setCurrentIndex((m_tabWidget->currentIndex() + m_tabWidget->count() - 1) % m_tabWidget->count());
}
}
void MaterialEditorWindow::SelectNextTab()
{
if (m_tabWidget->count() > 1)
{
m_tabWidget->setCurrentIndex((m_tabWidget->currentIndex() + 1) % m_tabWidget->count());
}
}
} // namespace MaterialEditor
#include <Window/moc_MaterialEditorWindow.cpp>
@@ -0,0 +1,141 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <Atom/Document/MaterialDocumentNotificationBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <AzQtComponents/Components/DockMainWindow.h>
#include <AzQtComponents/Components/FancyDocking.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <AzQtComponents/Components/Widgets/TabWidget.h>
#include <Atom/Window/MaterialEditorWindowRequestBus.h>
#include <Viewport/MaterialViewportWidget.h>
#include <Window/ToolBar/MaterialEditorToolBar.h>
#include <Window/StatusBar/StatusBarWidget.h>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
AZ_POP_DISABLE_WARNING
#endif
namespace AzToolsFramework
{
class CScriptTermDialog;
}
namespace MaterialEditor
{
/**
* MaterialEditorWindow is the main class. Its responsibility is limited to initializing and connecting
* its panels, managing selection of assets, and performing high-level actions like saving. It contains...
* 1) MaterialBrowser - The user browses for Material (.material) assets.
* 2) MaterialViewport - The user can see the selected Material applied to a model.
* 3) MaterialPropertyInspector - The user edits the properties of the selected Material.
*/
class MaterialEditorWindow
: public AzQtComponents::DockMainWindow
, private MaterialEditorWindowRequestBus::Handler
, private MaterialDocumentNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MaterialEditorWindow, AZ::SystemAllocator, 0);
MaterialEditorWindow(QWidget* parent = 0);
~MaterialEditorWindow();
private:
// MaterialEditorWindowRequestBus::Handler overrides...
bool AddDockWidget(const AZStd::string& name, QWidget* widget, uint32_t area, uint32_t orientation) override;
void RemoveDockWidget(const AZStd::string& name) override;
void SetDockWidgetVisible(const AZStd::string& name, bool visible) override;
bool IsDockWidgetVisible(const AZStd::string& name) const override;
AZStd::vector<AZStd::string> GetDockWidgetNames() const override;
void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override;
void LockViewportRenderTargetSize(uint32_t width, uint32_t height) override;
void UnlockViewportRenderTargetSize() override;
// MaterialDocumentNotificationBus::Handler overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OnDocumentClosed(const AZ::Uuid& documentId) override;
void OnDocumentModified(const AZ::Uuid& documentId) override;
void OnDocumentUndoStateChanged(const AZ::Uuid& documentId) override;
void OnDocumentSaved(const AZ::Uuid& documentId) override;
void SetupMenu();
void SetupMenu_New();
void SetupTabs();
void AddTabForDocumentId(const AZ::Uuid& documentId);
void RemoveTabForDocumentId(const AZ::Uuid& documentId);
void UpdateTabForDocumentId(const AZ::Uuid& documentId);
QString GetDocumentPath(const AZ::Uuid& documentId) const;
AZ::Uuid GetDocumentIdFromTab(const int tabIndex) const;
void OpenTabContextMenu();
void SelectPreviousTab();
void SelectNextTab();
void closeEvent(QCloseEvent* closeEvent) override;
AzQtComponents::FancyDocking* m_advancedDockManager = nullptr;
QWidget* m_centralWidget = nullptr;
QMenuBar* m_menuBar = nullptr;
AzQtComponents::TabWidget* m_tabWidget = nullptr;
MaterialViewportWidget* m_materialViewport = nullptr;
MaterialEditorToolBar* m_toolBar = nullptr;
AZStd::unordered_map <AZStd::string, AzQtComponents::StyledDockWidget*> m_dockWidgets;
QMenu* m_menuFile = {};
QMenu* m_menuNew = {};
QAction* m_actionNewFromOther = {};
QAction* m_actionOpen = {};
QAction* m_actionOpenRecent = {};
QAction* m_actionClose = {};
QAction* m_actionCloseAll = {};
QAction* m_actionCloseOthers = {};
QAction* m_actionSave = {};
QAction* m_actionSaveAsCopy = {};
QAction* m_actionSaveAsChild = {};
QAction* m_actionSaveAll = {};
QAction* m_actionExit = {};
QMenu* m_menuEdit = {};
QAction* m_actionUndo = {};
QAction* m_actionRedo = {};
QAction* m_actionPreferences = {};
QMenu* m_menuView = {};
QAction* m_actionAssetBrowser = {};
QAction* m_actionInspector = {};
QAction* m_actionConsole = {};
QAction* m_actionPythonTerminal = {};
QAction* m_actionPerfMonitor = {};
QAction* m_actionViewportSettings = {};
QAction* m_actionNextTab = {};
QAction* m_actionPreviousTab = {};
QMenu* m_menuHelp = {};
QAction* m_actionHelp = {};
QAction* m_actionAbout = {};
StatusBarWidget* m_statusBar = {};
};
} // namespace MaterialEditor
@@ -0,0 +1,117 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <Atom/Window/MaterialEditorWindowFactoryRequestBus.h>
#include <Source/Window/MaterialEditorWindowComponent.h>
#include <Source/Window/MaterialEditorWindow.h>
#include <Source/Window/MaterialTypesLibrary.h>
namespace MaterialEditor
{
void MaterialEditorWindowComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<MaterialEditorWindowComponent, AZ::Component>()
->Version(0);
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<MaterialEditorWindowFactoryRequestBus>("MaterialEditorWindowFactoryRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "materialeditor")
->Event("CreateMaterialEditorWindow", &MaterialEditorWindowFactoryRequestBus::Events::CreateMaterialEditorWindow)
->Event("DestroyMaterialEditorWindow", &MaterialEditorWindowFactoryRequestBus::Events::DestroyMaterialEditorWindow)
;
behaviorContext->EBus<MaterialEditorWindowRequestBus>("MaterialEditorWindowRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "materialeditor")
->Event("SetDockWidgetVisible", &MaterialEditorWindowRequestBus::Events::SetDockWidgetVisible)
->Event("IsDockWidgetVisible", &MaterialEditorWindowRequestBus::Events::IsDockWidgetVisible)
->Event("GetDockWidgetNames", &MaterialEditorWindowRequestBus::Events::GetDockWidgetNames)
->Event("ResizeViewportRenderTarget", &MaterialEditorWindowRequestBus::Events::ResizeViewportRenderTarget)
->Event("LockViewportRenderTargetSize", &MaterialEditorWindowRequestBus::Events::LockViewportRenderTargetSize)
->Event("UnlockViewportRenderTargetSize", &MaterialEditorWindowRequestBus::Events::UnlockViewportRenderTargetSize)
;
}
MaterialTypesLibrary::Reflect(context);
}
void MaterialEditorWindowComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("AssetBrowserService", 0x1e54fffb));
required.push_back(AZ_CRC("PropertyManagerService", 0x63a3d7ad));
required.push_back(AZ_CRC("SourceControlService", 0x67f338fd));
}
void MaterialEditorWindowComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("MaterialEditorWindowService", 0xb6e7d922));
}
void MaterialEditorWindowComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("MaterialEditorWindowService", 0xb6e7d922));
}
void MaterialEditorWindowComponent::Init()
{
}
void MaterialEditorWindowComponent::Activate()
{
AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect();
MaterialEditorWindowFactoryRequestBus::Handler::BusConnect();
AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequests::EnableSourceControl, true);
}
void MaterialEditorWindowComponent::Deactivate()
{
MaterialEditorWindowFactoryRequestBus::Handler::BusDisconnect();
AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect();
m_window.reset();
}
void MaterialEditorWindowComponent::CreateMaterialEditorWindow()
{
m_materialBrowserInteractions.reset(aznew MaterialBrowserInteractions);
m_window.reset(aznew MaterialEditorWindow);
m_window->show();
}
void MaterialEditorWindowComponent::DestroyMaterialEditorWindow()
{
m_window.reset();
}
QWidget* MaterialEditorWindowComponent::GetAppMainWindow()
{
return m_window.get();
}
}
@@ -0,0 +1,62 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <Atom/Window/MaterialEditorWindowFactoryRequestBus.h>
#include <Source/Window/MaterialBrowserInteractions.h>
#include <Source/Window/MaterialEditorWindow.h>
namespace MaterialEditor
{
//! MaterialEditorWindowComponent is the entry point for the Material Editor gem user interface, and is mainly
//! used for initialization and registration of other classes, including MaterialEditorWindow.
class MaterialEditorWindowComponent
: public AZ::Component
, private AzToolsFramework::EditorWindowRequestBus::Handler
, private MaterialEditorWindowFactoryRequestBus::Handler
{
public:
AZ_COMPONENT(MaterialEditorWindowComponent, "{03976F19-3C74-49FE-A15F-7D3CADBA616C}");
static void Reflect(AZ::ReflectContext* context);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
private:
////////////////////////////////////////////////////////////////////////
// MaterialEditorWindowFactoryRequestBus::Handler overrides...
void CreateMaterialEditorWindow() override;
void DestroyMaterialEditorWindow() override;
////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorWindowRequests::Bus::Handler
QWidget* GetAppMainWindow() override;
//////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
AZStd::unique_ptr<MaterialEditorWindow> m_window;
AZStd::unique_ptr<MaterialBrowserInteractions> m_materialBrowserInteractions;
};
}
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/Window/MaterialEditorWindowModule.h>
#include <Window/MaterialEditorWindowComponent.h>
void InitMaterialEditorResources()
{
//Must register qt resources from other modules
Q_INIT_RESOURCE(MaterialEditor);
Q_INIT_RESOURCE(InspectorWidget);
}
namespace MaterialEditor
{
MaterialEditorWindowModule::MaterialEditorWindowModule()
{
InitMaterialEditorResources();
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
m_descriptors.insert(m_descriptors.end(), {
MaterialEditorWindowComponent::CreateDescriptor(),
});
}
AZ::ComponentTypeList MaterialEditorWindowModule::GetRequiredSystemComponents() const
{
return AZ::ComponentTypeList{
azrtti_typeid<MaterialEditorWindowComponent>(),
};
}
}
@@ -0,0 +1,261 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.h>
#include <Atom/Document/MaterialDocumentRequestBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <Source/Window/MaterialInspector/MaterialInspector.h>
namespace MaterialEditor
{
MaterialInspector::MaterialInspector(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
{
MaterialDocumentNotificationBus::Handler::BusConnect();
}
MaterialInspector::~MaterialInspector()
{
MaterialDocumentNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void MaterialInspector::Reset()
{
m_documentId = AZ::Uuid::CreateNull();
m_groups = {};
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
void MaterialInspector::OnDocumentOpened(const AZ::Uuid& documentId)
{
AddGroupsBegin();
m_documentId = documentId;
bool isOpen = false;
MaterialDocumentRequestBus::EventResult(isOpen, m_documentId, &MaterialDocumentRequestBus::Events::IsOpen);
if (!m_documentId.IsNull() && isOpen)
{
// Create the top group for displaying details about the material
AddDetailsGroup();
// Create groups for displaying editable UV names
AddUvNamesGroup();
// Create groups for displaying editable properties
AddPropertiesGroup();
AtomToolsFramework::InspectorRequestBus::Handler::BusConnect(m_documentId);
}
AddGroupsEnd();
}
void MaterialInspector::AddDetailsGroup()
{
const AZ::RPI::MaterialTypeSourceData* materialTypeSourceData = nullptr;
MaterialDocumentRequestBus::EventResult(materialTypeSourceData, m_documentId, &MaterialDocumentRequestBus::Events::GetMaterialTypeSourceData);
const AZStd::string groupNameId = "details";
const AZStd::string groupDisplayName = "Details";
const AZStd::string groupDescription = materialTypeSourceData->m_description;
auto& group = m_groups[groupNameId];
AtomToolsFramework::DynamicProperty property;
MaterialDocumentRequestBus::EventResult(property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("details.materialType"));
group.m_properties.emplace_back(AZStd::move(property));
property = {};
MaterialDocumentRequestBus::EventResult(property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("details.parentMaterial"));
group.m_properties.emplace_back(AZStd::move(property));
AddGroup(groupNameId, groupDisplayName, groupDescription,
new AtomToolsFramework::InspectorPropertyGroupWidget(&group, group.TYPEINFO_Uuid(), this));
}
void MaterialInspector::AddUvNamesGroup()
{
AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset;
MaterialDocumentRequestBus::EventResult(materialAsset, m_documentId, &MaterialDocumentRequestBus::Events::GetAsset);
const AZStd::string groupNameId = UvGroupName;
const AZStd::string groupDisplayName = "UV Names";
const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model.";
auto& group = m_groups[groupNameId];
const auto& uvNameMap = materialAsset->GetMaterialTypeAsset()->GetUvNameMap();
group.m_properties.reserve(uvNameMap.size());
for (const auto& uvNamePair : uvNameMap)
{
AtomToolsFramework::DynamicProperty property;
MaterialDocumentRequestBus::EventResult(property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::RPI::MaterialPropertyId(groupNameId, uvNamePair.m_shaderInput.ToString()).GetFullName());
group.m_properties.emplace_back(AZStd::move(property));
}
AddGroup(groupNameId, groupDisplayName, groupDescription,
new AtomToolsFramework::InspectorPropertyGroupWidget(&group, group.TYPEINFO_Uuid(), this));
}
void MaterialInspector::AddPropertiesGroup()
{
const AZ::RPI::MaterialTypeSourceData* materialTypeSourceData = nullptr;
MaterialDocumentRequestBus::EventResult(materialTypeSourceData, m_documentId, &MaterialDocumentRequestBus::Events::GetMaterialTypeSourceData);
for (const auto& groupDefinition : materialTypeSourceData->GetGroupDefinitionsInDisplayOrder())
{
const AZStd::string& groupNameId = groupDefinition.m_nameId;
const AZStd::string& groupDisplayName = !groupDefinition.m_displayName.empty() ? groupDefinition.m_displayName : groupNameId;
const AZStd::string& groupDescription = !groupDefinition.m_description.empty() ? groupDefinition.m_description : groupDisplayName;
auto& group = m_groups[groupNameId];
const auto& propertyLayout = materialTypeSourceData->m_propertyLayout;
const auto& propertyListItr = propertyLayout.m_properties.find(groupNameId);
if (propertyListItr != propertyLayout.m_properties.end())
{
group.m_properties.reserve(propertyListItr->second.size());
for (const auto& propertyDefinition : propertyListItr->second)
{
AtomToolsFramework::DynamicProperty property;
MaterialDocumentRequestBus::EventResult(property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::RPI::MaterialPropertyId(groupNameId, propertyDefinition.m_nameId).GetFullName());
group.m_properties.emplace_back(AZStd::move(property));
}
}
AddGroup(groupNameId, groupDisplayName, groupDescription,
new AtomToolsFramework::InspectorPropertyGroupWidget(&group, group.TYPEINFO_Uuid(), this));
}
}
void MaterialInspector::OnDocumentPropertyValueModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property)
{
for (auto& groupPair : m_groups)
{
for (auto& reflectedProperty : groupPair.second.m_properties)
{
if (reflectedProperty.GetId() == property.GetId())
{
if (!AZ::RPI::MaterialUtils::ArePropertyValuesEqual(reflectedProperty.GetValue(), property.GetValue()))
{
reflectedProperty.SetValue(property.GetValue());
AtomToolsFramework::InspectorRequestBus::Event(documentId, &AtomToolsFramework::InspectorRequestBus::Events::RefreshGroup, groupPair.first);
}
return;
}
}
}
}
void MaterialInspector::OnDocumentPropertyConfigModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property)
{
for (auto& groupPair : m_groups)
{
for (auto& reflectedProperty : groupPair.second.m_properties)
{
if (reflectedProperty.GetId() == property.GetId())
{
// Visibility changes require the entire reflected property editor tree for this group to be rebuilt
if (reflectedProperty.GetVisibility() != property.GetVisibility())
{
reflectedProperty.SetConfig(property.GetConfig());
AtomToolsFramework::InspectorRequestBus::Event(documentId, &AtomToolsFramework::InspectorRequestBus::Events::RebuildGroup, groupPair.first);
}
else
{
reflectedProperty.SetConfig(property.GetConfig());
AtomToolsFramework::InspectorRequestBus::Event(documentId, &AtomToolsFramework::InspectorRequestBus::Events::RefreshGroup, groupPair.first);
}
return;
}
}
}
}
void MaterialInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
// For some reason the reflected property editor notifications are not symmetrical
// This function is called continuously anytime a property changes until the edit has completed
// Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has started and ended
const AtomToolsFramework::DynamicProperty* property = FindPropertyForNode(pNode);
if (property)
{
if (m_activeProperty != property)
{
m_activeProperty = property;
MaterialDocumentRequestBus::Event(m_documentId, &MaterialDocumentRequestBus::Events::BeginEdit);
}
}
}
void MaterialInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
const AtomToolsFramework::DynamicProperty* property = FindPropertyForNode(pNode);
if (property)
{
if (m_activeProperty == property)
{
MaterialDocumentRequestBus::Event(m_documentId, &MaterialDocumentRequestBus::Events::SetPropertyValue,
property->GetId(), property->GetValue());
}
}
}
void MaterialInspector::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode)
{
// As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been completed but they are not being called following that pattern.
// when this function executes the changes to the property are ready to be committed or reverted
const AtomToolsFramework::DynamicProperty* property = FindPropertyForNode(pNode);
if (property)
{
if (m_activeProperty == property)
{
MaterialDocumentRequestBus::Event(m_documentId, &MaterialDocumentRequestBus::Events::SetPropertyValue,
property->GetId(), property->GetValue());
MaterialDocumentRequestBus::Event(m_documentId, &MaterialDocumentRequestBus::Events::EndEdit);
m_activeProperty = nullptr;
}
}
}
const AtomToolsFramework::DynamicProperty* MaterialInspector::FindPropertyForNode(AzToolsFramework::InstanceDataNode* pNode) const
{
// Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property
const AZ::SerializeContext::ClassElement* elementData = pNode->GetElementMetadata();
for (const AzToolsFramework::InstanceDataNode* currentNode = pNode; currentNode; currentNode = currentNode->GetParent())
{
const AZ::SerializeContext* context = currentNode->GetSerializeContext();
const AZ::SerializeContext::ClassData* classData = currentNode->GetClassMetadata();
if (context && classData)
{
if (context->CanDowncast(classData->m_typeId, azrtti_typeid<AtomToolsFramework::DynamicProperty>(), classData->m_azRtti, nullptr))
{
return static_cast<const AtomToolsFramework::DynamicProperty*>(currentNode->FirstInstance());
}
}
}
return nullptr;
}
} // namespace MaterialEditor
#include <Source/Window/MaterialInspector/moc_MaterialInspector.cpp>
@@ -0,0 +1,71 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/containers/unordered_map.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <Atom/Document/MaterialDocumentNotificationBus.h>
#endif
namespace MaterialEditor
{
//! Provides controls for viewing and editing a material document settings.
//! The settings can be divided into cards, with each one showing a subset of properties.
class MaterialInspector
: public AtomToolsFramework::InspectorWidget
, public MaterialDocumentNotificationBus::Handler
, public AzToolsFramework::IPropertyEditorNotify
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MaterialInspector, AZ::SystemAllocator, 0);
explicit MaterialInspector(QWidget* parent = nullptr);
~MaterialInspector() override;
// AtomToolsFramework::InspectorRequestBus::Handler overrides...
void Reset() override;
private:
void AddDetailsGroup();
void AddUvNamesGroup();
void AddPropertiesGroup();
// MaterialDocumentNotificationBus::Handler implementation
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OnDocumentPropertyValueModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property) override;
void OnDocumentPropertyConfigModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property) override;
// AzToolsFramework::IPropertyEditorNotify overrides...
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override {}
void RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode*, const QPoint&) override {}
void PropertySelectionChanged(AzToolsFramework::InstanceDataNode*, bool) override {}
const AtomToolsFramework::DynamicProperty* FindPropertyForNode(AzToolsFramework::InstanceDataNode* pNode) const;
// Tracking the property that is activiley being edited in the inspector
const AtomToolsFramework::DynamicProperty* m_activeProperty = nullptr;
AZ::Uuid m_documentId = AZ::Uuid::CreateNull();
AZStd::unordered_map<AZStd::string, AtomToolsFramework::DynamicPropertyGroup> m_groups;
};
} // namespace MaterialEditor
@@ -0,0 +1,48 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#undef RC_INVOKED
#include <Source/Window/MaterialTypesLibrary.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace MaterialEditor
{
void MaterialTypeEntry::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->RegisterGenericType<AZStd::vector<MaterialTypeEntry>>();
serializeContext->Class<MaterialTypeEntry>()
->Version(1)
->Field("displayName", &MaterialTypeEntry::m_displayName)
->Field("filePath", &MaterialTypeEntry::m_filePath)
;
}
}
void MaterialTypesLibrary::Reflect(AZ::ReflectContext* context)
{
MaterialTypeEntry::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MaterialTypesLibrary>()
->Version(1)
->Field("entries", &MaterialTypesLibrary::m_entries)
;
}
}
}
@@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace MaterialEditor
{
//! MaterialTypeEntry describes a materialType file that can be used to create new material
struct MaterialTypeEntry final
{
AZ_TYPE_INFO(MaterialTypeEntry, "{ECD408E0-61B7-4553-B966-6131820B684E}");
AZ_CLASS_ALLOCATOR(MaterialTypeEntry, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_displayName;
AZStd::string m_filePath;
};
//! MaterialTypesLibrary is a collection of MaterialTypeEntries
struct MaterialTypesLibrary final
{
AZ_TYPE_INFO(MaterialTypesLibrary, "{C062C6F6-2AFB-4695-9E31-2AD5067A9073}");
AZ_CLASS_ALLOCATOR(MaterialTypesLibrary, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
AZStd::vector<MaterialTypeEntry> m_entries;
};
}
@@ -0,0 +1,62 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/Viewport/PerformanceMonitorRequestBus.h>
#include <Source/Window/PerformanceMonitor/PerformanceMonitorWidget.h>
#include <Source/Window/PerformanceMonitor/ui_PerformanceMonitorWidget.h>
#include <Qtimer>
namespace MaterialEditor
{
PerformanceMonitorWidget::PerformanceMonitorWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::PerformanceMonitorWidget)
{
m_ui->setupUi(this);
m_updateTimer.setInterval(UpdateIntervalMs);
connect(&m_updateTimer, &QTimer::timeout, this, &PerformanceMonitorWidget::UpdateMetrics);
}
PerformanceMonitorWidget::~PerformanceMonitorWidget() = default;
void PerformanceMonitorWidget::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
m_updateTimer.start();
PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, true);
}
void PerformanceMonitorWidget::hideEvent(QHideEvent* event)
{
QWidget::hideEvent(event);
m_updateTimer.stop();
PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, false);
}
void PerformanceMonitorWidget::UpdateMetrics()
{
PerformanceMetrics metrics;
PerformanceMonitorRequestBus::BroadcastResult(metrics, &PerformanceMonitorRequestBus::Handler::GetMetrics);
m_ui->m_cpuFrameTimeValue->setText(QString("%1 ms").arg(QString::number(metrics.m_cpuFrameTimeMs, 'f', 2)));
m_ui->m_gpuFrameTimeValue->setText(QString("%1 ms").arg(QString::number(metrics.m_gpuFrameTimeMs, 'f', 2)));
int frameRate = metrics.m_cpuFrameTimeMs > 0 ? aznumeric_cast<int>(1000 / metrics.m_cpuFrameTimeMs) : 0;
m_ui->m_frameRateValue->setText(QString::number(frameRate));
}
} // namespace MaterialEditor
#include <Source/Window/PerformanceMonitor/moc_PerformanceMonitorWidget.cpp>
@@ -0,0 +1,48 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <QWidget>
#include <Qtimer>
namespace Ui
{
class PerformanceMonitorWidget;
}
namespace MaterialEditor
{
//! Displays performance metrics for Material Editor
class PerformanceMonitorWidget
: public QWidget
{
Q_OBJECT
public:
PerformanceMonitorWidget(QWidget* parent = nullptr);
~PerformanceMonitorWidget();
private slots:
void UpdateMetrics();
private:
void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
QScopedPointer<Ui::PerformanceMonitorWidget> m_ui;
QTimer m_updateTimer;
//! interval to request performance metrics
static constexpr int UpdateIntervalMs = 1000;
};
} // namespace MaterialEditor
@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PerformanceMonitorWidget</class>
<widget class="QWidget" name="PerformanceMonitorWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>237</width>
<height>200</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string>Performance Monitor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="m_frameRateLayout">
<item>
<widget class="QLabel" name="m_frameRateTitle">
<property name="text">
<string>Frame Rate:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_frameRateValue">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="m_cpuFrameTimeLayout">
<item>
<widget class="QLabel" name="m_cpuFrameTimeTitle">
<property name="text">
<string>CPU Frame Time:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_cpuFrameTimeValue">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="m_gpuFrameTimeLayout">
<item>
<widget class="QLabel" name="m_gpuFrameTimeTitle">
<property name="text">
<string>GPU Frame Time:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_gpuFrameTimeValue">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>67</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Window/StatusBar/StatusBarWidget.h>
#include <Source/Window/StatusBar/ui_StatusBarWidget.h>
namespace MaterialEditor
{
StatusBarWidget::StatusBarWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::StatusBarWidget)
{
m_ui->setupUi(this);
}
StatusBarWidget::~StatusBarWidget() = default;
void StatusBarWidget::UpdateStatusInfo(const QString& status)
{
m_ui->m_statusLabel->setText(QString("<font color=\"White\">%1</font>").arg(status));
}
void StatusBarWidget::UpdateStatusWarning(const QString& status)
{
m_ui->m_statusLabel->setText(QString("<font color=\"Yellow\">%1</font>").arg(status));
}
void StatusBarWidget::UpdateStatusError(const QString& status)
{
m_ui->m_statusLabel->setText(QString("<font color=\"Red\">%1</font>").arg(status));
}
} // namespace MaterialEditor
#include <Source/Window/StatusBar/moc_StatusBarWidget.cpp>
@@ -0,0 +1,46 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWidget>
AZ_POP_DISABLE_WARNING
#endif
namespace Ui
{
class StatusBarWidget;
}
namespace MaterialEditor
{
//! Status bar for MaterialEditor.
class StatusBarWidget
: public QWidget
{
Q_OBJECT
public:
StatusBarWidget(QWidget* parent = nullptr);
~StatusBarWidget();
void UpdateStatusInfo(const QString& status);
void UpdateStatusWarning(const QString& status);
void UpdateStatusError(const QString& status);
private:
QScopedPointer<Ui::StatusBarWidget> m_ui;
};
} // namespace MaterialEditor
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StatusBarWidget</class>
<widget class="QWidget" name="StatusBarWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>691</width>
<height>165</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Status Bar</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="m_statusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../MaterialEditor.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,45 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzQtComponents/Components/Widgets/Slider.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Source/Window/ToolBar/FovSliderWidget.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QHBoxLayout>
#include <QLabel>
AZ_POP_DISABLE_WARNING
namespace MaterialEditor
{
FovSliderWidget::FovSliderWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout();
m_label = new QLabel("Field of View");
layout->addWidget(m_label);
AzQtComponents::SliderInt* slider = new AzQtComponents::SliderInt(Qt::Orientation::Horizontal);
slider->setRange(60, 120);
connect(slider, &AzQtComponents::SliderInt::valueChanged, this, &FovSliderWidget::SliderValueChanged);
slider->setValue(90);
layout->addWidget(slider);
setLayout(layout);
}
void FovSliderWidget::SliderValueChanged(int value) const
{
m_label->setText(QString("Field of View (%1)").arg(value));
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView,
aznumeric_cast<float>(value));
}
} // namespace MaterialEditor
@@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/PlatformDef.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QLabel>
#include <QWidget>
AZ_POP_DISABLE_WARNING
namespace MaterialEditor
{
//! Widget for adjusting Field of View in viewport
class FovSliderWidget
: public QWidget
{
Q_OBJECT
public:
FovSliderWidget(QWidget* parent = 0);
~FovSliderWidget() = default;
private:
QLabel* m_label;
private Q_SLOTS:
void SliderValueChanged(int value) const;
};
} // namespace MaterialEditor
@@ -0,0 +1,109 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Window/ToolBar/LightingPresetComboBox.h>
#include <Atom/Viewport/MaterialViewportRequestBus.h>
#include <Atom/Feature/Utils/LightingPreset.h>
namespace MaterialEditor
{
LightingPresetComboBox::LightingPresetComboBox(QWidget* parent)
: QComboBox(parent)
{
connect(this, static_cast<void(QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, [this](int index) {
if (index >= 0 && index < m_presets.size())
{
MaterialViewportRequestBus::Broadcast(
&MaterialViewportRequestBus::Events::SelectLightingPreset, m_presets[index]);
}
});
Refresh();
MaterialViewportNotificationBus::Handler::BusConnect();
}
LightingPresetComboBox::~LightingPresetComboBox()
{
MaterialViewportNotificationBus::Handler::BusDisconnect();
}
void LightingPresetComboBox::Refresh()
{
clear();
setDuplicatesEnabled(true);
m_presets.clear();
MaterialViewportRequestBus::BroadcastResult(m_presets, &MaterialViewportRequestBus::Events::GetLightingPresets);
AZStd::sort(m_presets.begin(), m_presets.end(), [](const auto& a, const auto& b) {
return a->m_displayName < b->m_displayName; });
blockSignals(true);
for (const auto& preset : m_presets)
{
addItem(preset->m_displayName.c_str());
}
blockSignals(false);
AZ::Render::LightingPresetPtr preset;
MaterialViewportRequestBus::BroadcastResult(preset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(preset);
}
void LightingPresetComboBox::OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset)
{
auto presetItr = AZStd::find(m_presets.begin(), m_presets.end(), preset);
if (presetItr != m_presets.end())
{
setCurrentIndex(AZStd::distance(m_presets.begin(), presetItr));
}
}
void LightingPresetComboBox::OnLightingPresetAdded(AZ::Render::LightingPresetPtr preset)
{
if (!m_reloading)
{
Refresh();
}
}
void LightingPresetComboBox::OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset)
{
if (!m_reloading)
{
auto presetItr = AZStd::find(m_presets.begin(), m_presets.end(), preset);
if (presetItr != m_presets.end())
{
setItemText(AZStd::distance(m_presets.begin(), presetItr), preset->m_displayName.c_str());
}
else
{
Refresh();
}
}
}
void LightingPresetComboBox::OnBeginReloadContent()
{
m_reloading = true;
}
void LightingPresetComboBox::OnEndReloadContent()
{
m_reloading = false;
Refresh();
}
} // namespace MaterialEditor
#include <Source/Window/ToolBar/moc_LightingPresetComboBox.cpp>
@@ -0,0 +1,42 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QComboBox>
#include <Atom/Viewport/MaterialViewportNotificationBus.h>
#endif
namespace MaterialEditor
{
class LightingPresetComboBox
: public QComboBox
, public MaterialViewportNotificationBus::Handler
{
Q_OBJECT
public:
LightingPresetComboBox(QWidget* parent = 0);
~LightingPresetComboBox();
void Refresh();
private:
// MaterialViewportNotificationBus::Handler overrides...
void OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset) override;
void OnLightingPresetAdded(AZ::Render::LightingPresetPtr preset) override;
void OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset) override;
void OnBeginReloadContent() override;
void OnEndReloadContent() override;
bool m_reloading = false;
AZ::Render::LightingPresetPtrVector m_presets;
};
} // namespace MaterialEditor
@@ -0,0 +1,92 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Window/ToolBar/MaterialEditorToolBar.h>
#include <Source/Window/ToolBar/ModelPresetComboBox.h>
#include <Source/Window/ToolBar/LightingPresetComboBox.h>
#include <Source/Window/ToolBar/FovSliderWidget.h>
#include <Atom/Document/MaterialEditorSettingsBus.h>
#include <Atom/Viewport/MaterialViewportRequestBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <AzQtComponents/Components/Widgets/ToolBar.h>
#include <QMenu>
#include <QToolButton>
#include <QAction>
AZ_POP_DISABLE_WARNING
namespace MaterialEditor
{
MaterialEditorToolBar::MaterialEditorToolBar(QWidget* parent)
: QToolBar(parent)
{
AzQtComponents::ToolBar::addMainToolBarStyle(this);
// Add toggle grid button
QAction* toggleGrid = addAction(QIcon(":/Icons/grid.svg"), "Toggle Grid");
toggleGrid->setCheckable(true);
connect(toggleGrid, &QAction::triggered, [this, toggleGrid]() {
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::SetGridEnabled, toggleGrid->isChecked());
});
bool enableGrid = false;
MaterialViewportRequestBus::BroadcastResult(enableGrid, &MaterialViewportRequestBus::Events::GetGridEnabled);
toggleGrid->setChecked(enableGrid);
// Add toggle shadow catcher button
QAction* toggleShadowCatcher = addAction(QIcon(":/Icons/shadow.svg"), "Toggle Shadow Catcher");
toggleShadowCatcher->setCheckable(true);
connect(toggleShadowCatcher, &QAction::triggered, [this, toggleShadowCatcher]() {
MaterialViewportRequestBus::Broadcast(&MaterialViewportRequestBus::Events::SetShadowCatcherEnabled, toggleShadowCatcher->isChecked());
});
bool enableShadowCatcher = false;
MaterialViewportRequestBus::BroadcastResult(enableShadowCatcher, &MaterialViewportRequestBus::Events::GetShadowCatcherEnabled);
toggleShadowCatcher->setChecked(enableShadowCatcher);
// Add mapping selection button
//[GFX TODO][ATOM-3992]
QToolButton* toneMappingButton = new QToolButton(this);
QMenu* toneMappingMenu = new QMenu(toneMappingButton);
toneMappingMenu->addAction("None", [this]() {
MaterialEditorSettingsRequestBus::Broadcast(&MaterialEditorSettingsRequests::SetStringProperty, "toneMapping", "None");
});
toneMappingMenu->addAction("Gamma2.2", [this]() {
MaterialEditorSettingsRequestBus::Broadcast(&MaterialEditorSettingsRequests::SetStringProperty, "toneMapping", "Gamma2.2");
});
toneMappingMenu->addAction("ACES", [this]() {
MaterialEditorSettingsRequestBus::Broadcast(&MaterialEditorSettingsRequests::SetStringProperty, "toneMapping", "ACES");
});
toneMappingButton->setMenu(toneMappingMenu);
toneMappingButton->setText("Tone Mapping");
toneMappingButton->setIcon(QIcon(":/Icons/toneMapping.svg"));
toneMappingButton->setPopupMode(QToolButton::InstantPopup);
// hiding button until DisplayMapper supports changing settings at run time
toneMappingButton->setVisible(false);
addWidget(toneMappingButton);
// Add model combo box
auto modelPresetComboBox = new ModelPresetComboBox(this);
modelPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents);
addWidget(modelPresetComboBox);
// Add lighting preset combo box
auto lightingPresetComboBox = new LightingPresetComboBox(this);
lightingPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents);
addWidget(lightingPresetComboBox);
FovSliderWidget* fovSlider = new FovSliderWidget(this);
fovSlider->setFixedWidth(250);
addWidget(fovSlider);
}
} // namespace MaterialEditor
#include <Source/Window/ToolBar/moc_MaterialEditorToolBar.cpp>
@@ -0,0 +1,28 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QToolBar>
#endif
namespace MaterialEditor
{
class MaterialEditorToolBar
: public QToolBar
{
Q_OBJECT
public:
MaterialEditorToolBar(QWidget* parent = 0);
};
} // namespace MaterialEditor
@@ -0,0 +1,109 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Window/ToolBar/ModelPresetComboBox.h>
#include <Atom/Viewport/MaterialViewportRequestBus.h>
#include <Atom/Feature/Utils/ModelPreset.h>
namespace MaterialEditor
{
ModelPresetComboBox::ModelPresetComboBox(QWidget* parent)
: QComboBox(parent)
{
connect(this, static_cast<void(QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, [this](int index) {
if (index >= 0 && index < m_presets.size())
{
MaterialViewportRequestBus::Broadcast(
&MaterialViewportRequestBus::Events::SelectModelPreset, m_presets[index]);
}
});
Refresh();
MaterialViewportNotificationBus::Handler::BusConnect();
}
ModelPresetComboBox::~ModelPresetComboBox()
{
MaterialViewportNotificationBus::Handler::BusDisconnect();
}
void ModelPresetComboBox::Refresh()
{
clear();
setDuplicatesEnabled(true);
m_presets.clear();
MaterialViewportRequestBus::BroadcastResult(m_presets, &MaterialViewportRequestBus::Events::GetModelPresets);
AZStd::sort(m_presets.begin(), m_presets.end(), [](const auto& a, const auto& b) {
return a->m_displayName < b->m_displayName; });
blockSignals(true);
for (const auto& preset : m_presets)
{
addItem(preset->m_displayName.c_str());
}
blockSignals(false);
AZ::Render::ModelPresetPtr preset;
MaterialViewportRequestBus::BroadcastResult(preset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
OnModelPresetSelected(preset);
}
void ModelPresetComboBox::OnModelPresetSelected(AZ::Render::ModelPresetPtr preset)
{
auto presetItr = AZStd::find(m_presets.begin(), m_presets.end(), preset);
if (presetItr != m_presets.end())
{
setCurrentIndex(AZStd::distance(m_presets.begin(), presetItr));
}
}
void ModelPresetComboBox::OnModelPresetAdded(AZ::Render::ModelPresetPtr preset)
{
if (!m_reloading)
{
Refresh();
}
}
void ModelPresetComboBox::OnModelPresetChanged(AZ::Render::ModelPresetPtr preset)
{
if (!m_reloading)
{
auto presetItr = AZStd::find(m_presets.begin(), m_presets.end(), preset);
if (presetItr != m_presets.end())
{
setItemText(AZStd::distance(m_presets.begin(), presetItr), preset->m_displayName.c_str());
}
else
{
Refresh();
}
}
}
void ModelPresetComboBox::OnBeginReloadContent()
{
m_reloading = true;
}
void ModelPresetComboBox::OnEndReloadContent()
{
m_reloading = false;
Refresh();
}
} // namespace MaterialEditor
#include <Source/Window/ToolBar/moc_ModelPresetComboBox.cpp>
@@ -0,0 +1,42 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QComboBox>
#include <Atom/Viewport/MaterialViewportNotificationBus.h>
#endif
namespace MaterialEditor
{
class ModelPresetComboBox
: public QComboBox
, public MaterialViewportNotificationBus::Handler
{
Q_OBJECT
public:
ModelPresetComboBox(QWidget* parent = 0);
~ModelPresetComboBox();
void Refresh();
private:
// MaterialViewportNotificationBus::Handler overrides...
void OnModelPresetSelected(AZ::Render::ModelPresetPtr preset) override;
void OnModelPresetAdded(AZ::Render::ModelPresetPtr preset) override;
void OnModelPresetChanged(AZ::Render::ModelPresetPtr preset) override;
void OnBeginReloadContent() override;
void OnEndReloadContent() override;
bool m_reloading = false;
AZ::Render::ModelPresetPtrVector m_presets;
};
} // namespace MaterialEditor
@@ -0,0 +1,112 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Window/ViewportSettingsInspector/ViewportSettingsInspector.h>
#include <Atom/Viewport/MaterialViewportRequestBus.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
namespace MaterialEditor
{
ViewportSettingsInspector::ViewportSettingsInspector(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
{
MaterialViewportNotificationBus::Handler::BusConnect();
}
ViewportSettingsInspector::~ViewportSettingsInspector()
{
m_lightingPreset.reset();
m_modelPreset.reset();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void ViewportSettingsInspector::Popuate()
{
AddGroupsBegin();
m_modelPreset.reset();
MaterialViewportRequestBus::BroadcastResult(m_modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
if (m_modelPreset)
{
const AZStd::string groupNameId = "model";
const AZStd::string groupDisplayName = "Model";
const AZStd::string groupDescription = "Model";
AddGroup(groupNameId, groupDisplayName, groupDescription,
new AtomToolsFramework::InspectorPropertyGroupWidget(m_modelPreset.get(), m_modelPreset.get()->TYPEINFO_Uuid(), this));
}
m_lightingPreset.reset();
MaterialViewportRequestBus::BroadcastResult(m_lightingPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
if (m_lightingPreset)
{
const AZStd::string groupNameId = "lighting";
const AZStd::string groupDisplayName = "Lighting";
const AZStd::string groupDescription = "Lighting";
AddGroup(groupNameId, groupDisplayName, groupDescription,
new AtomToolsFramework::InspectorPropertyGroupWidget(m_lightingPreset.get(), m_lightingPreset.get()->TYPEINFO_Uuid(), this));
}
AddGroupsEnd();
}
void ViewportSettingsInspector::Reset()
{
m_lightingPreset.reset();
m_modelPreset.reset();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
void ViewportSettingsInspector::OnLightingPresetSelected([[maybe_unused]] AZ::Render::LightingPresetPtr preset)
{
if (m_lightingPreset != preset)
{
Popuate();
}
}
void ViewportSettingsInspector::OnModelPresetSelected([[maybe_unused]] AZ::Render::ModelPresetPtr preset)
{
if (m_modelPreset != preset)
{
Popuate();
}
}
void ViewportSettingsInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
AZ_UNUSED(pNode);
}
void ViewportSettingsInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
AZ_UNUSED(pNode);
MaterialViewportNotificationBus::Broadcast(&MaterialViewportNotificationBus::Events::OnLightingPresetChanged, m_lightingPreset);
MaterialViewportNotificationBus::Broadcast(&MaterialViewportNotificationBus::Events::OnModelPresetChanged, m_modelPreset);
}
void ViewportSettingsInspector::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode)
{
AZ_UNUSED(pNode);
MaterialViewportNotificationBus::Broadcast(&MaterialViewportNotificationBus::Events::OnLightingPresetChanged, m_lightingPreset);
MaterialViewportNotificationBus::Broadcast(&MaterialViewportNotificationBus::Events::OnModelPresetChanged, m_modelPreset);
}
} // namespace MaterialEditor
#include <Source/Window/ViewportSettingsInspector/moc_ViewportSettingsInspector.cpp>
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <Atom/Viewport/MaterialViewportNotificationBus.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/Feature/Utils/ModelPreset.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#endif
namespace MaterialEditor
{
//! Provides controls for viewing and editing a material document settings.
//! The settings can be divided into cards, with each one showing a subset of properties.
class ViewportSettingsInspector
: public AtomToolsFramework::InspectorWidget
, private AzToolsFramework::IPropertyEditorNotify
, private MaterialViewportNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ViewportSettingsInspector, AZ::SystemAllocator, 0);
explicit ViewportSettingsInspector(QWidget* parent = nullptr);
~ViewportSettingsInspector() override;
private:
void Popuate();
// AtomToolsFramework::InspectorRequestBus::Handler overrides...
void Reset() override;
// MaterialViewportNotificationBus::Handler overrides...
void OnLightingPresetSelected([[maybe_unused]] AZ::Render::LightingPresetPtr preset) override;
void OnModelPresetSelected([[maybe_unused]] AZ::Render::ModelPresetPtr preset) override;
// AzToolsFramework::IPropertyEditorNotify overrides...
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override {}
void RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode*, const QPoint&) override {}
void PropertySelectionChanged(AzToolsFramework::InstanceDataNode*, bool) override {}
AZ::Render::ModelPresetPtr m_modelPreset;
AZ::Render::LightingPresetPtr m_lightingPreset;
};
} // namespace MaterialEditor