OpenVDB  8.1.0
AX

AX Language Documentation

Welcome to the AX Language documentation. These docs provide detailed information on the AX language including syntax, data types, available functionality (including OpenVDB specific methods) execution structure and control flow. See the OpenVDB AX documentation for the C++ developer documentation.
Note
Some sections of this document are still to be completed. Please get in touch should you have any questions!

Contents

Introduction

AX is a fast and portable JIT compiled expression language, designed for writing volume and point kernels represented as OpenVDB grids. It was originally developed by the RNDFX team at DNEG to provide a more consistent way for artists to be able to interface with OpenVDB data across a variety of in house and external digital content creation software, whilst retaining the performance achievable by custom C++ operators.
The language is predominately inspired by SideFX's VEX language which itself takes elements from C, C++ and the Renderman Shading Language. The design of AX uses concepts from these languages and refrains from deviating from their syntax too significantly. Specifically, it does not aim to change what are already well established and heavily used language syntaxes within the Visual Effects industry; on the contrary, AX aims to provide an interface to which a user with previous or little programming experience can easily transition to. However, it does introduce new concepts where the creators deemed necessary to provide the best and most representative syntax relating to the design for OpenVDB volume and point modification.

AX Programs

AX programs represent a set of statements which read and write to geometry, with each program designed in such a way that it can be executed in a highly parallelized framework to access and update individual geometric components. For example, consider a mesh with point, vertex and primitive attributes. AX programs are designed to run independently across "elements" of input geometry and process "attributes" from a particular element. In this case, the element level would correlate to either points, vertexes or primitives, and a single element would be a unique instance of one of these. These programs are at their most efficient writing to the currently processing element, however do provide varying levels of access to the geometry as a whole.
Note that native AX only supports the modification of OpenVDB data. Currently, that means that native AX programs can be written and built for execution over OpenVDB Points or OpenVDB Volumes. For any given AX program, the execution context (what it's processing) may change what functionality is available and, more importantly, the behaviour of reading/writing from attributes. See the axexecutionxcontext for details.
Whilst powerful, AX is not a catch all replacement for all types of VDB operations. Some of this is related to its infancy i.e. missing native support for some useful functions, the foundations of which may be supported by AX but have not yet been exposed. However there may be certain paradigms which AX is better tailored to support than others. Grid reductions, for example, typically require the passing of a state between programs; a design pattern which AX is less equipped to handle. AX kernels can be abstractly thought of as a "foreach()" function (see A Program Example) and whilst there are ways to achieve reductions, it is an example which would be better suited to a custom C++ implementation.
For details of extending AX for custom geometry, see the OpenVDB AX developer documentation. Note that custom geometry support requires C++ extension.

A Program Example

Before getting into the technical depths of the language, it's a good idea to observe a small but complete AX program which could be compiled and executed. To begin with, here is a very simple example of a program that reads and writes to a particular attribute:
a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b
a + b
a - b
a * b
a / b
a % b
a & b
a | b
a ^ b
a << b
a >> b
a == b
a != b
a < b
a > b
a <= b
a >= b
a && b
a || b
+a
-a
~a
!a
++a
--a
a++
a--
a[]
a[,]
a.b
a(...)
a, b
a ? b : c
{ a,b ... }

Binary Operators

Binary operators are the most common inbuilt operators for AX types. These operators take two inputs; a left hand side and a right hand side. There exists a number of valid combinations of AX types with different binary operators, as well as different implicit casting schemes depending on the binary operation being performed.

Assignments

Assignment operators are the only way to directly modify the value of local and external data (except for Functions which take references). The assignment operators have the following forms:
Operator NameOperator SyntaxReturns
Simple assignmentlhs = rhs Reference to lhs
Addition assignmentlhs += rhs Reference to lhs
Subtraction assignmentlhs -= rhs Reference to lhs
Multiplication assignmentlhs *= rhs Reference to lhs
Division assignmentlhs /= rhs Reference to lhs
Modulo assignmentlhs %= rhs Reference to lhs
Bitwise AND assignmentlhs &= rhs Reference to lhs
Bitwise OR assignmentlhs |= rhs Reference to lhs
Bitwise XOR assignmentlhs ^= rhs Reference to lhs
Bitwise shift left assignmentlhs <<= rhs Reference to lhs
Bitwise shift right assignmentlhs >>= rhs Reference to lhs
These can be further categorised into two types; direct assignment, which is comprised only of the simple assignment operator, and compound assignments, which is the set of all other assignment operators.
Direct Assignment
Direct assignments replace the contents of the lhs with a copy of the rhs:
lhs = rhs
The rhs side is not modified and the lhs is not read - only written to. If the rhs type does not match the lhs type, the rhs is first implicitly converted into a new temporary with the lhs type before being copied into the lhs. The following combination of AX type categories are supported for direct assignment (if an operation is not listed, it is not supported):
Left Operand TypeBinary Op(s)Right Operand TypeDescription
scalar= scalar On type mismatch, right hand side is copied and implicitly cast to the left hand side type.
vector = scalar Each element of the vector is set to the right hand side scalar. i.e.
a[0] = b; ... a[n-1] = b;
where n is the size of the vector. If the scalar type does not match element type of vector, the scalar is copied and implicitly cast to that type.
vector

Component wise assignment i.e.
a[0] = b; ... a[n-1] = b;
where n is the size of the vector.

Access [a] Access [a,b] Result
A[0] A[0,0] $ a_{00} $ A[0] A[0,0] $ a_{00} $
A[1] A[0,1] $ a_{01} $ A[1] A[0,1] $ a_{01} $
A[2] A[0,2] $ a_{02} $ A[2] A[0,2] $ a_{02} $
A[3] A[1,0] $ a_{10} $ A[3] A[0,3] $ a_{03} $
A[4] A[1,1] $ a_{10} $ A[4] A[1,0] $ a_{10} $
A[5] A[1,2] $ a_{12} $ A[5] A[1,1] $ a_{11} $
A[6] A[2,0] $ a_{20} $ A[6] A[1,2] $ a_{12} $
A[7] A[2,1] $ a_{21} $ A[7] A[1,3] $ a_{13} $
A[8] A[2,2] $ a_{22} $ A[8] A[2,0] $ a_{20} $
A[9] A[2,1] $ a_{21} $
A[10] A[2,2] $ a_{22} $
A[11] A[2,3] $ a_{23} $
A[12] A[3,0] $ a_{30} $
A[13] A[3,1] $ a_{31} $
A[14] A[3,2] $ a_{32} $
A[15] A[3,3] $ a_{33} $

Other

A number of uncategorised operators that are more general are documented in this section. They have the following forms:
Operator NameOperator SyntaxReturns
Call / Explicit Cast a(...) Returns the result of a function call or inbuilt explicit cast
Comma a,  b Returns the value of b after chained evaluation
Ternary Conditional a ? b : c b if a is true, c otherwise.
Vector/Matrix Initializer { a... } Returns a temporary vector or matrix
Call / Explicit Cast
The function call operator provides the ability to invoke functions or invoke a supported explicit cast. It has the form:
func(a, b, c ...)
Where func is a function name or a valid explicit cast typename. Explicit casts are only supported for scalar types. For example:
1
()
Parenthesis
Left-to-right
2
a++ a--
Suffix/postfix axopunincdec
type()
Functional cast
a()
Function call
a[] .
axopaccess
3
++a --a
Prefix axopunincdec
Right-to-left
+a -a
Unary plus and minus
! ~
axopbinlogical NOT and axopunlogical NOT
4
a*b a/b ab
Multiplication, division, and remainder
Left-to-right
5
a+b a−b
Addition and subtraction
6
<< >>
Bitwise left shift and right shift
7
< <=
For axopcomparison operators < and ≤ respectively
> >=
For axopcomparison operators > and ≥ respectively
8
== !=
For axopcomparison operators = and ≠ respectively
9
&
axopbinarithmetic AND
10
^
axopbinarithmetic XOR (exclusive or)
11
|
axopbinarithmetic OR (inclusive or)
12
&&
axopbinlogical AND
13
||
axopbinlogical OR
14
a?b:c
Ternary operator
Right-to-left
=
Direct assignment
+= -=
Compound assignment by sum and difference
*= /= %=
Compound assignment by product, quotient, and remainder
<<= >>=
Compound assignment by bitwise left shift and right shift
&= ^= |=
Compound assignment by bitwise AND, XOR, and OR
15
,
Comma
Left-to-right
Note that associativity of unary operators is redundant (unary prefix operators are always right-to-left and unary postfix operators are always left-to-right). Operators that have the same precedence are bound to their arguments in the direction of their associativity. For example, the expression a = b = c is parsed as a = (b = c), and not as (a = b) = c because of right-to-left associativity of assignment, but a + b − c is parsed (a + b) − c and not a + (b − c) because of left-to-right associativity of addition and subtraction. Note that this is based off the C++ operator precedence.


Tokens

Variable Identifiers

Literals

Literals can be thought of as constant values that are known at compile time. e.g.
i@a = 1; // the number 1 is a literal
As AX supports different bit width scalar literals, it also supports suffix literal tokens which, when using with a scalar literal, represent a literal of a specific size. AX also supports implicit casting for all assignment and arithmetic operations, so any literal can be used to assign any scalar a value.
TypeLiteral Tokens
boolTokens true and false
int32No suffix, automatically infered from integral literals
int64The letter l e.g.
int64 a = 10l;
floatThe letter f e.g.
float a = 0.0f;
doubleNo suffix, automatically infered from floating point literals.
stringCharacter strings wrapped in double quotes " "

Comments

Keywords

Reserved Keywords



Syntax

Attribute Access

External Parameter Access

Declarations

Scopes

Branching (if / else)

Loops



Functions

For a list of all functions, see the AX Function List.
AX supports a variety of mathematical, string, utility and custom function calls which can be used from within an AX program. Native function support is constantly growing and we encourage users who wish to see particular methods exposed and supported natively by AX to contact us. Whilst we can't guarantee to accommodate every function, these requests provide the developers insights into how the language is being used.
Function Execution Context
Some functions depend on the currently processing axexecutionxcontext to work correctly and will fail if called from an "invalid" context. Specifically, there exists functions which operate on OpenVDB points or OpenVDB volume grids and interact with the point or voxel data. Some of these methods are incompatible for the other grid type (for example, deleting a point from a Point Data Grid) and will result in the compilation failure of an AX program if an attempt is made it use them.

User Functions

User function declarations are not currently supported, but exist on the AX Roadmap as a near future feature.


AX VEX Support