ofVbo#drawXxxの実装

# ofVbo#drawXxxの実装
シェーダ周りで気になることがいくつかあったので調べてみました。
基準となるパスは `openframeworks/libs/openframeworks` です。

* どういう場合にデフォルトシェーダが設定されるかを確認します。
* シェーダにデフォルトで設定される変数を確認します。

# バージョン
of_v0.10.1_vs2017_release

# 調査内容
依存関係の浅いところから並べています。  

## ./gl/ofVbo.cpp

drawXxx()  
ofGetGLRenderer() を読んでいく

````cpp

//--------------------------------------------------------------
void ofVbo::draw(int drawMode, int first, int total) const{
	ofGetGLRenderer()->draw(*this,drawMode,first,total);
}

//--------------------------------------------------------------
void ofVbo::drawElements(int drawMode, int amt, int offsetelements) const{
	ofGetGLRenderer()->drawElements(*this,drawMode,amt,offsetelements);
}

//--------------------------------------------------------------
void ofVbo::drawInstanced(int drawMode, int first, int total, int primCount) const{
	ofGetGLRenderer()->drawInstanced(*this,drawMode,first,total,primCount);
}

//--------------------------------------------------------------
void ofVbo::drawElementsInstanced(int drawMode, int amt, int primCount) const{
	ofGetGLRenderer()->drawElementsInstanced(*this,drawMode,amt,primCount);
}

````

## ./gl/ofGLUtils.cpp

ofGetGLRenderer()  
環境によってレンダラを切り替えられる模様  
プログラマブルシェーダ`でないときに`上の実装が使われる

````cpp
#ifndef TARGET_PROGRAMMABLE_GL
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLRenderer::TYPE || ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLRenderer>();
	}
}
#else
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLProgrammableRenderer>();
	}
}
#endif
````

## ./utils/ofConstants.h

筆者はWindows10を使っているので TARGET_WIN32 だけが定義される。
なので、先程のメソッドは上のものが使用される。

````cpp
// 		helpful:
// 		http://www.ogre3d.org/docs/api/html/OgrePlatform_8h-source.html

#if defined( __WIN32__ ) || defined( _WIN32 )
	#define TARGET_WIN32
#elif defined( __APPLE_CC__)
    #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
    #include <TargetConditionals.h>
	#if (TARGET_OS_IPHONE || TARGET_OS_IOS || TARGET_OS_SIMULATOR || TARGET_OS_IPHONE_SIMULATOR) && !TARGET_OS_TV && !TARGET_OS_WATCH
        #define TARGET_OF_IPHONE
        #define TARGET_OF_IOS
        #define TARGET_OPENGLES
        #include <unistd.h>
    #elif TARGET_OS_TV
        #define TARGET_OF_IOS
        #define TARGET_OF_TVOS
        #define TARGET_OPENGLES
        #include <unistd.h>
    #elif TARGET_OS_WATCH
        #define TARGET_OF_IOS
        #define TARGET_OF_WATCHOS
        #define TARGET_OPENGLES
        #include <unistd.h>
	#else
		#define TARGET_OSX
	#endif
#elif defined (__ANDROID__)
	#define TARGET_ANDROID
	#define TARGET_OPENGLES
#elif defined(__ARMEL__)
	#define TARGET_LINUX
	#define TARGET_OPENGLES
	#define TARGET_LINUX_ARM
#elif defined(__EMSCRIPTEN__)
	#define TARGET_EMSCRIPTEN
	#define TARGET_OPENGLES
	#define TARGET_NO_THREADS
	#define TARGET_PROGRAMMABLE_GL
	#define TARGET_IMPLEMENTS_URL_LOADER
#else
	#define TARGET_LINUX
#endif
````

## ./gl/ofGLUtils.cpp(再掲)

ofGetGLRenderer()  
ofGetCurrentRenderer() を読んでいく

````cpp
#ifndef TARGET_PROGRAMMABLE_GL
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLRenderer::TYPE || ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLRenderer>();
	}
}
#else
...

中略

...
#endif
````

## ./app/ofAppRunner.cpp

ofGetCurrentRenderer()  
getCurrentWindow() を読んでいく

````cpp
//--------------------------------------
shared_ptr<ofBaseRenderer> & ofGetCurrentRenderer(){
	return mainLoop()->getCurrentWindow()->renderer();
}
````

## ./app/ofAppRunner.cpp

mainLoop()  
メインループを生成して返す。
staticとついているので一度しか生成されない。

