1use super::*;
2
3#[derive(Default)]
9pub(super) struct LibraryEffects<'a> {
10 pub(super) effects: HashMap<&'a str, Effect<'a>>,
15}
16
17pub(super) struct Effect<'a> {
23 pub(super) id: &'a str,
25 pub(super) profile: ProfileCommon<'a>,
28}
29
30pub(super) struct ProfileCommon<'a> {
36 pub(super) surfaces: HashMap<&'a str, Surface<'a>>,
39 pub(super) samplers: HashMap<&'a str, Sampler<'a>>,
40
41 pub(super) technique: Technique<'a>,
42}
43
44pub(super) struct Technique<'a> {
45 #[allow(dead_code)] pub(super) ty: ShadeType,
47
48 pub(super) emission: ColorAndTexture<'a>,
50 pub(super) ambient: ColorAndTexture<'a>,
51 pub(super) diffuse: ColorAndTexture<'a>,
52 pub(super) specular: ColorAndTexture<'a>,
53 pub(super) reflective: ColorAndTexture<'a>,
54 pub(super) transparent: ColorAndTexture<'a>,
55 pub(super) has_transparency: bool,
56 pub(super) rgb_transparency: bool,
57 pub(super) invert_transparency: bool,
58
59 pub(super) shininess: f32,
60 pub(super) reflectivity: f32,
61 pub(super) transparency: f32,
62 pub(super) index_of_refraction: f32,
63
64 pub(super) double_sided: bool,
66
67 pub(super) bump: Texture<'a>,
69
70 pub(super) wireframe: bool,
72 pub(super) faceted: bool,
73}
74
75pub(super) struct Surface<'a> {
81 pub(super) init_from: Uri<'a, Image<'a>>,
82}
83
84pub(super) struct Sampler<'a> {
90 pub(super) source: &'a str,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98pub(super) enum ShadeType {
99 Constant,
100 Lambert,
101 Phong,
102 Blinn,
103}
104
105pub(super) struct ColorAndTexture<'a> {
106 pub(super) color: Color4,
107 pub(super) texture: Texture<'a>,
108}
109
110impl ColorAndTexture<'_> {
111 fn new(color: Color4) -> Self {
112 Self {
113 color,
114 texture: Texture {
115 texture: "",
116 },
118 }
119 }
120}
121
122pub(super) struct Texture<'a> {
123 pub(super) texture: &'a str,
124 }
126
127pub(super) fn parse_library_effects<'a>(
131 cx: &mut Context<'a>,
132 node: xml::Node<'a, '_>,
133) -> io::Result<()> {
134 debug_assert_eq!(node.tag_name().name(), "library_effects");
135 for child in node.element_children() {
139 match child.tag_name().name() {
140 "effect" => {
141 let effect = parse_effect(cx, child)?;
142 cx.library_effects.effects.insert(effect.id, effect);
143 }
144 "asset" | "extra" => { }
145 _ => return Err(error::unexpected_child_elem(child)),
146 }
147 }
148
149 Ok(())
153}
154
155fn parse_effect<'a>(cx: &mut Context<'a>, node: xml::Node<'a, '_>) -> io::Result<Effect<'a>> {
178 debug_assert_eq!(node.tag_name().name(), "effect");
179 let id = node.required_attribute("id")?;
180 let mut profile = None;
181
182 for child in node.element_children() {
183 if child.tag_name().name() == "profile_COMMON" {
184 profile = Some(parse_profile_common(cx, child)?);
185 }
186 }
187
188 let profile = match profile {
189 Some(profile) => profile,
190 None => return Err(error::exactly_one_elem(node, "profile_COMMON")),
191 };
192
193 Ok(Effect {
194 id,
195 profile,
197 })
198}
199
200fn parse_profile_common<'a>(
220 cx: &mut Context<'a>,
221 node: xml::Node<'a, '_>,
222) -> io::Result<ProfileCommon<'a>> {
223 debug_assert_eq!(node.tag_name().name(), "profile_COMMON");
224 let mut surfaces = HashMap::default();
225 let mut samplers = HashMap::default();
226 let mut technique = None;
227
228 for child in node.element_children() {
229 match child.tag_name().name() {
230 "newparam" => {
231 parse_newparam(cx, child, &mut surfaces, &mut samplers)?;
232 }
233 "technique" => {
234 for t in child.element_children() {
235 let name = t.tag_name().name();
236 match name {
237 "constant" | "lambert" | "phong" | "blinn" => {
238 technique = Some(parse_technique(t, name.parse().unwrap())?);
239 }
240 "asset" | "extra" => { }
241 _ => {}
242 }
243 }
244 }
245 "asset" | "extra" => { }
246 _ => return Err(error::unexpected_child_elem(child)),
247 }
248 }
249
250 let technique = match technique {
251 Some(technique) => technique,
252 None => return Err(error::exactly_one_elem(node, "technique")),
254 };
255
256 Ok(ProfileCommon {
257 surfaces,
259 samplers,
260 technique,
261 })
262}
263
264fn parse_newparam<'a>(
265 _cx: &mut Context<'a>,
266 node: xml::Node<'a, '_>,
267 surfaces: &mut HashMap<&'a str, Surface<'a>>,
268 samplers: &mut HashMap<&'a str, Sampler<'a>>,
269) -> io::Result<()> {
270 debug_assert_eq!(node.tag_name().name(), "newparam");
271 let sid = node.required_attribute("sid")?;
272
273 for child in node.element_children() {
274 match child.tag_name().name() {
275 "surface" => {
276 if let Some(init) = child.child("init_from") {
278 surfaces.insert(
279 sid,
280 Surface {
281 init_from: Uri::from_id(init.trimmed_text()),
282 },
283 );
284 }
285 }
286 "sampler2D" => {
287 if let Some(source) = child.child("source") {
289 samplers.insert(
290 sid,
291 Sampler {
292 source: source.trimmed_text(),
293 },
294 );
295 }
296 }
297 _ => return Err(error::unexpected_child_elem(child)),
298 }
299 }
300
301 Ok(())
302}
303
304impl FromStr for ShadeType {
305 type Err = io::Error;
306
307 fn from_str(s: &str) -> Result<Self, Self::Err> {
308 Ok(match s {
309 "constant" => Self::Constant,
310 "lambert" => Self::Lambert,
311 "phong" => Self::Phong,
312 "blinn" => Self::Blinn,
313 _ => bail!("unknown shade type {:?}", s),
314 })
315 }
316}
317
318fn parse_technique<'a>(node: xml::Node<'a, '_>, ty: ShadeType) -> io::Result<Technique<'a>> {
332 debug_assert_eq!(node.tag_name().name().parse::<ShadeType>().unwrap(), ty);
333 let mut effect = Technique::new(ty);
334
335 for child in node.element_children() {
336 let name = child.tag_name().name();
337 match name {
338 "emission" => {
340 parse_effect_color(
341 child,
342 &mut effect.emission.color,
343 &mut effect.emission.texture,
344 )?;
345 }
346 "ambient" => {
347 parse_effect_color(
348 child,
349 &mut effect.ambient.color,
350 &mut effect.ambient.texture,
351 )?;
352 }
353 "diffuse" => {
354 parse_effect_color(
355 child,
356 &mut effect.diffuse.color,
357 &mut effect.diffuse.texture,
358 )?;
359 }
360 "specular" => {
361 parse_effect_color(
362 child,
363 &mut effect.specular.color,
364 &mut effect.specular.texture,
365 )?;
366 }
367 "reflective" => {
368 parse_effect_color(
369 child,
370 &mut effect.reflective.color,
371 &mut effect.reflective.texture,
372 )?;
373 }
374 "transparent" => {
375 effect.has_transparency = true;
376 if let Some(opaque) = child.parse_attribute::<Opaque>("opaque")? {
377 effect.rgb_transparency = opaque.rgb_transparency();
378 effect.invert_transparency = opaque.invert_transparency();
379 }
380 parse_effect_color(
381 child,
382 &mut effect.transparent.color,
383 &mut effect.transparent.texture,
384 )?;
385 }
386
387 "shininess" => {
389 if let Some(n) = parse_effect_float(child)? {
390 effect.shininess = n;
391 }
392 }
393 "reflectivity" => {
394 if let Some(n) = parse_effect_float(child)? {
395 effect.reflectivity = n;
396 }
397 }
398 "transparency" => {
399 if let Some(n) = parse_effect_float(child)? {
400 effect.transparency = n;
401 }
402 }
403 "index_of_refraction" => {
404 if let Some(n) = parse_effect_float(child)? {
405 effect.index_of_refraction = n;
406 }
407 }
408
409 "double_sided" => {
411 effect.double_sided = node.parse_required_attribute(name)?;
412 }
413
414 "bump" => {
416 let mut dummy = [0.; 4];
417 parse_effect_color(child, &mut dummy, &mut effect.bump)?;
418 }
419
420 "wireframe" => {
422 effect.wireframe = node.parse_required_attribute(name)?;
423 }
424 "faceted" => {
425 effect.faceted = node.parse_required_attribute(name)?;
426 }
427
428 _ => {}
429 }
430 }
431
432 Ok(effect)
433}
434
435impl Technique<'_> {
436 fn new(ty: ShadeType) -> Self {
437 Self {
438 ty,
439 emission: ColorAndTexture::new([0.0, 0.0, 0.0, 1.0]),
440 ambient: ColorAndTexture::new([0.1, 0.1, 0.1, 1.0]),
441 diffuse: ColorAndTexture::new([0.6, 0.6, 0.6, 1.0]),
442 specular: ColorAndTexture::new([0.4, 0.4, 0.4, 1.0]),
443 transparent: ColorAndTexture::new([1.0, 1.0, 1.0, 1.0]),
445 reflective: ColorAndTexture::new([0.0, 0.0, 0.0, 1.0]),
446 shininess: 10.0,
447 index_of_refraction: 1.0,
448 reflectivity: 0.0,
449 transparency: 1.0,
451 has_transparency: false,
452 rgb_transparency: false,
453 invert_transparency: false,
454 double_sided: false,
455 bump: Texture {
456 texture: "",
457 },
459 wireframe: false,
460 faceted: false,
461 }
462 }
463}
464
465fn parse_effect_color<'a>(
478 node: xml::Node<'a, '_>,
479 color: &mut Color4,
480 texture: &mut Texture<'a>,
481) -> io::Result<()> {
482 for child in node.element_children() {
483 match child.tag_name().name() {
484 "color" => {
485 let content = xml::comma_to_period(child.trimmed_text());
486 let mut iter = xml::parse_float_array_exact(&content, 4);
487 let map_err = |e| {
489 format_err!(
490 "{e} in <{}> element ({})",
491 child.tag_name().name(),
492 child.text_location(),
493 )
494 };
495 let r = iter.next().unwrap().map_err(map_err)?;
496 let g = iter.next().unwrap().map_err(map_err)?;
497 let b = iter.next().unwrap().map_err(map_err)?;
498 let a = iter.next().unwrap().map_err(map_err)?;
499 *color = [r, g, b, a];
500 }
501 "texture" => {
502 let _texcoord = child.required_attribute("texcoord")?;
503 *texture = Texture {
504 texture: child.required_attribute("texture")?,
505 };
507 }
508 "param" => {
509 }
516 _ => {}
517 }
518 }
519 Ok(())
520}
521
522fn parse_effect_float(node: xml::Node<'_, '_>) -> io::Result<Option<f32>> {
523 let mut float = None;
524
525 for child in node.element_children() {
526 match child.tag_name().name() {
527 "float" => {
528 let content = xml::comma_to_period(child.trimmed_text());
529 float = Some(
530 float::parse(content.as_bytes())
531 .ok_or_else(|| format_err!("error while parsing a float"))?,
532 );
533 }
534 "param" => {
535 }
542 _ => return Err(error::unexpected_child_elem(child)),
543 }
544 }
545
546 Ok(float)
547}
548
549#[allow(non_camel_case_types)]
550#[derive(Clone, Copy, PartialEq, Eq)]
551enum Opaque {
552 A_ZERO,
553 A_ONE,
554 RGB_ZERO,
555 RGB_ONE,
556}
557
558impl Opaque {
559 fn rgb_transparency(self) -> bool {
560 matches!(self, Self::RGB_ZERO | Self::RGB_ONE)
561 }
562 fn invert_transparency(self) -> bool {
563 matches!(self, Self::RGB_ZERO | Self::A_ZERO)
564 }
565}
566
567impl FromStr for Opaque {
568 type Err = io::Error;
569
570 fn from_str(s: &str) -> Result<Self, Self::Err> {
571 Ok(match s {
572 "A_ZERO" => Self::A_ZERO,
573 "A_ONE" => Self::A_ONE,
574 "RGB_ZERO" => Self::RGB_ZERO,
575 "RGB_ONE" => Self::RGB_ONE,
576 _ => bail!("unknown opaque type {:?}", s),
577 })
578 }
579}