diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-09-11 21:10:02 +0000 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-09-11 21:10:02 +0000 |
| commit | 6035757b5ce0a795c9a96895721dbe621f379387 (patch) | |
| tree | bbb578a7ca83aad5996e16414b038161ec45ac9c | |
| parent | 0198478d81c9cc1d4a27f718982b748b2261b657 (diff) | |
tessellation, moves widget, smooth animation, wip
| -rw-r--r-- | data/chess.blend | bin | 9294348 -> 9299940 bytes | |||
| -rw-r--r-- | shaders/GBuffPbrInstanced.vert | 48 | ||||
| -rw-r--r-- | shaders/GBuffPbrTexArray.frag | 278 | ||||
| -rw-r--r-- | shaders/pntriangles.tesc | 92 | ||||
| -rw-r--r-- | shaders/pntriangles.tese | 97 | ||||
| -rw-r--r-- | shaders/shadow.geom | 20 | ||||
| -rw-r--r-- | src/Animation/AngleAnimation.cs | 41 | ||||
| -rw-r--r-- | src/Animation/Animation.cs | 39 | ||||
| -rw-r--r-- | src/Animation/AnimationTemplate.cs | 22 | ||||
| -rw-r--r-- | src/Animation/BezierPath.cs | 22 | ||||
| -rw-r--r-- | src/Animation/FloatAnimation.cs | 24 | ||||
| -rw-r--r-- | src/Animation/FloatAnimation2.cs | 60 | ||||
| -rw-r--r-- | src/Animation/MathHelper.cs | 32 | ||||
| -rw-r--r-- | src/Animation/Path.cs | 5 | ||||
| -rw-r--r-- | src/Animation/PathAnimation.cs | 36 | ||||
| -rw-r--r-- | src/Animation/ShakeAnimation.cs | 30 | ||||
| -rw-r--r-- | src/MoveHistoryWidget.cs | 201 | ||||
| -rw-r--r-- | src/Piece.cs | 12 | ||||
| -rw-r--r-- | src/VkChess.cs | 422 | ||||
| -rw-r--r-- | src/deferredPbrRendererBase.cs | 13 | ||||
| -rw-r--r-- | ui/chess.crow | 4 | ||||
| -rw-r--r-- | ui/winMoves.crow | 7 | ||||
| -rw-r--r-- | ui/winOptions.crow | 10 | ||||
| -rw-r--r-- | vkChess.net.csproj | 4 |
24 files changed, 983 insertions, 536 deletions
diff --git a/data/chess.blend b/data/chess.blend Binary files differindex 7f2f5f5..7238e77 100644 --- a/data/chess.blend +++ b/data/chess.blend diff --git a/shaders/GBuffPbrInstanced.vert b/shaders/GBuffPbrInstanced.vert index 1095beb..12d2bd9 100644 --- a/shaders/GBuffPbrInstanced.vert +++ b/shaders/GBuffPbrInstanced.vert @@ -9,30 +9,36 @@ layout (location = 4) in vec4 inColor; layout (location = 5) in mat4 inModel; layout (set = 0, binding = 0) uniform UBO { - mat4 projection; - mat4 model; - mat4 view; + mat4 projection; + mat4 model; + mat4 view; } ubo; -layout (location = 0) out vec3 outWorldPos; -layout (location = 1) out vec3 outNormal; -layout (location = 2) out vec2 outUV0; -layout (location = 3) out vec2 outUV1; -layout (location = 4) out vec4 outColor; +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec2 outUV0; +layout (location = 2) out vec2 outUV1; +layout (location = 3) out vec4 outColor; -out gl_PerVertex +/*out gl_PerVertex { - vec4 gl_Position; -}; + vec4 gl_Position; +};*/ -void main() -{ - vec4 locPos = ubo.model * inModel * vec4(inPos, 1.0); - //locPos.y = -locPos.y; - outNormal = normalize(transpose(inverse(mat3(ubo.model * inModel))) * inNormal); - outColor = inColor; - outWorldPos = locPos.xyz; - outUV0 = inUV0; - outUV1 = inUV1; - gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0); +void main() +{ + /*vec4 locPos = ubo.model * inModel * vec4(inPos, 1.0); + + outNormal = normalize(transpose(inverse(mat3(ubo.model * inModel))) * inNormal); + outColor = inColor; + outWorldPos = locPos.xyz; + outUV0 = inUV0; + outUV1 = inUV1; + gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0);*/ + + outNormal = normalize(transpose(inverse(mat3(ubo.model * inModel))) * inNormal); + outUV0 = inUV0; + outUV1 = inUV1; + outColor = inColor; + + gl_Position = ubo.model * inModel * vec4(inPos, 1.0); } diff --git a/shaders/GBuffPbrTexArray.frag b/shaders/GBuffPbrTexArray.frag index e0d9f4f..c99e0d7 100644 --- a/shaders/GBuffPbrTexArray.frag +++ b/shaders/GBuffPbrTexArray.frag @@ -9,25 +9,25 @@ layout (constant_id = 1) const float FAR_PLANE = 256.0f; layout (constant_id = 2) const int MAT_COUNT = 1; struct Material { - vec4 baseColorFactor; - vec4 emissiveFactor; - vec4 diffuseFactor; - vec4 specularFactor; - - float workflow; - uint tex0; - uint tex1; - int baseColorTex; - - int physicalDescTex; - int normalTex; - int occlusionTex; - int emissiveTex; - - float metallicFactor; - float roughnessFactor; - float alphaMask; - float alphaMaskCutoff; + vec4 baseColorFactor; + vec4 emissiveFactor; + vec4 diffuseFactor; + vec4 specularFactor; + + float workflow; + uint tex0; + uint tex1; + int baseColorTex; + + int physicalDescTex; + int normalTex; + int occlusionTex; + int emissiveTex; + + float metallicFactor; + float roughnessFactor; + float alphaMask; + float alphaMaskCutoff; }; const float M_PI = 3.141592653589793; const float c_MinRoughness = 0.04; @@ -50,13 +50,13 @@ layout (location = 3) in vec2 inUV1; layout (location = 4) in vec4 inColor; layout (set = 0, binding = 5) uniform UBOMaterials { - Material materials[MAT_COUNT]; + Material materials[MAT_COUNT]; }; layout (set = 0, binding = 7) uniform sampler2DArray texArray; layout (push_constant) uniform PushCsts { - layout(offset = 64) - int materialIdx; + layout(offset = 64) + int materialIdx; }; @@ -67,147 +67,147 @@ layout (location = 3) out vec4 outPos; vec4 SRGBtoLINEAR(vec4 srgbIn) { - #ifdef MANUAL_SRGB - #ifdef SRGB_FAST_APPROXIMATION - vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); - #else //SRGB_FAST_APPROXIMATION - vec3 bLess = step(vec3(0.04045),srgbIn.xyz); - vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); - #endif //SRGB_FAST_APPROXIMATION - return vec4(linOut,srgbIn.w);; - #else //MANUAL_SRGB - return srgbIn; - #endif //MANUAL_SRGB + #ifdef MANUAL_SRGB + #ifdef SRGB_FAST_APPROXIMATION + vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); + #else //SRGB_FAST_APPROXIMATION + vec3 bLess = step(vec3(0.04045),srgbIn.xyz); + vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); + #endif //SRGB_FAST_APPROXIMATION + return vec4(linOut,srgbIn.w);; + #else //MANUAL_SRGB + return srgbIn; + #endif //MANUAL_SRGB } // Find the normal for this fragment, pulling either from a predefined normal map // or from the interpolated mesh normal and tangent attributes. vec3 getNormal() { - vec3 tangentNormal; - // Perturb normal, see http://www.thetenthplanet.de/archives/1180 - if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL) - tangentNormal = texture(texArray, vec3(inUV0, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; - else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL) - tangentNormal = texture(texArray, vec3(inUV1, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; - else - return normalize(inNormal); - - vec3 q1 = dFdx(inWorldPos); - vec3 q2 = dFdy(inWorldPos); - vec2 st1 = dFdx(inUV0); - vec2 st2 = dFdy(inUV0); + vec3 tangentNormal; + // Perturb normal, see http://www.thetenthplanet.de/archives/1180 + if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(texArray, vec3(inUV0, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; + else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(texArray, vec3(inUV1, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; + else + return normalize(inNormal); + + vec3 q1 = dFdx(inWorldPos); + vec3 q2 = dFdy(inWorldPos); + vec2 st1 = dFdx(inUV0); + vec2 st2 = dFdy(inUV0); - vec3 N = normalize(inNormal); - vec3 T = normalize(q1 * st2.t - q2 * st1.t); - vec3 B = -normalize(cross(N, T)); - mat3 TBN = mat3(T, B, N); + vec3 N = normalize(inNormal); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); - return normalize(TBN * tangentNormal); + return normalize(TBN * tangentNormal); } -// Gets metallic factor from specular glossiness workflow inputs +// Gets metallic factor from specular glossiness workflow inputs float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) { - float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); - float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); - if (perceivedSpecular < c_MinRoughness) { - return 0.0; - } - float a = c_MinRoughness; - float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; - float c = c_MinRoughness - perceivedSpecular; - float D = max(b * b - 4.0 * a * c, 0.0); - return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); + float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); + float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); + if (perceivedSpecular < c_MinRoughness) { + return 0.0; + } + float a = c_MinRoughness; + float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; + float c = c_MinRoughness - perceivedSpecular; + float D = max(b * b - 4.0 * a * c, 0.0); + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); } float linearDepth(float depth) { - float z = depth * 2.0f - 1.0f; - return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); } -void main() +void main() { - float perceptualRoughness; - float metallic; - vec4 baseColor; - vec3 emissive = vec3(0); - - baseColor = materials[materialIdx].baseColorFactor; - - if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) { - perceptualRoughness = materials[materialIdx].roughnessFactor; - metallic = materials[materialIdx].metallicFactor; - // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. - // This layout intentionally reserves the 'r' channel for (optional) occlusion map data - if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ - perceptualRoughness *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).g; - metallic *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).b; - }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ - perceptualRoughness *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).g; - metallic *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).b; - } - perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); - metallic = clamp(metallic, 0.0, 1.0); + float perceptualRoughness; + float metallic; + vec4 baseColor; + vec3 emissive = vec3(0); + + baseColor = materials[materialIdx].baseColorFactor; + + if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) { + perceptualRoughness = materials[materialIdx].roughnessFactor; + metallic = materials[materialIdx].metallicFactor; + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).g; + metallic *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).b; + }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).g; + metallic *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).b; + } + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // The albedo may be defined from a base texture or a flat color + if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); + else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].baseColorTex))); + } + + if (materials[materialIdx].alphaMask == 1.0f) { + if (baseColor.a < materials[materialIdx].alphaMaskCutoff) + discard; + } + + if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) { + // Values from specular glossiness workflow are converted to metallic roughness + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).a; + else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).a; + else + perceptualRoughness = 0.0; + + const float epsilon = 1e-6; + + vec4 diffuse = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); + vec3 specular = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex))).rgb; + + float maxSpecular = max(max(specular.r, specular.g), specular.b); + + // Convert metallic value from specular glossiness inputs + metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); - // The albedo may be defined from a base texture or a flat color - if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR) - baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); - else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR) - baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].baseColorTex))); - } - - if (materials[materialIdx].alphaMask == 1.0f) { - if (baseColor.a < materials[materialIdx].alphaMaskCutoff) - discard; - } + vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb; + vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb; + baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); - if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) { - // Values from specular glossiness workflow are converted to metallic roughness - if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) - perceptualRoughness = 1.0 - texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).a; - else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) - perceptualRoughness = 1.0 - texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).a; - else - perceptualRoughness = 0.0; + } - const float epsilon = 1e-6; + baseColor *= inColor; - vec4 diffuse = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); - vec3 specular = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex))).rgb; + const float u_OcclusionStrength = 1.0f; + const float u_EmissiveFactor = 1.0f; + float ao = 1.0f; - float maxSpecular = max(max(specular.r, specular.g), specular.b); + if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; + else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; - // Convert metallic value from specular glossiness inputs - metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); + if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO) + ao = texture(texArray, vec3(inUV0, materials[materialIdx].occlusionTex)).r; + else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO) + ao = texture(texArray, vec3(inUV1, materials[materialIdx].occlusionTex)).r; - vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb; - vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb; - baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); + vec3 n = getNormal(); + vec3 p = inWorldPos; - } - - baseColor *= inColor; - - const float u_OcclusionStrength = 1.0f; - const float u_EmissiveFactor = 1.0f; - float ao = 1.0f; - - if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE) - emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; - else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE) - emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; - - if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO) - ao = texture(texArray, vec3(inUV0, materials[materialIdx].occlusionTex)).r; - else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO) - ao = texture(texArray, vec3(inUV1, materials[materialIdx].occlusionTex)).r; - - vec3 n = getNormal(); - vec3 p = inWorldPos; - - outColorRough = vec4 (baseColor.rgb, perceptualRoughness); - outEmitMetal = vec4 (emissive, metallic); - outN_AO = vec4 (n, ao); - outPos = vec4 (p, linearDepth(gl_FragCoord.z)); + outColorRough = vec4 (baseColor.rgb, perceptualRoughness); + outEmitMetal = vec4 (emissive, metallic); + outN_AO = vec4 (n, ao); + outPos = vec4 (p, linearDepth(gl_FragCoord.z)); }
\ No newline at end of file diff --git a/shaders/pntriangles.tesc b/shaders/pntriangles.tesc new file mode 100644 index 0000000..21fd448 --- /dev/null +++ b/shaders/pntriangles.tesc @@ -0,0 +1,92 @@ +#version 450 + +// PN patch data +struct PnPatch +{ + float b210; + float b120; + float b021; + float b012; + float b102; + float b201; + float b111; + float n110; + float n011; + float n101; +}; + +/*layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; +} ubo;*/ + +const float tessLevel = 3.0; + +layout (vertices=3) out; + +layout (location = 0) in vec3 inNormal[]; +layout (location = 1) in vec2 inUV0[]; +layout (location = 2) in vec2 inUV1[]; +layout (location = 3) in vec4 inColor[]; + +layout(location = 0) out vec3 outNormal[3]; +layout(location = 3) out vec2 outUV0[3]; +layout(location = 6) out vec2 outUV1[3]; +layout(location = 9) patch out vec4 outColor; +layout(location = 10) out PnPatch outPatch[3]; + + +float wij(int i, int j) +{ + return dot(gl_in[j].gl_Position.xyz - gl_in[i].gl_Position.xyz, inNormal[i]); +} + +float vij(int i, int j) +{ + vec3 Pj_minus_Pi = gl_in[j].gl_Position.xyz + - gl_in[i].gl_Position.xyz; + vec3 Ni_plus_Nj = inNormal[i]+inNormal[j]; + return 2.0*dot(Pj_minus_Pi, Ni_plus_Nj)/dot(Pj_minus_Pi, Pj_minus_Pi); +} + +void main() +{ + // get data + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + outNormal[gl_InvocationID] = inNormal[gl_InvocationID]; + outUV0[gl_InvocationID] = inUV0[gl_InvocationID]; + outUV1[gl_InvocationID] = inUV1[gl_InvocationID]; + outColor = inColor[gl_InvocationID]; + + // set base + float P0 = gl_in[0].gl_Position[gl_InvocationID]; + float P1 = gl_in[1].gl_Position[gl_InvocationID]; + float P2 = gl_in[2].gl_Position[gl_InvocationID]; + float N0 = inNormal[0][gl_InvocationID]; + float N1 = inNormal[1][gl_InvocationID]; + float N2 = inNormal[2][gl_InvocationID]; + + // compute control points + outPatch[gl_InvocationID].b210 = (2.0*P0 + P1 - wij(0,1)*N0)/3.0; + outPatch[gl_InvocationID].b120 = (2.0*P1 + P0 - wij(1,0)*N1)/3.0; + outPatch[gl_InvocationID].b021 = (2.0*P1 + P2 - wij(1,2)*N1)/3.0; + outPatch[gl_InvocationID].b012 = (2.0*P2 + P1 - wij(2,1)*N2)/3.0; + outPatch[gl_InvocationID].b102 = (2.0*P2 + P0 - wij(2,0)*N2)/3.0; + outPatch[gl_InvocationID].b201 = (2.0*P0 + P2 - wij(0,2)*N0)/3.0; + float E = ( outPatch[gl_InvocationID].b210 + + outPatch[gl_InvocationID].b120 + + outPatch[gl_InvocationID].b021 + + outPatch[gl_InvocationID].b012 + + outPatch[gl_InvocationID].b102 + + outPatch[gl_InvocationID].b201 ) / 6.0; + float V = (P0 + P1 + P2)/3.0; + outPatch[gl_InvocationID].b111 = E + (E - V)*0.5; + outPatch[gl_InvocationID].n110 = N0+N1-vij(0,1)*(P1-P0); + outPatch[gl_InvocationID].n011 = N1+N2-vij(1,2)*(P2-P1); + outPatch[gl_InvocationID].n101 = N2+N0-vij(2,0)*(P0-P2); + + // set tess levels + gl_TessLevelOuter[gl_InvocationID] = tessLevel; + gl_TessLevelInner[0] = tessLevel; +}
\ No newline at end of file diff --git a/shaders/pntriangles.tese b/shaders/pntriangles.tese new file mode 100644 index 0000000..e8af9d8 --- /dev/null +++ b/shaders/pntriangles.tese @@ -0,0 +1,97 @@ +#version 450 + +// PN patch data +struct PnPatch +{ + float b210; + float b120; + float b021; + float b012; + float b102; + float b201; + float b111; + float n110; + float n011; + float n101; +}; + +layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +const float tessAlpha = 1.0; + +layout(triangles, fractional_odd_spacing, ccw) in; + +layout(location = 0) in vec3 inNormal[]; +layout(location = 3) in vec2 inUV0[]; +layout(location = 6) in vec2 inUV1[]; +layout(location = 9) patch in vec4 inColor; +layout(location = 10) in PnPatch inPnPatch[]; + +layout (location = 0) out vec3 outWorldPos; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec2 outUV0; +layout (location = 3) out vec2 outUV1; +layout (location = 4) out vec4 outColor; + +#define uvw gl_TessCoord + +void main() +{ + vec3 uvwSquared = uvw * uvw; + vec3 uvwCubed = uvwSquared * uvw; + + // extract control points + vec3 b210 = vec3(inPnPatch[0].b210, inPnPatch[1].b210, inPnPatch[2].b210); + vec3 b120 = vec3(inPnPatch[0].b120, inPnPatch[1].b120, inPnPatch[2].b120); + vec3 b021 = vec3(inPnPatch[0].b021, inPnPatch[1].b021, inPnPatch[2].b021); + vec3 b012 = vec3(inPnPatch[0].b012, inPnPatch[1].b012, inPnPatch[2].b012); + vec3 b102 = vec3(inPnPatch[0].b102, inPnPatch[1].b102, inPnPatch[2].b102); + vec3 b201 = vec3(inPnPatch[0].b201, inPnPatch[1].b201, inPnPatch[2].b201); + vec3 b111 = vec3(inPnPatch[0].b111, inPnPatch[1].b111, inPnPatch[2].b111); + + // extract control normals + vec3 n110 = normalize(vec3(inPnPatch[0].n110, inPnPatch[1].n110, inPnPatch[2].n110)); + vec3 n011 = normalize(vec3(inPnPatch[0].n011, inPnPatch[1].n011, inPnPatch[2].n011)); + vec3 n101 = normalize(vec3(inPnPatch[0].n101, inPnPatch[1].n101, inPnPatch[2].n101)); + + // compute texcoords + outUV0 = gl_TessCoord[2]*inUV0[0] + gl_TessCoord[0]*inUV0[1] + gl_TessCoord[1]*inUV0[2]; + outUV1 = gl_TessCoord[2]*inUV1[0] + gl_TessCoord[0]*inUV1[1] + gl_TessCoord[1]*inUV1[2]; + + // normal + // Barycentric normal + vec3 barNormal = gl_TessCoord[2]*inNormal[0] + gl_TessCoord[0]*inNormal[1] + gl_TessCoord[1]*inNormal[2]; + vec3 pnNormal = inNormal[0]*uvwSquared[2] + inNormal[1]*uvwSquared[0] + inNormal[2]*uvwSquared[1] + + n110*uvw[2]*uvw[0] + n011*uvw[0]*uvw[1]+ n101*uvw[2]*uvw[1]; + outNormal = tessAlpha * pnNormal + (1.0 - tessAlpha) * barNormal; + + // compute interpolated pos + vec3 barPos = gl_TessCoord[2]*gl_in[0].gl_Position.xyz + + gl_TessCoord[0]*gl_in[1].gl_Position.xyz + + gl_TessCoord[1]*gl_in[2].gl_Position.xyz; + + // save some computations + uvwSquared *= 3.0; + + // compute PN position + vec3 pnPos = gl_in[0].gl_Position.xyz*uvwCubed[2] + + gl_in[1].gl_Position.xyz*uvwCubed[0] + + gl_in[2].gl_Position.xyz*uvwCubed[1] + + b210*uvwSquared[2]*uvw[0] + + b120*uvwSquared[0]*uvw[2] + + b201*uvwSquared[2]*uvw[1] + + b021*uvwSquared[0]*uvw[1] + + b102*uvwSquared[1]*uvw[2] + + b012*uvwSquared[1]*uvw[0] + + b111*6.0*uvw[0]*uvw[1]*uvw[2]; + + outColor = inColor; + // final position and normal + outWorldPos = (1.0 - tessAlpha) * barPos + tessAlpha * pnPos; + + gl_Position = ubo.projection * ubo.view * vec4 (outWorldPos, 1.0); +}
\ No newline at end of file diff --git a/shaders/shadow.geom b/shaders/shadow.geom index b3d66fc..c38bc4a 100644 --- a/shaders/shadow.geom +++ b/shaders/shadow.geom @@ -6,27 +6,27 @@ layout (triangles, invocations = NUM_LIGHTS) in; layout (triangle_strip, max_vertices = 3) out; struct Light { - vec4 position; - vec4 color; - mat4 mvp; + vec4 position; + vec4 color; + mat4 mvp; }; layout (set = 0, binding = 0) uniform UBO { - mat4 projection; - mat4 model; - mat4 view; + mat4 projection; + mat4 model; + mat4 view; }; layout (set = 0, binding = 1) uniform UBOLights { - Light lights[NUM_LIGHTS]; + Light lights[NUM_LIGHTS]; }; //layout (location = 0) in int inInstanceIndex[]; -void main() +void main() { for (int i = 0; i < gl_in.length(); i++) { - gl_Layer = gl_InvocationID; - gl_Position = lights[gl_InvocationID].mvp * model * gl_in[i].gl_Position; + gl_Layer = gl_InvocationID; + gl_Position = lights[gl_InvocationID].mvp * model * gl_in[i].gl_Position; EmitVertex(); } EndPrimitive(); diff --git a/src/Animation/AngleAnimation.cs b/src/Animation/AngleAnimation.cs index 543fe13..02faef7 100644 --- a/src/Animation/AngleAnimation.cs +++ b/src/Animation/AngleAnimation.cs @@ -1,44 +1,19 @@ -// -// AngleAnimation.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2015 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; namespace vke { - public class AngleAnimation : FloatAnimation + public class AngleAnimation : FloatAnimation2 { #region CTOR - public AngleAnimation(Object instance, string PropertyName, float Target, float step = 0.1f) : - base(instance,PropertyName,Target,step){} + public AngleAnimation(Object instance, string PropertyName, float Target, int stepCount = 20) : + base (instance,PropertyName,MathHelper.NormalizeAngle(Target),stepCount) { + initialValue = MathHelper.NormalizeAngle (initialValue); + } #endregion - - public override void Process() - { - base.Process(); - - float value = getValue(); - if (value < -MathHelper.TwoPi) - setValue(value + MathHelper.TwoPi); - else if (value >= MathHelper.TwoPi) - setValue(value - MathHelper.TwoPi); - } } } diff --git a/src/Animation/Animation.cs b/src/Animation/Animation.cs index 21b141a..6ca7af8 100644 --- a/src/Animation/Animation.cs +++ b/src/Animation/Animation.cs @@ -1,23 +1,6 @@ -// -// Animation.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2015 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections.Generic; @@ -86,7 +69,7 @@ namespace vke if (a.delayStartMs > 0) a.timer.Start (); - + AnimationList.Add (a); } @@ -102,14 +85,14 @@ namespace vke // if (frame % 20 == 0){ // foreach (Player p in MagicEngine.CurrentEngine.Players) { // foreach (CardInstance c in p.InPlay.Cards.Where(ci => ci.HasAbility(AbilityEnum.Flying) && ci.z < 0.4f)) { -// +// // } // } // } // #endregion //Stopwatch animationTime = new Stopwatch(); //animationTime.Start(); - + const int maxAnim = 200000; int count = 0; @@ -118,9 +101,9 @@ namespace vke if (anims.Count == 0) anims = new Stack<Animation> (AnimationList); } - + while (anims.Count > 0 && count < maxAnim) { - Animation a = anims.Pop (); + Animation a = anims.Pop (); if (a == null) continue; if (a.timer.IsRunning) { @@ -133,7 +116,7 @@ namespace vke a.Process (); count++; } - + //animationTime.Stop(); //Debug.WriteLine("animation: {0} ticks \t {1} ms ", animationTime.ElapsedTicks,animationTime.ElapsedMilliseconds); } @@ -141,7 +124,7 @@ namespace vke { for (int i = 0; i < AnimationList.Count; i++) { Animation anim = AnimationList [i]; - if (anim == null) { + if (anim == null) { continue; } if (anim.AnimatedInstance == instance && anim.propertyName == PropertyName) { @@ -155,7 +138,7 @@ namespace vke public virtual void Process () {} public void CancelAnimation() { - //Debug.WriteLine("Cancel anim: " + this.ToString()); + //Debug.WriteLine("Cancel anim: " + this.ToString()); AnimationList.Remove(this); } public void RaiseAnimationFinishedEvent() @@ -168,7 +151,5 @@ namespace vke { Debug.WriteLine ("\t\tAnimation finished: " + a.ToString ()); } - - } } diff --git a/src/Animation/AnimationTemplate.cs b/src/Animation/AnimationTemplate.cs index 995eb0a..d5c784c 100644 --- a/src/Animation/AnimationTemplate.cs +++ b/src/Animation/AnimationTemplate.cs @@ -1,23 +1,7 @@ -// -// FloatAnimation.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2015 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; using MiscUtil; using System.Reflection; diff --git a/src/Animation/BezierPath.cs b/src/Animation/BezierPath.cs index 5e381c4..a3126e3 100644 --- a/src/Animation/BezierPath.cs +++ b/src/Animation/BezierPath.cs @@ -1,23 +1,7 @@ -// -// BezierPath.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; using System.Numerics; diff --git a/src/Animation/FloatAnimation.cs b/src/Animation/FloatAnimation.cs index d0d1fd9..a7e4bf7 100644 --- a/src/Animation/FloatAnimation.cs +++ b/src/Animation/FloatAnimation.cs @@ -1,23 +1,7 @@ -// -// FloatAnimation.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2015 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; namespace vke @@ -48,7 +32,7 @@ namespace vke Step = -Step; } else if (Step > 0) - Step = -Step; + Step = -Step; } #endregion diff --git a/src/Animation/FloatAnimation2.cs b/src/Animation/FloatAnimation2.cs new file mode 100644 index 0000000..1d5c919 --- /dev/null +++ b/src/Animation/FloatAnimation2.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace vke +{ + public class FloatAnimation2 : Animation + { + + public float TargetValue; + protected float initialValue; + int stepCount, currentStep; + + #region CTOR + public FloatAnimation2(Object instance, string _propertyName, float Target, int _stepCount = 20) + : base(instance, _propertyName) + { + + TargetValue = Target; + + float value = getValue(); + initialValue = value; + + stepCount = _stepCount; + } + #endregion + + bool smooth = true; + + float smoothedStep (float step) => (-MathF.Cos (step * MathF.PI) + 1)/2.0f; + + public override void Process() + { + + currentStep++; + + float t = (float)currentStep / (float)stepCount; + if (smooth) + t = smoothedStep (t); + + setValue (initialValue + t * (TargetValue - initialValue)); + + if (currentStep < stepCount) + return; + + setValue(TargetValue); + AnimationList.Remove(this); + RaiseAnimationFinishedEvent (); + } + + public override string ToString () + { + return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,stepCount); + } + } + +} + diff --git a/src/Animation/MathHelper.cs b/src/Animation/MathHelper.cs index 6a066b6..35a3009 100644 --- a/src/Animation/MathHelper.cs +++ b/src/Animation/MathHelper.cs @@ -1,32 +1,16 @@ -// -// MathHelper.cs +// Copyright (c) 2019-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp_bruyere@hotmail.com> -// -// Copyright (c) 2019 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; namespace vke { public static class MathHelper { public const float Pi = (float)Math.PI; public const float TwoPi = (float)Math.PI * 2; + public static float NormalizeAngle (float a) { + float tmp = a % MathHelper.TwoPi; + if (tmp < 0) + tmp += MathHelper.TwoPi; + return tmp; + } } } diff --git a/src/Animation/Path.cs b/src/Animation/Path.cs index 6847e99..1f04e09 100644 --- a/src/Animation/Path.cs +++ b/src/Animation/Path.cs @@ -1,4 +1,7 @@ -using System.Numerics; +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System.Numerics; namespace vke { public class Path diff --git a/src/Animation/PathAnimation.cs b/src/Animation/PathAnimation.cs index 60b00a3..54c30e9 100644 --- a/src/Animation/PathAnimation.cs +++ b/src/Animation/PathAnimation.cs @@ -1,33 +1,16 @@ -// -// FloatAnimation.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2015 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; using System.Numerics; namespace vke { public class PathAnimation : Animation<Vector3> - { + { Path path; - int stepCount; - int currentStep; + int stepCount, currentStep; #region CTOR public PathAnimation(Object instance, string _propertyName, Path _path, int _stepCount = 20) @@ -39,17 +22,24 @@ namespace vke #endregion + bool smooth = true; + + float smoothedStep (float step) => (-MathF.Cos (step * MathF.PI) + 1)/2.0f; + public override void Process() { currentStep++; float t = (float)currentStep / (float)stepCount; + if (smooth) + t = smoothedStep (t); + Vector3 pos = path.GetStep (t); setValue(pos); if (currentStep < stepCount) return; - + AnimationList.Remove(this); RaiseAnimationFinishedEvent (); } diff --git a/src/Animation/ShakeAnimation.cs b/src/Animation/ShakeAnimation.cs index 26172b5..37a089b 100644 --- a/src/Animation/ShakeAnimation.cs +++ b/src/Animation/ShakeAnimation.cs @@ -1,23 +1,7 @@ -// -// ShakeAnimation.cs +// Copyright (c) 2015-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2015 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; namespace vke @@ -32,8 +16,8 @@ namespace vke #region CTOR public ShakeAnimation( - Object instance, - string _propertyName, + Object instance, + string _propertyName, float lowBound, float highBound) : base(instance, _propertyName) { @@ -50,10 +34,10 @@ namespace vke public override void Process () { - float value = getValue (); + float value = getValue (); float step = stepMin + (float)random.NextDouble () * stepMax; - if (rising) { + if (rising) { value += step; if (value > HighBound) { value = HighBound; diff --git a/src/MoveHistoryWidget.cs b/src/MoveHistoryWidget.cs new file mode 100644 index 0000000..963a89c --- /dev/null +++ b/src/MoveHistoryWidget.cs @@ -0,0 +1,201 @@ +// Copyright (c) 2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Xml.Serialization; +using System.ComponentModel; +using System.Collections; +using Crow.Drawing; +using Crow; +using System.Collections.Generic; + +namespace vkChess +{ + public class MoveHistoryWidget : ScrollingObject + { + IList<string> moves; + int visibleLines = 1; + FontExtents fe; + + public virtual IList<string> Moves { + get => moves; + set { + if (moves == value) + return; + if (moves is ObservableList<string> ol) { + ol.ListAdd -= Lines_ListAdd; + ol.ListRemove -= Lines_ListRemove; + ol.ListClear -= Lines_ListClear; + } + moves = value; + if (moves is ObservableList<string> olNew) { + olNew.ListAdd += Lines_ListAdd; + olNew.ListRemove += Lines_ListRemove; + olNew.ListClear += Lines_ListClear; + + updateMaxScrollAndFocusedIndices (); + } else + ScrollY = MaxScrollY = CurrentMoveIndex = hoverMoveIdx = 0; + NotifyValueChanged ("Moves", moves); + RegisterForGraphicUpdate (); + } + } + void updateMaxScrollAndFocusedIndices () { + MaxScrollY = moves.Count - visibleLines; + ScrollY = 0; + CurrentMoveIndex = 0; + hoverMoveIdx = - 1; + } + void Lines_ListAdd (object sender, ListChangedEventArg e) + { + updateMaxScrollAndFocusedIndices (); + RegisterForRedraw (); + } + + void Lines_ListRemove (object sender, ListChangedEventArg e) + { + updateMaxScrollAndFocusedIndices (); + RegisterForRedraw (); + } + void Lines_ListClear (object sender, ListClearEventArg e) + { + ScrollY = MaxScrollY = CurrentMoveIndex = 0; + hoverMoveIdx = -1; + RegisterForRedraw (); + } + + + + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Height) { + using (ImageSurface img = new ImageSurface (Format.Argb32, 10, 10)) { + using (Context gr = new Context (img)) { + //Cairo.FontFace cf = gr.GetContextFontFace (); + this.setFontForContext (gr); + fe = gr.FontExtents; + } + } + lineHeight = fe.Height + 2 * moveMargin + moveSpacing; + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / lineHeight); + MaxScrollY = moves == null ? 0 : moves.Count - visibleLines; + } + } + double lineHeight = 1; + + int moveMargin = 3, moveSpacing = 2; + + int hoverMoveIdx = -1, currentMoveIndex; + + public int CurrentMoveIndex { + get => currentMoveIndex; + set { + if (currentMoveIndex == value) + return; + currentMoveIndex = value; + NotifyValueChangedAuto (currentMoveIndex); + RegisterForRedraw (); + } + } + + protected override void onDraw (Context gr) + { + base.onDraw (gr); + + if (moves == null) + return; + + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + Rectangle r = ClientRectangle; + + + double y = ClientRectangle.Y; + double x = ClientRectangle.X - ScrollX; + + string[] movesCopy; + lock (moves) { + movesCopy = new string[moves.Count]; + moves.CopyTo (movesCopy, 0); + } + + + for (int i = 0; i < visibleLines; i++) { + int idx = i + ScrollY; + int mi = movesCopy.Length - (1 + idx); + if (mi < 0) + break; + string m = movesCopy[mi]; + Color bg; + Color fg; + if (mi % 2 > 0) { + if (idx == currentMoveIndex) { + bg = Colors.DarkBlue; + fg = Colors.White; + } else if (idx == hoverMoveIdx) { + bg = Colors.Black; + fg = Colors.White; + } else { + bg = Colors.Black; + fg = Colors.DimGrey; + } + } else { + if (idx == currentMoveIndex) { + bg = Colors.Blue; + fg = Colors.White; + } else if (idx == hoverMoveIdx) { + bg = Colors.White; + fg = Colors.Black; + } else { + bg = Colors.Grey; + fg = Colors.DarkGrey; + } + } + + gr.SetSource (bg); + gr.Rectangle (x, y, (double)r.Width, lineHeight - moveSpacing); + gr.Fill (); + + y += moveMargin; + + gr.SetSource (fg); + TextExtents te = gr.TextExtents(m); + gr.MoveTo (x + 0.5 * (r.Width - te.Width), y + fe.Ascent); + gr.ShowText (m); + y += fe.Height + moveMargin + moveSpacing; + } + } + public override void onMouseMove(object sender, MouseMoveEventArgs e) + { + base.onMouseMove(sender, e); + + if (Moves == null) { + hoverMoveIdx = -1; + return; + } + + PointD mouseLocalPos = ScreenPointToLocal (e.Position); + + hoverMoveIdx = ScrollY + (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / lineHeight)), moves.Count - 1); + RegisterForRedraw (); + } + public override void onMouseClick(object sender, MouseButtonEventArgs e) + { + if (e.Button == Glfw.MouseButton.Left) { + CurrentMoveIndex = hoverMoveIdx; + e.Handled = true; + } + base.onMouseClick(sender, e); + } + public override void onMouseLeave(object sender, MouseMoveEventArgs e) + { + hoverMoveIdx = -1; + base.onMouseLeave(sender, e); + } + } +} + diff --git a/src/Piece.cs b/src/Piece.cs index cc10324..f56a8c0 100644 --- a/src/Piece.cs +++ b/src/Piece.cs @@ -80,7 +80,7 @@ namespace vkChess { public Crow.Point BoardCell; public Vector3 Position { - get { return position; } + get => position; set { if (value == position) return; @@ -90,7 +90,7 @@ namespace vkChess { } public float X { - get { return position.X; } + get => position.X; set { if (position.X == value) return; @@ -99,7 +99,7 @@ namespace vkChess { } } public float Y { - get { return position.Y; } + get => position.Y; set { if (position.Y == value) return; @@ -108,7 +108,7 @@ namespace vkChess { } } public float Z { - get { return position.Z; } + get => position.Z; set { if (position.Z == value) return; @@ -117,7 +117,7 @@ namespace vkChess { } } public float XAngle { - get { return xAngle; } + get => xAngle; set { if (xAngle == value) return; @@ -126,7 +126,7 @@ namespace vkChess { } } public float ZAngle { - get { return zAngle; } + get => zAngle; set { if (zAngle == value) return; diff --git a/src/VkChess.cs b/src/VkChess.cs index ef7039b..f83b1d6 100644 --- a/src/VkChess.cs +++ b/src/VkChess.cs @@ -10,6 +10,8 @@ using vke.glTF; using Glfw; using Vulkan; using System.Reflection; +using System.Net; +using System.Runtime.InteropServices; namespace vkChess { @@ -50,6 +52,24 @@ namespace vkChess using (VkChess app = new VkChess ()) app.Run (); } + + public CommandGroup Commands => new CommandGroup ( + new CommandGroup ("Menu", + new Command ("New Game", () => loadWindow ("ui/newGame.crow", this)), + new Command ("Save", () => loadWindow ("ui/newGame.crow", this)), + new Command ("Load", () => loadWindow ("ui/newGame.crow", this)), + new Command ("Options", () => loadWindow ("ui/winOptions.crow", this)), + new Command ("Moves", () => loadWindow ("ui/winMoves.crow", this)), +#if DEBUG + new Command ("Log", () => loadWindow ("ui/winLog.crow", this)), +#endif + new Command ("Quit", () => Close()) + ), + new Command ("Undo", () => undo()) + //new Command ("Redo", () => Close()) + ); + + public override string [] EnabledInstanceExtensions => new string [] { #if DEBUG //Ext.I.VK_EXT_debug_utils, @@ -70,7 +90,9 @@ namespace vkChess #if PIPELINE_STATS enabled_features.pipelineStatisticsQuery = available_features.pipelineStatisticsQuery; #endif + enabled_features.tessellationShader = available_features.tessellationShader; } + #if PIPELINE_STATS PipelineStatisticsQueryPool statPool; TimestampQueryPool timestampQPool; @@ -84,79 +106,37 @@ namespace vkChess #if DEBUG //vke.DebugUtils.Messenger dbgmsg; #endif - protected override void CreateRenderPass () { - renderPass = new RenderPass (dev, swapChain.ColorFormat, DeferredPbrRendererBase.NUM_SAMPLES); - } + + Queue transferQ; GraphicPipeline plToneMap; DescriptorSetLayout dslToneMap; - protected override void CreateDescriptors () { - dsPool = EnableReflections ? - new DescriptorPool (dev, 1, - new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 4), - new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 1)) : - new DescriptorPool (dev, 1, - new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 2)); - descSet = dsPool.Allocate (dslToneMap); - } - protected override void CreatePipeline () { - using (GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, DeferredPbrRendererBase.NUM_SAMPLES)) { - if (DeferredPbrRendererBase.NUM_SAMPLES != VkSampleCountFlags.SampleCount1) { - cfg.multisampleState.sampleShadingEnable = true; - cfg.multisampleState.minSampleShading = 0.5f; - } - dslToneMap = new DescriptorSetLayout (dev, 0, - new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), - new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) - ); - if (EnableReflections) { - dslToneMap.Bindings.AddRange ( new VkDescriptorSetLayoutBinding[] { - new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), - new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), - new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer) - }); - cfg.Layout = new PipelineLayout (dev, - new VkPushConstantRange (VkShaderStageFlags.Fragment, 20u), dslToneMap); - } else - cfg.Layout = new PipelineLayout (dev, - new VkPushConstantRange (VkShaderStageFlags.Fragment, 8u), dslToneMap); - - cfg.RenderPass = renderPass; - cfg.AddShaders ( - new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv"), - new ShaderInfo (dev, VkShaderStageFlags.Fragment, - EnableReflections ? - "#vkChess.net.tone_mapping_ssr.frag.spv" : "#vkChess.net.tone_mapping.frag.spv") - ); - - plToneMap = new GraphicPipeline (cfg); - + bool updateRendererDescriptors; + public VkSampleCountFlags AvailableSampleCount => ~phy.Limits.framebufferColorSampleCounts; + public VkSampleCountFlags maxSampleCount { + get { + int sampleMask = (int)phy.Limits.framebufferColorSampleCounts; + for (int i = 6; i > 0; i--) + if ((sampleMask >> i) > 0) + return (VkSampleCountFlags)Math.Pow(2, i); + return VkSampleCountFlags.SampleCount1; } } - protected override void recordUICmd (PrimaryCommandBuffer cmd, int imageIndex) { - renderPass.Begin(cmd, frameBuffers[imageIndex]); - - cmd.SetViewport (frameBuffers[imageIndex].Width, frameBuffers[imageIndex].Height); - cmd.SetScissor (frameBuffers[imageIndex].Width, frameBuffers[imageIndex].Height); - - if (EnableReflections) { - cmd.PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 16, new float[] { Exposure, Gamma, SSRStep, SSRThreshold }, 0); - cmd.PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 4, SSRMaxStepCount, 16); - } else - cmd.PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 16, new float[] { Exposure, Gamma}, 0); - - plToneMap.BindDescriptorSet (cmd, descSet); - plToneMap.Bind (cmd); - cmd.Draw (3, 1, 0, 0); - - renderPass.End (cmd); + protected override void createQueues () { + base.createQueues (); + transferQ = new Queue (dev, VkQueueFlags.Transfer); } protected override void initVulkan () { - DeferredPbrRendererBase.NUM_SAMPLES = SampleCount; - initLog (); base.initVulkan (); + iFace.SetWindowIcon ("#Crow.Icons.crow.png"); + + VkSampleCountFlags maxSamples = maxSampleCount; + if (SampleCount > maxSamples) + SampleCount = maxSamples; + DeferredPbrRendererBase.NUM_SAMPLES = SampleCount; + #if DEBUG /*dbgmsg = new vke.DebugUtils.Messenger (instance, VkDebugUtilsMessageTypeFlagsEXT.PerformanceEXT | VkDebugUtilsMessageTypeFlagsEXT.ValidationEXT | VkDebugUtilsMessageTypeFlagsEXT.GeneralEXT, VkDebugUtilsMessageSeverityFlagsEXT.InfoEXT | @@ -174,7 +154,6 @@ namespace vkChess timestampQPool = new TimestampQueryPool (dev); #endif - //Configuration.Global.Set ("StockfishPath", "/usr/games/stockfish"); cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount); @@ -189,7 +168,7 @@ namespace vkChess // Matrix4x4.CreateScale(1f / Math.Max(Math.Max(renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); if (EnableReflections) { - DescriptorSetWrites dsw = new DescriptorSetWrites (descSet, + DescriptorSetWrites dsw = new DescriptorSetWrites (descriptorSet, dslToneMap.Bindings[4]); dsw.Write (dev, renderer.uboMatrices.Descriptor); } @@ -214,11 +193,89 @@ namespace vkChess camera.AspectRatio = Width / Height; camera.Model = Matrix4x4.CreateScale (0.5f); } - Queue transferQ; - protected override void createQueues () { - base.createQueues (); - transferQ = new Queue (dev, VkQueueFlags.Transfer); + + #region VKCrowWindow pipeline overrides + protected override void CreateRenderPass () { + renderPass = new RenderPass (dev, swapChain.ColorFormat, DeferredPbrRendererBase.NUM_SAMPLES); + } + protected override void CreateAndAllocateDescriptors () { + descriptorPool = EnableReflections ? + new DescriptorPool (dev, 1, + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 4), + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 1)) : + new DescriptorPool (dev, 1, + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 2)); + descriptorSet = descriptorPool.Allocate (dslToneMap); } + protected override void CreatePipeline () { + using (GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, DeferredPbrRendererBase.NUM_SAMPLES)) { + if (DeferredPbrRendererBase.NUM_SAMPLES != VkSampleCountFlags.SampleCount1) { + cfg.multisampleState.sampleShadingEnable = true; + cfg.multisampleState.minSampleShading = 0.5f; + } + dslToneMap = new DescriptorSetLayout (dev, 0, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + if (EnableReflections) { + dslToneMap.Bindings.AddRange ( new VkDescriptorSetLayoutBinding[] { + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer) + }); + cfg.Layout = new PipelineLayout (dev, + new VkPushConstantRange (VkShaderStageFlags.Fragment, 20u), dslToneMap); + } else + cfg.Layout = new PipelineLayout (dev, + new VkPushConstantRange (VkShaderStageFlags.Fragment, 8u), dslToneMap); + + cfg.RenderPass = renderPass; + cfg.AddShaders ( + new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv"), + new ShaderInfo (dev, VkShaderStageFlags.Fragment, + EnableReflections ? + "#vkChess.net.tone_mapping_ssr.frag.spv" : "#vkChess.net.tone_mapping.frag.spv") + ); + + plToneMap = new GraphicPipeline (cfg); + + } + } + protected override void buildCommandBuffer (PrimaryCommandBuffer cmd, int imageIndex) { + if (updateRendererDescriptors) { + if (EnableReflections) { + DescriptorSetWrites dsw = new DescriptorSetWrites (descriptorSet, + dslToneMap.Bindings[1], dslToneMap.Bindings[2], dslToneMap.Bindings[3]); + dsw.Write (dev, renderer.HDROutput.Descriptor, renderer.GBuffPosDepthOutput.Descriptor, renderer.GBuffN_AO.Descriptor); + } else { + DescriptorSetWrites dsw = new DescriptorSetWrites (descriptorSet, dslToneMap.Bindings[1]); + dsw.Write (dev, renderer.HDROutput.Descriptor); + } + updateRendererDescriptors = false; + } + + renderer?.recordDraw (cmd, imageIndex, instanceBuff, instancedCmds?.ToArray ()); + + + renderPass.Begin(cmd, frameBuffers[imageIndex]); + + cmd.SetViewport (frameBuffers[imageIndex].Width, frameBuffers[imageIndex].Height); + cmd.SetScissor (frameBuffers[imageIndex].Width, frameBuffers[imageIndex].Height); + + if (EnableReflections) { + cmd.PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 16, new float[] { Exposure, Gamma, SSRStep, SSRThreshold }, 0); + cmd.PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 4, SSRMaxStepCount, 16); + } else + cmd.PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 16, new float[] { Exposure, Gamma}, 0); + + plToneMap.BindDescriptorSet (cmd, descriptorSet); + plToneMap.Bind (cmd); + cmd.Draw (3, 1, 0, 0); + + renderPass.End (cmd); + } + #endregion + string [] cubemapPathes = { "data/textures/papermill.ktx", @@ -258,27 +315,6 @@ namespace vkChess public PbrModelTexArray.Material[] Materials => (renderer.model as PbrModelTexArray).materials; - - void buildCommandBuffers () { - dev.WaitIdle (); - - cmdPool.Reset (); //VkCommandPoolResetFlags.ReleaseResources); - - for (int i = 0; i < swapChain.ImageCount; ++i) { - cmds [i].Start (); -#if PIPELINE_STATS - statPool.Begin (cmds[i]); - renderer.recordDraw (cmds[i], i, instanceBuff, instancedCmds?.ToArray ()); - statPool.End (cmds[i]); -#else - renderer?.recordDraw (cmds [i], i, instanceBuff, instancedCmds?.ToArray ()); - - recordUICmd (cmds[i], i); -#endif - cmds [i].End (); - } - } - public override void UpdateView () { dev.WaitIdle (); if (updateViewRequested) { @@ -296,7 +332,6 @@ namespace vkChess const int fpsAccumLimit = 20; - public override void Update () { base.Update (); @@ -342,19 +377,11 @@ namespace vkChess } protected override void OnResize () { - base.OnResize (); renderer.Resize (); - if (EnableReflections) { - DescriptorSetWrites dsw = new DescriptorSetWrites (descSet, - dslToneMap.Bindings[1], dslToneMap.Bindings[2], dslToneMap.Bindings[3]); - dsw.Write (dev, renderer.HDROutput.Descriptor, renderer.GBuffPosDepthOutput.Descriptor, renderer.GBuffN_AO.Descriptor); - } else { - DescriptorSetWrites dsw = new DescriptorSetWrites (descSet, dslToneMap.Bindings[1]); - dsw.Write (dev, renderer.HDROutput.Descriptor); - } + updateRendererDescriptors = true; + base.OnResize (); - buildCommandBuffers (); updateViewRequested = true; } @@ -412,9 +439,7 @@ namespace vkChess CameraPosition = new Vector3 (camera.Position.X, camera.Position.Y, -camera.Position.Z); } protected override void onMouseMove (double xPos, double yPos) { - base.onMouseMove (xPos, yPos); - - if (MouseIsInInterface) + if (iFace.OnMouseMove ((int)xPos, (int)yPos)) return; double diffX = lastMouseX - xPos; @@ -454,9 +479,9 @@ namespace vkChess NotifyValueChanged ("DebugCurCellPos", p?.BoardCell); if (p != null) { - NotifyValueChanged ("DebugCurCell", getChessCell (p.BoardCell.X, p.BoardCell.Y)); + NotifyValueChanged ("DebugCurCell", (object)getChessCell (p.BoardCell.X, p.BoardCell.Y)); }else - NotifyValueChanged ("DebugCurCell", ""); + NotifyValueChanged ("DebugCurCell", (object)""); } protected override void onMouseButtonDown (MouseButton button) { base.onMouseButtonDown (button); @@ -619,13 +644,15 @@ namespace vkChess EnableHint = false; //if (currentState != GameState.Pad && currentState != GameState.Checkmate && !playerIsAi (CurrentPlayer) && playerIsAi (Opponent))//undo ai move - if (!playerIsAi (CurrentPlayer) && playerIsAi (Opponent)) + if (currentState < GameState.Pad || (!playerIsAi (CurrentPlayer) && playerIsAi (Opponent))) undoLastMove (); undoLastMove (); startTurn (); NotifyValueChanged ("board", board); + NotifyValueChanged ("CurrentState", currentState); + NotifyValueChanged ("CurrentStateColor", CurrentStateColor); EnableHint = hintIsEnabled; } @@ -645,20 +672,6 @@ namespace vkChess }; } - public CommandGroup Commands => new CommandGroup ( - new CommandGroup ("Menu", - new Command ("New Game", () => loadWindow ("ui/newGame.crow", this)), - new Command ("Save", () => loadWindow ("ui/newGame.crow", this)), - new Command ("Load", () => loadWindow ("ui/newGame.crow", this)), - new Command ("Options", () => loadWindow ("ui/winOptions.crow", this)), -#if DEBUG - new Command ("Log", () => loadWindow ("ui/winLog.crow", this)), -#endif - new Command ("Quit", () => Close()) - ), - new Command ("Undo", () => undo()) - //new Command ("Redo", () => Close()) - ); void terminateCurrentGame () { if (CurrentState < GameState.Play) return; @@ -671,10 +684,6 @@ namespace vkChess System.Threading.Thread.Sleep (10); } } - public float CameraAngleY { - get => CameraRotation.Y; - set => CameraRotation = new Vector3(CameraRotation.X, value, CameraRotation.Z); - } void onNewWhiteGame (object sender, MouseButtonEventArgs e) { closeWindow ("ui/newGame.crow"); if (CurrentState < GameState.Play) @@ -696,7 +705,7 @@ namespace vkChess if (enableHint) sendToStockfish ("go infinite"); - Animation.StartAnimation (new AngleAnimation (this, "CameraAngleY", 0, 0.15f)); + Animation.StartAnimation (new AngleAnimation (this, "CameraAngleY", 0, 50)); } void onNewBlackGame (object sender, MouseButtonEventArgs e) { closeWindow ("ui/newGame.crow"); @@ -721,7 +730,7 @@ namespace vkChess else sendToStockfish ("go depth 1"); - Animation.StartAnimation (new AngleAnimation (this, "CameraAngleY", MathHelper.Pi, 0.15f)); + Animation.StartAnimation (new AngleAnimation (this, "CameraAngleY", MathHelper.Pi, 50)); } void onPromoteToQueenClick (object sender, MouseButtonEventArgs e) { closeWindow ("ui/promote.crow"); @@ -867,7 +876,6 @@ namespace vkChess } } - float normalizeAngle (float a) => a - (int)(a / MathHelper.TwoPi); public Vector3 CameraRotation { get => ExtensionMethods.ParseVec3 (Configuration.Global.Get<string> ("CameraRotation", "<0.6,0,0>")); set { @@ -875,9 +883,9 @@ namespace vkChess return; Vector3 tmp = new Vector3 ( - normalizeAngle (value.X), - normalizeAngle (value.Y), - normalizeAngle (value.Z) + MathHelper.NormalizeAngle (value.X), + MathHelper.NormalizeAngle (value.Y), + MathHelper.NormalizeAngle (value.Z) ); Configuration.Global.Set ("CameraRotation", tmp.ToString()); NotifyValueChanged ("CameraRotation", tmp); @@ -885,6 +893,10 @@ namespace vkChess updateViewRequested = true; } } + public float CameraAngleY { + get => CameraRotation.Y; + set => CameraRotation = new Vector3(CameraRotation.X, value, CameraRotation.Z); + } public Vector3 CameraPosition { get => ExtensionMethods.ParseVec3 (Configuration.Global.Get<string> ("CameraPosition", "<0,0,12>")); set { @@ -942,6 +954,39 @@ namespace vkChess p.SetColor (value); } } + public bool OptColorIsExpanded { + get => Configuration.Global.Get ("OptColorIsExpanded", false); + set { + if (OptColorIsExpanded == value) + return; + Configuration.Global.Set ("OptColorIsExpanded", value); + } + } + public bool OptRenderingIsExpanded { + get => Configuration.Global.Get ("OptRenderingIsExpanded", false); + set { + if (OptRenderingIsExpanded == value) + return; + Configuration.Global.Set ("OptRenderingIsExpanded", value); + } + } + public bool OptCameraIsExpanded { + get => Configuration.Global.Get ("OptCameraIsExpanded", false); + set { + if (OptCameraIsExpanded == value) + return; + Configuration.Global.Set ("OptCameraIsExpanded", value); + } + } + public bool OptGameIsExpanded { + get => Configuration.Global.Get ("OptGameIsExpanded", true); + set { + if (OptGameIsExpanded == value) + return; + Configuration.Global.Set ("OptGameIsExpanded", value); + } + } + #endregion @@ -964,12 +1009,46 @@ namespace vkChess } #endregion + #region move history show + int historyIdx = 0; + public int HistoryMoveIndex { + get => historyIdx; + set { + if (historyIdx == value) + return; + + if (historyIdx == 0) { + //save current positions + savedMoves = StockfishMoves; + } + + historyIdx = value; + NotifyValueChanged (historyIdx); + + if (historyIdx == 0) { + //restore current saved positions + StockfishMoves = savedMoves; + savedMoves = null; + resync3DScene (); + startTurn (); + } else { + StockfishMoves = savedMoves.Take (savedMoves.Count - historyIdx).ToList(); + resync3DScene (); + } + } + } + + IList<string> savedMoves; + + + #endregion + #region Stockfish object movesMutex = new object (); Process stockfish; volatile bool waitAnimationFinished; //Queue<string> stockfishCmdQueue = new Queue<string> (); - List<String> stockfishMoves = new List<string> (); + IList<String> stockfishMoves = new ObservableList<string> (); bool enableHint; public bool EnableHint { @@ -1004,7 +1083,7 @@ namespace vkChess if (value == StockfishPath) return; Configuration.Global.Set ("StockfishPath", value); - NotifyValueChanged ("StockfishPath", value); + NotifyValueChanged (value); initStockfish (); } @@ -1086,11 +1165,13 @@ namespace vkChess } } - public List<String> StockfishMoves { + public IList<String> StockfishMoves { get => stockfishMoves; set { stockfishMoves = value; } } void saveCurrentGame() { + if (historyIdx > 0) + StockfishMoves = savedMoves; Configuration.Global.Set ("CurrentGame", stockfishPositionCommand); } void loadCurrentGame() { @@ -1100,7 +1181,7 @@ namespace vkChess string curGame = Configuration.Global.Get<string> ("CurrentGame"); if (string.IsNullOrEmpty (curGame)) return; - stockfishMoves = curGame.Split (' ').ToList(); + stockfishMoves = new ObservableList<string> (curGame.Split (' ')); if (currentState < GameState.Play) return; @@ -1116,7 +1197,6 @@ namespace vkChess syncStockfish(); } - void initStockfish () { if (currentState == GameState.Init) throw new Exception ("init stockfish impossible: 3d scene must be initialized first."); @@ -1133,6 +1213,17 @@ namespace vkChess stockfish = null; } + if (string.IsNullOrEmpty (StockfishPath)) { + if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) + StockfishPath = "./stockfish_14_x64"; + else if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + if (RuntimeInformation.OSArchitecture == Architecture.X64) + StockfishPath = "stockfish_14_x64.exe"; + else if (RuntimeInformation.OSArchitecture == Architecture.X86) + StockfishPath = "stockfish_14_32bit.exe"; + } + } + if (!File.Exists (StockfishPath)) { NotifyValueChanged ("StockfishNotFound", true); return; @@ -1159,7 +1250,6 @@ namespace vkChess void syncStockfish () { if (currentState < GameState.Play) return; - NotifyValueChanged ("StockfishMoves", StockfishMoves); sendToStockfish ("setoption name Skill Level value " + (CurrentPlayer == ChessColor.White ? WhitesLevel.ToString() : BlacksLevel.ToString())); //sendToStockfish ($"setoption name nodestime value 10"); sendToStockfish ($"position startpos moves {stockfishPositionCommand}"); @@ -1243,7 +1333,7 @@ namespace vkChess case "Stockfish": //stockfish starting CurrentState = GameState.StockFishStarted; - NotifyValueChanged("StockfishVersion", tmp[1]); + NotifyValueChanged("StockfishVersion", (object)tmp[1]); sendToStockfish ("uci"); break; } @@ -1316,18 +1406,34 @@ namespace vkChess currentState = value; - if (currentState > GameState.Play) { + if (currentState > GameState.Pad) { Point kPos = CurrentPlayerPieces.First (p => p.Type == PieceType.King).BoardCell; Piece.UpdateCase (kPos.X, kPos.Y, kingCheckedColor); if (currentState == GameState.Checkmate) { Piece king = CurrentPlayerPieces.First (p => p.Type == PieceType.King); - Animation.StartAnimation (new FloatAnimation (king, "Z", 0.4f, 0.04f)); - Animation.StartAnimation (new AngleAnimation (king, "XAngle", MathHelper.Pi * 0.55f, 0.09f)); - Animation.StartAnimation (new AngleAnimation (king, "ZAngle", king.ZAngle + 0.3f, 0.5f)); + Animation.StartAnimation (new FloatAnimation2 (king, "Z", 0.4f, 20)); + Animation.StartAnimation (new AngleAnimation (king, "XAngle", MathHelper.Pi * 0.55f)); + Animation.StartAnimation (new AngleAnimation (king, "ZAngle", king.ZAngle + 0.3f)); } } NotifyValueChanged ("CurrentState", currentState); + NotifyValueChanged ("CurrentStateColor", CurrentStateColor); + } + } + Color CurrentStateColor { + get { + switch (currentState) { + case GameState.Play: + return Colors.DarkGrey; + case GameState.Pad: + return Colors.DarkMagenta; + case GameState.Checked: + return Colors.DarkOrange; + case GameState.Checkmate: + return Colors.DarkRed; + } + return Colors.DarkSlateGrey; } } ChessColor currentPlayer; @@ -1343,15 +1449,12 @@ namespace vkChess } } - public ChessColor Opponent { - get { return CurrentPlayer == ChessColor.White ? ChessColor.Black : ChessColor.White; } - } - public Piece [] OpponentPieces { - get { return CurrentPlayer == ChessColor.White ? blackPieces : whitePieces; } - } - public Piece [] CurrentPlayerPieces { - get { return CurrentPlayer == ChessColor.White ? whitePieces : blackPieces; } - } + public ChessColor Opponent => + CurrentPlayer == ChessColor.White ? ChessColor.Black : ChessColor.White; + public Piece [] OpponentPieces => + CurrentPlayer == ChessColor.White ? blackPieces : whitePieces; + public Piece [] CurrentPlayerPieces + => CurrentPlayer == ChessColor.White ? whitePieces : blackPieces; public int GetPawnPromotionY (ChessColor color) => color == ChessColor.White ? 7 : 0; Point Active { @@ -1374,11 +1477,11 @@ namespace vkChess } if (active < 0) { - NotifyValueChanged ("ActCell", ""); + NotifyValueChanged ("ActCell", (object)""); return; } - NotifyValueChanged ("ActCell", getChessCell (active.X, active.Y)); + NotifyValueChanged ("ActCell", (object)getChessCell (active.X, active.Y)); Piece.UpdateCase (active.X, active.Y, activeColor); ValidPositionsForActivePce = new List<Point> (); @@ -1429,7 +1532,7 @@ namespace vkChess if (selection >= 0) Piece.UpdateCase (selection.X, selection.Y, selectionPosColor); - NotifyValueChanged ("SelCell", getChessCell (selection.X, selection.Y)); + NotifyValueChanged ("SelCell", (object)getChessCell (selection.X, selection.Y)); } } int[] mesheIndexes; @@ -1441,7 +1544,6 @@ namespace vkChess cptWhiteOut = 0; cptBlackOut = 0; StockfishMoves.Clear (); - NotifyValueChanged ("StockfishMoves", StockfishMoves); Active = -1; @@ -1554,7 +1656,6 @@ namespace vkChess cptWhiteOut = 0; cptBlackOut = 0; StockfishMoves.Clear (); - NotifyValueChanged ("StockfishMoves", StockfishMoves); Active = -1; board = new Piece [8, 8]; @@ -1985,7 +2086,6 @@ namespace vkChess enPassant = true; StockfishMoves.Add (move); - NotifyValueChanged ("StockfishMoves", StockfishMoves); board [pStart.X, pStart.Y] = null; Point pTarget = pEnd; @@ -2035,6 +2135,12 @@ namespace vkChess if (currentState == GameState.Checked || currentState == GameState.Checkmate) { Point kPos = CurrentPlayerPieces.First (p => p.Type == PieceType.King).BoardCell; Piece.UpdateCase (kPos.X, kPos.Y, -kingCheckedColor); + if (currentState == GameState.Checkmate) { + Piece king = CurrentPlayerPieces.First (p => p.Type == PieceType.King); + Animation.StartAnimation (new FloatAnimation2 (king, "Z", 0, 20)); + Animation.StartAnimation (new AngleAnimation (king, "XAngle", 0)); + Animation.StartAnimation (new AngleAnimation (king, "ZAngle", 0)); + } } string move = StockfishMoves [StockfishMoves.Count - 1]; @@ -2079,7 +2185,7 @@ namespace vkChess resetBoard (false); foreach (string m in moves) { processMove (m, false); - CurrentPlayer = Opponent; + currentPlayer = Opponent; } } diff --git a/src/deferredPbrRendererBase.cs b/src/deferredPbrRendererBase.cs index a647179..9e18607 100644 --- a/src/deferredPbrRendererBase.cs +++ b/src/deferredPbrRendererBase.cs @@ -213,7 +213,8 @@ namespace vkChess init_renderpass (); descLayoutMain = new DescriptorSetLayout (dev, - new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),//matrices and params + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment | + VkShaderStageFlags.TessellationControl | VkShaderStageFlags.TessellationEvaluation, VkDescriptorType.UniformBuffer),//matrices and params new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),//irradiance new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),//prefil new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),//lut brdf @@ -265,9 +266,16 @@ namespace vkChess new SpecializationConstant<float> (0, nearPlane), new SpecializationConstant<float> (1, farPlane), new SpecializationConstant<int> (2, MAX_MATERIAL_COUNT))) { + cfg.inputAssemblyState.topology = VkPrimitiveTopology.PatchList; + + cfg.AddShader (dev, VkShaderStageFlags.TessellationControl, "#vkChess.net.pntriangles.tesc.spv"); + cfg.AddShader (dev, VkShaderStageFlags.TessellationEvaluation, "#vkChess.net.pntriangles.tese.spv"); cfg.AddShader (dev, VkShaderStageFlags.Vertex, "#vkChess.net.GBuffPbrInstanced.vert.spv"); + cfg.TessellationPatchControlPoints = 3; + cfg.SubpassIndex = SP_DEPTH_PREPASS; + depthPrepassPipeline = new GraphicPipeline (cfg, "Depth prepass"); cfg.depthStencilState.depthCompareOp = VkCompareOp.Equal; @@ -276,11 +284,12 @@ namespace vkChess cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); cfg.SubpassIndex = SP_MODELS; - cfg.AddShader (dev, VkShaderStageFlags.Fragment, "#vkChess.net.GBuffPbrTexArray.frag.spv", constants); + gBuffPipeline = new GraphicPipeline (cfg, "GBuff"); } + cfg.inputAssemblyState.topology = VkPrimitiveTopology.TriangleList; cfg.rasterizationState.cullMode = VkCullModeFlags.Front; //COMPOSE PIPELINE cfg.blendAttachments.Clear (); diff --git a/ui/chess.crow b/ui/chess.crow index 4227d4d..6657492 100644 --- a/ui/chess.crow +++ b/ui/chess.crow @@ -16,9 +16,9 @@ Background="Transparent" Foreground="White" Unchecked = "{Background=Transparent}"/> <Widget Width="Stretched"/> - <VerticalStack Style="hsStatus"> + <VerticalStack Style="hsStatus" Width="160"> <Label Style="labStatus" Text="State:" /> - <Label Style="labStatus2" Text="{CurrentState}" /> + <Label Style="labStatus2" Text="{CurrentState}" Background="{CurrentStateColor}"/> </VerticalStack> <VerticalStack Style="hsStatus"> <Label Style="labStatus" Text="Hover:" /> diff --git a/ui/winMoves.crow b/ui/winMoves.crow new file mode 100644 index 0000000..5a80573 --- /dev/null +++ b/ui/winMoves.crow @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<Window Caption="Moves" Top="10" Left="10" Width="100" Height="50%" > + <VerticalStack> + <MoveHistoryWidget Margin="10" Moves="{StockfishMoves}" CurrentMoveIndex="{²HistoryMoveIndex}"/> + </VerticalStack> +</Window> + diff --git a/ui/winOptions.crow b/ui/winOptions.crow index 050aa98..5d58924 100644 --- a/ui/winOptions.crow +++ b/ui/winOptions.crow @@ -12,7 +12,7 @@ </HorizontalStack> </VerticalStack> <!--<HorizontalStack Height="Fit">--> - <Expandable Caption="Game" IsExpanded="false"> + <Expandable Caption="Game" IsExpanded="{²OptGameIsExpanded}"> <VerticalStack Height="Fit" Spacing="3" Margin="10"> <HorizontalStack Height="Fit"> <Label Width="Stretched" Text="Whites Level:"/> @@ -50,7 +50,7 @@ </HorizontalStack> </VerticalStack> </HorizontalStack>--> - <Expandable Caption="Camera" IsExpanded="false"> + <Expandable Caption="Camera" IsExpanded="{²OptCameraIsExpanded}"> <VerticalStack Height="Fit" Margin="10"> <Vector3Widget Caption="Rotation" Value="{²CameraRotation}" SmallIncrement="0.01" LargeIncrement="0.1" Minimum="-7.0" Maximum="7.0" Decimals="2"/> <Vector3Widget Caption="Position" Value="{²CameraPosition}" SmallIncrement="0.1" LargeIncrement="1.0" Minimum="-20.0" Maximum="20.0" Decimals="1"/> @@ -60,7 +60,7 @@ </HorizontalStack> </VerticalStack> </Expandable> - <Expandable Caption="Rendering" IsExpanded="false"> + <Expandable Caption="Rendering" IsExpanded="{²OptRenderingIsExpanded}"> <VerticalStack Height="Fit" Margin="10"> <HorizontalStack Style="Slid" > <Label Text="Exposure:" Width="100"/> @@ -75,7 +75,7 @@ <Slider Style="LightingSlider" Value="{²IBLAmbient}" Maximum="50.0" SmallIncrement="0.01" LargeIncrement="0.1"/> </HorizontalStack> <CheckBox Caption="Enable Reflections" IsChecked="{²EnableReflections}" /> - <EnumSelector Caption="Sample count" EnumValue="{²SampleCount}" Height="Fit" Width="Stretched"> + <EnumSelector Caption="Sample count" ForceRadioButton="true" BitFieldExcludeMask="{AvailableSampleCount}" EnumValue="{²SampleCount}" Height="Fit" Width="Stretched"> <Template> <GroupBox Caption="{./Caption}" CornerRadius="{./CornerRadius}" Foreground="{./Foreground}" Background="{./Background}"> <Wrapper Margin="5" Orientation="Vertical" Height="Fit" Width="Stretched" Name="Content"/> @@ -84,7 +84,7 @@ </EnumSelector> </VerticalStack> </Expandable> - <Expandable Caption="Colors" IsExpanded="false"> + <Expandable Caption="Colors" IsExpanded="{²OptColorIsExpanded}"> <VerticalStack Height="Fit" Margin="10"> <Vector3Widget Caption="Black color" Value="{²BlackColor}" SmallIncrement="0.01" LargeIncrement="0.1" Maximum="1.0" Decimals="2"/> <Vector3Widget Caption="White color" Value="{²WhiteColor}" SmallIncrement="0.01" LargeIncrement="0.1" Maximum="1.0" Decimals="2"/> diff --git a/vkChess.net.csproj b/vkChess.net.csproj index d030901..a79c014 100644 --- a/vkChess.net.csproj +++ b/vkChess.net.csproj @@ -11,7 +11,7 @@ </PropertyGroup> <ItemGroup> - <GLSLShader Include="shaders\**\*.frag;shaders\**\*.vert;shaders\**\*.comp;shaders\**\*.geom" /> + <GLSLShader Include="shaders\**\*.frag;shaders\**\*.vert;shaders\**\*.comp;shaders\**\*.geom;shaders\**\*.tesc;shaders\**\*.tese" /> <None Include="data\**\*.*;ui\**\*.*"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> @@ -19,7 +19,7 @@ </ItemGroup> <ItemGroup> <PackageReference Include="VkCrowWindow" Version="0.3.4-beta" /> - <PackageReference Include="vke" Version="0.2.2-beta" /> + <PackageReference Include="vke" Version="0.2.4-beta" /> <PackageReference Include="SpirVTasks" Version="0.1.44" /> <PackageReference Include="vke.EnvironmentPipeline" Version="0.2.0-beta" /> <PackageReference Include="vke.gltfLoader" Version="0.2.0-beta" /> |