````cpp
//--------------------------------------
namespace{
    shared_ptr<ofMainLoop> & mainLoop(){
        static shared_ptr<ofMainLoop> * mainLoop(new shared_ptr<ofMainLoop>(new ofMainLoop));
        return *mainLoop;
    }

中略
}
````

## ./app/ofMainLoop.cpp

getCurrentWindow()  
ofMainLoopのコンストラクタでは`currentWindow`を設定していないので、setしているところを探す。

````cpp
shared_ptr<ofAppBaseWindow> ofMainLoop::getCurrentWindow(){
	return currentWindow.lock();
}
````

## ./app/ofMainLoop.cpp

setCurrentWindow()

````cpp

void ofMainLoop::setCurrentWindow(shared_ptr<ofAppBaseWindow> window){
	currentWindow = window;
}

void ofMainLoop::setCurrentWindow(ofAppBaseWindow * window){
	if(currentWindow.lock().get() == window){
		return;
	}
	for(auto i: windowsApps){
		if(i.first.get() == window){
			currentWindow = i.first;
			break;
		}
	}
}
````

## ./app/ofAppGLFWWindow.cpp

setCurrent()  
setCurrentWindow() を読んでいるのはここだけ。
実は`ofAppBaseGLWindow`のサブクラスには`ofAppGlutWindow`というのもあるのですが、
こちらでは setCurrentWindow() を読んでいないみたいです。
[こちら](https://openframeworks.cc/documentation/application/ofAppGLFWWindow/)のリンク曰く、GLUTからGLFWに移行したとのことなのでこれであっているのでしょうか
であるなら、先程の`mainLoop()->getCurrentWindow()->renderer()`は`ofAppGLFWWindow`の renderer() が呼ばれているはず。

````cpp
//------------------------------------------------------------
ofAppGLFWWindow * ofAppGLFWWindow::setCurrent(GLFWwindow* windowP){
	ofAppGLFWWindow * instance = static_cast<ofAppGLFWWindow *>(glfwGetWindowUserPointer(windowP));
	shared_ptr<ofMainLoop> mainLoop = ofGetMainLoop();
	if(mainLoop){
		mainLoop->setCurrentWindow(instance);
	}
	instance->makeCurrent();
	return instance;
}
````

## ./app/ofAppGLFWWindow

renderer()  
`currentRenderer`は setup() で初期化されます。

````cpp
//--------------------------------------------
shared_ptr<ofBaseRenderer> & ofAppGLFWWindow::renderer(){
	return currentRenderer;
}
````

setup()  
実際にはかなり長いメソッドですが、`currentRenderer`を初期化してる部分に絞って抜粋。
GLESか否かで分岐しますが、どちらも`ofGLProgrammableRenderer`か`ofGLRenderer`を選択するようです。
`TARGET_OPENGLES` は  `./utils/ofConstants.h` でも登場していますが、モバイル端末などで選択されるフラグのようです。
また、筆者の環境ではOpenGLのメジャーバージョンは4なので`ofGLProgrammableRenderer`へ進みます。

````cpp

void ofAppGLFWWindow::setup(const ofGLFWWindowSettings & _settings){
	...
	中略
	...
	glfwWindowHint(GLFW_SAMPLES, settings.numSamples);
	glfwWindowHint(GLFW_RESIZABLE, settings.resizable);
	glfwWindowHint(GLFW_DECORATED, settings.decorated);
    #ifdef TARGET_OPENGLES
	    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, settings.glesVersion);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
		glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
		if(settings.glesVersion>=2){
			currentRenderer = std::make_shared<ofGLProgrammableRenderer>(this);
		}else{
			currentRenderer = std::make_shared<ofGLRenderer>(this);
		}
    #else
	    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, settings.glVersionMajor);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, settings.glVersionMinor);
		if((settings.glVersionMajor==3 && settings.glVersionMinor>=2) || settings.glVersionMajor>=4){
			glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
		}
		if(settings.glVersionMajor>=3){
			glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
			currentRenderer = std::make_shared<ofGLProgrammableRenderer>(this);
		}else{
			currentRenderer = std::make_shared<ofGLRenderer>(this);
		}
    #endif
		...
		中略
		...
#endif
}

````

## ./gl/ofGLUtils.cpp(再掲)

ofGetGLRenderer()  
ofGetCurrentRenderer() が `ofGLProgrammableRenderer` を返すことがわかったので、
getType() の実装へ進みます。

````cpp
#ifndef TARGET_PROGRAMMABLE_GL
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLRenderer::TYPE || ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLRenderer>();
	}
}
#else
...

中略

...
#endif
````

