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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
//#![warn(missing_docs)]
//#![warn(missing_doc_code_examples)]
//! # Nodal Scene Interface -- ɴsɪ
//! A flexible, modern API for offline 3D renderers
//!
//! [Nsɪ](https://nsi.readthedocs.io/) is built around the concept of
//! nodes. Each node has a *unique handle* to identify it. It also has
//! a [type](node) which describes its intended function in the scene.
//!
//! Nodes are abstract containers for data. The interpretation depends
//! on the node type. Nodes can also be [connected to each
//! other](https://nsi.readthedocs.io/en/latest/guidelines.html#basic-scene-anatomy)
//! to express relationships.
//!
//! Data is stored on nodes as *attributes*. Each attribute has a name
//! which is unique on the node and a type which describes the kind of
//! data it holds (strings, integer numbers, floating point numbers,
//! etc.). The standard ɴsɪ attribute names are exposed as typed
//! constants -- see [Typed attribute names](#typed-attribute-names) below.
//!
//! Relationships and data flow between nodes are represented as
//! connections. Connections have a source and a destination. Both can
//! be either a node or a specific attribute of a node. There are no
//! type restrictions for connections in the interface itself. It is
//! acceptable to connect attributes of different types or even
//! attributes to nodes. The validity of such connections depends on
//! the types of the nodes involved.
//!
//! What we refer to as the ɴsɪ has two major components:
//!
//! 1. Methods to create nodes, attributes and their connections. These are
//! attached to a rendering [`Context`].
//!
//! 2. [Nodes](node) understood by the renderer.
//!
//! Much of the complexity and expressiveness of the interface comes
//! from
//! [the supported nodes](https://nsi.readthedocs.io/en/latest/nodes.html).
//!
//! The first part was kept deliberately simple to make it easy to
//! support multiple ways of creating nodes.
//!
//! ## Examples
//!
//! ```
//! // Create a context to send the scene to.
//! let ctx = nsi::Context::new(None).expect("Could not create NSI context.");
//!
//! // Create a dodecahedron.
//!
//! // 12 regular pentagon faces.
//! let face_index: [i32; 60] = [
//! 0, 16, 2, 10, 8, 0, 8, 4, 14, 12, 16, 17, 1, 12, 0, 1, 9, 11, 3, 17, 1,
//! 12, 14, 5, 9, 2, 13, 15, 6, 10, 13, 3, 17, 16, 2, 3, 11, 7, 15, 13, 4,
//! 8, 10, 6, 18, 14, 5, 19, 18, 4, 5, 19, 7, 11, 9, 15, 7, 19, 18, 6,
//! ];
//!
//! // Golden ratio.
//! let phi: f32 = 0.5 * (1.0 + 5_f32.sqrt());
//!
//! // Golden ratio conjugate.
//! let phi_c: f32 = phi - 1.0;
//!
//! // 20 control points, each a Point3F32 ([f32; 3]).
//! // Length divisibility by 3 is enforced at the type level.
//! let positions: [nsi::Point3F32; 20] = [
//! [1., 1., 1.],
//! [1., 1., -1.],
//! [1., -1., 1.],
//! [1., -1., -1.],
//! [-1., 1., 1.],
//! [-1., 1., -1.],
//! [-1., -1., 1.],
//! [-1., -1., -1.],
//! [0., phi_c, phi],
//! [0., phi_c, -phi],
//! [0., -phi_c, phi],
//! [0., -phi_c, -phi],
//! [phi_c, phi, 0.],
//! [phi_c, -phi, 0.],
//! [-phi_c, phi, 0.],
//! [-phi_c, -phi, 0.],
//! [phi, 0., phi_c],
//! [phi, 0., -phi_c],
//! [-phi, 0., phi_c],
//! [-phi, 0., -phi_c],
//! ];
//!
//! // Create a new mesh node and call it 'dodecahedron'.
//! ctx.create("dodecahedron", nsi::MESH, None);
//!
//! // Connect the 'dodecahedron' node to the scene's root.
//! ctx.connect("dodecahedron", None, nsi::ROOT, "objects", None);
//!
//! // Define the geometry of the 'dodecahedron' node.
//! ctx.set_attribute(
//! "dodecahedron",
//! &[
//! // Typed name: `nsi::POSITION` is `Attribute<[nsi::Point3F32]>`.
//! // Wrong-shape data (e.g. a `&[f32]`) is rejected by rustc at
//! // this call site.
//! nsi::point_slice!(nsi::POSITION, &positions),
//! nsi::i32_slice!("P.indices", &face_index),
//! // 5 vertices per each face.
//! nsi::i32_slice!("nvertices", &[5; 12]),
//! // Render this as a subdivison surface.
//! nsi::string!("subdivision.scheme", "catmull-clark"),
//! // Crease each of the dodecahedron's 30 edges. Each edge is
//! // a pair (start, end) of vertex indices into `positions`,
//! // so this list is twice as long as `creasesharpness`.
//! nsi::i32_slice!(
//! "subdivision.creasevertices",
//! &[
//! 0, 8, 0, 12, 0, 16, 1, 9, 1, 12, 1, 17, 2, 10, 2, 13,
//! 2, 16, 3, 11, 3, 13, 3, 17, 4, 8, 4, 14, 4, 18, 5, 9,
//! 5, 14, 5, 19, 6, 10, 6, 15, 6, 18, 7, 11, 7, 15, 7, 19,
//! 8, 10, 9, 11, 12, 14, 13, 15, 16, 17, 18, 19,
//! ]
//! ),
//! nsi::f32_slice!("subdivision.creasesharpness", &[4.2; 30]),
//! ],
//! );
//! ```
//! ## More Examples
//!
//! These can be found in the [`examples`](https://github.com/virtualritz/nsi/tree/master/examples)
//! folder.
//!
//! *All the examples in this crate require a (free) [3Delight](https://www.3delight.com/)
//! installation to run!*
//!
//! ### Interactive
//!
//! Demonstrates using the [`FnStatus`] callback closure during rendering and a
//! channel for communicating between main- and rendering thread(s).
//!
//! ### Jupyter
//!
//! Render directly into a Jupyter notebook.
//!
//! Follow
//! [these instructions](https://github.com/google/evcxr/blob/master/evcxr_jupyter/README.md)
//! to get a Rust Jupyter kernel up and running first.
//!
//! ### Output
//!
//! This is a full [`output`] example showing color conversion and writing data
//! out to 8bit/channel PNG and 32bit/channel (float) OpenEXR formats.
//!
//! ### Volume
//!
//! Demonstrates rendering an [OpenVDB](https://www.openvdb.org/) asset. Mostly
//! through the [`toolbelt`] helpers.
//!
//! ## Crate Organization
//!
//! The `nsi` crate is a facade. The work is split across smaller crates so
//! consumers can pick the layer they need:
//!
//! - [`nsi-trait`](https://crates.io/crates/nsi-trait) -- pure-Rust trait
//! crate. Defines [`Nsi`] (`self` _is_ the context), the [`Attribute`]
//! typed-name machinery, [`NodeType`], [`Action`], and the standard
//! node-type / attribute-name constants. No FFI deps.
//! - [`nsi-ffi-wrap`](https://crates.io/crates/nsi-ffi-wrap) -- FFI wrapper.
//! Provides [`Context`], the C-API loader (dynamic via `dlopen2` or static
//! via `link_lib3delight`), the parameter macros (`f32!`, `point_slice!`,
//! …), and [`FfiApiAdapter`] which exposes any pure-Rust [`Nsi`] impl
//! through the C API (handle-mapping is internal, via a factory closure).
//! - [`nsi-3delight`](https://crates.io/crates/nsi-3delight) -- helpers for
//! the 3Delight renderer (environment lights, shader graphs, etc.).
//! - [`nsi-toolbelt`](https://crates.io/crates/nsi-toolbelt) -- convenience
//! scene-construction helpers (handle generation, append/prepend,
//! transform shortcuts).
//! - [`nsi-jupyter`](https://crates.io/crates/nsi-jupyter) -- render into a
//! Jupyter notebook.
//! - [`nsi-sys`](https://crates.io/crates/nsi-sys) -- auto-generated bindings
//! for the NSI C header.
//! - [`nsi-procedural`](https://crates.io/crates/nsi-procedural) -- scaffolding
//! for writing procedural-node plugins.
//!
//! The `nsi` crate re-exports everything from `nsi-ffi-wrap` (which itself
//! pulls types and constants from `nsi-trait`) so a typical user only needs
//! to depend on `nsi`.
//!
//! ## Typed Attribute Names
//!
//! The standard ɴsɪ attribute names are exposed as typed constants of
//! [`Attribute<T>`], where `T` describes the data shape the attribute
//! accepts. Examples:
//!
//! ```text
//! Attribute<f32> -- e.g. nsi::FIELD_OF_VIEW
//! Attribute<i32> -- e.g. nsi::U_COUNT, nsi::U_ORDER
//! Attribute<[f32]> -- e.g. nsi::U_KNOT, nsi::TRIM_CURVES_KNOT
//! Attribute<[Point3F32]> -- nsi::POSITION (length always divisible by 3)
//! Attribute<[Point4F32]> -- nsi::WEIGHTED_POSITION (rational xyzw points)
//! Attribute<Matrix4F64> -- nsi::MATRIX
//! ```
//!
//! The Rust constant identifiers are derived from the new ɴsɪ naming
//! convention (see the `naming-convention.md` chapter in the ɴsɪ spec). The
//! wire-side string literals each constant points to currently still hold
//! the legacy names (`"fov"`, `"nu"`, `"transformationmatrix"`, …) so the
//! constants work against today's renderers; switching to the new wire
//! names is a one-line change per constant when the renderer ships them.
//!
//! Renderer-specific or experimental attributes are added in their own
//! crates without touching this one -- `Attribute::new("custom_name")` is
//! `const`, so consumers declare their own typed constants.
//!
//! Note: the parameter macros (`nsi::f32!`, `nsi::point_slice!`, …)
//! currently accept the wire-side string literal directly; static
//! verification against [`Attribute<T>`] is in progress.
//!
//! ## Getting Pixels
//!
//! The crate has support for streaming pixels from the renderer, via callbacks
//! (i.e. closures) during and/or after rendering via the [`output`] module.
//! This module is enabled through the feature of the same name (see below).
//!
//! It should be straightforward to create an `async` implementation with this
//! or use channels to stream pixels back to a main thread (see the
//! `interactive` example).
//!
//! ## Cargo Features
//!
//! * [`output`] -- Add support for streaming pixels from the renderer to the
//! calling context via closures.
//!
//! * [`jupyter`] -- Add support for rendering to Jupyter notebooks (when using
//! a [Rust Jupyter kernel](https://github.com/google/evcxr)).
//!
//! * [`toolbelt`] -- Add convenience methods that work with a [`Context`].
//!
//! * [`delight`] -- Add some nodes & shaders specific to 3Delight.
//!
//! * `nightly` -- Enable some unstable features (suggested if you build with a
//! `nightly` toolchain)
//!
//! * `ustr_handles` -- use [`ustr`](https://crates.io/crates/ustr) for node
//! handles. This will give a you a speed boost if your node names aren't
//! changing while an app using ɴsɪ is running but is not advised otherwise
//! (`ustr` are never freed).
//!
//! ## Linking Style
//!
//! The 3Delight dynamic library (`lib3delight`) can either be linked to during
//! build or loaded at runtime.
//!
//! By default the lib is loaded at runtime.
//!
//! * Load `lib3deligh` at runtime (default). This has several advantages:
//!
//! 1. If you ship your application or library you can ship it without the
//! library. It can still run and will print an informative error if the
//! library cannot be loaded.
//!
//! 2. A user can install an updated version of the renderer and stuff will
//! ‘just work’.
//!
//! * Dynamically link against `lib3delight`.
//!
//! * `lib3delight` becomes a dependency. If it cannot be found your lib/app
//! will not load/start.
//!
//! * The feature is called `link_lib3delight`.
//!
//! * You should disable default features (they are not needed/used) in this
//! case:
//!
//! ```toml
//! [dependencies]
//! nsi = { version = "0.7", default-features = false, features = ["link_lib3delight"] }
//! ```
//!
//! * Download `lib3delight` during build.
//!
//! * `lib3delight` is downloaded during build. Note that this may be an
//! outdated version. This feature mainly exists for CI purposes.
//!
//! * The feature is called `download_lib3delight`.
// Re-export everything from nsi_ffi_wrap, which includes Action from nsi-trait crate
pub use *;