Integrating OpenMfx as an host#
For a basic example, here is sdk/examples/cpp/host/main.cpp
:
1#include <OpenMfx/Sdk/Cpp/Host/Host>
2#include <OpenMfx/Sdk/Cpp/Host/MeshEffect>
3#include <OpenMfx/Sdk/Cpp/Host/EffectRegistry>
4#include <OpenMfx/Sdk/Cpp/Host/EffectLibrary>
5#include <OpenMfx/Sdk/Cpp/Host/MeshProps>
6#include <OpenMfx/Sdk/Cpp/Host/AttributeProps>
7
8#include <exception>
9#include <iostream>
10#include <vector>
11#include <array>
12#include <stdexcept>
13
14/**
15 * To illustrate this demo, we use an arbitrary mesh structure, here a very
16 * simple one, that can only handle triangle meshes.
17 */
18struct MyMeshStruct {
19 std::vector<std::array<float, 3>> points;
20 std::vector<std::array<int, 3>> triangles;
21 bool isInput = true;
22};
23
24/**
25 * We subclass the OpenMfx::MfxHost base class in order to tell how to expose
26 * our custom MyMeshStruct structure as an OpenMfx mesh to the effect, and how
27 * to read back.
28 */
29class MyHost : public OpenMfx::Host {
30 // Callback triggered at the beginning of meshEffectSuite->inputGetMesh()
31 OfxStatus BeforeMeshGet(OpenMfx::Mesh* mfxMesh) override;
32 // Callback triggered at the beginning of meshEffectSuite->inputReleaseMesh()
33 OfxStatus BeforeMeshRelease(OpenMfx::Mesh* mfxMesh) override;
34 // (The callbacks are defined bellow, after the main function)
35};
36
37// Just get an hardcoded mesh for the sake of the example.
38const MyMeshStruct& getSomeMesh();
39
40void run(const char* filepath) {
41 // We first create the main host object. It must outlive any other OpenMfx
42 // related operation so it is usually a singleton used throughout the whole
43 // application.
44 MyHost host;
45 // (Optional: We alias some host functions to be able to easily use the
46 // OpenMfx API from within this function.)
47 const auto& propertySuite = host.propertySuite;
48
49 // We then get the global registry. This is a singleton that avoid loading
50 // multiple times the same EffectLibrary when multiple parts of the program
51 // want to use effects from the same .ofx file.
52 auto& registry = OpenMfx::EffectRegistry::GetInstance();
53 // Before using the registry, we must connect it to our host.
54 registry.setHost(&host);
55
56 // We can now load an effect library (or retrieve it if it was already loaded)
57 // if the file does not exist, or is not an OpenMfx library, this returns
58 // a null pointer.
59 // NB: It is important to call releaseLibrary once we no longer use it.
60 OpenMfx::EffectLibrary* library = registry.getLibrary(filepath);
61 if (nullptr == library) {
62 throw std::runtime_error("Could not load binary!");
63 }
64
65 // An OpenFX library may contain both Mesh effects and Image effects, so it is
66 // possible that effectCount (the number of Mesh effects found) is zero.
67 if (library->effectCount() == 0) {
68 throw std::runtime_error("No Mesh Effect found in library!");
69 }
70
71 // At this point, the effect's load() action has still not been called, but
72 // we can list its identifier and version number. This can be interesting to
73 // quickly list all available effects at startup.
74 std::cout << "Using plugin '" << library->effectIdentifier(0) << "'" << std::endl;
75
76 // The effect registry handles the loading and description of the effect for us
77 // (and remembers it from one use of the effect to another one, there is no
78 // need to call the describe action more than once for the whole application).
79 OpenMfx::MeshEffect* effectDescriptor = registry.getEffectDescriptor(library, 0);
80 if (!effectDescriptor) {
81 throw std::runtime_error("Could not load effect!");
82 }
83
84 // For each use of the effect, we instantiate it. An instance is allowed to
85 // retain "memory" from previous calls to the main cook function, which is why
86 // you may want to use multiple instances of the same effect. It also stores
87 // the value of its input meshes and parameters.
88 OpenMfx::MeshEffect* effectInstance;
89 if (!host.CreateInstance(effectDescriptor, effectInstance)) {
90 throw std::runtime_error("Could not create instance!");
91 }
92
93 // Create the mesh structures for input/output data
94 // We mark them with a boolean for BeforeMeshGet to know which one is which.
95 MyMeshStruct inputMeshData = getSomeMesh();
96 inputMeshData.isInput = true;
97 MyMeshStruct outputMeshData;
98 outputMeshData.isInput = false;
99
100 // Set the inputs meshes of the effect instance
101 // NB: the structure where the output mesh is stored is also considered as an
102 // "input" in OpenMfx' vocable (maybe not the best idea ever but it behaves so
103 // similarily all the time there was no clear reason to give it a different
104 // name and duplicate all the functions). An effect has at most one output,
105 // which is always called kOfxMeshMainOutput.
106 int inputCount = effectInstance->inputs.count();
107 std::cout << "Found " << inputCount << " inputs:" << std::endl;
108 for (int i = 0; i < inputCount; ++i) {
109 auto& input = effectInstance->inputs[i];
110 std::cout << " - " << input.name() << std::endl;
111 if (input.name() != kOfxMeshMainOutput) {
112 // We provide a pointer to our own data structure but do convert to OpenMfx
113 // yet because inputGetMesh might not be called. We will do it in
114 // BeforeMeshGet instead.
115 propertySuite->propSetPointer(&input.mesh.properties, kOfxMeshPropInternalData, 0, (void*)&inputMeshData);
116 }
117 else {
118 propertySuite->propSetPointer(&input.mesh.properties, kOfxMeshPropInternalData, 0, (void*)&outputMeshData);
119 }
120 }
121
122 // Set the parameters of the effect instance
123 int paramCount = effectInstance->parameters.count();
124 std::cout << "Found " << paramCount << " parameters:" << std::endl;
125 for (int i = 0; i < paramCount; ++i) {
126 const auto& param = effectInstance->parameters[i];
127 std::cout << " - " << param.name << std::endl;
128 }
129
130 // Call the main function, which computes the output of the effect, given its
131 // input mesh(es) and parameters. The cook action can be called many times.
132 host.Cook(effectInstance);
133
134 std::cout << "Output mesh has:" << std::endl;
135 std::cout << " - " << outputMeshData.points.size() << " points" << std::endl;
136 std::cout << " - " << outputMeshData.triangles.size() << " triangles" << std::endl;
137
138 // Once done with the instance, we must destroy it.
139 host.DestroyInstance(effectInstance);
140
141 // Release the library, this means we will no longer use the effectDescriptor
142 // and if the registry figures out that nobody is using the library any more,
143 // the descriptor is destroyed and the library unloaded.
144 registry.releaseLibrary(library);
145}
146
147int main(int argc, char** argv) {
148 const char* filepath = "../../c/plugins/Debug/OpenMfx_Example_C_Plugin_mirror.ofx";
149 if (argc > 1) filepath = argv[1];
150
151 try {
152 run(filepath);
153 }
154 catch (const std::runtime_error& err) {
155 std::cerr << err.what() << std::endl;
156 return EXIT_FAILURE;
157 }
158 return EXIT_SUCCESS;
159}
160
161
162OfxStatus MyHost::BeforeMeshGet(OpenMfx::Mesh* mfxMesh) {
163 MyMeshStruct* myMesh = nullptr;
164 propertySuite->propGetPointer(&mfxMesh->properties, kOfxMeshPropInternalData, 0, (void**)&myMesh);
165
166 if (myMesh == nullptr)
167 return kOfxStatErrFatal;
168
169 if (!myMesh->isInput)
170 return kOfxStatOK;
171
172 OpenMfx::MeshProps props;
173 props.pointCount = static_cast<int>(myMesh->points.size());
174 props.cornerCount = 3 * static_cast<int>(myMesh->triangles.size());
175 props.faceCount = static_cast<int>(myMesh->triangles.size());
176 props.constantFaceSize = 3;
177 props.noLooseEdge = true;
178 props.setProperties(propertySuite , &mfxMesh->properties);
179
180 // Convert to attributes
181 int attributeCount = mfxMesh->attributes.count();
182 for (int i = 0; i < attributeCount; ++i) {
183 auto& attribute = mfxMesh->attributes[i];
184 if (attribute.attachment() == OpenMfx::AttributeAttachment::Point && attribute.name() == kOfxMeshAttribPointPosition) {
185 attribute.setComponentCount(3);
186 attribute.setType(OpenMfx::AttributeType::Float);
187 attribute.setData((void*)myMesh->points.data());
188 attribute.setByteStride(3 * sizeof(float));
189 }
190 else if (attribute.attachment() == OpenMfx::AttributeAttachment::Corner && attribute.name() == kOfxMeshAttribCornerPoint) {
191 attribute.setComponentCount(1);
192 attribute.setType(OpenMfx::AttributeType::Int);
193 attribute.setData((void*)myMesh->triangles.data());
194 attribute.setByteStride(3 * sizeof(float));
195 }
196 else if (attribute.attachment() == OpenMfx::AttributeAttachment::Face && attribute.name() == kOfxMeshAttribFaceSize) {
197 attribute.setComponentCount(1);
198 attribute.setType(OpenMfx::AttributeType::Int);
199 attribute.setData(nullptr); // we use kOfxMeshPropConstantFaceSize instead
200 attribute.setByteStride(0);
201 }
202 }
203
204 return kOfxStatOK;
205}
206
207OfxStatus MyHost::BeforeMeshRelease(OpenMfx::Mesh* mfxMesh) {
208 MyMeshStruct* myMesh = nullptr;
209 propertySuite->propGetPointer(&mfxMesh->properties, kOfxMeshPropInternalData, 0, (void**)&myMesh);
210
211 if (myMesh == nullptr)
212 return kOfxStatErrFatal;
213
214 if (myMesh->isInput)
215 return kOfxStatOK;
216
217 OpenMfx::MeshProps props;
218 props.fetchProperties(propertySuite, &mfxMesh->properties);
219
220 if (props.constantFaceSize != 3)
221 return kOfxStatErrUnsupported;
222
223 myMesh->points.resize(props.pointCount);
224 myMesh->triangles.resize(props.faceCount);
225
226 // Convert from attributes
227 int attributeCount = mfxMesh->attributes.count();
228 OpenMfx::AttributeProps attributeProps;
229 for (int i = 0; i < attributeCount; ++i) {
230 auto& attribute = mfxMesh->attributes[i];
231 attributeProps.fetchProperties(propertySuite, &attribute.properties);
232 if (attribute.attachment() == OpenMfx::AttributeAttachment::Point && attribute.name() == kOfxMeshAttribPointPosition) {
233 if (attributeProps.type != OpenMfx::AttributeType::Float)
234 return kOfxStatErrUnsupported;
235 for (int j = 0; j < props.pointCount; ++j) {
236 float* P = attributeProps.at<float>(j);
237 for (int k = 0; k < 3; ++k) {
238 myMesh->points[j][k] = P[k];
239 }
240 }
241 }
242 else if (attribute.attachment() == OpenMfx::AttributeAttachment::Corner && attribute.name() == kOfxMeshAttribCornerPoint) {
243 if (attributeProps.type != OpenMfx::AttributeType::Int)
244 return kOfxStatErrUnsupported;
245 for (int j = 0; j < props.faceCount; ++j) {
246 for (int k = 0; k < 3; ++k) {
247 int C = *attributeProps.at<int>(3 * j + k);
248 myMesh->triangles[j][k] = C;
249 }
250 }
251 }
252 }
253
254 return kOfxStatOK;
255}
256
257const MyMeshStruct& getSomeMesh() {
258 static MyMeshStruct inputMeshData = {
259 {
260 {-1.0, -1.0, -1.0},
261 {1.0, -1.0, -1.0},
262 {-1.0, 1.0, -1.0},
263 {1.0, 1.0, -1.0},
264 {-1.0, -1.0, 1.0},
265 {1.0, -1.0, 1.0},
266 {-1.0, 1.0, 1.0},
267 {1.0, 1.0, 1.0},
268 },
269 {
270 {0, 2, 3}, {0, 3, 1},
271 {4, 5, 7}, {4, 7, 6},
272 {0, 1, 5}, {0, 5, 4},
273 {1, 3, 7}, {1, 7, 5},
274 {3, 2, 6}, {3, 6, 7},
275 {2, 0, 4}, {2, 4, 6},
276 },
277 };
278 return inputMeshData;
279}