添加复制的资源

This commit is contained in:
xsl
2025-09-23 20:03:06 +08:00
parent 48517e685d
commit 599e041732
60 changed files with 1570 additions and 0 deletions
@@ -0,0 +1,120 @@
{
"accessors" : [
{
"bufferView" : 0,
"componentType" : 5126,
"count" : 24,
"type" : "VEC3"
},
{
"bufferView" : 1,
"componentType" : 5126,
"count" : 24,
"max" : [
1,
1,
1
],
"min" : [
-1,
-1,
-1
],
"type" : "VEC3"
},
{
"bufferView" : 2,
"componentType" : 5126,
"count" : 24,
"type" : "VEC4"
},
{
"bufferView" : 3,
"componentType" : 5126,
"count" : 24,
"type" : "VEC2"
},
{
"bufferView" : 4,
"componentType" : 5121,
"count" : 36,
"type" : "SCALAR"
}
],
"asset" : {
"generator" : "Khronos Blender glTF 2.0 I/O",
"version" : "2.0"
},
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 0
},
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 288
},
{
"buffer" : 0,
"byteLength" : 384,
"byteOffset" : 576
},
{
"buffer" : 0,
"byteLength" : 192,
"byteOffset" : 960
},
{
"buffer" : 0,
"byteLength" : 36,
"byteOffset" : 1152
}
],
"buffers" : [
{
"byteLength" : 1188,
"uri" : "data:application/octet-stream;base64,AACAvwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AACAPwAAAAAAAACAAACAPwAAAAAAAACAAACAPwAAAAAAAACAAAAAgAAAAIAAAIA/AAAAgAAAAIAAAIA/AAAAgAAAAIAAAIA/AAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAIAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgL8AAACAAAAAAAAAgD8AAACAAACAvwAAgD8AAIC/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAPwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAvwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIA/AACAPwAAgD8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIC/AAAAAAAAAAAAAIC/AACAvwAAAAAAAAAAAACAvwAAgL8AAAAAAAAAAAAAgL8AAIC/AACAPwAAAAAAAACAAACAvwAAgD8AAAAAAAAAgAAAgL8AAIA/AAAAAAAAAIAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAvwAAgD8AAAAAAAAAAAAAgL8AAIA/AACAPwAAAAAAAACAAACAPwAAgD8AAAAAAAAAgAAAgD8AAIA/AAAAAAAAAIAAAIA/AACAPwAAAAAAAACAAACAvwAAgD8AAAAAAAAAgAAAgL8AAIA/AAAAAAAAAIAAAIC/AACAPwAAAAAAAACAAACAPwAAgD8AAAAAAAAAgAAAgD8AAIA/AAAAAAAAAIAAAIA/AAAAAAAAAAAAAIC/AACAvwAAgD8AAAAAAAAAgAAAgL8AAAAAAAAAAAAAgL8AAIA/AACAPwAAAAAAAACAAACAPwAAgD8AAAAAAAAAgAAAgL8AAIA/AAAAAAAAAIAAAIA/AAAAAAAAgD8AAIC/AAAAQAAAgL8AAIA/AAAAAAAAgD8AAIC/AAAAQAAAgL8AAIA/AAAAAAAAgD8AAIA/AAAAQAAAgD8AAIA/AAAAAAAAgD8AAIA/AAAAQAAAgD8AAIA/AAAAAAAAgD8AAIA/AAAAQAAAAAAAAABAAAAAAAAAgD8AAIC/AAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAIA/AACAPwAAgL8AAIA/AAECAwQFBgcICQoLDA0ODxARABIBAxMEBhQHCRUKDBYNDxcQ"
}
],
"extensions" : {},
"materials" : [
{
"name" : "Material",
"pbrMetallicRoughness" : {}
}
],
"meshes" : [
{
"name" : "Cube.001",
"primitives" : [
{
"attributes" : {
"NORMAL" : 0,
"POSITION" : 1,
"TANGENT" : 2,
"TEXCOORD_0" : 3
},
"indices" : 4,
"material" : 0
}
]
}
],
"nodes" : [
{
"mesh" : 0,
"name" : "Cube"
}
],
"scene" : 0,
"scenes" : [
{
"name" : "Scene",
"nodes" : [
0
]
}
]
}
@@ -0,0 +1,34 @@
////
- Copyright (c) 2024-2025, Sascha Willems
-
- SPDX-License-Identifier: Apache-2.0
-
- Licensed under the Apache License, Version 2.0 the "License";
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
////
= Shaders
== Shader languages
This folder contains the textual shaders for the samples. All samples come with GLSL shaders and some optionally with HLSL shaders. For samples that support both shader language this is a good way to compare GLSL to HLSL syntax when targeting SPIR-V for Vulkan.
== Compiling shaders
The samples load offline compiled SPIR-V variants of the GLSL/HLSL shaders. If you have the appropriate compiler installed, e.g. via the LunarG VUlkan SDK, shaders will be automatically compiled when building the samples project.
== Further information
The xref:guide:ROOT:index.adoc[Vulkan Guide] contains further information on how to use HLSL with Vulkan and how it compares to GLSL:
* xref:guide::hlsl.adoc[HLSL in Vulkan]
* xref:guide::high_level_shader_language_comparison.adoc[High level shading language comparison]
@@ -0,0 +1,89 @@
#version 320 es
/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
precision highp float;
layout(set = 0, binding = 0) uniform sampler2D base_color_texture;
layout(location = 0) in vec4 in_pos;
layout(location = 1) in vec2 in_uv;
layout(location = 2) in vec3 in_normal;
layout(location = 0) out vec4 o_color;
layout(set = 0, binding = 1) uniform GlobalUniform
{
mat4 model;
mat4 view_proj;
vec3 camera_position;
}
global_uniform;
// Push constants come with a limitation in the size of data.
// The standard requires at least 128 bytes
layout(push_constant, std430) uniform PBRMaterialUniform
{
vec4 base_color_factor;
float metallic_factor;
float roughness_factor;
}
pbr_material_uniform;
#include "lighting.h"
layout(set = 0, binding = 4) uniform LightsInfo
{
Light directional_lights[48];
Light point_lights[48];
Light spot_lights[48];
}
lights_info;
layout(constant_id = 0) const uint DIRECTIONAL_LIGHT_COUNT = 0U;
layout(constant_id = 1) const uint POINT_LIGHT_COUNT = 0U;
layout(constant_id = 2) const uint SPOT_LIGHT_COUNT = 0U;
void main(void)
{
vec3 normal = normalize(in_normal);
vec3 light_contribution = vec3(0.0);
for (uint i = 0U; i < DIRECTIONAL_LIGHT_COUNT; ++i)
{
light_contribution += apply_directional_light(lights_info.directional_lights[i], normal);
}
for (uint i = 0U; i < POINT_LIGHT_COUNT; ++i)
{
light_contribution += apply_point_light(lights_info.point_lights[i], in_pos.xyz, normal);
}
for (uint i = 0U; i < SPOT_LIGHT_COUNT; ++i)
{
light_contribution += apply_spot_light(lights_info.spot_lights[i], in_pos.xyz, normal);
}
vec4 base_color = vec4(1.0, 0.0, 0.0, 1.0);
base_color = texture(base_color_texture, in_uv);
vec3 ambient_color = vec3(0.2) * base_color.xyz;
o_color = vec4(ambient_color + light_contribution * base_color.xyz, base_color.w);
}
Binary file not shown.
@@ -0,0 +1,42 @@
#version 320 es
/* Copyright (c) 2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texcoord_0;
layout(location = 2) in vec3 normal;
layout(set = 0, binding = 1) uniform GlobalUniform {
mat4 model;
mat4 view_proj;
vec3 camera_position;
} global_uniform;
layout (location = 0) out vec4 o_pos;
layout (location = 1) out vec2 o_uv;
layout (location = 2) out vec3 o_normal;
void main(void)
{
o_pos = global_uniform.model * vec4(position, 1.0);
o_uv = texcoord_0;
o_normal = mat3(global_uniform.model) * normal;
gl_Position = global_uniform.view_proj * o_pos;
}
Binary file not shown.
@@ -0,0 +1,30 @@
#version 320 es
/* Copyright (c) 2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
precision mediump float;
layout (binding = 0) uniform sampler2D fontSampler;
layout (location = 0) in vec2 inUV;
layout (location = 1) in vec4 inColor;
layout (location = 0) out vec4 outColor;
void main()
{
outColor = inColor * texture(fontSampler, inUV);
}
Binary file not shown.
@@ -0,0 +1,42 @@
#version 320 es
/* Copyright (c) 2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
precision mediump float;
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUV;
layout (location = 2) in vec4 inColor;
layout (push_constant) uniform PushConstants {
mat4 transform;
} pushConstants;
layout (location = 0) out vec2 outUV;
layout (location = 1) out vec4 outColor;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outUV = inUV;
outColor = inColor;
gl_Position = pushConstants.transform * vec4(inPos.xy, 0.0, 1.0);
}
Binary file not shown.
@@ -0,0 +1,52 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
struct Light
{
vec4 position; // position.w represents type of light
vec4 color; // color.w represents light intensity
vec4 direction; // direction.w represents range
vec2 info; // (only used for spot lights) info.x represents light inner cone angle, info.y represents light outer cone angle
};
vec3 apply_directional_light(Light light, vec3 normal)
{
vec3 world_to_light = -light.direction.xyz;
world_to_light = normalize(world_to_light);
float ndotl = clamp(dot(normal, world_to_light), 0.0, 1.0);
return ndotl * light.color.w * light.color.rgb;
}
vec3 apply_point_light(Light light, vec3 pos, vec3 normal)
{
vec3 world_to_light = light.position.xyz - pos;
float dist = length(world_to_light) * 0.005;
float atten = 1.0 / (dist * dist);
world_to_light = normalize(world_to_light);
float ndotl = clamp(dot(normal, world_to_light), 0.0, 1.0);
return ndotl * light.color.w * atten * light.color.rgb;
}
vec3 apply_spot_light(Light light, vec3 pos, vec3 normal)
{
vec3 light_to_pixel = normalize(pos - light.position.xyz);
float theta = dot(light_to_pixel, normalize(light.direction.xyz));
float inner_cone_angle = light.info.x;
float outer_cone_angle = light.info.y;
float intensity = (theta - outer_cone_angle) / (inner_cone_angle - outer_cone_angle);
return smoothstep(0.0, 1.0, intensity) * light.color.w * light.color.rgb;
}
@@ -0,0 +1,31 @@
#version 450
/* Copyright (c) 2022-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#extension GL_EXT_nonuniform_qualifier : require
layout (set = 0, binding = 1) uniform sampler2D textures[];
layout (location = 0) in vec2 inUV;
layout (location = 1) flat in int inTexIndex;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = texture(textures[nonuniformEXT(inTexIndex)], inUV);
}
@@ -0,0 +1,37 @@
#version 450
/* Copyright (c) 2022-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inUV;
layout (location = 2) in int inTexIndex;
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 view;
} ubo;
layout (location = 0) out vec2 outUV;
layout (location = 1) flat out int outTexIndex;
void main()
{
outUV = inUV;
outTexIndex = inTexIndex;
gl_Position = ubo.projection * ubo.view * vec4(inPos.xyz, 1.0);
}
@@ -0,0 +1,33 @@
/* Copyright (c) 2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
[[vk::binding(1, 0)]]
Texture2D textures[] : register(t1, space0);
[[vk::binding(1, 0)]]
SamplerState samplers[] : register(s1, space0);
struct VSOutput
{
float4 Pos : SV_POSITION;
[[vk::location(0)]] float2 UV : TEXCOORD0;
[[vk::location(1)]] nointerpolation int TexIndex : TEXCOORD1;
};
float4 main(VSOutput input) : SV_TARGET
{
return textures[NonUniformResourceIndex(input.TexIndex)].Sample(samplers[NonUniformResourceIndex(input.TexIndex)], input.UV);
}
@@ -0,0 +1,48 @@
/* Copyright (c) 2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
struct VSInput
{
[[vk::location(0)]] float3 Pos : POSITION0;
[[vk::location(1)]] float2 UV : TEXCOORD0;
[[vk::location(2)]] int TexIndex : TEXCOORD1;
};
struct UBO
{
float4x4 projection;
float4x4 view;
};
[[vk::binding(0, 0)]]
ConstantBuffer<UBO> ubo : register(b0, space0);
struct VSOutput
{
float4 Pos : SV_POSITION;
[[vk::location(0)]] float2 UV : TEXCOORD0;
[[vk::location(1)]] nointerpolation int TexIndex : TEXCOORD1;
};
VSOutput main(VSInput input)
{
VSOutput output = (VSOutput) 0;
output.UV = input.UV;
output.TexIndex = input.TexIndex;
output.Pos = mul(ubo.projection, mul(ubo.view, float4(input.Pos.xyz, 1.0)));
return output;
}
@@ -0,0 +1,32 @@
/* Copyright (c) 2025, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
[[vk::binding(1, 0)]]
Sampler2D textures[] : register(t1, space0);
struct VSOutput
{
float4 Pos : SV_POSITION;
[[vk::location(0)]] float2 UV : TEXCOORD0;
[[vk::location(1)]] nointerpolation int TexIndex : TEXCOORD1;
};
[shader("fragment")]
float4 main(VSOutput input) : SV_TARGET
{
return textures[NonUniformResourceIndex(input.TexIndex)].Sample(input.UV);
}
@@ -0,0 +1,49 @@
/* Copyright (c) 2025, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
struct VSInput
{
[[vk::location(0)]] float3 Pos : POSITION0;
[[vk::location(1)]] float2 UV : TEXCOORD0;
[[vk::location(2)]] int TexIndex : TEXCOORD1;
};
struct UBO
{
float4x4 projection;
float4x4 view;
};
[[vk::binding(0, 0)]]
ConstantBuffer<UBO> ubo : register(b0, space0);
struct VSOutput
{
float4 Pos : SV_POSITION;
[[vk::location(0)]] float2 UV : TEXCOORD0;
[[vk::location(1)]] nointerpolation int TexIndex : TEXCOORD1;
};
[shader("vertex")]
VSOutput main(VSInput input)
{
VSOutput output = (VSOutput) 0;
output.UV = input.UV;
output.TexIndex = input.TexIndex;
output.Pos = mul(ubo.projection, mul(ubo.view, float4(input.Pos.xyz, 1.0)));
return output;
}
@@ -0,0 +1,15 @@
#version 450
// 输入变量(与顶点着色器输出对应)
layout(location = 0) in vec2 inTexCoord;
// 输出颜色
layout(location = 0) out vec4 outColor;
// 纹理采样器
layout(binding = 0) uniform sampler2D texSampler;
void main() {
// 采样纹理
outColor = texture(texSampler, inTexCoord);
}
@@ -0,0 +1,65 @@
#version 450
// 硬编码的顶点数据 - 4个顶点组成三角形带覆盖整个屏幕
// const vec2 positions[6] = vec2[6](
// vec2( 1.0, 1.0), // 右上 - 三角形1
// vec2(-1.0, 1.0), // 左上 - 三角形1
// vec2(-1.0, -1.0), // 左下 - 三角形1
// vec2(-1.0, -1.0), // 左下 - 三角形2
// vec2( 1.0, -1.0), // 右下 - 三角形2
// vec2( 1.0, 1.0) // 右上 - 三角形2
// );
const float offset = (640-480)/(480.0);
const vec2 positions[6] = vec2[6](
vec2( 1.0, -1.0 -offset), // 右下
vec2(1.0, 1.0 + offset), // 右上 - 三角形1
vec2(-1.0, 1.0 + offset), // 左上 - 三角形1
vec2(-1.0, 1.0 + offset), // 左上 - 三角形2
vec2( -1.0, -1.0-offset), // 左下 - 三角形2
vec2( 1.0, -1.0-offset) // 右下 - 三角形2
);
//const float offset = (640-480)/(640.0*2); // 假设宽高比为4:3
// const float offset = 0;
// const vec2 positions[6] = vec2[6](
// vec2( -1.0 - offset, 1.0), // 右上 -> 左上
// vec2(-1.0 - offset, -1.0), // 左上 - 左下
// vec2(1.0 + offset, -1.0), // 左下 - 右下
// vec2(1.0 + offset, -1.0), // 左下 - 右下
// vec2( 1.0 + offset, 1.0), // 右下 - 右上
// vec2( -1.0 - offset, 1.0) // 右上 - 左上
// );
const vec2 texCoords[6] = vec2[6](
vec2(1.0, 1.0), // 右上
vec2(0.0, 1.0), // 左上
vec2(0.0, 0.0), // 左下
vec2(0.0, 0.0), // 左下
vec2(1.0, 0.0), // 右下
vec2(1.0, 1.0) // 右上
);
// 输出变量
layout(location = 0) out vec2 outTexCoord;
void main() {
// 获取顶点位置
vec2 position = positions[gl_VertexIndex];
// 设置输出位置(Vulkan使用不同的坐标系)
gl_Position = vec4(position, 0.0, 1.0);
// 输出纹理坐标
outTexCoord = texCoords[gl_VertexIndex];
}
@@ -0,0 +1,29 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec3 fragLineStart;
layout(location = 2) in vec3 fragLineEnd;
layout(location = 0) out vec4 outColor;
void main() {
vec2 fragPos = gl_FragCoord.xy;
// 网格参数
float gridSize = 14.0; // 网格大小
float dotRadius = 6.0; // 点状半径
// 计算当前片段所在的网格和网格中心坐标
vec2 gridCoord = fragPos / gridSize;
vec2 gridCenter = (floor(gridCoord) + 0.5) * gridSize;
// 计算片段到网格中心的距离
float distanceToCenter = length(fragPos - gridCenter);
// 如果距离超过点状半径,丢弃(创建点状虚线)
if (distanceToCenter > dotRadius && fragColor.y > 0.5) {
discard;
}
outColor = vec4(fragColor, 1.0);
}
@@ -0,0 +1,27 @@
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec3 inLineStart;
layout(location = 3) in vec3 inLineEnd;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec3 fragLineStart;
layout(location = 2) out vec3 fragLineEnd;
layout(binding = 0) uniform UniformBufferObject {
mat4 proj;
mat4 model;
mat4 view;
} ubo;
void main() {
mat4 worldviewproj = ubo.proj * ubo.view * ubo.model;
gl_Position = worldviewproj * vec4(inPosition, 1.0);
fragColor = inColor;
fragLineStart = (worldviewproj * vec4(inLineStart, 1.0)).xyz;
fragLineEnd = (worldviewproj * vec4(inLineEnd, 1.0)).xyz;
}
@@ -0,0 +1,11 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(1, 0, 0, 1.0);
}
@@ -0,0 +1,23 @@
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec3 inLineStart;
layout(location = 3) in vec3 inLineEnd;
layout(location = 0) out vec3 fragColor;
layout(binding = 0) uniform UniformBufferObject {
mat4 proj;
mat4 model;
mat4 view;
} ubo;
void main() {
//mat4 worldviewproj = ubo.proj * ubo.view * ubo.model;
mat4 mvp = ubo.proj * ubo.view * ubo.model;
//gl_Position = worldviewproj * vec4(inPosition, 1.0);
gl_Position = mvp * vec4(inPosition, 1.0);
fragColor = inColor;
}
@@ -0,0 +1,11 @@
#version 450
// 从顶点着色器接收的颜色
layout(location = 0) in vec3 fragColor;
// 输出颜色
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0); // 使用传入的颜色
}
@@ -0,0 +1,21 @@
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
layout(binding = 0) uniform UniformBufferObject {
mat4 proj;
mat4 model;
mat4 view;
} ubo;
void main() {
// 预计算MVP矩阵以减少乘法次数
mat4 mvp = ubo.proj * ubo.view * ubo.model;
//vec3 pos = vec3(2.0, inPosition.y, inPosition.z);
gl_Position = mvp * vec4(inPosition, 1.0);
fragColor = inColor;
gl_PointSize = 2.0;
}
@@ -0,0 +1,41 @@
#version 450
/* Copyright (c) 2019-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout (binding = 1) uniform sampler2D samplerColor;
layout (location = 0) in vec2 inUV;
layout (location = 1) in float inLodBias;
layout (location = 2) in vec3 inNormal;
layout (location = 3) in vec3 inViewVec;
layout (location = 4) in vec3 inLightVec;
layout (location = 0) out vec4 outFragColor;
void main()
{
vec4 color = texture(samplerColor, inUV, inLodBias);
vec3 N = normalize(inNormal);
vec3 L = normalize(inLightVec);
vec3 V = normalize(inViewVec);
vec3 R = reflect(-L, N);
vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a;
outFragColor = vec4(diffuse * color.rgb + specular, 1.0);
}
@@ -0,0 +1,57 @@
#version 450
/* Copyright (c) 2019-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inUV;
layout (location = 2) in vec3 inNormal;
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 model;
vec4 viewPos;
float lodBias;
} ubo;
layout (location = 0) out vec2 outUV;
layout (location = 1) out float outLodBias;
layout (location = 2) out vec3 outNormal;
layout (location = 3) out vec3 outViewVec;
layout (location = 4) out vec3 outLightVec;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outUV = inUV;
outLodBias = ubo.lodBias;
vec3 worldPos = vec3(ubo.model * vec4(inPos, 1.0));
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
vec4 pos = ubo.model * vec4(inPos, 1.0);
outNormal = mat3(inverse(transpose(ubo.model))) * inNormal;
vec3 lightPos = vec3(0.0);
vec3 lPos = mat3(ubo.model) * lightPos.xyz;
outLightVec = lPos - pos.xyz;
outViewVec = ubo.viewPos.xyz - pos.xyz;
}
@@ -0,0 +1,43 @@
/* Copyright (c) 2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
[[vk::binding(1, 0)]]
Texture2D textureColor : register(t1);
[[vk::binding(1, 0)]]
SamplerState samplerColor : register(s1);
struct VSOutput
{
float4 Pos : SV_POSITION;
[[vk::location(0)]] float2 UV : TEXCOORD0;
[[vk::location(1)]] float LodBias : FLOAT0;
[[vk::location(2)]] float3 Normal : NORMAL0;
[[vk::location(3)]] float3 ViewVec : VECTOR0;
[[vk::location(4)]] float3 LightVec : VECTOR1;
};
float4 main(VSOutput input) : SV_TARGET0
{
float4 color = textureColor.SampleBias(samplerColor, input.UV, input.LodBias);
float3 N = normalize(input.Normal);
float3 L = normalize(input.LightVec);
float3 V = normalize(input.ViewVec);
float3 R = reflect(-L, N);
float3 diffuse = max(dot(N, L), 0.0).xxx;
float3 specular = (pow(max(dot(R, V), 0.0), 16.0)).xxx * color.a;
return float4(diffuse * color.rgb + specular, 1.0);
}
@@ -0,0 +1,60 @@
/* Copyright (c) 2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
struct VSInput
{
[[vk::location(0)]] float3 Pos : POSITION0;
[[vk::location(1)]] float2 UV : TEXCOORD0;
[[vk::location(2)]] float3 Normal : NORMAL0;
};
struct UBO
{
float4x4 projection;
float4x4 model;
float4 viewPos;
float lodBias;
};
[[vk::binding(0, 0)]]
ConstantBuffer<UBO> ubo : register(b0);
struct VSOutput
{
float4 Pos : SV_POSITION;
[[vk::location(0)]] float2 UV : TEXCOORD0;
[[vk::location(1)]] float LodBias : FLOAT0;
[[vk::location(2)]] float3 Normal : NORMAL0;
[[vk::location(3)]] float3 ViewVec : VECTOR0;
[[vk::location(4)]] float3 LightVec : VECTOR1;
};
VSOutput main(VSInput input)
{
VSOutput output = (VSOutput) 0;
float3 vecPos = mul((float3x3) ubo.model, input.Pos.xyz).xyz;
float3 lightPos = mul((float3x3) ubo.model, (1.0).xxx);
output.UV = input.UV;
output.LodBias = ubo.lodBias;
output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0)));
output.Normal = mul((float3x3) ubo.model, input.Normal);
output.LightVec = lightPos - vecPos;
output.ViewVec = ubo.viewPos.xyz - vecPos;
return output;
}
@@ -0,0 +1,28 @@
#version 320 es
/* Copyright (c) 2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
precision mediump float;
layout(location = 0) in vec3 in_color;
layout(location = 0) out vec4 out_color;
void main()
{
out_color = vec4(in_color, 1.0);
}
@@ -0,0 +1,40 @@
#version 320 es
/* Copyright (c) 2019-2023, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
precision mediump float;
layout(location = 0) out vec3 out_color;
vec2 triangle_positions[3] = vec2[](
vec2(0.5, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
vec3 triangle_colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
void main()
{
gl_Position = vec4(triangle_positions[gl_VertexIndex], 0.5, 1.0);
out_color = triangle_colors[gl_VertexIndex];
}
@@ -0,0 +1,29 @@
#version 450
/* Copyright (c) 2019, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout (binding = 0) uniform sampler2D fontSampler;
layout (location = 0) in vec2 inUV;
layout (location = 1) in vec4 inColor;
layout (location = 0) out vec4 outColor;
void main()
{
outColor = inColor * texture(fontSampler, inUV);
}
@@ -0,0 +1,40 @@
#version 450
/* Copyright (c) 2019, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUV;
layout (location = 2) in vec4 inColor;
layout (push_constant) uniform PushConstants {
mat4 transform;
} pushConstants;
layout (location = 0) out vec2 outUV;
layout (location = 1) out vec4 outColor;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outUV = inUV;
outColor = inColor;
gl_Position = pushConstants.transform * vec4(inPos.xy, 0.0, 1.0);
}
@@ -0,0 +1,391 @@
package com.khronos.vulkan_samples;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
public class AssetsCopyUtil {
private static final String TAG = "AssetsCopyUtil";
private static final String PREF_NAME = "assets_copy_pref";
private static final String PREF_KEY_COPIED_FILES = "copied_files";
/**
* 复制 assets 到应用专属外部存储的 files 目录(递归复制子文件夹)
*/
public static void copyAssetsToAppFiles(Context context, String assetsPath) {
try {
AssetManager assetManager = context.getAssets();
// 获取应用专属外部存储的 files 目录
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) {
Log.e(TAG, "External files directory is not available");
return;
}
// 递归复制
copyAssetsRecursive(context, assetManager, assetsPath, externalFilesDir.getAbsolutePath());
Log.i(TAG, "Assets copy completed for: " + assetsPath);
} catch (Exception e) {
Log.e(TAG, "Error copying assets from: " + assetsPath, e);
}
}
/**
* 递归复制 assets 中的文件和文件夹
*/
private static void copyAssetsRecursive(Context context, AssetManager assetManager,
String assetsPath, String targetBasePath) {
try {
// 获取 assets 路径下的所有项目
String[] items = assetManager.list(assetsPath);
if (items == null || items.length == 0) {
Log.w(TAG, "No items found in assets path: " + assetsPath);
return;
}
// 创建目标目录
File targetDir = new File(targetBasePath, assetsPath);
if (!targetDir.exists() && !targetDir.mkdirs()) {
Log.e(TAG, "Failed to create target directory: " + targetDir.getAbsolutePath());
return;
}
// 获取已复制的文件列表
Set<String> copiedFiles = getCopiedFiles(context);
boolean hasNewFiles = false;
for (String item : items) {
String assetItemPath = assetsPath.isEmpty() ? item : assetsPath + "/" + item;
String fileKey = assetItemPath;
// 检查是否已经复制过
if (copiedFiles.contains(fileKey)) {
Log.d(TAG, "Item already copied: " + assetItemPath);
continue;
}
try {
// 判断是文件还是目录的正确方法
boolean isFile = isAssetFile(assetManager, assetsPath, item);
if (isFile) {
// 复制文件
if (copySingleFile(assetManager, assetItemPath, targetDir, item)) {
copiedFiles.add(fileKey);
hasNewFiles = true;
Log.d(TAG, "Successfully copied file: " + assetItemPath);
}
} else {
// 递归复制子目录
copyAssetsRecursive(context, assetManager, assetItemPath, targetBasePath);
// 将目录也标记为已处理
copiedFiles.add(fileKey);
hasNewFiles = true;
Log.d(TAG, "Processed directory: " + assetItemPath);
}
} catch (Exception e) {
Log.e(TAG, "Error processing: " + assetItemPath, e);
}
}
// 如果有新文件被复制,更新记录
if (hasNewFiles) {
saveCopiedFiles(context, copiedFiles);
}
} catch (Exception e) {
Log.e(TAG, "Error in recursive copy for path: " + assetsPath, e);
}
}
/**
* 正确判断 assets 中的项目是文件还是目录
*/
private static boolean isAssetFile(AssetManager assetManager, String basePath, String item) {
try {
// 方法1:尝试列出子项,如果能列出且不为空,说明是目录
String testPath = basePath.isEmpty() ? item : basePath + "/" + item;
String[] subItems = assetManager.list(testPath);
// 如果子项为空,说明是文件或者是空目录
if (subItems == null) {
return true; // 应该是文件
}
// 如果有子项,说明是目录
if (subItems.length > 0) {
return false; // 是目录
}
// 如果子项为空数组,需要进一步判断
// 尝试打开它,如果能打开说明是文件,不能打开说明是空目录
try {
InputStream test = assetManager.open(testPath);
test.close();
return true; // 能打开,是文件
} catch (Exception e) {
return false; // 不能打开,是空目录
}
} catch (Exception e) {
Log.e(TAG, "Error checking asset type: " + item, e);
return true; // 默认当作文件处理
}
}
/**
* 更可靠的递归复制方法
*/
public static void copyAssetsToAppFilesSimple(Context context, String assetsPath) {
try {
AssetManager assetManager = context.getAssets();
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) {
Log.e(TAG, "External storage not available");
return;
}
// 创建目标目录
File targetBaseDir = new File(externalFilesDir, assetsPath);
if (!targetBaseDir.exists() && !targetBaseDir.mkdirs()) {
Log.e(TAG, "Failed to create base directory");
return;
}
copyAssetsRecursiveSimple(assetManager, assetsPath, targetBaseDir.getAbsolutePath());
Log.i(TAG, "Simple copy completed for: " + assetsPath);
} catch (Exception e) {
Log.e(TAG, "Error in simple copy", e);
}
}
private static void copyAssetsRecursiveSimple(AssetManager assetManager, String assetsPath, String targetPath) {
try {
String[] items = assetManager.list(assetsPath);
if (items == null || items.length == 0) {
return;
}
for (String item : items) {
String fullAssetPath = assetsPath.isEmpty() ? item : assetsPath + "/" + item;
// 判断是文件还是目录
boolean isFile = isAssetFileSimple(assetManager, fullAssetPath);
if (isFile) {
// 复制文件
copySingleFileSimple(assetManager, fullAssetPath, targetPath, item);
Log.d(TAG, "Copied file: " + fullAssetPath + " to " + targetPath);
} else {
// 创建子目录并递归复制
File subDir = new File(targetPath, item);
if (!subDir.exists()) {
subDir.mkdirs();
}
copyAssetsRecursiveSimple(assetManager, fullAssetPath, subDir.getAbsolutePath());
Log.d(TAG, "Processed directory: " + fullAssetPath);
}
}
} catch (Exception e) {
Log.e(TAG, "Error in simple recursive copy: " + assetsPath, e);
}
}
private static boolean isAssetFileSimple(AssetManager assetManager, String path) {
try {
String[] subItems = assetManager.list(path);
if (subItems == null) return true; // 应该是文件
if (subItems.length > 0) return false; // 有子项,是目录
// 空数组,尝试打开判断
try {
InputStream test = assetManager.open(path);
test.close();
return true; // 能打开,是文件
} catch (Exception e) {
return false; // 不能打开,是空目录
}
} catch (Exception e) {
return true; // 出错时默认当作文件
}
}
/**
* 复制单个文件
*/
private static boolean copySingleFile(AssetManager assetManager, String assetFilePath, File targetDir, String filename) {
InputStream in = null;
OutputStream out = null;
try {
File outFile = new File(targetDir, filename);
in = assetManager.open(assetFilePath);
out = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
Log.d(TAG, "File copied: " + outFile.getAbsolutePath());
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to copy file: " + assetFilePath, e);
return false;
} finally {
try {
if (in != null) in.close();
if (out != null) {
out.flush();
out.close();
}
} catch (Exception e) {
Log.e(TAG, "Error closing streams", e);
}
}
}
private static void copySingleFileSimple(AssetManager assetManager, String assetFilePath,
String targetPath, String filename) {
InputStream in = null;
OutputStream out = null;
try {
File outFile = new File(targetPath, filename);
in = assetManager.open(assetFilePath);
out = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
} catch (Exception e) {
Log.e(TAG, "Error copying: " + assetFilePath, e);
} finally {
try {
if (in != null) in.close();
if (out != null) {
out.flush();
out.close();
}
} catch (Exception e) {
Log.e(TAG, "Error closing streams", e);
}
}
}
/**
* 调试方法:打印 assets 结构
*/
public static void printAssetsStructure(Context context, String path) {
try {
Log.d(TAG, "=== Assets Structure for: " + path + " ===");
AssetManager am = context.getAssets();
printAssetsRecursive(am, path, 0);
Log.d(TAG, "=== End of Assets Structure ===");
} catch (Exception e) {
Log.e(TAG, "Error printing assets structure", e);
}
}
private static void printAssetsRecursive(AssetManager am, String path, int depth) {
try {
String indent = "";
for (int i = 0; i < depth; i++) indent += " ";
String[] items = am.list(path);
if (items != null) {
for (String item : items) {
String fullPath = path.isEmpty() ? item : path + "/" + item;
boolean isFile = isAssetFileSimple(am, fullPath);
if (isFile) {
Log.d(TAG, indent + "📄 " + item + " (file)");
} else {
Log.d(TAG, indent + "📁 " + item + " (dir)");
printAssetsRecursive(am, fullPath, depth + 1);
}
}
}
} catch (Exception e) {
Log.e(TAG, "Error in recursive print", e);
}
}
/**
* 获取已复制的文件列表
*/
@SuppressWarnings("unchecked")
private static Set<String> getCopiedFiles(Context context) {
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.getStringSet(PREF_KEY_COPIED_FILES, new HashSet<String>());
}
/**
* 保存已复制的文件列表
*/
private static void saveCopiedFiles(Context context, Set<String> copiedFiles) {
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.edit()
.putStringSet(PREF_KEY_COPIED_FILES, new HashSet<>(copiedFiles))
.apply();
}
/**
* 清空复制记录
*/
public static void clearCopyRecords(Context context) {
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.edit()
.remove(PREF_KEY_COPIED_FILES)
.apply();
Log.d(TAG, "Copy records cleared");
}
/**
* 检查外部存储是否可用
*/
public static boolean isExternalStorageAvailable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
/**
* 获取目标文件的完整路径
*/
public static String getFilePath(Context context, String relativePath) {
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) return null;
File targetFile = new File(externalFilesDir, relativePath);
return targetFile.getAbsolutePath();
}
/**
* 检查文件是否存在
*/
public static boolean fileExists(Context context, String relativePath) {
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) return false;
File targetFile = new File(externalFilesDir, relativePath);
return targetFile.exists();
}
}