diff --git a/Gems/Atom/Feature/Common/Assets/LookupTables/LUT_IdentityLinear_16x16x16.azasset b/Gems/Atom/Feature/Common/Assets/LookupTables/LUT_IdentityLinear_16x16x16.azasset new file mode 100644 index 0000000000..53984d0413 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/LookupTables/LUT_IdentityLinear_16x16x16.azasset @@ -0,0 +1,4105 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "LookupTableAsset", + "ClassData": { + "Name": "LookupTable", + "Intervals": [0, 68, 136, 204, 272, 341, 409, 477, 545, 613, 682, 750, 818, 886, 954, 1023], + "Values": [0, 0, 0, +0, 0, 272, +0, 0, 545, +0, 0, 818, +0, 0, 1091, +0, 0, 1364, +0, 0, 1637, +0, 0, 1910, +0, 0, 2183, +0, 0, 2457, +0, 0, 2729, +0, 0, 3003, +0, 0, 3275, +0, 0, 3549, +0, 0, 3821, +0, 0, 4095, +0, 272, 0, +0, 272, 272, +0, 272, 545, +0, 272, 818, +0, 272, 1091, +0, 272, 1364, +0, 272, 1637, +0, 272, 1910, +0, 272, 2183, +0, 272, 2457, +0, 272, 2729, +0, 272, 3003, +0, 272, 3275, +0, 272, 3549, +0, 272, 3821, +0, 272, 4095, +0, 545, 0, +0, 545, 272, +0, 545, 545, +0, 545, 818, +0, 545, 1091, +0, 545, 1364, +0, 545, 1637, +0, 545, 1910, +0, 545, 2183, +0, 545, 2457, +0, 545, 2729, +0, 545, 3003, +0, 545, 3275, +0, 545, 3549, +0, 545, 3821, +0, 545, 4095, +0, 818, 0, +0, 818, 272, +0, 818, 545, +0, 818, 818, +0, 818, 1091, +0, 818, 1364, +0, 818, 1637, +0, 818, 1910, +0, 818, 2183, +0, 818, 2457, +0, 818, 2729, +0, 818, 3003, +0, 818, 3275, +0, 818, 3549, +0, 818, 3821, +0, 818, 4095, +0, 1091, 0, +0, 1091, 272, +0, 1091, 545, +0, 1091, 818, +0, 1091, 1091, +0, 1091, 1364, +0, 1091, 1637, +0, 1091, 1910, +0, 1091, 2183, +0, 1091, 2457, +0, 1091, 2729, +0, 1091, 3003, +0, 1091, 3275, +0, 1091, 3549, +0, 1091, 3821, +0, 1091, 4095, +0, 1364, 0, +0, 1364, 272, +0, 1364, 545, +0, 1364, 818, +0, 1364, 1091, +0, 1364, 1364, +0, 1364, 1637, +0, 1364, 1910, +0, 1364, 2183, +0, 1364, 2457, +0, 1364, 2729, +0, 1364, 3003, +0, 1364, 3275, +0, 1364, 3549, +0, 1364, 3821, +0, 1364, 4095, +0, 1637, 0, +0, 1637, 272, +0, 1637, 545, +0, 1637, 818, +0, 1637, 1091, +0, 1637, 1364, +0, 1637, 1637, +0, 1637, 1910, +0, 1637, 2183, +0, 1637, 2457, +0, 1637, 2729, +0, 1637, 3003, +0, 1637, 3275, +0, 1637, 3549, +0, 1637, 3821, +0, 1637, 4095, +0, 1910, 0, +0, 1910, 272, +0, 1910, 545, +0, 1910, 818, +0, 1910, 1091, +0, 1910, 1364, +0, 1910, 1637, +0, 1910, 1910, +0, 1910, 2183, +0, 1910, 2457, +0, 1910, 2729, +0, 1910, 3003, +0, 1910, 3275, +0, 1910, 3549, +0, 1910, 3821, +0, 1910, 4095, +0, 2183, 0, +0, 2183, 272, +0, 2183, 545, +0, 2183, 818, +0, 2183, 1091, +0, 2183, 1364, +0, 2183, 1637, +0, 2183, 1910, +0, 2183, 2183, +0, 2183, 2457, +0, 2183, 2729, +0, 2183, 3003, +0, 2183, 3275, +0, 2183, 3549, +0, 2183, 3821, +0, 2183, 4095, +0, 2457, 0, +0, 2457, 272, +0, 2457, 545, +0, 2457, 818, +0, 2457, 1091, +0, 2457, 1364, +0, 2457, 1637, +0, 2457, 1910, +0, 2457, 2183, +0, 2457, 2457, +0, 2457, 2729, +0, 2457, 3003, +0, 2457, 3275, +0, 2457, 3549, +0, 2457, 3821, +0, 2457, 4095, +0, 2729, 0, +0, 2729, 272, +0, 2729, 545, +0, 2729, 818, +0, 2729, 1091, +0, 2729, 1364, +0, 2729, 1637, +0, 2729, 1910, +0, 2729, 2183, +0, 2729, 2457, +0, 2729, 2729, +0, 2729, 3003, +0, 2729, 3275, +0, 2729, 3549, +0, 2729, 3821, +0, 2729, 4095, +0, 3003, 0, +0, 3003, 272, +0, 3003, 545, +0, 3003, 818, +0, 3003, 1091, +0, 3003, 1364, +0, 3003, 1637, +0, 3003, 1910, +0, 3003, 2183, +0, 3003, 2457, +0, 3003, 2729, +0, 3003, 3003, +0, 3003, 3275, +0, 3003, 3549, +0, 3003, 3821, +0, 3003, 4095, +0, 3275, 0, +0, 3275, 272, +0, 3275, 545, +0, 3275, 818, +0, 3275, 1091, +0, 3275, 1364, +0, 3275, 1637, +0, 3275, 1910, +0, 3275, 2183, +0, 3275, 2457, +0, 3275, 2729, +0, 3275, 3003, +0, 3275, 3275, +0, 3275, 3549, +0, 3275, 3821, +0, 3275, 4095, +0, 3549, 0, +0, 3549, 272, +0, 3549, 545, +0, 3549, 818, +0, 3549, 1091, +0, 3549, 1364, +0, 3549, 1637, +0, 3549, 1910, +0, 3549, 2183, +0, 3549, 2457, +0, 3549, 2729, +0, 3549, 3003, +0, 3549, 3275, +0, 3549, 3549, +0, 3549, 3821, +0, 3549, 4095, +0, 3821, 0, +0, 3821, 272, +0, 3821, 545, +0, 3821, 818, +0, 3821, 1091, +0, 3821, 1364, +0, 3821, 1637, +0, 3821, 1910, +0, 3821, 2183, +0, 3821, 2457, +0, 3821, 2729, +0, 3821, 3003, +0, 3821, 3275, +0, 3821, 3549, +0, 3821, 3821, +0, 3821, 4095, +0, 4095, 0, +0, 4095, 272, +0, 4095, 545, +0, 4095, 818, +0, 4095, 1091, +0, 4095, 1364, +0, 4095, 1637, +0, 4095, 1910, +0, 4095, 2183, +0, 4095, 2457, +0, 4095, 2729, +0, 4095, 3003, +0, 4095, 3275, +0, 4095, 3549, +0, 4095, 3821, +0, 4095, 4095, +272, 0, 0, +272, 0, 272, +272, 0, 545, +272, 0, 818, +272, 0, 1091, +272, 0, 1364, +272, 0, 1637, +272, 0, 1910, +272, 0, 2183, +272, 0, 2457, +272, 0, 2729, +272, 0, 3003, +272, 0, 3275, +272, 0, 3549, +272, 0, 3821, +272, 0, 4095, +272, 272, 0, +272, 272, 272, +272, 272, 545, +272, 272, 818, +272, 272, 1091, +272, 272, 1364, +272, 272, 1637, +272, 272, 1910, +272, 272, 2183, +272, 272, 2457, +272, 272, 2729, +272, 272, 3003, +272, 272, 3275, +272, 272, 3549, +272, 272, 3821, +272, 272, 4095, +272, 545, 0, +272, 545, 272, +272, 545, 545, +272, 545, 818, +272, 545, 1091, +272, 545, 1364, +272, 545, 1637, +272, 545, 1910, +272, 545, 2183, +272, 545, 2457, +272, 545, 2729, +272, 545, 3003, +272, 545, 3275, +272, 545, 3549, +272, 545, 3821, +272, 545, 4095, +272, 818, 0, +272, 818, 272, +272, 818, 545, +272, 818, 818, +272, 818, 1091, +272, 818, 1364, +272, 818, 1637, +272, 818, 1910, +272, 818, 2183, +272, 818, 2457, +272, 818, 2729, +272, 818, 3003, +272, 818, 3275, +272, 818, 3549, +272, 818, 3821, +272, 818, 4095, +272, 1091, 0, +272, 1091, 272, +272, 1091, 545, +272, 1091, 818, +272, 1091, 1091, +272, 1091, 1364, +272, 1091, 1637, +272, 1091, 1910, +272, 1091, 2183, +272, 1091, 2457, +272, 1091, 2729, +272, 1091, 3003, +272, 1091, 3275, +272, 1091, 3549, +272, 1091, 3821, +272, 1091, 4095, +272, 1364, 0, +272, 1364, 272, +272, 1364, 545, +272, 1364, 818, +272, 1364, 1091, +272, 1364, 1364, +272, 1364, 1637, +272, 1364, 1910, +272, 1364, 2183, +272, 1364, 2457, +272, 1364, 2729, +272, 1364, 3003, +272, 1364, 3275, +272, 1364, 3549, +272, 1364, 3821, +272, 1364, 4095, +272, 1637, 0, +272, 1637, 272, +272, 1637, 545, +272, 1637, 818, +272, 1637, 1091, +272, 1637, 1364, +272, 1637, 1637, +272, 1637, 1910, +272, 1637, 2183, +272, 1637, 2457, +272, 1637, 2729, +272, 1637, 3003, +272, 1637, 3275, +272, 1637, 3549, +272, 1637, 3821, +272, 1637, 4095, +272, 1910, 0, +272, 1910, 272, +272, 1910, 545, +272, 1910, 818, +272, 1910, 1091, +272, 1910, 1364, +272, 1910, 1637, +272, 1910, 1910, +272, 1910, 2183, +272, 1910, 2457, +272, 1910, 2729, +272, 1910, 3003, +272, 1910, 3275, +272, 1910, 3549, +272, 1910, 3821, +272, 1910, 4095, +272, 2183, 0, +272, 2183, 272, +272, 2183, 545, +272, 2183, 818, +272, 2183, 1091, +272, 2183, 1364, +272, 2183, 1637, +272, 2183, 1910, +272, 2183, 2183, +272, 2183, 2457, +272, 2183, 2729, +272, 2183, 3003, +272, 2183, 3275, +272, 2183, 3549, +272, 2183, 3821, +272, 2183, 4095, +272, 2457, 0, +272, 2457, 272, +272, 2457, 545, +272, 2457, 818, +272, 2457, 1091, +272, 2457, 1364, +272, 2457, 1637, +272, 2457, 1910, +272, 2457, 2183, +272, 2457, 2457, +272, 2457, 2729, +272, 2457, 3003, +272, 2457, 3275, +272, 2457, 3549, +272, 2457, 3821, +272, 2457, 4095, +272, 2729, 0, +272, 2729, 272, +272, 2729, 545, +272, 2729, 818, +272, 2729, 1091, +272, 2729, 1364, +272, 2729, 1637, +272, 2729, 1910, +272, 2729, 2183, +272, 2729, 2457, +272, 2729, 2729, +272, 2729, 3003, +272, 2729, 3275, +272, 2729, 3549, +272, 2729, 3821, +272, 2729, 4095, +272, 3003, 0, +272, 3003, 272, +272, 3003, 545, +272, 3003, 818, +272, 3003, 1091, +272, 3003, 1364, +272, 3003, 1637, +272, 3003, 1910, +272, 3003, 2183, +272, 3003, 2457, +272, 3003, 2729, +272, 3003, 3003, +272, 3003, 3275, +272, 3003, 3549, +272, 3003, 3821, +272, 3003, 4095, +272, 3275, 0, +272, 3275, 272, +272, 3275, 545, +272, 3275, 818, +272, 3275, 1091, +272, 3275, 1364, +272, 3275, 1637, +272, 3275, 1910, +272, 3275, 2183, +272, 3275, 2457, +272, 3275, 2729, +272, 3275, 3003, +272, 3275, 3275, +272, 3275, 3549, +272, 3275, 3821, +272, 3275, 4095, +272, 3549, 0, +272, 3549, 272, +272, 3549, 545, +272, 3549, 818, +272, 3549, 1091, +272, 3549, 1364, +272, 3549, 1637, +272, 3549, 1910, +272, 3549, 2183, +272, 3549, 2457, +272, 3549, 2729, +272, 3549, 3003, +272, 3549, 3275, +272, 3549, 3549, +272, 3549, 3821, +272, 3549, 4095, +272, 3821, 0, +272, 3821, 272, +272, 3821, 545, +272, 3821, 818, +272, 3821, 1091, +272, 3821, 1364, +272, 3821, 1637, +272, 3821, 1910, +272, 3821, 2183, +272, 3821, 2457, +272, 3821, 2729, +272, 3821, 3003, +272, 3821, 3275, +272, 3821, 3549, +272, 3821, 3821, +272, 3821, 4095, +272, 4095, 0, +272, 4095, 272, +272, 4095, 545, +272, 4095, 818, +272, 4095, 1091, +272, 4095, 1364, +272, 4095, 1637, +272, 4095, 1910, +272, 4095, 2183, +272, 4095, 2457, +272, 4095, 2729, +272, 4095, 3003, +272, 4095, 3275, +272, 4095, 3549, +272, 4095, 3821, +272, 4095, 4095, +545, 0, 0, +545, 0, 272, +545, 0, 545, +545, 0, 818, +545, 0, 1091, +545, 0, 1364, +545, 0, 1637, +545, 0, 1910, +545, 0, 2183, +545, 0, 2457, +545, 0, 2729, +545, 0, 3003, +545, 0, 3275, +545, 0, 3549, +545, 0, 3821, +545, 0, 4095, +545, 272, 0, +545, 272, 272, +545, 272, 545, +545, 272, 818, +545, 272, 1091, +545, 272, 1364, +545, 272, 1637, +545, 272, 1910, +545, 272, 2183, +545, 272, 2457, +545, 272, 2729, +545, 272, 3003, +545, 272, 3275, +545, 272, 3549, +545, 272, 3821, +545, 272, 4095, +545, 545, 0, +545, 545, 272, +545, 545, 545, +545, 545, 818, +545, 545, 1091, +545, 545, 1364, +545, 545, 1637, +545, 545, 1910, +545, 545, 2183, +545, 545, 2457, +545, 545, 2729, +545, 545, 3003, +545, 545, 3275, +545, 545, 3549, +545, 545, 3821, +545, 545, 4095, +545, 818, 0, +545, 818, 272, +545, 818, 545, +545, 818, 818, +545, 818, 1091, +545, 818, 1364, +545, 818, 1637, +545, 818, 1910, +545, 818, 2183, +545, 818, 2457, +545, 818, 2729, +545, 818, 3003, +545, 818, 3275, +545, 818, 3549, +545, 818, 3821, +545, 818, 4095, +545, 1091, 0, +545, 1091, 272, +545, 1091, 545, +545, 1091, 818, +545, 1091, 1091, +545, 1091, 1364, +545, 1091, 1637, +545, 1091, 1910, +545, 1091, 2183, +545, 1091, 2457, +545, 1091, 2729, +545, 1091, 3003, +545, 1091, 3275, +545, 1091, 3549, +545, 1091, 3821, +545, 1091, 4095, +545, 1364, 0, +545, 1364, 272, +545, 1364, 545, +545, 1364, 818, +545, 1364, 1091, +545, 1364, 1364, +545, 1364, 1637, +545, 1364, 1910, +545, 1364, 2183, +545, 1364, 2457, +545, 1364, 2729, +545, 1364, 3003, +545, 1364, 3275, +545, 1364, 3549, +545, 1364, 3821, +545, 1364, 4095, +545, 1637, 0, +545, 1637, 272, +545, 1637, 545, +545, 1637, 818, +545, 1637, 1091, +545, 1637, 1364, +545, 1637, 1637, +545, 1637, 1910, +545, 1637, 2183, +545, 1637, 2457, +545, 1637, 2729, +545, 1637, 3003, +545, 1637, 3275, +545, 1637, 3549, +545, 1637, 3821, +545, 1637, 4095, +545, 1910, 0, +545, 1910, 272, +545, 1910, 545, +545, 1910, 818, +545, 1910, 1091, +545, 1910, 1364, +545, 1910, 1637, +545, 1910, 1910, +545, 1910, 2183, +545, 1910, 2457, +545, 1910, 2729, +545, 1910, 3003, +545, 1910, 3275, +545, 1910, 3549, +545, 1910, 3821, +545, 1910, 4095, +545, 2183, 0, +545, 2183, 272, +545, 2183, 545, +545, 2183, 818, +545, 2183, 1091, +545, 2183, 1364, +545, 2183, 1637, +545, 2183, 1910, +545, 2183, 2183, +545, 2183, 2457, +545, 2183, 2729, +545, 2183, 3003, +545, 2183, 3275, +545, 2183, 3549, +545, 2183, 3821, +545, 2183, 4095, +545, 2457, 0, +545, 2457, 272, +545, 2457, 545, +545, 2457, 818, +545, 2457, 1091, +545, 2457, 1364, +545, 2457, 1637, +545, 2457, 1910, +545, 2457, 2183, +545, 2457, 2457, +545, 2457, 2729, +545, 2457, 3003, +545, 2457, 3275, +545, 2457, 3549, +545, 2457, 3821, +545, 2457, 4095, +545, 2729, 0, +545, 2729, 272, +545, 2729, 545, +545, 2729, 818, +545, 2729, 1091, +545, 2729, 1364, +545, 2729, 1637, +545, 2729, 1910, +545, 2729, 2183, +545, 2729, 2457, +545, 2729, 2729, +545, 2729, 3003, +545, 2729, 3275, +545, 2729, 3549, +545, 2729, 3821, +545, 2729, 4095, +545, 3003, 0, +545, 3003, 272, +545, 3003, 545, +545, 3003, 818, +545, 3003, 1091, +545, 3003, 1364, +545, 3003, 1637, +545, 3003, 1910, +545, 3003, 2183, +545, 3003, 2457, +545, 3003, 2729, +545, 3003, 3003, +545, 3003, 3275, +545, 3003, 3549, +545, 3003, 3821, +545, 3003, 4095, +545, 3275, 0, +545, 3275, 272, +545, 3275, 545, +545, 3275, 818, +545, 3275, 1091, +545, 3275, 1364, +545, 3275, 1637, +545, 3275, 1910, +545, 3275, 2183, +545, 3275, 2457, +545, 3275, 2729, +545, 3275, 3003, +545, 3275, 3275, +545, 3275, 3549, +545, 3275, 3821, +545, 3275, 4095, +545, 3549, 0, +545, 3549, 272, +545, 3549, 545, +545, 3549, 818, +545, 3549, 1091, +545, 3549, 1364, +545, 3549, 1637, +545, 3549, 1910, +545, 3549, 2183, +545, 3549, 2457, +545, 3549, 2729, +545, 3549, 3003, +545, 3549, 3275, +545, 3549, 3549, +545, 3549, 3821, +545, 3549, 4095, +545, 3821, 0, +545, 3821, 272, +545, 3821, 545, +545, 3821, 818, +545, 3821, 1091, +545, 3821, 1364, +545, 3821, 1637, +545, 3821, 1910, +545, 3821, 2183, +545, 3821, 2457, +545, 3821, 2729, +545, 3821, 3003, +545, 3821, 3275, +545, 3821, 3549, +545, 3821, 3821, +545, 3821, 4095, +545, 4095, 0, +545, 4095, 272, +545, 4095, 545, +545, 4095, 818, +545, 4095, 1091, +545, 4095, 1364, +545, 4095, 1637, +545, 4095, 1910, +545, 4095, 2183, +545, 4095, 2457, +545, 4095, 2729, +545, 4095, 3003, +545, 4095, 3275, +545, 4095, 3549, +545, 4095, 3821, +545, 4095, 4095, +818, 0, 0, +818, 0, 272, +818, 0, 545, +818, 0, 818, +818, 0, 1091, +818, 0, 1364, +818, 0, 1637, +818, 0, 1910, +818, 0, 2183, +818, 0, 2457, +818, 0, 2729, +818, 0, 3003, +818, 0, 3275, +818, 0, 3549, +818, 0, 3821, +818, 0, 4095, +818, 272, 0, +818, 272, 272, +818, 272, 545, +818, 272, 818, +818, 272, 1091, +818, 272, 1364, +818, 272, 1637, +818, 272, 1910, +818, 272, 2183, +818, 272, 2457, +818, 272, 2729, +818, 272, 3003, +818, 272, 3275, +818, 272, 3549, +818, 272, 3821, +818, 272, 4095, +818, 545, 0, +818, 545, 272, +818, 545, 545, +818, 545, 818, +818, 545, 1091, +818, 545, 1364, +818, 545, 1637, +818, 545, 1910, +818, 545, 2183, +818, 545, 2457, +818, 545, 2729, +818, 545, 3003, +818, 545, 3275, +818, 545, 3549, +818, 545, 3821, +818, 545, 4095, +818, 818, 0, +818, 818, 272, +818, 818, 545, +818, 818, 818, +818, 818, 1091, +818, 818, 1364, +818, 818, 1637, +818, 818, 1910, +818, 818, 2183, +818, 818, 2457, +818, 818, 2729, +818, 818, 3003, +818, 818, 3275, +818, 818, 3549, +818, 818, 3821, +818, 818, 4095, +818, 1091, 0, +818, 1091, 272, +818, 1091, 545, +818, 1091, 818, +818, 1091, 1091, +818, 1091, 1364, +818, 1091, 1637, +818, 1091, 1910, +818, 1091, 2183, +818, 1091, 2457, +818, 1091, 2729, +818, 1091, 3003, +818, 1091, 3275, +818, 1091, 3549, +818, 1091, 3821, +818, 1091, 4095, +818, 1364, 0, +818, 1364, 272, +818, 1364, 545, +818, 1364, 818, +818, 1364, 1091, +818, 1364, 1364, +818, 1364, 1637, +818, 1364, 1910, +818, 1364, 2183, +818, 1364, 2457, +818, 1364, 2729, +818, 1364, 3003, +818, 1364, 3275, +818, 1364, 3549, +818, 1364, 3821, +818, 1364, 4095, +818, 1637, 0, +818, 1637, 272, +818, 1637, 545, +818, 1637, 818, +818, 1637, 1091, +818, 1637, 1364, +818, 1637, 1637, +818, 1637, 1910, +818, 1637, 2183, +818, 1637, 2457, +818, 1637, 2729, +818, 1637, 3003, +818, 1637, 3275, +818, 1637, 3549, +818, 1637, 3821, +818, 1637, 4095, +818, 1910, 0, +818, 1910, 272, +818, 1910, 545, +818, 1910, 818, +818, 1910, 1091, +818, 1910, 1364, +818, 1910, 1637, +818, 1910, 1910, +818, 1910, 2183, +818, 1910, 2457, +818, 1910, 2729, +818, 1910, 3003, +818, 1910, 3275, +818, 1910, 3549, +818, 1910, 3821, +818, 1910, 4095, +818, 2183, 0, +818, 2183, 272, +818, 2183, 545, +818, 2183, 818, +818, 2183, 1091, +818, 2183, 1364, +818, 2183, 1637, +818, 2183, 1910, +818, 2183, 2183, +818, 2183, 2457, +818, 2183, 2729, +818, 2183, 3003, +818, 2183, 3275, +818, 2183, 3549, +818, 2183, 3821, +818, 2183, 4095, +818, 2457, 0, +818, 2457, 272, +818, 2457, 545, +818, 2457, 818, +818, 2457, 1091, +818, 2457, 1364, +818, 2457, 1637, +818, 2457, 1910, +818, 2457, 2183, +818, 2457, 2457, +818, 2457, 2729, +818, 2457, 3003, +818, 2457, 3275, +818, 2457, 3549, +818, 2457, 3821, +818, 2457, 4095, +818, 2729, 0, +818, 2729, 272, +818, 2729, 545, +818, 2729, 818, +818, 2729, 1091, +818, 2729, 1364, +818, 2729, 1637, +818, 2729, 1910, +818, 2729, 2183, +818, 2729, 2457, +818, 2729, 2729, +818, 2729, 3003, +818, 2729, 3275, +818, 2729, 3549, +818, 2729, 3821, +818, 2729, 4095, +818, 3003, 0, +818, 3003, 272, +818, 3003, 545, +818, 3003, 818, +818, 3003, 1091, +818, 3003, 1364, +818, 3003, 1637, +818, 3003, 1910, +818, 3003, 2183, +818, 3003, 2457, +818, 3003, 2729, +818, 3003, 3003, +818, 3003, 3275, +818, 3003, 3549, +818, 3003, 3821, +818, 3003, 4095, +818, 3275, 0, +818, 3275, 272, +818, 3275, 545, +818, 3275, 818, +818, 3275, 1091, +818, 3275, 1364, +818, 3275, 1637, +818, 3275, 1910, +818, 3275, 2183, +818, 3275, 2457, +818, 3275, 2729, +818, 3275, 3003, +818, 3275, 3275, +818, 3275, 3549, +818, 3275, 3821, +818, 3275, 4095, +818, 3549, 0, +818, 3549, 272, +818, 3549, 545, +818, 3549, 818, +818, 3549, 1091, +818, 3549, 1364, +818, 3549, 1637, +818, 3549, 1910, +818, 3549, 2183, +818, 3549, 2457, +818, 3549, 2729, +818, 3549, 3003, +818, 3549, 3275, +818, 3549, 3549, +818, 3549, 3821, +818, 3549, 4095, +818, 3821, 0, +818, 3821, 272, +818, 3821, 545, +818, 3821, 818, +818, 3821, 1091, +818, 3821, 1364, +818, 3821, 1637, +818, 3821, 1910, +818, 3821, 2183, +818, 3821, 2457, +818, 3821, 2729, +818, 3821, 3003, +818, 3821, 3275, +818, 3821, 3549, +818, 3821, 3821, +818, 3821, 4095, +818, 4095, 0, +818, 4095, 272, +818, 4095, 545, +818, 4095, 818, +818, 4095, 1091, +818, 4095, 1364, +818, 4095, 1637, +818, 4095, 1910, +818, 4095, 2183, +818, 4095, 2457, +818, 4095, 2729, +818, 4095, 3003, +818, 4095, 3275, +818, 4095, 3549, +818, 4095, 3821, +818, 4095, 4095, +1091, 0, 0, +1091, 0, 272, +1091, 0, 545, +1091, 0, 818, +1091, 0, 1091, +1091, 0, 1364, +1091, 0, 1637, +1091, 0, 1910, +1091, 0, 2183, +1091, 0, 2457, +1091, 0, 2729, +1091, 0, 3003, +1091, 0, 3275, +1091, 0, 3549, +1091, 0, 3821, +1091, 0, 4095, +1091, 272, 0, +1091, 272, 272, +1091, 272, 545, +1091, 272, 818, +1091, 272, 1091, +1091, 272, 1364, +1091, 272, 1637, +1091, 272, 1910, +1091, 272, 2183, +1091, 272, 2457, +1091, 272, 2729, +1091, 272, 3003, +1091, 272, 3275, +1091, 272, 3549, +1091, 272, 3821, +1091, 272, 4095, +1091, 545, 0, +1091, 545, 272, +1091, 545, 545, +1091, 545, 818, +1091, 545, 1091, +1091, 545, 1364, +1091, 545, 1637, +1091, 545, 1910, +1091, 545, 2183, +1091, 545, 2457, +1091, 545, 2729, +1091, 545, 3003, +1091, 545, 3275, +1091, 545, 3549, +1091, 545, 3821, +1091, 545, 4095, +1091, 818, 0, +1091, 818, 272, +1091, 818, 545, +1091, 818, 818, +1091, 818, 1091, +1091, 818, 1364, +1091, 818, 1637, +1091, 818, 1910, +1091, 818, 2183, +1091, 818, 2457, +1091, 818, 2729, +1091, 818, 3003, +1091, 818, 3275, +1091, 818, 3549, +1091, 818, 3821, +1091, 818, 4095, +1091, 1091, 0, +1091, 1091, 272, +1091, 1091, 545, +1091, 1091, 818, +1091, 1091, 1091, +1091, 1091, 1364, +1091, 1091, 1637, +1091, 1091, 1910, +1091, 1091, 2183, +1091, 1091, 2457, +1091, 1091, 2729, +1091, 1091, 3003, +1091, 1091, 3275, +1091, 1091, 3549, +1091, 1091, 3821, +1091, 1091, 4095, +1091, 1364, 0, +1091, 1364, 272, +1091, 1364, 545, +1091, 1364, 818, +1091, 1364, 1091, +1091, 1364, 1364, +1091, 1364, 1637, +1091, 1364, 1910, +1091, 1364, 2183, +1091, 1364, 2457, +1091, 1364, 2729, +1091, 1364, 3003, +1091, 1364, 3275, +1091, 1364, 3549, +1091, 1364, 3821, +1091, 1364, 4095, +1091, 1637, 0, +1091, 1637, 272, +1091, 1637, 545, +1091, 1637, 818, +1091, 1637, 1091, +1091, 1637, 1364, +1091, 1637, 1637, +1091, 1637, 1910, +1091, 1637, 2183, +1091, 1637, 2457, +1091, 1637, 2729, +1091, 1637, 3003, +1091, 1637, 3275, +1091, 1637, 3549, +1091, 1637, 3821, +1091, 1637, 4095, +1091, 1910, 0, +1091, 1910, 272, +1091, 1910, 545, +1091, 1910, 818, +1091, 1910, 1091, +1091, 1910, 1364, +1091, 1910, 1637, +1091, 1910, 1910, +1091, 1910, 2183, +1091, 1910, 2457, +1091, 1910, 2729, +1091, 1910, 3003, +1091, 1910, 3275, +1091, 1910, 3549, +1091, 1910, 3821, +1091, 1910, 4095, +1091, 2183, 0, +1091, 2183, 272, +1091, 2183, 545, +1091, 2183, 818, +1091, 2183, 1091, +1091, 2183, 1364, +1091, 2183, 1637, +1091, 2183, 1910, +1091, 2183, 2183, +1091, 2183, 2457, +1091, 2183, 2729, +1091, 2183, 3003, +1091, 2183, 3275, +1091, 2183, 3549, +1091, 2183, 3821, +1091, 2183, 4095, +1091, 2457, 0, +1091, 2457, 272, +1091, 2457, 545, +1091, 2457, 818, +1091, 2457, 1091, +1091, 2457, 1364, +1091, 2457, 1637, +1091, 2457, 1910, +1091, 2457, 2183, +1091, 2457, 2457, +1091, 2457, 2729, +1091, 2457, 3003, +1091, 2457, 3275, +1091, 2457, 3549, +1091, 2457, 3821, +1091, 2457, 4095, +1091, 2729, 0, +1091, 2729, 272, +1091, 2729, 545, +1091, 2729, 818, +1091, 2729, 1091, +1091, 2729, 1364, +1091, 2729, 1637, +1091, 2729, 1910, +1091, 2729, 2183, +1091, 2729, 2457, +1091, 2729, 2729, +1091, 2729, 3003, +1091, 2729, 3275, +1091, 2729, 3549, +1091, 2729, 3821, +1091, 2729, 4095, +1091, 3003, 0, +1091, 3003, 272, +1091, 3003, 545, +1091, 3003, 818, +1091, 3003, 1091, +1091, 3003, 1364, +1091, 3003, 1637, +1091, 3003, 1910, +1091, 3003, 2183, +1091, 3003, 2457, +1091, 3003, 2729, +1091, 3003, 3003, +1091, 3003, 3275, +1091, 3003, 3549, +1091, 3003, 3821, +1091, 3003, 4095, +1091, 3275, 0, +1091, 3275, 272, +1091, 3275, 545, +1091, 3275, 818, +1091, 3275, 1091, +1091, 3275, 1364, +1091, 3275, 1637, +1091, 3275, 1910, +1091, 3275, 2183, +1091, 3275, 2457, +1091, 3275, 2729, +1091, 3275, 3003, +1091, 3275, 3275, +1091, 3275, 3549, +1091, 3275, 3821, +1091, 3275, 4095, +1091, 3549, 0, +1091, 3549, 272, +1091, 3549, 545, +1091, 3549, 818, +1091, 3549, 1091, +1091, 3549, 1364, +1091, 3549, 1637, +1091, 3549, 1910, +1091, 3549, 2183, +1091, 3549, 2457, +1091, 3549, 2729, +1091, 3549, 3003, +1091, 3549, 3275, +1091, 3549, 3549, +1091, 3549, 3821, +1091, 3549, 4095, +1091, 3821, 0, +1091, 3821, 272, +1091, 3821, 545, +1091, 3821, 818, +1091, 3821, 1091, +1091, 3821, 1364, +1091, 3821, 1637, +1091, 3821, 1910, +1091, 3821, 2183, +1091, 3821, 2457, +1091, 3821, 2729, +1091, 3821, 3003, +1091, 3821, 3275, +1091, 3821, 3549, +1091, 3821, 3821, +1091, 3821, 4095, +1091, 4095, 0, +1091, 4095, 272, +1091, 4095, 545, +1091, 4095, 818, +1091, 4095, 1091, +1091, 4095, 1364, +1091, 4095, 1637, +1091, 4095, 1910, +1091, 4095, 2183, +1091, 4095, 2457, +1091, 4095, 2729, +1091, 4095, 3003, +1091, 4095, 3275, +1091, 4095, 3549, +1091, 4095, 3821, +1091, 4095, 4095, +1364, 0, 0, +1364, 0, 272, +1364, 0, 545, +1364, 0, 818, +1364, 0, 1091, +1364, 0, 1364, +1364, 0, 1637, +1364, 0, 1910, +1364, 0, 2183, +1364, 0, 2457, +1364, 0, 2729, +1364, 0, 3003, +1364, 0, 3275, +1364, 0, 3549, +1364, 0, 3821, +1364, 0, 4095, +1364, 272, 0, +1364, 272, 272, +1364, 272, 545, +1364, 272, 818, +1364, 272, 1091, +1364, 272, 1364, +1364, 272, 1637, +1364, 272, 1910, +1364, 272, 2183, +1364, 272, 2457, +1364, 272, 2729, +1364, 272, 3003, +1364, 272, 3275, +1364, 272, 3549, +1364, 272, 3821, +1364, 272, 4095, +1364, 545, 0, +1364, 545, 272, +1364, 545, 545, +1364, 545, 818, +1364, 545, 1091, +1364, 545, 1364, +1364, 545, 1637, +1364, 545, 1910, +1364, 545, 2183, +1364, 545, 2457, +1364, 545, 2729, +1364, 545, 3003, +1364, 545, 3275, +1364, 545, 3549, +1364, 545, 3821, +1364, 545, 4095, +1364, 818, 0, +1364, 818, 272, +1364, 818, 545, +1364, 818, 818, +1364, 818, 1091, +1364, 818, 1364, +1364, 818, 1637, +1364, 818, 1910, +1364, 818, 2183, +1364, 818, 2457, +1364, 818, 2729, +1364, 818, 3003, +1364, 818, 3275, +1364, 818, 3549, +1364, 818, 3821, +1364, 818, 4095, +1364, 1091, 0, +1364, 1091, 272, +1364, 1091, 545, +1364, 1091, 818, +1364, 1091, 1091, +1364, 1091, 1364, +1364, 1091, 1637, +1364, 1091, 1910, +1364, 1091, 2183, +1364, 1091, 2457, +1364, 1091, 2729, +1364, 1091, 3003, +1364, 1091, 3275, +1364, 1091, 3549, +1364, 1091, 3821, +1364, 1091, 4095, +1364, 1364, 0, +1364, 1364, 272, +1364, 1364, 545, +1364, 1364, 818, +1364, 1364, 1091, +1364, 1364, 1364, +1364, 1364, 1637, +1364, 1364, 1910, +1364, 1364, 2183, +1364, 1364, 2457, +1364, 1364, 2729, +1364, 1364, 3003, +1364, 1364, 3275, +1364, 1364, 3549, +1364, 1364, 3821, +1364, 1364, 4095, +1364, 1637, 0, +1364, 1637, 272, +1364, 1637, 545, +1364, 1637, 818, +1364, 1637, 1091, +1364, 1637, 1364, +1364, 1637, 1637, +1364, 1637, 1910, +1364, 1637, 2183, +1364, 1637, 2457, +1364, 1637, 2729, +1364, 1637, 3003, +1364, 1637, 3275, +1364, 1637, 3549, +1364, 1637, 3821, +1364, 1637, 4095, +1364, 1910, 0, +1364, 1910, 272, +1364, 1910, 545, +1364, 1910, 818, +1364, 1910, 1091, +1364, 1910, 1364, +1364, 1910, 1637, +1364, 1910, 1910, +1364, 1910, 2183, +1364, 1910, 2457, +1364, 1910, 2729, +1364, 1910, 3003, +1364, 1910, 3275, +1364, 1910, 3549, +1364, 1910, 3821, +1364, 1910, 4095, +1364, 2183, 0, +1364, 2183, 272, +1364, 2183, 545, +1364, 2183, 818, +1364, 2183, 1091, +1364, 2183, 1364, +1364, 2183, 1637, +1364, 2183, 1910, +1364, 2183, 2183, +1364, 2183, 2457, +1364, 2183, 2729, +1364, 2183, 3003, +1364, 2183, 3275, +1364, 2183, 3549, +1364, 2183, 3821, +1364, 2183, 4095, +1364, 2457, 0, +1364, 2457, 272, +1364, 2457, 545, +1364, 2457, 818, +1364, 2457, 1091, +1364, 2457, 1364, +1364, 2457, 1637, +1364, 2457, 1910, +1364, 2457, 2183, +1364, 2457, 2457, +1364, 2457, 2729, +1364, 2457, 3003, +1364, 2457, 3275, +1364, 2457, 3549, +1364, 2457, 3821, +1364, 2457, 4095, +1364, 2729, 0, +1364, 2729, 272, +1364, 2729, 545, +1364, 2729, 818, +1364, 2729, 1091, +1364, 2729, 1364, +1364, 2729, 1637, +1364, 2729, 1910, +1364, 2729, 2183, +1364, 2729, 2457, +1364, 2729, 2729, +1364, 2729, 3003, +1364, 2729, 3275, +1364, 2729, 3549, +1364, 2729, 3821, +1364, 2729, 4095, +1364, 3003, 0, +1364, 3003, 272, +1364, 3003, 545, +1364, 3003, 818, +1364, 3003, 1091, +1364, 3003, 1364, +1364, 3003, 1637, +1364, 3003, 1910, +1364, 3003, 2183, +1364, 3003, 2457, +1364, 3003, 2729, +1364, 3003, 3003, +1364, 3003, 3275, +1364, 3003, 3549, +1364, 3003, 3821, +1364, 3003, 4095, +1364, 3275, 0, +1364, 3275, 272, +1364, 3275, 545, +1364, 3275, 818, +1364, 3275, 1091, +1364, 3275, 1364, +1364, 3275, 1637, +1364, 3275, 1910, +1364, 3275, 2183, +1364, 3275, 2457, +1364, 3275, 2729, +1364, 3275, 3003, +1364, 3275, 3275, +1364, 3275, 3549, +1364, 3275, 3821, +1364, 3275, 4095, +1364, 3549, 0, +1364, 3549, 272, +1364, 3549, 545, +1364, 3549, 818, +1364, 3549, 1091, +1364, 3549, 1364, +1364, 3549, 1637, +1364, 3549, 1910, +1364, 3549, 2183, +1364, 3549, 2457, +1364, 3549, 2729, +1364, 3549, 3003, +1364, 3549, 3275, +1364, 3549, 3549, +1364, 3549, 3821, +1364, 3549, 4095, +1364, 3821, 0, +1364, 3821, 272, +1364, 3821, 545, +1364, 3821, 818, +1364, 3821, 1091, +1364, 3821, 1364, +1364, 3821, 1637, +1364, 3821, 1910, +1364, 3821, 2183, +1364, 3821, 2457, +1364, 3821, 2729, +1364, 3821, 3003, +1364, 3821, 3275, +1364, 3821, 3549, +1364, 3821, 3821, +1364, 3821, 4095, +1364, 4095, 0, +1364, 4095, 272, +1364, 4095, 545, +1364, 4095, 818, +1364, 4095, 1091, +1364, 4095, 1364, +1364, 4095, 1637, +1364, 4095, 1910, +1364, 4095, 2183, +1364, 4095, 2457, +1364, 4095, 2729, +1364, 4095, 3003, +1364, 4095, 3275, +1364, 4095, 3549, +1364, 4095, 3821, +1364, 4095, 4095, +1637, 0, 0, +1637, 0, 272, +1637, 0, 545, +1637, 0, 818, +1637, 0, 1091, +1637, 0, 1364, +1637, 0, 1637, +1637, 0, 1910, +1637, 0, 2183, +1637, 0, 2457, +1637, 0, 2729, +1637, 0, 3003, +1637, 0, 3275, +1637, 0, 3549, +1637, 0, 3821, +1637, 0, 4095, +1637, 272, 0, +1637, 272, 272, +1637, 272, 545, +1637, 272, 818, +1637, 272, 1091, +1637, 272, 1364, +1637, 272, 1637, +1637, 272, 1910, +1637, 272, 2183, +1637, 272, 2457, +1637, 272, 2729, +1637, 272, 3003, +1637, 272, 3275, +1637, 272, 3549, +1637, 272, 3821, +1637, 272, 4095, +1637, 545, 0, +1637, 545, 272, +1637, 545, 545, +1637, 545, 818, +1637, 545, 1091, +1637, 545, 1364, +1637, 545, 1637, +1637, 545, 1910, +1637, 545, 2183, +1637, 545, 2457, +1637, 545, 2729, +1637, 545, 3003, +1637, 545, 3275, +1637, 545, 3549, +1637, 545, 3821, +1637, 545, 4095, +1637, 818, 0, +1637, 818, 272, +1637, 818, 545, +1637, 818, 818, +1637, 818, 1091, +1637, 818, 1364, +1637, 818, 1637, +1637, 818, 1910, +1637, 818, 2183, +1637, 818, 2457, +1637, 818, 2729, +1637, 818, 3003, +1637, 818, 3275, +1637, 818, 3549, +1637, 818, 3821, +1637, 818, 4095, +1637, 1091, 0, +1637, 1091, 272, +1637, 1091, 545, +1637, 1091, 818, +1637, 1091, 1091, +1637, 1091, 1364, +1637, 1091, 1637, +1637, 1091, 1910, +1637, 1091, 2183, +1637, 1091, 2457, +1637, 1091, 2729, +1637, 1091, 3003, +1637, 1091, 3275, +1637, 1091, 3549, +1637, 1091, 3821, +1637, 1091, 4095, +1637, 1364, 0, +1637, 1364, 272, +1637, 1364, 545, +1637, 1364, 818, +1637, 1364, 1091, +1637, 1364, 1364, +1637, 1364, 1637, +1637, 1364, 1910, +1637, 1364, 2183, +1637, 1364, 2457, +1637, 1364, 2729, +1637, 1364, 3003, +1637, 1364, 3275, +1637, 1364, 3549, +1637, 1364, 3821, +1637, 1364, 4095, +1637, 1637, 0, +1637, 1637, 272, +1637, 1637, 545, +1637, 1637, 818, +1637, 1637, 1091, +1637, 1637, 1364, +1637, 1637, 1637, +1637, 1637, 1910, +1637, 1637, 2183, +1637, 1637, 2457, +1637, 1637, 2729, +1637, 1637, 3003, +1637, 1637, 3275, +1637, 1637, 3549, +1637, 1637, 3821, +1637, 1637, 4095, +1637, 1910, 0, +1637, 1910, 272, +1637, 1910, 545, +1637, 1910, 818, +1637, 1910, 1091, +1637, 1910, 1364, +1637, 1910, 1637, +1637, 1910, 1910, +1637, 1910, 2183, +1637, 1910, 2457, +1637, 1910, 2729, +1637, 1910, 3003, +1637, 1910, 3275, +1637, 1910, 3549, +1637, 1910, 3821, +1637, 1910, 4095, +1637, 2183, 0, +1637, 2183, 272, +1637, 2183, 545, +1637, 2183, 818, +1637, 2183, 1091, +1637, 2183, 1364, +1637, 2183, 1637, +1637, 2183, 1910, +1637, 2183, 2183, +1637, 2183, 2457, +1637, 2183, 2729, +1637, 2183, 3003, +1637, 2183, 3275, +1637, 2183, 3549, +1637, 2183, 3821, +1637, 2183, 4095, +1637, 2457, 0, +1637, 2457, 272, +1637, 2457, 545, +1637, 2457, 818, +1637, 2457, 1091, +1637, 2457, 1364, +1637, 2457, 1637, +1637, 2457, 1910, +1637, 2457, 2183, +1637, 2457, 2457, +1637, 2457, 2729, +1637, 2457, 3003, +1637, 2457, 3275, +1637, 2457, 3549, +1637, 2457, 3821, +1637, 2457, 4095, +1637, 2729, 0, +1637, 2729, 272, +1637, 2729, 545, +1637, 2729, 818, +1637, 2729, 1091, +1637, 2729, 1364, +1637, 2729, 1637, +1637, 2729, 1910, +1637, 2729, 2183, +1637, 2729, 2457, +1637, 2729, 2729, +1637, 2729, 3003, +1637, 2729, 3275, +1637, 2729, 3549, +1637, 2729, 3821, +1637, 2729, 4095, +1637, 3003, 0, +1637, 3003, 272, +1637, 3003, 545, +1637, 3003, 818, +1637, 3003, 1091, +1637, 3003, 1364, +1637, 3003, 1637, +1637, 3003, 1910, +1637, 3003, 2183, +1637, 3003, 2457, +1637, 3003, 2729, +1637, 3003, 3003, +1637, 3003, 3275, +1637, 3003, 3549, +1637, 3003, 3821, +1637, 3003, 4095, +1637, 3275, 0, +1637, 3275, 272, +1637, 3275, 545, +1637, 3275, 818, +1637, 3275, 1091, +1637, 3275, 1364, +1637, 3275, 1637, +1637, 3275, 1910, +1637, 3275, 2183, +1637, 3275, 2457, +1637, 3275, 2729, +1637, 3275, 3003, +1637, 3275, 3275, +1637, 3275, 3549, +1637, 3275, 3821, +1637, 3275, 4095, +1637, 3549, 0, +1637, 3549, 272, +1637, 3549, 545, +1637, 3549, 818, +1637, 3549, 1091, +1637, 3549, 1364, +1637, 3549, 1637, +1637, 3549, 1910, +1637, 3549, 2183, +1637, 3549, 2457, +1637, 3549, 2729, +1637, 3549, 3003, +1637, 3549, 3275, +1637, 3549, 3549, +1637, 3549, 3821, +1637, 3549, 4095, +1637, 3821, 0, +1637, 3821, 272, +1637, 3821, 545, +1637, 3821, 818, +1637, 3821, 1091, +1637, 3821, 1364, +1637, 3821, 1637, +1637, 3821, 1910, +1637, 3821, 2183, +1637, 3821, 2457, +1637, 3821, 2729, +1637, 3821, 3003, +1637, 3821, 3275, +1637, 3821, 3549, +1637, 3821, 3821, +1637, 3821, 4095, +1637, 4095, 0, +1637, 4095, 272, +1637, 4095, 545, +1637, 4095, 818, +1637, 4095, 1091, +1637, 4095, 1364, +1637, 4095, 1637, +1637, 4095, 1910, +1637, 4095, 2183, +1637, 4095, 2457, +1637, 4095, 2729, +1637, 4095, 3003, +1637, 4095, 3275, +1637, 4095, 3549, +1637, 4095, 3821, +1637, 4095, 4095, +1910, 0, 0, +1910, 0, 272, +1910, 0, 545, +1910, 0, 818, +1910, 0, 1091, +1910, 0, 1364, +1910, 0, 1637, +1910, 0, 1910, +1910, 0, 2183, +1910, 0, 2457, +1910, 0, 2729, +1910, 0, 3003, +1910, 0, 3275, +1910, 0, 3549, +1910, 0, 3821, +1910, 0, 4095, +1910, 272, 0, +1910, 272, 272, +1910, 272, 545, +1910, 272, 818, +1910, 272, 1091, +1910, 272, 1364, +1910, 272, 1637, +1910, 272, 1910, +1910, 272, 2183, +1910, 272, 2457, +1910, 272, 2729, +1910, 272, 3003, +1910, 272, 3275, +1910, 272, 3549, +1910, 272, 3821, +1910, 272, 4095, +1910, 545, 0, +1910, 545, 272, +1910, 545, 545, +1910, 545, 818, +1910, 545, 1091, +1910, 545, 1364, +1910, 545, 1637, +1910, 545, 1910, +1910, 545, 2183, +1910, 545, 2457, +1910, 545, 2729, +1910, 545, 3003, +1910, 545, 3275, +1910, 545, 3549, +1910, 545, 3821, +1910, 545, 4095, +1910, 818, 0, +1910, 818, 272, +1910, 818, 545, +1910, 818, 818, +1910, 818, 1091, +1910, 818, 1364, +1910, 818, 1637, +1910, 818, 1910, +1910, 818, 2183, +1910, 818, 2457, +1910, 818, 2729, +1910, 818, 3003, +1910, 818, 3275, +1910, 818, 3549, +1910, 818, 3821, +1910, 818, 4095, +1910, 1091, 0, +1910, 1091, 272, +1910, 1091, 545, +1910, 1091, 818, +1910, 1091, 1091, +1910, 1091, 1364, +1910, 1091, 1637, +1910, 1091, 1910, +1910, 1091, 2183, +1910, 1091, 2457, +1910, 1091, 2729, +1910, 1091, 3003, +1910, 1091, 3275, +1910, 1091, 3549, +1910, 1091, 3821, +1910, 1091, 4095, +1910, 1364, 0, +1910, 1364, 272, +1910, 1364, 545, +1910, 1364, 818, +1910, 1364, 1091, +1910, 1364, 1364, +1910, 1364, 1637, +1910, 1364, 1910, +1910, 1364, 2183, +1910, 1364, 2457, +1910, 1364, 2729, +1910, 1364, 3003, +1910, 1364, 3275, +1910, 1364, 3549, +1910, 1364, 3821, +1910, 1364, 4095, +1910, 1637, 0, +1910, 1637, 272, +1910, 1637, 545, +1910, 1637, 818, +1910, 1637, 1091, +1910, 1637, 1364, +1910, 1637, 1637, +1910, 1637, 1910, +1910, 1637, 2183, +1910, 1637, 2457, +1910, 1637, 2729, +1910, 1637, 3003, +1910, 1637, 3275, +1910, 1637, 3549, +1910, 1637, 3821, +1910, 1637, 4095, +1910, 1910, 0, +1910, 1910, 272, +1910, 1910, 545, +1910, 1910, 818, +1910, 1910, 1091, +1910, 1910, 1364, +1910, 1910, 1637, +1910, 1910, 1910, +1910, 1910, 2183, +1910, 1910, 2457, +1910, 1910, 2729, +1910, 1910, 3003, +1910, 1910, 3275, +1910, 1910, 3549, +1910, 1910, 3821, +1910, 1910, 4095, +1910, 2183, 0, +1910, 2183, 272, +1910, 2183, 545, +1910, 2183, 818, +1910, 2183, 1091, +1910, 2183, 1364, +1910, 2183, 1637, +1910, 2183, 1910, +1910, 2183, 2183, +1910, 2183, 2457, +1910, 2183, 2729, +1910, 2183, 3003, +1910, 2183, 3275, +1910, 2183, 3549, +1910, 2183, 3821, +1910, 2183, 4095, +1910, 2457, 0, +1910, 2457, 272, +1910, 2457, 545, +1910, 2457, 818, +1910, 2457, 1091, +1910, 2457, 1364, +1910, 2457, 1637, +1910, 2457, 1910, +1910, 2457, 2183, +1910, 2457, 2457, +1910, 2457, 2729, +1910, 2457, 3003, +1910, 2457, 3275, +1910, 2457, 3549, +1910, 2457, 3821, +1910, 2457, 4095, +1910, 2729, 0, +1910, 2729, 272, +1910, 2729, 545, +1910, 2729, 818, +1910, 2729, 1091, +1910, 2729, 1364, +1910, 2729, 1637, +1910, 2729, 1910, +1910, 2729, 2183, +1910, 2729, 2457, +1910, 2729, 2729, +1910, 2729, 3003, +1910, 2729, 3275, +1910, 2729, 3549, +1910, 2729, 3821, +1910, 2729, 4095, +1910, 3003, 0, +1910, 3003, 272, +1910, 3003, 545, +1910, 3003, 818, +1910, 3003, 1091, +1910, 3003, 1364, +1910, 3003, 1637, +1910, 3003, 1910, +1910, 3003, 2183, +1910, 3003, 2457, +1910, 3003, 2729, +1910, 3003, 3003, +1910, 3003, 3275, +1910, 3003, 3549, +1910, 3003, 3821, +1910, 3003, 4095, +1910, 3275, 0, +1910, 3275, 272, +1910, 3275, 545, +1910, 3275, 818, +1910, 3275, 1091, +1910, 3275, 1364, +1910, 3275, 1637, +1910, 3275, 1910, +1910, 3275, 2183, +1910, 3275, 2457, +1910, 3275, 2729, +1910, 3275, 3003, +1910, 3275, 3275, +1910, 3275, 3549, +1910, 3275, 3821, +1910, 3275, 4095, +1910, 3549, 0, +1910, 3549, 272, +1910, 3549, 545, +1910, 3549, 818, +1910, 3549, 1091, +1910, 3549, 1364, +1910, 3549, 1637, +1910, 3549, 1910, +1910, 3549, 2183, +1910, 3549, 2457, +1910, 3549, 2729, +1910, 3549, 3003, +1910, 3549, 3275, +1910, 3549, 3549, +1910, 3549, 3821, +1910, 3549, 4095, +1910, 3821, 0, +1910, 3821, 272, +1910, 3821, 545, +1910, 3821, 818, +1910, 3821, 1091, +1910, 3821, 1364, +1910, 3821, 1637, +1910, 3821, 1910, +1910, 3821, 2183, +1910, 3821, 2457, +1910, 3821, 2729, +1910, 3821, 3003, +1910, 3821, 3275, +1910, 3821, 3549, +1910, 3821, 3821, +1910, 3821, 4095, +1910, 4095, 0, +1910, 4095, 272, +1910, 4095, 545, +1910, 4095, 818, +1910, 4095, 1091, +1910, 4095, 1364, +1910, 4095, 1637, +1910, 4095, 1910, +1910, 4095, 2183, +1910, 4095, 2457, +1910, 4095, 2729, +1910, 4095, 3003, +1910, 4095, 3275, +1910, 4095, 3549, +1910, 4095, 3821, +1910, 4095, 4095, +2183, 0, 0, +2183, 0, 272, +2183, 0, 545, +2183, 0, 818, +2183, 0, 1091, +2183, 0, 1364, +2183, 0, 1637, +2183, 0, 1910, +2183, 0, 2183, +2183, 0, 2457, +2183, 0, 2729, +2183, 0, 3003, +2183, 0, 3275, +2183, 0, 3549, +2183, 0, 3821, +2183, 0, 4095, +2183, 272, 0, +2183, 272, 272, +2183, 272, 545, +2183, 272, 818, +2183, 272, 1091, +2183, 272, 1364, +2183, 272, 1637, +2183, 272, 1910, +2183, 272, 2183, +2183, 272, 2457, +2183, 272, 2729, +2183, 272, 3003, +2183, 272, 3275, +2183, 272, 3549, +2183, 272, 3821, +2183, 272, 4095, +2183, 545, 0, +2183, 545, 272, +2183, 545, 545, +2183, 545, 818, +2183, 545, 1091, +2183, 545, 1364, +2183, 545, 1637, +2183, 545, 1910, +2183, 545, 2183, +2183, 545, 2457, +2183, 545, 2729, +2183, 545, 3003, +2183, 545, 3275, +2183, 545, 3549, +2183, 545, 3821, +2183, 545, 4095, +2183, 818, 0, +2183, 818, 272, +2183, 818, 545, +2183, 818, 818, +2183, 818, 1091, +2183, 818, 1364, +2183, 818, 1637, +2183, 818, 1910, +2183, 818, 2183, +2183, 818, 2457, +2183, 818, 2729, +2183, 818, 3003, +2183, 818, 3275, +2183, 818, 3549, +2183, 818, 3821, +2183, 818, 4095, +2183, 1091, 0, +2183, 1091, 272, +2183, 1091, 545, +2183, 1091, 818, +2183, 1091, 1091, +2183, 1091, 1364, +2183, 1091, 1637, +2183, 1091, 1910, +2183, 1091, 2183, +2183, 1091, 2457, +2183, 1091, 2729, +2183, 1091, 3003, +2183, 1091, 3275, +2183, 1091, 3549, +2183, 1091, 3821, +2183, 1091, 4095, +2183, 1364, 0, +2183, 1364, 272, +2183, 1364, 545, +2183, 1364, 818, +2183, 1364, 1091, +2183, 1364, 1364, +2183, 1364, 1637, +2183, 1364, 1910, +2183, 1364, 2183, +2183, 1364, 2457, +2183, 1364, 2729, +2183, 1364, 3003, +2183, 1364, 3275, +2183, 1364, 3549, +2183, 1364, 3821, +2183, 1364, 4095, +2183, 1637, 0, +2183, 1637, 272, +2183, 1637, 545, +2183, 1637, 818, +2183, 1637, 1091, +2183, 1637, 1364, +2183, 1637, 1637, +2183, 1637, 1910, +2183, 1637, 2183, +2183, 1637, 2457, +2183, 1637, 2729, +2183, 1637, 3003, +2183, 1637, 3275, +2183, 1637, 3549, +2183, 1637, 3821, +2183, 1637, 4095, +2183, 1910, 0, +2183, 1910, 272, +2183, 1910, 545, +2183, 1910, 818, +2183, 1910, 1091, +2183, 1910, 1364, +2183, 1910, 1637, +2183, 1910, 1910, +2183, 1910, 2183, +2183, 1910, 2457, +2183, 1910, 2729, +2183, 1910, 3003, +2183, 1910, 3275, +2183, 1910, 3549, +2183, 1910, 3821, +2183, 1910, 4095, +2183, 2183, 0, +2183, 2183, 272, +2183, 2183, 545, +2183, 2183, 818, +2183, 2183, 1091, +2183, 2183, 1364, +2183, 2183, 1637, +2183, 2183, 1910, +2183, 2183, 2183, +2183, 2183, 2457, +2183, 2183, 2729, +2183, 2183, 3003, +2183, 2183, 3275, +2183, 2183, 3549, +2183, 2183, 3821, +2183, 2183, 4095, +2183, 2457, 0, +2183, 2457, 272, +2183, 2457, 545, +2183, 2457, 818, +2183, 2457, 1091, +2183, 2457, 1364, +2183, 2457, 1637, +2183, 2457, 1910, +2183, 2457, 2183, +2183, 2457, 2457, +2183, 2457, 2729, +2183, 2457, 3003, +2183, 2457, 3275, +2183, 2457, 3549, +2183, 2457, 3821, +2183, 2457, 4095, +2183, 2729, 0, +2183, 2729, 272, +2183, 2729, 545, +2183, 2729, 818, +2183, 2729, 1091, +2183, 2729, 1364, +2183, 2729, 1637, +2183, 2729, 1910, +2183, 2729, 2183, +2183, 2729, 2457, +2183, 2729, 2729, +2183, 2729, 3003, +2183, 2729, 3275, +2183, 2729, 3549, +2183, 2729, 3821, +2183, 2729, 4095, +2183, 3003, 0, +2183, 3003, 272, +2183, 3003, 545, +2183, 3003, 818, +2183, 3003, 1091, +2183, 3003, 1364, +2183, 3003, 1637, +2183, 3003, 1910, +2183, 3003, 2183, +2183, 3003, 2457, +2183, 3003, 2729, +2183, 3003, 3003, +2183, 3003, 3275, +2183, 3003, 3549, +2183, 3003, 3821, +2183, 3003, 4095, +2183, 3275, 0, +2183, 3275, 272, +2183, 3275, 545, +2183, 3275, 818, +2183, 3275, 1091, +2183, 3275, 1364, +2183, 3275, 1637, +2183, 3275, 1910, +2183, 3275, 2183, +2183, 3275, 2457, +2183, 3275, 2729, +2183, 3275, 3003, +2183, 3275, 3275, +2183, 3275, 3549, +2183, 3275, 3821, +2183, 3275, 4095, +2183, 3549, 0, +2183, 3549, 272, +2183, 3549, 545, +2183, 3549, 818, +2183, 3549, 1091, +2183, 3549, 1364, +2183, 3549, 1637, +2183, 3549, 1910, +2183, 3549, 2183, +2183, 3549, 2457, +2183, 3549, 2729, +2183, 3549, 3003, +2183, 3549, 3275, +2183, 3549, 3549, +2183, 3549, 3821, +2183, 3549, 4095, +2183, 3821, 0, +2183, 3821, 272, +2183, 3821, 545, +2183, 3821, 818, +2183, 3821, 1091, +2183, 3821, 1364, +2183, 3821, 1637, +2183, 3821, 1910, +2183, 3821, 2183, +2183, 3821, 2457, +2183, 3821, 2729, +2183, 3821, 3003, +2183, 3821, 3275, +2183, 3821, 3549, +2183, 3821, 3821, +2183, 3821, 4095, +2183, 4095, 0, +2183, 4095, 272, +2183, 4095, 545, +2183, 4095, 818, +2183, 4095, 1091, +2183, 4095, 1364, +2183, 4095, 1637, +2183, 4095, 1910, +2183, 4095, 2183, +2183, 4095, 2457, +2183, 4095, 2729, +2183, 4095, 3003, +2183, 4095, 3275, +2183, 4095, 3549, +2183, 4095, 3821, +2183, 4095, 4095, +2457, 0, 0, +2457, 0, 272, +2457, 0, 545, +2457, 0, 818, +2457, 0, 1091, +2457, 0, 1364, +2457, 0, 1637, +2457, 0, 1910, +2457, 0, 2183, +2457, 0, 2457, +2457, 0, 2729, +2457, 0, 3003, +2457, 0, 3275, +2457, 0, 3549, +2457, 0, 3821, +2457, 0, 4095, +2457, 272, 0, +2457, 272, 272, +2457, 272, 545, +2457, 272, 818, +2457, 272, 1091, +2457, 272, 1364, +2457, 272, 1637, +2457, 272, 1910, +2457, 272, 2183, +2457, 272, 2457, +2457, 272, 2729, +2457, 272, 3003, +2457, 272, 3275, +2457, 272, 3549, +2457, 272, 3821, +2457, 272, 4095, +2457, 545, 0, +2457, 545, 272, +2457, 545, 545, +2457, 545, 818, +2457, 545, 1091, +2457, 545, 1364, +2457, 545, 1637, +2457, 545, 1910, +2457, 545, 2183, +2457, 545, 2457, +2457, 545, 2729, +2457, 545, 3003, +2457, 545, 3275, +2457, 545, 3549, +2457, 545, 3821, +2457, 545, 4095, +2457, 818, 0, +2457, 818, 272, +2457, 818, 545, +2457, 818, 818, +2457, 818, 1091, +2457, 818, 1364, +2457, 818, 1637, +2457, 818, 1910, +2457, 818, 2183, +2457, 818, 2457, +2457, 818, 2729, +2457, 818, 3003, +2457, 818, 3275, +2457, 818, 3549, +2457, 818, 3821, +2457, 818, 4095, +2457, 1091, 0, +2457, 1091, 272, +2457, 1091, 545, +2457, 1091, 818, +2457, 1091, 1091, +2457, 1091, 1364, +2457, 1091, 1637, +2457, 1091, 1910, +2457, 1091, 2183, +2457, 1091, 2457, +2457, 1091, 2729, +2457, 1091, 3003, +2457, 1091, 3275, +2457, 1091, 3549, +2457, 1091, 3821, +2457, 1091, 4095, +2457, 1364, 0, +2457, 1364, 272, +2457, 1364, 545, +2457, 1364, 818, +2457, 1364, 1091, +2457, 1364, 1364, +2457, 1364, 1637, +2457, 1364, 1910, +2457, 1364, 2183, +2457, 1364, 2457, +2457, 1364, 2729, +2457, 1364, 3003, +2457, 1364, 3275, +2457, 1364, 3549, +2457, 1364, 3821, +2457, 1364, 4095, +2457, 1637, 0, +2457, 1637, 272, +2457, 1637, 545, +2457, 1637, 818, +2457, 1637, 1091, +2457, 1637, 1364, +2457, 1637, 1637, +2457, 1637, 1910, +2457, 1637, 2183, +2457, 1637, 2457, +2457, 1637, 2729, +2457, 1637, 3003, +2457, 1637, 3275, +2457, 1637, 3549, +2457, 1637, 3821, +2457, 1637, 4095, +2457, 1910, 0, +2457, 1910, 272, +2457, 1910, 545, +2457, 1910, 818, +2457, 1910, 1091, +2457, 1910, 1364, +2457, 1910, 1637, +2457, 1910, 1910, +2457, 1910, 2183, +2457, 1910, 2457, +2457, 1910, 2729, +2457, 1910, 3003, +2457, 1910, 3275, +2457, 1910, 3549, +2457, 1910, 3821, +2457, 1910, 4095, +2457, 2183, 0, +2457, 2183, 272, +2457, 2183, 545, +2457, 2183, 818, +2457, 2183, 1091, +2457, 2183, 1364, +2457, 2183, 1637, +2457, 2183, 1910, +2457, 2183, 2183, +2457, 2183, 2457, +2457, 2183, 2729, +2457, 2183, 3003, +2457, 2183, 3275, +2457, 2183, 3549, +2457, 2183, 3821, +2457, 2183, 4095, +2457, 2457, 0, +2457, 2457, 272, +2457, 2457, 545, +2457, 2457, 818, +2457, 2457, 1091, +2457, 2457, 1364, +2457, 2457, 1637, +2457, 2457, 1910, +2457, 2457, 2183, +2457, 2457, 2457, +2457, 2457, 2729, +2457, 2457, 3003, +2457, 2457, 3275, +2457, 2457, 3549, +2457, 2457, 3821, +2457, 2457, 4095, +2457, 2729, 0, +2457, 2729, 272, +2457, 2729, 545, +2457, 2729, 818, +2457, 2729, 1091, +2457, 2729, 1364, +2457, 2729, 1637, +2457, 2729, 1910, +2457, 2729, 2183, +2457, 2729, 2457, +2457, 2729, 2729, +2457, 2729, 3003, +2457, 2729, 3275, +2457, 2729, 3549, +2457, 2729, 3821, +2457, 2729, 4095, +2457, 3003, 0, +2457, 3003, 272, +2457, 3003, 545, +2457, 3003, 818, +2457, 3003, 1091, +2457, 3003, 1364, +2457, 3003, 1637, +2457, 3003, 1910, +2457, 3003, 2183, +2457, 3003, 2457, +2457, 3003, 2729, +2457, 3003, 3003, +2457, 3003, 3275, +2457, 3003, 3549, +2457, 3003, 3821, +2457, 3003, 4095, +2457, 3275, 0, +2457, 3275, 272, +2457, 3275, 545, +2457, 3275, 818, +2457, 3275, 1091, +2457, 3275, 1364, +2457, 3275, 1637, +2457, 3275, 1910, +2457, 3275, 2183, +2457, 3275, 2457, +2457, 3275, 2729, +2457, 3275, 3003, +2457, 3275, 3275, +2457, 3275, 3549, +2457, 3275, 3821, +2457, 3275, 4095, +2457, 3549, 0, +2457, 3549, 272, +2457, 3549, 545, +2457, 3549, 818, +2457, 3549, 1091, +2457, 3549, 1364, +2457, 3549, 1637, +2457, 3549, 1910, +2457, 3549, 2183, +2457, 3549, 2457, +2457, 3549, 2729, +2457, 3549, 3003, +2457, 3549, 3275, +2457, 3549, 3549, +2457, 3549, 3821, +2457, 3549, 4095, +2457, 3821, 0, +2457, 3821, 272, +2457, 3821, 545, +2457, 3821, 818, +2457, 3821, 1091, +2457, 3821, 1364, +2457, 3821, 1637, +2457, 3821, 1910, +2457, 3821, 2183, +2457, 3821, 2457, +2457, 3821, 2729, +2457, 3821, 3003, +2457, 3821, 3275, +2457, 3821, 3549, +2457, 3821, 3821, +2457, 3821, 4095, +2457, 4095, 0, +2457, 4095, 272, +2457, 4095, 545, +2457, 4095, 818, +2457, 4095, 1091, +2457, 4095, 1364, +2457, 4095, 1637, +2457, 4095, 1910, +2457, 4095, 2183, +2457, 4095, 2457, +2457, 4095, 2729, +2457, 4095, 3003, +2457, 4095, 3275, +2457, 4095, 3549, +2457, 4095, 3821, +2457, 4095, 4095, +2729, 0, 0, +2729, 0, 272, +2729, 0, 545, +2729, 0, 818, +2729, 0, 1091, +2729, 0, 1364, +2729, 0, 1637, +2729, 0, 1910, +2729, 0, 2183, +2729, 0, 2457, +2729, 0, 2729, +2729, 0, 3003, +2729, 0, 3275, +2729, 0, 3549, +2729, 0, 3821, +2729, 0, 4095, +2729, 272, 0, +2729, 272, 272, +2729, 272, 545, +2729, 272, 818, +2729, 272, 1091, +2729, 272, 1364, +2729, 272, 1637, +2729, 272, 1910, +2729, 272, 2183, +2729, 272, 2457, +2729, 272, 2729, +2729, 272, 3003, +2729, 272, 3275, +2729, 272, 3549, +2729, 272, 3821, +2729, 272, 4095, +2729, 545, 0, +2729, 545, 272, +2729, 545, 545, +2729, 545, 818, +2729, 545, 1091, +2729, 545, 1364, +2729, 545, 1637, +2729, 545, 1910, +2729, 545, 2183, +2729, 545, 2457, +2729, 545, 2729, +2729, 545, 3003, +2729, 545, 3275, +2729, 545, 3549, +2729, 545, 3821, +2729, 545, 4095, +2729, 818, 0, +2729, 818, 272, +2729, 818, 545, +2729, 818, 818, +2729, 818, 1091, +2729, 818, 1364, +2729, 818, 1637, +2729, 818, 1910, +2729, 818, 2183, +2729, 818, 2457, +2729, 818, 2729, +2729, 818, 3003, +2729, 818, 3275, +2729, 818, 3549, +2729, 818, 3821, +2729, 818, 4095, +2729, 1091, 0, +2729, 1091, 272, +2729, 1091, 545, +2729, 1091, 818, +2729, 1091, 1091, +2729, 1091, 1364, +2729, 1091, 1637, +2729, 1091, 1910, +2729, 1091, 2183, +2729, 1091, 2457, +2729, 1091, 2729, +2729, 1091, 3003, +2729, 1091, 3275, +2729, 1091, 3549, +2729, 1091, 3821, +2729, 1091, 4095, +2729, 1364, 0, +2729, 1364, 272, +2729, 1364, 545, +2729, 1364, 818, +2729, 1364, 1091, +2729, 1364, 1364, +2729, 1364, 1637, +2729, 1364, 1910, +2729, 1364, 2183, +2729, 1364, 2457, +2729, 1364, 2729, +2729, 1364, 3003, +2729, 1364, 3275, +2729, 1364, 3549, +2729, 1364, 3821, +2729, 1364, 4095, +2729, 1637, 0, +2729, 1637, 272, +2729, 1637, 545, +2729, 1637, 818, +2729, 1637, 1091, +2729, 1637, 1364, +2729, 1637, 1637, +2729, 1637, 1910, +2729, 1637, 2183, +2729, 1637, 2457, +2729, 1637, 2729, +2729, 1637, 3003, +2729, 1637, 3275, +2729, 1637, 3549, +2729, 1637, 3821, +2729, 1637, 4095, +2729, 1910, 0, +2729, 1910, 272, +2729, 1910, 545, +2729, 1910, 818, +2729, 1910, 1091, +2729, 1910, 1364, +2729, 1910, 1637, +2729, 1910, 1910, +2729, 1910, 2183, +2729, 1910, 2457, +2729, 1910, 2729, +2729, 1910, 3003, +2729, 1910, 3275, +2729, 1910, 3549, +2729, 1910, 3821, +2729, 1910, 4095, +2729, 2183, 0, +2729, 2183, 272, +2729, 2183, 545, +2729, 2183, 818, +2729, 2183, 1091, +2729, 2183, 1364, +2729, 2183, 1637, +2729, 2183, 1910, +2729, 2183, 2183, +2729, 2183, 2457, +2729, 2183, 2729, +2729, 2183, 3003, +2729, 2183, 3275, +2729, 2183, 3549, +2729, 2183, 3821, +2729, 2183, 4095, +2729, 2457, 0, +2729, 2457, 272, +2729, 2457, 545, +2729, 2457, 818, +2729, 2457, 1091, +2729, 2457, 1364, +2729, 2457, 1637, +2729, 2457, 1910, +2729, 2457, 2183, +2729, 2457, 2457, +2729, 2457, 2729, +2729, 2457, 3003, +2729, 2457, 3275, +2729, 2457, 3549, +2729, 2457, 3821, +2729, 2457, 4095, +2729, 2729, 0, +2729, 2729, 272, +2729, 2729, 545, +2729, 2729, 818, +2729, 2729, 1091, +2729, 2729, 1364, +2729, 2729, 1637, +2729, 2729, 1910, +2729, 2729, 2183, +2729, 2729, 2457, +2729, 2729, 2729, +2729, 2729, 3003, +2729, 2729, 3275, +2729, 2729, 3549, +2729, 2729, 3821, +2729, 2729, 4095, +2729, 3003, 0, +2729, 3003, 272, +2729, 3003, 545, +2729, 3003, 818, +2729, 3003, 1091, +2729, 3003, 1364, +2729, 3003, 1637, +2729, 3003, 1910, +2729, 3003, 2183, +2729, 3003, 2457, +2729, 3003, 2729, +2729, 3003, 3003, +2729, 3003, 3275, +2729, 3003, 3549, +2729, 3003, 3821, +2729, 3003, 4095, +2729, 3275, 0, +2729, 3275, 272, +2729, 3275, 545, +2729, 3275, 818, +2729, 3275, 1091, +2729, 3275, 1364, +2729, 3275, 1637, +2729, 3275, 1910, +2729, 3275, 2183, +2729, 3275, 2457, +2729, 3275, 2729, +2729, 3275, 3003, +2729, 3275, 3275, +2729, 3275, 3549, +2729, 3275, 3821, +2729, 3275, 4095, +2729, 3549, 0, +2729, 3549, 272, +2729, 3549, 545, +2729, 3549, 818, +2729, 3549, 1091, +2729, 3549, 1364, +2729, 3549, 1637, +2729, 3549, 1910, +2729, 3549, 2183, +2729, 3549, 2457, +2729, 3549, 2729, +2729, 3549, 3003, +2729, 3549, 3275, +2729, 3549, 3549, +2729, 3549, 3821, +2729, 3549, 4095, +2729, 3821, 0, +2729, 3821, 272, +2729, 3821, 545, +2729, 3821, 818, +2729, 3821, 1091, +2729, 3821, 1364, +2729, 3821, 1637, +2729, 3821, 1910, +2729, 3821, 2183, +2729, 3821, 2457, +2729, 3821, 2729, +2729, 3821, 3003, +2729, 3821, 3275, +2729, 3821, 3549, +2729, 3821, 3821, +2729, 3821, 4095, +2729, 4095, 0, +2729, 4095, 272, +2729, 4095, 545, +2729, 4095, 818, +2729, 4095, 1091, +2729, 4095, 1364, +2729, 4095, 1637, +2729, 4095, 1910, +2729, 4095, 2183, +2729, 4095, 2457, +2729, 4095, 2729, +2729, 4095, 3003, +2729, 4095, 3275, +2729, 4095, 3549, +2729, 4095, 3821, +2729, 4095, 4095, +3003, 0, 0, +3003, 0, 272, +3003, 0, 545, +3003, 0, 818, +3003, 0, 1091, +3003, 0, 1364, +3003, 0, 1637, +3003, 0, 1910, +3003, 0, 2183, +3003, 0, 2457, +3003, 0, 2729, +3003, 0, 3003, +3003, 0, 3275, +3003, 0, 3549, +3003, 0, 3821, +3003, 0, 4095, +3003, 272, 0, +3003, 272, 272, +3003, 272, 545, +3003, 272, 818, +3003, 272, 1091, +3003, 272, 1364, +3003, 272, 1637, +3003, 272, 1910, +3003, 272, 2183, +3003, 272, 2457, +3003, 272, 2729, +3003, 272, 3003, +3003, 272, 3275, +3003, 272, 3549, +3003, 272, 3821, +3003, 272, 4095, +3003, 545, 0, +3003, 545, 272, +3003, 545, 545, +3003, 545, 818, +3003, 545, 1091, +3003, 545, 1364, +3003, 545, 1637, +3003, 545, 1910, +3003, 545, 2183, +3003, 545, 2457, +3003, 545, 2729, +3003, 545, 3003, +3003, 545, 3275, +3003, 545, 3549, +3003, 545, 3821, +3003, 545, 4095, +3003, 818, 0, +3003, 818, 272, +3003, 818, 545, +3003, 818, 818, +3003, 818, 1091, +3003, 818, 1364, +3003, 818, 1637, +3003, 818, 1910, +3003, 818, 2183, +3003, 818, 2457, +3003, 818, 2729, +3003, 818, 3003, +3003, 818, 3275, +3003, 818, 3549, +3003, 818, 3821, +3003, 818, 4095, +3003, 1091, 0, +3003, 1091, 272, +3003, 1091, 545, +3003, 1091, 818, +3003, 1091, 1091, +3003, 1091, 1364, +3003, 1091, 1637, +3003, 1091, 1910, +3003, 1091, 2183, +3003, 1091, 2457, +3003, 1091, 2729, +3003, 1091, 3003, +3003, 1091, 3275, +3003, 1091, 3549, +3003, 1091, 3821, +3003, 1091, 4095, +3003, 1364, 0, +3003, 1364, 272, +3003, 1364, 545, +3003, 1364, 818, +3003, 1364, 1091, +3003, 1364, 1364, +3003, 1364, 1637, +3003, 1364, 1910, +3003, 1364, 2183, +3003, 1364, 2457, +3003, 1364, 2729, +3003, 1364, 3003, +3003, 1364, 3275, +3003, 1364, 3549, +3003, 1364, 3821, +3003, 1364, 4095, +3003, 1637, 0, +3003, 1637, 272, +3003, 1637, 545, +3003, 1637, 818, +3003, 1637, 1091, +3003, 1637, 1364, +3003, 1637, 1637, +3003, 1637, 1910, +3003, 1637, 2183, +3003, 1637, 2457, +3003, 1637, 2729, +3003, 1637, 3003, +3003, 1637, 3275, +3003, 1637, 3549, +3003, 1637, 3821, +3003, 1637, 4095, +3003, 1910, 0, +3003, 1910, 272, +3003, 1910, 545, +3003, 1910, 818, +3003, 1910, 1091, +3003, 1910, 1364, +3003, 1910, 1637, +3003, 1910, 1910, +3003, 1910, 2183, +3003, 1910, 2457, +3003, 1910, 2729, +3003, 1910, 3003, +3003, 1910, 3275, +3003, 1910, 3549, +3003, 1910, 3821, +3003, 1910, 4095, +3003, 2183, 0, +3003, 2183, 272, +3003, 2183, 545, +3003, 2183, 818, +3003, 2183, 1091, +3003, 2183, 1364, +3003, 2183, 1637, +3003, 2183, 1910, +3003, 2183, 2183, +3003, 2183, 2457, +3003, 2183, 2729, +3003, 2183, 3003, +3003, 2183, 3275, +3003, 2183, 3549, +3003, 2183, 3821, +3003, 2183, 4095, +3003, 2457, 0, +3003, 2457, 272, +3003, 2457, 545, +3003, 2457, 818, +3003, 2457, 1091, +3003, 2457, 1364, +3003, 2457, 1637, +3003, 2457, 1910, +3003, 2457, 2183, +3003, 2457, 2457, +3003, 2457, 2729, +3003, 2457, 3003, +3003, 2457, 3275, +3003, 2457, 3549, +3003, 2457, 3821, +3003, 2457, 4095, +3003, 2729, 0, +3003, 2729, 272, +3003, 2729, 545, +3003, 2729, 818, +3003, 2729, 1091, +3003, 2729, 1364, +3003, 2729, 1637, +3003, 2729, 1910, +3003, 2729, 2183, +3003, 2729, 2457, +3003, 2729, 2729, +3003, 2729, 3003, +3003, 2729, 3275, +3003, 2729, 3549, +3003, 2729, 3821, +3003, 2729, 4095, +3003, 3003, 0, +3003, 3003, 272, +3003, 3003, 545, +3003, 3003, 818, +3003, 3003, 1091, +3003, 3003, 1364, +3003, 3003, 1637, +3003, 3003, 1910, +3003, 3003, 2183, +3003, 3003, 2457, +3003, 3003, 2729, +3003, 3003, 3003, +3003, 3003, 3275, +3003, 3003, 3549, +3003, 3003, 3821, +3003, 3003, 4095, +3003, 3275, 0, +3003, 3275, 272, +3003, 3275, 545, +3003, 3275, 818, +3003, 3275, 1091, +3003, 3275, 1364, +3003, 3275, 1637, +3003, 3275, 1910, +3003, 3275, 2183, +3003, 3275, 2457, +3003, 3275, 2729, +3003, 3275, 3003, +3003, 3275, 3275, +3003, 3275, 3549, +3003, 3275, 3821, +3003, 3275, 4095, +3003, 3549, 0, +3003, 3549, 272, +3003, 3549, 545, +3003, 3549, 818, +3003, 3549, 1091, +3003, 3549, 1364, +3003, 3549, 1637, +3003, 3549, 1910, +3003, 3549, 2183, +3003, 3549, 2457, +3003, 3549, 2729, +3003, 3549, 3003, +3003, 3549, 3275, +3003, 3549, 3549, +3003, 3549, 3821, +3003, 3549, 4095, +3003, 3821, 0, +3003, 3821, 272, +3003, 3821, 545, +3003, 3821, 818, +3003, 3821, 1091, +3003, 3821, 1364, +3003, 3821, 1637, +3003, 3821, 1910, +3003, 3821, 2183, +3003, 3821, 2457, +3003, 3821, 2729, +3003, 3821, 3003, +3003, 3821, 3275, +3003, 3821, 3549, +3003, 3821, 3821, +3003, 3821, 4095, +3003, 4095, 0, +3003, 4095, 272, +3003, 4095, 545, +3003, 4095, 818, +3003, 4095, 1091, +3003, 4095, 1364, +3003, 4095, 1637, +3003, 4095, 1910, +3003, 4095, 2183, +3003, 4095, 2457, +3003, 4095, 2729, +3003, 4095, 3003, +3003, 4095, 3275, +3003, 4095, 3549, +3003, 4095, 3821, +3003, 4095, 4095, +3275, 0, 0, +3275, 0, 272, +3275, 0, 545, +3275, 0, 818, +3275, 0, 1091, +3275, 0, 1364, +3275, 0, 1637, +3275, 0, 1910, +3275, 0, 2183, +3275, 0, 2457, +3275, 0, 2729, +3275, 0, 3003, +3275, 0, 3275, +3275, 0, 3549, +3275, 0, 3821, +3275, 0, 4095, +3275, 272, 0, +3275, 272, 272, +3275, 272, 545, +3275, 272, 818, +3275, 272, 1091, +3275, 272, 1364, +3275, 272, 1637, +3275, 272, 1910, +3275, 272, 2183, +3275, 272, 2457, +3275, 272, 2729, +3275, 272, 3003, +3275, 272, 3275, +3275, 272, 3549, +3275, 272, 3821, +3275, 272, 4095, +3275, 545, 0, +3275, 545, 272, +3275, 545, 545, +3275, 545, 818, +3275, 545, 1091, +3275, 545, 1364, +3275, 545, 1637, +3275, 545, 1910, +3275, 545, 2183, +3275, 545, 2457, +3275, 545, 2729, +3275, 545, 3003, +3275, 545, 3275, +3275, 545, 3549, +3275, 545, 3821, +3275, 545, 4095, +3275, 818, 0, +3275, 818, 272, +3275, 818, 545, +3275, 818, 818, +3275, 818, 1091, +3275, 818, 1364, +3275, 818, 1637, +3275, 818, 1910, +3275, 818, 2183, +3275, 818, 2457, +3275, 818, 2729, +3275, 818, 3003, +3275, 818, 3275, +3275, 818, 3549, +3275, 818, 3821, +3275, 818, 4095, +3275, 1091, 0, +3275, 1091, 272, +3275, 1091, 545, +3275, 1091, 818, +3275, 1091, 1091, +3275, 1091, 1364, +3275, 1091, 1637, +3275, 1091, 1910, +3275, 1091, 2183, +3275, 1091, 2457, +3275, 1091, 2729, +3275, 1091, 3003, +3275, 1091, 3275, +3275, 1091, 3549, +3275, 1091, 3821, +3275, 1091, 4095, +3275, 1364, 0, +3275, 1364, 272, +3275, 1364, 545, +3275, 1364, 818, +3275, 1364, 1091, +3275, 1364, 1364, +3275, 1364, 1637, +3275, 1364, 1910, +3275, 1364, 2183, +3275, 1364, 2457, +3275, 1364, 2729, +3275, 1364, 3003, +3275, 1364, 3275, +3275, 1364, 3549, +3275, 1364, 3821, +3275, 1364, 4095, +3275, 1637, 0, +3275, 1637, 272, +3275, 1637, 545, +3275, 1637, 818, +3275, 1637, 1091, +3275, 1637, 1364, +3275, 1637, 1637, +3275, 1637, 1910, +3275, 1637, 2183, +3275, 1637, 2457, +3275, 1637, 2729, +3275, 1637, 3003, +3275, 1637, 3275, +3275, 1637, 3549, +3275, 1637, 3821, +3275, 1637, 4095, +3275, 1910, 0, +3275, 1910, 272, +3275, 1910, 545, +3275, 1910, 818, +3275, 1910, 1091, +3275, 1910, 1364, +3275, 1910, 1637, +3275, 1910, 1910, +3275, 1910, 2183, +3275, 1910, 2457, +3275, 1910, 2729, +3275, 1910, 3003, +3275, 1910, 3275, +3275, 1910, 3549, +3275, 1910, 3821, +3275, 1910, 4095, +3275, 2183, 0, +3275, 2183, 272, +3275, 2183, 545, +3275, 2183, 818, +3275, 2183, 1091, +3275, 2183, 1364, +3275, 2183, 1637, +3275, 2183, 1910, +3275, 2183, 2183, +3275, 2183, 2457, +3275, 2183, 2729, +3275, 2183, 3003, +3275, 2183, 3275, +3275, 2183, 3549, +3275, 2183, 3821, +3275, 2183, 4095, +3275, 2457, 0, +3275, 2457, 272, +3275, 2457, 545, +3275, 2457, 818, +3275, 2457, 1091, +3275, 2457, 1364, +3275, 2457, 1637, +3275, 2457, 1910, +3275, 2457, 2183, +3275, 2457, 2457, +3275, 2457, 2729, +3275, 2457, 3003, +3275, 2457, 3275, +3275, 2457, 3549, +3275, 2457, 3821, +3275, 2457, 4095, +3275, 2729, 0, +3275, 2729, 272, +3275, 2729, 545, +3275, 2729, 818, +3275, 2729, 1091, +3275, 2729, 1364, +3275, 2729, 1637, +3275, 2729, 1910, +3275, 2729, 2183, +3275, 2729, 2457, +3275, 2729, 2729, +3275, 2729, 3003, +3275, 2729, 3275, +3275, 2729, 3549, +3275, 2729, 3821, +3275, 2729, 4095, +3275, 3003, 0, +3275, 3003, 272, +3275, 3003, 545, +3275, 3003, 818, +3275, 3003, 1091, +3275, 3003, 1364, +3275, 3003, 1637, +3275, 3003, 1910, +3275, 3003, 2183, +3275, 3003, 2457, +3275, 3003, 2729, +3275, 3003, 3003, +3275, 3003, 3275, +3275, 3003, 3549, +3275, 3003, 3821, +3275, 3003, 4095, +3275, 3275, 0, +3275, 3275, 272, +3275, 3275, 545, +3275, 3275, 818, +3275, 3275, 1091, +3275, 3275, 1364, +3275, 3275, 1637, +3275, 3275, 1910, +3275, 3275, 2183, +3275, 3275, 2457, +3275, 3275, 2729, +3275, 3275, 3003, +3275, 3275, 3275, +3275, 3275, 3549, +3275, 3275, 3821, +3275, 3275, 4095, +3275, 3549, 0, +3275, 3549, 272, +3275, 3549, 545, +3275, 3549, 818, +3275, 3549, 1091, +3275, 3549, 1364, +3275, 3549, 1637, +3275, 3549, 1910, +3275, 3549, 2183, +3275, 3549, 2457, +3275, 3549, 2729, +3275, 3549, 3003, +3275, 3549, 3275, +3275, 3549, 3549, +3275, 3549, 3821, +3275, 3549, 4095, +3275, 3821, 0, +3275, 3821, 272, +3275, 3821, 545, +3275, 3821, 818, +3275, 3821, 1091, +3275, 3821, 1364, +3275, 3821, 1637, +3275, 3821, 1910, +3275, 3821, 2183, +3275, 3821, 2457, +3275, 3821, 2729, +3275, 3821, 3003, +3275, 3821, 3275, +3275, 3821, 3549, +3275, 3821, 3821, +3275, 3821, 4095, +3275, 4095, 0, +3275, 4095, 272, +3275, 4095, 545, +3275, 4095, 818, +3275, 4095, 1091, +3275, 4095, 1364, +3275, 4095, 1637, +3275, 4095, 1910, +3275, 4095, 2183, +3275, 4095, 2457, +3275, 4095, 2729, +3275, 4095, 3003, +3275, 4095, 3275, +3275, 4095, 3549, +3275, 4095, 3821, +3275, 4095, 4095, +3549, 0, 0, +3549, 0, 272, +3549, 0, 545, +3549, 0, 818, +3549, 0, 1091, +3549, 0, 1364, +3549, 0, 1637, +3549, 0, 1910, +3549, 0, 2183, +3549, 0, 2457, +3549, 0, 2729, +3549, 0, 3003, +3549, 0, 3275, +3549, 0, 3549, +3549, 0, 3821, +3549, 0, 4095, +3549, 272, 0, +3549, 272, 272, +3549, 272, 545, +3549, 272, 818, +3549, 272, 1091, +3549, 272, 1364, +3549, 272, 1637, +3549, 272, 1910, +3549, 272, 2183, +3549, 272, 2457, +3549, 272, 2729, +3549, 272, 3003, +3549, 272, 3275, +3549, 272, 3549, +3549, 272, 3821, +3549, 272, 4095, +3549, 545, 0, +3549, 545, 272, +3549, 545, 545, +3549, 545, 818, +3549, 545, 1091, +3549, 545, 1364, +3549, 545, 1637, +3549, 545, 1910, +3549, 545, 2183, +3549, 545, 2457, +3549, 545, 2729, +3549, 545, 3003, +3549, 545, 3275, +3549, 545, 3549, +3549, 545, 3821, +3549, 545, 4095, +3549, 818, 0, +3549, 818, 272, +3549, 818, 545, +3549, 818, 818, +3549, 818, 1091, +3549, 818, 1364, +3549, 818, 1637, +3549, 818, 1910, +3549, 818, 2183, +3549, 818, 2457, +3549, 818, 2729, +3549, 818, 3003, +3549, 818, 3275, +3549, 818, 3549, +3549, 818, 3821, +3549, 818, 4095, +3549, 1091, 0, +3549, 1091, 272, +3549, 1091, 545, +3549, 1091, 818, +3549, 1091, 1091, +3549, 1091, 1364, +3549, 1091, 1637, +3549, 1091, 1910, +3549, 1091, 2183, +3549, 1091, 2457, +3549, 1091, 2729, +3549, 1091, 3003, +3549, 1091, 3275, +3549, 1091, 3549, +3549, 1091, 3821, +3549, 1091, 4095, +3549, 1364, 0, +3549, 1364, 272, +3549, 1364, 545, +3549, 1364, 818, +3549, 1364, 1091, +3549, 1364, 1364, +3549, 1364, 1637, +3549, 1364, 1910, +3549, 1364, 2183, +3549, 1364, 2457, +3549, 1364, 2729, +3549, 1364, 3003, +3549, 1364, 3275, +3549, 1364, 3549, +3549, 1364, 3821, +3549, 1364, 4095, +3549, 1637, 0, +3549, 1637, 272, +3549, 1637, 545, +3549, 1637, 818, +3549, 1637, 1091, +3549, 1637, 1364, +3549, 1637, 1637, +3549, 1637, 1910, +3549, 1637, 2183, +3549, 1637, 2457, +3549, 1637, 2729, +3549, 1637, 3003, +3549, 1637, 3275, +3549, 1637, 3549, +3549, 1637, 3821, +3549, 1637, 4095, +3549, 1910, 0, +3549, 1910, 272, +3549, 1910, 545, +3549, 1910, 818, +3549, 1910, 1091, +3549, 1910, 1364, +3549, 1910, 1637, +3549, 1910, 1910, +3549, 1910, 2183, +3549, 1910, 2457, +3549, 1910, 2729, +3549, 1910, 3003, +3549, 1910, 3275, +3549, 1910, 3549, +3549, 1910, 3821, +3549, 1910, 4095, +3549, 2183, 0, +3549, 2183, 272, +3549, 2183, 545, +3549, 2183, 818, +3549, 2183, 1091, +3549, 2183, 1364, +3549, 2183, 1637, +3549, 2183, 1910, +3549, 2183, 2183, +3549, 2183, 2457, +3549, 2183, 2729, +3549, 2183, 3003, +3549, 2183, 3275, +3549, 2183, 3549, +3549, 2183, 3821, +3549, 2183, 4095, +3549, 2457, 0, +3549, 2457, 272, +3549, 2457, 545, +3549, 2457, 818, +3549, 2457, 1091, +3549, 2457, 1364, +3549, 2457, 1637, +3549, 2457, 1910, +3549, 2457, 2183, +3549, 2457, 2457, +3549, 2457, 2729, +3549, 2457, 3003, +3549, 2457, 3275, +3549, 2457, 3549, +3549, 2457, 3821, +3549, 2457, 4095, +3549, 2729, 0, +3549, 2729, 272, +3549, 2729, 545, +3549, 2729, 818, +3549, 2729, 1091, +3549, 2729, 1364, +3549, 2729, 1637, +3549, 2729, 1910, +3549, 2729, 2183, +3549, 2729, 2457, +3549, 2729, 2729, +3549, 2729, 3003, +3549, 2729, 3275, +3549, 2729, 3549, +3549, 2729, 3821, +3549, 2729, 4095, +3549, 3003, 0, +3549, 3003, 272, +3549, 3003, 545, +3549, 3003, 818, +3549, 3003, 1091, +3549, 3003, 1364, +3549, 3003, 1637, +3549, 3003, 1910, +3549, 3003, 2183, +3549, 3003, 2457, +3549, 3003, 2729, +3549, 3003, 3003, +3549, 3003, 3275, +3549, 3003, 3549, +3549, 3003, 3821, +3549, 3003, 4095, +3549, 3275, 0, +3549, 3275, 272, +3549, 3275, 545, +3549, 3275, 818, +3549, 3275, 1091, +3549, 3275, 1364, +3549, 3275, 1637, +3549, 3275, 1910, +3549, 3275, 2183, +3549, 3275, 2457, +3549, 3275, 2729, +3549, 3275, 3003, +3549, 3275, 3275, +3549, 3275, 3549, +3549, 3275, 3821, +3549, 3275, 4095, +3549, 3549, 0, +3549, 3549, 272, +3549, 3549, 545, +3549, 3549, 818, +3549, 3549, 1091, +3549, 3549, 1364, +3549, 3549, 1637, +3549, 3549, 1910, +3549, 3549, 2183, +3549, 3549, 2457, +3549, 3549, 2729, +3549, 3549, 3003, +3549, 3549, 3275, +3549, 3549, 3549, +3549, 3549, 3821, +3549, 3549, 4095, +3549, 3821, 0, +3549, 3821, 272, +3549, 3821, 545, +3549, 3821, 818, +3549, 3821, 1091, +3549, 3821, 1364, +3549, 3821, 1637, +3549, 3821, 1910, +3549, 3821, 2183, +3549, 3821, 2457, +3549, 3821, 2729, +3549, 3821, 3003, +3549, 3821, 3275, +3549, 3821, 3549, +3549, 3821, 3821, +3549, 3821, 4095, +3549, 4095, 0, +3549, 4095, 272, +3549, 4095, 545, +3549, 4095, 818, +3549, 4095, 1091, +3549, 4095, 1364, +3549, 4095, 1637, +3549, 4095, 1910, +3549, 4095, 2183, +3549, 4095, 2457, +3549, 4095, 2729, +3549, 4095, 3003, +3549, 4095, 3275, +3549, 4095, 3549, +3549, 4095, 3821, +3549, 4095, 4095, +3821, 0, 0, +3821, 0, 272, +3821, 0, 545, +3821, 0, 818, +3821, 0, 1091, +3821, 0, 1364, +3821, 0, 1637, +3821, 0, 1910, +3821, 0, 2183, +3821, 0, 2457, +3821, 0, 2729, +3821, 0, 3003, +3821, 0, 3275, +3821, 0, 3549, +3821, 0, 3821, +3821, 0, 4095, +3821, 272, 0, +3821, 272, 272, +3821, 272, 545, +3821, 272, 818, +3821, 272, 1091, +3821, 272, 1364, +3821, 272, 1637, +3821, 272, 1910, +3821, 272, 2183, +3821, 272, 2457, +3821, 272, 2729, +3821, 272, 3003, +3821, 272, 3275, +3821, 272, 3549, +3821, 272, 3821, +3821, 272, 4095, +3821, 545, 0, +3821, 545, 272, +3821, 545, 545, +3821, 545, 818, +3821, 545, 1091, +3821, 545, 1364, +3821, 545, 1637, +3821, 545, 1910, +3821, 545, 2183, +3821, 545, 2457, +3821, 545, 2729, +3821, 545, 3003, +3821, 545, 3275, +3821, 545, 3549, +3821, 545, 3821, +3821, 545, 4095, +3821, 818, 0, +3821, 818, 272, +3821, 818, 545, +3821, 818, 818, +3821, 818, 1091, +3821, 818, 1364, +3821, 818, 1637, +3821, 818, 1910, +3821, 818, 2183, +3821, 818, 2457, +3821, 818, 2729, +3821, 818, 3003, +3821, 818, 3275, +3821, 818, 3549, +3821, 818, 3821, +3821, 818, 4095, +3821, 1091, 0, +3821, 1091, 272, +3821, 1091, 545, +3821, 1091, 818, +3821, 1091, 1091, +3821, 1091, 1364, +3821, 1091, 1637, +3821, 1091, 1910, +3821, 1091, 2183, +3821, 1091, 2457, +3821, 1091, 2729, +3821, 1091, 3003, +3821, 1091, 3275, +3821, 1091, 3549, +3821, 1091, 3821, +3821, 1091, 4095, +3821, 1364, 0, +3821, 1364, 272, +3821, 1364, 545, +3821, 1364, 818, +3821, 1364, 1091, +3821, 1364, 1364, +3821, 1364, 1637, +3821, 1364, 1910, +3821, 1364, 2183, +3821, 1364, 2457, +3821, 1364, 2729, +3821, 1364, 3003, +3821, 1364, 3275, +3821, 1364, 3549, +3821, 1364, 3821, +3821, 1364, 4095, +3821, 1637, 0, +3821, 1637, 272, +3821, 1637, 545, +3821, 1637, 818, +3821, 1637, 1091, +3821, 1637, 1364, +3821, 1637, 1637, +3821, 1637, 1910, +3821, 1637, 2183, +3821, 1637, 2457, +3821, 1637, 2729, +3821, 1637, 3003, +3821, 1637, 3275, +3821, 1637, 3549, +3821, 1637, 3821, +3821, 1637, 4095, +3821, 1910, 0, +3821, 1910, 272, +3821, 1910, 545, +3821, 1910, 818, +3821, 1910, 1091, +3821, 1910, 1364, +3821, 1910, 1637, +3821, 1910, 1910, +3821, 1910, 2183, +3821, 1910, 2457, +3821, 1910, 2729, +3821, 1910, 3003, +3821, 1910, 3275, +3821, 1910, 3549, +3821, 1910, 3821, +3821, 1910, 4095, +3821, 2183, 0, +3821, 2183, 272, +3821, 2183, 545, +3821, 2183, 818, +3821, 2183, 1091, +3821, 2183, 1364, +3821, 2183, 1637, +3821, 2183, 1910, +3821, 2183, 2183, +3821, 2183, 2457, +3821, 2183, 2729, +3821, 2183, 3003, +3821, 2183, 3275, +3821, 2183, 3549, +3821, 2183, 3821, +3821, 2183, 4095, +3821, 2457, 0, +3821, 2457, 272, +3821, 2457, 545, +3821, 2457, 818, +3821, 2457, 1091, +3821, 2457, 1364, +3821, 2457, 1637, +3821, 2457, 1910, +3821, 2457, 2183, +3821, 2457, 2457, +3821, 2457, 2729, +3821, 2457, 3003, +3821, 2457, 3275, +3821, 2457, 3549, +3821, 2457, 3821, +3821, 2457, 4095, +3821, 2729, 0, +3821, 2729, 272, +3821, 2729, 545, +3821, 2729, 818, +3821, 2729, 1091, +3821, 2729, 1364, +3821, 2729, 1637, +3821, 2729, 1910, +3821, 2729, 2183, +3821, 2729, 2457, +3821, 2729, 2729, +3821, 2729, 3003, +3821, 2729, 3275, +3821, 2729, 3549, +3821, 2729, 3821, +3821, 2729, 4095, +3821, 3003, 0, +3821, 3003, 272, +3821, 3003, 545, +3821, 3003, 818, +3821, 3003, 1091, +3821, 3003, 1364, +3821, 3003, 1637, +3821, 3003, 1910, +3821, 3003, 2183, +3821, 3003, 2457, +3821, 3003, 2729, +3821, 3003, 3003, +3821, 3003, 3275, +3821, 3003, 3549, +3821, 3003, 3821, +3821, 3003, 4095, +3821, 3275, 0, +3821, 3275, 272, +3821, 3275, 545, +3821, 3275, 818, +3821, 3275, 1091, +3821, 3275, 1364, +3821, 3275, 1637, +3821, 3275, 1910, +3821, 3275, 2183, +3821, 3275, 2457, +3821, 3275, 2729, +3821, 3275, 3003, +3821, 3275, 3275, +3821, 3275, 3549, +3821, 3275, 3821, +3821, 3275, 4095, +3821, 3549, 0, +3821, 3549, 272, +3821, 3549, 545, +3821, 3549, 818, +3821, 3549, 1091, +3821, 3549, 1364, +3821, 3549, 1637, +3821, 3549, 1910, +3821, 3549, 2183, +3821, 3549, 2457, +3821, 3549, 2729, +3821, 3549, 3003, +3821, 3549, 3275, +3821, 3549, 3549, +3821, 3549, 3821, +3821, 3549, 4095, +3821, 3821, 0, +3821, 3821, 272, +3821, 3821, 545, +3821, 3821, 818, +3821, 3821, 1091, +3821, 3821, 1364, +3821, 3821, 1637, +3821, 3821, 1910, +3821, 3821, 2183, +3821, 3821, 2457, +3821, 3821, 2729, +3821, 3821, 3003, +3821, 3821, 3275, +3821, 3821, 3549, +3821, 3821, 3821, +3821, 3821, 4095, +3821, 4095, 0, +3821, 4095, 272, +3821, 4095, 545, +3821, 4095, 818, +3821, 4095, 1091, +3821, 4095, 1364, +3821, 4095, 1637, +3821, 4095, 1910, +3821, 4095, 2183, +3821, 4095, 2457, +3821, 4095, 2729, +3821, 4095, 3003, +3821, 4095, 3275, +3821, 4095, 3549, +3821, 4095, 3821, +3821, 4095, 4095, +4095, 0, 0, +4095, 0, 272, +4095, 0, 545, +4095, 0, 818, +4095, 0, 1091, +4095, 0, 1364, +4095, 0, 1637, +4095, 0, 1910, +4095, 0, 2183, +4095, 0, 2457, +4095, 0, 2729, +4095, 0, 3003, +4095, 0, 3275, +4095, 0, 3549, +4095, 0, 3821, +4095, 0, 4095, +4095, 272, 0, +4095, 272, 272, +4095, 272, 545, +4095, 272, 818, +4095, 272, 1091, +4095, 272, 1364, +4095, 272, 1637, +4095, 272, 1910, +4095, 272, 2183, +4095, 272, 2457, +4095, 272, 2729, +4095, 272, 3003, +4095, 272, 3275, +4095, 272, 3549, +4095, 272, 3821, +4095, 272, 4095, +4095, 545, 0, +4095, 545, 272, +4095, 545, 545, +4095, 545, 818, +4095, 545, 1091, +4095, 545, 1364, +4095, 545, 1637, +4095, 545, 1910, +4095, 545, 2183, +4095, 545, 2457, +4095, 545, 2729, +4095, 545, 3003, +4095, 545, 3275, +4095, 545, 3549, +4095, 545, 3821, +4095, 545, 4095, +4095, 818, 0, +4095, 818, 272, +4095, 818, 545, +4095, 818, 818, +4095, 818, 1091, +4095, 818, 1364, +4095, 818, 1637, +4095, 818, 1910, +4095, 818, 2183, +4095, 818, 2457, +4095, 818, 2729, +4095, 818, 3003, +4095, 818, 3275, +4095, 818, 3549, +4095, 818, 3821, +4095, 818, 4095, +4095, 1091, 0, +4095, 1091, 272, +4095, 1091, 545, +4095, 1091, 818, +4095, 1091, 1091, +4095, 1091, 1364, +4095, 1091, 1637, +4095, 1091, 1910, +4095, 1091, 2183, +4095, 1091, 2457, +4095, 1091, 2729, +4095, 1091, 3003, +4095, 1091, 3275, +4095, 1091, 3549, +4095, 1091, 3821, +4095, 1091, 4095, +4095, 1364, 0, +4095, 1364, 272, +4095, 1364, 545, +4095, 1364, 818, +4095, 1364, 1091, +4095, 1364, 1364, +4095, 1364, 1637, +4095, 1364, 1910, +4095, 1364, 2183, +4095, 1364, 2457, +4095, 1364, 2729, +4095, 1364, 3003, +4095, 1364, 3275, +4095, 1364, 3549, +4095, 1364, 3821, +4095, 1364, 4095, +4095, 1637, 0, +4095, 1637, 272, +4095, 1637, 545, +4095, 1637, 818, +4095, 1637, 1091, +4095, 1637, 1364, +4095, 1637, 1637, +4095, 1637, 1910, +4095, 1637, 2183, +4095, 1637, 2457, +4095, 1637, 2729, +4095, 1637, 3003, +4095, 1637, 3275, +4095, 1637, 3549, +4095, 1637, 3821, +4095, 1637, 4095, +4095, 1910, 0, +4095, 1910, 272, +4095, 1910, 545, +4095, 1910, 818, +4095, 1910, 1091, +4095, 1910, 1364, +4095, 1910, 1637, +4095, 1910, 1910, +4095, 1910, 2183, +4095, 1910, 2457, +4095, 1910, 2729, +4095, 1910, 3003, +4095, 1910, 3275, +4095, 1910, 3549, +4095, 1910, 3821, +4095, 1910, 4095, +4095, 2183, 0, +4095, 2183, 272, +4095, 2183, 545, +4095, 2183, 818, +4095, 2183, 1091, +4095, 2183, 1364, +4095, 2183, 1637, +4095, 2183, 1910, +4095, 2183, 2183, +4095, 2183, 2457, +4095, 2183, 2729, +4095, 2183, 3003, +4095, 2183, 3275, +4095, 2183, 3549, +4095, 2183, 3821, +4095, 2183, 4095, +4095, 2457, 0, +4095, 2457, 272, +4095, 2457, 545, +4095, 2457, 818, +4095, 2457, 1091, +4095, 2457, 1364, +4095, 2457, 1637, +4095, 2457, 1910, +4095, 2457, 2183, +4095, 2457, 2457, +4095, 2457, 2729, +4095, 2457, 3003, +4095, 2457, 3275, +4095, 2457, 3549, +4095, 2457, 3821, +4095, 2457, 4095, +4095, 2729, 0, +4095, 2729, 272, +4095, 2729, 545, +4095, 2729, 818, +4095, 2729, 1091, +4095, 2729, 1364, +4095, 2729, 1637, +4095, 2729, 1910, +4095, 2729, 2183, +4095, 2729, 2457, +4095, 2729, 2729, +4095, 2729, 3003, +4095, 2729, 3275, +4095, 2729, 3549, +4095, 2729, 3821, +4095, 2729, 4095, +4095, 3003, 0, +4095, 3003, 272, +4095, 3003, 545, +4095, 3003, 818, +4095, 3003, 1091, +4095, 3003, 1364, +4095, 3003, 1637, +4095, 3003, 1910, +4095, 3003, 2183, +4095, 3003, 2457, +4095, 3003, 2729, +4095, 3003, 3003, +4095, 3003, 3275, +4095, 3003, 3549, +4095, 3003, 3821, +4095, 3003, 4095, +4095, 3275, 0, +4095, 3275, 272, +4095, 3275, 545, +4095, 3275, 818, +4095, 3275, 1091, +4095, 3275, 1364, +4095, 3275, 1637, +4095, 3275, 1910, +4095, 3275, 2183, +4095, 3275, 2457, +4095, 3275, 2729, +4095, 3275, 3003, +4095, 3275, 3275, +4095, 3275, 3549, +4095, 3275, 3821, +4095, 3275, 4095, +4095, 3549, 0, +4095, 3549, 272, +4095, 3549, 545, +4095, 3549, 818, +4095, 3549, 1091, +4095, 3549, 1364, +4095, 3549, 1637, +4095, 3549, 1910, +4095, 3549, 2183, +4095, 3549, 2457, +4095, 3549, 2729, +4095, 3549, 3003, +4095, 3549, 3275, +4095, 3549, 3549, +4095, 3549, 3821, +4095, 3549, 4095, +4095, 3821, 0, +4095, 3821, 272, +4095, 3821, 545, +4095, 3821, 818, +4095, 3821, 1091, +4095, 3821, 1364, +4095, 3821, 1637, +4095, 3821, 1910, +4095, 3821, 2183, +4095, 3821, 2457, +4095, 3821, 2729, +4095, 3821, 3003, +4095, 3821, 3275, +4095, 3821, 3549, +4095, 3821, 3821, +4095, 3821, 4095, +4095, 4095, 0, +4095, 4095, 272, +4095, 4095, 545, +4095, 4095, 818, +4095, 4095, 1091, +4095, 4095, 1364, +4095, 4095, 1637, +4095, 4095, 1910, +4095, 4095, 2183, +4095, 4095, 2457, +4095, 4095, 2729, +4095, 4095, 3003, +4095, 4095, 3275, +4095, 4095, 3549, +4095, 4095, 3821, +4095, 4095, 4095] + } +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass index eec3634045..2347aeb811 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass @@ -105,6 +105,11 @@ } ] }, + { + "Name": "LutGenerationPass", + "TemplateName": "LutGenerationTemplate", + "Enabled": true + }, { "Name": "LookModificationTransformPass", "TemplateName": "LookModificationTransformTemplate", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LutGeneration.pass b/Gems/Atom/Feature/Common/Assets/Passes/LutGeneration.pass new file mode 100644 index 0000000000..e95a9872a5 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/LutGeneration.pass @@ -0,0 +1,199 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "LutGenerationTemplate", + "PassClass": "LutGenerationPass", + "Slots": [ + { + "Name": "LutOutput", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "DontCare" + } + } + ], + "ImageAttachments": [ + { + "Name": "ColorGradingLut", + "ImageDescriptor": { + "Format": "R32G32B32A32_FLOAT", + "BindFlags": [ + "ShaderWrite" + ] + } + } + ], + "Connections": [ + { + "LocalSlot": "LutOutput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "ColorGradingLut" + } + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/ColorGrading/LutGeneration.shader" + }, + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_colorGradingExposure", + "Value": 0.0 // unconstrained, log2 stops + }, + { + "Name": "m_colorGradingContrast", + "Value": 0.0 // -100 ... 100 + }, + { + "Name": "m_colorGradingHueShift", + "Value": 0.0 // 0 ... 1, can wrap + }, + { + "Name": "m_colorGradingPreSaturation", + "Value": 1.0 // -100 ... 100 + }, + { + "Name": "m_colorFilterIntensity", + "Value": 1.0 // unconstrained, log2 stops + }, + { + "Name": "m_colorFilterMultiply", + "Value": 0.0 // modulate, 0 ... 1 + }, + { + "Name": "m_whiteBalanceKelvin", + "Value": 6600.0 // 1000.0f ... 40000.0f kelvin + }, + { + "Name": "m_whiteBalanceTint", + "Value": 0.0 // -100 ... 100 + }, + { + "Name": "m_splitToneBalance", + "Value": 0.0 // -1 ... 1 + }, + { + "Name": "m_splitToneWeight", + "Value": 0.0 // 0 ... 1 + }, + { + "Name": "m_colorGradingPostSaturation", + "Value": 1.0 // -100 ... 100 + }, + { + "Name": "m_smhShadowsStart", + "Value": 0.0 // 0 ... 1 + }, + { + "Name": "m_smhShadowsEnd", + "Value": 0.3 // 0 ... 1 + }, + { + "Name": "m_smhHighlightsStart", + "Value": 0.55 // 0 ... 1 + }, + { + "Name": "m_smhHighlightsEnd", + "Value": 1.0 // 0 ... 1 + }, + { + "Name": "m_smhWeight", + "Value": 0.0 // 0 ... 1 + } + ], + // The colors defined here are expected to be in linear rgb color space. + // These are converted to ACEScg color space within the HDRColorGrading.azsl shader + "ColorMappings": [ + { + "Name": "m_colorFilterSwatch", + "Value": [ + 1.0, + 0.5, + 0.5, + 1.0 + ] + }, + { + "Name": "m_splitToneShadowsColor", + "Value": [ + 1.0, + 0.1, + 0.1, + 1.0 + ] + }, + { + "Name": "m_splitToneHighlightsColor", + "Value": [ + 0.1, + 1.0, + 0.1, + 1.0 + ] + }, + { + "Name": "m_smhShadowsColor", + "Value": [ + 1.0, + 0.25, + 0.25, + 1.0 + ] + }, + { + "Name": "m_smhMidtonesColor", + "Value": [ + 0.1, + 0.1, + 1.0, + 1.0 + ] + }, + { + "Name": "m_smhHighlightsColor", + "Value": [ + 1.0, + 0.0, + 1.0, + 1.0 + ] + } + ], + "Float3Mappings": [ + { + "Name": "m_channelMixingRed", + "Value": [ + 1.0, + 0.0, + 0.0 + ] + }, + { + "Name": "m_channelMixingGreen", + "Value": [ + 0.0, + 1.0, + 0.0 + ] + }, + { + "Name": "m_channelMixingBlue", + "Value": [ + 0.0, + 0.0, + 1.0 + ] + } + ] + } + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index 76285b00c7..7770b326a6 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -511,7 +511,11 @@ { "Name": "HDRColorGradingTemplate", "Path": "Passes/HDRColorGrading.pass" - } + }, + { + "Name": "LutGenerationTemplate", + "Path": "Passes/LutGeneration.pass" + } ] } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli index e84b18c550..1528bdc63e 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli @@ -59,7 +59,7 @@ UNDISCLOSED. #pragma once -static const float HALF_MAX = 65504.0f; +#include float AcesCcToLinear(float value) { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl new file mode 100644 index 0000000000..314481b6db --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.azsl @@ -0,0 +1,256 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +#include + +#include +#include +#include +#include +#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli> +#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli> +#include <3rdParty/Features/PostProcessing/KelvinToRgb.azsli> + +static const float FloatEpsilon = 1.192092896e-07; // 1.0 + FloatEpsilon != 1.0, smallest positive float +static const float FloatMin = FLOAT_32_MIN; // Min float number that is positive +static const float FloatMax = FLOAT_32_MAX; // Max float number representable + +static const float AcesCcMidGrey = 0.4135884; + +float3 convert2Dto3DLutCoords(float2 uv) +{ + const float height = 16.0; + const float width = 256.0; + + float2 adjustedUv = float2(uv.x * width, uv.y * height); + float3 coords = float3(adjustedUv.x%height, adjustedUv.x/height, adjustedUv.y); + return coords/height; +} + +ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback +{ + // framebuffer sampler + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + // Identity LUTs + Texture3D m_identityLut16x16x16; + + float m_colorGradingExposure; + float m_colorGradingContrast; + float m_colorGradingHueShift; + float m_colorGradingPreSaturation; + float m_colorFilterIntensity; + float m_colorFilterMultiply; + float m_whiteBalanceKelvin; + float m_whiteBalanceTint; + float m_splitToneBalance; + float m_splitToneWeight; + float m_colorGradingPostSaturation; + float m_smhShadowsStart; + float m_smhShadowsEnd; + float m_smhHighlightsStart; + float m_smhHighlightsEnd; + float m_smhWeight; + + float3 m_channelMixingRed; + float3 m_channelMixingGreen; + float3 m_channelMixingBlue; + + float4 m_colorFilterSwatch; + float4 m_splitToneShadowsColor; + float4 m_splitToneHighlightsColor; + + float4 m_smhShadowsColor; + float4 m_smhMidtonesColor; + float4 m_smhHighlightsColor; +} + +float SaturateWithEpsilon(float value) +{ + return clamp(value, FloatEpsilon, 1.0f); +} + +// Below are the color grading functions. These expect the frame color to be in ACEScg space. +// Note that some functions may have some quirks in their implementation and is subject to change. +float3 ColorGradePostExposure (float3 frameColor, float exposure) +{ + frameColor *= pow(2.0f, exposure); + return frameColor; +} + +// The contrast equation is performed in ACEScc (logarithmic) color space. +float3 ColorGradingContrast (float3 frameColor, float midgrey, float amount) +{ + const float contrastAdjustment = amount * 0.01f + 1.0f; + frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScg, ColorSpaceId::ACEScc); + frameColor = (frameColor - midgrey) * contrastAdjustment + midgrey; + return frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScc, ColorSpaceId::ACEScg); +} + +// The swatchColor param expects a linear RGB value. +float3 ColorGradeColorFilter (float3 frameColor, float3 swatchColor, float alpha) +{ + swatchColor = TransformColor(swatchColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + swatchColor *= pow(2.0f, PassSrg::m_colorFilterIntensity); + const float3 frameAdjust = frameColor * swatchColor; + return frameColor = lerp(frameColor, frameAdjust, alpha); +} + +float3 ColorGradeHueShift (float3 frameColor, float amount) +{ + float3 frameHsv = RgbToHsv(frameColor); + const float hue = frameHsv.x + amount; + frameHsv.x = RotateHue(hue, 0.0, 1.0); + return HsvToRgb(frameHsv); +} + +float3 ColorGradeSaturation (float3 frameColor, float control) +{ + const float vLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); + return (frameColor - vLuminance) * control + vLuminance; +} + +float3 ColorGradeKelvinColorTemp(float3 frameColor, float kelvin) +{ + const float3 kColor = TransformColor(KelvinToRgb(kelvin), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + const float luminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); + const float3 resHsl = RgbToHsl(frameColor.rgb * kColor.rgb); // Apply Kelvin color and convert to HSL + return HslToRgb(float3(resHsl.xy, luminance)); // Preserve luminance +} + +// pow(f, e) won't work if f is negative, or may cause inf/NAN. +float3 NoNanPow(float3 base, float3 power) +{ + return pow(max(abs(base), float3(FloatEpsilon, FloatEpsilon, FloatEpsilon)), power); +} + +float3 ColorGradeSplitTone (float3 frameColor, float balance, float weight) +{ + float3 frameSplitTone = NoNanPow(frameColor, 1.0 / 2.2); + const float t = SaturateWithEpsilon(CalculateLuminance(SaturateWithEpsilon(frameSplitTone), ColorSpaceId::ACEScg) + balance); + const float3 shadows = lerp(0.5, PassSrg::m_splitToneShadowsColor.rgb, 1.0 - t); + const float3 highlights = lerp(0.5, PassSrg::m_splitToneHighlightsColor.rgb, t); + frameSplitTone = BlendMode_SoftLight(frameSplitTone, shadows); + frameSplitTone = BlendMode_SoftLight(frameSplitTone, highlights); + frameSplitTone = NoNanPow(frameSplitTone, 2.2); + return lerp(frameColor.rgb, frameSplitTone.rgb, weight); +} + +float3 ColorGradeChannelMixer (float3 frameColor) +{ + return mul(float3x3(PassSrg::m_channelMixingRed.rgb, + PassSrg::m_channelMixingGreen.rgb, + PassSrg::m_channelMixingBlue.rgb), + frameColor); +} + +float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStart, float shadowsEnd, + float highlightsStart, float highlightsEnd, float weight, + float4 shadowsColor, float4 midtonesColor, float4 highlightsColor) +{ + const float3 shadowsColorACEScg = TransformColor(shadowsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + const float3 midtonesColorACEScg = TransformColor(midtonesColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + const float3 highlightsColorACEScg = TransformColor(highlightsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + + const float cLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); + const float shadowsWeight = 1.0 - smoothstep(shadowsStart, shadowsEnd, cLuminance); + const float highlightsWeight = smoothstep(highlightsStart, highlightsEnd, cLuminance); + const float midtonesWeight = 1.0 - shadowsWeight - highlightsWeight; + + const float3 frameSmh = frameColor * shadowsColorACEScg * shadowsWeight + + frameColor * midtonesColorACEScg * midtonesWeight + + frameColor * highlightsColorACEScg * highlightsWeight; + return lerp(frameColor.rgb, frameSmh.rgb, weight); +} + +float3 ColorGrade(float3 frameColor) +{ + frameColor = ColorGradePostExposure(frameColor, PassSrg::m_colorGradingExposure); + frameColor = ColorGradeKelvinColorTemp(frameColor, PassSrg::m_whiteBalanceKelvin); + frameColor = ColorGradingContrast(frameColor, AcesCcMidGrey, PassSrg::m_colorGradingContrast); + frameColor = ColorGradeColorFilter(frameColor, PassSrg::m_colorFilterSwatch.rgb, + PassSrg::m_colorFilterMultiply); + frameColor = max(frameColor, 0.0); + frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation); + frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight); + frameColor = ColorGradeChannelMixer(frameColor); + frameColor = max(frameColor, 0.0); + frameColor = ColorGradeShadowsMidtonesHighlights(frameColor, PassSrg::m_smhShadowsStart, PassSrg::m_smhShadowsEnd, + PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight, + PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor); + frameColor = ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift); + frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation); + return frameColor.rgb; +} + +struct PSOutput +{ + float4 m_lutOutput : SV_Target0; +}; + +float3 GetSourceLutLinearColor(float3 baseColor, Texture3D sourceLut, ShaperType shaperType, float shaperBias, float shaperScale) +{ + // Convert from reference linearColor to the lutCoordinate for this Lut + float3 lutCoord = LinearToShaper(baseColor, shaperType, shaperBias, shaperScale); + + // Adjust coordinate to the domain excluding the outer half texel in all directions + uint3 outputDimensions; + sourceLut.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z); + float3 coordBias = 1.0 / (2.0 * outputDimensions); + float3 coordScale = (outputDimensions - 1.0) / outputDimensions; + lutCoord = (lutCoord * coordScale) + coordBias; + + float3 lutColor = sourceLut.SampleLevel(PassSrg::LinearSampler, lutCoord, 0).rgb; + // Convert to linear + float3 linearColor = ShaperToLinear(lutColor, shaperType, shaperBias, shaperScale); + return linearColor; +} + +float3 InverseGamma(float3 color) +{ + return pow(color, 2.2); +} + +PSOutput MainPS(VSOutput IN) +{ + int shaperNum = 0; + ShaperType shaperType = (ShaperType)shaperNum; + //ShaperType shaperType = ShaperType::ShaperLinear; + float bias = 0.0; + float scale = 1.0; + + PSOutput OUT; + + float3 baseCoords = convert2Dto3DLutCoords(IN.m_texCoord); + float3 baseColor = ShaperToLinear(baseCoords, shaperType, bias, scale); + + float3 lutColor = PassSrg::m_identityLut16x16x16.Sample(PassSrg::LinearSampler, baseColor, 0.0).rgb; + //float3 lutColor = GetSourceLutLinearColor(baseColor, PassSrg::m_identityLut16x16x16, shaperType, bias, scale); + + //float3 gradedColor = float4(ColorGrade(lutColor), 1.0); + + //float3 finalColor = LinearToShaper(gradedColor, shaperType, bias, scale); + + OUT.m_lutOutput = float4(lutColor, 1.0); + + return OUT; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader new file mode 100644 index 0000000000..4e2c83db35 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/ColorGrading/LutGeneration.shader @@ -0,0 +1,22 @@ +{ + "Source" : "LutGeneration", + + "DepthStencilState" : { + "Depth" : { "Enable" : false } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 60614993b5..575e0dcd01 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -86,7 +86,6 @@ set(FILES Passes/CascadedShadowmaps.pass Passes/CheckerboardResolveColor.pass Passes/CheckerboardResolveDepth.pass - Passes/HDRColorGrading.pass Passes/ContrastAdaptiveSharpening.pass Passes/ConvertToAcescg.pass Passes/DebugOverlayParent.pass @@ -144,6 +143,7 @@ set(FILES Passes/ForwardSubsurfaceMSAA.pass Passes/FullscreenCopy.pass Passes/FullscreenOutputOnly.pass + Passes/HDRColorGrading.pass Passes/ImGui.pass Passes/KawaseShadowBlur.pass Passes/LightAdaptationParent.pass @@ -158,6 +158,7 @@ set(FILES Passes/LowEndPipeline.pass Passes/LuminanceHeatmap.pass Passes/LuminanceHistogramGenerator.pass + Passes/LutGeneration.pass Passes/MainPipeline.pass Passes/MainPipelineRenderToTexture.pass Passes/ThumbnailPipeline.pass @@ -316,6 +317,8 @@ set(FILES Shaders/BRDFTexture/BRDFTextureCS.shader Shaders/Checkerboard/CheckerboardColorResolveCS.azsl Shaders/Checkerboard/CheckerboardColorResolveCS.shader + Shaders/ColorGrading/LutGeneration.azsl + Shaders/ColorGrading/LutGeneration.shader Shaders/Depth/DepthPass.azsl Shaders/Depth/DepthPass.shader Shaders/Depth/DepthPassTransparentMax.shader diff --git a/Gems/Atom/Feature/Common/Code/CMakeLists.txt b/Gems/Atom/Feature/Common/Code/CMakeLists.txt index 9c4ffe5b5e..1e597d1795 100644 --- a/Gems/Atom/Feature/Common/Code/CMakeLists.txt +++ b/Gems/Atom/Feature/Common/Code/CMakeLists.txt @@ -39,6 +39,7 @@ ly_add_target( Gem::Atom_Utils.Static Gem::Atom_Feature_Common.Public Gem::ImGui.imguilib + 3rdParty::tiff #3rdParty::lux_core # AZ_TRAIT_LUXCORE_SUPPORTED is disabled in every platform, Issue #3915 will remove RUNTIME_DEPENDENCIES Gem::ImGui.imguilib diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl index 241ba3b5de..c82cbc2c9c 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl @@ -10,6 +10,7 @@ // PARAM(NAME, MEMBER_NAME, DEFAULT_VALUE, ...) AZ_GFX_BOOL_PARAM(Enabled, m_enabled, false) +AZ_GFX_BOOL_PARAM(GenerateLut, m_generateLut, false) AZ_GFX_FLOAT_PARAM(ColorGradingExposure, m_colorGradingExposure, 0.0) AZ_GFX_FLOAT_PARAM(ColorGradingContrast, m_colorGradingContrast, 0.0) AZ_GFX_FLOAT_PARAM(ColorGradingHueShift, m_colorGradingHueShift, 0.0) diff --git a/Gems/Atom/Feature/Common/Code/Source/ColorGrading/LutGenerationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ColorGrading/LutGenerationPass.cpp new file mode 100644 index 0000000000..126106d3a3 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ColorGrading/LutGenerationPass.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma optimize("", off) + +#include +#include + +#include +#include + +namespace AZ +{ + namespace Render + { + + RPI::Ptr LutGenerationPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew LutGenerationPass(descriptor); + return AZStd::move(pass); + } + + LutGenerationPass::LutGenerationPass(const RPI::PassDescriptor& descriptor) + : HDRColorGradingPass(descriptor) + { + } + + void LutGenerationPass::BuildInternal() + { + auto scene = GetScene(); + if (scene) + { + AcesDisplayMapperFeatureProcessor* dmfp = scene->GetFeatureProcessor(); + if (dmfp) + { + // load the image assets + auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath( + "lookuptables/lut_identitylinear_16x16x16.azasset", AZ::RPI::AssetUtils::TraceLevel::Error); + AZ_Assert(assetId.IsValid(), "LUT Asset is not valid."); + dmfp->GetLutFromAssetId(m_colorGradingLut, assetId); + + // set srg image index + m_shaderResourceGroup->SetImageView(m_identityLut16x16x16Index, m_colorGradingLut.m_lutStreamingImage->GetImageView()); + + // Get the output image attachment + RPI::Ptr attachment = FindOwnedAttachment(Name{ "ColorGradingLut" }); + RHI::ImageDescriptor& imageDescriptor = attachment->m_descriptor.m_image; + imageDescriptor.m_size = RHI::Size( + m_colorGradingLut.m_lutStreamingImage->GetDescriptor().m_size.m_width*m_colorGradingLut.m_lutStreamingImage->GetDescriptor().m_size.m_width, + m_colorGradingLut.m_lutStreamingImage->GetDescriptor().m_size.m_height, + 1); + } + } + + HDRColorGradingPass::BuildInternal(); + } + + void LutGenerationPass::InitializeInternal() + { + HDRColorGradingPass::InitializeInternal(); + + m_identityLut16x16x16Index.Reset(); + m_identityLut32x32x32Index.Reset(); + m_identityLut64x64x64Index.Reset(); + + //// load the image assets + //DisplayMapperAssetLut m_colorGradingLut; + //auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath("lookuptables/lut_identitylinear_16x16x16.azasset", AZ::RPI::AssetUtils::TraceLevel::Error); + //AcesDisplayMapperFeatureProcessor* dmfp = GetScene()->GetFeatureProcessor(); + //dmfp->GetLutFromAssetId(m_colorGradingLut, assetId); + + //// set srg image index + //m_shaderResourceGroup->SetImageView(m_identityLut16x16x16Index, m_colorGradingLut.m_lutStreamingImage->GetImageView()); + } + + void LutGenerationPass::FrameBeginInternal(FramePrepareParams params) + { + HDRColorGradingPass::FrameBeginInternal(params); + } + + void LutGenerationPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) + { + HDRColorGradingPass::BuildCommandListInternal(context); + } + + bool LutGenerationPass::IsEnabled() const + { + //const auto* colorGradingSettings = GetHDRColorGradingSettings(); + //return colorGradingSettings ? colorGradingSettings->GetGenerateLut() : false; + return true; + } + } // namespace Render +} // namespace AZ + +#pragma optimize("", on) diff --git a/Gems/Atom/Feature/Common/Code/Source/ColorGrading/LutGenerationPass.h b/Gems/Atom/Feature/Common/Code/Source/ColorGrading/LutGenerationPass.h new file mode 100644 index 0000000000..321ceb7e74 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ColorGrading/LutGenerationPass.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace AZ +{ + namespace Render + { + class LutGenerationPass + : public AZ::Render::HDRColorGradingPass + { + public: + AZ_RTTI(LutGenerationPass, "{C21DABA8-B538-4C80-BA18-5B97CC9259E5}", AZ::RPI::FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(LutGenerationPass, SystemAllocator, 0); + + virtual ~LutGenerationPass() = default; + + //! Creates a ColorGradingPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + LutGenerationPass(const RPI::PassDescriptor& descriptor); + + void BuildInternal() override; + void InitializeInternal() override; + void FrameBeginInternal(FramePrepareParams params) override; + void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override; + bool IsEnabled() const override; + private: + RHI::ShaderInputNameIndex m_identityLut16x16x16Index = "m_identityLut16x16x16"; + RHI::ShaderInputNameIndex m_identityLut32x32x32Index = "m_identityLut32x32x32"; + RHI::ShaderInputNameIndex m_identityLut64x64x64Index = "m_identityLut64x64x64"; + + DisplayMapperAssetLut m_colorGradingLut; + + bool m_isInitialized = false; + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 22c5f37ff8..4ddf609f41 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -230,6 +231,7 @@ namespace AZ passSystem->AddPassCreator(Name("HDRColorGradingPass"), &HDRColorGradingPass::Create); passSystem->AddPassCreator(Name("LookModificationCompositePass"), &LookModificationCompositePass::Create); passSystem->AddPassCreator(Name("LookModificationTransformPass"), &LookModificationPass::Create); + passSystem->AddPassCreator(Name("LutGenerationPass"), &LutGenerationPass::Create); passSystem->AddPassCreator(Name("SMAAEdgeDetectionPass"), &SMAAEdgeDetectionPass::Create); passSystem->AddPassCreator(Name("SMAABlendingWeightCalculationPass"), &SMAABlendingWeightCalculationPass::Create); passSystem->AddPassCreator(Name("SMAANeighborhoodBlendingPass"), &SMAANeighborhoodBlendingPass::Create); diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 51e18b8e31..e5f7858510 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -35,6 +35,7 @@ #include #include +#include namespace AZ { namespace Render @@ -106,6 +107,48 @@ namespace AZ return FrameCaptureOutputResult{FrameCaptureResult::InternalError, "Unable to save frame capture output to '" + outputFilePath + "'"}; } + FrameCaptureOutputResult TiffFrameCaptureOutput( + const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + AZStd::shared_ptr> buffer = readbackResult.m_dataBuffer; + const uint32_t width = readbackResult.m_imageDescriptor.m_size.m_width; + const uint32_t height = readbackResult.m_imageDescriptor.m_size.m_height; + const uint32_t numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format); + const uint32_t bytesPerChannel = AZ::RHI::GetFormatSize(readbackResult.m_imageDescriptor.m_format) / numChannels; + const uint32_t bitsPerChannel = bytesPerChannel * 8; + + TIFF* out = TIFFOpen(outputFilePath.c_str(), "w"); + TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(out, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, numChannels); + TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitsPerChannel); + TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); // interpret each pixel as a float + + size_t pitch = width * numChannels * bytesPerChannel; + AZ_Assert((pitch * height) == buffer->size(), "Image buffer does not match allocated bytes for tiff saving.") + unsigned char* raster = (unsigned char*)_TIFFmalloc((tsize_t)(pitch * height)); + memcpy(raster, buffer->data(), pitch * height); + bool success = true; + for (uint32_t h = 0; h < height; ++h) + { + size_t offset = h * pitch; + int err = TIFFWriteScanline(out, raster + offset, h, 0); + if (err < 0) + { + success = false; + break; + } + } + _TIFFfree(raster); + TIFFClose(out); + return success ? FrameCaptureOutputResult{ FrameCaptureResult::Success, AZStd::nullopt } + : FrameCaptureOutputResult{ FrameCaptureResult::InternalError, "Unable to save tif frame capture output to " + outputFilePath }; + } + FrameCaptureOutputResult DdsFrameCaptureOutput( const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) { @@ -488,6 +531,12 @@ namespace AZ m_result = ddsFrameCapture.m_result; m_latestCaptureInfo = ddsFrameCapture.m_errorMessage.value_or(""); } + else if (extension == "tiff" || extension == "tif") + { + const auto tifFrameCapture = TiffFrameCaptureOutput(m_outputFilePath, readbackResult); + m_result = tifFrameCapture.m_result; + m_latestCaptureInfo = tifFrameCapture.m_errorMessage.value_or(""); + } else if (extension == "png") { if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM || diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h index e5eaf7db26..f00716ffcd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h @@ -41,10 +41,9 @@ namespace AZ void FrameBeginInternal(FramePrepareParams params) override; bool IsEnabled() const override; - private: const HDRColorGradingSettings* GetHDRColorGradingSettings() const; - void SetSrgConstants(); - + virtual void SetSrgConstants(); + private: RHI::ShaderInputNameIndex m_colorGradingExposureIndex = "m_colorGradingExposure"; RHI::ShaderInputNameIndex m_colorGradingContrastIndex = "m_colorGradingContrast"; RHI::ShaderInputNameIndex m_colorGradingHueShiftIndex = "m_colorGradingHueShift"; diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 18cdc273d2..71a0a8a1f4 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -66,6 +66,8 @@ set(FILES Source/AuxGeom/DynamicPrimitiveProcessor.h Source/AuxGeom/FixedShapeProcessor.cpp Source/AuxGeom/FixedShapeProcessor.h + Source/ColorGrading/LutGenerationPass.cpp + Source/ColorGrading/LutGenerationPass.h Source/CoreLights/CapsuleLightFeatureProcessor.h Source/CoreLights/CapsuleLightFeatureProcessor.cpp Source/CoreLights/CascadedShadowmapsPass.h @@ -212,8 +214,6 @@ set(FILES Source/PostProcessing/BloomCompositePass.cpp Source/PostProcessing/BloomParentPass.h Source/PostProcessing/BloomParentPass.cpp - Source/PostProcessing/HDRColorGradingPass.cpp - Source/PostProcessing/HDRColorGradingPass.h Source/PostProcessing/DepthOfFieldCompositePass.h Source/PostProcessing/DepthOfFieldCompositePass.cpp Source/PostProcessing/DepthOfFieldBokehBlurPass.h @@ -234,6 +234,8 @@ set(FILES Source/PostProcessing/EyeAdaptationPass.h Source/PostProcessing/FastDepthAwareBlurPasses.cpp Source/PostProcessing/FastDepthAwareBlurPasses.h + Source/PostProcessing/HDRColorGradingPass.cpp + Source/PostProcessing/HDRColorGradingPass.h Source/PostProcessing/LookModificationCompositePass.cpp Source/PostProcessing/LookModificationCompositePass.h Source/PostProcessing/LookModificationTransformPass.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp index 49c8275141..5be2eca676 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include namespace AZ { @@ -31,6 +32,12 @@ namespace AZ ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) ->Attribute(Edit::Attributes::AutoExpand, true) ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need to create page for PostProcessing. + ->ClassElement(AZ::Edit::ClassElements::Group, "LUT Generation") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->UIElement(AZ::Edit::UIHandlers::Button, "Generate LUT", "Generates a LUT from the scene's enabled color grading blend.") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") + ->Attribute(AZ::Edit::Attributes::ButtonText, "Generate LUT") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorHDRColorGradingComponent::GenerateLut) ; editContext->Class( @@ -136,6 +143,69 @@ namespace AZ { } + void EditorHDRColorGradingComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if (m_controller.m_configuration.m_generateLut && m_frameCounter) + { + --m_frameCounter; + return; + } + else if (m_controller.m_configuration.m_generateLut && m_lutGenerationInProgress) + { + const char* LutAttachment = "LutOutput"; + const AZStd::vector LutGenerationPassHierarchy{ "LutGenerationPass" }; + + // capture frame + AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect(); + + bool startedCapture = false; + AZ::Render::FrameCaptureRequestBus::BroadcastResult( + startedCapture, + &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachment, + LutGenerationPassHierarchy, + AZStd::string(LutAttachment), TempTiffFilePath, + AZ::RPI::PassAttachmentReadbackOption::Output); + + m_lutGenerationInProgress = !startedCapture; + } + } + + void EditorHDRColorGradingComponent::OnCaptureFinished([[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]]const AZStd::string& info) + { + char resolvedInputFilePath[AZ_MAX_PATH_LEN] = { 0 }; + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(TempTiffFilePath, resolvedInputFilePath, AZ_MAX_PATH_LEN); + char resolvedOutputFilePath[AZ_MAX_PATH_LEN] = { 0 }; + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(GeneratedLutFilePath, resolvedOutputFilePath, AZ_MAX_PATH_LEN); + + AZStd::vector pythonArgs + { + "--i", resolvedInputFilePath, + "--o", resolvedOutputFilePath + }; + + AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast( + &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, + "@devroot@/Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/tiff_to_3dl_azasset.py", pythonArgs); + + m_controller.m_configuration.m_generateLut = false; + m_controller.OnConfigChanged(); + + AZ::TickBus::Handler::BusDisconnect(); + AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect(); + } + + void EditorHDRColorGradingComponent::GenerateLut() + { + // turn on lut generation pass + m_lutGenerationInProgress = true; + m_controller.m_configuration.m_generateLut = true; + m_controller.OnConfigChanged(); + + m_frameCounter = FramesToWait; + + AZ::TickBus::Handler::BusConnect(); + } + u32 EditorHDRColorGradingComponent::OnConfigurationChanged() { m_controller.OnConfigChanged(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h index 8eea568e3a..f890cd28b7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h @@ -8,16 +8,23 @@ #pragma once +#include #include +#include #include namespace AZ { namespace Render { + static const char* const TempTiffFilePath{ "@projectcache@/LutGeneration/SavedLut.tiff" }; + static const char* const GeneratedLutFilePath{ "@projectcache@/LutGeneration/SavedLut" }; + class EditorHDRColorGradingComponent final : public AzToolsFramework::Components:: EditorComponentAdapter + , private TickBus::Handler + , private FrameCaptureNotificationBus::Handler { public: using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; @@ -25,11 +32,27 @@ namespace AZ static void Reflect(AZ::ReflectContext* context); + static const int FramesToWait = 5; + //static const char* LutAttachment; + //static const AZStd::vector LutGenerationPassHierarchy; + EditorHDRColorGradingComponent() = default; EditorHDRColorGradingComponent(const HDRColorGradingComponentConfig& config); //! EditorRenderComponentAdapter overrides... AZ::u32 OnConfigurationChanged() override; + + private: + // AZ::TickBus overrides ... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + // FrameCaptureNotificationBus overrides ... + void OnCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override; + + void GenerateLut(); + + AZStd::atomic_bool m_lutGenerationInProgress = false; + int m_frameCounter; }; } // namespace Render } // namespace AZ