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}