Hey Chris,
Sorry about those docs, they still need to be updated (it's a high priority).
You can extend AX with your own functions via the API as long as you don't mind brining in LLVM headers. You can build any function you want using the `ax::codegen::FunctionBuilder` and register it on a compiler using the `ax::codegen::FunctionRegistry` - this will allow you to call it from AX. Adding a texture lookup function makes total sense and is something we'd want to natively support as designing functions which do things like internal caching across threads isn't brilliant with the current API (and I assume your texture function will want to do something like this for, say, recently accessed arbitrary files).
Still, this is totally doable, here's an example (I have not compiled/tested this):
```c++
/// Some singleton class which handles reading and resolving textures from filenames.
/// Methods will all need to be thread safe if thread local storage isn't used
/// and recent IO should be cached, etc, etc.
struct TextureInterface
{
inline static auto& Get() { static TextureInterface I; return I; }
// an actualy texture with an "eval" method on it that takes uv coords
struct Texture2D;
// get a texture
const Texture2D* get(const char*);
private:
TextureInterface() = default;
};
/// along with other standard header you need to import codegen stuff
#include <openvdb/ax/codegen/Functions.h>
#include <openvdb/ax/codegen/FunctionTypes.h>
#include <openvdb/ax/codegen/FunctionRegistry.h>
/// Function are registered with a callback that essentially only builds/compiles
/// the function if it's been used. Callbacks return FunctionGroups which represent
/// a function "myfunction" with all its possible signatures.
///
/// See PointFunctions.cc, VolumeFunctions.cc and StandatdFunctions.cc for a whole
// lot more examples.
///
inline openvdb::ax::codegen::FunctionGroup::UniquePtr
texture_function_creator(const openvdb::ax::FunctionOptions& op)
{
static auto tex = [](float u, float v,
const openvdb::ax::codegen::String* texture)
{
// Could instead have one per thread, but this requires static
// thread local i.e. tbb::enumerable_thread_specific. See
// rand() implementation in StandardFunctions.cc.
TextureInterface& interface = TextureInterface::Get(); // each thread shares the same instance
Texture2D* tex = interface.get(texture->c_str());
return tex ? tex->eval(u, v) : 0.0f; // tex might not exist
}
/// The FunctionBuilder uses the builder pattern to create a function object
return openvdb::ax::codegen::FunctionBuilder("texture")
.addSignature<float(float,float,openvdb::ax::codegen::String*)>
((float(*)(float,float,openvdb::ax::codegen::String*))(tex)) // add the function
.setArgumentNames({"u", "v", "filename"}) // set its argument names (optional, only for used printing)
.setDocumentation("Evaluate a 2D texture value.") // set docs (optional, only for used printing)
.get();
}
/// When we create the compiler, we now set our own function registry:
auto compiler = openvdb::ax::Compiler::create();
auto functionRegistry = openvdb::ax::codegen::createDefaultRegistry();
functionRegistry->insert("texture", texture_function_creator);
compiler.setFunctionRegistry(std::move(functionRegistry));
/// do compile and execute.
```
You could then invoke this within AX, i.e:
```
float u = ...
float v = ...
float result = texture(u, v, "/path/to/a/texture.tex");
```
A native solution will probably end up with some custom framework for better internal caching. We also want to add plugin support in the future too.
Nick