khora_io/asset/decoders/mesh/resource_resolver.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! External-resource resolution for complex asset formats like GLTF.
16
17use std::error::Error;
18use std::path::{Path, PathBuf};
19
20/// Resolves external resources (buffers, images) referenced by a URI within
21/// an asset file.
22pub trait GltfResourceResolver: Send + Sync {
23 /// Resolves an external buffer URI to its binary data.
24 fn resolve_buffer(&self, uri: &str) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>>;
25
26 /// Resolves an external image URI to its binary data.
27 fn resolve_image(&self, uri: &str) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>>;
28}
29
30/// Resolves resources from the local filesystem relative to a base path.
31pub struct FileSystemResolver {
32 base_path: PathBuf,
33}
34
35impl FileSystemResolver {
36 /// Creates a new `FileSystemResolver` with a specified base path.
37 pub fn new(base_path: impl AsRef<Path>) -> Self {
38 Self {
39 base_path: base_path.as_ref().to_path_buf(),
40 }
41 }
42}
43
44impl GltfResourceResolver for FileSystemResolver {
45 fn resolve_buffer(&self, uri: &str) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
46 let path = self.base_path.join(uri);
47 std::fs::read(&path)
48 .map_err(|e| format!("Failed to read external buffer from '{:?}': {}", path, e).into())
49 }
50
51 fn resolve_image(&self, uri: &str) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
52 let path = self.base_path.join(uri);
53 std::fs::read(&path)
54 .map_err(|e| format!("Failed to read external image from '{:?}': {}", path, e).into())
55 }
56}
57
58/// Resolver that fails on every external URI lookup. Suitable as a default
59/// for self-contained `.glb` files (which embed all buffers + images) and
60/// as a placeholder when no project context is available.
61///
62/// `MeshDispatcher::default` uses this. For `.gltf` files referencing
63/// external `.bin` / texture buffers, construct
64/// [`MeshDispatcher::new(Arc::new(FileSystemResolver::new(...)))`] instead.
65pub struct NoOpResourceResolver;
66
67impl GltfResourceResolver for NoOpResourceResolver {
68 fn resolve_buffer(&self, uri: &str) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
69 Err(format!(
70 "NoOpResourceResolver cannot fetch external buffer '{}'; \
71 use FileSystemResolver for .gltf files with external buffers.",
72 uri
73 )
74 .into())
75 }
76 fn resolve_image(&self, uri: &str) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
77 Err(format!(
78 "NoOpResourceResolver cannot fetch external image '{}'; \
79 use FileSystemResolver for .gltf files with external textures.",
80 uri
81 )
82 .into())
83 }
84}