Legacy Mesh Source Plugin Tutorial
From K-3D
Overview
In this tutorial we will create a very simple plugin for making a poly face like the one you see below. Once you complete this tutorial maybe you should read the Legacy Plugin Tutorial for a more general knowledge.

That face will have properties: width and height. The other properties that you see on the image above come somehow by "default". (inherited)
- Notice 1: This tutorial works on linux, if you work with windows you'll have to research how to do the same. If you want to make a Tutorial for windows you are very welcome to upload it to this wiki. (or you can leave in the discussion about this article your wish to have one. If there is enough people, probably we will come up with one tutorial)
- Notice 2: If you have a question, opinion, suggestion or improvement about this tutorial or about developing on k3d, please do not hesitate to contact us on the k3d-development mailing list. Also feel free to leave your contributions (orthographic and grammatical corrections too) on the discussion (tab above also) of this page. Joaquin
What you need
- A GNU\Linux installed
- K3d installed and the k3dsdk.
- See Getting Started and Generic Build.
- In my tests I used the k3dsdk from the .tar source code, because it failed with the installed k3dsdk.
- Warning: This tutorial is based on the k3d 0.6 API. If you are using k3d 0.7 some of the interfaces have been moved to the k3d::legacy interface. (as you can see in the doxygen files in the docs links)
- gcc and g++ installed. (version 4 would be ok)
- A text editor.
- Know something about the linux command line. (when you see a $ that means the command line)
- A basic knowledge of C++ and C.
- If you know C, you can read this quick tutorial to catch up. (could also be good if you know another language)
- If you want to learn C read this tutorial and then read the tutorial above.
- Here there is a list of C and C++ tutorials.
Compiling and loading
Showtime!
Here is the whole code, below are the steps for compiling and loading it into k3d.
#include <k3dsdk/imaterial.h>
#include <k3dsdk/node.h>
#include <k3dsdk/persistent.h>
#include <k3dsdk/material.h>
#include <k3dsdk/material_client.h>
#include <k3dsdk/measurement.h>
#include <k3dsdk/mesh_source.h>
#include <k3dsdk/module.h>
#include <iterator>
namespace libk3dmymodule
{
/////////////////////////////////////////////////////////////////////////////
// poly_face_implementation
class my_poly_face_implementation :
public k3d::material_client<k3d::mesh_source<k3d::persistent<k3d::node> > >
{
typedef k3d::material_client<k3d::mesh_source<k3d::persistent<k3d::node> > > base;
public:
my_poly_face_implementation(k3d::idocument& Document) :
k3d::material_client<k3d::mesh_source<k3d::persistent<k3d::node> > >(Document),
m_width(init_owner(*this) + init_name("width") + init_label(_("Width")) + init_description(_("Cube width")) + init_value(5.0) + init_step_increment(0.1) + init_units(typeid(k3d::measurement::distance))),
m_height(init_owner(*this) + init_name("height") + init_label(_("Height")) + init_description(_("Cube height")) + init_value(5.0) + init_step_increment(0.1) + init_units(typeid(k3d::measurement::distance)))
{
m_material.changed_signal().connect(make_reset_mesh_slot());
m_width.changed_signal().connect(make_reset_mesh_slot());
m_height.changed_signal().connect(make_reset_mesh_slot());
}
void on_create_mesh(k3d::mesh& Mesh)
{
Mesh.polyhedra.push_back(new k3d::polyhedron());
k3d::polyhedron& polyhedron = *Mesh.polyhedra.back();
const double width = m_width.value();
const double height = m_height.value();
k3d::imaterial* const material = m_material.value();
#if 1 //point block
k3d::point* a_point = new k3d::point(0, 0, 0);
Mesh.points.push_back(a_point);
k3d::point* b_point = new k3d::point(width, 0, 0);
Mesh.points.push_back(b_point);
k3d::point* c_point = new k3d::point(width, height, 0);
Mesh.points.push_back(c_point);
k3d::point* d_point = new k3d::point(0, height, 0);
Mesh.points.push_back(d_point);
#endif
#if 1 //edges block
boost::multi_array<k3d::split_edge*, 1> edges(boost::extents[5]);
edges[0] = new k3d::split_edge(a_point);
edges[1] = new k3d::split_edge(b_point);
edges[2] = new k3d::split_edge(c_point);
edges[3] = new k3d::split_edge(d_point);
for(unsigned long i = 0; i < 4; ++i)
edges[i]->face_clockwise = edges[(i+1)%4];
#endif
#if 1 //face block
k3d::face* const new_face = new k3d::face(edges[0], material);
polyhedron.faces.push_back(new_face);
#endif
assert_warning(is_valid(polyhedron));
}
void on_update_mesh(k3d::mesh& Mesh)
{
}
k3d::iplugin_factory& factory()
{
return get_factory();
}
static k3d::iplugin_factory& get_factory()
{
static k3d::plugin_factory<k3d::document_plugin<my_poly_face_implementation>, k3d::interface_list<k3d::imesh_source > > factory(
k3d::uuid(0x51ecc5ce, 0xf8bf43ed, 0xabba2ed7, 0x3e1ed1d3),
"MyPlugin",
"Generates a face",
"Tutorials",
k3d::iplugin_factory::EXPERIMENTAL);
return factory;
}
private:
k3d_data(double, immutable_name, change_signal, with_undo, local_storage, no_constraint, measurement_property, with_serialization) m_width;
k3d_data(double, immutable_name, change_signal, with_undo, local_storage, no_constraint, measurement_property, with_serialization) m_height;
};
/////////////////////////////////////////////////////////////////////////////
// poly_face_factory
k3d::iplugin_factory& poly_face_factory()
{
return my_poly_face_implementation::get_factory();
}
} // namespace libk3dmymodule
K3D_MODULE_START(libk3dmymodule, k3d::uuid(0xb1aa0867, 0x37404b32, 0xb80add63, 0xfb02fed0), Registry)
Registry.register_factory(libk3dmymodule::my_poly_face_implementation::get_factory());
K3D_MODULE_END
- Create somewhere a dir for your new plugin. A plugin goes inside a module that's why the name.
$ mkdir your_name_module
- So, copy-paste the code to a text file. For example with the name my_poly_face.cpp (must be .cpp) and save the file on the created directory.
- Let's compile! On the created directory run :
$ gcc -shared -o my_poly_face.so my_poly_face.cpp `k3d-config --cflags --libs` -lstdc++
- Probably that will fail. Could be because it doesn't find the headers (.h), it doesn't find the k3d-config or both.
- For the k3d commands: If you installed k3d from the tar.gz and you have another version which is from the distribution, you will probably want to use the newer version installed from the tar.gz. So for the following commands replace: (where /usr/local/k3d is the directory where k3d is installed)
- k3d -> /usr/local/k3d/bin/k3d
- k3d-uuidgen -> /usr/local/k3d/bin/k3d-uuidgen
- k3d-config -> /usr/local/k3d/bin/k3d-config
- Replace where needed. The command should look like:
$ gcc -shared -o my_poly_face.so my_poly_face.cpp `/usr/local/k3d/bin/k3d-config --cflags --libs` -lstdc++
- For the headers: If you have problems with the headers (i.e. you dont have them on /usr/include/ or you have two versions of k3d) you can tell the compiler where your headers are with the -I option.
- For example, if you extracted the .tar on /home/user/. The command should look like:
$ gcc -shared -o my_poly_face.so my_poly_face.cpp `k3d-config --cflags --libs` -lstdc++ -I/home/user/k3d-0.6.3.1/
- Where /home/user/k3d-0.6.3.1/ has the k3dsdk directory (also could be /usr/local/k3d/include/k3d/ if installed)
- If everything went well you compiled your new module. It is time to try it.
- First check it alone.
$ k3d --plugins `pwd` --log-level debug --ui none --exit
- That should print
INFO: Loading plugin module my_poly_face.so
- Which indicates that your new module was successfully loaded!
- Now with all the others.
- As you can see the --plugins option tells to load from the k3d directory (&) and from the current directory (pwd).
$ k3d --plugins "&:`pwd`" --log-level debug
- That should give you a screen like this:

- On the menu Create->Tutorials->MyPlugin:

- Play with the width and height properties.
Analyzing the code
By analyzing the code we will see which parts of the code you have to change in order to create new plugins. (that creates meshes)
The mesh generation
At this point we analyze the core of our plugin. This is where you will probably want to work to make new objects.
- Let's analyze the code step by step in the on_create_mesh() function:
- First the function receives as an argument the mesh where it can work (a "pointer" in fact).
void on_create_mesh(k3d::mesh& Mesh)
{
- It creates a new k3d polyhedron and puts it on the mesh with the push_back function:
- Then it asks for that polyhedron an points to it with the polyhedron variable.
Mesh.polyhedra.push_back(new k3d::polyhedron());
k3d::polyhedron& polyhedron = *Mesh.polyhedra.back();
- Those lines are the same as writing:
k3d::polyhedron& polyhedron = *(k3d::polyhedron*)new k3d::polyhedron();
Mesh.polyhedra.push_back(&polyhedron);
- It evaluates the values of the properties:
const double width = m_width.value();
const double height = m_height.value();
k3d::imaterial* const material = m_material.value();
- Now it will use those properties and the polyhedron mesh to create the object.
- First it creates the points.
- Then the edges.
- Then the faces.
The points
- This block creates the points you see in the figure. (where w=width and h=height)

#if 1 //point block
k3d::point* a_point = new k3d::point(0, 0, 0);
- The k3d::point is the class for representing points on the k3d meshes. (k3d::point doc)
Mesh.points.push_back(a_point);
- It puts the point into the mesh.
k3d::point* b_point = new k3d::point(width, 0, 0);
Mesh.points.push_back(b_point);
k3d::point* c_point = new k3d::point(width, height, 0);
Mesh.points.push_back(c_point);
k3d::point* d_point = new k3d::point(0, height, 0);
Mesh.points.push_back(d_point);
#endif
- If you comment the other two blocks of code setting the #if 1 to #if 0. You can compile and run k3d and you'll see how the vertices are created independent from the faces. As you can see in the image:

The edges
- If you commented the edges block, uncomment it.
#if 1 //edges block
boost::multi_array<k3d::split_edge*, 1> edges(boost::extents[5]);
- Here it creates a multidimensional array of the boost library, called edges.
- boost::multi_array<k3d::split_edge*, 1> creates a multidimensional array of pointer to k3d::split_edge of dimension 1.
- (boost::extents[5]) tells how many elements the array has.
- Here it creates a multidimensional array of the boost library, called edges.
edges[0] = new k3d::split_edge(a_point);
- Here it creates an edge (k3d::split_edge doc). It only needs one vertex for its creation, later we set the other vertices that define the edge.
edges[1] = new k3d::split_edge(b_point);
edges[2] = new k3d::split_edge(c_point);
edges[3] = new k3d::split_edge(d_point);
for(unsigned long i = 0; i < 4; ++i)
edges[i]->face_clockwise = edges[(i+1)%4];
- Here we set the other vertices that define the edge. As you can see is a loop that makes the face we want to construct as in the image:

#endif
- If you compile this code with the face block commented it will run, but you won't see any difference, because the edges are drawn when there is a face. Which is the next step.
The face
#if 1 //face block
k3d::face* const new_face = new k3d::face(edges[0], material);
- The k3d::face is the class for representing faces on the k3d meshes. (kd3::face doc)
- It needs the first edge of the loops of edges that creates the face.
- The k3d::face is the class for representing faces on the k3d meshes. (kd3::face doc)
polyhedron.faces.push_back(new_face);
- Finally it inserts it on the mesh.
Final Checks
For debugging purposes we check that what we made is a valid mesh. That's all
assert_warning(is_valid(polyhedron));
- In case you make a solid object you can also use the assert_warning(is_solid(polyhedron)); check.
The properties
- Here we are going to see how to add a new property.
- See the lines:
m_width(init_owner(*this) + init_name("width") + init_label(_("Width")) + init_description(_("Cube width")) + init_value(5.0) + init_step_increment(0.1) + init_units(typeid(k3d::measurement::distance))),
m_height(init_owner(*this) + init_name("height") + init_label(_("Height")) + init_description(_("Cube height")) + init_value(5.0) + init_step_increment(0.1) + init_units(typeid(k3d::measurement::distance)))
...
m_width.changed_signal().connect(make_reset_mesh_slot());
m_height.changed_signal().connect(make_reset_mesh_slot());
...
private:
k3d_data(double, immutable_name, change_signal, with_undo, local_storage, no_constraint, measurement_property, with_serialization) m_width;
k3d_data(double, immutable_name, change_signal, with_undo, local_storage, no_constraint, measurement_property, with_serialization) m_height;
- As you can see those lines tell k3d that we have two properties that are float numbers.
- If you want to add a new property look on the modules/ directory of the k3d source code for a module that has a property similar to the one you want.
- For example we add an int property, that would be the number of rows. (taken from the grid module)
m_rows(init_owner(*this) + init_name("rows") + init_label(_("Rows")) + init_description(_("Row number (Y axis)")) + init_value(1) + init_constraint(constraint::minimum(1L)) + init_step_increment(1) + init_units(typeid(k3d::measurement::scalar))),
m_width(init_owner(*this) + init_name("width") + init_label(_("Width")) + init_description(_("Cube width")) + init_value(5.0) + init_step_increment(0.1) + init_units(typeid(k3d::measurement::distance))),
m_height(init_owner(*this) + init_name("height") + init_label(_("Height")) + init_description(_("Cube height")) + init_value(5.0) + init_step_increment(0.1) + init_units(typeid(k3d::measurement::distance)))
...
m_rows.changed_signal().connect(make_reset_mesh_slot());
m_width.changed_signal().connect(make_reset_mesh_slot());
m_height.changed_signal().connect(make_reset_mesh_slot());
...
private:
k3d_data(long, immutable_name, change_signal, with_undo, local_storage, with_constraint, measurement_property, with_serialization) m_rows;
k3d_data(double, immutable_name, change_signal, with_undo, local_storage, no_constraint, measurement_property, with_serialization) m_width;
k3d_data(double, immutable_name, change_signal, with_undo, local_storage, no_constraint, measurement_property, with_serialization) m_height;
- That would look like.

- So looking up in the k3d source code you can add several properties, as a first approach.
- Notice that the m_rows, m_width, m_height are variables so you can change that name at will as long as they match in the code.
The names and Ids
You should change the following names in the code so that you make your own plugin with its own meaning on their names.
- libk3dmymodule
- my_poly_face_implementation
- m_width, m_height
- poly_face_factory
- Remember that if you change the name of a variable, class, etc, do it in the whole code.
Now check the get_factory code:
static k3d::iplugin_factory& get_factory()
{
static k3d::plugin_factory<k3d::document_plugin<my_poly_face_implementation>, k3d::interface_list<k3d::imesh_source > > factory(
k3d::uuid(0x51ecc5ce, 0xf8bf43ed, 0xabba2ed7, 0x3e1ed1d3),
"MyPlugin",
"Generates a face",
"Tutorials",
k3d::iplugin_factory::EXPERIMENTAL);
return factory;
}
- You must change the 0x51ecc5ce, 0xf8bf43ed, 0xabba2ed7, 0x3e1ed1d3 sequence. (this identifies every plugin)
- You can do it with the k3d-uuidgen command. See Legacy Plugin Tutorial for more details.
- It is not recommended to change the "MyPlugin" string right away, because if you do you will have to create a new .svg icon with that name an put it on the k3d dir. (do it when you finish your plugin)
- You can change "Generates a face" line with a good description of your plugin.
- You can change "Tutorials" line, this tells in which submenu from the menu Create goes your plugin.
Let's check the end of the file:
K3D_MODULE_START(libk3dmymodule, k3d::uuid(0xb1aa0867, 0x37404b32, 0xb80add63, 0xfb02fed0), Registry)
Registry.register_factory(libk3dmymodule::my_poly_face_implementation::get_factory());
K3D_MODULE_END
- Again you have to change the 0xb1aa0867, 0x37404b32, 0xb80add63, 0xfb02fed0 sequence. It also must be different from the one we mention above.

