Back to plugin tutorials

Your first AC3D plugin

Armed with the SDK header and your template code from the previous section, you are ready to create your first plugin.

Hello World, AC3D style

We are going to create a test plugin. The first iteration of this plugin will simply print "Hello world" when the user invokes the plugin's menu item.

This code is based off of the code from the previous section.

Create a new AC3D plugin project. Enter this code into a file named "first_plugin.cpp". Then, either change the #include "ac_plugin.h" to point to wherever you keep your "ac_plugin.h" file, or create a copy of "ac_plugin.h" to your working directory.

You may also pick up this code here.
#ifdef _WINDOWS
#ifndef WINDOWS
#define WINDOWS
#endif
#endif

#include "ac_plugin.h"

//////////////////////////////////////////////////////////////
// This is our plugin's main function.
static void first_plugin()
{
   // This command throws up a message box to the user.
   // Note that I added "first_plugin : " to the beginning of the
   // text.  This lets the user know what operation was responsible
   // for sending them the message.
   message_dialog( "first_plugin : Hello world!" );
   // This displays a message on the AC3D status bar.
   display_message( "first_plugin : I just sent you a message" );
}
//////////////////////////////////////////////////////////////
// The plugin init function
AC3D_PLUGIN_FUNC int AC3DPluginInit( AC3DPluginInitData *d )
{
   static bool firstTime = true;
   if( firstTime )
   {
      // Add our command to AC3D.
      ac_add_command( "my_first_plugin", first_plugin );
      // Add a new menu item for our plugin under the "Tools" menu.
      // This will show up as "First plugin" under the Tools menu,
      // and will call the function we defined above.
      ac_add_tools_menu_item( "First plugin",
                              "ac3d my_first_plugin",
                              "My first plugin" );
      firstTime = false;
   }
   return 0;
}
//////////////////////////////////////////////////////////////
AC3D_PLUGIN_FUNC int AC3DPluginExit()
{
   return 0;
}
//////////////////////////////////////////////////////////////
AC3D_PLUGIN_FUNC char *AC3DPluginAbout()
{
   return( "First plugin - version 1.0 - Me" );
}
//////////////////////////////////////////////////////////////
AC3D_PLUGIN_FUNC char *AC3DPluginInfo()
{
   return( "This is my first plugin." );
}

Compile this file as first_plugin.p and copy the resulting file to your AC3D /plugins folder, then start AC3D. you should find a "First plugin" menu item under the Tools menu. Execute it, and you should see:

Click OK, and you will see this message in the AC3D status bar.

Making it functional

With the success of our first plugin, it's time to move onto a plugin that actually does something.

