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