nsi_3delight/lib.rs
1//! Helpers for using ɴsɪ with 3Delight.
2//!
3//! Shortcuts for instancing common nodes.
4use nsi_core as nsi;
5use nsi_toolbelt::{append, generate_or_use_handle, node, rotation};
6
7/// Creates a typical environment node.
8///
9/// A latitutde-lungitude environment map will be aligned as-shot
10/// with the horizon along the X-Z plane at infinity.
11///
12/// If `handle` is [`None`] a random handle is generated.
13///
14/// # Arguments
15/// * `angle` – In degrees; specifies how much to rotate the environment around
16/// the Y (up) axis.
17///
18/// * `visible` – If the environment is visible to the camera.
19///
20/// Returns `handle` and the handle of the created `shader`.
21///
22/// Note that the `shader` node is empty. It is up to the user
23/// to set the resp. attributes on the node or hook up an OSL
24/// network below it.
25pub fn environment(
26 ctx: &nsi::Context,
27 handle: Option<&str>,
28 angle: Option<f64>,
29 visible: Option<bool>,
30) -> (String, String) {
31 // Create a rotation transform – this is the handle we return.
32 let rotation = rotation(ctx, None, angle.unwrap_or(0.0), &[0.0, 1.0, 0.0]);
33
34 let environment = generate_or_use_handle(handle, Some("environment"));
35
36 // Set up an environment light.
37 append(
38 ctx,
39 &rotation,
40 None,
41 &node(
42 ctx,
43 Some(environment.as_str()),
44 nsi::node::ENVIRONMENT,
45 None,
46 ),
47 );
48
49 let shader = node(ctx, None, nsi::node::SHADER, None);
50
51 append(
52 ctx,
53 &environment,
54 Some("geometryattributes"),
55 append(
56 ctx,
57 &node(
58 ctx,
59 None,
60 nsi::node::ATTRIBUTES,
61 Some(&[nsi::integer!(
62 "visibility.camera",
63 visible.unwrap_or(true) as _
64 )]),
65 ),
66 Some("surfaceshader"),
67 shader.as_str(),
68 )
69 .0,
70 );
71
72 (rotation, shader)
73}
74
75/// Creates a textured environment light.
76///
77/// If `handle` is [`None`] a random handle is generated.
78///
79/// # Arguments
80/// * `texture – A latitude-longitude texture map in one of these formats:
81/// * TIFF
82/// * JPEG
83/// * Radiance
84/// * OpenEXR
85/// * GIF
86/// * IFF
87/// * SGI
88/// * PIC
89/// * Photoshop PSD
90/// * TGA
91///
92/// * `angle` – In degrees; specifies how much to rotate the environment around
93/// the Y (up) axis.
94///
95/// * `exposure` – Scales the intensity in [stops or EV values](https://en.wikipedia.org/wiki/Exposure_value).
96///
97/// * `visible` – If the environment is visible to the camera.
98///
99/// Returns `handle` and the handle of the created `shader`.
100///
101/// Note that the `shader` node is empty. It is up to the user
102/// to set the resp. attributes on the node or hook up an OSL
103/// network below it.
104pub fn environment_texture<'a, 'b>(
105 ctx: &nsi::Context<'a>,
106 handle: Option<&str>,
107 texture: &str,
108 angle: Option<f64>,
109 exposure: Option<f32>,
110 visible: Option<bool>,
111 args: Option<&nsi::ArgSlice<'b, 'a>>,
112) -> (String, String)
113where
114 'a: 'b,
115{
116 let (rotation, shader) = environment(ctx, handle, angle, visible);
117
118 // Environment light attributes.
119 ctx.set_attribute(
120 shader.as_str(),
121 &[
122 nsi::string!("shaderfilename", "${DELIGHT}/osl/environmentLight"),
123 nsi::float!("intensity", 2.0f32.powf(exposure.unwrap_or(0.0))),
124 nsi::string!("image", texture),
125 ],
126 );
127
128 if let Some(args) = args {
129 ctx.set_attribute(shader.as_str(), args);
130 }
131
132 (rotation, shader)
133}
134
135/// **Convenience method; not part of the official ɴsɪ API.**
136///
137/// Creates a physically plausible, procedural sky environment light.
138///
139/// If `handle` is [`None`] a random handle is generated.
140///
141/// # Arguments
142/// * `angle` – In degrees; specifies how much to rotate the environment around
143/// the Y (up) axis.
144///
145/// * `exposure` – Scales the intensity in [stops or EV values](https://en.wikipedia.org/wiki/Exposure_value).
146///
147/// * `visible` – If the environment is visible to the camera.
148///
149/// Returns `handle` and the handle of the created `shader`.
150///
151/// Note that this instances a `dlSky` shader. Using the returned `shader`
152/// handle you can set more attributes on this node.
153pub fn environment_sky<'a, 'b>(
154 ctx: &nsi::Context<'a>,
155 handle: Option<&str>,
156 angle: Option<f64>,
157 exposure: Option<f32>,
158 visible: Option<bool>,
159 args: Option<&nsi::ArgSlice<'b, 'a>>,
160) -> (String, String)
161where
162 'a: 'b,
163{
164 let (rotation, shader) = environment(ctx, handle, angle, visible);
165
166 // Environment light attributes.
167 ctx.set_attribute(
168 shader.as_str(),
169 &[
170 nsi::string!("shaderfilename", "${DELIGHT}/osl/dlSky"),
171 nsi::float!("intensity", 2.0f32.powf(exposure.unwrap_or(0.0))),
172 ],
173 );
174
175 if let Some(args) = args {
176 ctx.set_attribute(shader.as_str(), args);
177 }
178
179 (rotation, shader)
180}