We'll change this plugin to perform the following tasks:

  • Scale the selected vertices in the current document down by 50% toward the origin { 0,0,0 }.
  • If there is no selected geometry in the current AC3D document, warn the user that nothing was done.
  • A word about linked lists

    At the time of this tutorial, AC3D stores all of its geometry in linked lists (warning: link takes you off-site). All of the Objects, Surfaces, and Vertices in an AC3D document are stored this way.

    ac_plugin.h provides a List object that encapsulates the linked list.

    If you are not familiar with linked lists, it may do some good to brush up on them, but this tutorial will get you through their usage for now.

    To get the selected vertices in the current document, use this function from ac_plugin.h:

    ac_selection_get_vertices_all()

    This gets an AC3D List (linked list) containing all vertices in the current document.

    An AC3D linked list node is described as:

    typedef void *ListData;
    typedef struct listitem
    {
        ListData data;
        struct listitem *next;
    } List;

    The "data" field is a void pointer to the data we want (a Vertex, in this case), and "next" points to the next element in the list.

    An AC3D Vertex is described as:

    typedef struct vertex_t
    {
       float x, y, z;
    } Vertex;

    These are the actual x/y/z coordinates of a vertex, and changing these will change the vertex location in AC3D.

    So, let's replace the contents of our first_plugin() function to the following snippet:

    static void first_plugin()
    {
    // Get a list of all selected vertices in the current document
    List *vlist = ac_selection_get_vertices_all();
    // If this list is NULL, then nothing was selected. We can exit the plugin.
    if( vlist == NULL )
    {
    message_dialog( "first_plugin : nothing was selected" );
    return;
    }
    // Let's keep up with the number of vertices we change
    int num_verts_scaled = 0;
    // Iterate through the vertices
    for( List *pv = vlist; pv != NULL; pv = pv->next )
    {
    // Cast the current list item as a Vertex
    Vertex *currentVertex = ( Vertex * )( pv->data );
    // Scale the vertex by 50%
    currentVertex->x = currentVertex->x * 0.5f;
    currentVertex->y = currentVertex->y * 0.5f;
    currentVertex->z = currentVertex->z * 0.5f;
    // Increment our counter
    num_verts_scaled = num_verts_scaled + 1;
    }
    // We're done with the List --- we need to clean it up.
    // Some Lists returned from AC3D need to be cleaned up, some do not.
    // This one needs it.

    list_free( &vlist );
    // Recalculate the selected normals
    selected_calc_normals();
    // Recalculate the AC3D selection box.

    find_bounding_box();
    // Redraw all of the AC3D views to reflect any changes.
    redraw_all();
    // Update the User Interface
    display_status();
    // Show the user what we did
    display_message( "first_plugin : scaled %d vertices toward {0,0,0}", num_verts_scaled );
    }

    You can grab the entire plugin file here. Note that I also changed the version number in the AC3DPluginAbout() function.

    Note that this plugin does not resize an object by 50% --- it just moves the vertices halfway toward {0,0,0}. Not the most useful plugin, but it definitely does something.

    Adding Undo

    AC3D's undo feature is a very powerful feature, especially when compared to other 3D modelers/applications. With the exception of very few functions that destroy Undo history, all AC3D actions, including selections, can be Undone/Redone.

    Now, if we do not include an Undo for a plugin that changes geometry, this will be a terrible thing, since it may mess up the Undo change a bit.

    Fortunately, our particular plugin only needs two code changes to make it Undo-compliant. Be thankful for this, as some plugins may require you to implement your own Undo, which can be a nasty business in some cases.

    The changes are highlighted below, or you can pick up the file here.

    static void first_plugin()
    {
    // Get a list of all selected vertices in the current document
    List *vlist = ac_selection_get_vertices_all();
    // Store the original positions in Undo // "move vertices" is the text that will appear in the Undo menu text. add_undoable_vertex_positions( "move vertices", vlist );
    // If this list is NULL, then nothing was selected. We can exit the plugin.
    if( vlist == NULL )
    {
    message_dialog( "first_plugin : nothing was selected" );
    return;
    }
    // Let's keep up with the number of vertices we change
    int num_verts_scaled = 0;
    // Iterate through the vertices
    for( List *pv = vlist; pv != NULL; pv = pv->next )
    {
    // Cast the current list item as a Vertex
    Vertex *currentVertex = ( Vertex * )( pv->data );
    // Scale the vertex by 50%
    currentVertex->x = currentVertex->x * 0.5f;
    currentVertex->y = currentVertex->y * 0.5f;
    currentVertex->z = currentVertex->z * 0.5f;
    // Increment our counter
    num_verts_scaled = num_verts_scaled + 1;
    }

    // Do not free the list; AC3D's Undo mechanism will take care of this.
    //list_free( &vlist );
    // Recalculate the selected normals
    selected_calc_normals();
    // Recalculate the AC3D selection box.
    find_bounding_box();
    // Redraw all of the AC3D views to reflect any changes.
    redraw_all();
    // Update the User Interface
    display_status();
    // Show the user what we did
    display_message( "first_plugin : scaled %d vertices toward {0,0,0}", num_verts_scaled );
    }

    You can now Undo/Redo the plugin's actions.

    It is important that you coment out the code where we "cleaned up" the vlist. Otherwise, Undo will probably throw an exception, since it still uses the list.

     

    Back to plugin tutorials



    All content Copyright © 2006 Dennis Hawthorne, except where explicitly noted
    supercoldmilk © 2006 Dennis Hawthorne