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
//! Helpers for using ɴsɪ with 3Delight.
//!
//! Shortcuts for instancing common nodes.
use nsi_core as nsi;
use nsi_toolbelt::{append, generate_or_use_handle, node, rotation};

/// Creates a typical environment node.
///
/// A latitutde-lungitude environment map will be aligned as-shot
/// with the horizon along the X-Z plane at infinity.
///
/// If `handle` is [`None`] a random handle is generated.
///
/// # Arguments
/// * `angle` – In degrees; specifies how much to rotate the environment around
///   the Y (up) axis.
///
/// * `visible` – If the environment is visible to the camera.
///
/// Returns `handle` and the handle of the created `shader`.
///
/// Note that the `shader` node is empty. It is up to the user
/// to set the resp. attributes on the node or hook up an OSL
/// network below it.
pub fn environment(
    ctx: &nsi::Context,
    handle: Option<&str>,
    angle: Option<f64>,
    visible: Option<bool>,
) -> (String, String) {
    // Create a rotation transform – this is the handle we return.
    let rotation = rotation(ctx, None, angle.unwrap_or(0.0), &[0.0, 1.0, 0.0]);

    let environment = generate_or_use_handle(handle, Some("environment"));

    // Set up an environment light.
    append(
        ctx,
        &rotation,
        None,
        &node(
            ctx,
            Some(environment.as_str()),
            nsi::node::ENVIRONMENT,
            None,
        ),
    );

    let shader = node(ctx, None, nsi::node::SHADER, None);

    append(
        ctx,
        &environment,
        Some("geometryattributes"),
        append(
            ctx,
            &node(
                ctx,
                None,
                nsi::node::ATTRIBUTES,
                Some(&[nsi::integer!(
                    "visibility.camera",
                    visible.unwrap_or(true) as _
                )]),
            ),
            Some("surfaceshader"),
            shader.as_str(),
        )
        .0,
    );

    (rotation, shader)
}

/// Creates a textured environment light.
///
/// If `handle` is [`None`] a random handle is generated.
///
/// # Arguments
/// * `texture – A latitude-longitude texture map in one of these formats:
///     * TIFF
///     * JPEG
///     * Radiance
///     * OpenEXR
///     * GIF
///     * IFF
///     * SGI
///     * PIC
///     * Photoshop PSD
///     * TGA
///
/// * `angle` – In degrees; specifies how much to rotate the environment around
///   the Y (up) axis.
///
/// * `exposure` – Scales the intensity in [stops or EV values](https://en.wikipedia.org/wiki/Exposure_value).
///
/// * `visible` – If the environment is visible to the camera.
///
/// Returns `handle` and the handle of the created `shader`.
///
/// Note that the `shader` node is empty. It is up to the user
/// to set the resp. attributes on the node or hook up an OSL
/// network below it.
pub fn environment_texture<'a, 'b>(
    ctx: &nsi::Context<'a>,
    handle: Option<&str>,
    texture: &str,
    angle: Option<f64>,
    exposure: Option<f32>,
    visible: Option<bool>,
    args: Option<&nsi::ArgSlice<'b, 'a>>,
) -> (String, String)
where
    'a: 'b,
{
    let (rotation, shader) = environment(ctx, handle, angle, visible);

    // Environment light attributes.
    ctx.set_attribute(
        shader.as_str(),
        &[
            nsi::string!("shaderfilename", "${DELIGHT}/osl/environmentLight"),
            nsi::float!("intensity", 2.0f32.powf(exposure.unwrap_or(0.0))),
            nsi::string!("image", texture),
        ],
    );

    if let Some(args) = args {
        ctx.set_attribute(shader.as_str(), args);
    }

    (rotation, shader)
}

/// **Convenience method; not part of the official ɴsɪ API.**
///
/// Creates a physically plausible, procedural sky environment light.
///
/// If `handle` is [`None`] a random handle is generated.
///
/// # Arguments
/// * `angle` – In degrees; specifies how much to rotate the environment around
///   the Y (up) axis.
///
/// * `exposure` – Scales the intensity in [stops or EV values](https://en.wikipedia.org/wiki/Exposure_value).
///
/// * `visible` – If the environment is visible to the camera.
///
/// Returns `handle` and the handle of the created `shader`.
///
/// Note that this instances a `dlSky` shader. Using the returned  `shader`
/// handle you can set more attributes on this node.
pub fn environment_sky<'a, 'b>(
    ctx: &nsi::Context<'a>,
    handle: Option<&str>,
    angle: Option<f64>,
    exposure: Option<f32>,
    visible: Option<bool>,
    args: Option<&nsi::ArgSlice<'b, 'a>>,
) -> (String, String)
where
    'a: 'b,
{
    let (rotation, shader) = environment(ctx, handle, angle, visible);

    // Environment light attributes.
    ctx.set_attribute(
        shader.as_str(),
        &[
            nsi::string!("shaderfilename", "${DELIGHT}/osl/dlSky"),
            nsi::float!("intensity", 2.0f32.powf(exposure.unwrap_or(0.0))),
        ],
    );

    if let Some(args) = args {
        ctx.set_attribute(shader.as_str(), args);
    }

    (rotation, shader)
}