## ./gl/ofGLProgrammableRenderer.h

文字列を返すようです。

````h
...
中略
...
    static const std::string TYPE;
	const std::string & getType(){ return TYPE; }

...
中略
... 
````

## ./gl/ofGLProgrammableRenderer.cpp

ということで、
`ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE`は true を返します。

````cpp
const string ofGLProgrammableRenderer::TYPE="ProgrammableGL";
````

## ./gl/ofGLUtils.cpp(再掲)

よって、呼び出されるのはこれです。
戻り値は `ofGLProgrammableRenderer`

````cpp
return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
````

## ./gl/ofGLProgrammableRenderer.cpp

draw()  
なので、ofVbo#draw はこれを呼んでいる。

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::draw(const ofVbo & vbo, GLuint drawMode, int first, int total) const{
	if(vbo.getUsingVerts()) {
		vbo.bind();
		const_cast<ofGLProgrammableRenderer*>(this)->setAttributes(vbo.getUsingVerts(),vbo.getUsingColors(),vbo.getUsingTexCoords(),vbo.getUsingNormals());
		glDrawArrays(drawMode, first, total);
		vbo.unbind();
	}
}
````

setAttributes()

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::setAttributes(bool vertices, bool color, bool tex, bool normals){
	bool wasColorsEnabled = colorsEnabled;
	bool wasUsingTexture = texCoordsEnabled & (currentTextureTarget!=OF_NO_TEXTURE);

	texCoordsEnabled = tex;
	colorsEnabled = color;
	normalsEnabled = normals;

	if(!uniqueShader || currentMaterial){
		beginDefaultShader();
	}

	bool usingTexture = tex & (currentTextureTarget!=OF_NO_TEXTURE);
	if(wasUsingTexture!=usingTexture){
		if(currentShader) currentShader->setUniform1f(USE_TEXTURE_UNIFORM,usingTexture);
	}
	if(wasColorsEnabled!=color){
		if(currentShader) currentShader->setUniform1f(USE_COLORS_UNIFORM,color);
	}
}
````

beginDefaultShader()  
ここでデフォルトで適用するシェーダを決める。
oFのデフォルトの Xxx#draw ではこのシェーダが使われていると思われる。
`ofGetCurrentRenderer()->draw()`で検索すると、具体的にどのクラスで使われているかわかる。
※ `ofGetCurrentRenderer() == ofGetGLRenderer()`であることはすでに分かっているため

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::beginDefaultShader(){
	if(usingCustomShader && !currentMaterial)	return;

	const ofShader * nextShader = nullptr;

	if(!uniqueShader || currentMaterial){
        if(currentMaterial){
            nextShader = &currentMaterial->getShader(currentTextureTarget,colorsEnabled,*this);

		}else if(bitmapStringEnabled){
			nextShader = &bitmapStringShader;

		}else if(colorsEnabled && texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexColor;
				break;
	#endif
			}

		}else if(colorsEnabled){
			nextShader = &defaultNoTexColor;

		}else if(texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectNoColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DNoColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexNoColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexNoColor;
				break;
	#endif
			}

		}else{
			nextShader = &defaultNoTexNoColor;
		}

	}else{
		nextShader = &defaultUniqueShader;
	}

	if(nextShader){
        if(!currentShader || *currentShader!=*nextShader){
			settingDefaultShader = true;
			bind(*nextShader);
			settingDefaultShader = false;
		}
	}
}
````


setAttributes()(再掲)  
以下の条件を`満たさなければ`カスタムしたシェーダを使えることがわかる。

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::setAttributes(bool vertices, bool color, bool tex, bool normals){
	...
	中略
	...

	if(!uniqueShader || currentMaterial){
		beginDefaultShader();
	}
	...
	中略
	...
}
````

## ./app/ofGLProgrammableRenderer.cpp

コンストラクタではfalseに設定されており、uniqueShader が trueになるのはここだけ。
ということは、ラズパイ環境でないかぎり必ず beginDefaultShader() は呼ばれてしまう。

````cpp
#ifdef TARGET_RASPBERRY_PI
	uniqueShader = true;
#else
	uniqueShader = false;
#endif
````

