diff --git a/addon_config.mk b/addon_config.mk new file mode 100644 index 0000000..8529e37 --- /dev/null +++ b/addon_config.mk @@ -0,0 +1,76 @@ +# All variables and this file are optional, if they are not present the PG and the +# makefiles will try to parse the correct values from the file system. +# +# Variables that specify exclusions can use % as a wildcard to specify that anything in +# that position will match. A partial path can also be specified to, for example, exclude +# a whole folder from the parsed paths from the file system +# +# Variables can be specified using = or += +# = will clear the contents of that variable both specified from the file or the ones parsed +# from the file system +# += will add the values to the previous ones in the file or the ones parsed from the file +# system +# +# The PG can be used to detect errors in this file, just create a new project with this addon +# and the PG will write to the console the kind of error and in which line it is + +meta: +ADDON_NAME = ofxPiMapper +ADDON_DESCRIPTION = Projection Mapping Addon optimized for the Raspberry Pi +ADDON_AUTHOR = kr15h aspeteRakete +ADDON_TAGS = "Raspberry Pi" "Projection Mapping" +ADDON_URL = http://github.com/kr15h/ofxPiMapper + +common: +# dependencies with other addons, a list of them separated by spaces +# or use += in several lines +ADDON_DEPENDENCIES = ofxIO ofxXmlSettings ofxGui + +# include search paths, this will be usually parsed from the file system +# but if the addon or addon libraries need special search paths they can be +# specified here separated by spaces or one per line using += +# ADDON_INCLUDES = + +# any special flag that should be passed to the compiler when using this +# addon +# ADDON_CFLAGS = + +# any special flag that should be passed to the linker when using this +# addon, also used for system libraries with -lname +# ADDON_LDFLAGS = + +# linux only, any library that should be included in the project using +# pkg-config +# ADDON_PKG_CONFIG_LIBRARIES = + +# osx/iOS only, any framework that should be included in the project +# ADDON_FRAMEWORKS = + +# source files, these will be usually parsed from the file system looking +# in the src folders in libs and the root of the addon. if your addon needs +# to include files in different places or a different set of files per platform +# they can be specified here +# ADDON_SOURCES = + +# some addons need resources to be copied to the bin/data folder of the project +# specify here any files that need to be copied, you can use wildcards like * and ? +# ADDON_DATA = + +# when parsing the file system looking for libraries exclude this for all or +# a specific platform +# ADDON_LIBS_EXCLUDE = + +linux64: +# binary libraries, these will be usually parsed from the file system but some +# libraries need to passed to the linker in a specific order +ADDON_LIBS = +linux: +ADDON_LIBS = +win_cb: +ADDON_LIBS = +linuxarmv6l: +ADDON_LIBS = +linuxarmv7l: +ADDON_LIBS = +android/armeabi: +ADDON_LIBS = diff --git a/example/addons.make b/example/addons.make index e98663f..6546213 100644 --- a/example/addons.make +++ b/example/addons.make @@ -1,3 +1,5 @@ ofxPiMapper -ofxGui +ofxIO ofxXmlSettings +ofxGui +ofxOMXPlayer diff --git a/example/bin/data/sources/videos/test.mov b/example/bin/data/sources/videos/test.mov new file mode 100644 index 0000000..48a307b Binary files /dev/null and b/example/bin/data/sources/videos/test.mov differ diff --git a/example/src/ofApp.cpp b/example/src/ofApp.cpp index 260ebfe..101e14e 100755 --- a/example/src/ofApp.cpp +++ b/example/src/ofApp.cpp @@ -2,6 +2,12 @@ void ofApp::setup() { bShowInfo = false; + + // Pass pointers to our media server instance to both: + // surface manager and it's gui, only then we will be able to + // load surface data from xml settings files + surfaceManager.setMediaServer(&mediaServer); + gui.setMediaServer(&mediaServer); // check if the surfaces.xml file is there // if not - load defaultSurfaces.xml @@ -17,7 +23,8 @@ void ofApp::setup() { // Create FBO fbo = new ofFbo(); fbo->allocate(500, 500); - setFboAsTexture(); + fboSource = new ofx::piMapper::BaseSource(&fbo->getTextureReference()); + setFboAsSource(); // Genereate rects int numRects = 20; // change this to add more or less rects @@ -114,7 +121,7 @@ void ofApp::keyPressed(int key) { surfaceManager.saveXmlSettings("surfaces.xml"); break; case 'a': - setFboAsTexture(); + setFboAsSource(); break; case OF_KEY_BACKSPACE: surfaceManager.removeSelectedSurface(); @@ -178,6 +185,6 @@ void ofApp::addSurface() { surfaceManager.selectSurface(surfaceManager.size() - 1); } -void ofApp::setFboAsTexture() { - surfaceManager.getSurface(0)->setTexture(&fbo->getTextureReference()); +void ofApp::setFboAsSource() { + surfaceManager.getSurface(0)->setSource(fboSource); } \ No newline at end of file diff --git a/example/src/ofApp.h b/example/src/ofApp.h index 1a81109..b3455a3 100755 --- a/example/src/ofApp.h +++ b/example/src/ofApp.h @@ -2,6 +2,7 @@ #include "ofMain.h" #include "ofxPiMapper.h" +#include "BaseSource.h" class ofApp : public ofBaseApp { public: @@ -15,13 +16,15 @@ class ofApp : public ofBaseApp { void addRandomSurface(); void addQuadSurface(); void addSurface(); - void setFboAsTexture(); + void setFboAsSource(); ofImage image; + ofx::piMapper::MediaServer mediaServer; ofx::piMapper::SurfaceManager surfaceManager; ofx::piMapper::SurfaceManagerGui gui; bool bShowInfo; ofFbo* fbo; + ofx::piMapper::BaseSource* fboSource; vector rects; vector rectSpeeds; }; \ No newline at end of file diff --git a/src/MediaServer/DirectoryWatcher.cpp b/src/MediaServer/DirectoryWatcher.cpp new file mode 100644 index 0000000..e55fa79 --- /dev/null +++ b/src/MediaServer/DirectoryWatcher.cpp @@ -0,0 +1,46 @@ +// +// DirectoryWatcher.cpp +// example +// +// Created by felix on 23.09.14. +// +// + +#include "DirectoryWatcher.h" + +namespace ofx { + namespace piMapper { + DirectoryWatcher::DirectoryWatcher(std::string path, int watcherMediaType) { + mediaType = watcherMediaType; + // Decide what filter we need depending on media type + if (mediaType == SourceType::SOURCE_TYPE_VIDEO) { + filter = new VideoPathFilter(); + } else if (mediaType == SourceType::SOURCE_TYPE_IMAGE) { + filter = new ImagePathFilter(); + } else { + ofLogFatalError("DirectoryWatcher::DirectoryWatcher", "Unkonwn media type"); + std::exit(EXIT_FAILURE); + } + dirWatcher.registerAllEvents(this); + // For some reason the filters are not working, + // we leave just the path here and do the filter logic in the listeners + dirWatcher.addPath(path); + // Initial directory listing. Fill the file paths vector. + IO::DirectoryUtils::list(path, filePaths, true, filter); + } + + DirectoryWatcher::~DirectoryWatcher() { + delete filter; + filter = NULL; + } + + std::vector& DirectoryWatcher::getFilePaths() { + return filePaths; + } + + int DirectoryWatcher::getMediaType() { + return mediaType; + } + + } // namespace piMapper +} // namespace ofx \ No newline at end of file diff --git a/src/MediaServer/DirectoryWatcher.h b/src/MediaServer/DirectoryWatcher.h new file mode 100644 index 0000000..40f0bf4 --- /dev/null +++ b/src/MediaServer/DirectoryWatcher.h @@ -0,0 +1,144 @@ +// +// DirectoryWatcher.h +// example +// +// Created by felix on 23.09.14. +// +// + +#pragma once + +#include "ofMain.h" +#include "ofxIO.h" +#include "SourceType.h" + +namespace ofx { +namespace piMapper { + + class BasePathFilter : public ofx::IO::AbstractPathFilter { + public: + BasePathFilter() {}; + virtual ~BasePathFilter() {}; + virtual bool accept(const Poco::Path& path) const {}; + }; + +class VideoPathFilter : public BasePathFilter { + public: + VideoPathFilter() {}; + virtual ~VideoPathFilter() {}; + + bool accept(const Poco::Path& path) const { + return !Poco::File(path).isHidden() && + (ofIsStringInString(path.toString(), ".mp4") || + ofIsStringInString(path.toString(), ".h264")|| + ofIsStringInString(path.toString(), ".mov") || + ofIsStringInString(path.toString(), ".avi") || + ofIsStringInString(path.toString(), ".mpeg")); + } +}; + +class ImagePathFilter : public BasePathFilter { + public: + ImagePathFilter() {}; + virtual ~ImagePathFilter() {}; + + bool accept(const Poco::Path& path) const { + return !Poco::File(path).isHidden() && + (ofIsStringInString(path.toString(), ".png") || + ofIsStringInString(path.toString(), ".jpg") || + ofIsStringInString(path.toString(), ".jpeg")); + } +}; + +class DirectoryWatcher { + public: + DirectoryWatcher(std::string path, int watcherMediaType); + ~DirectoryWatcher(); + + // TODO make useful stuff with onDirectoryWatcher* + void onDirectoryWatcherItemAdded( + const ofx::IO::DirectoryWatcherManager::DirectoryEvent& evt) { + string path = evt.item.path(); + Poco::Path pocoPath = Poco::Path(path); + if (!filter->accept(pocoPath)) { + return; + } + filePaths.push_back(path); + ofNotifyEvent(onItemAdded, path, this); + } + + void onDirectoryWatcherItemRemoved( + const ofx::IO::DirectoryWatcherManager::DirectoryEvent& evt) { + string path = evt.item.path(); + Poco::Path pocoPath = Poco::Path(path); + if (!filter->accept(pocoPath)) { + return; + } + // Remove path from vector + for (int i = 0; i < filePaths.size(); i++) { + if (path == filePaths[i]) { + filePaths.erase(filePaths.begin() + i); + break; + } + } + ofNotifyEvent(onItemRemoved, path, this); + } + + void onDirectoryWatcherItemModified( + const ofx::IO::DirectoryWatcherManager::DirectoryEvent& evt) { + string path = evt.item.path(); + Poco::Path pocoPath = Poco::Path(path); + if (!filter->accept(pocoPath)) { + return; + } + ofNotifyEvent(onItemModified, path, this); + } + + void onDirectoryWatcherItemMovedFrom( + const ofx::IO::DirectoryWatcherManager::DirectoryEvent& evt) { + string path = evt.item.path(); + Poco::Path pocoPath = Poco::Path(path); + if (!filter->accept(pocoPath)) { + return; + } + ofLogNotice("ofApp::onDirectoryWatcherItemMovedFrom") + << "Moved From: " << path; + ofNotifyEvent(onItemMovedFrom, path, this); + } + + void onDirectoryWatcherItemMovedTo( + const ofx::IO::DirectoryWatcherManager::DirectoryEvent& evt) { + string path = evt.item.path(); + Poco::Path pocoPath = Poco::Path(path); + if (!filter->accept(pocoPath)) { + return; + } + ofLogNotice("ofApp::onDirectoryWatcherItemMovedTo") + << "Moved To: " << path; + ofNotifyEvent(onItemMovedTo, path, this); + } + + void onDirectoryWatcherError(const Poco::Exception& exc) { + ofLogError("ofApp::onDirectoryWatcherError") + << "Error: " << exc.displayText(); + } + + // Getters + std::vector& getFilePaths(); + int getMediaType(); + + // Custom events + ofEvent onItemAdded; + ofEvent onItemRemoved; + ofEvent onItemModified; + ofEvent onItemMovedFrom; + ofEvent onItemMovedTo; + + private: + ofx::IO::DirectoryWatcherManager dirWatcher; + BasePathFilter* filter; + std::vector filePaths; + int mediaType; +}; +} +} \ No newline at end of file diff --git a/src/MediaServer/MediaServer.cpp b/src/MediaServer/MediaServer.cpp new file mode 100644 index 0000000..f8539bc --- /dev/null +++ b/src/MediaServer/MediaServer.cpp @@ -0,0 +1,300 @@ +// +// MediaServer.cpp +// example +// +// Created by felix on 13.09.14. +// +// + +#include "MediaServer.h" + +namespace ofx { +namespace piMapper { + + MediaServer::MediaServer(): + videoWatcher(ofToDataPath(DEFAULT_VIDEOS_DIR, true), SourceType::SOURCE_TYPE_VIDEO), + imageWatcher(ofToDataPath(DEFAULT_IMAGES_DIR, true), SourceType::SOURCE_TYPE_IMAGE) { + addWatcherListeners(); + } + + MediaServer::~MediaServer() { + removeWatcherListeners(); + }; + + int MediaServer::getNumImages() { return imageWatcher.getFilePaths().size(); } + int MediaServer::getNumVideos() { return videoWatcher.getFilePaths().size(); } + + std::vector& MediaServer::getImagePaths() { + return imageWatcher.getFilePaths(); + } + + std::vector MediaServer::getImageNames() { + std::vector imageNames; + for (int i = 0; i < getNumImages(); i++) { + // Split image path + std::vector pathParts = ofSplitString(getImagePaths()[i], "/"); + // And get only the last piece + std::string name = pathParts[pathParts.size()-1]; + imageNames.push_back(name); + } + return imageNames; + } + + std::vector& MediaServer::getVideoPaths() { + return videoWatcher.getFilePaths(); + } + + std::vector MediaServer::getVideoNames() { + std::vector videoNames; + for (int i = 0; i < getNumVideos(); i++) { + // Split video path + std::vector pathParts = ofSplitString(getVideoPaths()[i], "/"); + // And get only the last piece + std::string name = pathParts[pathParts.size()-1]; + videoNames.push_back(name); + } + return videoNames; + } + + + + BaseSource* MediaServer::loadMedia(string &path, int mediaType) { + // Chose load method depending on type + if (mediaType == SourceType::SOURCE_TYPE_IMAGE) { + return loadImage(path); + } else if (mediaType == SourceType::SOURCE_TYPE_VIDEO) { + return loadVideo(path); + } else { + std::stringstream ss; + ss << "Can not load media of unknown type: " << mediaType; + ofLogFatalError("MediaServer") << ss.str(); + std::exit(EXIT_FAILURE); + } + return NULL; + } + + BaseSource* MediaServer::loadImage(string& path) { + ImageSource* imageSource = NULL; + // Check if this image is already loaded + bool isImageLoaded = false; + if (loadedSources.count(path)) { + imageSource = static_cast(loadedSources[path]); + isImageLoaded = true; + } + // If image is loaded + if (isImageLoaded) { + // Increase reference count of this source + //referenceCount[path]++; + imageSource->referenceCount++; + std::stringstream refss; + refss << "Current reference count for " << path << " = " << imageSource->referenceCount; + ofLogNotice("MediaServer") << refss.str(); + // Notify objects registered to onImageLoaded event + std::stringstream ss; + ss << "Image " << path << " already loaded"; + ofLogNotice("MediaServer") << ss.str(); + ofNotifyEvent(onImageLoaded, path, this); + return imageSource; + } + // Else load fresh + imageSource = new ImageSource(); + imageSource->loadImage(path); + loadedSources[path] = imageSource; + // Set reference count of this image path to 1 + //referenceCount[path] = 1; + std::stringstream refss; + refss << "Initialized reference count of " << path << " to " << imageSource->referenceCount; + ofLogNotice("MediaServer") << refss.str(); + // Notify objects registered to onImageLoaded event + ofNotifyEvent(onImageLoaded, path, this); + return imageSource; + } + + void MediaServer::unloadImage(string& path) { + ImageSource* source = static_cast(getSourceByPath(path)); + ofLogNotice("MediaServer") << "Unload image, current reference count: " << source->referenceCount; + source->referenceCount--; + // Unload only if reference count is less or equal to 0 + ofLogNotice("MediaServer") << "New reference count: " << source->referenceCount; + if (source->referenceCount > 0) { + ofLogNotice("MediaServer") << "Not unloading image as it is being referenced elsewhere"; + return; + } + // Reference count 0 or less, unload image + std::stringstream ss; + ss << "Removing image " << path; + ofLogNotice("MediaServer") << ss.str(); + // Destroy image source + if (loadedSources.count(path)) { + ofLogNotice("MediaServer") << "Source count BEFORE image removal: " << loadedSources.size() << endl; + loadedSources[path]->clear(); + std::map::iterator it = loadedSources.find(path); + delete it->second; + loadedSources.erase(it); + ofLogNotice("MediaServer") << "Source count AFTER image removal: " << loadedSources.size() << endl; + ofNotifyEvent(onImageUnloaded, path, this); + return; + } + // Something wrong here, we should be out of the routine by now + std::stringstream failss; + failss << "Failed to remove image source: " << path; + ofLogFatalError("MediaServer") << failss.str(); + std::exit(EXIT_FAILURE); + } + + BaseSource* MediaServer::loadVideo(string& path) { + VideoSource* videoSource = NULL; + // Check if this video is already loaded + bool isVideoLoaded = false; + if (loadedSources.count(path)) { + videoSource = static_cast(loadedSources[path]); + isVideoLoaded = true; + } + // If is loaded + if (isVideoLoaded) { + // Increase reference count of this source + videoSource->referenceCount++; + std::stringstream refss; + refss << "Current reference count for " << path << " = " << videoSource->referenceCount; + ofLogNotice("MediaServer") << refss.str(); + // Notify objects registered to onImageLoaded event + std::stringstream ss; + ss << "Video " << path << " already loaded"; + ofLogNotice("MediaServer") << ss.str(); + ofNotifyEvent(onVideoLoaded, path, this); + return videoSource; + } + // Else load fresh + videoSource = new VideoSource(); + videoSource->loadVideo(path); + loadedSources[path] = videoSource; + // Set reference count of this image path to 1 + //referenceCount[path] = 1; + std::stringstream refss; + refss << "Initialized reference count of " << path << " to " << videoSource->referenceCount; + ofLogNotice("MediaServer") << refss.str(); + ofNotifyEvent(onVideoLoaded, path, this); + return videoSource; + } + + void MediaServer::unloadVideo(string& path) { + VideoSource* videoSource = static_cast(getSourceByPath(path)); + // Decrease reference count of the video + //referenceCount[path]--; + videoSource->referenceCount--; + // Unload only if reference count is less or equal to 0 + if (videoSource->referenceCount > 0) { + ofLogNotice("MediaServer") << "Not unloading video as it is being referenced elsewhere"; + return; + } + // Reference count 0 or less, let's unload the video + ofLogNotice("MediaServer") << "Removing video " << path; + // Distroy video source + if (loadedSources.count(path)) { + ofLogNotice("MediaServer") << "Source count before video removal: " << loadedSources.size() << endl; + videoSource->clear(); + std::map::iterator it = loadedSources.find(path); + delete it->second; + loadedSources.erase(it); + ofLogNotice("MediaServer") << "Source count after video removal: " << loadedSources.size() << endl; + ofNotifyEvent(onVideoUnloaded, path, this); + return; + } + // Something wrong here, we should be out of the routine by now + std::stringstream failss; + failss << "Failed to remove video source: " << path; + ofLogFatalError("MediaServer") << failss.str(); + std::exit(EXIT_FAILURE); + } + + void MediaServer::unloadMedia(string &path) { + if (loadedSources.count(path)) { + BaseSource* mediaSource = getSourceByPath(path); + if (mediaSource->getType() == SourceType::SOURCE_TYPE_IMAGE) { + unloadImage(path); + } else if (mediaSource->getType() == SourceType::SOURCE_TYPE_VIDEO) { + unloadVideo(path); + } else { + // Oh my god, what to do!? Relax and exit. + ofLogFatalError("MediaServer") << "Attempt to unload media of unknown type"; + std::exit(EXIT_FAILURE); + } + } else { + std:stringstream ss; + ss << "Media with path " << path << " is not loaded and thus can't be unloaded"; + ofLogFatalError("MediaServer") << ss.str(); + std::exit(EXIT_FAILURE); + } + } + + // Clear all loaded media + void MediaServer::clear() { + typedef std::map::iterator it_type; + for (it_type i = loadedSources.begin(); i != loadedSources.end(); i++) { + delete i->second; + } + loadedSources.clear(); + } + + BaseSource* MediaServer::getSourceByPath(std::string& mediaPath) { + if (loadedSources.count(mediaPath)) { + return loadedSources[mediaPath]; + } + // Source not found, exit with error + std::stringstream ss; + ss << "Could not find source by path: " << mediaPath; + ofLogFatalError("MediaServer") << ss.str(); + std::exit(EXIT_FAILURE); + } + + std::string MediaServer::getDefaultImageDir() { + return DEFAULT_IMAGES_DIR; + } + + std::string MediaServer::getDefaultVideoDir() { + return DEFAULT_VIDEOS_DIR; + } + + std::string MediaServer::getDefaultMediaDir(int sourceType) { + if (sourceType == SourceType::SOURCE_TYPE_IMAGE) { + return getDefaultImageDir(); + } else if (sourceType == SourceType::SOURCE_TYPE_VIDEO) { + return getDefaultVideoDir(); + } else { + std::stringstream ss; + ss << "Could not get default media dir. Unknown source type: " << sourceType; + ofLogFatalError("MediaServer") << ss.str(); + std::exit(EXIT_FAILURE); + } + } + + void MediaServer::handleImageAdded(string& path) { + ofNotifyEvent(onImageAdded, path, this); + } + void MediaServer::handleImageRemoved(string& path) { + ofNotifyEvent(onImageRemoved, path, this); + } + + void MediaServer::handleVideoAdded(string& path) { + ofNotifyEvent(onVideoAdded, path, this); + } + void MediaServer::handleVideoRemoved(string& path) { + ofNotifyEvent(onVideoRemoved, path, this); + } + + void MediaServer::addWatcherListeners() { + ofAddListener(imageWatcher.onItemAdded, this, &MediaServer::handleImageAdded); + ofAddListener(imageWatcher.onItemRemoved, this, &MediaServer::handleImageRemoved); + ofAddListener(videoWatcher.onItemAdded, this, &MediaServer::handleVideoAdded); + ofAddListener(videoWatcher.onItemRemoved, this, &MediaServer::handleVideoRemoved); + } + + void MediaServer::removeWatcherListeners() { + ofRemoveListener(imageWatcher.onItemAdded, this, &MediaServer::handleImageAdded); + ofRemoveListener(imageWatcher.onItemRemoved, this, &MediaServer::handleImageRemoved); + ofRemoveListener(videoWatcher.onItemAdded, this, &MediaServer::handleVideoAdded); + ofRemoveListener(videoWatcher.onItemRemoved, this, &MediaServer::handleVideoRemoved); + } + +} // namespace piMapper +} // namespace ofx \ No newline at end of file diff --git a/src/MediaServer/MediaServer.h b/src/MediaServer/MediaServer.h new file mode 100644 index 0000000..3e700e1 --- /dev/null +++ b/src/MediaServer/MediaServer.h @@ -0,0 +1,91 @@ +// +// MediaServer.h +// example +// +// Created by felix on 13.09.14. +// +// + +#pragma once + +#include "ofMain.h" +#include "DirectoryWatcher.h" +#include "BaseSource.h" +#include "ImageSource.h" +#include "VideoSource.h" +#include "SourceType.h" + +#define DEFAULT_IMAGES_DIR "sources/images/" +#define DEFAULT_VIDEOS_DIR "sources/videos/" + +namespace ofx { +namespace piMapper { + +class MediaServer { + public: + MediaServer(); + virtual ~MediaServer(); + + int getNumVideos(); + int getNumImages(); + std::vector& getVideoPaths(); + std::vector getVideoNames(); + std::vector& getImagePaths(); + std::vector getImageNames(); + + BaseSource* loadMedia(string& path, int mediaType); + BaseSource* loadImage(string& path); + void unloadImage(string& path); + BaseSource* loadVideo(string& path); + void unloadVideo(string& path); + void unloadMedia(string& path); + void clear(); // Force all loaded source unload + BaseSource* getSourceByPath(std::string& mediaPath); + std::string getDefaultImageDir(); + std::string getDefaultVideoDir(); + std::string getDefaultMediaDir(int sourceType); + + // Custom events + ofEvent onImageAdded; + ofEvent onImageRemoved; + ofEvent onVideoAdded; + ofEvent onVideoRemoved; + ofEvent onImageLoaded; + ofEvent onImageUnloaded; + ofEvent onVideoLoaded; + ofEvent onVideoUnloaded; + + private: + // Directory Watchers + ofx::piMapper::DirectoryWatcher videoWatcher; + ofx::piMapper::DirectoryWatcher imageWatcher; + std::map loadedSources; + // imageWatcher event listeners + void handleImageAdded(string& path); + void handleImageRemoved(string& path); + // TODO rest of listeners + /* + void onImageModified(); + void onImageMovedFrom(); + void onImageMovedTo(); + */ + + // videoWatcher event listeners + void handleVideoAdded(string& path); + void handleVideoRemoved(string& path); + // TODO rest of listeners + /* + void onVideoModified(); + void onVideoMovedFrom(); + void onVideoMovedTo(); + */ + + // Add/remove event listeners. + // Add event listeners to image and video watcher events. + void addWatcherListeners(); + + // Remove event listeners to image and video watcher events + void removeWatcherListeners(); +}; +} // namespace piMapper +} // namespace ofx diff --git a/src/Sources/BaseSource.cpp b/src/Sources/BaseSource.cpp new file mode 100644 index 0000000..d984b72 --- /dev/null +++ b/src/Sources/BaseSource.cpp @@ -0,0 +1,59 @@ +#include "BaseSource.h" + +namespace ofx { + namespace piMapper { + BaseSource::BaseSource() { + //cout << "BaseSource" << endl; + init(); + } + + BaseSource::BaseSource(ofTexture* newTexture) { + init(); + texture = newTexture; + } + + BaseSource::~BaseSource() {} + + ofTexture* BaseSource::getTexture() { + return texture; + } + + std::string& BaseSource::getName() { + return name; + } + + bool BaseSource::isLoadable() { + return loadable; + } + + bool BaseSource::isLoaded() { + return loaded; + } + + int BaseSource::getType() { + return type; + } + + std::string& BaseSource::getPath() { + return path; + } + + void BaseSource::init() { + texture = NULL; + name = ""; + path = ""; + loadable = false; + loaded = false; + type = SourceType::SOURCE_TYPE_NONE; + referenceCount = 1; // We have one instance on init + } + + void BaseSource::setNameFromPath(std::string& fullPath) { + std::vector pathParts; + //cout << "fullPath: " << fullPath << endl; + pathParts = ofSplitString(fullPath, "/"); // Maybe on win "/" is "\", have to test + //cout << "lastPathPart: " << pathParts[pathParts.size() - 1] << endl; + name = pathParts[pathParts.size() - 1]; + } + } +} \ No newline at end of file diff --git a/src/Sources/BaseSource.h b/src/Sources/BaseSource.h new file mode 100644 index 0000000..fb5e567 --- /dev/null +++ b/src/Sources/BaseSource.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ofMain.h" +#include "SourceType.h" + +namespace ofx { + namespace piMapper { + // Use this for adding generative sources to your surfaces + class BaseSource { + public: + BaseSource(); + BaseSource(ofTexture* newTexture); // Only one clean way of passing the texture + ~BaseSource(); + ofTexture* getTexture(); + std::string& getName(); + bool isLoadable(); // Maybe the loading features shoud go to a derrived class + bool isLoaded(); // as BaseSourceLoadable + int getType(); + std::string& getPath(); + virtual void clear() {}; + int referenceCount; + + private: + void init(); + + protected: + void setNameFromPath(std::string& fullPath); + ofTexture* texture; + std::string name; + std::string path; // This is set only if loadable is true + bool loadable; // If the source can be loaded from disk like image and video + bool loaded; // Is the source loaded? + int type; + }; + } +} diff --git a/src/Sources/ImageSource.cpp b/src/Sources/ImageSource.cpp new file mode 100644 index 0000000..0cf8dde --- /dev/null +++ b/src/Sources/ImageSource.cpp @@ -0,0 +1,39 @@ +#include "ImageSource.h" + +namespace ofx { + namespace piMapper { + ImageSource::ImageSource() { + //cout << "ImageSource" << endl; + loadable = true; + loaded = false; + type = SourceType::SOURCE_TYPE_IMAGE; + } + + ImageSource::~ImageSource() {} + + void ImageSource::loadImage(std::string& filePath) { + path = filePath; + //cout << "loading image: " << filePath << endl; + setNameFromPath(filePath); + //cout << "path: " << path << endl; + image = new ofImage(); + if (!image->loadImage(filePath)) { + ofLogWarning("ImageSource") << "Could not load image"; + //std::exit(EXIT_FAILURE); + } + texture = &image->getTextureReference(); + loaded = true; + } + + void ImageSource::clear() { + texture = NULL; + image->clear(); + delete image; + image = NULL; + //path = ""; + //name = ""; + loaded = false; + } + + } +} \ No newline at end of file diff --git a/src/Sources/ImageSource.h b/src/Sources/ImageSource.h new file mode 100644 index 0000000..29a5d79 --- /dev/null +++ b/src/Sources/ImageSource.h @@ -0,0 +1,18 @@ +#pragma once + +#include "BaseSource.h" + +namespace ofx { + namespace piMapper { + class ImageSource : public BaseSource { + public: + ImageSource(); + ~ImageSource(); + std::string& getPath(); + void loadImage(std::string& filePath); + void clear(); + private: + ofImage* image; + }; + } +} \ No newline at end of file diff --git a/src/Sources/SourceType.h b/src/Sources/SourceType.h new file mode 100644 index 0000000..515e2e4 --- /dev/null +++ b/src/Sources/SourceType.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ofLog.h" + +#define SOURCE_TYPE_NAME_NONE "none" +#define SOURCE_TYPE_NAME_IMAGE "image" +#define SOURCE_TYPE_NAME_VIDEO "video" + +namespace ofx { + namespace piMapper { + class SourceType { + public: + enum { SOURCE_TYPE_NONE, SOURCE_TYPE_IMAGE, SOURCE_TYPE_VIDEO }; + + static std::string GetSourceTypeName(int sourceTypeEnum) { + if (sourceTypeEnum == SOURCE_TYPE_IMAGE) { + return SOURCE_TYPE_NAME_IMAGE; + } else if (sourceTypeEnum == SOURCE_TYPE_VIDEO) { + return SOURCE_TYPE_NAME_VIDEO; + } else if (sourceTypeEnum == SOURCE_TYPE_NONE) { + return SOURCE_TYPE_NAME_NONE; + } else { + std::stringstream ss; + ss << "Invalid source type: " << sourceTypeEnum; + ofLogFatalError("SourceType") << ss.str(); + std::exit(EXIT_FAILURE); + } + }; + + static int GetSourceTypeEnum(std::string sourceTypeName) { + if (sourceTypeName == SOURCE_TYPE_NAME_IMAGE) { + return SOURCE_TYPE_IMAGE; + } else if (sourceTypeName == SOURCE_TYPE_NAME_VIDEO) { + return SOURCE_TYPE_VIDEO; + } else if (sourceTypeName == SOURCE_TYPE_NAME_NONE) { + return SOURCE_TYPE_NONE; + } else { + std::stringstream ss; + ss << "Invalid source type name: " << sourceTypeName; + ofLogFatalError("SourceType") << ss.str(); + std::exit(EXIT_FAILURE); + } + } + }; + } +} \ No newline at end of file diff --git a/src/Sources/VideoSource.cpp b/src/Sources/VideoSource.cpp new file mode 100644 index 0000000..4ccbdb4 --- /dev/null +++ b/src/Sources/VideoSource.cpp @@ -0,0 +1,74 @@ +#include "VideoSource.h" + +namespace ofx { + namespace piMapper { + VideoSource::VideoSource() { + //cout << "VideoSource constr" << endl; + loadable = true; + loaded = false; + type = SourceType::SOURCE_TYPE_VIDEO; +#ifdef TARGET_RASPBERRY_PI + omxPlayer = NULL; +#else + videoPlayer = NULL; + +#endif + } + + VideoSource::~VideoSource() {} + + void VideoSource::loadVideo(std::string& filePath) { + path = filePath; + //cout << "loading video: " << filePath << endl; + setNameFromPath(filePath); +#ifdef TARGET_RASPBERRY_PI + // Do things with the OMX player + ofxOMXPlayerSettings settings; + settings.videoPath = filePath; + settings.useHDMIForAudio = true; //default true + settings.enableTexture = true; //default true + settings.enableLooping = true; //default true + settings.enableAudio = false; //default true, save resources by disabling + //settings.doFlipTexture = true; //default false + omxPlayer = new ofxOMXPlayer(); + omxPlayer->setup(settings); + texture = &(omxPlayer->getTextureReference()); +#else + // regular ofVideoPlayer + videoPlayer = new ofVideoPlayer(); + videoPlayer->loadMovie(filePath); + videoPlayer->setLoopState(OF_LOOP_NORMAL); + videoPlayer->play(); + texture = &(videoPlayer->getTextureReference()); + ofAddListener(ofEvents().update, this, &VideoSource::update); +#endif + loaded = true; + } + + void VideoSource::clear() { + texture = NULL; +#ifdef TARGET_RASPBERRY_PI + omxPlayer->close(); + delete omxPlayer; + omxPlayer = NULL; +#else + ofRemoveListener(ofEvents().update, this, &VideoSource::update); + videoPlayer->stop(); + videoPlayer->close(); + delete videoPlayer; + videoPlayer = NULL; +#endif + //path = ""; + //name = ""; + loaded = false; + } + +#ifndef TARGET_RASPBERRY_PI + void VideoSource::update(ofEventArgs &args) { + if (videoPlayer != NULL) { + videoPlayer->update(); + } + } +#endif + } +} \ No newline at end of file diff --git a/src/Sources/VideoSource.h b/src/Sources/VideoSource.h new file mode 100644 index 0000000..9ab8a97 --- /dev/null +++ b/src/Sources/VideoSource.h @@ -0,0 +1,48 @@ +#pragma once + +#include "ofMain.h" +#include "BaseSource.h" +#ifdef TARGET_RASPBERRY_PI + /* + Notice that if you get an error like this: + fatal error: libavcodec/opt.h: No such file or directory + Create a file opt.h in addons/ofxOMXPlayer/libs/ffmpeg/libavcodec/ + with the following contents: + + #ifndef AVCODEC_OPT_H + #define AVCODEC_OPT_H + #include "libavcodec/version.h" + #if FF_API_OPT_H + #include "libavutil/opt.h" + #endif + #endif // AVCODEC_OPT_H + + More here: https://github.com/jvcleave/ofxOMXPlayer/issues/34 + */ + #include "ofxOMXPlayer.h" +#endif + +namespace ofx { + namespace piMapper { + class VideoSource : public BaseSource { + public: + VideoSource(); + ~VideoSource(); + std::string& getPath(); + void loadVideo(std::string& path); + void clear(); +#ifndef TARGET_RASPBERRY_PI + void update(ofEventArgs& args); +#endif + + private: +#ifdef TARGET_RASPBERRY_PI + ofxOMXPlayer* omxPlayer; // Naming different for less confusion +#else + // Go with ofVideoPlayer or + // TODO: High Performance Video player on newer Macs + ofVideoPlayer* videoPlayer; +#endif + }; + } +} \ No newline at end of file diff --git a/src/SourcesEditor.cpp b/src/SourcesEditor.cpp deleted file mode 100644 index 452ddb9..0000000 --- a/src/SourcesEditor.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "SourcesEditor.h" - -namespace ofx { -namespace piMapper { -SourcesEditor::SourcesEditor() { - defImgDir = DEFAULT_IMAGES_DIR; - registerAppEvents(); -} - -SourcesEditor::~SourcesEditor() { - unregisterAppEvents(); - delete gui; - while (images.size()) { - delete images.back(); - images.pop_back(); - } -} - -void SourcesEditor::registerAppEvents() { - ofAddListener(ofEvents().setup, this, &SourcesEditor::setup); -} - -void SourcesEditor::unregisterAppEvents() { - ofRemoveListener(ofEvents().setup, this, &SourcesEditor::setup); -} - -void SourcesEditor::setup(ofEventArgs& args) { - gui = new RadioList(); - - // read directory contents - ofDirectory imgDir; - imgDir.listDir(defImgDir); - imgDir.sort(); - - vector vnames; - - for (int i = 0; i < (int)imgDir.size(); i++) { - // images[i].loadImage(imgDir.getPath(i)); - vnames.push_back(imgDir.getName(i)); - } - - gui->setup("Images", vnames); - gui->setPosition(20, 20); - ofAddListener(gui->radioSelectedEvent, this, &SourcesEditor::guiEvent); -} - -void SourcesEditor::draw() { - // Don't draw if there is no source selected - if (surfaceManager->getSelectedSurface() == NULL) { - return; - } - - gui->draw(); -} - -void SourcesEditor::loadImage(string name, string path) { - images.push_back(new ofImage()); - images.back()->loadImage(path); - - imageNames.push_back(name); - - ofSendMessage("imageLoaded"); -} - -void SourcesEditor::disable() { gui->disable(); } - -void SourcesEditor::enable() { - // Don't enable if there is no surface selected - if (surfaceManager->getSelectedSurface() == NULL) { - cout << "No surface selected. Not enable()ing source list." << endl; - return; - } - - gui->enable(); -} - -void SourcesEditor::setSurfaceManager(SurfaceManager* newSurfaceManager) { - surfaceManager = newSurfaceManager; -} - -void SourcesEditor::selectImageSourceRadioButton(string name) { - if (name == "none") { - gui->unselectAll(); - return; - } else { - int i; - for (i = 0; i < gui->size(); i++) { - if (gui->getItemName(i) == name) { - gui->selectItem(i); - return; - } - } - } -} - -int SourcesEditor::getLoadedTexCount() { return images.size(); } - -ofTexture* SourcesEditor::getTexture(int index) { - if (index >= images.size()) { - throw std::runtime_error("Texture index out of bounds."); - } - - return &images[index]->getTextureReference(); -} - -void SourcesEditor::guiEvent(string& imageName) { - string name = imageName; - - if (surfaceManager->getSelectedSurface() == NULL) { - return; - } - - stringstream ss; - ss << defImgDir << name; - cout << "attempt to load image: " << ss.str() << endl; - ofTexture* texture = surfaceManager->loadImageSource(name, ss.str()); - surfaceManager->getSelectedSurface()->setTexture(texture); - surfaceManager->manageMemory(); -} -} -} \ No newline at end of file diff --git a/src/SourcesEditor.h b/src/SourcesEditor.h deleted file mode 100644 index ee4462b..0000000 --- a/src/SourcesEditor.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "ofGraphics.h" -#include "ofEvents.h" -#include "SurfaceManager.h" -#include "RadioList.h" - -#define DEFAULT_IMAGES_DIR "sources/images/"; - -namespace ofx { -namespace piMapper { -class SourcesEditor { - public: - SourcesEditor(); - ~SourcesEditor(); - - void registerAppEvents(); - void unregisterAppEvents(); - - void setup(ofEventArgs& args); - void draw(); - void loadImage(string name, string path); - void disable(); - void enable(); - void setSurfaceManager(SurfaceManager* newSurfaceManager); - void selectImageSourceRadioButton(string name); - - int getLoadedTexCount(); - ofTexture* getTexture(int index); - - private: - SurfaceManager* surfaceManager; - RadioList* gui; - string defImgDir; - void guiEvent(string& imageName); - vector images; - vector imageNames; -}; -} -} \ No newline at end of file diff --git a/src/SurfaceManager.cpp b/src/SurfaceManager.cpp index d29ca09..c2c8c68 100644 --- a/src/SurfaceManager.cpp +++ b/src/SurfaceManager.cpp @@ -2,7 +2,10 @@ namespace ofx { namespace piMapper { -SurfaceManager::SurfaceManager() {} + SurfaceManager::SurfaceManager() { + // Init variables + mediaServer = NULL; + } SurfaceManager::~SurfaceManager() { clear(); } @@ -18,19 +21,21 @@ void SurfaceManager::addSurface(int surfaceType) { } else if (surfaceType == SurfaceType::QUAD_SURFACE) { surfaces.push_back(new QuadSurface()); } else { - throw std::runtime_error("Attempt to add non-existing surface type."); + ofLogFatalError("SurfaceManager") << "Attempt to add non-existing surface type"; + std::exit(EXIT_FAILURE); } } -void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr) { +void SurfaceManager::addSurface(int surfaceType, BaseSource* newSource) { if (surfaceType == SurfaceType::TRIANGLE_SURFACE) { surfaces.push_back(new TriangleSurface()); - surfaces.back()->setTexture(texturePtr); + surfaces.back()->setSource(newSource); } else if (surfaceType == SurfaceType::QUAD_SURFACE) { surfaces.push_back(new QuadSurface()); - surfaces.back()->setTexture(texturePtr); + surfaces.back()->setSource(newSource); } else { - throw std::runtime_error("Attempt to add non-existing surface type."); + ofLogFatalError("SurfaceManager") << "Attempt to add non-existing surface type"; + std::exit(EXIT_FAILURE); } } @@ -67,11 +72,12 @@ void SurfaceManager::addSurface(int surfaceType, vector vertices, surfaces.back()->setTexCoord(i, texCoords[i]); } } else { - throw std::runtime_error("Attempt to add non-existing surface type."); + ofLogFatalError("SurfaceManager") << "Attempt to add non-existing surface type"; + std::exit(EXIT_FAILURE); } } -void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, +void SurfaceManager::addSurface(int surfaceType, BaseSource* newSource, vector vertices, vector texCoords) { if (surfaceType == SurfaceType::TRIANGLE_SURFACE) { @@ -84,7 +90,7 @@ void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, } surfaces.push_back(new TriangleSurface()); - surfaces.back()->setTexture(texturePtr); + surfaces.back()->setSource(newSource); for (int i = 0; i < 3; i++) { surfaces.back()->setVertex(i, vertices[i]); @@ -100,20 +106,22 @@ void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, } surfaces.push_back(new QuadSurface()); - surfaces.back()->setTexture(texturePtr); + surfaces.back()->setSource(newSource); for (int i = 0; i < 4; i++) { surfaces.back()->setVertex(i, vertices[i]); surfaces.back()->setTexCoord(i, texCoords[i]); } } else { - throw std::runtime_error("Attempt to add non-existing surface type."); + ofLogFatalError("SurfaceManager") << "Attempt to add non-existing surface type"; + std::exit(EXIT_FAILURE); } } void SurfaceManager::removeSelectedSurface() { - if (selectedSurface == NULL) return; - + if (selectedSurface == NULL) { + return; + } for (int i = 0; i < surfaces.size(); i++) { if (surfaces[i] == selectedSurface) { delete surfaces[i]; @@ -124,61 +132,23 @@ void SurfaceManager::removeSelectedSurface() { } } -void SurfaceManager::manageMemory() { - // check if each of the sources is assigned to a surface or not - for (int i = 0; i < loadedImageSources.size(); i++) { - bool bAssigned = false; - - for (int j = 0; j < surfaces.size(); j++) { - if (surfaces[j]->getTexture() == - &loadedImageSources[i]->getTextureReference()) { - bAssigned = true; - break; - } - } - - if (!bAssigned) { - // purge the image source from memory - delete loadedImageSources[i]; - loadedImageSources.erase(loadedImageSources.begin() + i); - cout << "Deleting image source: " << loadedImageSourceNames[i] << endl; - loadedImageSourceNames.erase(loadedImageSourceNames.begin() + i); - i--; - } - } -} - void SurfaceManager::clear() { // delete all extra allocations from the heap while (surfaces.size()) { delete surfaces.back(); surfaces.pop_back(); } - - while (loadedImageSources.size()) { - delete loadedImageSources.back(); - loadedImageSources.pop_back(); - } - - while (loadedImageSourceNames.size()) { - loadedImageSourceNames.pop_back(); - } } -// String getTypeString(SurfaceType e) -// { -// switch e -// { -// case TRINAGLE_SURFACE: return "TRINAGLE_SURFACE"; -// case QUAD_SURFACE: return "QUAD_SURFACE"; -// default: throw Exception("Bad MyEnum"); -// } -// } - void SurfaceManager::saveXmlSettings(string fileName) { + // Exit if mediaServer not set + if (mediaServer == NULL) { + ofLogFatalError("SurfaceManager") << "Media server not set"; + std::exit(EXIT_FAILURE); + } + // We need a fresh copy of the xml settings object xmlSettings.clear(); - - // save surfaces + // Save surfaces xmlSettings.addTag("surfaces"); xmlSettings.pushTag("surfaces"); for (int i = 0; i < surfaces.size(); i++) { @@ -214,37 +184,31 @@ void SurfaceManager::saveXmlSettings(string fileName) { xmlSettings.popTag(); // texCoord } xmlSettings.popTag(); // texCoords - xmlSettings.addTag("source"); xmlSettings.pushTag("source"); - - xmlSettings.addValue("source-type", "image"); - xmlSettings.addValue("source-name", getSurfaceSourceName(surface)); - // xmlSettings.addValue("source-path", "/root/etc/image.jpg"); + string sourceTypeName = SourceType::GetSourceTypeName(surface->getSource()->getType()); + cout << "sourceTypeName: " << sourceTypeName << endl; + xmlSettings.addValue("source-type", sourceTypeName); + xmlSettings.addValue("source-name", surface->getSource()->getName()); xmlSettings.popTag(); // source - - // xmlSettings.addTag("type"); - // xmlSettings.pushTag("type"); - // // surfaceType == SurfaceType::TRIANGLE_SURFACE - // SurfaceType surfaceType = &surface->getType(); - // xmlSettings.addValue("surface-type", surfaceType); - // xmlSettings.popTag(); // type - xmlSettings.popTag(); // surface } xmlSettings.popTag(); // surfaces - xmlSettings.save(fileName); } void SurfaceManager::loadXmlSettings(string fileName) { + // Exit if there is no media server + if (mediaServer == NULL) { + ofLogFatalError("SurfaceManager") << "Media server not set"; + std::exit(EXIT_FAILURE); + } if (!xmlSettings.loadFile(fileName)) { - ofLog(OF_LOG_WARNING, "Could not load XML settings."); + ofLogWarning("SurfaceManager") << "Could not load XML settings"; return; } - if (!xmlSettings.tagExists("surfaces")) { - ofLog(OF_LOG_WARNING, "XML settings is empty or has wrong markup."); + ofLogWarning("SurfaceManager") << "XML settings is empty or has wrong markup"; return; } @@ -255,31 +219,27 @@ void SurfaceManager::loadXmlSettings(string fileName) { xmlSettings.pushTag("surface", i); // attempt to load surface source xmlSettings.pushTag("source"); - string sourceType = xmlSettings.getValue("source-type", "image"); - string sourceName = xmlSettings.getValue("source-name", "none"); - ofTexture* sourceTexture = NULL; - if (sourceName != "none") { - stringstream ss; - ss << "sources/images/" << sourceName; // TODO: reuse constants here - sourceTexture = loadImageSource(sourceName, ss.str()); + string sourceType = xmlSettings.getValue("source-type", ""); + string sourceName = xmlSettings.getValue("source-name", ""); + BaseSource* source = NULL; + if (sourceName != "" && sourceName != "none" && sourceType != "") { + // Load source depending on type + int typeEnum = SourceType::GetSourceTypeEnum(sourceType); + // Construct full path + string dir = mediaServer->getDefaultMediaDir(typeEnum); + std::stringstream pathss; + pathss << ofToDataPath(dir, true) << sourceName; + string sourcePath = pathss.str(); + // Load media by using full path + source = mediaServer->loadMedia(sourcePath, typeEnum); } xmlSettings.popTag(); // source - - // // attempt to load surface type - // ofLog(OF_LOG_WARNING, "Attempt to load surface type."); - // xmlSettings.pushTag("type"); - // string surfaceType = xmlSettings.getValue("surface-type", - // "TRIANGLE_SURFACE"); - // xmlSettings.popTag(); // type - xmlSettings.pushTag("vertices"); vector vertices; - int vertexCount = xmlSettings.getNumTags("vertex"); - // it's a triangle ? if (vertexCount == 3) { - ofLog(OF_LOG_NOTICE, "create Triangle"); + //ofLog(OF_LOG_NOTICE, "create Triangle"); xmlSettings.pushTag("vertex", 0); vertices.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 0.0f))); @@ -320,8 +280,8 @@ void SurfaceManager::loadXmlSettings(string fileName) { // now we have variables sourceName and sourceTexture // by checking those we can use one or another addSurface method - if (sourceName != "none" && sourceTexture != NULL) { - addSurface(SurfaceType::TRIANGLE_SURFACE, sourceTexture, vertices, + if (sourceName != "none" && source != NULL) { + addSurface(SurfaceType::TRIANGLE_SURFACE, source, vertices, texCoords); } else { addSurface(SurfaceType::TRIANGLE_SURFACE, vertices, texCoords); @@ -381,8 +341,8 @@ void SurfaceManager::loadXmlSettings(string fileName) { // now we have variables sourceName and sourceTexture // by checking those we can use one or another addSurface method - if (sourceName != "none" && sourceTexture != NULL) { - addSurface(SurfaceType::QUAD_SURFACE, sourceTexture, vertices, + if (sourceName != "none" && source != NULL) { + addSurface(SurfaceType::QUAD_SURFACE, source, vertices, texCoords); } else { addSurface(SurfaceType::QUAD_SURFACE, vertices, texCoords); @@ -394,6 +354,10 @@ void SurfaceManager::loadXmlSettings(string fileName) { xmlSettings.popTag(); // surfaces } + + void SurfaceManager::setMediaServer(MediaServer* newMediaServer) { + mediaServer = newMediaServer; + } BaseSurface* SurfaceManager::selectSurface(int index) { if (index >= surfaces.size()) { @@ -406,46 +370,12 @@ BaseSurface* SurfaceManager::selectSurface(int index) { ofSendMessage("surfaceSelected"); } -BaseSurface* SurfaceManager::getSelectedSurface() { return selectedSurface; } - -void SurfaceManager::deselectSurface() { selectedSurface = NULL; } - -ofTexture* SurfaceManager::loadImageSource(string name, string path) { - // check if it is loaded - for (int i = 0; i < loadedImageSourceNames.size(); i++) { - if (loadedImageSourceNames[i] == name) { - // this image is already loaded - return &loadedImageSources[i]->getTextureReference(); - } - } - - // not loaded - load - ofImage* image = new ofImage(); - if (!image->loadImage(path)) { - return NULL; - } - loadedImageSources.push_back(image); - loadedImageSourceNames.push_back(name); - return &image->getTextureReference(); +BaseSurface* SurfaceManager::getSelectedSurface() { + return selectedSurface; } -string SurfaceManager::getSelectedSurfaceSourceName() { - if (selectedSurface == NULL) { - return "none"; - } - - return getSurfaceSourceName(selectedSurface); -} - -string SurfaceManager::getSurfaceSourceName(BaseSurface* surface) { - ofTexture* tex = surface->getTexture(); - for (int i = 0; i < loadedImageSources.size(); i++) { - if (tex == &loadedImageSources[i]->getTextureReference()) { - return loadedImageSourceNames[i]; - } - } - - return "none"; +void SurfaceManager::deselectSurface() { + selectedSurface = NULL; } BaseSurface* SurfaceManager::getSurface(int index) { diff --git a/src/SurfaceManager.h b/src/SurfaceManager.h index 38af95f..2e6eb6f 100755 --- a/src/SurfaceManager.h +++ b/src/SurfaceManager.h @@ -4,6 +4,9 @@ #include "TriangleSurface.h" #include "QuadSurface.h" #include "SurfaceType.h" +#include "MediaServer.h" +#include "BaseSource.h" +#include "SourceType.h" #include "ofEvents.h" #include "ofxXmlSettings.h" @@ -19,32 +22,28 @@ class SurfaceManager { void draw(); void addSurface(int surfaceType); - void addSurface(int surfaceType, ofTexture* texturePtr); + void addSurface(int surfaceType, BaseSource* newSource); void addSurface(int surfaceType, vector vertices, vector texCoords); - void addSurface(int surfaceType, ofTexture* texturePtr, + void addSurface(int surfaceType, BaseSource* newSource, vector vertices, vector texCoords); void removeSelectedSurface(); - void manageMemory(); // deletes unasigned sources void clear(); void saveXmlSettings(string fileName); void loadXmlSettings(string fileName); + void setMediaServer(MediaServer* newMediaServer); BaseSurface* getSurface(int index); int size(); BaseSurface* selectSurface(int index); BaseSurface* getSelectedSurface(); void deselectSurface(); - ofTexture* loadImageSource(string name, string path); - string getSelectedSurfaceSourceName(); - string getSurfaceSourceName(BaseSurface* surface); private: - vector surfaces; + std::vector surfaces; BaseSurface* selectedSurface; - vector loadedImageSourceNames; - vector loadedImageSources; ofxXmlSettings xmlSettings; + MediaServer* mediaServer; }; } } \ No newline at end of file diff --git a/src/SurfaceManagerGui.cpp b/src/SurfaceManagerGui.cpp index ce73e99..dee1584 100644 --- a/src/SurfaceManagerGui.cpp +++ b/src/SurfaceManagerGui.cpp @@ -174,6 +174,13 @@ void SurfaceManagerGui::setSurfaceManager(SurfaceManager* newSurfaceManager) { sourcesEditor.setSurfaceManager(surfaceManager); } + // Set external media server so we can access it from wherever we need + void SurfaceManagerGui::setMediaServer(MediaServer* newMediaServer) { + mediaServer = newMediaServer; + // Set the media server of the sources editor here + sourcesEditor.setMediaServer(mediaServer); + } + void SurfaceManagerGui::setMode(int newGuiMode) { if (newGuiMode != GuiMode::NONE && newGuiMode != GuiMode::TEXTURE_MAPPING && newGuiMode != GuiMode::PROJECTION_MAPPING && @@ -191,8 +198,8 @@ void SurfaceManagerGui::setMode(int newGuiMode) { if (guiMode == GuiMode::SOURCE_SELECTION) { sourcesEditor.enable(); - string sourceName = surfaceManager->getSelectedSurfaceSourceName(); - sourcesEditor.selectImageSourceRadioButton(sourceName); + //string sourceName = surfaceManager->getSelectedSurfaceSourceName(); + //sourcesEditor.selectImageSourceRadioButton(sourceName); } else { sourcesEditor.disable(); } diff --git a/src/SurfaceManagerGui.h b/src/SurfaceManagerGui.h index 1c62cf6..7e998a8 100644 --- a/src/SurfaceManagerGui.h +++ b/src/SurfaceManagerGui.h @@ -27,6 +27,7 @@ class SurfaceManagerGui { void mouseReleased(ofMouseEventArgs& args); void mouseDragged(ofMouseEventArgs& args); void setSurfaceManager(SurfaceManager* newSurfaceManager); + void setMediaServer(MediaServer* newMediaServer); void setMode(int newGuiMode); void drawSelectedSurfaceHighlight(); void drawSelectedSurfaceTextureHighlight(); @@ -35,6 +36,7 @@ class SurfaceManagerGui { private: SurfaceManager* surfaceManager; + MediaServer* mediaServer; TextureEditor textureEditor; ProjectionEditor projectionEditor; SourcesEditor sourcesEditor; diff --git a/src/SurfaceType.h b/src/SurfaceType.h index 9db0cca..e00e875 100755 --- a/src/SurfaceType.h +++ b/src/SurfaceType.h @@ -1,9 +1,9 @@ #pragma once namespace ofx { -namespace piMapper { -struct SurfaceType { - enum { TRIANGLE_SURFACE, QUAD_SURFACE }; -}; -} + namespace piMapper { + struct SurfaceType { + enum { TRIANGLE_SURFACE, QUAD_SURFACE }; + }; + } } \ No newline at end of file diff --git a/src/BaseSurface.cpp b/src/Surfaces/BaseSurface.cpp similarity index 57% rename from src/BaseSurface.cpp rename to src/Surfaces/BaseSurface.cpp index b674cd8..d6a1ce5 100644 --- a/src/BaseSurface.cpp +++ b/src/Surfaces/BaseSurface.cpp @@ -7,6 +7,12 @@ BaseSurface::BaseSurface() { ofEnableNormalizedTexCoords(); createDefaultTexture(); } + + BaseSurface::~BaseSurface() { + delete defaultSource; + defaultSource = NULL; + defaultTexture.clear(); + } void BaseSurface::createDefaultTexture() { ofPixels pixels; @@ -38,33 +44,47 @@ void BaseSurface::createDefaultTexture() { // load pixels into texture defaultTexture.loadData(pixels); - - // Assign default texture to texture pointer - texture = &defaultTexture; + // Create new source to be the default + defaultSource = new BaseSource(&defaultTexture); + source = defaultSource; } void BaseSurface::drawTexture(ofVec2f position) { + if (source->getTexture() == NULL) { + ofLogWarning("BaseSurface") << "Source texture empty. Not drawing."; + return; + } + ofMesh texMesh; texMesh.addVertex(position); - texMesh.addVertex(position + ofVec2f(texture->getWidth(), 0.0f)); + texMesh.addVertex(position + ofVec2f(source->getTexture()->getWidth(), 0.0f)); texMesh.addVertex(position + - ofVec2f(texture->getWidth(), texture->getHeight())); - texMesh.addVertex(position + ofVec2f(0.0f, texture->getHeight())); + ofVec2f(source->getTexture()->getWidth(), source->getTexture()->getHeight())); + texMesh.addVertex(position + ofVec2f(0.0f, source->getTexture()->getHeight())); texMesh.addTriangle(0, 2, 3); texMesh.addTriangle(0, 1, 2); texMesh.addTexCoord(ofVec2f(0.0f, 0.0f)); texMesh.addTexCoord(ofVec2f(1.0f, 0.0f)); texMesh.addTexCoord(ofVec2f(1.0f, 1.0f)); texMesh.addTexCoord(ofVec2f(0.0f, 1.0f)); - texture->bind(); + source->getTexture()->bind(); texMesh.draw(); - texture->unbind(); + source->getTexture()->unbind(); } -void BaseSurface::setTexture(ofTexture* texturePtr) { texture = texturePtr; } + //void BaseSurface::setTexture(ofTexture* texturePtr) { texture = texturePtr; } + void BaseSurface::setSource(BaseSource* newSource) { + source = newSource; + } -ofTexture* BaseSurface::getTexture() { return texture; } + //ofTexture* BaseSurface::getTexture() { return texture; } + BaseSource* BaseSurface::getSource() { + return source; + } -ofTexture* BaseSurface::getDefaultTexture() { return &defaultTexture; } + //ofTexture* BaseSurface::getDefaultTexture() { return &defaultTexture; } + BaseSource* BaseSurface::getDefaultSource() { + return defaultSource; + } } } \ No newline at end of file diff --git a/src/BaseSurface.h b/src/Surfaces/BaseSurface.h similarity index 69% rename from src/BaseSurface.h rename to src/Surfaces/BaseSurface.h index bf6703f..28b8b52 100644 --- a/src/BaseSurface.h +++ b/src/Surfaces/BaseSurface.h @@ -2,6 +2,7 @@ #include "ofMain.h" #include +#include "BaseSource.h" using namespace std; @@ -10,6 +11,7 @@ namespace piMapper { class BaseSurface { public: BaseSurface(); + ~BaseSurface(); virtual void setup() {}; virtual void draw() {}; virtual void setVertex(int index, ofVec2f p) {}; @@ -24,16 +26,21 @@ class BaseSurface { // Draws a texture using ofMesh void drawTexture(ofVec2f position); - void setTexture(ofTexture* texturePtr); - - ofTexture* getTexture(); - ofTexture* getDefaultTexture(); - + //void setTexture(ofTexture* texturePtr); + void setSource(BaseSource* newSource); + + //ofTexture* getTexture(); + //ofTexture* getDefaultTexture(); + BaseSource* getSource(); + BaseSource* getDefaultSource(); + protected: ofMesh mesh; - ofTexture* texture; + //ofTexture* texture; ofTexture defaultTexture; - + BaseSource* source; + BaseSource* defaultSource; + void createDefaultTexture(); }; } diff --git a/src/QuadSurface.cpp b/src/Surfaces/QuadSurface.cpp similarity index 90% rename from src/QuadSurface.cpp rename to src/Surfaces/QuadSurface.cpp index c72587c..cffd145 100644 --- a/src/QuadSurface.cpp +++ b/src/Surfaces/QuadSurface.cpp @@ -22,14 +22,14 @@ void QuadSurface::setup() { ofVec2f t3 = ofVec2f(ofVec2f(1.0f, 1.0f)); ofVec2f t4 = ofVec2f(ofVec2f(0.0f, 1.0f)); - setup(p1, p2, p3, p4, t1, t2, t3, t4, texture); + setup(p1, p2, p3, p4, t1, t2, t3, t4, source); } void QuadSurface::setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofVec2f t4, - ofTexture* texturePtr) { + BaseSource* newSource) { // Assign texture - texture = texturePtr; + source = newSource; // Clear mesh mesh.clear(); @@ -68,6 +68,11 @@ void QuadSurface::setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, } void QuadSurface::draw() { + if (source->getTexture() == NULL) { + ofLogWarning("QuadSurface") << "Source texture empty. Not drawing."; + return; + } + /*if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ calculate4dTextureCoords(); }*/ @@ -75,9 +80,9 @@ void QuadSurface::draw() { glTexCoordPointer(4, GL_FLOAT, 0, quadTexCoordinates); glVertexPointer(3, GL_FLOAT, 0, quadVertices); - texture->bind(); + source->getTexture()->bind(); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, quadIndices); - texture->unbind(); + source->getTexture()->unbind(); } void QuadSurface::setVertex(int index, ofVec2f p) { @@ -154,7 +159,7 @@ ofPolyline QuadSurface::getHitArea() { ofPolyline QuadSurface::getTextureHitArea() { ofPolyline line; vector& texCoords = mesh.getTexCoords(); - ofVec2f textureSize = ofVec2f(texture->getWidth(), texture->getHeight()); + ofVec2f textureSize = ofVec2f(source->getTexture()->getWidth(), source->getTexture()->getHeight()); for (int i = 0; i < texCoords.size(); i++) { line.addVertex(ofPoint(texCoords[i] * textureSize)); } diff --git a/src/QuadSurface.h b/src/Surfaces/QuadSurface.h similarity index 87% rename from src/QuadSurface.h rename to src/Surfaces/QuadSurface.h index 223e367..6422a6f 100755 --- a/src/QuadSurface.h +++ b/src/Surfaces/QuadSurface.h @@ -14,7 +14,7 @@ class QuadSurface : public BaseSurface { void setup(); void setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, ofVec2f t1, - ofVec2f t2, ofVec2f t3, ofVec2f t4, ofTexture* texturePtr); + ofVec2f t2, ofVec2f t3, ofVec2f t4, BaseSource* newSource); void draw(); void setVertex(int index, ofVec2f p); diff --git a/src/TriangleSurface.cpp b/src/Surfaces/TriangleSurface.cpp similarity index 85% rename from src/TriangleSurface.cpp rename to src/Surfaces/TriangleSurface.cpp index b5d816e..f370080 100644 --- a/src/TriangleSurface.cpp +++ b/src/Surfaces/TriangleSurface.cpp @@ -3,13 +3,10 @@ namespace ofx { namespace piMapper { TriangleSurface::TriangleSurface() { - cout << "TriangleSurface constructor." << endl; setup(); } -TriangleSurface::~TriangleSurface() { - cout << "TriangleSurface destructor." << endl; -} +TriangleSurface::~TriangleSurface() {} void TriangleSurface::setup() { // Create 3 points for the triangle @@ -22,13 +19,13 @@ void TriangleSurface::setup() { ofVec2f t2 = ofVec2f(0, 1.0f); ofVec2f t3 = ofVec2f(1, 1.0f); - setup(p1, p2, p3, t1, t2, t3, texture); + setup(p1, p2, p3, t1, t2, t3, source); } void TriangleSurface::setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, - ofVec2f t2, ofVec2f t3, ofTexture* texturePtr) { + ofVec2f t2, ofVec2f t3, BaseSource* newSource) { // Assign texture - texture = texturePtr; + source = newSource; // Clear mesh mesh.clear(); @@ -45,9 +42,14 @@ void TriangleSurface::setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, } void TriangleSurface::draw() { - texture->bind(); + if (source->getTexture() == NULL) { + ofLogWarning("TriangleSurface") << "Source texture is empty. Not drawing."; + return; + } + + source->getTexture()->bind(); mesh.draw(); - texture->unbind(); + source->getTexture()->unbind(); } void TriangleSurface::setVertex(int index, ofVec2f p) { @@ -120,7 +122,7 @@ ofPolyline TriangleSurface::getHitArea() { ofPolyline TriangleSurface::getTextureHitArea() { ofPolyline line; vector& texCoords = mesh.getTexCoords(); - ofVec2f textureSize = ofVec2f(texture->getWidth(), texture->getHeight()); + ofVec2f textureSize = ofVec2f(source->getTexture()->getWidth(), source->getTexture()->getHeight()); for (int i = 0; i < texCoords.size(); i++) { line.addVertex(ofPoint(texCoords[i] * textureSize)); } diff --git a/src/TriangleSurface.h b/src/Surfaces/TriangleSurface.h similarity index 93% rename from src/TriangleSurface.h rename to src/Surfaces/TriangleSurface.h index c3e968c..073719b 100644 --- a/src/TriangleSurface.h +++ b/src/Surfaces/TriangleSurface.h @@ -13,7 +13,7 @@ class TriangleSurface : public BaseSurface { void setup(); void setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, - ofVec2f t3, ofTexture* texturePtr); + ofVec2f t3, BaseSource* newSource); void draw(); void setVertex(int index, ofVec2f p); void setTexCoord(int index, ofVec2f t); diff --git a/src/BaseJoint.cpp b/src/UserInterface/BaseJoint.cpp similarity index 100% rename from src/BaseJoint.cpp rename to src/UserInterface/BaseJoint.cpp diff --git a/src/BaseJoint.h b/src/UserInterface/BaseJoint.h similarity index 100% rename from src/BaseJoint.h rename to src/UserInterface/BaseJoint.h diff --git a/src/CircleJoint.cpp b/src/UserInterface/CircleJoint.cpp similarity index 100% rename from src/CircleJoint.cpp rename to src/UserInterface/CircleJoint.cpp diff --git a/src/CircleJoint.h b/src/UserInterface/CircleJoint.h similarity index 100% rename from src/CircleJoint.h rename to src/UserInterface/CircleJoint.h diff --git a/src/EditorType.h b/src/UserInterface/EditorType.h similarity index 100% rename from src/EditorType.h rename to src/UserInterface/EditorType.h diff --git a/src/GuiMode.h b/src/UserInterface/GuiMode.h similarity index 100% rename from src/GuiMode.h rename to src/UserInterface/GuiMode.h diff --git a/src/ProjectionEditor.cpp b/src/UserInterface/ProjectionEditor.cpp similarity index 100% rename from src/ProjectionEditor.cpp rename to src/UserInterface/ProjectionEditor.cpp diff --git a/src/ProjectionEditor.h b/src/UserInterface/ProjectionEditor.h similarity index 100% rename from src/ProjectionEditor.h rename to src/UserInterface/ProjectionEditor.h diff --git a/src/ui/RadioList.cpp b/src/UserInterface/RadioList.cpp similarity index 70% rename from src/ui/RadioList.cpp rename to src/UserInterface/RadioList.cpp index 812ed6d..3ccc71f 100644 --- a/src/ui/RadioList.cpp +++ b/src/UserInterface/RadioList.cpp @@ -7,21 +7,22 @@ RadioList::RadioList() { storedSelectedItem = 0; } -RadioList::RadioList(vector& labels) { +RadioList::RadioList(vector& labels, vector& values) { RadioList(); - setup(labels); + setup(labels, values); } -RadioList::RadioList(string title, vector& labels) { +RadioList::RadioList(string title, vector& labels, vector& values) { RadioList(); - setup(title, labels); + setup(title, labels, values); } RadioList::~RadioList() { clear(); } -void RadioList::setup(vector& labels) { +void RadioList::setup(vector& labels, vector& values) { // Copy incomming labels for later use storedLabels = labels; + storedValues = values; // Create toggles with labels from the labels arg int i; @@ -35,15 +36,13 @@ void RadioList::setup(vector& labels) { toggle->registerMouseEvents(); #endif } - - cout << "num items: " << guiGroup.getNumControls() << endl; } -void RadioList::setup(string title, vector& labels) { +void RadioList::setup(string title, vector& labels, vector& values) { // Store title for later use storedTitle = title; guiGroup.setName(title); - setup(labels); + setup(labels, values); } void RadioList::draw() { guiGroup.draw(); } @@ -68,11 +67,36 @@ void RadioList::selectItem(int index) { toggle->removeListener(this, &RadioList::onToggleClicked); *toggle = true; // Select the specific radio button toggle->addListener(this, &RadioList::onToggleClicked); - string name = toggle->getName(); - ofNotifyEvent(radioSelectedEvent, name, this); - + //string name = toggle->getName(); + // Throw event with value that is image path instead of name + string value = storedValues[index]; + ofNotifyEvent(onRadioSelected, value, this); storedSelectedItem = index; } + + bool RadioList::selectItemByValue(std::string itemValue) { + if (itemValue == "") { + ofLogNotice("RadioList") << "Item value empty"; + return false; + } + unselectAll(); + int itemIndex = -1; + for (int i = 0; i < storedValues.size(); i++) { + if (itemValue == storedValues[i]) { + itemIndex = i; + break; + } + } + if (itemIndex >= 0) { + ofxToggle* toggle = static_cast(guiGroup.getControl(itemIndex)); + toggle->removeListener(this, &RadioList::onToggleClicked); + *toggle = true; // Select the specific radio button + toggle->addListener(this, &RadioList::onToggleClicked); + return true; + } + ofLogNotice("RadioList") << "Item with value " << itemValue << " not found"; + return false; + } void RadioList::enable() { if (guiGroup.getNumControls() >= 0) { @@ -80,7 +104,7 @@ void RadioList::enable() { } // Rebuild everyting - setup(storedTitle, storedLabels); + setup(storedTitle, storedLabels, storedValues); // Select the stored selected item without throwing an event ofxToggle* toggle = @@ -136,7 +160,7 @@ string RadioList::getItemName(int index) { return toggle->getName(); } -int RadioList::size() { return guiGroup.getNumControls(); } +int RadioList::size() { return storedValues.size(); } void RadioList::onToggleClicked(bool& toggleValue) { diff --git a/src/ui/RadioList.h b/src/UserInterface/RadioList.h similarity index 72% rename from src/ui/RadioList.h rename to src/UserInterface/RadioList.h index 45b7327..c0e1daf 100644 --- a/src/ui/RadioList.h +++ b/src/UserInterface/RadioList.h @@ -10,17 +10,18 @@ namespace piMapper { class RadioList { public: RadioList(); - RadioList(vector &labels); - RadioList(string title, vector &labels); + RadioList(vector& labels, vector& values); + RadioList(string title, vector& labels, vector& values); ~RadioList(); - void setup(vector &labels); - void setup(string title, vector &labels); + void setup(vector &labels, vector& values); + void setup(string title, vector& labels, vector& values); void draw(); void setTitle(string title); void setPosition(ofPoint p); void setPosition(float x, float y); void selectItem(int index); + bool selectItemByValue(std::string itemValue); void enable(); void disable(); void clear(); @@ -37,10 +38,11 @@ class RadioList { // Use ofAddListener(RadioListInstance.radioSelectedEvent, listenerClassPtr, // &listenerClass::listenerMethod) // to listen to this. Listner method void listenerMethod(string & radioName) - ofEvent radioSelectedEvent; + ofEvent onRadioSelected; private: vector storedLabels; + vector storedValues; string storedTitle; ofxGuiGroup guiGroup; int storedSelectedItem; diff --git a/src/UserInterface/SourcesEditor.cpp b/src/UserInterface/SourcesEditor.cpp new file mode 100644 index 0000000..d106d67 --- /dev/null +++ b/src/UserInterface/SourcesEditor.cpp @@ -0,0 +1,271 @@ +#include "SourcesEditor.h" + +namespace ofx { +namespace piMapper { + SourcesEditor::SourcesEditor() { + init(); + // Create new MediaServer instance, + // we will need to clear this in the deconstr + mediaServer = new MediaServer(); + isMediaServerExternal = false; + addMediaServerListeners(); + } + + SourcesEditor::SourcesEditor(MediaServer* externalMediaServer) { + init(); + // Assign external MediaServer instance pointer + mediaServer = externalMediaServer; + isMediaServerExternal = true; + addMediaServerListeners(); + } + + SourcesEditor::~SourcesEditor() { + unregisterAppEvents(); + delete imageSelector; + delete videoSelector; + removeMediaServerListeners(); + clearMediaServer(); + } + + void SourcesEditor::registerAppEvents() { + ofAddListener(ofEvents().setup, this, &SourcesEditor::setup); + } + + void SourcesEditor::unregisterAppEvents() { + ofRemoveListener(ofEvents().setup, this, &SourcesEditor::setup); + } + + void SourcesEditor::setup(ofEventArgs& args) { + imageSelector = new RadioList(); + videoSelector = new RadioList(); + + // Get media count + int numImages = mediaServer->getNumImages(); + int numVideos = mediaServer->getNumVideos(); + + // Depending on media count, decide what to load and initialize + if (numImages) { + // Get image names from media server + vector imageNames = mediaServer->getImageNames(); + imageSelector->setup("Images", imageNames, mediaServer->getImagePaths()); + ofAddListener(imageSelector->onRadioSelected, this, &SourcesEditor::handleImageSelected); + } + if (numVideos) { + vector videoNames = mediaServer->getVideoNames(); + videoSelector->setup("Videos", videoNames, mediaServer->getVideoPaths()); + ofAddListener(videoSelector->onRadioSelected, this, &SourcesEditor::handleVideoSelected); + } + + if (numImages) { + imageSelector->setPosition(20, 20); + if (numVideos) { + videoSelector->setPosition(250, 20); + } + } else { + if (numVideos) { + videoSelector->setPosition(20, 20); + } + } + + } + + void SourcesEditor::draw() { + // Don't draw if there is no source selected + if (surfaceManager->getSelectedSurface() == NULL) { + ofLogNotice("SourcesEditor") << "No surface selected"; + return; + } + if (imageSelector->size()) { + imageSelector->draw(); + } + if (videoSelector->size()) { + videoSelector->draw(); + } + + } + + void SourcesEditor::disable() { + if (imageSelector->size()) { + imageSelector->disable(); + } + if (videoSelector->size()) { + videoSelector->disable(); + } + } + + void SourcesEditor::enable() { + // Don't enable if there is no surface selected + if (surfaceManager->getSelectedSurface() == NULL) { + ofLogNotice("SourcesEditor") << "No surface selected. Not enabling and not showing source list."; + return; + } + if (imageSelector->size()) { + imageSelector->enable(); + } + if (videoSelector->size()) { + videoSelector->enable(); + } + BaseSource* source = surfaceManager->getSelectedSurface()->getSource(); + selectSourceRadioButton(source->getPath()); + } + + void SourcesEditor::setSurfaceManager(SurfaceManager* newSurfaceManager) { + surfaceManager = newSurfaceManager; + } + + void SourcesEditor::setMediaServer(MediaServer* newMediaServer) { + // If the new media server is not valid + if (newMediaServer == NULL) { + // Log an error and return from the routine + ofLogFatalError("SourcesEditor") << "New media server is NULL"; + std::exit(EXIT_FAILURE); + } + // Attempt to clear existing media server and assign new one + clearMediaServer(); + //cout << "old ms addr: " << mediaServer << endl; + //cout << "new ms addr: " << newMediaServer << endl; + mediaServer = newMediaServer; + isMediaServerExternal = true; + } + + void SourcesEditor::selectSourceRadioButton(std::string& sourcePath) { + if (sourcePath == "") { + ofLogNotice("SourcesEditor") << "Path is empty"; + if (imageSelector->size()) { + imageSelector->unselectAll(); + } + if (videoSelector->size()) { + videoSelector->unselectAll(); + } + return; + } else { + // Check image selector first + bool imageRadioSelected = false; + bool videoRadioSelected = false; + if (imageSelector->size()) { + imageRadioSelected = imageSelector->selectItemByValue(sourcePath); + } + if (videoSelector->size()) { + videoRadioSelected = videoSelector->selectItemByValue(sourcePath); + } + if (imageRadioSelected || videoRadioSelected) { + return; + } + // Log warning if we are still here + ofLogWarning("SourcesEditor") << "Could not find option in any of the source lists"; + } + } + + void SourcesEditor::init() { + mediaServer = NULL; // Pointers to NULL pointer so we can check later + isMediaServerExternal = false; + registerAppEvents(); + } + + void SourcesEditor::addMediaServerListeners() { + // Check if the media server is valid + if (mediaServer == NULL) { + ofLogError("SourcesEditor::addMediaServerListeners", "Media server not set"); + return; + } + // Add listeners to custom events of the media server + ofAddListener(mediaServer->onImageAdded, this, &SourcesEditor::handleImageAdded); + ofAddListener(mediaServer->onImageRemoved, this, &SourcesEditor::handleImageRemoved); + ofAddListener(mediaServer->onVideoAdded, this, &SourcesEditor::handleVideoAdded); + ofAddListener(mediaServer->onVideoRemoved, this, &SourcesEditor::handleVideoRemoved); + ofAddListener(mediaServer->onImageLoaded, this, &SourcesEditor::handleImageLoaded); + ofAddListener(mediaServer->onImageUnloaded, this, &SourcesEditor::handleImageUnloaded); + + } + + void SourcesEditor::removeMediaServerListeners() { + // Check if the media server is valid + if (mediaServer == NULL) { + ofLogError("SourcesEditor::addMediaServerListeners", "Media server not set"); + return; + } + // Remove listeners to custom events of the media server + ofRemoveListener(mediaServer->onImageAdded, this, &SourcesEditor::handleImageAdded); + ofRemoveListener(mediaServer->onImageRemoved, this, &SourcesEditor::handleImageRemoved); + ofRemoveListener(mediaServer->onVideoAdded, this, &SourcesEditor::handleVideoAdded); + ofRemoveListener(mediaServer->onVideoRemoved, this, &SourcesEditor::handleVideoRemoved); + ofRemoveListener(mediaServer->onImageLoaded, this, &SourcesEditor::handleImageLoaded); + ofRemoveListener(mediaServer->onImageUnloaded, this, &SourcesEditor::handleImageUnloaded); + } + + void SourcesEditor::handleImageSelected(string& imagePath) { + // Unselect video item if any selected + videoSelector->unselectAll(); + BaseSurface* surface = surfaceManager->getSelectedSurface(); + if (surface == NULL) { + ofLogNotice("SourcesEditor") << "No surface selected"; + return; + } + // Unload old media + BaseSource* source = surface->getSource(); + if (source->isLoadable()) { + mediaServer->unloadMedia(source->getPath()); + } + // Load new image + surface->setSource(mediaServer->loadImage(imagePath)); + } + + void SourcesEditor::handleVideoSelected(string& videoPath) { + // Unselect image item if any selected + imageSelector->unselectAll(); + BaseSurface* surface = surfaceManager->getSelectedSurface(); + if (surface == NULL) { + ofLogNotice("SourcesEditor") << "No surface selected"; + return; + } + // Unload old media + BaseSource* source = surface->getSource(); + if (source->isLoadable()) { + mediaServer->unloadMedia(source->getPath()); + } + // Load new video + surface->setSource(mediaServer->loadVideo(videoPath)); + } + + void SourcesEditor::clearMediaServer() { + // If mediaServer is local, clear it + if (!isMediaServerExternal) { + // Clear all loaded sources + mediaServer->clear(); + // Destroy the pointer and set it to NULL pointer + delete mediaServer; + mediaServer = NULL; + } + } + + void SourcesEditor::handleImageAdded(string& path) { + cout << "image added: " << path << endl; + } + + void SourcesEditor::handleImageRemoved(string& path) { + cout << "image removed: " << path << endl; + } + + void SourcesEditor::handleVideoAdded(string& path) { + cout << "video added: " << path << endl; + } + + void SourcesEditor::handleVideoRemoved(string& path) { + cout << "video removed: " << path << endl; + } + + void SourcesEditor::handleImageLoaded(string& path) { + cout << "Image loaded: " << path << endl; + + // Test image unload + // mediaServer->unloadImage(path); + + //BaseSource* source = mediaServer->getSourceByPath(path); + //surfaceManager->getSelectedSurface()->setSource(source); + } + + void SourcesEditor::handleImageUnloaded(string& path) { + cout << "Image unloaded: " << path << endl; + } +} +} \ No newline at end of file diff --git a/src/UserInterface/SourcesEditor.h b/src/UserInterface/SourcesEditor.h new file mode 100644 index 0000000..e0edecc --- /dev/null +++ b/src/UserInterface/SourcesEditor.h @@ -0,0 +1,73 @@ +#pragma once + +#include "ofGraphics.h" +#include "ofEvents.h" +#include "SurfaceManager.h" +#include "RadioList.h" +#include "MediaServer.h" + +namespace ofx { +namespace piMapper { +class SourcesEditor { + public: + // Default contructor that initializes media server locally, + // thus requiring to delete the media server from memory on deconstr + SourcesEditor(); + + // Alternative constructor that allows to assign external media server + SourcesEditor(MediaServer* externalMediaServer); + ~SourcesEditor(); + + void registerAppEvents(); + void unregisterAppEvents(); + + void setup(ofEventArgs& args); + void draw(); + void loadImage(string name, string path); + void disable(); + void enable(); + void setSurfaceManager(SurfaceManager* newSurfaceManager); + + // Sets external MediaServer + void setMediaServer(MediaServer* newMediaServer); + //void selectImageSourceRadioButton(string name); + void selectSourceRadioButton(std::string& sourcePath); + + int getLoadedTexCount(); + ofTexture* getTexture(int index); + + private: + MediaServer* mediaServer; + SurfaceManager* surfaceManager; + RadioList* imageSelector; + RadioList* videoSelector; + + // Is the media server pointer local or from somewhere else? + // We use this to determine if we are allowed to clear media server locally. + bool isMediaServerExternal; + + // Init handles variable initialization in all constructors + void init(); + + // Methods for adding and removing listeners to the media server + void addMediaServerListeners(); + void removeMediaServerListeners(); + + // Handles GUI event, whenever someone has clicked on a radio button + void handleImageSelected(string& imagePath); + void handleVideoSelected(string& videoPath); + + // Careful clearing of the media server, + // clears only if the media server has been initialized locally + void clearMediaServer(); + + // MediaServer event handlers + void handleImageAdded(string& path); + void handleImageRemoved(string& path); + void handleVideoAdded(string& path); + void handleVideoRemoved(string& path); + void handleImageLoaded(string& path); + void handleImageUnloaded(string& path); +}; +} +} \ No newline at end of file diff --git a/src/TextureEditor.cpp b/src/UserInterface/TextureEditor.cpp similarity index 92% rename from src/TextureEditor.cpp rename to src/UserInterface/TextureEditor.cpp index f86d464..f7be105 100755 --- a/src/TextureEditor.cpp +++ b/src/UserInterface/TextureEditor.cpp @@ -45,8 +45,8 @@ void TextureEditor::update(ofEventArgs& args) { if (surface == NULL) return; // update surface if one of the joints is being dragged - ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), - surface->getTexture()->getHeight()); + ofVec2f textureSize = ofVec2f(surface->getSource()->getTexture()->getWidth(), + surface->getSource()->getTexture()->getHeight()); // Get selected joint index int selectedJointIndex = 0; @@ -138,8 +138,8 @@ void TextureEditor::createJoints() { if (surface == NULL) return; clearJoints(); vector& texCoords = surface->getTexCoords(); - ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), - surface->getTexture()->getHeight()); + ofVec2f textureSize = ofVec2f(surface->getSource()->getTexture()->getWidth(), + surface->getSource()->getTexture()->getHeight()); for (int i = 0; i < texCoords.size(); i++) { joints.push_back(new CircleJoint()); @@ -163,8 +163,8 @@ void TextureEditor::unselectAllJoints() { void TextureEditor::moveTexCoords(ofVec2f by) { if (surface == NULL) return; vector& texCoords = surface->getTexCoords(); - ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), - surface->getTexture()->getHeight()); + ofVec2f textureSize = ofVec2f(surface->getSource()->getTexture()->getWidth(), + surface->getSource()->getTexture()->getHeight()); for (int i = 0; i < texCoords.size(); i++) { joints[i]->position += by; texCoords[i] = joints[i]->position / textureSize; diff --git a/src/TextureEditor.h b/src/UserInterface/TextureEditor.h similarity index 100% rename from src/TextureEditor.h rename to src/UserInterface/TextureEditor.h diff --git a/src/ofxPiMapper.h b/src/ofxPiMapper.h index b0b5fea..975b9f6 100644 --- a/src/ofxPiMapper.h +++ b/src/ofxPiMapper.h @@ -1,4 +1,6 @@ #pragma once #include "SurfaceManager.h" -#include "SurfaceManagerGui.h" \ No newline at end of file +#include "SurfaceManagerGui.h" + +#include "MediaServer.h" \ No newline at end of file