NUKE's 3D architecture
======================

Introduction
------------

NUKE's 3D support is based around the ``DD::Image::GeoOp`` base class. This is
the 3D equivalent of the ``DD::Image::Iop`` class: it provides all the common
parts of handling 3D data in NUKE.

There are specialised subclasses of ``GeoOp`` for creating and modifying
geometry (``DD::Image::SourceGeo`` and ``DD::Image::ModifyGeo`` respectively),
although they're a convience rather than a requirement: it's possible for any
subclass of GeoOp to create or modify geometry.

There's more detailed information about ``GeoOp`` and its subclasses on the
:ref:`writing-geo-ops` page.

Ops which take a 3D scene and produce a 2D (or deep) image are referred to as
renderers. There are some base classes which the NDK provides that may help in
implementing these, such as ``DD::Image::RenderScene``. NUKE's
``ScanlineRenderer`` inherits from this class, for example.

When passing 3D data through the DAG, NUKE uses a flattened representation
rather than a full scene graph. A scene consists primarily of a list of
objects, where each object is specified in a local coordinate system and has a
matrix to transform it into world coordinates. A scene also has a list of lights
and cameras.

3D objects are represented individually by the ``GeoInfo`` class. This stores
the world transform matrix and the lists of points, vertices, primitives and
attributes for the object. Additional information like normals and texture
coordinates is stored in specially named attributes ("N" and "uv"
respectively). Attributes are stored in groups which determine their
cardinality: Group_Points attributes will have one value per point,
Group_Vertices attributes will have one per vertex, Group_Primitives has one
per primitive and so on. Attributes can be added dynamically to any of these
groups. Attribute names must be unique within their group, but otherwise they
can have whatever name you like and there is no limit to the number you can
add.

A GeoInfo also has a pointer to a material. There is a single material at most
per object. Objects which don't have a material will show up with a default
gray colour in the viewer and will come out black in a render.

Materials in NUKE's 3D system can be any 2D op. In the general case the output
of the 2D op will be used to texture the geometry. There is a special type of
Iop, ``DD::Image::Material``, which is a base class for ops which provide more
complex material behaviour.


Core classes
------------

.. cpp:class:: DD::Image::Scene

  Contains all the information about a 3D scene, including lights, cameras and
  a flattened list of the geometry.

.. cpp:class:: DD::Image::GeometryList

  A list of geometry items which packs the data together in a cache-friendly
  arrangement. This is used to hold the geometry data for a Scene.

.. cpp:class:: DD::Image::GeoInfo

  A single piece of geometry, consisting of:

  - a list of points;
  - a list of primitives, whose vertices refer to indices in the points list;
  - a collection of attributes;
  - a pointer to a material op; and
  - a local-to-world transformation matrix.

.. cpp:class:: DD::Image::Primitive

  A simple geometric object such as a point, triangle or polygon. This is a
  pure virtual base class. See the :ref:`3d-primitives` page for more details
  about these.

.. cpp:class:: DD::Image::Attribute

  A container for per-point, per-vertex, per-primitive or per-object data.
  Attributes have a name and an array of data items. A per-point attribute,
  for example, will have one array entry per point in the object. The group of
  an attribute is whether it's per-point, per-vertex, etc.

  Attributes can hold different types of data. For example, vertex normals are
  stored in an attribute where every data item is a Vector3. Other available
  types include ints, floats, strings, matrices and arbitrary pointers.

  Attribute names are unique for each group. You can have attributes with the
  same name in different groups, but you can't have two attributes with the
  same name in the same group.

  Attributes are usually accessed via the AttribContext class rather than
  directly. This gives an extra level of safety: the AttribContext will check
  that you're getting the correct type of data out of the Attribute. You would
  generally only go directly to the Attribute when speed was of the essence.

.. cpp:class:: DD::Image::Camera

  A camera, obviously.

.. cpp:class:: DD::Image::Light

  A light. There are a few different kinds of light that NUKE supports:
  directional lights, point lights and so on.

.. cpp:class:: DD::Image::Material

  A base class for Iops which provide special behaviour (custom shading, etc.)
  when used as the material for a 3D object.

  Any 2D op can be used as a material. If the op is not a subclass of Material,
  it will be used as a 2D texture.


Coordinate systems
------------------

NUKE uses a right-handed coordinate system.

Points for each GeoInfo are defined in object (local) coordinates. To convert
these into absolute positions in world-space (world coordinates), they must be
multiplied by the GeoInfo's matrix.

Axis ops can be used to define new coordinate systems. They provide controls
for scaling, rotation, translation, skew, etc. The op combines these into a
matrix which can be used to transform coordinates from the Axis' space into
world space.

Matrices in NUKE use column-major ordering, to match OpenGL.