beginDefaultShader()(再掲)  
* `カスタムシェーダが使われている` || `マテリアルがある` ときになにもしないことがわかる。
* `次のシェーダがある` && `現在のシェーダがある` && `現在のシェーダと次のシェーダが違う`ときにデフォルトシェーダがバインドされることがわかる。

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::beginDefaultShader(){
	if(usingCustomShader && !currentMaterial)	return;

	const ofShader * nextShader = nullptr;

	if(!uniqueShader || currentMaterial){
        if(currentMaterial){
            nextShader = &currentMaterial->getShader(currentTextureTarget,colorsEnabled,*this);

		}else if(bitmapStringEnabled){
			nextShader = &bitmapStringShader;

		}else if(colorsEnabled && texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexColor;
				break;
	#endif
			}

		}else if(colorsEnabled){
			nextShader = &defaultNoTexColor;

		}else if(texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectNoColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DNoColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexNoColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexNoColor;
				break;
	#endif
			}

		}else{
			nextShader = &defaultNoTexNoColor;
		}

	}else{
		nextShader = &defaultUniqueShader;
	}

	if(nextShader){
        if(!currentShader || *currentShader!=*nextShader){
			settingDefaultShader = true;
			bind(*nextShader);
			settingDefaultShader = false;
		}
	}
}
````

bind()  
`usingCustomShader` を true にしているのはここだけ。

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::bind(const ofShader & shader){
    if(currentShader && *currentShader==shader){
		return;
    }
	glUseProgram(shader.getProgram());

	currentShader = &shader;
	uploadMatrices();
	setDefaultUniforms();
	if(!settingDefaultShader){
		usingCustomShader = true;
	}
}
````

## ./gl/ofShader.cpp

begin()  
そして、ofShader#beginではこれを呼んでいるので
ちゃんとそのように書いていればシェーダは切り替わるはず。
ただし、`glUseProgram(myShader);` のあとに `ofObject#draw` とした場合には上書きされてしまう可能性がある。

````cpp
//--------------------------------------------------------------
void ofShader::begin()  const{
	ofGetGLRenderer()->bind(*this);
}

````

## ./app/ofGLProgrammableRenderer.cpp

uploadMatrices(), setDefaultUniforms()  
これは `ofShader::bind` から呼ばれる。
デフォルトで定義されるシェーダ変数にどんな値が入っているかがわかる。  

````cpp
//----------------------------------------------------------
void ofGLProgrammableRenderer::uploadMatrices(){
	if(!currentShader) return;
	currentShader->setUniformMatrix4f(MODEL_MATRIX_UNIFORM, matrixStack.getModelMatrix());
	currentShader->setUniformMatrix4f(VIEW_MATRIX_UNIFORM, matrixStack.getViewMatrix());
	currentShader->setUniformMatrix4f(MODELVIEW_MATRIX_UNIFORM, matrixStack.getModelViewMatrix());
	currentShader->setUniformMatrix4f(PROJECTION_MATRIX_UNIFORM, matrixStack.getProjectionMatrix());
	currentShader->setUniformMatrix4f(TEXTURE_MATRIX_UNIFORM, matrixStack.getTextureMatrix());
	currentShader->setUniformMatrix4f(MODELVIEW_PROJECTION_MATRIX_UNIFORM, matrixStack.getModelViewProjectionMatrix());
	if(currentMaterial){
		currentMaterial->uploadMatrices(*currentShader,*this);
	}
}

//----------------------------------------------------------
void ofGLProgrammableRenderer::setDefaultUniforms(){
	if(!currentShader) return;
	currentShader->setUniform4f(COLOR_UNIFORM, currentStyle.color.r/255.,currentStyle.color.g/255.,currentStyle.color.b/255.,currentStyle.color.a/255.);
	bool usingTexture = texCoordsEnabled & (currentTextureTarget!=OF_NO_TEXTURE);
	currentShader->setUniform1f(USE_TEXTURE_UNIFORM,usingTexture);
	currentShader->setUniform1f(USE_COLORS_UNIFORM,colorsEnabled);
	if(currentMaterial){
		currentMaterial->updateMaterial(*currentShader,*this);
		currentMaterial->updateLights(*currentShader,*this);
	}
}

````

## ./gl/ofShader.cpp
setUniformMatrix4f()

デフォルトの変数が見つからなかった場合にはちゃんと何もしないようになっている。
(他のsetUniformXxxも同様)

````cpp
//--------------------------------------------------------------
void ofShader::setUniformMatrix4f(const string & name, const glm::mat4 & m, int count) const{
	if(bLoaded) {
		int loc = getUniformLocation(name);
		if (loc != -1) glUniformMatrix4fv(loc, count, GL_FALSE, glm::value_ptr(m));
	}
}

````

# ライセンス
ここで転載したソースコードはMITライセンスで公開されています。  
[全文](https://github.com/openframeworks/openFrameworks/blob/master/LICENSE.md)