Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

first commit and almost ready to go

  • Loading branch information...
commit d9cc5ceaa2535059c1a14eb43bc1811681edde4a 1 parent 9b464ff
@patriciogonzalezvivo authored
View
2  .gitignore
@@ -0,0 +1,2 @@
+
+.DS_Store
View
3  example/addons.make
@@ -0,0 +1,3 @@
+ofxGLEditor
+ofxComposer
+ofxXmlSettings
View
144 example/bin/data/config.xml
@@ -0,0 +1,144 @@
+<web>http://www.patriciogonzalezvivo.com</web>
+<general>
+ <fullscreen>0</fullscreen>
+ <width>640</width>
+ <height>480</height>
+</general>
+<surface>
+ <id>0</id>
+ <type>ofxGLEditor</type>
+ <path>none</path>
+ <visible>0</visible>
+ <texture>
+ <point>
+ <x>36.4827</x>
+ <y>547.362</y>
+ </point>
+ <point>
+ <x>713.935</x>
+ <y>548.331</y>
+ </point>
+ <point>
+ <x>714.652</x>
+ <y>40.2088</y>
+ </point>
+ <point>
+ <x>37.1894</x>
+ <y>39.2807</y>
+ </point>
+ </texture>
+ <mask>
+ <point>
+ <x>0</x>
+ <y>0</y>
+ </point>
+ <point>
+ <x>1</x>
+ <y>0</y>
+ </point>
+ <point>
+ <x>1</x>
+ <y>1</y>
+ </point>
+ <point>
+ <x>0</x>
+ <y>1</y>
+ </point>
+ </mask>
+ <out>
+ <active>1</active>
+ </out>
+</surface>
+<surface>
+ <id>1</id>
+ <type>ofVideoGrabber</type>
+ <path>0</path>
+ <visible>1</visible>
+ <texture>
+ <point>
+ <x>42.4797</x>
+ <y>587.36</y>
+ </point>
+ <point>
+ <x>228.504</x>
+ <y>587.36</y>
+ </point>
+ <point>
+ <x>228.504</x>
+ <y>726.878</y>
+ </point>
+ <point>
+ <x>42.4799</x>
+ <y>726.878</y>
+ </point>
+ </texture>
+ <mask>
+ <point>
+ <x>0</x>
+ <y>0</y>
+ </point>
+ <point>
+ <x>1</x>
+ <y>0</y>
+ </point>
+ <point>
+ <x>1</x>
+ <y>1</y>
+ </point>
+ <point>
+ <x>0</x>
+ <y>1</y>
+ </point>
+ </mask>
+ <out>
+ <active>1</active>
+ </out>
+</surface>
+<surface>
+ <id>2</id>
+ <type>ofShader</type>
+ <path>/Users/Patricio/Dropbox/Public/GPU-toolbox/pattern1.fs</path>
+ <visible>1</visible>
+ <frag>uniform float time;&#x0A;uniform vec2 resolution;&#x0A;uniform vec2 mouse;&#x0A;&#x0A;uniform sampler2DRect backbuffer; // Previus frame for PingPong&#x0A;&#x0A;uniform sampler2DRect tex0; // First dot on the left of the patch&#x0A;uniform sampler2DRect tex1; // Second dot on the left of the patch&#x0A;&#x0A;const float pi = 3.1415926;&#x0A;&#x0A;void main(void){&#x0A; vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;&#x0A; p.y *= resolution.y/resolution.x;&#x0A; p *= 30.0;&#x0A; const float tot = pi*2.0;&#x0A; const float n = 9.0;&#x0A; const float df = tot/n;&#x0A; float c = 0.0;&#x0A; float t = time*2.0;&#x0A;&#x0A; for (float phi =0.0; phi &lt; tot; phi+=df){&#x0A; c+=cos(cos(phi)*p.x+sin(phi)*p.y +t);&#x0A; }&#x0A; &#x0A; vec2 st = gl_FragCoord.xy;&#x0A;&#x0A; //vec4 A = texture2DRect(tex0, st);&#x0A; vec4 A = vec4(1.0,1.0,1.0,1.0);&#x0A; //vec4 B = texture2DRect(tex1, st);&#x0A; vec4 B = vec4(0.0,0.0,0.0,1.0);&#x0A;&#x0A; gl_FragColor = mix(A,B,c);&#x0A;}&#x0A;&#x0A;&#x0A;</frag>
+ <format>6408</format>
+ <passes>1</passes>
+ <texture>
+ <point>
+ <x>298.545</x>
+ <y>584.576</y>
+ </point>
+ <point>
+ <x>488.034</x>
+ <y>584.573</y>
+ </point>
+ <point>
+ <x>488.035</x>
+ <y>726.691</y>
+ </point>
+ <point>
+ <x>298.546</x>
+ <y>726.692</y>
+ </point>
+ </texture>
+ <mask>
+ <point>
+ <x>0</x>
+ <y>0</y>
+ </point>
+ <point>
+ <x>1</x>
+ <y>0</y>
+ </point>
+ <point>
+ <x>1</x>
+ <y>1</y>
+ </point>
+ <point>
+ <x>0</x>
+ <y>1</y>
+ </point>
+ </mask>
+ <out>
+ <active>1</active>
+ </out>
+</surface>
View
BIN  example/bin/data/menlo.ttf
Binary file not shown
View
15 example/src/main.cpp
@@ -0,0 +1,15 @@
+#include "ofMain.h"
+#include "testApp.h"
+#include "ofAppGlutWindow.h"
+
+//========================================================================
+int main( ){
+
+ ofAppGlutWindow window;
+ ofSetupOpenGL(&window, 1024, 768, OF_WINDOW); // <-------- setup the GL context
+
+ // this kicks off the running of my app
+ // can be OF_WINDOW or OF_FULLSCREEN
+ // pass in width and height too:
+ ofRunApp( new testApp());
+}
View
63 example/src/testApp.cpp
@@ -0,0 +1,63 @@
+#include "testApp.h"
+
+//-------------------------------------------------------------- SETING
+void testApp::setup(){
+ ofEnableAlphaBlending();
+ ofEnableSmoothing();
+ ofSetVerticalSync(false);
+
+ composer.load("config.xml");
+}
+
+//-------------------------------------------------------------- LOOP
+void testApp::update(){
+ composer.update();
+
+ ofSetWindowTitle( ofToString( ofGetFrameRate()));
+}
+
+
+void testApp::draw(){
+ ofBackgroundGradient(ofColor::gray, ofColor::black);
+ ofSetColor(255,255);
+
+ composer.draw();
+}
+
+
+//-------------------------------------------------------------- EVENTS
+void testApp::keyPressed(int key){
+}
+
+void testApp::keyReleased(int key){
+}
+
+void testApp::mouseMoved(int x, int y ){
+}
+
+void testApp::mouseDragged(int x, int y, int button){
+}
+
+void testApp::mousePressed(int x, int y, int button){
+
+}
+
+void testApp::mouseReleased(int x, int y, int button){
+
+}
+
+void testApp::windowResized(int w, int h){
+
+}
+
+void testApp::gotMessage(ofMessage msg){
+}
+
+
+void testApp::dragEvent(ofDragInfo dragInfo){
+ if( dragInfo.files.size() > 0 ){
+ for(int i = 0; i < dragInfo.files.size(); i++){
+ composer.addPatch( dragInfo.files[i], dragInfo.position );
+ }
+ }
+}
View
24 example/src/testApp.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "ofMain.h"
+
+#include "ofxComposer.h"
+
+class testApp : public ofBaseApp{
+public:
+ void setup();
+ void update();
+ void draw();
+
+ void keyPressed (int key);
+ void keyReleased(int key);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased(int x, int y, int button);
+ void windowResized(int w, int h);
+ void dragEvent(ofDragInfo dragInfo);
+ void gotMessage(ofMessage msg);
+
+ ofxComposer composer;
+};
View
368 src/ofxComposer.cpp
@@ -0,0 +1,368 @@
+//
+// ofxComposer.cpp
+// GPUBurner
+//
+// Created by Patricio Gonzalez Vivo on 3/9/12.
+// Copyright (c) 2012 http://www.PatricioGonzalezVivo.com All rights reserved.
+//
+
+#include "ofxComposer.h"
+
+ofxComposer::ofxComposer(){
+ ofAddListener(ofEvents().mouseMoved, this, &ofxComposer::_mouseMoved);
+ ofAddListener(ofEvents().mousePressed, this, &ofxComposer::_mousePressed);
+ ofAddListener(ofEvents().mouseReleased, this, &ofxComposer::_mouseReleased);
+ ofAddListener(ofEvents().keyPressed, this, &ofxComposer::_keyPressed);
+ ofAddListener(ofEvents().windowResized, this, &ofxComposer::_windowResized);
+
+ configFile = "config.xml";
+ selected = -1;
+ selectedDot = -1;
+ bGLEditor = false;
+}
+
+void ofxComposer::load(string _fileConfig){
+ if (_fileConfig != "default")
+ configFile = _fileConfig;
+
+ ofxXmlSettings XML;
+
+ patches.clear();
+ if (XML.loadFile(_fileConfig)){
+ int totalPatchs = XML.getNumTags("surface");
+
+ // Load each surface present on the xml file
+ //
+ for(int i = 0; i < totalPatchs ; i++){
+ ofxPatch *nPatch = new ofxPatch();
+ bool loaded = nPatch->loadSettings(i, "config.xml");
+
+ if (loaded){
+ if (nPatch->getType() == "ofxGLEditor"){
+ ofLog(OF_LOG_NOTICE,"ofxComposer: ofxGLEditor loaded");
+ editor.setup("menlo.ttf");
+ editor.setCurrentEditor(1);
+ editorFbo.allocate(ofGetWindowWidth(), ofGetWindowHeight());
+ editorFbo.begin();
+ ofClear(0, 150);
+ editorFbo.end();
+
+ nPatch->setTexture( editorFbo.getTextureReference(), 0);
+
+ bGLEditor = true;
+ }
+
+ if (nPatch->getType() == "input"){
+ nPatch->setTexture( editorFbo.getTextureReference(), 0);
+ }
+
+ // Insert the new patch into the verctor
+ //
+ patches.push_back(nPatch);
+
+ // Listen to close bottom on the titleBar
+ //
+ ofAddListener( patches[ patches.size()-1 ]->title->close , this, &ofxComposer::closePatch);
+ }
+ }
+
+
+ // Load links between Patchs
+ //
+ for(int i = 0; i < totalPatchs ; i++){
+ if (XML.pushTag("surface", i)){
+ if (XML.pushTag("out")){
+
+ int totalLinks = XML.getNumTags("dot");
+ for(int j = 0; j < totalLinks ; j++){
+
+ if (XML.pushTag("dot",j)){
+ int toId = XML.getValue("to", 0);
+ int toTex = XML.getValue("tex", 0);
+
+ // If everything goes ok "i" will match the position of the vector
+ // with the position on the XML, in the same place of the vector array
+ // defined on the previus loop
+ //
+ connect( i , fromIDtoArrayPos( toId ), toTex);
+
+ XML.popTag();
+ }
+ }
+ XML.popTag();
+ }
+ XML.popTag();
+ }
+ }
+ }
+}
+
+void ofxComposer::setTexture(ofTexture &_texture, int _nId ){
+ int n = fromIDtoArrayPos(_nId);
+ if (n != -1)
+ patches[n]->setTexture( _texture , 0);
+}
+
+ofTexture& ofxComposer::getTexture(int _nId){
+ int n = fromIDtoArrayPos(_nId);
+
+ if ( n != -1){
+ if ((n < patches.size()) && (n >= 0) )
+ return patches[n]->getTextureReference();
+ }
+};
+
+int ofxComposer::fromIDtoArrayPos(int _nId){
+ int found = -1;
+
+ for (int i = 0; i < patches.size(); i++){
+ if (patches[i]->getId() == _nId){
+ found = i;
+ }
+ }
+
+ return found;
+}
+
+bool ofxComposer::addPatch(string _filePath, ofPoint _position){
+ bool loaded = false;
+
+ ofxPatch *nPatch = new ofxPatch();
+ loaded = nPatch->loadFile( _filePath, "config.xml" );
+
+ if ( loaded ){
+ //nPatch->saveSettings();
+ nPatch->move( _position );
+ nPatch->scale(0.5);
+
+ patches.push_back(nPatch);
+
+ ofAddListener( patches[ patches.size()-1 ]->title->close , this, &ofxComposer::closePatch);
+ }
+
+ return loaded;
+}
+
+bool ofxComposer::connect( int fromPatchN, int toPatchN, int inDotN ){
+ bool connected = false;
+
+ if (patches[ toPatchN ]->getType() == "ofShader") {
+ LinkDot newDot;
+ newDot.pos = patches[ fromPatchN ]->getOutPutPosition();
+ newDot.toId = patches[ toPatchN ]->getId();
+ newDot.to = &(patches[ toPatchN ]->inPut[ inDotN ]);
+ newDot.toShader = patches[ toPatchN ]->getShader();
+ newDot.nTex = inDotN;
+
+ patches[ fromPatchN ]->outPut.push_back( newDot );
+ connected = true;
+ }
+
+ return connected;
+}
+
+void ofxComposer::closePatch( int &_nId ){
+ int n = fromIDtoArrayPos(_nId);
+
+ bool deleted = false;
+
+ if ( (n >= 0) && ( n < patches.size() ) ){
+
+ int targetId = patches[n]->getId();
+ int targetTag = 0;
+
+ // Delete object from the vector
+ //
+ patches.erase(patches.begin()+ n);
+
+ // Delete XML Data
+ //
+ ofxXmlSettings XML;
+ if ( XML.loadFile( configFile ) ){
+ int totalSurfaces = XML.getNumTags("surface");
+ for (int i = 0; i < totalSurfaces; i++){
+ if (XML.pushTag("surface", i)){
+ if ( XML.getValue("id", -1) == targetId){
+ targetTag = i;
+ }
+ XML.popTag();
+ }
+ }
+
+ XML.removeTag("surface", targetTag);
+ XML.saveFile();
+ }
+
+ // Delete links Dependences
+ //
+ for (int i = 0; i < patches.size(); i++){
+ for (int j = patches[i]->outPut.size()-1; j >= 0 ; j--){
+ if ( patches[i]->outPut[j].toId == targetId ){
+ patches[i]->outPut.erase(patches[i]->outPut.begin() + j );
+ patches[i]->saveSettings();
+ }
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------- LOOP
+void ofxComposer::update(){
+ for(int i = 0; i < patches.size(); i++){
+ patches[i]->update();
+ }
+}
+
+
+void ofxComposer::draw(){
+ ofPushView();
+ ofPushStyle();
+ ofPushMatrix();
+
+ ofEnableAlphaBlending();
+ for(int i = 0; i < patches.size(); i++){
+ patches[i]->draw();
+ }
+
+ if ( bGLEditor && (selected >= 0)){
+ if (patches[selected]->getType() == "ofShader"){
+ editorFbo.begin();
+ ofClear(0, 150);
+ ofRotate(180, 0, 1, 0);
+ editor.draw();
+ editorFbo.end();
+ } else {
+ editorFbo.begin();
+ ofClear(0, 50);
+ editorFbo.end();
+ }
+ }
+
+ if (selectedDot >= 0){
+ ofLine(patches[selectedDot]->getOutPutPosition(), ofPoint(ofGetMouseX(),ofGetMouseY()));
+ }
+
+ ofDisableBlendMode();
+ ofEnableAlphaBlending();
+
+ ofPopMatrix();
+ ofPopStyle();
+ ofPopView();
+}
+
+//-------------------------------------------------------------- EVENTS
+void ofxComposer::_keyPressed(ofKeyEventArgs &e){
+ if (bGLEditor)
+ editor.keyPressed(e.key);
+
+ if (selected >= 0){
+ if (patches[selected]->getType() == "ofShader"){
+ patches[selected]->setFrag(editor.getText(1));
+ patches[selected]->saveSettings();
+ }
+ }
+
+ switch (e.key) {
+ case OF_KEY_F1:
+ ofToggleFullscreen();
+ break;
+ }
+}
+
+void ofxComposer::_mouseMoved(ofMouseEventArgs &e){
+ ofVec2f mouse = ofVec2f(e.x, e.y);
+
+ for(int i = patches.size() -1 ; i >= 0; i--){
+ if (patches[i]->isOver(mouse)){
+ focusOnPatch(i);
+ break;
+ }
+ }
+}
+
+void ofxComposer::focusOnPatch( int _arrayPos ){
+ selected = _arrayPos;
+
+ for(int i = 0; i < patches.size(); i++){
+ if (i == selected){
+ patches[i]->bActive = true;
+ } else {
+ patches[i]->bActive = false;
+ }
+ }
+}
+
+void ofxComposer::_mousePressed(ofMouseEventArgs &e){
+ ofVec2f mouse = ofVec2f(e.x, e.y);
+
+ selectedDot = -1;
+ for(int i = 0; i < patches.size(); i++){
+ if ( (patches[i]->getOutPutPosition().distance(mouse) < 5) && (patches[i]->bEditMode) && !(patches[i]->bEditMask) ){
+ selectedDot = i;
+ patches[i]->bActive = false;
+ selected = -1;
+ }
+ }
+
+ if (selectedDot == -1){
+ for(int i = 0; i < patches.size(); i++){
+ if ((patches[i]->bActive) && (patches[i]->bEditMode) && !(patches[i]->bEditMask)){
+ selected = i;
+
+ if (bGLEditor && (patches[selected]->getType() == "ofShader")){
+ editor.setText(patches[i]->getFrag(), 1);
+ }
+ }
+ }
+ }
+}
+
+void ofxComposer::_mouseReleased(ofMouseEventArgs &e){
+ ofVec2f mouse = ofVec2f(e.x, e.y);
+
+ if (selectedDot != -1){
+ for(int i = 0; i < patches.size(); i++){
+
+ if ((selectedDot != i) && // If not him self
+ (patches[i]->getType() == "ofShader") && // The target it´s a shader
+ (patches[i]->bEditMode) && // And we are in editMode and not on maskMode
+ !(patches[i]->bEditMask) ){
+
+ for (int j = 0; j < patches[i]->inPut.size(); j++){
+
+ // And after checking in each dot of each shader...
+ // ... fin the one where the mouse it´s over
+ //
+ if ( patches[i]->inPut[j].pos.distance(mouse) < 5){
+
+ // Once he founds it
+ // make the link and forget the selection
+ //
+ connect( selectedDot , i, j );
+ patches[selectedDot]->saveSettings();
+ selectedDot = -1;
+ }
+ }
+ }
+ }
+
+ // If he release the mouse over nothing it will clear all
+ // the connections of that dot.
+ //
+ if (selectedDot != -1){
+ patches[selectedDot]->outPut.clear();
+ patches[selectedDot]->saveSettings();
+ selectedDot = -1;
+ }
+ }
+}
+
+void ofxComposer::_windowResized(ofResizeEventArgs &e){
+ if (bGLEditor ){
+ editor.reShape();
+ editorFbo.allocate(e.width, e.height);
+ editorFbo.begin();
+ ofClear(0, 150);
+ editorFbo.end();
+ }
+}
View
58 src/ofxComposer.h
@@ -0,0 +1,58 @@
+//
+// ofxComposer.h
+// GPUBurner
+//
+// Created by Patricio Gonzalez Vivo on 3/9/12.
+// Copyright (c) 2012 http://www.PatricioGonzalezVivo.com All rights reserved.
+//
+
+#ifndef OFXCOMPOSER
+#define OFXCOMPOSER
+
+#include "ofMain.h"
+#include "ofxPatch.h"
+
+#include "ofxGLEditor.h"
+
+class ofxComposer {
+public:
+ ofxComposer();
+
+ ofTexture& operator[](int _nId);
+
+ void load(string _fileConfig = "default");
+ bool addPatch(string _filePath, ofPoint _position);
+
+ void setTexture(ofTexture &_texture, int _nId);
+ ofTexture& getTexture(int _nId);
+
+ void update();
+ void draw();
+
+private:
+ // Events
+ void _mouseMoved(ofMouseEventArgs &e);
+ void _keyPressed(ofKeyEventArgs &e);
+ void _mousePressed(ofMouseEventArgs &e);
+ void _mouseReleased(ofMouseEventArgs &e);
+ void _windowResized(ofResizeEventArgs &e);
+
+ void focusOnPatch( int _arrayPos );
+ void closePatch( int &_nId );
+ bool connect( int _fromPatchN, int _toPatchN, int _inDotN );
+ int fromIDtoArrayPos( int _nId );
+
+ ofxGLEditor editor;
+ ofFbo editorFbo;
+
+ vector<ofxPatch*> patches;
+ string configFile;
+
+ int selectedDot;
+ int selected;
+
+ bool bGLEditor;
+};
+
+
+#endif
View
1,372 src/ofxPatch.cpp
@@ -0,0 +1,1372 @@
+//
+// ofxPatch.cpp
+// emptyExample
+//
+// Created by Patricio Gonzalez Vivo on 3/9/12.
+// Copyright (c) 2012 http://www.PatricioGonzalezVivo.com All rights reserved.
+//
+
+#include "ofxPatch.h"
+
+ofxPatch::ofxPatch(){
+ nId = -1;
+ type = "none";
+ filePath = "none";
+ configFile = "config.xml";
+
+ selectedMaskCorner = -1;
+ selectedTextureCorner = -1;
+
+ bEditMode = true;
+ bEditMask = false;
+ bActive = false;
+ bVisible = true;
+
+ bMasking = false;
+
+ bUpdateMask = true;
+ bUpdateCoord= true;
+
+ image = NULL;
+ shader = NULL;
+ videoPlayer = NULL;
+ videoGrabber= NULL;
+ texture = NULL;
+
+ width = 640;
+ height = 480;
+
+ string shaderProgram = "#version 120\n\
+#extension GL_ARB_texture_rectangle : enable\n\
+\n\
+uniform sampler2DRect tex0;\n\
+uniform sampler2DRect maskTex;\n\
+uniform float texOpacity;\n\
+uniform float maskOpacity;\n\
+\n\
+void main (void){\n\
+ vec2 pos = gl_TexCoord[0].st;\n\
+ \n\
+ vec4 src = texture2DRect(tex0, pos);\n\
+ float mask = texture2DRect(maskTex, pos).r;\n\
+ \n\
+ gl_FragColor = vec4( src.rgb * texOpacity , clamp( min(src.a,mask) , maskOpacity, 1.0));\n\
+}\n";
+ maskShader.setupShaderFromSource(GL_FRAGMENT_SHADER, shaderProgram);
+ maskShader.linkProgram();
+ maskFbo.allocate(width, height);
+
+ maskCorners.addVertex(0.0,0.0);
+ maskCorners.addVertex(1.0,0.0);
+ maskCorners.addVertex(1.0,1.0);
+ maskCorners.addVertex(0.0,1.0);
+
+ textureCorners.addVertex(0.0,0.0);
+ textureCorners.addVertex(width,0.0);
+ textureCorners.addVertex(width,height);
+ textureCorners.addVertex(0.0,height);
+
+ title = new ofxTitleBar(&box, &nId);
+ title->addButton('m', &bEditMask, TOGGLE_BUTTON);
+ title->addButton('v', &bVisible, TOGGLE_BUTTON);
+ ofAddListener( title->reset , this, &ofxPatch::_reMakeFrame);
+
+ ofAddListener(ofEvents().mousePressed, this, &ofxPatch::_mousePressed);
+ ofAddListener(ofEvents().mouseDragged, this, &ofxPatch::_mouseDragged);
+ ofAddListener(ofEvents().mouseReleased, this, &ofxPatch::_mouseReleased);
+ ofAddListener(ofEvents().keyPressed, this, &ofxPatch::_keyPressed);
+};
+
+ofxPatch::~ofxPatch(){
+ if ( image != NULL )
+ delete image;
+
+ if ( shader != NULL )
+ delete shader;
+
+ if ( videoPlayer != NULL )
+ delete videoPlayer;
+
+ if ( videoGrabber != NULL )
+ delete videoGrabber;
+
+ if ( texture != NULL )
+ delete texture;
+
+ inPut.clear();
+ outPut.clear();
+ textureCorners.clear();
+ maskCorners.clear();
+}
+
+//------------------------------------------------------------------- SET's & GET's
+void ofxPatch::setFrag( string _code){
+ if ( shader != NULL ){
+ if (shader->setFragmentShader( _code )){
+ saveSettings();
+ // Calculate the need dots.
+ //
+ if (shader != NULL){
+ if (shader->getNumberOfTextures() > inPut.size()){
+ LinkDot p;
+ bUpdateCoord = true;
+ inPut.push_back(p);
+ } else if (shader->getNumberOfTextures() < inPut.size()) {
+ inPut.erase( inPut.end() );
+ bUpdateCoord = true;
+ }
+ }
+ }
+ }
+}
+
+void ofxPatch::setTexture(ofTexture& tex, int _texNum){
+ if ( shader != NULL ){
+ shader->setTexture(tex, _texNum);
+ } else if ( (type == "ofxGLEditor") || (type == "input") ){
+ texture = &tex;
+ }
+}
+
+string ofxPatch::getFrag(){
+ if ( shader != NULL ){
+ return shader->getFragmentShader();
+ }
+}
+
+ofTexture& ofxPatch::getSrcTexture(){
+ if (image != NULL)
+ return image->getTextureReference();
+ else if (videoPlayer != NULL)
+ return videoPlayer->getTextureReference();
+ else if (videoGrabber != NULL)
+ return videoGrabber->getTextureReference();
+ else if (texture != NULL)
+ return *texture;
+ else if (shader != NULL)
+ return shader->getTextureReference();
+ else if (texture != NULL)
+ return *texture;
+}
+
+ofTexture& ofxPatch::getTextureReference(){
+ if (bMasking)
+ return maskFbo.dst->getTextureReference();
+ else
+ return getSrcTexture();
+}
+
+//---------------------------------------------------------------------- LOOPS
+void ofxPatch::update(){
+ if ((width != getSrcTexture().getWidth()) ||
+ (height != getSrcTexture().getHeight()) ){
+ width = getSrcTexture().getWidth();
+ height = getSrcTexture().getHeight();
+
+ bUpdateCoord = true;
+ }
+
+ if (bUpdateMask){
+ // If the texture change or it's new it will update some parameters
+ // like the size of the FBO, de mask and the matrix
+ //
+ if ((maskFbo.src->getWidth() != getSrcTexture().getWidth()) ||
+ (maskFbo.src->getHeight() != getSrcTexture().getHeight()) ){
+ width = getSrcTexture().getWidth();
+ height = getSrcTexture().getHeight();
+
+ maskFbo.allocate(width,height);
+ }
+
+ // Generate masking contour
+ //
+ maskFbo.src->begin();
+ ofClear(0,255);
+ ofBeginShape();
+ ofSetColor(255, 255, 255);
+ for(int i = 0; i < maskCorners.size(); i++ ){
+ ofVertex(maskCorners[i].x*width,maskCorners[i].y*height);
+ }
+ ofEndShape(true);
+ maskFbo.src->end();
+
+ bUpdateMask = false;
+ }
+
+ if (bMasking){
+ float texOpacity = 0.0;
+ float maskOpacity = 0.0;
+
+ if ( textureCorners.inside(ofGetMouseX(), ofGetMouseY()) && (bEditMode)){
+ texOpacity = 1.0;
+ maskOpacity = 0.5;
+ } else if (!bEditMode){
+ texOpacity = 1.0;
+ maskOpacity = 0.0;
+ } else {
+ texOpacity = 0.8;
+ maskOpacity = 0.0;
+ }
+
+ maskFbo.dst->begin();
+ ofClear(0, 0);
+ maskShader.begin();
+ maskShader.setUniformTexture("maskTex", maskFbo.src->getTextureReference(), 1 );
+ maskShader.setUniform1f("texOpacity", texOpacity);
+ maskShader.setUniform1f("maskOpacity", maskOpacity);
+
+ getSrcTexture().draw(0,0);
+
+ maskShader.end();
+ maskFbo.dst->end();
+ }
+
+ if (bUpdateCoord){
+ doSurfaceToScreenMatrix();
+
+ box = textureCorners.getBoundingBox();
+ outPutPos.set( box.x + box.width + 4, box.y + box.height*0.5 );
+ for(int i = 0; i < outPut.size(); i++){
+ outPut[i].pos = outPutPos;
+ }
+ int total = inPut.size();
+ for(int i = 0; i < total; i++){
+ inPut[i].pos.set( box.x - 4, box.y + (box.height/(total))* (i+0.5) );
+ }
+
+ bUpdateCoord = false;
+ }
+
+ // Update shader or video content
+ //
+ if (videoPlayer != NULL){
+ videoPlayer->update();
+ } else if (videoGrabber != NULL){
+ videoGrabber->update();
+ } else if (shader != NULL){
+ shader->update();
+ }
+
+ // Send the texture to the linked Patches
+ //
+ for ( int i = 0; i < outPut.size(); i++ ){
+ if (outPut[i].toShader != NULL){
+ outPut[i].toShader->setTexture( getTextureReference(), outPut[i].nTex );
+ }
+ }
+}
+
+void ofxPatch::draw(){
+
+ if ( bEditMode || bVisible ) {
+
+ if (bActive || !bEditMode)
+ ofSetColor(255, 255);
+ else
+ ofSetColor(200, 255);
+
+ ofPushMatrix();
+ glMultMatrixf(glMatrix);
+ getTextureReference().draw(0,0);
+ ofPopMatrix();
+ }
+
+ if (bEditMode){
+ ofPushStyle();
+
+ if (title != NULL)
+ title->draw();
+
+ if ( !bEditMask ){
+ ofFill();
+ // Draw dragables texture corners
+ //
+ for(int i = 0; i < 4; i++){
+ if ( ( selectedTextureCorner == i) || ( ofDist(ofGetMouseX(), ofGetMouseY(), textureCorners[i].x, textureCorners[i].y) <= 4 ) ) ofSetColor(200,255);
+ else ofSetColor(200,100);
+
+ ofRect(textureCorners[i].x-4,textureCorners[i].y-4, 8,8);
+
+ // Draw contour Line
+ //
+ ofLine(textureCorners[i].x, textureCorners[i].y, textureCorners[(i+1)%4].x, textureCorners[(i+1)%4].y);
+ }
+ } else {
+ // Draw dragables mask corners
+ //
+ for(int i = 0; i < maskCorners.size(); i++){
+ ofVec3f pos = ofVec3f( maskCorners[i].x * width, maskCorners[i].y * height, 0.0);
+ pos = surfaceToScreenMatrix * pos;
+
+ if ( (selectedMaskCorner == i) || ( ofDist(ofGetMouseX(), ofGetMouseY(), pos.x, pos.y) <= 4 ) ) {
+ ofSetColor(255,255);
+ ofCircle( pos, 4);
+ ofSetColor(255,100);
+ ofFill();
+ } else {
+ ofNoFill();
+ ofSetColor(255,100);
+ }
+
+ ofCircle( pos, 4);
+
+ // Draw contour mask line
+ //
+ ofSetColor(255,200);
+ ofVec3f nextPos = ofVec3f(maskCorners[(i+1)%maskCorners.size()].x*width,
+ maskCorners[(i+1)%maskCorners.size()].y*height, 0.0);
+ nextPos = surfaceToScreenMatrix * nextPos;
+ ofLine(pos.x,pos.y,nextPos.x,nextPos.y);
+ }
+ }
+
+ if (type == "ofShader"){
+ if (shader != NULL){
+ if ( !(shader->isOk()) ){
+ ofSetColor(0, 100);
+ ofFill();
+ ofRect(box);
+
+ ofSetColor(255, 0, 0);
+ ofDrawBitmapString("Error", box.x + 5, box.y + 15);
+ ofNoFill();
+ ofRect(box);
+ }
+
+ // Draw the linking dots
+ //
+ for(int i = 0; i < inPut.size(); i++){
+ ofSetColor(255, 150);
+ ofNoFill();
+ ofCircle(inPut[i].pos, 5);
+ }
+ }
+ }
+
+ // Draw the output linking dot
+ //
+ ofNoFill();
+ ofSetColor(255, 150);
+ ofCircle(getOutPutPosition(), 5);
+ ofPopStyle();
+
+ // Draw the links
+ //
+ for (int i = 0; i < outPut.size(); i++){
+ if (outPut[i].to != NULL){
+ ofSetColor(150);
+ ofFill();
+ ofCircle(outPut[i].pos, 3);
+ ofLine(outPut[i].pos, outPut[i].to->pos);
+ ofCircle(outPut[i].to->pos, 3);
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------- PUBLIC ACTIONS
+//
+void ofxPatch::move(ofPoint _pos){
+ ofVec2f diff = _pos - getPos();
+
+ for(int i = 0; i < 4; i++){
+ textureCorners[i] += diff;
+ }
+
+ bUpdateCoord = true;
+}
+
+void ofxPatch::scale(float _scale){
+ for(int i = 0; i < 4; i++){
+ ofVec2f center = getPos();
+ ofVec2f fromCenterToCorner = textureCorners[i] - center;
+
+ float radio = fromCenterToCorner.length();
+ float angle = -1.0*atan2f(fromCenterToCorner.x,fromCenterToCorner.y)+(PI/2);
+
+ radio *= _scale;
+
+ textureCorners[i] = center + ofPoint(radio * cos(angle),
+ radio * sin(angle),
+ 0.0);
+ }
+
+ bUpdateCoord = true;
+}
+
+void ofxPatch::rotate(float _rotAngle){
+ for(int i = 0; i < 4; i++){
+ ofVec2f center = getPos();
+ ofVec2f fromCenterToCorner = textureCorners[i] - center;
+
+ float radio = fromCenterToCorner.length();
+ float angle = -1.0*atan2f(fromCenterToCorner.x,fromCenterToCorner.y)+(PI/2);
+
+ angle += _rotAngle;
+
+ textureCorners[i] = center + ofPoint(radio * cos(angle),
+ radio * sin(angle),
+ 0.0);
+ }
+
+ bUpdateCoord = true;
+}
+
+
+// ----------------------------------------------------------------PRIVATE ACTIONS
+//
+
+void ofxPatch::doSurfaceToScreenMatrix(){
+ ofPoint src[4];
+
+ src[0].set(0, 0);
+ src[1].set(width,0.0);
+ src[2].set(width,height);
+ src[3].set(0, height);
+
+ ofPoint dst[4];
+ for(int i = 0; i < 4; i++){
+ dst[i] = textureCorners[i];
+ }
+
+ x = textureCorners.getCentroid2D().x;
+ y = textureCorners.getCentroid2D().y;
+
+ // create the equation system to be solved
+ //
+ // from: Multiple View Geometry in Computer Vision 2ed
+ // Hartley R. and Zisserman A.
+ //
+ // x' = xH
+ // where H is the homography: a 3 by 3 matrix
+ // that transformed to inhomogeneous coordinates for each point
+ // gives the following equations for each point:
+ //
+ // x' * (h31*x + h32*y + h33) = h11*x + h12*y + h13
+ // y' * (h31*x + h32*y + h33) = h21*x + h22*y + h23
+ //
+ // as the homography is scale independent we can let h33 be 1 (indeed any of the terms)
+ // so for 4 points we have 8 equations for 8 terms to solve: h11 - h32
+ // after ordering the terms it gives the following matrix
+ // that can be solved with gaussian elimination:
+
+ float P[8][9]=
+ {
+ {-src[0].x, -src[0].y, -1, 0, 0, 0, src[0].x*dst[0].x, src[0].y*dst[0].x, -dst[0].x }, // h11
+ { 0, 0, 0, -src[0].x, -src[0].y, -1, src[0].x*dst[0].y, src[0].y*dst[0].y, -dst[0].y }, // h12
+
+ {-src[1].x, -src[1].y, -1, 0, 0, 0, src[1].x*dst[1].x, src[1].y*dst[1].x, -dst[1].x }, // h13
+ { 0, 0, 0, -src[1].x, -src[1].y, -1, src[1].x*dst[1].y, src[1].y*dst[1].y, -dst[1].y }, // h21
+
+ {-src[2].x, -src[2].y, -1, 0, 0, 0, src[2].x*dst[2].x, src[2].y*dst[2].x, -dst[2].x }, // h22
+ { 0, 0, 0, -src[2].x, -src[2].y, -1, src[2].x*dst[2].y, src[2].y*dst[2].y, -dst[2].y }, // h23
+
+ {-src[3].x, -src[3].y, -1, 0, 0, 0, src[3].x*dst[3].x, src[3].y*dst[3].x, -dst[3].x }, // h31
+ { 0, 0, 0, -src[3].x, -src[3].y, -1, src[3].x*dst[3].y, src[3].y*dst[3].y, -dst[3].y }, // h32
+ };
+
+ doGaussianElimination(&P[0][0],9);
+
+ // gaussian elimination gives the results of the equation system
+ // in the last column of the original matrix.
+ // opengl needs the transposed 4x4 matrix:
+ float aux_H[]= {P[0][8],P[3][8],0,P[6][8], // h11 h21 0 h31
+ P[1][8],P[4][8],0,P[7][8], // h12 h22 0 h32
+ 0 , 0,0,0, // 0 0 0 0
+ P[2][8],P[5][8],0,1 // h13 h23 0 h33
+ };
+
+ for(int i=0; i<16; i++)
+ glMatrix[i] = aux_H[i];
+
+ surfaceToScreenMatrix(0,0)=P[0][8];
+ surfaceToScreenMatrix(0,1)=P[1][8];
+ surfaceToScreenMatrix(0,2)=0;
+ surfaceToScreenMatrix(0,3)=P[2][8];
+
+ surfaceToScreenMatrix(1,0)=P[3][8];
+ surfaceToScreenMatrix(1,1)=P[4][8];
+ surfaceToScreenMatrix(1,2)=0;
+ surfaceToScreenMatrix(1,3)=P[5][8];
+
+ surfaceToScreenMatrix(2,0)=0;
+ surfaceToScreenMatrix(2,1)=0;
+ surfaceToScreenMatrix(2,2)=0;
+ surfaceToScreenMatrix(2,3)=0;
+
+ surfaceToScreenMatrix(3,0)=P[6][8];
+ surfaceToScreenMatrix(3,1)=P[7][8];
+ surfaceToScreenMatrix(3,2)=0;
+ surfaceToScreenMatrix(3,3)=1;
+}
+
+void ofxPatch::doScreenToSurfaceMatrix(){
+ ofPoint dst[4];
+
+ dst[0].set(0, 0);
+ dst[1].set(width,0.0);
+ dst[2].set(width,height);
+ dst[3].set(0, height);
+
+ ofPoint src[4];
+ for(int i = 0; i < 4; i++){
+ src[i] = textureCorners[i];
+ }
+
+ // create the equation system to be solved
+ //
+ // from: Multiple View Geometry in Computer Vision 2ed
+ // Hartley R. and Zisserman A.
+ //
+ // x' = xH
+ // where H is the homography: a 3 by 3 matrix
+ // that transformed to inhomogeneous coordinates for each point
+ // gives the following equations for each point:
+ //
+ // x' * (h31*x + h32*y + h33) = h11*x + h12*y + h13
+ // y' * (h31*x + h32*y + h33) = h21*x + h22*y + h23
+ //
+ // as the homography is scale independent we can let h33 be 1 (indeed any of the terms)
+ // so for 4 points we have 8 equations for 8 terms to solve: h11 - h32
+ // after ordering the terms it gives the following matrix
+ // that can be solved with gaussian elimination:
+
+ float P[8][9]=
+ {
+ {-src[0].x, -src[0].y, -1, 0, 0, 0, src[0].x*dst[0].x, src[0].y*dst[0].x, -dst[0].x }, // h11
+ { 0, 0, 0, -src[0].x, -src[0].y, -1, src[0].x*dst[0].y, src[0].y*dst[0].y, -dst[0].y }, // h12
+
+ {-src[1].x, -src[1].y, -1, 0, 0, 0, src[1].x*dst[1].x, src[1].y*dst[1].x, -dst[1].x }, // h13
+ { 0, 0, 0, -src[1].x, -src[1].y, -1, src[1].x*dst[1].y, src[1].y*dst[1].y, -dst[1].y }, // h21
+
+ {-src[2].x, -src[2].y, -1, 0, 0, 0, src[2].x*dst[2].x, src[2].y*dst[2].x, -dst[2].x }, // h22
+ { 0, 0, 0, -src[2].x, -src[2].y, -1, src[2].x*dst[2].y, src[2].y*dst[2].y, -dst[2].y }, // h23
+
+ {-src[3].x, -src[3].y, -1, 0, 0, 0, src[3].x*dst[3].x, src[3].y*dst[3].x, -dst[3].x }, // h31
+ { 0, 0, 0, -src[3].x, -src[3].y, -1, src[3].x*dst[3].y, src[3].y*dst[3].y, -dst[3].y }, // h32
+ };
+
+ doGaussianElimination(&P[0][0],9);
+
+ screenToSurfaceMatrix(0,0)=P[0][8];
+ screenToSurfaceMatrix(0,1)=P[1][8];
+ screenToSurfaceMatrix(0,2)=0;
+ screenToSurfaceMatrix(0,3)=P[2][8];
+
+ screenToSurfaceMatrix(1,0)=P[3][8];
+ screenToSurfaceMatrix(1,1)=P[4][8];
+ screenToSurfaceMatrix(1,2)=0;
+ screenToSurfaceMatrix(1,3)=P[5][8];
+
+ screenToSurfaceMatrix(2,0)=0;
+ screenToSurfaceMatrix(2,1)=0;
+ screenToSurfaceMatrix(2,2)=0;
+ screenToSurfaceMatrix(2,3)=0;
+
+ screenToSurfaceMatrix(3,0)=P[6][8];
+ screenToSurfaceMatrix(3,1)=P[7][8];
+ screenToSurfaceMatrix(3,2)=0;
+ screenToSurfaceMatrix(3,3)=1;
+
+}
+
+void ofxPatch::doGaussianElimination(float *input, int n){
+ // ported to c from pseudocode in
+ // http://en.wikipedia.org/wiki/Gaussian_elimination
+
+ float * A = input;
+ int i = 0;
+ int j = 0;
+ int m = n-1;
+ while (i < m && j < n)
+ {
+ // Find pivot in column j, starting in row i:
+ int maxi = i;
+ for(int k = i+1; k<m; k++)
+ {
+ if(fabs(A[k*n+j]) > fabs(A[maxi*n+j]))
+ {
+ maxi = k;
+ }
+ }
+ if (A[maxi*n+j] != 0)
+ {
+ //swap rows i and maxi, but do not change the value of i
+ if(i!=maxi)
+ for(int k=0; k<n; k++)
+ {
+ float aux = A[i*n+k];
+ A[i*n+k]=A[maxi*n+k];
+ A[maxi*n+k]=aux;
+ }
+ //Now A[i,j] will contain the old value of A[maxi,j].
+ //divide each entry in row i by A[i,j]
+ float A_ij=A[i*n+j];
+ for(int k=0; k<n; k++)
+ {
+ A[i*n+k]/=A_ij;
+ }
+ //Now A[i,j] will have the value 1.
+ for(int u = i+1; u< m; u++)
+ {
+ //subtract A[u,j] * row i from row u
+ float A_uj = A[u*n+j];
+ for(int k=0; k<n; k++)
+ {
+ A[u*n+k]-=A_uj*A[i*n+k];
+ }
+ //Now A[u,j] will be 0, since A[u,j] - A[i,j] * A[u,j] = A[u,j] - 1 * A[u,j] = 0.
+ }
+
+ i++;
+ }
+ j++;
+ }
+
+ //back substitution
+ for(int i=m-2; i>=0; i--)
+ {
+ for(int j=i+1; j<n-1; j++)
+ {
+ A[i*n+m]-=A[i*n+j]*A[j*n+m];
+ //A[i*n+j]=0;
+ }
+ }
+}
+
+// -------------------------------------------------------------------- EVENTS
+void ofxPatch::_reMakeFrame( int &_nId ){
+ if (type == "ofxGLEditor"){
+ textureCorners[0].set(0, height);
+ textureCorners[1].set(width, height);
+ textureCorners[2].set(width, 0);
+ textureCorners[3].set(0, 0);
+ } else {
+ textureCorners[0].set(0, 0);
+ textureCorners[1].set(width, 0);
+ textureCorners[2].set(width, height);
+ textureCorners[3].set(0, height);
+ }
+
+ bUpdateCoord = true;
+ saveSettings();
+}
+
+void ofxPatch::_mousePressed(ofMouseEventArgs &e){
+ ofVec3f mouse = ofVec3f(e.x, e.y, 0.0);
+
+ if (bEditMode && bActive ){
+ if (!bEditMask){
+ // Editing the texture corners
+ //
+ for(int i = 0; i < 4; i++){
+ if ( ofDist(e.x, e.y, textureCorners[i].x, textureCorners[i].y) <= 10 )
+ selectedTextureCorner = i;
+ }
+ } else {
+ // Editing the mask corners
+ //
+ bool overDot = false;
+ for(int i = 0; i < maskCorners.size(); i++){
+ ofVec3f pos = ofVec3f( maskCorners[i].x * width, maskCorners[i].y * height, 0.0);
+ pos = surfaceToScreenMatrix * pos;
+
+ if ( ofDist(e.x, e.y, pos.x, pos.y) <= 10 ){
+ selectedMaskCorner = i;
+ overDot = true;
+ }
+ }
+
+ // Add new Dot if it's over the line
+ //
+ if (!overDot){
+ doScreenToSurfaceMatrix();
+ mouse = screenToSurfaceMatrix * mouse;
+ mouse.x = mouse.x / width;
+ mouse.y = mouse.y / height;
+
+ int addNew = -1;
+
+ // Search for the right placer to incert the point in the array
+ //
+ for (int i = 0; i < maskCorners.size(); i++){
+ int next = (i+1)%maskCorners.size();
+
+ ofVec2f AtoM = mouse - maskCorners[i];
+ ofVec2f AtoB = maskCorners[next] - maskCorners[i];
+
+ float a = atan2f(AtoM.x, AtoM.y);
+ float b = atan2f(AtoB.x, AtoB.y);
+
+ if ( abs(a - b) < 0.05){
+ addNew = next;
+ }
+ }
+
+ if (addNew >= 0 ){
+ maskCorners.getVertices().insert( maskCorners.getVertices().begin()+addNew, mouse);
+ selectedMaskCorner = addNew;
+ }
+
+ }
+ }
+ }
+}
+
+void ofxPatch::_mouseDragged(ofMouseEventArgs &e){
+ ofVec3f mouse = ofVec3f(e.x, e.y,0);
+ ofVec3f mouseLast = ofVec3f(ofGetPreviousMouseX(),ofGetPreviousMouseY(),0);
+
+ if (bEditMode){
+ if (!bEditMask){
+ // Drag texture corners
+ //
+ if (( selectedTextureCorner >= 0) && ( selectedTextureCorner < 4) ){
+
+ if (e.button == 2){
+ // Deformation
+ //
+ textureCorners[selectedTextureCorner].x = ofGetMouseX();
+ textureCorners[selectedTextureCorner].y = ofGetMouseY();
+
+ bUpdateCoord = true;
+
+ } else if ( ofGetKeyPressed('r') ){
+ // Rotation
+ //
+ ofVec2f center = getPos();
+
+ ofVec2f fromCenterTo = mouseLast - center;
+ float prevAngle = -1.0*atan2f(fromCenterTo.x,fromCenterTo.y)+(PI/2);
+
+ fromCenterTo = mouse - center;
+ float actualAngle = -1.0*atan2f(fromCenterTo.x,fromCenterTo.y)+(PI/2);
+
+ float dif = actualAngle-prevAngle;
+
+ rotate(dif);
+ } else if (( e.button == 1 ) || ofGetKeyPressed('a') ){
+ // Centered Scale
+ //
+ float prevDist = mouseLast.distance(getPos());
+ float actualDist = mouse.distance(getPos());
+
+ float dif = actualDist/prevDist;
+
+ scale(dif);
+ } else {
+ // Corner Scale
+ //
+ ofVec2f center = getPos();
+
+ int opositCorner = (selectedTextureCorner - 2 < 0)? (4+selectedTextureCorner-2) : (selectedTextureCorner-2);
+ ofVec2f toOpositCorner = center - textureCorners[opositCorner];
+
+ float prevDist = mouseLast.distance( textureCorners[opositCorner] );
+ float actualDist = mouse.distance( textureCorners[opositCorner] );
+
+ float dif = actualDist/prevDist;
+
+ move( textureCorners[opositCorner] + toOpositCorner * dif );
+ scale(dif);
+ }
+
+ // Drag all the surface
+ //
+ } else if ( isOver(mouse) && bActive ){
+ for (int i = 0; i < 4; i++){
+ textureCorners[i] += mouse-mouseLast;
+ }
+
+ bUpdateCoord = true;
+ mouseLast = mouse;
+ }
+ } else {
+
+ // Drag mask points
+ //
+ for(int i = 0; i < maskCorners.size(); i++){
+ ofVec3f pos = ofVec3f( maskCorners[i].x * width, maskCorners[i].y * height, 0.0);
+ pos = surfaceToScreenMatrix * pos;
+
+ if ((selectedMaskCorner >= 0) && (selectedMaskCorner < maskCorners.size() )){
+ ofVec3f newPos = ofVec3f(mouse.x, mouse.y, 0.0);
+ doScreenToSurfaceMatrix();
+
+ newPos = screenToSurfaceMatrix * mouse;
+ newPos.x = ofClamp(newPos.x / width, 0.0, 1.0);
+ newPos.y = ofClamp(newPos.y / height, 0.0, 1.0);
+
+ maskCorners[selectedMaskCorner] = newPos;
+ bMasking = true;
+
+ bUpdateMask = true;
+ }
+ }
+ }
+ }
+}
+
+void ofxPatch::_mouseReleased(ofMouseEventArgs &e){
+ ofVec3f mouse = ofVec3f(e.x, e.y,0);
+ if (bEditMode || isOver(mouse)){
+ if (!bEditMask){
+ if (( selectedTextureCorner >= 0) && ( selectedTextureCorner < 4) ){
+ selectedTextureCorner = -1;
+ }
+ saveSettings();
+ } else {
+ bUpdateMask = true;
+ saveSettings();
+ selectedMaskCorner = -1;
+ }
+ }
+}
+
+//Key Events
+void ofxPatch::_keyPressed(ofKeyEventArgs &e){
+
+ switch (e.key) {
+ case OF_KEY_F3:
+ bEditMode = !bEditMode;
+ break;
+ }
+
+ if (bActive && bEditMode & bEditMask) {
+
+ // Delete the selected mask point
+ //
+ if ( (e.key == 'd') &&
+ (selectedMaskCorner >= 0) &&
+ (selectedMaskCorner < maskCorners.size() ) ){
+ maskCorners.getVertices().erase(maskCorners.getVertices().begin()+ selectedMaskCorner );
+ selectedMaskCorner = -1;
+
+ bUpdateMask = true;
+ saveSettings();
+
+ bMasking = true;
+ }
+
+ // Reset all the mask or the texture
+ //
+ if ( (e.key == 'c') ){
+ maskCorners.clear();
+ selectedMaskCorner = -1;
+ maskCorners.addVertex(0.0,0.0);
+ maskCorners.addVertex(1.0,0.0);
+ maskCorners.addVertex(1.0,1.0);
+ maskCorners.addVertex(0.0,1.0);
+
+ bUpdateMask = true;
+ saveSettings();
+
+ bMasking = false;
+ }
+ }
+}
+
+// ------------------------------------------------------------- LOAD & SAVE
+bool ofxPatch::loadFile(string _filePath, string _configFile){
+ bool loaded = false;
+
+ if (_configFile != "none")
+ configFile = _configFile;
+
+ ofFile file = ofFile(_filePath);
+
+ filePath = file.getAbsolutePath();
+ string ext = file.getExtension();
+
+ // Load General setup variables
+ //
+ ofxXmlSettings XML;
+ if (XML.loadFile(configFile)){
+ if (XML.getValue("general:fullscreen", false ) == true){
+ width = ofGetScreenWidth();
+ height = ofGetScreenHeight();
+ } else {
+ width = XML.getValue("general:width", 640 );
+ height = XML.getValue("general:height", 480 );
+ }
+ }
+
+ if ((ext == "jpg") || (ext == "JPG") ||
+ (ext == "jpeg")|| (ext == "JPEG")||
+ (ext == "png") || (ext == "PNG") ||
+ (ext == "gif") || (ext == "GIF") ||
+ (ext == "bmp") || (ext == "BMP") ){
+
+ type = "ofImage";
+
+ if ( image != NULL )
+ delete image;
+
+ image = new ofImage();
+ loaded = image->loadImage( filePath );
+ width = image->getWidth();
+ height = image->getHeight();
+
+ } else if ((ext == "mov") || (ext == "MOV") ||
+ (ext == "mpg") || (ext == "MPG") ||
+ (ext == "mp4") || (ext == "MP4") ||
+ (ext == "m4v") || (ext == "M4V") ){
+
+ type = "ofVideoPlayer";
+
+ if ( videoPlayer != NULL )
+ delete videoPlayer;
+
+ videoPlayer = new ofVideoPlayer();
+ loaded = videoPlayer->loadMovie( filePath );
+ videoPlayer->play();
+ width = videoPlayer->getWidth();
+ height = videoPlayer->getHeight();
+
+ } else if ((ext == "frag") || (ext == "FRAG") ||
+ (ext == "fs") || (ext == "FS") ){
+
+ ofBuffer content = ofBufferFromFile( filePath );
+
+ type = "ofShader";
+
+ if ( shader != NULL )
+ delete shader;
+
+ shader = new ofxShaderObj();
+ shader->allocate(width,height);
+ loaded = shader->setFragmentShader(content.getText());
+
+ inPut.clear();
+ for ( int i = 0; i < shader->getNumberOfTextures(); i++){
+ LinkDot p;
+ inPut.push_back(p);
+ }
+
+ } else if ((ext == "lut") || (ext == "LUT") ||
+ (ext == "cube") || (ext == "CUBE")){
+
+ type = "ofTexture";
+
+ if ( texture != NULL )
+ delete texture;
+
+ ofBuffer buffer = ofBufferFromFile( filePath );
+
+ int mapSize = 32;
+ width = mapSize * mapSize;
+ height = mapSize;
+
+ float * pixels = new float[mapSize * mapSize * mapSize * 3];
+
+ texture = new ofTexture();
+ texture->allocate( width, height, GL_RGB32F);
+ for(int z=0; z<mapSize ; z++){
+ for(int y=0; y<mapSize; y++){
+ for(int x=0; x<mapSize; x++){
+ string content = buffer.getNextLine();
+ int pos = x + y*height + z*width;
+
+ vector <string> splitString = ofSplitString(content, " ", true, true);
+
+ if (splitString.size() >=3) {
+ pixels[pos*3 + 0] = ofToFloat(splitString[0]);
+ pixels[pos*3 + 1] = ofToFloat(splitString[1]);
+ pixels[pos*3 + 2] = ofToFloat(splitString[2]);
+ }
+ }
+ }
+ }
+ texture->loadData( pixels, width, height, GL_RGB);
+
+ loaded = (buffer.size() > 0)? true: false;
+
+ } else
+ ofLog(OF_LOG_ERROR, "ofxComposer: ofxPatch: can«t load file " + filePath + " Extention " + ext + " not know" );
+
+ if ( loaded ){
+ textureCorners[0].set(0, 0);
+ textureCorners[1].set(width, 0);
+ textureCorners[2].set(width, height);
+ textureCorners[3].set(0, height);
+
+ title->setTitle( file.getFileName() );
+
+ bUpdateMask = true;
+ bUpdateCoord = true;
+ }
+
+ return loaded;
+}
+
+bool ofxPatch::loadSettings( int _nTag, string _configFile){
+ bool loaded = false;
+
+ ofxXmlSettings XML;
+
+ if (_configFile != "none")
+ configFile = _configFile;
+
+ if (XML.loadFile(configFile)){
+ maskCorners.clear();
+
+ if (XML.getValue("general:fullscreen", false ) == true){
+ width = ofGetScreenWidth();
+ height = ofGetScreenHeight();
+ } else {
+ width = XML.getValue("general:width", 640 );
+ height = XML.getValue("general:height", 480 );
+ }
+
+ if (XML.pushTag("surface", _nTag)){
+
+ // Load the type and do what it have to
+ //
+ nId = XML.getValue("id", 0);
+ type = XML.getValue("type","none");
+ bVisible = XML.getValue("visible", true);
+ string path = XML.getValue("path", "none" );
+ filePath = path;
+
+ // Except the ofVideoGrabber that it«s a device instead of a file
+ // and ofShader that it will read the data stored on the XML
+ // the rest it been load by the loadFile function
+ //
+ if ( type == "ofVideoGrabber" ){
+ videoGrabber = new ofVideoGrabber();
+ videoGrabber->setDeviceID( XML.getValue("path", 0 ) );
+
+ width = XML.getValue("width", width);
+ height = XML.getValue("height", height);
+
+ title->setTitle(ofToString(nId) + ":" + type );
+ loaded = videoGrabber->initGrabber(width, height);
+
+ if (loaded){
+ width = videoGrabber->getWidth();
+ height = videoGrabber->getHeight();
+ }
+ } else if ( type == "ofShader" ){
+ shader = new ofxShaderObj();
+ width = XML.getValue("width", width);
+ height = XML.getValue("height", height);
+ shader->allocate(width, height, XML.getValue("format", GL_RGBA));
+ shader->setPasses( XML.getValue("passes", 1) );
+
+ loaded = shader->setFragmentShader( XML.getValue("frag", "Error: data not found...") );
+
+ if ( loaded ){
+ inPut.clear();
+ for ( int i = 0; i < shader->getNumberOfTextures(); i++){
+ LinkDot p;
+ inPut.push_back(p);
+ }
+
+ ofFile file;
+ file.open(path);
+ title->setTitle(ofToString(nId) + ":" + file.getFileName() );
+ }
+ } else if( type == "ofxGLEditor" ){
+ title->setTitle(ofToString(nId) + ":" + type );
+ loaded = true;
+ } else if ( type == "input") {
+ title->setTitle(ofToString(nId) + ":" + type );
+ loaded = true;
+ } else {
+ loaded = loadFile( path );
+ }
+
+ if (loaded){
+ // Load the texture coorners
+ //
+ if (XML.pushTag("texture")){
+ for(int i = 0; i < 4; i++){
+ if (XML.pushTag("point",i)){
+ textureCorners[i].set(XML.getValue("x", 0.0),XML.getValue("y", 0.0));
+ XML.popTag();
+ }
+ }
+ XML.popTag();
+ }
+
+ // Load the mask path
+ if ( XML.pushTag("mask") ){
+ int totalMaskCorners = XML.getNumTags("point");
+ if (totalMaskCorners > 0){
+ maskCorners.clear();
+ }
+
+ for(int i = 0; i < totalMaskCorners; i++){
+ XML.pushTag("point",i);
+ maskCorners.addVertex( XML.getValue("x", 0.0),XML.getValue("y", 0.0));
+ XML.popTag(); // Pop "point"
+ }
+ XML.popTag(); // Pop "mask"
+
+ if ( maskCorners.getVertices().size() == 4 ){
+ if ((maskCorners.getVertices()[0] == ofPoint(0.0,0.0)) &&
+ (maskCorners.getVertices()[1] == ofPoint(1.0,0.0)) &&
+ (maskCorners.getVertices()[2] == ofPoint(1.0,1.0)) &&
+ (maskCorners.getVertices()[3] == ofPoint(0.0,1.0)) )
+ bMasking = false;
+ else
+ bMasking = true;
+ } else {
+ bMasking = true;
+ }
+ }
+
+ bUpdateMask = true;
+ bUpdateCoord = true;
+
+ XML.popTag(); // Pop Surface
+ }
+ }
+ } else
+ ofLog(OF_LOG_ERROR,"ofxComposer: loading patch n¼ " + ofToString(nId) + " on " + configFile );
+
+ return loaded;
+}
+
+bool ofxPatch::saveSettings(string _configFile){
+ bool saved = false;
+
+ ofxXmlSettings XML;
+
+ if (_configFile != "none")
+ configFile = _configFile;
+
+ // Open the configfile
+ //
+ if (XML.loadFile(configFile)){
+
+ // If it«s the first time it«s saving the information
+ // the nId it«s going to be -1 and it«s not going to be
+ // a place that holds the information. It«s that the case:
+ //
+ // 1- Search for the first free ID abailable number
+ //
+ // 2- Make the structure of the path that hold the information
+ //
+
+ if (nId == -1){
+ // Find a free id number for this object
+ //
+ int totalSurfaces = XML.getNumTags("surface");
+ int freeId = 0;
+ for (int i = 0; i < totalSurfaces; i++){
+ if (XML.pushTag("surface", i)){
+ if ( XML.getValue("id",-1) == freeId ){
+ freeId++;
+ i = 0;
+ }
+ XML.popTag();
+ }
+ }
+
+ if ( freeId >= 0){
+ nId = freeId;
+
+ // Insert a new surface tag at the end of the xml
+ // and fill it with the proper structure
+ //
+ int lastPlace = XML.addTag("surface");
+ if (XML.pushTag("surface", lastPlace)){
+
+ XML.addTag("id");
+ XML.setValue("id", nId);
+ XML.addTag("type");
+ XML.setValue("type", type);
+ XML.addTag("path");
+ XML.setValue("path", filePath);
+ XML.addTag("visible");
+ XML.setValue("visible", bVisible);
+
+ // For the moment the shader string it's the only one
+ // with an extra parametter.
+ //
+ if ( shader != NULL) {
+ XML.addTag("frag");
+ XML.setValue("frag", shader->getFragmentShader() );
+ XML.addTag("format");
+ XML.setValue("format", shader->getInternalFormat() );
+ XML.addTag("passes");
+ XML.setValue("passes", shader->getPasses() );
+ }
+
+ // Texture Corners
+ //
+ XML.addTag("texture");
+ if (XML.pushTag("texture")){
+ for(int i = 0; i < 4; i++){
+
+ XML.addTag("point");
+
+ XML.setValue("point:x",textureCorners[i].x, i);
+ XML.setValue("point:y",textureCorners[i].y, i);
+ }
+ XML.popTag();// Pop "texture"
+ }
+
+ // Mask Path
+ //
+ XML.addTag("mask");
+ if (XML.pushTag("mask")){
+ for(int i = 0; i < 4; i++){
+ XML.addTag("point");
+
+ XML.setValue("point:x",maskCorners[i].x, i);
+ XML.setValue("point:y",maskCorners[i].y, i);
+ }
+ XML.popTag();// Pop "mask"
+ }
+
+ // Save out linked dots
+ //
+ XML.addTag("out");
+ XML.setValue("out:active",1);
+ if(XML.pushTag("out")){
+ int totalSavedLinks = XML.getNumTags("dot");
+
+ for(int j = 0; j < outPut.size(); j++){
+ int tagNum = j;
+
+ // If need more tags
+ // add them
+ //
+ if (j >= totalSavedLinks)
+ tagNum = XML.addTag("dot");
+
+ XML.setValue("dot:to", outPut[j].toId , tagNum);
+ XML.setValue("dot:tex", outPut[j].nTex, tagNum);
+ }
+ XML.popTag();// pop "out"
+ saved = XML.saveFile();
+ }
+
+ XML.popTag();// pop "surface"
+ }
+ }
+
+ // This is necesary for making the initial matrix, mask FBO, mask path and texture corners.
+ //
+ if (saved){
+ ofLog(OF_LOG_NOTICE, "The patch have been asigned with ID " + ofToString(nId) + " and save the information" );
+ }
+
+ } else {
+
+ // If the ID it's different from -1 means that it was already
+ // find an ID and a place to hold the information so it'll
+ // proceed with a normma save process
+ //
+
+ // Get the total number of surfaces...
+ //
+ int totalSurfaces = XML.getNumTags("surface");
+
+ // ... and search for the right id for loading
+ //
+ for (int i = 0; i < totalSurfaces; i++){
+ if (XML.pushTag("surface", i)){
+
+ // Once it found the right surface that match the id ...
+ //
+ if ( XML.getValue("id", -1) == nId){
+
+ // General information
+ //
+ XML.setValue("path", filePath );
+ XML.setValue("visible", bVisible);
+
+ if ( shader != NULL ){
+ // Shader specific
+ //
+ XML.setValue("frag", shader->getFragmentShader() );
+ XML.setValue("format", shader->getInternalFormat() );
+ XML.setValue("passes", shader->getPasses() );
+ }
+
+ // Position of the texture coorners
+ //
+ if (XML.pushTag("texture")){
+ for(int i = 0; i < 4; i++){
+ XML.setValue("point:x",textureCorners[i].x, i);
+ XML.setValue("point:y",textureCorners[i].y, i);
+ }
+ XML.popTag(); // pop "texture"
+ }
+
+ // Mask path
+ //
+ if (XML.pushTag("mask")){
+ int totalSavedPoints = XML.getNumTags("point");
+
+ for(int j = 0; j < maskCorners.size(); j++){
+ int tagNum = j;
+
+ if (i >= totalSavedPoints)
+ tagNum = XML.addTag("point");
+
+ XML.setValue("point:x",maskCorners[j].x, tagNum);
+ XML.setValue("point:y",maskCorners[j].y, tagNum);
+ }
+
+ int totalCorners = maskCorners.size();
+ totalSavedPoints = XML.getNumTags("point");
+
+ if ( totalCorners < totalSavedPoints){
+ for(int j = totalSavedPoints; j > totalCorners; j--){
+ XML.removeTag("point",j-1);
+ }
+ }
+ XML.popTag(); // pop "mask"
+ }
+
+ // Save out linked dots
+ //
+ if(XML.pushTag("out")){
+ int totalSavedLinks = XML.getNumTags("dot");
+
+ for(int j = 0; j < outPut.size(); j++){
+ int tagNum = j;
+
+ // If need more tags
+ // add them
+ //
+ if (j >= totalSavedLinks)
+ tagNum = XML.addTag("dot");
+
+ XML.setValue("dot:to", outPut[j].toId , tagNum);
+ XML.setValue("dot:tex", outPut[j].nTex, tagNum);
+ }
+
+ // If there are too much tags
+ // delete them
+ //
+ int totalOutPuts = outPut.size();
+ if ( totalOutPuts < totalSavedLinks){
+ for(int j = totalSavedLinks; j > totalOutPuts; j--){
+ XML.removeTag("dot",j-1);
+ }
+ }
+ XML.popTag(); // pop "out"
+ }
+
+ // Once it finish save
+ //
+ saved = XML.saveFile();
+ }
+ XML.popTag(); // pop "surface"
+ }
+ }
+
+ if (saved){
+ ofLog(OF_LOG_NOTICE, "The patch with ID " + ofToString(nId) + " have save it information" );
+ }
+ }
+ } else
+ ofLog(OF_LOG_ERROR, "Couldn't save the patch ID " + ofToString(nId) + ", the file " + configFile + " was not found");
+
+ return saved;
+}
View
135 src/ofxPatch.h
@@ -0,0 +1,135 @@
+//
+// ofxPatch.h
+// emptyExample
+//
+// Created by Patricio Gonzalez Vivo on 3/9/12.
+// Copyright (c) 2012 http://www.PatricioGonzalezVivo.com All rights reserved.
+//
+
+#ifndef OFXPATCH
+#define OFXPATCH
+
+#include "ofMain.h"
+
+#include "ofxXmlSettings.h"
+
+#include "ofxTitleBar.h"
+#include "ofxShaderObj.h"
+#include "ofxPingPong.h"
+
+struct LinkDot{
+ LinkDot(){
+ to = NULL;
+ toShader = NULL;
+ nTex = 0;
+ }
+
+ ofPoint pos;
+ LinkDot *to;
+ int nTex;
+ int toId;
+ ofxShaderObj *toShader;
+};
+
+class ofxPatch{
+public:
+
+ ofxPatch();
+ ~ofxPatch();
+
+ bool loadFile(string _filePath, string _configFile = "none");
+
+ bool loadSettings(int _nTag, string _configFile = "none");
+ bool saveSettings(string _configFile = "none");
+
+ void setFrag( string _code);
+ //void setVert( string _code);
+ void setTexture(ofTexture& tex, int _texNum = 0);
+
+ int getId() const { return nId; };
+ ofPoint getPos() const { return ofPoint(x,y); };
+ string getType() const { return (shader != NULL)? "ofShader" : type; };
+ string getFrag();
+ //string getVert();
+ ofTexture& getTextureReference();
+ ofxShaderObj* getShader(){ if (getType() == "ofShader") return shader; else return NULL; };
+ ofPoint& getOutPutPosition(){ return outPutPos; };
+
+ void move(ofPoint _pos);
+ void scale(float _scale);
+ void rotate(float _angle);
+
+ void update();
+ void draw();
+
+ bool isOver(ofPoint _pos){ return textureCorners.inside(_pos); };
+
+ vector<LinkDot> outPut;
+ vector<LinkDot> inPut;
+
+ ofxTitleBar *title;
+
+ bool bActive;
+ bool bEditMode;
+ bool bEditMask;
+ bool bVisible;
+
+private:
+ void doSurfaceToScreenMatrix(); // Update the SurfaceToScreen transformation matrix
+ void doScreenToSurfaceMatrix(); // Update the ScreenToSurface transformation matrix
+ void doGaussianElimination(float *input, int n); // This is used making the matrix
+
+ // Mouse & Key Events ( it´s not better if is centralized on the composer )
+ //
+ void _mousePressed(ofMouseEventArgs &e);
+ void _mouseDragged(ofMouseEventArgs &e);
+ void _mouseReleased(ofMouseEventArgs &e);
+ void _keyPressed(ofKeyEventArgs &e);
+ void _reMakeFrame( int &_nId );
+
+
+ // 5 Sources Objects and one interface to rule them all
+ //
+ ofTexture& getSrcTexture();
+
+ ofImage *image;
+ ofVideoPlayer *videoPlayer;
+ ofVideoGrabber *videoGrabber;
+ ofxShaderObj *shader;
+ ofTexture *texture;
+
+ // Mask variables
+ //
+ ofxPingPong maskFbo;
+ ofShader maskShader;
+ ofPolyline maskCorners;
+ int selectedMaskCorner;
+
+ // Texture varialbes
+ //
+ ofPolyline textureCorners;
+ int selectedTextureCorner;
+ int textureWidth, textureHeight;
+
+ ofPoint src[4];
+ ofMatrix4x4 surfaceToScreenMatrix;
+ ofMatrix4x4 screenToSurfaceMatrix;
+ GLfloat glMatrix[16];
+
+ // General Variables
+ //
+ ofRectangle box;
+ ofPoint outPutPos;
+ string configFile;
+ string filePath;
+ string type;
+ float x, y;
+ int width, height;
+ int nId;
+
+ bool bMasking;
+ bool bUpdateMask;
+ bool bUpdateCoord;
+};
+
+#endif
View
51 src/ofxPingPong.h
@@ -0,0 +1,51 @@
+//
+// ofxPingPong.h
+// emptyExample
+//
+// Created by Patricio Gonzalez Vivo on 3/9/12.
+// Copyright (c) 2012 http://www.PatricioGonzalezVivo.com All rights reserved.
+//
+
+#ifndef OFXPINGPONG
+#define OFXPINGPONG
+
+class ofxPingPong {
+public:
+ void allocate( int _width, int _height, int _internalformat = GL_RGBA){
+ // Allocate
+ for(int i = 0; i < 2; i++)
+ FBOs[i].allocate(_width,_height, _internalformat );
+
+ // Clean
+ clear();
+
+ // Set everything to 0
+ flag = 0;
+ swap();
+ flag = 0;
+ }
+
+ void swap(){
+ src = &(FBOs[(flag)%2]);
+ dst = &(FBOs[++(flag)%2]);
+ }
+
+ void clear(){
+ for(int i = 0; i < 2; i++){
+ FBOs[i].begin();
+ ofClear(0,0);
+ FBOs[i].end();
+ }
+ }
+
+ ofFbo& operator[]( int n ){ return FBOs[n];}
+
+ ofFbo *src; // Source -> Ping
+ ofFbo *dst; // Destination -> Pong
+
+private:
+ ofFbo FBOs[2]; // Real addresses of ping/pong FBO´s
+ int flag; // Integer for making a quick swap
+};
+
+#endif
View
300 src/ofxShaderObj.cpp
@@ -0,0 +1,300 @@
+/*
+ * ofxShaderObj.h
+ *
+ * Created by Patricio Gonzalez Vivo on 03/07/11.
+ * Copyright 2011 http://www.PatricioGonzalezVivo.com All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ************************************************************************************
+ *
+ */
+
+#include "ofxShaderObj.h"
+
+ofxShaderObj::ofxShaderObj():nTextures(0){
+ // Simple effect just need this three variables
+ // For something more complex that require another structure, logic or more shaders working together
+ // think on making a new stand-alone class as the ofxBlur, ofxFluid, ofxGlow, etc ...
+ // Collaborations are welcome
+
+ passes = 1; // Number of itinerations needs. Default it´s 1;
+ internalFormat = GL_RGBA; // Tipe of GL textures
+
+ // And the fragSahder it self. Note that are defaul variables:
+ //
+ // - time
+ // - mouse position (normalized)
+ // - resolution
+ // - backbuffer texture
+ // - tex0, tex1, tex2, ... : this are dynamicaly defined and allocated and can be
+ // filled with information by using .begin(0) and .end(0), or .begin(1) and .end(1), etc
+ //
+ // This dafault shader it´s timer made of a mix on Ricardo Caballero´s webGL Sandbox
+ // http://mrdoob.com/projects/glsl_sandbox/
+ //
+
+ fragmentShader = "\n\
+\n\
+\n\
+uniform float time;\n\
+uniform vec2 mouse;\n\
+uniform vec2 resolution;\n\
+\n\
+uniform sampler2DRect backbuffer;\n\
+\n\
+uniform sampler2DRect tex0;\n\
+uniform vec2 size0;\n\
+\n\
+void main( void ){\n\
+ vec2 st = (gl_FragCoord.xy / resolution.xy);\n\
+ \n\
+ vec4 color = texture2DRect(tex0, st);\n\
+ \n\
+ gl_FragColor = vec4( color.r, color.g, color.b, color.a );\n\
+}\n\
+\n";
+
+ vertexShader = "void main(){\n\
+\n\
+gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\
+}\n";
+}
+
+ofxShaderObj::~ofxShaderObj(){
+ if (textures != NULL ){
+ if (nTextures > 0) {
+ delete [] textures;
+ }
+ }
+}
+
+ofxShaderObj& ofxShaderObj::operator =(ofxShaderObj& parent){
+ passes = parent.getPasses();
+ internalFormat = parent.getInternalFormat();
+ fragmentShader = parent.getFragmentShader();
+ ofVec2f resolution = parent.getResolution();
+ allocate(resolution.x, resolution.y);
+
+ return * this;
+}
+
+
+void ofxShaderObj::allocate(int _width, int _height, int _internalFormat){
+ width = _width;
+ height = _height;
+
+ if (_internalFormat != -1)
+ internalFormat = _internalFormat;
+
+ pingPong.allocate(width, height, internalFormat);
+ doFragmentShader();
+};
+
+bool ofxShaderObj::setFragmentShader(string _fragShader){
+ bool loaded = false;
+
+ if ( fragmentShader != _fragShader ){
+
+ ofShader test;
+ test.setupShaderFromSource(GL_FRAGMENT_SHADER, _fragShader);
+ bFine = test.linkProgram();
+
+ if( bFine ){
+ fragmentShader = _fragShader;
+ loaded = doFragmentShader();
+ }
+ }
+
+ return loaded;
+}
+
+/*
+bool ofxShaderObj::setVertexShader(string _vertShader){
+ bool loaded = false;
+
+ if ( vertexShader != _vertShader ){
+
+ ofShader test;
+ test.setupShaderFromSource(GL_VERTEX_SHADER, _vertShader);
+ bFine = test.linkProgram();
+
+ if( bFine ){
+ vertexShader = _vertShader;
+ loaded = doFragmentShader();
+ }
+ }
+
+ return loaded;
+}*/
+
+// A simplified way of filling the insides texture
+void ofxShaderObj::setTexture(ofTexture& tex, int _texNum){
+ if ((_texNum < nTextures) && ( _texNum >= 0)){
+ textures[_texNum].begin();
+ ofClear(0,255);
+ ofSetColor(255);
+ tex.draw(0,0);
+ textures[_texNum].end();
+ }
+};
+
+
+// ---------------------------------------------------------- LOOPS
+//
+// As most objects on openFrameworks, ofxShaderObj have to be updated() in order to process the information on the GPU
+void ofxShaderObj::update(){
+
+ // This process it´s going to be repited as many times as passes variable said
+ for(int i = 0; i < passes; i++) {
+
+ // All the process it´s done on the pingPong ofxSwapBuffer ( basicaly two ofFbo that have a swap() funtion )
+ pingPong.dst->begin();
+
+ ofClear(0);
+ shader.begin();
+
+ // The other ofFbo of the ofxSwapBuffer can be access by calling the unicode "backbuffer"
+ shader.setUniformTexture("backbuffer", pingPong.src->getTextureReference(), 0 );
+
+ // All the needed textures are provided to the shader by this loop
+ for( int i = 0; i < nTextures; i++){
+ string texName = "tex" + ofToString(i);
+ shader.setUniformTexture(texName.c_str(), textures[i].getTextureReference(), i+1 );
+ //string texRes = "size" + ofToString(i);
+ //shader.setUniform2f(texRes.c_str() , (float)textures[i].getWidth(), (float)textures[i].getHeight());
+ }
+
+ // Also there are some standar variables that are passes to the shaders
+ // this ones follows the standar used by Ricardo Caballero´s webGL Sandbox
+ // http://mrdoob.com/projects/glsl_sandbox/ and ShaderToy by Inigo Quilez http://www.iquilezles.org/apps/shadertoy/
+ // webGL interactive GLSL editors
+ //
+ shader.setUniform1f("time", (float)time );
+ //shader.setUniform2f("size", (float)width, (float)height);
+ shader.setUniform2f("resolution", (float)width, (float)height);
+ shader.setUniform2f("mouse", (float)(ofGetMouseX()/width), (float)(ofGetMouseY()/height));
+
+ // doFrame() it´s a built-in funtion of ofxShaderObj that only draw a white box in order to
+ // funtion as a frame here the textures could rest.
+ // If you want to distort the points of a textures, probably you want to re-define the renderFrame funtion.
+ renderFrame();
+
+ shader.end();
+
+ pingPong.dst->end();
+
+ pingPong.swap(); // Swap the ofFbo´s. Now dst it´s src and src it´s dst