Page tree

You are viewing the documentation for OpenSim 3.x. Are you looking for the latest OpenSim 4.0 Documentation?

Skip to end of metadata
Go to start of metadata

Note that the code in this example is meant to be instructive; the priority is demonstration of the API and not optimization of C++ code.

A. Create and compile the example project in the developer's guide

In the Getting Started as a Developer section of the Developer's Guide, read the following three pages:

  1. Technical Background
  2. Prerequisites
  3. How to Build a C++ Example

Follow the instructions in the Step-by-Step Example, and compile and run the exampleMain executable.

B. Create a basic CMake file

In this section, we will construct a basic CMake file for use with OpenSim. CMake is a cross-platform, open-source program for compiling software using simple platform-independent and compiler-independent configuration files. One of the challenges in developing a piece of software is ensuring that the created project works with different system configurations. By creating a "make" file that specifies the location of project files (such as source files, header files, and library files), the arduous part of assembling the project for a specific operating system and development environment can be handled for you.

Documentation for CMake can be found at CMake

In each section, read the following code, copy the code to your document, and complete any missing elements.

Set up the project workspace

  1. Create a new folder in your workspace to hold the DynamicWalker files.
  2. In the workspace, create a text file named DynamicWalkerBuildModel.cpp and a text file named CMakeLists.txt.
    • On Windows, you can do this by opening Notepad++, creating a new file (Ctrl+N), clicking Save As... (Ctrl+Alt+S), and selecting the appropriate type ("C++ source file" or "Normal text file").
    • Make sure that the file names do not end with ".cpp.txt" or ".txt.txt"; this might occur if you create empty files with File Explorer.
  3. Also, copy the file C:\OpenSim 3.3\sdk\FindOpenSim.cmake into the workspace (into the same folder as CMakeLists.txt); this file tells our CMake project how to find OpenSim's API.

 

Copy the following code blocks into the CMakeLists.txt file.

Basic CMake File
#------------------------------------------------------------------------
# CMake file for building an OpenSim project
# Author: The OpenSim Team
#------------------------------------------------------------------------
cmake_minimum_required(VERSION 2.6)
project(DynamicWalker)

A CMake file starts with a call to the "project" command. Here we create a new project named DynamicWalker.

# Detect the folder where OpenSim is installed.
set(OPENSIM_INSTALL_DIR $ENV{OPENSIM_HOME} CACHE PATH "Path to OpenSim Install.")

# Use the FindOpenSim.cmake script to get OpenSim's include directories
# and libraries.
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
find_package(OpenSim MODULE)

# We override the default CMAKE_CONFIGURATION_TYPES to avoid
# building in the Debug configuration, which does not work with the
# OpenSim API distributed via the GUI.
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release" 
        CACHE STRING "List of supported configuration types." FORCE)

# Setup Targets
set(TARGET DynamicWalkerBuild CACHE STRING "Name of the executable.")
set(SOURCE DynamicWalkerBuildModel.cpp CACHE STRING "Name of the source files.")
# Include OpenSim's and Simbody's headers.
include_directories(${OPENSIMSIMBODY_INCLUDE_DIRS})
# Define an executable to be created from the provided C++ source files.
add_executable(${TARGET} ${SOURCE})

# Link to OpenSim and Simbody libraries.
target_link_libraries(${TARGET} ${OPENSIMSIMBODY_LIBRARIES})

This next section of the file creates variables in the CMake space to hold the location of the source files, to find the OpenSim API on your computer, and to specify the configuration of the compiler. After a variable is created, it can be used later in the make file by enclosing the variable in curly-braces with a dollar sign prefix (i.e., ${variable}).

In CMake, variables can be created with the Set command. The Set command definition is: set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE]). In this example, we are only using set(<variable> <value> [CACHE <type> <docstring>].

In the CMake space, the variable is assigned the value and is available in the scope of the project. The use of the CACHE keyword puts the variable into the CMakeCache.txt file which is written into the build folder and reloaded each time you generate the project. The <type> is used by the CMake GUI to choose a widget (e.g., a text box, drop-down menu, or file dialog) to gather information from the user. The docstring is the tooltip that appears when the user hovers over the variable in the GUI.

The TARGET is the name of the individual project we are creating inside the overall project DynamicWalker. SOURCE is a semicolon-separated list of all of the files that will be used to compile the project.

The function declarations of the OpenSim and SimTK API are held in header files in a subfolder of the OpenSim installation directory. The include_directories command tells the compiler where these header files are located. The include directories are used to search for files that are included in C++ source files (using the "#include" directive).

The function definitions of the OpenSim and SimTK API are held in library files (.dll and .lib on Windows, .dylib on Mac, .so on Linux) in a subfolder of the OpenSim installation directory. The target_link_libraries command tells the compiler (actually, the linker) the location of these library files. When the program is compiled, these libraries are linked into the program so that the required external functions can be found when the program is executed.

Open the CMake GUI and generate a project using your created CMakeLists.txt file.

C. Compile and run "Hello OpenSim"

In this section, we create a template C++ file that we will use in the next part of the example to create the model.

Open the newly created project in your IDE (e.g., Visual Studio). Open up the blank .cpp file that was the source in your CMake file and copy the following code into the file.

//==============================================================================
//The OpenSim Main header must be included in all files
#include <OpenSim/OpenSim.h>
// Set the namespace to shorten the declarations
// Note: Several classes appear in both namespaces and require using the full name
using namespace OpenSim;
using namespace SimTK;
//______________________________________________________________________________
/**
 * 
 */
int main()
{
    try {
		// TODO: Add Code to Begin Model here
		// ********** BEGIN CODE **********


		// **********  END CODE  **********
 	}
    catch (OpenSim::Exception ex)
    {
        std::cout << ex.getMessage() << std::endl;
        return 1;
    }
    catch (SimTK::Exception::Base ex)
    {
        std::cout << ex.getMessage() << std::endl;
        return 1;
    }
    catch (std::exception ex)
    {
        std::cout << ex.what() << std::endl;
        return 1;
    }
    catch (...)
    {
        std::cout << "UNRECOGNIZED EXCEPTION" << std::endl;
        return 1;
    }
    std::cout << "OpenSim example completed successfully" << std::endl;
    std::cout << "Press return to continue" << std::endl;
    std::cin.get();
	return 0;
 } 

This first block of code can serve as a suitable template for API projects. In this block of code, the required header <OpenSim/OpenSim.h> is included which gives you access to all of the OpenSim and SimTK functions. In many programming languages, namespaces are used to separate classes and functions from different libraries. For example, both the OpenSim API and the SimTK API have a class called Body. Each Body class is put into the namespace of its respective library (i.e., OpenSim::Body and SimTK::Body). The using namespace lines allow the programmer to simply remove the prefix (e.g., "OpenSim::") from the name. However, by using both the OpenSim and SimTK namespaces, we re-introduce the ambiguity for Body (and any class names that are used by both libraries); therefore, you will need to use either OpenSim::Body or SimTK::Body to specify which class you want to use.

The entire main program is wrapped in a try/catch statement. The try/catch statement is used to handle exceptions that would result in crashes of the program. By catching an exception, the program can execute closing instructions, like printing an error message on the output stream (std::cout), before terminating the program.

 

 

  • No labels