Loading src/DetectionMethod.h +3 −1 Original line number Diff line number Diff line // Comment or uncomment this line to change the detection method used #define BLOB src/SkeletonFinder.cpp +53 −31 Original line number Diff line number Diff line // // SkeletonFinder.cpp // // Created by Pierre B�rki on 19.05.20. // Created by Pierre Bürki on 19.05.20. // Adapted from BlobFinder.cpp by maybites (14.02.14). // #include "SkeletonFinder.h" void SkeletonFinder::initGUI(ofxGui& gui) { /** * Creates and populates the GUI elements */ void SkeletonFinder::initGUI(ofxGui& gui) { panel = gui.addPanel(); panel->loadTheme("theme/theme_light.json"); Loading Loading @@ -36,16 +39,27 @@ void SkeletonFinder::initGUI(ofxGui& gui) { panel->setVisible(visible); } void SkeletonFinder::setTransformMatrix(ofMatrix4x4* mat) { /** * Set the transformation matrix pointer */ void SkeletonFinder::setTransformMatrix(ofMatrix4x4* mat) { transformMatrix = mat; } void SkeletonFinder::update(nuitrack::SkeletonData::Ptr data) { /** * Called each time a skeleton is detected on a frame * Copy the data for later use */ void SkeletonFinder::update(nuitrack::SkeletonData::Ptr data) { skeletons.clear(); // TODO: filter using the capture bounds for (nuitrack::Skeleton skel : data->getSkeletons()) { for (nuitrack::Skeleton skel : data->getSkeletons()) { vector<Joint> joints; for (nuitrack::Joint joint : skel.joints) { for (nuitrack::Joint joint : skel.joints) { glm::vec3 pos = ofxnui::Tracker::fromVector3(joint.real); // ofMatrix multiplication works in reverse Loading @@ -55,24 +69,36 @@ void SkeletonFinder::update(nuitrack::SkeletonData::Ptr data) { } Skeleton skeleton(skel.id, joints); if (!filtering.get() || isSkeletonInBounds(skeleton)) { if (!filtering.get() || isSkeletonInBounds(skeleton)) { skeletons.push_back(skeleton); } } } vector<Skeleton> SkeletonFinder::getSkeletons() const { /** * Getter for the skeletons container */ vector<Skeleton> SkeletonFinder::getSkeletons() const { return skeletons; } /** * Draw the sensor box * Note that the sensor box plays a role only if "filtering" is activated */ void SkeletonFinder::drawSensorBox() { sensorBox.draw(); } // adapted from ofxNuitrack example void SkeletonFinder::drawSkeletons() { /** * Draw all the skeletons in this->skeletons * Adapted from ofxNuitrack example */ void SkeletonFinder::drawSkeletons() { static vector<Bone> bones = { Bone(nuitrack::JOINT_WAIST, nuitrack::JOINT_TORSO, glm::vec3(0, 1, 0)), Bone(nuitrack::JOINT_TORSO, nuitrack::JOINT_NECK, glm::vec3(0, 1, 0)), Loading @@ -98,8 +124,10 @@ void SkeletonFinder::drawSkeletons() { }; ofSetColor(0, 255, 0); for (Skeleton skel : skeletons) { for (Bone bone : bones) { for (Skeleton skel : skeletons) { for (Bone bone : bones) { auto j1 = skel.joints[bone.from]; auto j2 = skel.joints[bone.to]; Loading @@ -112,23 +140,13 @@ void SkeletonFinder::drawSkeletons() { } } string SkeletonFinder::getShortDesc() /** * Called each time the sensor box boundaries are changed * * Not modified from BlobFinder.cpp */ void SkeletonFinder::updateSensorBox(int& value) { if (skeletons.size() == 0) { return "No skeleton found"; } else { ostringstream s; Skeleton skel = skeletons[0]; auto pos = skel.joints[nuitrack::JOINT_HEAD].pos; s << "Head position : (" << pos.x << ", " << pos.y << ", " << pos.z << ")"; return s.str(); } } void SkeletonFinder::updateSensorBox(int& value) { sensorBox.clear(); sensorBox.setMode(OF_PRIMITIVE_LINES); Loading Loading @@ -161,7 +179,11 @@ void SkeletonFinder::updateSensorBox(int& value) { //captureCam. } bool SkeletonFinder::isSkeletonInBounds(const Skeleton& skel) { /** * Returns true if the skeleton's head is in the sensor box */ bool SkeletonFinder::isSkeletonInBounds(const Skeleton& skel) { glm::vec3 headPos = skel.joints[nuitrack::JOINT_HEAD].pos; return headPos.x < sensorBoxLeft.get() * SCALE && headPos.x > sensorBoxRight.get() * SCALE Loading src/SkeletonFinder.h +0 −2 Original line number Diff line number Diff line Loading @@ -64,8 +64,6 @@ public: void drawSensorBox(); void drawSkeletons(); string getShortDesc(); vector<Skeleton> getSkeletons() const; private: Loading src/TrackingNetworkManager.cpp +21 −6 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ // // Created by maybites on 14.02.14. // // // Modified by Pierre Bürki on 23.07.2020 #include "TrackingNetworkManager.h" Loading Loading @@ -116,9 +116,11 @@ void TrackingNetworkManager::sendTrackingData(const BodyFinder& bodyFinder) { } #else vector<Skeleton> skeletons = bodyFinder.getSkeletons(); if (skeletons.size() > 0) { if (skeletons.size() > 0) { // Only one skeleton is to be on the scene for the perspective to work if (skeletons.size() > 1) { if (skeletons.size() > 1) { sendMultipleBodiesAlert(); return; } Loading @@ -127,14 +129,23 @@ void TrackingNetworkManager::sendTrackingData(const BodyFinder& bodyFinder) { #endif } void TrackingNetworkManager::sendMultipleBodiesAlert() { /** * Send a special message via OSC to signal multiple bodies */ void TrackingNetworkManager::sendMultipleBodiesAlert() { ofxOscMessage alertMsg; alertMsg.setAddress("/ks/server/track/multiple-bodies"); sendMessageToTrackingClients(alertMsg); } #ifdef BLOB void TrackingNetworkManager::sendBlobData(const BlobTracker& blob) { /** * Send a given blob via OSC * Slighty modified from the original so that it only sends the blob's head */ void TrackingNetworkManager::sendBlobData(const BlobTracker& blob) { ofxOscMessage blobMsg; blobMsg.setAddress("/ks/server/track/headblob"); Loading @@ -147,7 +158,11 @@ void TrackingNetworkManager::sendBlobData(const BlobTracker& blob) { sendMessageToTrackingClients(blobMsg); } #else void TrackingNetworkManager::sendSkeletonData(const Skeleton& skel) { /** * Send a given skeleton via OSC */ void TrackingNetworkManager::sendSkeletonData(const Skeleton& skel) { ofxOscMessage skeletonMsg; skeletonMsg.setAddress("/ks/server/track/skeleton"); Loading src/ofApp.cpp +74 −32 Original line number Diff line number Diff line // // ofApp.cpp // kinectTCPServer // // Created by maybites on 14.02.14. // // Edited by Pierre Bürki on 23.07.2020. #include "ofApp.h" #define RECONNECT_TIME 400 Loading @@ -7,7 +15,11 @@ #ifndef BLOB ofMatrix4x4 ofApp::makeNuitrackToRealSenseTransform() { /** * Create the constant nuitrack -> realsense transformation matrix */ ofMatrix4x4 ofApp::makeNuitrackToRealSenseTransform() { // Thankfully this matrix is symmetric, so we need not worry about the row-major-ness // of the matrix object float mat[16] = { Loading @@ -21,9 +33,13 @@ ofMatrix4x4 ofApp::makeNuitrackToRealSenseTransform() { const ofMatrix4x4 ofApp::nuitrackViewportToRealSenseViewportTransform = makeNuitrackToRealSenseTransform(); void ofApp::initNuitrack() { /** * Set up Nuitrack's config and callbacks */ void ofApp::initNuitrack() { nuitracker = ofxnui::Tracker::create(); nuitracker->init(""); nuitracker->init(); // depth feed settings nuitracker->setConfigValue("Realsense2Module.Depth.FPS", "30"); Loading Loading @@ -51,21 +67,34 @@ void ofApp::initNuitrack() { nuitracker->setConfigValue("Segmentation.MAX_DISTANCE", "7000"); nuitracker->setConfigValue("Skeletonization.MaxDistance", "7000"); nuitracker->setIssuesCallback([this](nuitrack::IssuesData::Ptr data) { nuitracker->setIssuesCallback( [this](nuitrack::IssuesData::Ptr data) { auto issue = data->getIssue<nuitrack::Issue>(); if (issue) { if (issue) { ofLogNotice() << "Issue detected! " << issue->getName() << " [" << issue->getId() << "]"; } }); nuitracker->setRGBCallback([this](nuitrack::RGBFrame::Ptr data) { } ); nuitracker->setRGBCallback( [this](nuitrack::RGBFrame::Ptr data) { pointCloudManager.updateRGB(data); }); nuitracker->setDepthCallback([this](nuitrack::DepthFrame::Ptr data) { } ); nuitracker->setDepthCallback( [this](nuitrack::DepthFrame::Ptr data) { pointCloudManager.updateDepth(data); }); nuitracker->setSkeletonCallback([this](nuitrack::SkeletonData::Ptr data) { } ); nuitracker->setSkeletonCallback( [this](nuitrack::SkeletonData::Ptr data) { tracker.update(data); }); } ); pointCloudManager.setDepthSensor(nuitracker->depthTracker); } Loading Loading @@ -105,15 +134,19 @@ void ofApp::setupTransformGui() { transformationGuiGroup.add(transformation.set("Transform", ofMatrix4x4())); guitransform->addGroup(transformationGuiGroup); // The GUI apparently cannot display matrices // Also this method apparently requires a reference for some reason // This method apparently requires a reference for some reason bool visible = false; guitransform->setVisible(visible); reloadTransformMatrix(); } void ofApp::reloadTransformMatrix() { /** * Load the transformation matrix from the xml file * Also applies the nuitrack->realsense transformation if needed */ void ofApp::reloadTransformMatrix() { guitransform->loadFromFile("transformation.xml"); #ifdef BLOB Loading @@ -124,7 +157,8 @@ void ofApp::reloadTransformMatrix() { #endif } void ofApp::setup(){ void ofApp::setup() { #ifdef BLOB if (ofIsGLProgrammableRenderer()) { Loading @@ -136,12 +170,8 @@ void ofApp::setup(){ ofLog(OF_LOG_NOTICE) << "MainAPP: looking for RealSense Device..."; // ofSetLogLevel(OF_LOG_VERBOSE); realSense = RSDevice::createUniquePtr(); realSense->checkConnectedDialog(); realSense->setVideoSize(REALSENSE_VIDEO_WIDTH, REALSENSE_VIDEO_HEIGHT); ofLog(OF_LOG_NOTICE) << "... RealSense Device found."; Loading @@ -161,7 +191,8 @@ void ofApp::setup(){ ofLog(OF_LOG_NOTICE) << "MainAPP: starting attached Device..."; #ifdef BLOB if (realSense->capture()) { if (realSense->capture()) { createGUIDeviceParams(); createGUIPostProcessingParams(); } Loading Loading @@ -238,15 +269,18 @@ void ofApp::createGUIPostProcessingParams() { #endif //-------------------------------------------------------------- void ofApp::update(){ void ofApp::update() { ofBackground(100, 100, 100); #ifdef BLOB if(realSense->update(ofxRealSenseTwo::PointCloud::VIDEO)) { if (maskUpdatesCounter < MASK_UPDATE_CYCLES) { if (maskUpdatesCounter == 0) { if(realSense->update(ofxRealSenseTwo::PointCloud::VIDEO)) { if (maskUpdatesCounter < MASK_UPDATE_CYCLES) { if (maskUpdatesCounter == 0) { tracker.clearMask(); } tracker.captureMaskBegin(); Loading @@ -254,7 +288,9 @@ void ofApp::update(){ tracker.captureMaskEnd(); maskUpdatesCounter++; } else { } else { // Cature captureCloud to FBO tracker.captureBegin(); drawCapturePointCloud(false); Loading @@ -271,6 +307,9 @@ void ofApp::update(){ networkMng.update(tracker); } /** * Save current settings to the various xml files */ void ofApp::saveSettings() { tracker.panel->saveToFile("tracking.xml"); Loading @@ -284,6 +323,9 @@ void ofApp::saveSettings() #endif } /** * Load the settings from the various xml files */ void ofApp::loadSettings() { tracker.panel->loadFromFile("tracking.xml"); Loading Loading
src/DetectionMethod.h +3 −1 Original line number Diff line number Diff line // Comment or uncomment this line to change the detection method used #define BLOB
src/SkeletonFinder.cpp +53 −31 Original line number Diff line number Diff line // // SkeletonFinder.cpp // // Created by Pierre B�rki on 19.05.20. // Created by Pierre Bürki on 19.05.20. // Adapted from BlobFinder.cpp by maybites (14.02.14). // #include "SkeletonFinder.h" void SkeletonFinder::initGUI(ofxGui& gui) { /** * Creates and populates the GUI elements */ void SkeletonFinder::initGUI(ofxGui& gui) { panel = gui.addPanel(); panel->loadTheme("theme/theme_light.json"); Loading Loading @@ -36,16 +39,27 @@ void SkeletonFinder::initGUI(ofxGui& gui) { panel->setVisible(visible); } void SkeletonFinder::setTransformMatrix(ofMatrix4x4* mat) { /** * Set the transformation matrix pointer */ void SkeletonFinder::setTransformMatrix(ofMatrix4x4* mat) { transformMatrix = mat; } void SkeletonFinder::update(nuitrack::SkeletonData::Ptr data) { /** * Called each time a skeleton is detected on a frame * Copy the data for later use */ void SkeletonFinder::update(nuitrack::SkeletonData::Ptr data) { skeletons.clear(); // TODO: filter using the capture bounds for (nuitrack::Skeleton skel : data->getSkeletons()) { for (nuitrack::Skeleton skel : data->getSkeletons()) { vector<Joint> joints; for (nuitrack::Joint joint : skel.joints) { for (nuitrack::Joint joint : skel.joints) { glm::vec3 pos = ofxnui::Tracker::fromVector3(joint.real); // ofMatrix multiplication works in reverse Loading @@ -55,24 +69,36 @@ void SkeletonFinder::update(nuitrack::SkeletonData::Ptr data) { } Skeleton skeleton(skel.id, joints); if (!filtering.get() || isSkeletonInBounds(skeleton)) { if (!filtering.get() || isSkeletonInBounds(skeleton)) { skeletons.push_back(skeleton); } } } vector<Skeleton> SkeletonFinder::getSkeletons() const { /** * Getter for the skeletons container */ vector<Skeleton> SkeletonFinder::getSkeletons() const { return skeletons; } /** * Draw the sensor box * Note that the sensor box plays a role only if "filtering" is activated */ void SkeletonFinder::drawSensorBox() { sensorBox.draw(); } // adapted from ofxNuitrack example void SkeletonFinder::drawSkeletons() { /** * Draw all the skeletons in this->skeletons * Adapted from ofxNuitrack example */ void SkeletonFinder::drawSkeletons() { static vector<Bone> bones = { Bone(nuitrack::JOINT_WAIST, nuitrack::JOINT_TORSO, glm::vec3(0, 1, 0)), Bone(nuitrack::JOINT_TORSO, nuitrack::JOINT_NECK, glm::vec3(0, 1, 0)), Loading @@ -98,8 +124,10 @@ void SkeletonFinder::drawSkeletons() { }; ofSetColor(0, 255, 0); for (Skeleton skel : skeletons) { for (Bone bone : bones) { for (Skeleton skel : skeletons) { for (Bone bone : bones) { auto j1 = skel.joints[bone.from]; auto j2 = skel.joints[bone.to]; Loading @@ -112,23 +140,13 @@ void SkeletonFinder::drawSkeletons() { } } string SkeletonFinder::getShortDesc() /** * Called each time the sensor box boundaries are changed * * Not modified from BlobFinder.cpp */ void SkeletonFinder::updateSensorBox(int& value) { if (skeletons.size() == 0) { return "No skeleton found"; } else { ostringstream s; Skeleton skel = skeletons[0]; auto pos = skel.joints[nuitrack::JOINT_HEAD].pos; s << "Head position : (" << pos.x << ", " << pos.y << ", " << pos.z << ")"; return s.str(); } } void SkeletonFinder::updateSensorBox(int& value) { sensorBox.clear(); sensorBox.setMode(OF_PRIMITIVE_LINES); Loading Loading @@ -161,7 +179,11 @@ void SkeletonFinder::updateSensorBox(int& value) { //captureCam. } bool SkeletonFinder::isSkeletonInBounds(const Skeleton& skel) { /** * Returns true if the skeleton's head is in the sensor box */ bool SkeletonFinder::isSkeletonInBounds(const Skeleton& skel) { glm::vec3 headPos = skel.joints[nuitrack::JOINT_HEAD].pos; return headPos.x < sensorBoxLeft.get() * SCALE && headPos.x > sensorBoxRight.get() * SCALE Loading
src/SkeletonFinder.h +0 −2 Original line number Diff line number Diff line Loading @@ -64,8 +64,6 @@ public: void drawSensorBox(); void drawSkeletons(); string getShortDesc(); vector<Skeleton> getSkeletons() const; private: Loading
src/TrackingNetworkManager.cpp +21 −6 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ // // Created by maybites on 14.02.14. // // // Modified by Pierre Bürki on 23.07.2020 #include "TrackingNetworkManager.h" Loading Loading @@ -116,9 +116,11 @@ void TrackingNetworkManager::sendTrackingData(const BodyFinder& bodyFinder) { } #else vector<Skeleton> skeletons = bodyFinder.getSkeletons(); if (skeletons.size() > 0) { if (skeletons.size() > 0) { // Only one skeleton is to be on the scene for the perspective to work if (skeletons.size() > 1) { if (skeletons.size() > 1) { sendMultipleBodiesAlert(); return; } Loading @@ -127,14 +129,23 @@ void TrackingNetworkManager::sendTrackingData(const BodyFinder& bodyFinder) { #endif } void TrackingNetworkManager::sendMultipleBodiesAlert() { /** * Send a special message via OSC to signal multiple bodies */ void TrackingNetworkManager::sendMultipleBodiesAlert() { ofxOscMessage alertMsg; alertMsg.setAddress("/ks/server/track/multiple-bodies"); sendMessageToTrackingClients(alertMsg); } #ifdef BLOB void TrackingNetworkManager::sendBlobData(const BlobTracker& blob) { /** * Send a given blob via OSC * Slighty modified from the original so that it only sends the blob's head */ void TrackingNetworkManager::sendBlobData(const BlobTracker& blob) { ofxOscMessage blobMsg; blobMsg.setAddress("/ks/server/track/headblob"); Loading @@ -147,7 +158,11 @@ void TrackingNetworkManager::sendBlobData(const BlobTracker& blob) { sendMessageToTrackingClients(blobMsg); } #else void TrackingNetworkManager::sendSkeletonData(const Skeleton& skel) { /** * Send a given skeleton via OSC */ void TrackingNetworkManager::sendSkeletonData(const Skeleton& skel) { ofxOscMessage skeletonMsg; skeletonMsg.setAddress("/ks/server/track/skeleton"); Loading
src/ofApp.cpp +74 −32 Original line number Diff line number Diff line // // ofApp.cpp // kinectTCPServer // // Created by maybites on 14.02.14. // // Edited by Pierre Bürki on 23.07.2020. #include "ofApp.h" #define RECONNECT_TIME 400 Loading @@ -7,7 +15,11 @@ #ifndef BLOB ofMatrix4x4 ofApp::makeNuitrackToRealSenseTransform() { /** * Create the constant nuitrack -> realsense transformation matrix */ ofMatrix4x4 ofApp::makeNuitrackToRealSenseTransform() { // Thankfully this matrix is symmetric, so we need not worry about the row-major-ness // of the matrix object float mat[16] = { Loading @@ -21,9 +33,13 @@ ofMatrix4x4 ofApp::makeNuitrackToRealSenseTransform() { const ofMatrix4x4 ofApp::nuitrackViewportToRealSenseViewportTransform = makeNuitrackToRealSenseTransform(); void ofApp::initNuitrack() { /** * Set up Nuitrack's config and callbacks */ void ofApp::initNuitrack() { nuitracker = ofxnui::Tracker::create(); nuitracker->init(""); nuitracker->init(); // depth feed settings nuitracker->setConfigValue("Realsense2Module.Depth.FPS", "30"); Loading Loading @@ -51,21 +67,34 @@ void ofApp::initNuitrack() { nuitracker->setConfigValue("Segmentation.MAX_DISTANCE", "7000"); nuitracker->setConfigValue("Skeletonization.MaxDistance", "7000"); nuitracker->setIssuesCallback([this](nuitrack::IssuesData::Ptr data) { nuitracker->setIssuesCallback( [this](nuitrack::IssuesData::Ptr data) { auto issue = data->getIssue<nuitrack::Issue>(); if (issue) { if (issue) { ofLogNotice() << "Issue detected! " << issue->getName() << " [" << issue->getId() << "]"; } }); nuitracker->setRGBCallback([this](nuitrack::RGBFrame::Ptr data) { } ); nuitracker->setRGBCallback( [this](nuitrack::RGBFrame::Ptr data) { pointCloudManager.updateRGB(data); }); nuitracker->setDepthCallback([this](nuitrack::DepthFrame::Ptr data) { } ); nuitracker->setDepthCallback( [this](nuitrack::DepthFrame::Ptr data) { pointCloudManager.updateDepth(data); }); nuitracker->setSkeletonCallback([this](nuitrack::SkeletonData::Ptr data) { } ); nuitracker->setSkeletonCallback( [this](nuitrack::SkeletonData::Ptr data) { tracker.update(data); }); } ); pointCloudManager.setDepthSensor(nuitracker->depthTracker); } Loading Loading @@ -105,15 +134,19 @@ void ofApp::setupTransformGui() { transformationGuiGroup.add(transformation.set("Transform", ofMatrix4x4())); guitransform->addGroup(transformationGuiGroup); // The GUI apparently cannot display matrices // Also this method apparently requires a reference for some reason // This method apparently requires a reference for some reason bool visible = false; guitransform->setVisible(visible); reloadTransformMatrix(); } void ofApp::reloadTransformMatrix() { /** * Load the transformation matrix from the xml file * Also applies the nuitrack->realsense transformation if needed */ void ofApp::reloadTransformMatrix() { guitransform->loadFromFile("transformation.xml"); #ifdef BLOB Loading @@ -124,7 +157,8 @@ void ofApp::reloadTransformMatrix() { #endif } void ofApp::setup(){ void ofApp::setup() { #ifdef BLOB if (ofIsGLProgrammableRenderer()) { Loading @@ -136,12 +170,8 @@ void ofApp::setup(){ ofLog(OF_LOG_NOTICE) << "MainAPP: looking for RealSense Device..."; // ofSetLogLevel(OF_LOG_VERBOSE); realSense = RSDevice::createUniquePtr(); realSense->checkConnectedDialog(); realSense->setVideoSize(REALSENSE_VIDEO_WIDTH, REALSENSE_VIDEO_HEIGHT); ofLog(OF_LOG_NOTICE) << "... RealSense Device found."; Loading @@ -161,7 +191,8 @@ void ofApp::setup(){ ofLog(OF_LOG_NOTICE) << "MainAPP: starting attached Device..."; #ifdef BLOB if (realSense->capture()) { if (realSense->capture()) { createGUIDeviceParams(); createGUIPostProcessingParams(); } Loading Loading @@ -238,15 +269,18 @@ void ofApp::createGUIPostProcessingParams() { #endif //-------------------------------------------------------------- void ofApp::update(){ void ofApp::update() { ofBackground(100, 100, 100); #ifdef BLOB if(realSense->update(ofxRealSenseTwo::PointCloud::VIDEO)) { if (maskUpdatesCounter < MASK_UPDATE_CYCLES) { if (maskUpdatesCounter == 0) { if(realSense->update(ofxRealSenseTwo::PointCloud::VIDEO)) { if (maskUpdatesCounter < MASK_UPDATE_CYCLES) { if (maskUpdatesCounter == 0) { tracker.clearMask(); } tracker.captureMaskBegin(); Loading @@ -254,7 +288,9 @@ void ofApp::update(){ tracker.captureMaskEnd(); maskUpdatesCounter++; } else { } else { // Cature captureCloud to FBO tracker.captureBegin(); drawCapturePointCloud(false); Loading @@ -271,6 +307,9 @@ void ofApp::update(){ networkMng.update(tracker); } /** * Save current settings to the various xml files */ void ofApp::saveSettings() { tracker.panel->saveToFile("tracking.xml"); Loading @@ -284,6 +323,9 @@ void ofApp::saveSettings() #endif } /** * Load the settings from the various xml files */ void ofApp::loadSettings() { tracker.panel->loadFromFile("tracking.xml"); Loading