openconfiguration/
scene.rs

1use openconfiguration_derive::Visitable;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5use crate::{
6    package::Package, script::Script, status::Status, support, Camera, CatalogEntry, Commercial,
7    Geometry, Material, Product, Representation,
8};
9
10#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Visitable)]
11#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
12#[cfg_attr(feature = "schema", schemars(deny_unknown_fields))]
13#[serde(rename_all = "camelCase")]
14/// The Scene is the central part of an OC data set. Each OC data set
15/// contains exactly one.
16///
17/// A Scene may contain initial product representations and/or updates.
18///
19/// A Scene may contain products that should be inserted instantly, as well
20/// as add-ons to be inserted later on and shown as placeholders before.
21pub struct Scene {
22    /// Reference to the JSON schema.
23    #[serde(rename = "$schema")]
24    #[cfg_attr(feature = "schema", schemars(default = "Scene::schema"))]
25    pub schema: Option<String>,
26    /// The mandatory attribute format must be structured as follows:
27    ///
28    /// "OpenConfiguration_<Major/>.<Minor/> [PRE]"
29    ///
30    /// Legal combinations of <Major/>.<Minor/> are: 1.0, 1.1 and 1.2
31    ///
32    /// The optional postfix " PRE" marks a preliminary format.
33    pub format: String,
34    /// This optional attribute contains informal information about the
35    /// creator of this OC data set.
36    pub creator: Option<String>,
37    /// This optional attribute contains informal information about the
38    /// projects used in this OC data set and its corresponding informal
39    /// versions.
40    pub packages: Option<Vec<Package>>,
41    /// This attribute contains general status information about the Scene contents.
42    pub status: Status,
43    /// The representations of the scene. Legal values are:
44    ///
45    /// "Standard" - Real-time mesh data.
46    ///
47    /// "CAD_BIM" - CAD/BIM volume bodies.
48    ///
49    /// "Photo" - High-resolution mesh data.
50    ///
51    /// This just lists possible usages and can be used for fast filtering,
52    /// it does not influence the actual data. A "Photo" renderer would still use
53    /// meshes attached as geometry.mesh
54    pub representations: Option<Vec<Representation>>,
55    /// This optional attribute may contain the id of a server-side session.
56    pub configuration_id: Option<String>,
57    /// This optional attribute may contain the link of the original configuration.
58    pub configuration_link: Option<String>,
59    /// Optional, embedded catalog.
60    pub catalog: Option<Vec<CatalogEntry>>,
61    /// The optional attribute provides a partial URI to be added to all
62    /// relative asset uris. A valid base path should normally start with
63    /// https:// or file://, and end with either a slash or the path separator character of the operating system.
64    /// "Relative" basePath is always relative to the scene.json path,
65    /// not to an application/deployment specific one!
66    /// example.configurator.com/assets/oc/scene.json containing "tex.jpg"
67    /// basepathes "gfx", "./gfx", "/gfx" are all resolved to the same
68    /// example.configurator.com/assets/oc/gfx/tex.jpg
69    pub base_path: Option<String>,
70    /// The mandatory attribute provides unique content hashes for assets
71    /// that are directly referenced in the OC data set. The specific hash
72    /// algorithm is unspecified. It may be an MD5 hash of the binary
73    /// content for instance. But low-res assets may use the same hash as
74    /// the originals, they are derived from. So, the only operation that is
75    /// legal for hash, is to compare them with an optionally existing one.
76    ///
77    /// All assets should have an entry here as downloaders may iterate through
78    /// this record rather than exploring the other data entities.
79    #[serde(deserialize_with = "crate::utils::deserialize_map_without_null_values")]
80    pub hashes: HashMap<String, String>,
81    /// Redirections maybe needed to convert absolute asset urls into file
82    /// names, especially when compiling a self-contained OC zip.
83    /// The attribute is optional.
84    ///
85    /// Version: OC 1.3
86    #[serde(
87        deserialize_with = "crate::utils::deserialize_optional_map_without_null_values",
88        default
89    )]
90    pub redirections: Option<HashMap<String, String>>,
91    /// The optional attribute contains all client-side JavaScript packages.
92    pub scripts: Option<Vec<Script>>,
93    /// The optional attribute provides an embedded geometry index.
94    ///
95    /// IGXC Compatibility: now it's embedded, self-contained geometries
96    /// rather than just geometry names. Thus, separate assignments of
97    /// geometry normal maps, deformations, etc. are removed. Also, the
98    /// embedded geometry definition is optional. There can be external
99    /// geometry definitions, too.
100    #[serde(
101        deserialize_with = "crate::utils::deserialize_optional_map_without_null_values",
102        default
103    )]
104    #[visitable(visit_with = "visit_geometries")]
105    pub geometries: Option<HashMap<String, Geometry>>,
106    /// The optional attribute provides links to external geometry indices.
107    /// The key should be a two-level technical namespace to speedup the
108    /// geometry lookup.
109    /// The value must be an absolute or relative URI.
110    /// The content of the value, must be de-serialized as GeometryIndex.
111    ///
112    /// IGXC Compatibility: In IGXC, this concept did not exist.
113    #[serde(
114        deserialize_with = "crate::utils::deserialize_optional_map_without_null_values",
115        default
116    )]
117    #[visitable(visit_with = "visit_geometry_indexes")]
118    pub geometry_indexes: Option<HashMap<String, String>>,
119    /// The optional attribute provides an embedded geometry index.
120    ///
121    /// IGXC Compatibility: The embedded material definition is optional.
122    /// There can be external material definitions, too.
123    #[serde(
124        deserialize_with = "crate::utils::deserialize_optional_map_without_null_values",
125        default
126    )]
127    #[visitable(visit_with = "visit_materials")]
128    pub materials: Option<HashMap<String, Material>>,
129    /// The optional attribute provides links to external geometry indices.
130    /// The key should be a two-level technical namespace to speedup the
131    /// geometry lookup.
132    /// The value must be an absolute or relative URI.
133    /// The content of the value, must be de-serialized as MaterialIndex.
134    ///
135    /// IGXC Compatibility: In IGXC, this concept did not exist.
136    #[serde(
137        deserialize_with = "crate::utils::deserialize_optional_map_without_null_values",
138        default
139    )]
140    #[visitable(visit_with = "visit_material_indexes")]
141    pub material_indexes: Option<HashMap<String, String>>,
142    /// An optional camera setup to restore the camera in another viewer.
143    pub camera: Option<Camera>,
144    /// The mandatory attribute contains the products to be inserted into/
145    /// updated in the client world.
146    ///
147    /// IGXC Compatibility: in IGXC, there is only one product.
148    pub products: Vec<Product>,
149    /// Commercial products without an own visual representation.
150    ///
151    /// Version: OC 1.3
152    pub com_products: Option<Vec<Commercial>>,
153}
154
155impl Scene {
156    pub const fn schema() -> &'static str {
157        "https://archive.intelligentgraphics.biz/schemas/openconfiguration/scene.json"
158    }
159
160    pub fn current_format() -> String {
161        format!("OpenConfiguration_{}", env!("CARGO_PKG_VERSION"))
162    }
163
164    pub fn new() -> Self {
165        Self::new_with_format(Self::current_format())
166    }
167
168    pub fn new_with_format(format: String) -> Self {
169        Self {
170            schema: Some(Scene::schema().to_owned()),
171            format,
172            creator: Default::default(),
173            packages: Default::default(),
174            status: Default::default(),
175            representations: Default::default(),
176            configuration_id: Default::default(),
177            configuration_link: Default::default(),
178            catalog: Default::default(),
179            base_path: Default::default(),
180            hashes: Default::default(),
181            redirections: Default::default(),
182            scripts: Default::default(),
183            geometries: Default::default(),
184            geometry_indexes: Default::default(),
185            materials: Default::default(),
186            material_indexes: Default::default(),
187            camera: Default::default(),
188            products: Default::default(),
189            com_products: Default::default(),
190        }
191    }
192}
193
194fn visit_geometries(
195    map: &mut Option<HashMap<String, Geometry>>,
196    visitor: &mut dyn support::Visitor,
197) {
198    if let Some(map) = map {
199        for (key, value) in map.iter_mut() {
200            if let Some(ig) = &mut value.ig {
201                visitor.visit_geometry(&key, ig);
202            }
203            support::Visitable::visit_with(value, visitor);
204        }
205    }
206}
207
208fn visit_geometry_indexes(
209    map: &mut Option<HashMap<String, String>>,
210    visitor: &mut dyn support::Visitor,
211) {
212    if let Some(map) = map {
213        for (key, value) in map.iter_mut() {
214            visitor.visit_geometry_index(&key, value);
215            visitor.visit_path(value);
216        }
217    }
218}
219
220fn visit_materials(
221    map: &mut Option<HashMap<String, Material>>,
222    visitor: &mut dyn support::Visitor,
223) {
224    if let Some(map) = map {
225        for (key, value) in map.iter_mut() {
226            if let Some(ig) = &mut value.ig {
227                visitor.visit_material(&key, ig);
228            }
229            support::Visitable::visit_with(value, visitor);
230        }
231    }
232}
233
234fn visit_material_indexes(
235    map: &mut Option<HashMap<String, String>>,
236    visitor: &mut dyn support::Visitor,
237) {
238    if let Some(map) = map {
239        for (key, value) in map.iter_mut() {
240            visitor.visit_material_index(&key, value);
241            visitor.visit_path(value);
242        }
243    }
244}