From e98cae857d1d6dd0ef904933687cb27c5117a0fc Mon Sep 17 00:00:00 2001 From: c-mendoza Date: Thu, 14 Dec 2017 13:35:04 -0500 Subject: [PATCH] CircleSurface + changes in various files to support the new surface --- src/Application/SettingsLoader.cpp | 85 +++---- src/Gui/Widgets/TextureEditorWidget.cpp | 3 +- src/Surfaces/CircleSurface.cpp | 299 ++++++++++++++++++++++++ src/Surfaces/CircleSurface.h | 63 +++++ src/Surfaces/SurfaceFactory.cpp | 7 + src/Surfaces/SurfaceFactory.h | 2 + src/Surfaces/SurfaceType.h | 3 +- 7 files changed, 421 insertions(+), 41 deletions(-) create mode 100644 src/Surfaces/CircleSurface.cpp create mode 100644 src/Surfaces/CircleSurface.h diff --git a/src/Application/SettingsLoader.cpp b/src/Application/SettingsLoader.cpp index 9f8d5fd..8f294e6 100644 --- a/src/Application/SettingsLoader.cpp +++ b/src/Application/SettingsLoader.cpp @@ -20,46 +20,46 @@ bool SettingsLoader::load( SurfaceManager & surfaceManager, MediaServer & mediaServer, string fileName){ - + ofxXmlSettings * xmlSettings = new ofxXmlSettings(); string sourceType = ""; string sourceName = ""; - + BaseSource * source = 0; - + if(!xmlSettings->loadFile(fileName)){ ofLogWarning("SettingsLoader::load()") << "Could not load XML settings"; return false; } - + if(!xmlSettings->tagExists("surfaces")){ xmlSettings->addTag("surfaces"); } - + // Count tags. unsigned int numPresets = xmlSettings->getNumTags("surfaces"); cout << "numPresets: " << numPresets << endl; - + // Clear previous presets and surfaces first. surfaceManager.clearPresets(); - + // Loop through tags in the XML. for(unsigned int i = 0; i < numPresets; ++i){ - + xmlSettings->pushTag("surfaces", i); - + SurfaceStack * surfaces = surfaceManager.createPreset(); int numSurfaces = xmlSettings->getNumTags("surface"); for(int i = 0; i < numSurfaces; i++){ if(xmlSettings->tagExists("surface", i)){ - + SurfaceType type = SurfaceType::NONE; if(xmlSettings->attributeExists("surface", "type")){ type = static_cast( xmlSettings->getAttribute("surface", "type", 0, i)); } - + xmlSettings->pushTag("surface", i); // attempt to load surface source @@ -122,6 +122,13 @@ bool SettingsLoader::load( quadSurface->setSource(source); } surfaces->push_back(quadSurface); + }else if(type == SurfaceType::CIRCLE_SURFACE){ + QuadSurface * base = (QuadSurface*)getQuadSurface(xmlSettings); + CircleSurface * circleSurface = new CircleSurface(*base); + if(sourceName != "none" && source != 0){ + circleSurface->setSource(source); + } + surfaces->push_back(circleSurface); }else if(type == SurfaceType::GRID_WARP_SURFACE){ BaseSurface * gridWarpSurface = getGridWarpSurface(xmlSettings); if(sourceName != "none" && source != 0){ @@ -227,13 +234,13 @@ bool SettingsLoader::save(SurfaceManager & surfaceManager, string fileName){ xmlSettings->addValue("gridRows", gws->getGridRows()); xmlSettings->popTag(); } - + xmlSettings->popTag(); // surface } xmlSettings->popTag(); // surfaces - + } // for - + xmlSettings->save(fileName); } @@ -245,17 +252,17 @@ bool SettingsLoader::create(string fileName){ BaseSurface * SettingsLoader::getTriangleSurface(ofxXmlSettings * xmlSettings){ vector vertices; - + if(xmlSettings->tagExists("vertices")){ xmlSettings->pushTag("vertices"); - + if(xmlSettings->tagExists("vertex", 0)){ xmlSettings->pushTag("vertex", 0); vertices.push_back(ofVec2f(xmlSettings->getValue("x", 0.0f), xmlSettings->getValue("y", 0.0f))); xmlSettings->popTag(); } - + if(xmlSettings->tagExists("vertex", 1)){ xmlSettings->pushTag("vertex", 1); vertices.push_back(ofVec2f(xmlSettings->getValue("x", 100.0f), @@ -277,14 +284,14 @@ BaseSurface * SettingsLoader::getTriangleSurface(ofxXmlSettings * xmlSettings){ if(xmlSettings->tagExists("texCoords")){ xmlSettings->pushTag("texCoords"); - + if(xmlSettings->tagExists("texCoord", 0)){ xmlSettings->pushTag("texCoord", 0); texCoords.push_back(ofVec2f(xmlSettings->getValue("x", 0.0f), xmlSettings->getValue("y", 0.0f))); xmlSettings->popTag(); } - + if(xmlSettings->tagExists("texCoord", 1)){ xmlSettings->pushTag("texCoord", 1); texCoords.push_back(ofVec2f(xmlSettings->getValue("x", 1.0f), @@ -308,16 +315,16 @@ BaseSurface * SettingsLoader::getTriangleSurface(ofxXmlSettings * xmlSettings){ SurfaceType::TRIANGLE_SURFACE); triangleSurface->setVertices(vertices); triangleSurface->setTexCoords(texCoords); - + return triangleSurface; } BaseSurface * SettingsLoader::getQuadSurface(ofxXmlSettings * xmlSettings){ vector vertices; - + if(xmlSettings->tagExists("vertices")){ xmlSettings->pushTag("vertices"); - + if(xmlSettings->tagExists("vertex", 0)){ xmlSettings->pushTag("vertex", 0); vertices.push_back(ofVec2f(xmlSettings->getValue("x", 0.0f), @@ -348,7 +355,7 @@ BaseSurface * SettingsLoader::getQuadSurface(ofxXmlSettings * xmlSettings){ xmlSettings->popTag(); // vertices } - + vector texCoords; if(xmlSettings->tagExists("texCoords")){ @@ -360,7 +367,7 @@ BaseSurface * SettingsLoader::getQuadSurface(ofxXmlSettings * xmlSettings){ xmlSettings->getValue("y", 0.0f))); xmlSettings->popTag(); } - + if(xmlSettings->tagExists("texCoord", 1)){ xmlSettings->pushTag("texCoord", 1); texCoords.push_back(ofVec2f(xmlSettings->getValue("x", 1.0f), @@ -384,14 +391,14 @@ BaseSurface * SettingsLoader::getQuadSurface(ofxXmlSettings * xmlSettings){ xmlSettings->popTag(); // texCoords } - + // Create and add quad surface BaseSurface * quadSurface = SurfaceFactory::instance()->createSurface( SurfaceType::QUAD_SURFACE); quadSurface->setVertices(vertices); quadSurface->setTexCoords(texCoords); - + // Read properties // Only perspective warping for now bool perspectiveWarping = false; @@ -402,18 +409,18 @@ BaseSurface * SettingsLoader::getQuadSurface(ofxXmlSettings * xmlSettings){ } QuadSurface * qs = (QuadSurface *)quadSurface; qs->setPerspectiveWarping(perspectiveWarping); - + return quadSurface; } BaseSurface * SettingsLoader::getGridWarpSurface(ofxXmlSettings * xmlSettings){ vector vertices; - + if(xmlSettings->tagExists("vertices")){ xmlSettings->pushTag("vertices"); - + int iv = 0; - + while(xmlSettings->tagExists("vertex", iv)){ xmlSettings->pushTag("vertex", iv); vertices.push_back(ofVec2f(xmlSettings->getValue("x", 0.0f), @@ -424,14 +431,14 @@ BaseSurface * SettingsLoader::getGridWarpSurface(ofxXmlSettings * xmlSettings){ xmlSettings->popTag(); // vertices } - + vector texCoords; if(xmlSettings->tagExists("texCoords")){ xmlSettings->pushTag("texCoords"); int it = 0; - + while(xmlSettings->tagExists("texCoord", it)){ xmlSettings->pushTag("texCoord", it); texCoords.push_back(ofVec2f(xmlSettings->getValue("x", 0.0f), @@ -442,7 +449,7 @@ BaseSurface * SettingsLoader::getGridWarpSurface(ofxXmlSettings * xmlSettings){ xmlSettings->popTag(); // texCoords } - + // Read properties // Only perspective warping for now int gridCols = 0; @@ -453,7 +460,7 @@ BaseSurface * SettingsLoader::getGridWarpSurface(ofxXmlSettings * xmlSettings){ gridRows = xmlSettings->getValue("gridRows", 0); xmlSettings->popTag(); // properties } - + // Create and add quad surface BaseSurface * gridWarpSurface = SurfaceFactory::instance()->createSurface( @@ -463,16 +470,16 @@ BaseSurface * SettingsLoader::getGridWarpSurface(ofxXmlSettings * xmlSettings){ ((GridWarpSurface *)gridWarpSurface)->createGridMesh(); gridWarpSurface->setVertices(vertices); gridWarpSurface->setTexCoords(texCoords); - + return gridWarpSurface; } BaseSurface * SettingsLoader::getHexagonSurface(ofxXmlSettings * xmlSettings){ vector vertices; - + if(xmlSettings->tagExists("vertices")){ xmlSettings->pushTag("vertices"); - + unsigned int v = 0; while(xmlSettings->tagExists("vertex", v)){ xmlSettings->pushTag("vertex", v); @@ -489,7 +496,7 @@ BaseSurface * SettingsLoader::getHexagonSurface(ofxXmlSettings * xmlSettings){ if(xmlSettings->tagExists("texCoords")){ xmlSettings->pushTag("texCoords"); - + unsigned int t = 0; while(xmlSettings->tagExists("texCoord", t)){ xmlSettings->pushTag("texCoord", t); @@ -508,7 +515,7 @@ BaseSurface * SettingsLoader::getHexagonSurface(ofxXmlSettings * xmlSettings){ SurfaceType::HEXAGON_SURFACE); hexagonSurface->setVertices(vertices); hexagonSurface->setTexCoords(texCoords); - + return hexagonSurface; } diff --git a/src/Gui/Widgets/TextureEditorWidget.cpp b/src/Gui/Widgets/TextureEditorWidget.cpp index 6ce0a88..617c0a6 100644 --- a/src/Gui/Widgets/TextureEditorWidget.cpp +++ b/src/Gui/Widgets/TextureEditorWidget.cpp @@ -149,7 +149,8 @@ void TextureEditorWidget::createJoints(){ if(surface->getType() == SurfaceType::TRIANGLE_SURFACE){ tc = texCoords; - }else if(surface->getType() == SurfaceType::QUAD_SURFACE){ + }else if(surface->getType() == SurfaceType::QUAD_SURFACE || + surface->getType() == SurfaceType::CIRCLE_SURFACE){ tc = texCoords; }else if(surface->getType() == SurfaceType::HEXAGON_SURFACE){ tc = texCoords; diff --git a/src/Surfaces/CircleSurface.cpp b/src/Surfaces/CircleSurface.cpp new file mode 100644 index 0000000..95af0b2 --- /dev/null +++ b/src/Surfaces/CircleSurface.cpp @@ -0,0 +1,299 @@ +// +// CircleSurface.cpp +// Copyright (c) 2017 Cristobal Mendoza +// http://cuppetellimendoza.com + +#include "CircleSurface.h" + +namespace ofx { +namespace piMapper { + +CircleSurface::CircleSurface() : QuadSurface() { + setup(); +} + +CircleSurface::CircleSurface(QuadSurface &surface) { + setup(); + setVertices(surface.getVertices()); + setTexCoords(surface.getTexCoords()); + setPerspectiveWarping(surface.getPerspectiveWarping()); +} + +CircleSurface::~CircleSurface() {} + +void CircleSurface::setup() { + + setPerspectiveWarping(true); + QuadSurface::setup(); + + updateMask = true; +// maskIsReady = false; + + glESVertexShader = STRINGIFY( + attribute vec4 position; + attribute vec4 color; + attribute vec4 normal; + attribute vec2 texcoord; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform sampler2D maskTex; + + varying vec4 colorVarying; + varying vec2 texCoordVarying; + + void main() { + + //get our current vertex position so we can modify it + vec4 pos = projectionMatrix*modelViewMatrix*position; + + gl_Position = pos; + colorVarying = color; + texCoordVarying = texcoord; + } + ); + + glESFragmentShader = STRINGIFY( +//#ifdef GL_ES +// define default precision for float, vec, mat. + precision highp float; +//#endif + + uniform sampler2D tex0; + uniform sampler2D maskTex; + uniform vec4 globalColor; + + varying vec2 texCoordVarying; + + void main (void) + { + vec2 pos = texCoordVarying; + vec3 src = texture2D(tex0, pos).rgb; + float mask = texture2D(maskTex, pos).r; + gl_FragColor = vec4( src , mask); + } + ); + + gl2FragmentShader = "#version 120\n #extension GL_ARB_texture_rectangle : enable\n"; + gl2FragmentShader += STRINGIFY( + uniform sampler2DRect tex0; + uniform sampler2DRect maskTex; + + void main (void) { + vec2 pos = gl_TexCoord[0].st; + + vec3 src = texture2DRect(tex0, pos).rgb; + float mask = texture2DRect(maskTex, pos).r; + + gl_FragColor = vec4(src, mask); + } + ); + +#ifdef TARGET_OPENGLES + maskShader.setupShaderFromSource(GL_VERTEX_SHADER, glESVertexShader); + maskShader.setupShaderFromSource(GL_FRAGMENT_SHADER, glESFragmentShader); + maskShader.bindDefaults(); + maskShader.linkProgram(); +#else + if (ofIsGLProgrammableRenderer()) { + + } else { + maskShader.setupShaderFromSource(GL_FRAGMENT_SHADER, gl2FragmentShader); + maskShader.linkProgram(); + } +#endif + + ofVec2f t1 = ofVec2f(ofVec2f(0.0f, 0.0f)); + ofVec2f t2 = ofVec2f(ofVec2f(1.0f, 0.0f)); + ofVec2f t3 = ofVec2f(ofVec2f(1.0f, 1.0f)); + ofVec2f t4 = ofVec2f(ofVec2f(0.0f, 1.0f)); + + defaultTexCoords.push_back(t1); + defaultTexCoords.push_back(t2); + defaultTexCoords.push_back(t3); + defaultTexCoords.push_back(t4); +} + +void CircleSurface::draw() { + + ofEnableAlphaBlending(); + if (source->getTexture() == 0) + { + return; + } + if (!source->getTexture()->isAllocated()){ + return; + } + + if (source != currentSource) { // Pointer comparison + // Create the mask here + setupTextures(); + currentSource = source; + } + + + // Save Normie state: + auto isNorm = ofGetUsingNormalizedTexCoords(); + + // If we get to this part of the function, the mask fbo + // should already be allocated and the mask texture created. + + ofEnableNormalizedTexCoords(); + // Get the texture from the source an store a copy + // of the source texture's id: + auto sourceTex = ofTexture(*(source->getTexture())); + auto sourceTexId = sourceTex.getTextureData().textureID; + + // Get the quad surface's mesh texture coordinates: + auto quadTexCoords = getMesh().getTexCoords(); + + maskMesh.clearTexCoords(); + + // Set the mesh's texture coords to the quads. + // This gets us the coordinates set in the TextureEditor + maskMesh.addTexCoords(quadTexCoords); + + float w = outputFbo.getWidth(); + float h = outputFbo.getHeight(); + + // Draw the scaled texture into an FBO + scaledSourceFbo.begin(true); + { + ofClear(0, 0, 0, 255); + ofSetupScreenOrtho(w, h, -1, 1); + ofEnableNormalizedTexCoords(); + ofFill(); + ofSetColor(255); + sourceTex.bind(); + maskMesh.draw(); + sourceTex.unbind(); + } + scaledSourceFbo.end(); + + // Now draw the texture, masked, into the outputFbo. + // This enables us to use the TextureEditor to determine + // what part of the source will be masked: + maskMesh.clearTexCoords(); + + // Set default normalized texCoords + maskMesh.addTexCoords(defaultTexCoords); + + outputFbo.begin(true); + { + ofClear(0, 0, 0, 0); + ofSetupScreenOrtho(w, h, -1, 1); + ofEnableNormalizedTexCoords(); + maskShader.begin(); + maskShader.setUniformTexture("maskTex", maskFbo.getTexture(), 1); + ofSetColor(255); + ofFill(); + ofSetRectMode(OF_RECTMODE_CORNER); + scaledSourceFbo.getTexture().bind(); + maskMesh.draw(); + scaledSourceFbo.getTexture().unbind(); + maskShader.end(); + } + outputFbo.end(); + + getMesh().clearTexCoords(); + getMesh().addTexCoords(defaultTexCoords); + + // Swap the texture id of the source with the one of our + // newly drawn outputFbo: + source->getTexture()->getTextureData().textureID = outputFbo.getTexture().getTextureData().textureID; + + // Draw the Quad: + QuadSurface::draw(); + + // Reset the texture id of the source + source->getTexture()->getTextureData().textureID = sourceTexId; + + // Reset the texture coords of the QuadSurface mesh: + getMesh().clearTexCoords(); + getMesh().addTexCoords(quadTexCoords); + + if (!isNorm) ofDisableNormalizedTexCoords(); +} + +void CircleSurface::setFeathering(float f) { + feathering = f; + updateMask = true; +} + +void CircleSurface::setupTextures() { + float w = source->getTexture()->getWidth(); + float h = source->getTexture()->getHeight(); + float dia = 0; + if (w > h) { + dia = h; + } else { + dia = w; + } + + float padding = 10; + + maskFbo.allocate(w, h); + maskFbo.begin(false); + ofPushStyle(); + ofSetupScreenOrtho(w, h, -1, 1); + ofClear(0, 0, 0, 255); + ofFill(); + ofSetColor(255); + ofSetCircleResolution(300); + ofDrawEllipse(w/2, h/2, w-padding, h-padding); + ofPopStyle(); + maskFbo.end(); + + outputFbo.allocate(w, h); + outputFbo.begin(); + ofClear(0, 0, 0, 255); + outputFbo.end(); + + scaledSourceFbo.allocate(w, h); + scaledSourceFbo.begin(); + ofClear(0, 0, 0, 255); + scaledSourceFbo.end(); + + // This is lifted from QuadSurface::setup to ensure that the two + // meshes are similar: + + // Create 4 points for the 2 triangles + ofVec2f p1 = ofVec2f(0, 0); + ofVec2f p2 = ofVec2f(0, h); + ofVec2f p3 = ofVec2f(w, h); + ofVec2f p4 = ofVec2f(w, 0); + + // Create 4 point for the texture coordinates + ofVec2f t1 = ofVec2f(ofVec2f(0.0f, 0.0f)); + ofVec2f t2 = ofVec2f(ofVec2f(1.0f, 0.0f)); + ofVec2f t3 = ofVec2f(ofVec2f(1.0f, 1.0f)); + ofVec2f t4 = ofVec2f(ofVec2f(0.0f, 1.0f)); + + // Clear maskMesh + maskMesh.clear(); + + // Create a surface with the points + maskMesh.addVertex(p1); + maskMesh.addVertex(p2); + maskMesh.addVertex(p3); + maskMesh.addVertex(p4); + + // Add 2 triangles + maskMesh.addTriangle(0, 2, 3); + maskMesh.addTriangle(0, 1, 2); + + // Add texture coordinates + maskMesh.addTexCoord(t1); + maskMesh.addTexCoord(t2); + maskMesh.addTexCoord(t3); + maskMesh.addTexCoord(t4); +} + + + +int CircleSurface::getType() { + return SurfaceType::CIRCLE_SURFACE; +} + +} +} \ No newline at end of file diff --git a/src/Surfaces/CircleSurface.h b/src/Surfaces/CircleSurface.h new file mode 100644 index 0000000..262736d --- /dev/null +++ b/src/Surfaces/CircleSurface.h @@ -0,0 +1,63 @@ +// +// CircleSurface.h +// Copyright (c) 2017 Cristobal Mendoza +// http://cuppetellimendoza.com + +#ifndef OFXPIMAPPER_CIRCLESURFACE_H +#define OFXPIMAPPER_CIRCLESURFACE_H + +#include "QuadSurface.h" + +#define STRINGIFY(A) #A + +namespace ofx { +namespace piMapper { + +class CircleSurface : public QuadSurface { + public: + CircleSurface(); + CircleSurface(QuadSurface &surface); + int getType() override; + ~CircleSurface(); + void draw() override; + void setup() override; + + // TODO: Feathering + void setFeathering(float f); + + protected: + void setupTextures(); + ofFbo maskFbo; + ofFbo scaledSourceFbo; + ofFbo outputFbo; + ofShader maskShader; + ofShader gradientShader; + float feathering = 0.0f; + bool updateMask; + bool maskIsReady; + + string glESFragmentShader; + string glESVertexShader; + + string gl2FragmentShader; + string gl2VertexShader; + + ofMesh maskMesh; + + // TODO: gl3 Shaders +// string gl3VertexShader; +// string gl3FragmentShader; + + private: + std::vector defaultTexCoords; + // We will use this pointer to determine if the source has changed. + // This is a total kludge, but it keeps me from messing with the + // upstream source. + BaseSource* currentSource = 0; +}; + +} +} + + +#endif //OFXPIMAPPER_CIRCLESURFACE_H diff --git a/src/Surfaces/SurfaceFactory.cpp b/src/Surfaces/SurfaceFactory.cpp index 1adbba2..b85ae01 100644 --- a/src/Surfaces/SurfaceFactory.cpp +++ b/src/Surfaces/SurfaceFactory.cpp @@ -21,6 +21,8 @@ BaseSurface * SurfaceFactory::createSurface(SurfaceType type){ return createGridWarpSurface(); }else if(type == SurfaceType::HEXAGON_SURFACE){ return createHexagonSurface(); + }else if(type == SurfaceType::CIRCLE_SURFACE){ + return createCircleSurface(); }else{ throw runtime_error("Undefined surface type"); } @@ -83,5 +85,10 @@ HexagonSurface * SurfaceFactory::createHexagonSurface(){ return hexagonSurface; } +CircleSurface * SurfaceFactory::createCircleSurface() { + CircleSurface * circleSurface = new CircleSurface(); + return circleSurface; +} + } // namespace piMapper } // namespace ofx diff --git a/src/Surfaces/SurfaceFactory.h b/src/Surfaces/SurfaceFactory.h index 90b2827..14cd012 100644 --- a/src/Surfaces/SurfaceFactory.h +++ b/src/Surfaces/SurfaceFactory.h @@ -7,6 +7,7 @@ #include "QuadSurface.h" #include "GridWarpSurface.h" #include "HexagonSurface.h" +#include "CircleSurface.h" namespace ofx { namespace piMapper { @@ -26,6 +27,7 @@ class SurfaceFactory { QuadSurface * createQuadSurface(); GridWarpSurface * createGridWarpSurface(); HexagonSurface * createHexagonSurface(); + CircleSurface* createCircleSurface(); }; } // namespace piMapper diff --git a/src/Surfaces/SurfaceType.h b/src/Surfaces/SurfaceType.h index ae52270..b039427 100644 --- a/src/Surfaces/SurfaceType.h +++ b/src/Surfaces/SurfaceType.h @@ -8,7 +8,8 @@ enum SurfaceType{ QUAD_SURFACE, GRID_WARP_SURFACE, HEXAGON_SURFACE, - NONE + NONE, + CIRCLE_SURFACE }; } // namespace piMapper