<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2006 Torus Knot Software Ltd
Also see acknowledgements in Readme.html

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.

You may alternatively use this source under the terms of a specific version of
the OGRE Unrestricted License provided you have obtained such a license from
Torus Knot Software Ltd.
-----------------------------------------------------------------------------
*/
#include "OgreStableHeaders.h"
#include "OgreCompositorChain.h"
#include "OgreCompositionTechnique.h"
#include "OgreCompositorInstance.h"
#include "OgreCompositionTargetPass.h"
#include "OgreCompositionPass.h"
#include "OgreViewport.h"
#include "OgreCamera.h"
#include "OgreRenderTarget.h"
#include "OgreLogManager.h"
#include "OgreCompositorManager.h"
#include "OgreSceneManager.h"
#include "OgreRenderQueueInvocation.h"
namespace Ogre {
CompositorChain::CompositorChain(Viewport *vp):
    mViewport(vp),
	mOriginalScene(0),
    mDirty(true),
	mAnyCompositorsEnabled(false)
{
	mOldClearEveryFrameBuffers = mViewport->getClearBuffers();
    assert(mViewport);
}
//-----------------------------------------------------------------------
CompositorChain::~CompositorChain()
{
	destroyResources();
}
//-----------------------------------------------------------------------
void CompositorChain::destroyResources(void)
{
	clearCompiledState();

	if (mViewport)
	{
		removeAllCompositors();
		/// Destroy "original scene" compositor instance
		if (mOriginalScene)
		{
			mViewport->getTarget()->removeListener(this);
			mOriginalScene->getTechnique()->destroyInstance(mOriginalScene);
			mOriginalScene = 0;
		}
		mViewport = 0;
	}
}
//-----------------------------------------------------------------------
CompositorInstance* CompositorChain::addCompositor(CompositorPtr filter, size_t addPosition, size_t technique)
{
	// Init on demand
	if (!mOriginalScene)
	{
		mViewport->getTarget()->addListener(this);

		/// Create base "original scene" compositor
		CompositorPtr base = CompositorManager::getSingleton().load("Ogre/Scene",
			ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
		mOriginalScene = base->getSupportedTechnique(0)->createInstance(this);
	}

	filter->touch();
    if(technique >= filter->getNumSupportedTechniques())
    {
        /// Warn user
        LogManager::getSingleton().logMessage(
            "CompositorChain: Compositor " + filter->getName() + " has no supported techniques.", LML_CRITICAL
        );
        return 0;
    }
    CompositionTechnique *tech = filter->getSupportedTechnique(technique);
    CompositorInstance *t = tech->createInstance(this);

    if(addPosition == LAST)
        addPosition = mInstances.size();
    else
        assert(addPosition <= mInstances.size() && "Index out of bounds.");
    mInstances.insert(mInstances.begin()+addPosition, t);

    mDirty = true;
	mAnyCompositorsEnabled = true;
    return t;
}
//-----------------------------------------------------------------------
void CompositorChain::removeCompositor(size_t index)
{
    assert (index < mInstances.size() && "Index out of bounds.");
    Instances::iterator i = mInstances.begin() + index;
    (*i)->getTechnique()->destroyInstance(*i);
    mInstances.erase(i);

    mDirty = true;
}
//-----------------------------------------------------------------------
size_t CompositorChain::getNumCompositors()
{
    return mInstances.size();
}
//-----------------------------------------------------------------------
void CompositorChain::removeAllCompositors()
{
    Instances::iterator i, iend;
    iend = mInstances.end();
    for (i = mInstances.begin(); i != iend; ++i)
    {
        (*i)->getTechnique()->destroyInstance(*i);
    }
    mInstances.clear();

    mDirty = true;
}
//-----------------------------------------------------------------------
void CompositorChain::_removeInstance(CompositorInstance *i)
{
	mInstances.erase(std::find(mInstances.begin(), mInstances.end(), i));
	i->getTechnique()->destroyInstance(i);
}
//-----------------------------------------------------------------------
void CompositorChain::_queuedOperation(CompositorInstance::RenderSystemOperation* op)
{
	mRenderSystemOperations.push_back(op);

}
//-----------------------------------------------------------------------
CompositorInstance *CompositorChain::getCompositor(size_t index)
{
    assert (index < mInstances.size() && "Index out of bounds.");
    return mInstances[index];
}

//-----------------------------------------------------------------------
CompositorChain::InstanceIterator CompositorChain::getCompositors()
{
    return InstanceIterator(mInstances.begin(), mInstances.end());
}
//-----------------------------------------------------------------------
void CompositorChain::setCompositorEnabled(size_t position, bool state)
{
    getCompositor(position)->setEnabled(state);
}
//-----------------------------------------------------------------------
void CompositorChain::preRenderTargetUpdate(const RenderTargetEvent& evt)
{
	/// Compile if state is dirty
	if(mDirty)
		_compile();

	// Do nothing if no compositors enabled
	if (!mAnyCompositorsEnabled)
	{
		return;
	}

	/// Update dependent render targets; this is done in the preRenderTarget
	/// and not the preViewportUpdate for a reason: at this time, the
	/// target Rendertarget will not yet have been set as current.
	/// ( RenderSystem::setViewport(...) ) if it would have been, the rendering
	/// order would be screwed up and problems would arise with copying rendertextures.
    Camera *cam = mViewport->getCamera();
    /// Iterate over compiled state
    CompositorInstance::CompiledState::iterator i;
    for(i=mCompiledState.begin(); i!=mCompiledState.end(); ++i)
    {
		/// Skip if this is a target that should only be initialised initially
		if(i->onlyInitial && i->hasBeenRendered)
			continue;
		i->hasBeenRendered = true;
		/// Setup and render
        preTargetOperation(*i, i->target->getViewport(0), cam);
        i->target->update();
        postTargetOperation(*i, i->target->getViewport(0), cam);
    }
}
//-----------------------------------------------------------------------
void CompositorChain::preViewportUpdate(const RenderTargetViewportEvent& evt)
{
	// Only set up if there is at least one compositor enabled, and it's this viewport
    if(evt.source != mViewport || !mAnyCompositorsEnabled)
        return;

	// set original scene details from viewport
	CompositionPass* pass = mOriginalScene->getTechnique()->getOutputTargetPass()->getPass(0);
	CompositionTargetPass* passParent = pass->getParent();
	if (pass->getClearBuffers() != mViewport->getClearBuffers() ||
		pass->getClearColour() != mViewport->getBackgroundColour() ||
		passParent->getVisibilityMask() != mViewport->getVisibilityMask() ||
		passParent->getMaterialScheme() != mViewport->getMaterialScheme() ||
		passParent->getShadowsEnabled() != mViewport->getShadowsEnabled())
	{
		// recompile if viewport settings are different
		pass->setClearBuffers(mViewport->getClearBuffers());
		pass->setClearColour(mViewport->getBackgroundColour());
		passParent->setVisibilityMask(mViewport->getVisibilityMask());
		passParent->setMaterialScheme(mViewport->getMaterialScheme());
		passParent->setShadowsEnabled(mViewport->getShadowsEnabled());
		_compile();
	}

	Camera *cam = mViewport->getCamera();
	/// Prepare for output operation
	preTargetOperation(mOutputOperation, mViewport, cam);
}
//-----------------------------------------------------------------------
void CompositorChain::preTargetOperation(CompositorInstance::TargetOperation &op, Viewport *vp, Camera *cam)
{
    SceneManager *sm = cam->getSceneManager();
	/// Set up render target listener
	mOurListener.setOperation(&op, sm, sm->getDestinationRenderSystem());
	mOurListener.notifyViewport(vp);
	/// Register it
	sm->addRenderQueueListener(&mOurListener);
	/// Set visiblity mask
	mOldVisibilityMask = sm->getVisibilityMask();
	sm->setVisibilityMask(op.visibilityMask);
	/// Set whether we find visibles
	mOldFindVisibleObjects = sm->getFindVisibleObjects();
	sm->setFindVisibleObjects(op.findVisibleObjects);
    /// Set LOD bias level
    mOldLodBias = cam->getLodBias();
    cam->setLodBias(cam->getLodBias() * op.lodBias);
	/// Set material scheme
	mOldMaterialScheme = vp->getMaterialScheme();
	vp->setMaterialScheme(op.materialScheme);
	/// Set shadows enabled
	mOldShadowsEnabled = vp->getShadowsEnabled();
	vp->setShadowsEnabled(op.shadowsEnabled);
    /// XXX TODO
    //vp->setClearEveryFrame( true );
    //vp->setOverlaysEnabled( false );
    //vp->setBackgroundColour( op.clearColour );
}
//-----------------------------------------------------------------------
void CompositorChain::postTargetOperation(CompositorInstance::TargetOperation &op, Viewport *vp, Camera *cam)
{
    SceneManager *sm = cam->getSceneManager();
	/// Unregister our listener
	sm->removeRenderQueueListener(&mOurListener);
	/// Restore default scene and camera settings
	sm->setVisibilityMask(mOldVisibilityMask);
	sm->setFindVisibleObjects(mOldFindVisibleObjects);
    cam->setLodBias(mOldLodBias);
	vp->setMaterialScheme(mOldMaterialScheme);
	vp->setShadowsEnabled(mOldShadowsEnabled);
}
//-----------------------------------------------------------------------
void CompositorChain::postViewportUpdate(const RenderTargetViewportEvent& evt)
{
	// Only tidy up if there is at least one compositor enabled, and it's this viewport
    if(evt.source != mViewport || !mAnyCompositorsEnabled)
        return;

	postTargetOperation(mOutputOperation, mViewport, mViewport->getCamera());
}
//-----------------------------------------------------------------------
void CompositorChain::viewportRemoved(const RenderTargetViewportEvent& evt)
{
	// check this is the viewport we're attached to (multi-viewport targets)
	if (evt.source == mViewport)
	{
		// this chain is now orphaned
		// can't delete it since held from outside, but release all resources being used
		destroyResources();
	}

}
//-----------------------------------------------------------------------
void CompositorChain::clearCompiledState()
{
	for (RenderSystemOperations::iterator i = mRenderSystemOperations.begin();
		i != mRenderSystemOperations.end(); ++i)
	{
		OGRE_DELETE *i;
	}
	mRenderSystemOperations.clear();

	/// Clear compiled state
	mCompiledState.clear();
	mOutputOperation = CompositorInstance::TargetOperation(0);

}
//-----------------------------------------------------------------------
void CompositorChain::_compile()
{
	clearCompiledState();

	bool compositorsEnabled = false;

    /// Set previous CompositorInstance for each compositor in the list
    CompositorInstance *lastComposition = mOriginalScene;
	mOriginalScene->mPreviousInstance = 0;
	CompositionPass* pass = mOriginalScene->getTechnique()->getOutputTargetPass()->getPass(0);
	pass->setClearBuffers(mViewport->getClearBuffers());
	pass->setClearColour(mViewport->getBackgroundColour());
    for(Instances::iterator i=mInstances.begin(); i!=mInstances.end(); ++i)
    {
        if((*i)->getEnabled())
        {
			compositorsEnabled = true;
            (*i)->mPreviousInstance = lastComposition;
            lastComposition = (*i);
        }
    }

    /// Compile misc targets
    lastComposition->_compileTargetOperations(mCompiledState);

    /// Final target viewport (0)
	mOutputOperation.renderSystemOperations.clear();
    lastComposition->_compileOutputOperation(mOutputOperation);

	// Deal with viewport settings
	if (compositorsEnabled != mAnyCompositorsEnabled)
	{
		mAnyCompositorsEnabled = compositorsEnabled;
		if (mAnyCompositorsEnabled)
		{
			// Save old viewport clearing options
			mOldClearEveryFrameBuffers = mViewport->getClearBuffers();
			// Don't clear anything every frame since we have our own clear ops
			mViewport->setClearEveryFrame(false);
		}
		else
		{
			// Reset clearing options
			mViewport->setClearEveryFrame(mOldClearEveryFrameBuffers > 0,
				mOldClearEveryFrameBuffers);
		}
	}

    mDirty = false;
}
//-----------------------------------------------------------------------
void CompositorChain::_markDirty()
{
    mDirty = true;
}
//-----------------------------------------------------------------------
Viewport *CompositorChain::getViewport()
{
    return mViewport;
}
//---------------------------------------------------------------------
void CompositorChain::_notifyViewport(Viewport* vp)
{
	mViewport = vp;
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::renderQueueStarted(uint8 id,
	const String& invocation, bool& skipThisQueue)
{
	// Skip when not matching viewport
	// shadows update is nested within main viewport update
	if (mSceneManager->getCurrentViewport() != mViewport)
		return;

	flushUpTo(id);
	/// If noone wants to render this queue, skip it
	/// Don't skip the OVERLAY queue because that's handled seperately
	if(!mOperation->renderQueues.test(id) && id!=RENDER_QUEUE_OVERLAY)
	{
		skipThisQueue = true;
	}
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::renderQueueEnded(uint8 id,
	const String& invocation, bool& repeatThisQueue)
{
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::setOperation(CompositorInstance::TargetOperation *op,SceneManager *sm,RenderSystem *rs)
{
	mOperation = op;
	mSceneManager = sm;
	mRenderSystem = rs;
	currentOp = op->renderSystemOperations.begin();
	lastOp = op->renderSystemOperations.end();
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::flushUpTo(uint8 id)
{
	/// Process all RenderSystemOperations up to and including render queue id.
    /// Including, because the operations for RenderQueueGroup x should be executed
	/// at the beginning of the RenderQueueGroup render for x.
	while(currentOp != lastOp && currentOp->first <= id)
	{
		currentOp->second->execute(mSceneManager, mRenderSystem);
		++currentOp;
	}
}
//-----------------------------------------------------------------------
}
<commit_msg>Patch 2690400: default material scheme when compiling compositors, so that quad materials get assigned correctly<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2006 Torus Knot Software Ltd
Also see acknowledgements in Readme.html

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.

You may alternatively use this source under the terms of a specific version of
the OGRE Unrestricted License provided you have obtained such a license from
Torus Knot Software Ltd.
-----------------------------------------------------------------------------
*/
#include "OgreStableHeaders.h"
#include "OgreCompositorChain.h"
#include "OgreCompositionTechnique.h"
#include "OgreCompositorInstance.h"
#include "OgreCompositionTargetPass.h"
#include "OgreCompositionPass.h"
#include "OgreViewport.h"
#include "OgreCamera.h"
#include "OgreRenderTarget.h"
#include "OgreLogManager.h"
#include "OgreCompositorManager.h"
#include "OgreSceneManager.h"
#include "OgreRenderQueueInvocation.h"
#include "OgreMaterialManager.h"

namespace Ogre {
CompositorChain::CompositorChain(Viewport *vp):
    mViewport(vp),
	mOriginalScene(0),
    mDirty(true),
	mAnyCompositorsEnabled(false)
{
	mOldClearEveryFrameBuffers = mViewport->getClearBuffers();
    assert(mViewport);
}
//-----------------------------------------------------------------------
CompositorChain::~CompositorChain()
{
	destroyResources();
}
//-----------------------------------------------------------------------
void CompositorChain::destroyResources(void)
{
	clearCompiledState();

	if (mViewport)
	{
		removeAllCompositors();
		/// Destroy "original scene" compositor instance
		if (mOriginalScene)
		{
			mViewport->getTarget()->removeListener(this);
			mOriginalScene->getTechnique()->destroyInstance(mOriginalScene);
			mOriginalScene = 0;
		}
		mViewport = 0;
	}
}
//-----------------------------------------------------------------------
CompositorInstance* CompositorChain::addCompositor(CompositorPtr filter, size_t addPosition, size_t technique)
{
	// Init on demand
	if (!mOriginalScene)
	{
		mViewport->getTarget()->addListener(this);

		/// Create base "original scene" compositor
		CompositorPtr base = CompositorManager::getSingleton().load("Ogre/Scene",
			ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
		mOriginalScene = base->getSupportedTechnique(0)->createInstance(this);
	}

	filter->touch();
    if(technique >= filter->getNumSupportedTechniques())
    {
        /// Warn user
        LogManager::getSingleton().logMessage(
            "CompositorChain: Compositor " + filter->getName() + " has no supported techniques.", LML_CRITICAL
        );
        return 0;
    }
    CompositionTechnique *tech = filter->getSupportedTechnique(technique);
    CompositorInstance *t = tech->createInstance(this);

    if(addPosition == LAST)
        addPosition = mInstances.size();
    else
        assert(addPosition <= mInstances.size() && "Index out of bounds.");
    mInstances.insert(mInstances.begin()+addPosition, t);

    mDirty = true;
	mAnyCompositorsEnabled = true;
    return t;
}
//-----------------------------------------------------------------------
void CompositorChain::removeCompositor(size_t index)
{
    assert (index < mInstances.size() && "Index out of bounds.");
    Instances::iterator i = mInstances.begin() + index;
    (*i)->getTechnique()->destroyInstance(*i);
    mInstances.erase(i);

    mDirty = true;
}
//-----------------------------------------------------------------------
size_t CompositorChain::getNumCompositors()
{
    return mInstances.size();
}
//-----------------------------------------------------------------------
void CompositorChain::removeAllCompositors()
{
    Instances::iterator i, iend;
    iend = mInstances.end();
    for (i = mInstances.begin(); i != iend; ++i)
    {
        (*i)->getTechnique()->destroyInstance(*i);
    }
    mInstances.clear();

    mDirty = true;
}
//-----------------------------------------------------------------------
void CompositorChain::_removeInstance(CompositorInstance *i)
{
	mInstances.erase(std::find(mInstances.begin(), mInstances.end(), i));
	i->getTechnique()->destroyInstance(i);
}
//-----------------------------------------------------------------------
void CompositorChain::_queuedOperation(CompositorInstance::RenderSystemOperation* op)
{
	mRenderSystemOperations.push_back(op);

}
//-----------------------------------------------------------------------
CompositorInstance *CompositorChain::getCompositor(size_t index)
{
    assert (index < mInstances.size() && "Index out of bounds.");
    return mInstances[index];
}

//-----------------------------------------------------------------------
CompositorChain::InstanceIterator CompositorChain::getCompositors()
{
    return InstanceIterator(mInstances.begin(), mInstances.end());
}
//-----------------------------------------------------------------------
void CompositorChain::setCompositorEnabled(size_t position, bool state)
{
    getCompositor(position)->setEnabled(state);
}
//-----------------------------------------------------------------------
void CompositorChain::preRenderTargetUpdate(const RenderTargetEvent& evt)
{
	/// Compile if state is dirty
	if(mDirty)
		_compile();

	// Do nothing if no compositors enabled
	if (!mAnyCompositorsEnabled)
	{
		return;
	}

	/// Update dependent render targets; this is done in the preRenderTarget
	/// and not the preViewportUpdate for a reason: at this time, the
	/// target Rendertarget will not yet have been set as current.
	/// ( RenderSystem::setViewport(...) ) if it would have been, the rendering
	/// order would be screwed up and problems would arise with copying rendertextures.
    Camera *cam = mViewport->getCamera();
    /// Iterate over compiled state
    CompositorInstance::CompiledState::iterator i;
    for(i=mCompiledState.begin(); i!=mCompiledState.end(); ++i)
    {
		/// Skip if this is a target that should only be initialised initially
		if(i->onlyInitial && i->hasBeenRendered)
			continue;
		i->hasBeenRendered = true;
		/// Setup and render
        preTargetOperation(*i, i->target->getViewport(0), cam);
        i->target->update();
        postTargetOperation(*i, i->target->getViewport(0), cam);
    }
}
//-----------------------------------------------------------------------
void CompositorChain::preViewportUpdate(const RenderTargetViewportEvent& evt)
{
	// Only set up if there is at least one compositor enabled, and it's this viewport
    if(evt.source != mViewport || !mAnyCompositorsEnabled)
        return;

	// set original scene details from viewport
	CompositionPass* pass = mOriginalScene->getTechnique()->getOutputTargetPass()->getPass(0);
	CompositionTargetPass* passParent = pass->getParent();
	if (pass->getClearBuffers() != mViewport->getClearBuffers() ||
		pass->getClearColour() != mViewport->getBackgroundColour() ||
		passParent->getVisibilityMask() != mViewport->getVisibilityMask() ||
		passParent->getMaterialScheme() != mViewport->getMaterialScheme() ||
		passParent->getShadowsEnabled() != mViewport->getShadowsEnabled())
	{
		// recompile if viewport settings are different
		pass->setClearBuffers(mViewport->getClearBuffers());
		pass->setClearColour(mViewport->getBackgroundColour());
		passParent->setVisibilityMask(mViewport->getVisibilityMask());
		passParent->setMaterialScheme(mViewport->getMaterialScheme());
		passParent->setShadowsEnabled(mViewport->getShadowsEnabled());
		_compile();
	}

	Camera *cam = mViewport->getCamera();
	/// Prepare for output operation
	preTargetOperation(mOutputOperation, mViewport, cam);
}
//-----------------------------------------------------------------------
void CompositorChain::preTargetOperation(CompositorInstance::TargetOperation &op, Viewport *vp, Camera *cam)
{
    SceneManager *sm = cam->getSceneManager();
	/// Set up render target listener
	mOurListener.setOperation(&op, sm, sm->getDestinationRenderSystem());
	mOurListener.notifyViewport(vp);
	/// Register it
	sm->addRenderQueueListener(&mOurListener);
	/// Set visiblity mask
	mOldVisibilityMask = sm->getVisibilityMask();
	sm->setVisibilityMask(op.visibilityMask);
	/// Set whether we find visibles
	mOldFindVisibleObjects = sm->getFindVisibleObjects();
	sm->setFindVisibleObjects(op.findVisibleObjects);
    /// Set LOD bias level
    mOldLodBias = cam->getLodBias();
    cam->setLodBias(cam->getLodBias() * op.lodBias);
	/// Set material scheme
	mOldMaterialScheme = vp->getMaterialScheme();
	vp->setMaterialScheme(op.materialScheme);
	/// Set shadows enabled
	mOldShadowsEnabled = vp->getShadowsEnabled();
	vp->setShadowsEnabled(op.shadowsEnabled);
    /// XXX TODO
    //vp->setClearEveryFrame( true );
    //vp->setOverlaysEnabled( false );
    //vp->setBackgroundColour( op.clearColour );
}
//-----------------------------------------------------------------------
void CompositorChain::postTargetOperation(CompositorInstance::TargetOperation &op, Viewport *vp, Camera *cam)
{
    SceneManager *sm = cam->getSceneManager();
	/// Unregister our listener
	sm->removeRenderQueueListener(&mOurListener);
	/// Restore default scene and camera settings
	sm->setVisibilityMask(mOldVisibilityMask);
	sm->setFindVisibleObjects(mOldFindVisibleObjects);
    cam->setLodBias(mOldLodBias);
	vp->setMaterialScheme(mOldMaterialScheme);
	vp->setShadowsEnabled(mOldShadowsEnabled);
}
//-----------------------------------------------------------------------
void CompositorChain::postViewportUpdate(const RenderTargetViewportEvent& evt)
{
	// Only tidy up if there is at least one compositor enabled, and it's this viewport
    if(evt.source != mViewport || !mAnyCompositorsEnabled)
        return;

	postTargetOperation(mOutputOperation, mViewport, mViewport->getCamera());
}
//-----------------------------------------------------------------------
void CompositorChain::viewportRemoved(const RenderTargetViewportEvent& evt)
{
	// check this is the viewport we're attached to (multi-viewport targets)
	if (evt.source == mViewport)
	{
		// this chain is now orphaned
		// can't delete it since held from outside, but release all resources being used
		destroyResources();
	}

}
//-----------------------------------------------------------------------
void CompositorChain::clearCompiledState()
{
	for (RenderSystemOperations::iterator i = mRenderSystemOperations.begin();
		i != mRenderSystemOperations.end(); ++i)
	{
		OGRE_DELETE *i;
	}
	mRenderSystemOperations.clear();

	/// Clear compiled state
	mCompiledState.clear();
	mOutputOperation = CompositorInstance::TargetOperation(0);

}
//-----------------------------------------------------------------------
void CompositorChain::_compile()
{
	clearCompiledState();

	bool compositorsEnabled = false;

	// force default scheme so materials for compositor quads will determined correctly
	MaterialManager& matMgr = MaterialManager::getSingleton();
	String prevMaterialScheme = matMgr.getActiveScheme();
	matMgr.setActiveScheme(MaterialManager::DEFAULT_SCHEME_NAME);

    /// Set previous CompositorInstance for each compositor in the list
    CompositorInstance *lastComposition = mOriginalScene;
	mOriginalScene->mPreviousInstance = 0;
	CompositionPass* pass = mOriginalScene->getTechnique()->getOutputTargetPass()->getPass(0);
	pass->setClearBuffers(mViewport->getClearBuffers());
	pass->setClearColour(mViewport->getBackgroundColour());
    for(Instances::iterator i=mInstances.begin(); i!=mInstances.end(); ++i)
    {
        if((*i)->getEnabled())
        {
			compositorsEnabled = true;
            (*i)->mPreviousInstance = lastComposition;
            lastComposition = (*i);
        }
    }

    /// Compile misc targets
    lastComposition->_compileTargetOperations(mCompiledState);

    /// Final target viewport (0)
	mOutputOperation.renderSystemOperations.clear();
    lastComposition->_compileOutputOperation(mOutputOperation);

	// Deal with viewport settings
	if (compositorsEnabled != mAnyCompositorsEnabled)
	{
		mAnyCompositorsEnabled = compositorsEnabled;
		if (mAnyCompositorsEnabled)
		{
			// Save old viewport clearing options
			mOldClearEveryFrameBuffers = mViewport->getClearBuffers();
			// Don't clear anything every frame since we have our own clear ops
			mViewport->setClearEveryFrame(false);
		}
		else
		{
			// Reset clearing options
			mViewport->setClearEveryFrame(mOldClearEveryFrameBuffers > 0,
				mOldClearEveryFrameBuffers);
		}
	}

	// restore material scheme
	matMgr.setActiveScheme(prevMaterialScheme);

    mDirty = false;
}
//-----------------------------------------------------------------------
void CompositorChain::_markDirty()
{
    mDirty = true;
}
//-----------------------------------------------------------------------
Viewport *CompositorChain::getViewport()
{
    return mViewport;
}
//---------------------------------------------------------------------
void CompositorChain::_notifyViewport(Viewport* vp)
{
	mViewport = vp;
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::renderQueueStarted(uint8 id,
	const String& invocation, bool& skipThisQueue)
{
	// Skip when not matching viewport
	// shadows update is nested within main viewport update
	if (mSceneManager->getCurrentViewport() != mViewport)
		return;

	flushUpTo(id);
	/// If noone wants to render this queue, skip it
	/// Don't skip the OVERLAY queue because that's handled seperately
	if(!mOperation->renderQueues.test(id) && id!=RENDER_QUEUE_OVERLAY)
	{
		skipThisQueue = true;
	}
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::renderQueueEnded(uint8 id,
	const String& invocation, bool& repeatThisQueue)
{
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::setOperation(CompositorInstance::TargetOperation *op,SceneManager *sm,RenderSystem *rs)
{
	mOperation = op;
	mSceneManager = sm;
	mRenderSystem = rs;
	currentOp = op->renderSystemOperations.begin();
	lastOp = op->renderSystemOperations.end();
}
//-----------------------------------------------------------------------
void CompositorChain::RQListener::flushUpTo(uint8 id)
{
	/// Process all RenderSystemOperations up to and including render queue id.
    /// Including, because the operations for RenderQueueGroup x should be executed
	/// at the beginning of the RenderQueueGroup render for x.
	while(currentOp != lastOp && currentOp->first <= id)
	{
		currentOp->second->execute(mSceneManager, mRenderSystem);
		++currentOp;
	}
}
//-----------------------------------------------------------------------
}
<|endoftext|>


<commit_before>//
//  Shader.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 07/02/2016.
//  Copyright 2016 Thomas Harte. All rights reserved.
//

#include "Shader.hpp"

#include <cstdio>
#include <cstring>

using namespace Outputs::Display::OpenGL;

namespace {
	// The below is disabled because it isn't context/thread-specific. Which makes it
	// fairly 'unuseful'.
//	Shader *bound_shader = nullptr;
}

GLuint Shader::compile_shader(const std::string &source, GLenum type) {
	GLuint shader = glCreateShader(type);
	const char *c_str = source.c_str();
	glShaderSource(shader, 1, &c_str, NULL);
	glCompileShader(shader);

#ifdef DEBUG
	GLint isCompiled = 0;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
	if(isCompiled == GL_FALSE) {
		GLint logLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
		if(logLength > 0) {
			GLchar *log = new GLchar[static_cast<std::size_t>(logLength)];
			glGetShaderInfoLog(shader, logLength, &logLength, log);
			printf("Compile log:\n%s\n", log);
			delete[] log;
		}

		throw (type == GL_VERTEX_SHADER) ? VertexShaderCompilationError : FragmentShaderCompilationError;
	}
#endif

	return shader;
}

Shader::Shader(const std::string &vertex_shader, const std::string &fragment_shader, const std::vector<AttributeBinding> &attribute_bindings) {
	init(vertex_shader, fragment_shader, attribute_bindings);
}

Shader::Shader(const std::string &vertex_shader, const std::string &fragment_shader, const std::vector<std::string> &binding_names) {
	std::vector<AttributeBinding> bindings;
	GLuint index = 0;
	for(const auto &name: binding_names) {
		bindings.emplace_back(name, index);
		++index;
	}
	init(vertex_shader, fragment_shader, bindings);
}

void Shader::init(const std::string &vertex_shader, const std::string &fragment_shader, const std::vector<AttributeBinding> &attribute_bindings) {
	shader_program_ = glCreateProgram();
	const GLuint vertex = compile_shader(vertex_shader, GL_VERTEX_SHADER);
	const GLuint fragment = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);

	glAttachShader(shader_program_, vertex);
	glAttachShader(shader_program_, fragment);

	for(const auto &binding : attribute_bindings) {
		glBindAttribLocation(shader_program_, binding.index, binding.name.c_str());
	}

	glLinkProgram(shader_program_);

#ifdef DEBUG
	GLint logLength;
	glGetProgramiv(shader_program_, GL_INFO_LOG_LENGTH, &logLength);
	if(logLength > 0) {
		GLchar *log = new GLchar[static_cast<std::size_t>(logLength)];
		glGetProgramInfoLog(shader_program_, logLength, &logLength, log);
		printf("Link log:\n%s\n", log);
		delete[] log;
	}

	GLint didLink = 0;
	glGetProgramiv(shader_program_, GL_LINK_STATUS, &didLink);
	if(didLink == GL_FALSE) {
		throw ProgramLinkageError;
	}
#endif
}

Shader::~Shader() {
//	if(bound_shader == this) Shader::unbind();
	glDeleteProgram(shader_program_);
}

void Shader::bind() const {
//	if(bound_shader != this) {
		glUseProgram(shader_program_);
//		bound_shader = this;
//	}
	flush_functions();
}

void Shader::unbind() {
//	bound_shader = nullptr;
	glUseProgram(0);
}

GLint Shader::get_attrib_location(const std::string &name) const {
	return glGetAttribLocation(shader_program_, name.c_str());
}

GLint Shader::get_uniform_location(const std::string &name) const {
	return glGetUniformLocation(shader_program_, name.c_str());
}

void Shader::enable_vertex_attribute_with_pointer(const std::string &name, GLint size, GLenum type, GLboolean normalised, GLsizei stride, const GLvoid *pointer, GLuint divisor) {
	GLint location = get_attrib_location(name);
	if(location >= 0) {
		glEnableVertexAttribArray((GLuint)location);
		glVertexAttribPointer((GLuint)location, size, type, normalised, stride, pointer);
		glVertexAttribDivisor((GLuint)location, divisor);
	}
}

// The various set_uniforms...
#define location() glGetUniformLocation(shader_program_, name.c_str())
void Shader::set_uniform(const std::string &name, GLint value) {
	enqueue_function([name, value, this] {
		glUniform1i(location(), value);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value) {
	enqueue_function([name, value, this] {
		glUniform1ui(location(), value);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value) {
	enqueue_function([name, value, this] {
		glUniform1f(location(), value);
	});
}

void Shader::set_uniform(const std::string &name, GLint value1, GLint value2) {
	enqueue_function([name, value1, value2, this] {
		glUniform2i(location(), value1, value2);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2) {
	enqueue_function([name, value1, value2, this] {
		GLint location = location();
		glUniform2f(location, value1, value2);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2) {
	enqueue_function([name, value1, value2, this] {
		glUniform2ui(location(), value1, value2);
	});
}

void Shader::set_uniform(const std::string &name, GLint value1, GLint value2, GLint value3) {
	enqueue_function([name, value1, value2, value3, this] {
		glUniform3i(location(), value1, value2, value3);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2, GLfloat value3) {
	enqueue_function([name, value1, value2, value3, this] {
		glUniform3f(location(), value1, value2, value3);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2, GLuint value3) {
	enqueue_function([name, value1, value2, value3, this] {
		glUniform3ui(location(), value1, value2, value3);
	});
}

void Shader::set_uniform(const std::string &name, GLint value1, GLint value2, GLint value3, GLint value4) {
	enqueue_function([name, value1, value2, value3, value4, this] {
		glUniform4i(location(), value1, value2, value3, value4);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4) {
	enqueue_function([name, value1, value2, value3, value4, this] {
		glUniform4f(location(), value1, value2, value3, value4);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2, GLuint value3, GLuint value4) {
	enqueue_function([name, value1, value2, value3, value4, this] {
		glUniform4ui(location(), value1, value2, value3, value4);
	});
}

void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLint *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
	GLint *values_copy = new GLint[number_of_values];
	std::memcpy(values_copy, values, sizeof(*values) * static_cast<std::size_t>(number_of_values));

	enqueue_function([name, size, count, values_copy, this] {
		switch(size) {
			case 1: glUniform1iv(location(), count, values_copy);	break;
			case 2: glUniform2iv(location(), count, values_copy);	break;
			case 3: glUniform3iv(location(), count, values_copy);	break;
			case 4: glUniform4iv(location(), count, values_copy);	break;
		}
		delete[] values_copy;
	});
}

void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLfloat *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
	GLfloat *values_copy = new GLfloat[number_of_values];
	std::memcpy(values_copy, values, sizeof(*values) * static_cast<std::size_t>(number_of_values));

	enqueue_function([name, size, count, values_copy, this] {
		switch(size) {
			case 1: glUniform1fv(location(), count, values_copy);	break;
			case 2: glUniform2fv(location(), count, values_copy);	break;
			case 3: glUniform3fv(location(), count, values_copy);	break;
			case 4: glUniform4fv(location(), count, values_copy);	break;
		}
		delete[] values_copy;
	});
}

void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLuint *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
	GLuint *values_copy = new GLuint[number_of_values];
	std::memcpy(values_copy, values, sizeof(*values) * static_cast<std::size_t>(number_of_values));

	enqueue_function([name, size, count, values_copy, this] {
		switch(size) {
			case 1: glUniform1uiv(location(), count, values_copy);	break;
			case 2: glUniform2uiv(location(), count, values_copy);	break;
			case 3: glUniform3uiv(location(), count, values_copy);	break;
			case 4: glUniform4uiv(location(), count, values_copy);	break;
		}
		delete[] values_copy;
	});
}

void Shader::set_uniform_matrix(const std::string &name, GLint size, bool transpose, const GLfloat *values) {
	set_uniform_matrix(name, size, 1, transpose, values);
}

void Shader::set_uniform_matrix(const std::string &name, GLint size, GLsizei count, bool transpose, const GLfloat *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size) * static_cast<std::size_t>(size);
	std::vector<GLfloat> values_copy(number_of_values);
	std::memcpy(values_copy.data(), values, sizeof(*values) * number_of_values);

	enqueue_function([name, size, count, transpose, values_copy, this] {
		GLboolean glTranspose = transpose ? GL_TRUE : GL_FALSE;
		switch(size) {
			case 2: glUniformMatrix2fv(location(), count, glTranspose, values_copy.data());	break;
			case 3: glUniformMatrix3fv(location(), count, glTranspose, values_copy.data());	break;
			case 4: glUniformMatrix4fv(location(), count, glTranspose, values_copy.data());	break;
		}
	});
}

void Shader::enqueue_function(std::function<void(void)> function) {
	std::lock_guard<std::mutex> function_guard(function_mutex_);
	enqueued_functions_.push_back(function);
}

void Shader::flush_functions() const {
	std::lock_guard<std::mutex> function_guard(function_mutex_);
	for(std::function<void(void)> function : enqueued_functions_) {
		function();
	}
	enqueued_functions_.clear();
}
<commit_msg>Switches the Shader class to using LOG.<commit_after>//
//  Shader.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 07/02/2016.
//  Copyright 2016 Thomas Harte. All rights reserved.
//

#include "Shader.hpp"

#include <cstring>
#include "../../Log.hpp"

using namespace Outputs::Display::OpenGL;

namespace {
	// The below is disabled because it isn't context/thread-specific. Which makes it
	// fairly 'unuseful'.
//	Shader *bound_shader = nullptr;
}

GLuint Shader::compile_shader(const std::string &source, GLenum type) {
	GLuint shader = glCreateShader(type);
	const char *c_str = source.c_str();
	glShaderSource(shader, 1, &c_str, NULL);
	glCompileShader(shader);

#ifndef NDEBUG
	GLint isCompiled = 0;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
	if(isCompiled == GL_FALSE) {
		GLint logLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
		if(logLength > 0) {
			GLchar *log = new GLchar[static_cast<std::size_t>(logLength)];
			glGetShaderInfoLog(shader, logLength, &logLength, log);
			LOG("Compile log:\n" << log);
			delete[] log;
		}

		throw (type == GL_VERTEX_SHADER) ? VertexShaderCompilationError : FragmentShaderCompilationError;
	}
#endif

	return shader;
}

Shader::Shader(const std::string &vertex_shader, const std::string &fragment_shader, const std::vector<AttributeBinding> &attribute_bindings) {
	init(vertex_shader, fragment_shader, attribute_bindings);
}

Shader::Shader(const std::string &vertex_shader, const std::string &fragment_shader, const std::vector<std::string> &binding_names) {
	std::vector<AttributeBinding> bindings;
	GLuint index = 0;
	for(const auto &name: binding_names) {
		bindings.emplace_back(name, index);
		++index;
	}
	init(vertex_shader, fragment_shader, bindings);
}

void Shader::init(const std::string &vertex_shader, const std::string &fragment_shader, const std::vector<AttributeBinding> &attribute_bindings) {
	shader_program_ = glCreateProgram();
	const GLuint vertex = compile_shader(vertex_shader, GL_VERTEX_SHADER);
	const GLuint fragment = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);

	glAttachShader(shader_program_, vertex);
	glAttachShader(shader_program_, fragment);

	for(const auto &binding : attribute_bindings) {
		glBindAttribLocation(shader_program_, binding.index, binding.name.c_str());
	}

	glLinkProgram(shader_program_);

#ifndef NDEBUG
	GLint logLength;
	glGetProgramiv(shader_program_, GL_INFO_LOG_LENGTH, &logLength);
	if(logLength > 0) {
		GLchar *log = new GLchar[static_cast<std::size_t>(logLength)];
		glGetProgramInfoLog(shader_program_, logLength, &logLength, log);
		LOG("Link log:\n" << log);
		delete[] log;
	}

	GLint didLink = 0;
	glGetProgramiv(shader_program_, GL_LINK_STATUS, &didLink);
	if(didLink == GL_FALSE) {
		throw ProgramLinkageError;
	}
#endif
}

Shader::~Shader() {
//	if(bound_shader == this) Shader::unbind();
	glDeleteProgram(shader_program_);
}

void Shader::bind() const {
//	if(bound_shader != this) {
		glUseProgram(shader_program_);
//		bound_shader = this;
//	}
	flush_functions();
}

void Shader::unbind() {
//	bound_shader = nullptr;
	glUseProgram(0);
}

GLint Shader::get_attrib_location(const std::string &name) const {
	return glGetAttribLocation(shader_program_, name.c_str());
}

GLint Shader::get_uniform_location(const std::string &name) const {
	return glGetUniformLocation(shader_program_, name.c_str());
}

void Shader::enable_vertex_attribute_with_pointer(const std::string &name, GLint size, GLenum type, GLboolean normalised, GLsizei stride, const GLvoid *pointer, GLuint divisor) {
	GLint location = get_attrib_location(name);
	if(location >= 0) {
		glEnableVertexAttribArray((GLuint)location);
		glVertexAttribPointer((GLuint)location, size, type, normalised, stride, pointer);
		glVertexAttribDivisor((GLuint)location, divisor);
	} else {
		LOG("Couldn't enable vertex attribute " << name);
	}
}

// The various set_uniforms...
#define location() glGetUniformLocation(shader_program_, name.c_str())
void Shader::set_uniform(const std::string &name, GLint value) {
	enqueue_function([name, value, this] {
		glUniform1i(location(), value);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value) {
	enqueue_function([name, value, this] {
		glUniform1ui(location(), value);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value) {
	enqueue_function([name, value, this] {
		glUniform1f(location(), value);
	});
}

void Shader::set_uniform(const std::string &name, GLint value1, GLint value2) {
	enqueue_function([name, value1, value2, this] {
		glUniform2i(location(), value1, value2);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2) {
	enqueue_function([name, value1, value2, this] {
		GLint location = location();
		glUniform2f(location, value1, value2);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2) {
	enqueue_function([name, value1, value2, this] {
		glUniform2ui(location(), value1, value2);
	});
}

void Shader::set_uniform(const std::string &name, GLint value1, GLint value2, GLint value3) {
	enqueue_function([name, value1, value2, value3, this] {
		glUniform3i(location(), value1, value2, value3);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2, GLfloat value3) {
	enqueue_function([name, value1, value2, value3, this] {
		glUniform3f(location(), value1, value2, value3);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2, GLuint value3) {
	enqueue_function([name, value1, value2, value3, this] {
		glUniform3ui(location(), value1, value2, value3);
	});
}

void Shader::set_uniform(const std::string &name, GLint value1, GLint value2, GLint value3, GLint value4) {
	enqueue_function([name, value1, value2, value3, value4, this] {
		glUniform4i(location(), value1, value2, value3, value4);
	});
}

void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4) {
	enqueue_function([name, value1, value2, value3, value4, this] {
		glUniform4f(location(), value1, value2, value3, value4);
	});
}

void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2, GLuint value3, GLuint value4) {
	enqueue_function([name, value1, value2, value3, value4, this] {
		glUniform4ui(location(), value1, value2, value3, value4);
	});
}

void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLint *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
	GLint *values_copy = new GLint[number_of_values];
	std::memcpy(values_copy, values, sizeof(*values) * static_cast<std::size_t>(number_of_values));

	enqueue_function([name, size, count, values_copy, this] {
		switch(size) {
			case 1: glUniform1iv(location(), count, values_copy);	break;
			case 2: glUniform2iv(location(), count, values_copy);	break;
			case 3: glUniform3iv(location(), count, values_copy);	break;
			case 4: glUniform4iv(location(), count, values_copy);	break;
		}
		delete[] values_copy;
	});
}

void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLfloat *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
	GLfloat *values_copy = new GLfloat[number_of_values];
	std::memcpy(values_copy, values, sizeof(*values) * static_cast<std::size_t>(number_of_values));

	enqueue_function([name, size, count, values_copy, this] {
		switch(size) {
			case 1: glUniform1fv(location(), count, values_copy);	break;
			case 2: glUniform2fv(location(), count, values_copy);	break;
			case 3: glUniform3fv(location(), count, values_copy);	break;
			case 4: glUniform4fv(location(), count, values_copy);	break;
		}
		delete[] values_copy;
	});
}

void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLuint *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
	GLuint *values_copy = new GLuint[number_of_values];
	std::memcpy(values_copy, values, sizeof(*values) * static_cast<std::size_t>(number_of_values));

	enqueue_function([name, size, count, values_copy, this] {
		switch(size) {
			case 1: glUniform1uiv(location(), count, values_copy);	break;
			case 2: glUniform2uiv(location(), count, values_copy);	break;
			case 3: glUniform3uiv(location(), count, values_copy);	break;
			case 4: glUniform4uiv(location(), count, values_copy);	break;
		}
		delete[] values_copy;
	});
}

void Shader::set_uniform_matrix(const std::string &name, GLint size, bool transpose, const GLfloat *values) {
	set_uniform_matrix(name, size, 1, transpose, values);
}

void Shader::set_uniform_matrix(const std::string &name, GLint size, GLsizei count, bool transpose, const GLfloat *values) {
	std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size) * static_cast<std::size_t>(size);
	std::vector<GLfloat> values_copy(number_of_values);
	std::memcpy(values_copy.data(), values, sizeof(*values) * number_of_values);

	enqueue_function([name, size, count, transpose, values_copy, this] {
		GLboolean glTranspose = transpose ? GL_TRUE : GL_FALSE;
		switch(size) {
			case 2: glUniformMatrix2fv(location(), count, glTranspose, values_copy.data());	break;
			case 3: glUniformMatrix3fv(location(), count, glTranspose, values_copy.data());	break;
			case 4: glUniformMatrix4fv(location(), count, glTranspose, values_copy.data());	break;
		}
	});
}

void Shader::enqueue_function(std::function<void(void)> function) {
	std::lock_guard<std::mutex> function_guard(function_mutex_);
	enqueued_functions_.push_back(function);
}

void Shader::flush_functions() const {
	std::lock_guard<std::mutex> function_guard(function_mutex_);
	for(std::function<void(void)> function : enqueued_functions_) {
		function();
	}
	enqueued_functions_.clear();
}
<|endoftext|>


<commit_before>#include "readyscene.h"
#include "../types.h"
#include <QGraphicsItem>
#include <QDebug>
#include <QGraphicsItemAnimation>
#include <QTimeLine>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QtGlobal>
#include "localgame.h"
#include "qgameitem.h"

// Constructor & Destructor
ReadyScene::ReadyScene(qreal x, qreal y,
                       qreal width, qreal height,
                       QObject *parent)
    : QGraphicsScene(x, y, width, height, parent)
{
    this->window = dynamic_cast<MainWindow*>(parent);
    if(this->window == NULL) qDebug() <<"window is null!";
    Q_CHECK_PTR(this->window);

    // setup Ready Scene
    setupReady();

    //first setup two players

    player1 = new Player(NULL,1);
    player2 = new Player(NULL,2);
    player3 = new Player(NULL,3);
    player4 = new Player(NULL,4);
    player_image1 = new ReadyPlayerImage(this,window,player1);
    player_image2 = new ReadyPlayerImage(this,window,player2);
    player_image3 = new ReadyPlayerImage(this,window,player3);
    player_image4 = new ReadyPlayerImage(this,window,player4);
    player_image1->setPos(150,200);
    player_image2->setPos(400,200);
    player_image3->setPos(650,200);
    player_image4->setPos(900,200);

}

ReadyScene::~ReadyScene()
{
    delete ready_button;
    delete background;
}

ReadyPlayerImage * ReadyScene::getPlayerImage(int player_id){
    switch(player_id){
    case 1:
        return player_image1;
    case 2:
        return player_image2;
    case 3:
        return player_image3;
    case 4:
        return player_image4;
    }
}

Player * ReadyPlayerImage::getPlayer(){
    return this->player;
}

// Methods
void ReadyScene::setupReady()
{
	// setup for ready

    // set background
    background = new QGameItem(this, window);
    background->setImage(":images/ready/readyscene.png");
    background->setPos(0, 0);

    // set buttons
    ready_button = new ReadyButton(this, window);
    ready_button->setPos(1185,20);
}

void ReadyScene::animateReady()
{

}

// ReadyButton
ReadyButton::ReadyButton(QGraphicsScene *scene, MainWindow *window)
    : QGameItem(scene, window)
{
    setImage(":images/ingame/pause/resume.png");
}

ReadyButton::~ReadyButton()
{

}

void ReadyButton::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "Start button clicked.";
    setImage(":images/ingame/pause/resume_pressed.png");
}

void ReadyButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    setImage(":images/ingame/pause/resume.png");

    //ReadyScene * rscene = scene();
    ReadyScene * rscene = dynamic_cast<ReadyScene*>(scene());

    for(int player_id = 1 ; player_id <=4 ; player_id ++){
        ReadyPlayerImage * rplayer_image
                = rscene->getPlayerImage(player_id);
        if(rplayer_image->getPlay()){
            LocalGame::getInst()->addPlayer(rplayer_image->getPlayer());
        }
    }

    // move to ready scene
    window->switchScene(SceneType::INGAME);
}

void ReadyButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    // ignore input in this case
    setImage(":images/ingame/pause/resume_pressed.png");
}

ReadyPlayerImage::ReadyPlayerImage(QGraphicsScene * scene,MainWindow* window,Player * player)
    : QGameItem(scene,window),player(player),moving(false),play(false)
{
    timeline = new QTimeLine(1000);
    timeline->setFrameRange(1,10);
    connect(timeline,SIGNAL(frameChanged(int)),this,SLOT(animatePlayerImage(int)));
    connect(timeline,SIGNAL(finished()),timeline,SLOT(start())); //run forever
    timeline->start();
    setAcceptHoverEvents(true);
    setPixmap(QPixmap(":/images/ready/plus_big.png"));
}

ReadyPlayerImage::~ReadyPlayerImage(){

}

void ReadyPlayerImage::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(!play)
        setPixmap(QPixmap(":/images/ready/plus_big_pressed.png"));
}

void ReadyPlayerImage::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if(!play){
        play = true;
    }
}

void ReadyPlayerImage::hoverEnterEvent(QGraphicsSceneHoverEvent *event){
    moving = true;
}

void ReadyPlayerImage::hoverLeaveEvent(QGraphicsSceneHoverEvent *event){
    moving = false;
}

void ReadyPlayerImage::setPlay(bool play){
    this->play = play;
}

bool ReadyPlayerImage::getPlay(){
    return play;
}

void ReadyPlayerImage::animatePlayerImage(int frame){
    if(!play){//not playing. don't show player image
        return;
    }

<<<<<<< HEAD
    setScale(2.5);
=======
>>>>>>> 9741ddde0c14bfdb9dae058504c626c2a05a24cf

    QString filename = QString(":/images/ingame/character/");

    filename += QString("top_down_");

    if(player->getId() == 1 || player->getId() == 3)
        filename += QString("io_");
    else if(player->getId() == 2 || player->getId() == 4)
        filename += QString("id_");

    filename += player->getColor() + QString("_");

    if(moving)
        filename += QString("walk_");
    else
        filename += QString("stand_");

    filename += QString::number(frame).rightJustified(3,'0') + QString(".png");

    setPixmap(QPixmap(filename));

    if(player->getId() == 4)
        setPos(QPointF(900,178));
}

<commit_msg>Resolved Conflict - sorry<commit_after>#include "readyscene.h"
#include "../types.h"
#include <QGraphicsItem>
#include <QDebug>
#include <QGraphicsItemAnimation>
#include <QTimeLine>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QtGlobal>
#include "localgame.h"
#include "qgameitem.h"

// Constructor & Destructor
ReadyScene::ReadyScene(qreal x, qreal y,
                       qreal width, qreal height,
                       QObject *parent)
    : QGraphicsScene(x, y, width, height, parent)
{
    this->window = dynamic_cast<MainWindow*>(parent);
    if(this->window == NULL) qDebug() <<"window is null!";
    Q_CHECK_PTR(this->window);

    // setup Ready Scene
    setupReady();

    //first setup two players

    player1 = new Player(NULL,1);
    player2 = new Player(NULL,2);
    player3 = new Player(NULL,3);
    player4 = new Player(NULL,4);
    player_image1 = new ReadyPlayerImage(this,window,player1);
    player_image2 = new ReadyPlayerImage(this,window,player2);
    player_image3 = new ReadyPlayerImage(this,window,player3);
    player_image4 = new ReadyPlayerImage(this,window,player4);
    player_image1->setPos(150,200);
    player_image2->setPos(400,200);
    player_image3->setPos(650,200);
    player_image4->setPos(900,200);

}

ReadyScene::~ReadyScene()
{
    delete ready_button;
    delete background;
}

ReadyPlayerImage * ReadyScene::getPlayerImage(int player_id){
    switch(player_id){
    case 1:
        return player_image1;
    case 2:
        return player_image2;
    case 3:
        return player_image3;
    case 4:
        return player_image4;
    }
}

Player * ReadyPlayerImage::getPlayer(){
    return this->player;
}

// Methods
void ReadyScene::setupReady()
{
	// setup for ready

    // set background
    background = new QGameItem(this, window);
    background->setImage(":images/ready/readyscene.png");
    background->setPos(0, 0);

    // set buttons
    ready_button = new ReadyButton(this, window);
    ready_button->setPos(1185,20);
}

void ReadyScene::animateReady()
{

}

// ReadyButton
ReadyButton::ReadyButton(QGraphicsScene *scene, MainWindow *window)
    : QGameItem(scene, window)
{
    setImage(":images/ingame/pause/resume.png");
}

ReadyButton::~ReadyButton()
{

}

void ReadyButton::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "Start button clicked.";
    setImage(":images/ingame/pause/resume_pressed.png");
}

void ReadyButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    setImage(":images/ingame/pause/resume.png");

    //ReadyScene * rscene = scene();
    ReadyScene * rscene = dynamic_cast<ReadyScene*>(scene());

    for(int player_id = 1 ; player_id <=4 ; player_id ++){
        ReadyPlayerImage * rplayer_image
                = rscene->getPlayerImage(player_id);
        if(rplayer_image->getPlay()){
            LocalGame::getInst()->addPlayer(rplayer_image->getPlayer());
        }
    }

    // move to ready scene
    window->switchScene(SceneType::INGAME);
}

void ReadyButton::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    // ignore input in this case
    setImage(":images/ingame/pause/resume_pressed.png");
}

ReadyPlayerImage::ReadyPlayerImage(QGraphicsScene * scene,MainWindow* window,Player * player)
    : QGameItem(scene,window),player(player),moving(false),play(false)
{
    timeline = new QTimeLine(1000);
    timeline->setFrameRange(1,10);
    connect(timeline,SIGNAL(frameChanged(int)),this,SLOT(animatePlayerImage(int)));
    connect(timeline,SIGNAL(finished()),timeline,SLOT(start())); //run forever
    timeline->start();
    setAcceptHoverEvents(true);
    setPixmap(QPixmap(":/images/ready/plus_big.png"));
}

ReadyPlayerImage::~ReadyPlayerImage(){

}

void ReadyPlayerImage::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(!play)
        setPixmap(QPixmap(":/images/ready/plus_big_pressed.png"));
}

void ReadyPlayerImage::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if(!play){
        play = true;
    }
}

void ReadyPlayerImage::hoverEnterEvent(QGraphicsSceneHoverEvent *event){
    moving = true;
}

void ReadyPlayerImage::hoverLeaveEvent(QGraphicsSceneHoverEvent *event){
    moving = false;
}

void ReadyPlayerImage::setPlay(bool play){
    this->play = play;
}

bool ReadyPlayerImage::getPlay(){
    return play;
}

void ReadyPlayerImage::animatePlayerImage(int frame){
    if(!play)//not playing. don't show player image
        return;

    setScale(2.5);

    QString filename = QString(":/images/ingame/character/");

    filename += QString("top_down_");

    if(player->getId() == 1 || player->getId() == 3)
        filename += QString("io_");
    else if(player->getId() == 2 || player->getId() == 4)
        filename += QString("id_");

    filename += player->getColor() + QString("_");

    if(moving)
        filename += QString("walk_");
    else
        filename += QString("stand_");

    filename += QString::number(frame).rightJustified(3,'0') + QString(".png");

    setPixmap(QPixmap(filename));

    if(player->getId() == 4)
        setPos(QPointF(900,178));
}

<|endoftext|>


<commit_before>/* Copyright 2009 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Use Google's libkml to out a KML file based upon the current network topology.
 * @author michael.elkins@cobham.com
 */

#define CUSTOM_ICON // use non-default icon for nodes

// libkml
#include "kml/convenience/convenience.h"
#include "kml/engine.h"
#include "kml/dom.h"

#include "libwatcher/watcherGraph.h"

#include <boost/lexical_cast.hpp>

using kmldom::ChangePtr;
using kmldom::CreatePtr;
using kmldom::CoordinatesPtr;
using kmldom::DocumentPtr;
using kmldom::FeaturePtr;
using kmldom::FolderPtr;
using kmldom::IconStyleIconPtr;
using kmldom::IconStylePtr;
using kmldom::KmlFactory;
using kmldom::KmlPtr;
using kmldom::LineStringPtr;
using kmldom::LineStylePtr;
using kmldom::PlacemarkPtr;
using kmldom::PointPtr;
using kmldom::StylePtr;
using kmldom::UpdatePtr;
using kmlengine::KmlFile;
using kmlengine::KmlFilePtr;

using namespace watcher;

namespace {

#ifdef CUSTOM_ICON
/*
 * Keep a mapping to the random Icon associated with a particular node so that the
 * same icon is used between invocations of write_kml().
 */
typedef std::map<NodeIdentifier, std::string> NodeIconMap ;
typedef NodeIconMap::iterator NodeIconMapIterator ;
NodeIconMap nodeIconMap;

const std::string BASE_ICON_URL = "http://maps.google.com/mapfiles/kml/shapes/";

//interesting icons for placemarks
const char *ICONS[] = {
    "cabs.png",
    "bus.png",
    "rail.png",
    "truck.png",
    "airports.png",
    "ferry.png",
    "heliport.png",
    "tram.png",
    "sailing.png"
};

// template to get the number of items in an array
template <typename T, int N> size_t sizeof_array( T (&)[N] ) { return N; }

void icon_setup(KmlFactory* kmlFac, DocumentPtr document)
{
    // set up styles for each icon we use, using the icon name as the id
    for (size_t i = 0; i < sizeof_array(ICONS); ++i) {
        IconStyleIconPtr icon(kmlFac->CreateIconStyleIcon());
        std::string url(BASE_ICON_URL + ICONS[i]);
        icon->set_href(url.c_str());

        IconStylePtr iconStyle(kmlFac->CreateIconStyle());
        iconStyle->set_icon(icon);

        StylePtr style(kmlFac->CreateStyle());
        style->set_iconstyle(iconStyle);
        style->set_id(ICONS[i]);

        document->add_styleselector(style);
    }
}
#endif // CUSTOM_ICON

void output_nodes(KmlFactory* kmlFac, const WatcherGraph& graph, FolderPtr folder)
{
    // iterate over all nodes in the graph
    WatcherGraph::vertexIterator vi, vend;
    for (tie(vi, vend) = vertices(graph.theGraph); vi != vend; ++vi) {
        const WatcherGraphNode &node = graph.theGraph[*vi];

        PlacemarkPtr ptr = kmlFac->CreatePlacemark();
        std::string ip(node.nodeId.to_string());
        ptr->set_name(ip); // textual label, can be html
        ptr->set_id(ip); // internal label for locating object in the DOM tree

        // set the location
        ptr->set_geometry(kmlconvenience::CreatePointLatLon(node.gpsData->y, node.gpsData->x));

        /*
        //id and target id are both set here
        //target id is required when changing some attribute of a feature already in the dom
        ptr->set_id("node0");
        ptr->set_targetid("node0");
        */

#ifdef CUSTOM_ICON
        // for testing purposes, randomly select an interesting icon to replace the default yellow pushpin
        std::string styleUrl;
        NodeIconMapIterator it = nodeIconMap.find(node.nodeId);
        if (it == nodeIconMap.end()) {
            styleUrl = std::string("#") + ICONS[ random() % sizeof_array(ICONS) ];
            nodeIconMap[node.nodeId] = styleUrl;
        } else
            styleUrl = it->second;
        ptr->set_styleurl(styleUrl);
#endif

        folder->add_feature(ptr);//add to DOM
    }
}

/*
 * Convert a watcher color to the KML format.
 */
std::string watcher_color_to_kml(const watcher::Color& color)
{
    char buf[sizeof("aabbggrr")];
    sprintf(buf, "%02x%02x%02x%02x", color.a, color.b, color.g, color.r);
    return std::string(buf);
}

void output_edges(KmlFactory* kmlFac, const WatcherGraph& graph, DocumentPtr document, FolderPtr folder)
{
    WatcherGraph::edgeIterator ei, eend;
    unsigned int count = 0;
    bool has_style = false;

    for (tie(ei, eend) = edges(graph.theGraph); ei != eend; ++ei, ++count) {
        const WatcherGraphEdge &edge = graph.theGraph[*ei];
        const WatcherGraphNode &node1 = graph.theGraph[source(*ei, graph.theGraph)];
        const WatcherGraphNode &node2 = graph.theGraph[target(*ei, graph.theGraph)];

        CoordinatesPtr coords = kmlFac->CreateCoordinates();
        coords->add_latlng(node1.gpsData->y, node1.gpsData->x);
        coords->add_latlng(node2.gpsData->y, node2.gpsData->x);

        LineStringPtr lineString = kmlFac->CreateLineString();
        lineString->set_coordinates(coords);

        PlacemarkPtr ptr = kmlFac->CreatePlacemark();
        ptr->set_geometry(lineString);

        // WatcherGraph only has a single style per layer, so we stash the value somewhere instead of creating multiple styles
        if (!has_style) {
            LineStylePtr lineStyle(kmlFac->CreateLineStyle());
            lineStyle->set_color(watcher_color_to_kml(edge.displayInfo->color));
            lineStyle->set_width(edge.displayInfo->width);

            StylePtr style(kmlFac->CreateStyle());
            style->set_linestyle(lineStyle);
            style->set_id("edge-style");

            document->add_styleselector(style);
            has_style = true;
        }

        std::string label("edge-" + boost::lexical_cast<std::string>(count));
        ptr->set_id(label);
        ptr->set_name(label);
        ptr->set_styleurl("#edge-style");

        folder->add_feature(ptr);//add to DOM
    }
}

} // end namespace

void write_kml(const WatcherGraph& graph, const std::string& outputFile)
{
    // libkml boilerplate
    KmlFactory* kmlFac(kmldom::KmlFactory::GetFactory());
    KmlPtr kml(kmlFac->CreateKml());

    /*
     * create initial DOM tree.  As nodes appear, they get added to the tree.
     * As nodes move, we update the position attribute
     * in the DOM, and regenerate the KML file.
     */
    DocumentPtr document(kmlFac->CreateDocument());
    kml->set_feature(document);

    FolderPtr folder(kmlFac->CreateFolder());
    folder->set_name("Watcher");
    document->add_feature(folder);

#ifdef CUSTOM_ICON
    icon_setup(kmlFac, document);
#endif // CUSTOM_ICON
    output_nodes(kmlFac, graph, folder);
    output_edges(kmlFac, graph, document, folder);

    kmlbase::File::WriteStringToFile(kmldom::SerializePretty(kml), outputFile);
}
<commit_msg>support labels on edges<commit_after>/* Copyright 2009 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Use Google's libkml to out a KML file based upon the current network topology.
 * @author michael.elkins@cobham.com
 */

#define CUSTOM_ICON // use non-default icon for nodes

// libkml
#include "kml/convenience/convenience.h"
#include "kml/engine.h"
#include "kml/dom.h"

#include "libwatcher/watcherGraph.h"

#include <boost/lexical_cast.hpp>

using kmldom::ChangePtr;
using kmldom::CreatePtr;
using kmldom::CoordinatesPtr;
using kmldom::DocumentPtr;
using kmldom::FeaturePtr;
using kmldom::FolderPtr;
using kmldom::IconStyleIconPtr;
using kmldom::IconStylePtr;
using kmldom::KmlFactory;
using kmldom::KmlPtr;
using kmldom::LabelStylePtr;
using kmldom::LineStringPtr;
using kmldom::LineStylePtr;
using kmldom::MultiGeometryPtr;
using kmldom::PlacemarkPtr;
using kmldom::PointPtr;
using kmldom::StylePtr;
using kmldom::UpdatePtr;
using kmlengine::KmlFile;
using kmlengine::KmlFilePtr;

using namespace watcher;

namespace {

#ifdef CUSTOM_ICON
/*
 * Keep a mapping to the random Icon associated with a particular node so that the
 * same icon is used between invocations of write_kml().
 */
typedef std::map<NodeIdentifier, std::string> NodeIconMap ;
typedef NodeIconMap::iterator NodeIconMapIterator ;
NodeIconMap nodeIconMap;

const std::string BASE_ICON_URL = "http://maps.google.com/mapfiles/kml/shapes/";

// interesting icons for placemarks
const char *ICONS[] = {
    "cabs.png",
    "bus.png",
    "rail.png",
    "truck.png",
    "airports.png",
    "ferry.png",
    "heliport.png",
    "tram.png",
    "sailing.png"
};

// template to get the number of items in an array
template <typename T, int N> size_t sizeof_array( T (&)[N] ) { return N; }

void icon_setup(KmlFactory* kmlFac, DocumentPtr document)
{
    // set up styles for each icon we use, using the icon name as the id
    for (size_t i = 0; i < sizeof_array(ICONS); ++i) {
        IconStyleIconPtr icon(kmlFac->CreateIconStyleIcon());
        std::string url(BASE_ICON_URL + ICONS[i]);
        icon->set_href(url.c_str());

        IconStylePtr iconStyle(kmlFac->CreateIconStyle());
        iconStyle->set_icon(icon);

        StylePtr style(kmlFac->CreateStyle());
        style->set_iconstyle(iconStyle);
        style->set_id(ICONS[i]);

        document->add_styleselector(style);
    }
}

/*
 * For testing purposes, randomly select an interesting icon to replace the default yellow pushpin.
 * In order to use the same icon between invocations of write_kml(), a map is used to store the icon
 * selected.
 */
void set_node_icon(const WatcherGraphNode& node, PlacemarkPtr ptr)
{
    std::string styleUrl;
    NodeIconMapIterator it = nodeIconMap.find(node.nodeId);
    if (it == nodeIconMap.end()) {
        styleUrl = std::string("#") + ICONS[ random() % sizeof_array(ICONS) ];
        nodeIconMap[node.nodeId] = styleUrl;
    } else
        styleUrl = it->second;
    ptr->set_styleurl(styleUrl);
}
#endif // CUSTOM_ICON

void output_nodes(KmlFactory* kmlFac, const WatcherGraph& graph, FolderPtr folder)
{
    // iterate over all nodes in the graph
    WatcherGraph::vertexIterator vi, vend;
    for (tie(vi, vend) = vertices(graph.theGraph); vi != vend; ++vi) {
        const WatcherGraphNode &node = graph.theGraph[*vi];

        PlacemarkPtr ptr = kmlFac->CreatePlacemark();
        std::string ip(node.nodeId.to_string());
        ptr->set_name(ip); // textual label, can be html
        //ptr->set_id(ip); // internal label for locating object in the DOM tree

        // set the location
        ptr->set_geometry(kmlconvenience::CreatePointLatLon(node.gpsData->y, node.gpsData->x));

        /*
        //target id is required when changing some attribute of a feature already in the dom
        ptr->set_targetid("node0");
        */

#ifdef CUSTOM_ICON
        set_node_icon(node, ptr);
#endif

        folder->add_feature(ptr);//add to DOM
    }
}

/*
 * Convert a watcher color to the KML format.
 */
std::string watcher_color_to_kml(const watcher::Color& color)
{
    char buf[sizeof("aabbggrr")];
    sprintf(buf, "%02x%02x%02x%02x", color.a, color.b, color.g, color.r);
    return std::string(buf);
}

void output_edges(KmlFactory* kmlFac, const WatcherGraph& graph, DocumentPtr document, FolderPtr folder)
{
    WatcherGraph::edgeIterator ei, eend;
    unsigned int count = 0;
    bool has_style = false;

    for (tie(ei, eend) = edges(graph.theGraph); ei != eend; ++ei, ++count) {
        const WatcherGraphEdge &edge = graph.theGraph[*ei];
        const WatcherGraphNode &node1 = graph.theGraph[source(*ei, graph.theGraph)];
        const WatcherGraphNode &node2 = graph.theGraph[target(*ei, graph.theGraph)];

        CoordinatesPtr coords = kmlFac->CreateCoordinates();
        coords->add_latlng(node1.gpsData->y, node1.gpsData->x);
        coords->add_latlng(node2.gpsData->y, node2.gpsData->x);

        LineStringPtr lineString = kmlFac->CreateLineString();
        lineString->set_coordinates(coords);

        CoordinatesPtr pointCoords(kmlFac->CreateCoordinates());
        //place label at the midpoint on the line between the two nodes
        pointCoords->add_latlng((node1.gpsData->y + node2.gpsData->y)/2,(node1.gpsData->x + node2.gpsData->x)/2);

        PointPtr point(kmlFac->CreatePoint());
        point->set_coordinates(pointCoords);

        /*
         * Google Earth doesn't allow a label to be attached to something without a Point, so
         * we need to create a container with the LineString and the Point at which to attach
         * the label/icon.
         */
        MultiGeometryPtr multiGeo(kmlFac->CreateMultiGeometry());
        multiGeo->add_geometry(lineString);
        multiGeo->add_geometry(point);

        PlacemarkPtr ptr = kmlFac->CreatePlacemark();
        ptr->set_geometry(multiGeo);

        // WatcherGraphEdge supports multiple labels per edge, but GE only supports one, so just use the first label in the list
        LabelDisplayInfoPtr labelDisplayInfo(edge.labels.front());
        if (labelDisplayInfo) {
            ptr->set_name(labelDisplayInfo->labelText);
        }

        // WatcherGraph only has a single style per layer, so we stash the value somewhere instead of creating multiple styles
        if (!has_style) {
            LineStylePtr lineStyle(kmlFac->CreateLineStyle());
            lineStyle->set_color(watcher_color_to_kml(edge.displayInfo->color));
            lineStyle->set_width(edge.displayInfo->width);

            IconStylePtr iconStyle(kmlFac->CreateIconStyle());
            iconStyle->set_scale(0); // scale to 0 should mean hide it?

            StylePtr style(kmlFac->CreateStyle());
            style->set_id("edge-style");
            style->set_linestyle(lineStyle);
            style->set_iconstyle(iconStyle);

            if (labelDisplayInfo) {
                LabelStylePtr labelStyle(kmlFac->CreateLabelStyle());
                labelStyle->set_color(watcher_color_to_kml(labelDisplayInfo->foregroundColor));
                style->set_labelstyle(labelStyle);
            }

            document->add_styleselector(style);
            has_style = true;
        }

        ptr->set_styleurl("#edge-style");

        folder->add_feature(ptr);//add to DOM
    }
}

} // end namespace

void write_kml(const WatcherGraph& graph, const std::string& outputFile)
{
    // libkml boilerplate
    KmlFactory* kmlFac(kmldom::KmlFactory::GetFactory());
    KmlPtr kml(kmlFac->CreateKml());

    /*
     * create initial DOM tree.  As nodes appear, they get added to the tree.
     * As nodes move, we update the position attribute
     * in the DOM, and regenerate the KML file.
     */
    DocumentPtr document(kmlFac->CreateDocument());
    kml->set_feature(document);

    FolderPtr folder(kmlFac->CreateFolder());
    folder->set_name("Watcher");
    document->add_feature(folder);

#ifdef CUSTOM_ICON
    icon_setup(kmlFac, document);
#endif // CUSTOM_ICON
    output_nodes(kmlFac, graph, folder);
    output_edges(kmlFac, graph, document, folder);

    kmlbase::File::WriteStringToFile(kmldom::SerializePretty(kml), outputFile);
}
<|endoftext|>


<commit_before>#include "VulkanGraphicsPipeline.hpp"

#include "default.vert.spv.hpp"

VulkanGraphicsPipeline::VulkanGraphicsPipeline(VkDevice device, VkExtent2D swapChainExtent) : vertexShader(DEFAULT_VERT_SPV, DEFAULT_VERT_SPV_LENGTH, device) {
    // Create shader stages.
    VkPipelineShaderStageCreateInfo vertexShaderStageCreateInfo = createShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexShader.getModule());

    VkPipelineShaderStageCreateInfo shaderStages[] = {vertexShaderStageCreateInfo};

    // Vertex input.
    VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
    vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    vertexInputInfo.vertexBindingDescriptionCount = 0;
    vertexInputInfo.pVertexBindingDescriptions = nullptr;
    vertexInputInfo.vertexAttributeDescriptionCount = 0;
    vertexInputInfo.pVertexAttributeDescriptions = nullptr;

    // Input assembly.
    VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
    inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    inputAssembly.primitiveRestartEnable = VK_FALSE;

    // Viewport.
    VkViewport viewport = {};
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = swapChainExtent.width;
    viewport.height = swapChainExtent.height;
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;
}

VulkanGraphicsPipeline::~VulkanGraphicsPipeline() {

}

VkPipelineShaderStageCreateInfo VulkanGraphicsPipeline::createShaderStage(VkShaderStageFlagBits flags, VkShaderModule module) {
    VkPipelineShaderStageCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    createInfo.stage = flags;
    createInfo.module = module;
    createInfo.pName = "main";

    return createInfo;
}
<commit_msg>Scissor<commit_after>#include "VulkanGraphicsPipeline.hpp"

#include "default.vert.spv.hpp"

VulkanGraphicsPipeline::VulkanGraphicsPipeline(VkDevice device, VkExtent2D swapChainExtent) : vertexShader(DEFAULT_VERT_SPV, DEFAULT_VERT_SPV_LENGTH, device) {
    // Create shader stages.
    VkPipelineShaderStageCreateInfo vertexShaderStageCreateInfo = createShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexShader.getModule());

    VkPipelineShaderStageCreateInfo shaderStages[] = {vertexShaderStageCreateInfo};

    // Vertex input.
    VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
    vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    vertexInputInfo.vertexBindingDescriptionCount = 0;
    vertexInputInfo.pVertexBindingDescriptions = nullptr;
    vertexInputInfo.vertexAttributeDescriptionCount = 0;
    vertexInputInfo.pVertexAttributeDescriptions = nullptr;

    // Input assembly.
    VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
    inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    inputAssembly.primitiveRestartEnable = VK_FALSE;

    // Viewport.
    VkViewport viewport = {};
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = swapChainExtent.width;
    viewport.height = swapChainExtent.height;
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;

    // Scissor.
    VkRect2D scissor = {};
    scissor.offset = {0, 0};
    scissor.extent = swapChainExtent;
}

VulkanGraphicsPipeline::~VulkanGraphicsPipeline() {

}

VkPipelineShaderStageCreateInfo VulkanGraphicsPipeline::createShaderStage(VkShaderStageFlagBits flags, VkShaderModule module) {
    VkPipelineShaderStageCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    createInfo.stage = flags;
    createInfo.module = module;
    createInfo.pName = "main";

    return createInfo;
}
<|endoftext|>


<commit_before>#include "AbsPositionOverview.h"

AbsPositionOverview::AbsPositionOverview(QObject *parent) :
    QObject(parent)
{
}

void AbsPositionOverview::parseGpsRawInt(LinkInterface *link, const mavlink_message_t &message, const mavlink_gps_raw_int_t &state)
{
    if (state.fix_type > 2)
    {
        this->setLat(state.lat);
        this->setLon(state.lon);
        this->setAlt(state.alt);
        this->setVel(state.vel);
    }
}

void AbsPositionOverview::parseGlobalPositionInt(LinkInterface *link, const mavlink_message_t &message, const mavlink_global_position_int_t &state)
{
    this->setRelativeAlt(state.relative_alt);
}

void AbsPositionOverview::messageReceived(LinkInterface* link,mavlink_message_t message)
{
    switch (message.msgid)
    {
        case MAVLINK_MSG_ID_GPS_RAW_INT:
        {
            mavlink_gps_raw_int_t state;
            mavlink_msg_gps_raw_int_decode(&message, &state);
            parseGpsRawInt(link,message,state);
            break;
        }
        case MAVLINK_MSG_ID_GLOBAL_POSITION_INT:
        {
            mavlink_global_position_int_t state;
            mavlink_msg_global_position_int_decode(&message, &state);
            parseGlobalPositionInt(link,message,state);
            break;
        }
    }
}
<commit_msg>UAS: Fix altitude not being reported in metres to PFD<commit_after>#include "AbsPositionOverview.h"

AbsPositionOverview::AbsPositionOverview(QObject *parent) :
    QObject(parent)
{
}

void AbsPositionOverview::parseGpsRawInt(LinkInterface *link, const mavlink_message_t &message, const mavlink_gps_raw_int_t &state)
{
    if (state.fix_type > 2)
    {
        this->setLat(state.lat);
        this->setLon(state.lon);
        this->setAlt(state.alt);
        this->setVel(state.vel);
    }
}

void AbsPositionOverview::parseGlobalPositionInt(LinkInterface *link, const mavlink_message_t &message, const mavlink_global_position_int_t &state)
{
    this->setRelativeAlt(state.relative_alt/1000.0);
}

void AbsPositionOverview::messageReceived(LinkInterface* link,mavlink_message_t message)
{
    switch (message.msgid)
    {
        case MAVLINK_MSG_ID_GPS_RAW_INT:
        {
            mavlink_gps_raw_int_t state;
            mavlink_msg_gps_raw_int_decode(&message, &state);
            parseGpsRawInt(link,message,state);
            break;
        }
        case MAVLINK_MSG_ID_GLOBAL_POSITION_INT:
        {
            mavlink_global_position_int_t state;
            mavlink_msg_global_position_int_decode(&message, &state);
            parseGlobalPositionInt(link,message,state);
            break;
        }
    }
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stx/exception.h>
#include <stx/uri.h>
#include <stx/protobuf/msg.h>
#include <stx/csv/CSVInputStream.h>
#include <common/CustomerDirectory.h>

using namespace stx;

namespace cm {

CustomerDirectory::CustomerDirectory(
    const String& path,
    const InetAddr master_addr) :
    master_addr_(master_addr) {
  mdb::MDBOptions mdb_opts;
  mdb_opts.data_filename = "cdb.db",
  mdb_opts.lock_filename = "cdb.db.lck";
  mdb_opts.duplicate_keys = false;

  db_ = mdb::MDB::open(path, mdb_opts);

  listCustomers([this] (const CustomerConfig& cfg) {
    customers_.emplace(cfg.customer(), new CustomerConfigRef(cfg));
  });

  sync();
}

RefPtr<CustomerConfigRef> CustomerDirectory::configFor(
    const String& customer_key) const {
  std::unique_lock<std::mutex> lk(mutex_);

  auto iter = customers_.find(customer_key);
  if (iter == customers_.end()) {
    RAISEF(kNotFoundError, "customer not found: $0", customer_key);
  }

  return iter->second;
}

void CustomerDirectory::listCustomers(
    Function<void (const CustomerConfig& cfg)> fn) const {
  auto prefix = "cfg~";

  Buffer key;
  Buffer value;

  auto txn = db_->startTransaction(true);
  txn->autoAbort();

  auto cursor = txn->getCursor();
  key.append(prefix);

  if (!cursor->getFirstOrGreater(&key, &value)) {
    return;
  }

  do {
    if (!StringUtil::beginsWith(key.toString(), prefix)) {
      break;
    }

    fn(msg::decode<CustomerConfig>(value));
  } while (cursor->getNext(&key, &value));

  cursor->close();
}

void CustomerDirectory::onCustomerConfigChange(
    Function<void (const CustomerConfig& cfg)> fn) {
  std::unique_lock<std::mutex> lk(mutex_);
  on_customer_change_.emplace_back(fn);
}

void CustomerDirectory::addTableDefinition(const TableDefinition& table) {
  std::unique_lock<std::mutex> lk(mutex_);

  if (!StringUtil::isShellSafe(table.table_name())) {
    RAISEF(
        kIllegalArgumentError,
        "invalid table name: '$0'",
        table.table_name());
  }

  if (!table.has_schema_name() && !table.has_schema_inline()) {
    RAISEF(
        kIllegalArgumentError,
        "can't add table without a schema: '$0'",
        table.table_name());
  }

  auto db_key = StringUtil::format(
      "tbl~$0~$1",
      table.customer(),
      table.table_name());

  auto buf = msg::encode(table);

  auto txn = db_->startTransaction(false);
  txn->autoAbort();

  txn->insert(db_key.data(), db_key.size(), buf->data(), buf->size());
  txn->commit();

  for (const auto& cb : on_table_change_) {
    cb(table);
  }
}

void CustomerDirectory::updateTableDefinition(const TableDefinition& table) {
  std::unique_lock<std::mutex> lk(mutex_);

  if (!StringUtil::isShellSafe(table.table_name())) {
    RAISEF(
        kIllegalArgumentError,
        "invalid table name: '$0'",
        table.table_name());
  }

  if (!table.has_schema_name() && !table.has_schema_inline()) {
    RAISEF(
        kIllegalArgumentError,
        "can't add table without a schema: '$0'",
        table.table_name());
  }

  auto db_key = StringUtil::format(
      "tbl~$0~$1",
      table.customer(),
      table.table_name());

  auto buf = msg::encode(table);

  auto txn = db_->startTransaction(false);
  txn->autoAbort();

  txn->update(db_key.data(), db_key.size(), buf->data(), buf->size());
  txn->commit();

  for (const auto& cb : on_table_change_) {
    cb(table);
  }
}

void CustomerDirectory::listTableDefinitions(
    Function<void (const TableDefinition& table)> fn) const {
  auto prefix = "tbl~";

  Buffer key;
  Buffer value;

  auto txn = db_->startTransaction(true);
  txn->autoAbort();

  auto cursor = txn->getCursor();
  key.append(prefix);

  if (!cursor->getFirstOrGreater(&key, &value)) {
    return;
  }

  do {
    if (!StringUtil::beginsWith(key.toString(), prefix)) {
      break;
    }

    fn(msg::decode<TableDefinition>(value));
  } while (cursor->getNext(&key, &value));

  cursor->close();
}

void CustomerDirectory::onTableDefinitionChange(
    Function<void (const TableDefinition& tbl)> fn) {
  std::unique_lock<std::mutex> lk(mutex_);
  on_table_change_.emplace_back(fn);
}

void CustomerDirectory::sync() {
  auto master_heads = fetchMasterHeads();

  Set<String> needs_update;
  {
    auto txn = db_->startTransaction(true);
    txn->autoAbort();

    for (const auto& head : master_heads) {
      auto db_key = StringUtil::format("head~$0", head.first);
      auto vstr = StringUtil::toString(head.second);

      auto lastver = txn->get(db_key);
      if (lastver.isEmpty() || lastver.get().toString() != vstr) {
        needs_update.emplace(head.first);
      }
    }

    txn->abort();
  }

  for (const auto& obj : needs_update) {
    syncObject(obj);
  }
}

void CustomerDirectory::syncObject(const String& obj) {
  logDebug("analyticsd", "Syncing config object '$0' from master", obj);

  static const String kCustomerPrefix = "customers/";
  if (StringUtil::beginsWith(obj, kCustomerPrefix)) {
    syncCustomerConfig(obj.substr(kCustomerPrefix.size()));
  }
}

void CustomerDirectory::syncCustomerConfig(const String& customer) {

  iputs("get customer config for: $0", customer);
  std::unique_lock<std::mutex> lk(mutex_);
  //auto db_key = StringUtil::format("cfg~$0", config.customer());
  //auto buf = msg::encode(config);

  //auto txn = db_->startTransaction(false);
  //txn->autoAbort();

  //txn->update(db_key.data(), db_key.size(), buf->data(), buf->size());
  //txn->commit();

  //customers_.emplace(config.customer(), new CustomerConfigRef(config));

  //for (const auto& cb : on_customer_change_) {
  //  cb(config);
  //}
}

HashMap<String, uint64_t> CustomerDirectory::fetchMasterHeads() const {
  auto uri = URI(
      StringUtil::format(
          "http://$0/analytics/master/heads",
          master_addr_.hostAndPort()));

  http::HTTPClient http;
  auto res = http.executeRequest(http::HTTPRequest::mkGet(uri));
  if (res.statusCode() != 200) {
    RAISEF(kRuntimeError, "error: $0", res.body().toString());
  }

  DefaultCSVInputStream csv(BufferInputStream::fromBuffer(&res.body()), '=');

  HashMap<String, uint64_t> heads;
  Vector<String> row;
  while (csv.readNextRow(&row)) {
    if (row.size() != 2) {
      RAISE(kRuntimeError, "invalid response");
    }

    heads[row[0]] = std::stoull(row[1]);
  }

  return heads;
}

} // namespace cm
<commit_msg>CustomerDirectory::syncCustomerConfig impl<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stx/exception.h>
#include <stx/uri.h>
#include <stx/protobuf/msg.h>
#include <stx/csv/CSVInputStream.h>
#include <common/CustomerDirectory.h>

using namespace stx;

namespace cm {

CustomerDirectory::CustomerDirectory(
    const String& path,
    const InetAddr master_addr) :
    master_addr_(master_addr) {
  mdb::MDBOptions mdb_opts;
  mdb_opts.data_filename = "cdb.db",
  mdb_opts.lock_filename = "cdb.db.lck";
  mdb_opts.duplicate_keys = false;

  db_ = mdb::MDB::open(path, mdb_opts);

  listCustomers([this] (const CustomerConfig& cfg) {
    customers_.emplace(cfg.customer(), new CustomerConfigRef(cfg));
  });

  sync();
}

RefPtr<CustomerConfigRef> CustomerDirectory::configFor(
    const String& customer_key) const {
  std::unique_lock<std::mutex> lk(mutex_);

  auto iter = customers_.find(customer_key);
  if (iter == customers_.end()) {
    RAISEF(kNotFoundError, "customer not found: $0", customer_key);
  }

  return iter->second;
}

void CustomerDirectory::listCustomers(
    Function<void (const CustomerConfig& cfg)> fn) const {
  auto prefix = "cfg~";

  Buffer key;
  Buffer value;

  auto txn = db_->startTransaction(true);
  txn->autoAbort();

  auto cursor = txn->getCursor();
  key.append(prefix);

  if (!cursor->getFirstOrGreater(&key, &value)) {
    return;
  }

  do {
    if (!StringUtil::beginsWith(key.toString(), prefix)) {
      break;
    }

    fn(msg::decode<CustomerConfig>(value));
  } while (cursor->getNext(&key, &value));

  cursor->close();
}

void CustomerDirectory::onCustomerConfigChange(
    Function<void (const CustomerConfig& cfg)> fn) {
  std::unique_lock<std::mutex> lk(mutex_);
  on_customer_change_.emplace_back(fn);
}

void CustomerDirectory::addTableDefinition(const TableDefinition& table) {
  std::unique_lock<std::mutex> lk(mutex_);

  if (!StringUtil::isShellSafe(table.table_name())) {
    RAISEF(
        kIllegalArgumentError,
        "invalid table name: '$0'",
        table.table_name());
  }

  if (!table.has_schema_name() && !table.has_schema_inline()) {
    RAISEF(
        kIllegalArgumentError,
        "can't add table without a schema: '$0'",
        table.table_name());
  }

  auto db_key = StringUtil::format(
      "tbl~$0~$1",
      table.customer(),
      table.table_name());

  auto buf = msg::encode(table);

  auto txn = db_->startTransaction(false);
  txn->autoAbort();

  txn->insert(db_key.data(), db_key.size(), buf->data(), buf->size());
  txn->commit();

  for (const auto& cb : on_table_change_) {
    cb(table);
  }
}

void CustomerDirectory::updateTableDefinition(const TableDefinition& table) {
  std::unique_lock<std::mutex> lk(mutex_);

  if (!StringUtil::isShellSafe(table.table_name())) {
    RAISEF(
        kIllegalArgumentError,
        "invalid table name: '$0'",
        table.table_name());
  }

  if (!table.has_schema_name() && !table.has_schema_inline()) {
    RAISEF(
        kIllegalArgumentError,
        "can't add table without a schema: '$0'",
        table.table_name());
  }

  auto db_key = StringUtil::format(
      "tbl~$0~$1",
      table.customer(),
      table.table_name());

  auto buf = msg::encode(table);

  auto txn = db_->startTransaction(false);
  txn->autoAbort();

  txn->update(db_key.data(), db_key.size(), buf->data(), buf->size());
  txn->commit();

  for (const auto& cb : on_table_change_) {
    cb(table);
  }
}

void CustomerDirectory::listTableDefinitions(
    Function<void (const TableDefinition& table)> fn) const {
  auto prefix = "tbl~";

  Buffer key;
  Buffer value;

  auto txn = db_->startTransaction(true);
  txn->autoAbort();

  auto cursor = txn->getCursor();
  key.append(prefix);

  if (!cursor->getFirstOrGreater(&key, &value)) {
    return;
  }

  do {
    if (!StringUtil::beginsWith(key.toString(), prefix)) {
      break;
    }

    fn(msg::decode<TableDefinition>(value));
  } while (cursor->getNext(&key, &value));

  cursor->close();
}

void CustomerDirectory::onTableDefinitionChange(
    Function<void (const TableDefinition& tbl)> fn) {
  std::unique_lock<std::mutex> lk(mutex_);
  on_table_change_.emplace_back(fn);
}

void CustomerDirectory::sync() {
  auto master_heads = fetchMasterHeads();

  Set<String> needs_update;
  {
    auto txn = db_->startTransaction(true);
    txn->autoAbort();

    for (const auto& head : master_heads) {
      auto db_key = StringUtil::format("head~$0", head.first);
      auto vstr = StringUtil::toString(head.second);

      auto lastver = txn->get(db_key);
      if (lastver.isEmpty() || lastver.get().toString() != vstr) {
        needs_update.emplace(head.first);
      }
    }

    txn->abort();
  }

  for (const auto& obj : needs_update) {
    syncObject(obj);
  }
}

void CustomerDirectory::syncObject(const String& obj) {
  logDebug("analyticsd", "Syncing config object '$0' from master", obj);

  static const String kCustomerPrefix = "customers/";
  if (StringUtil::beginsWith(obj, kCustomerPrefix)) {
    syncCustomerConfig(obj.substr(kCustomerPrefix.size()));
  }
}

void CustomerDirectory::syncCustomerConfig(const String& customer) {
  auto uri = URI(
      StringUtil::format(
          "http://$0/analytics/master/customer_config?customer=$1",
          master_addr_.hostAndPort(),
          URI::urlEncode(customer)));

  http::HTTPClient http;
  auto res = http.executeRequest(http::HTTPRequest::mkGet(uri));
  if (res.statusCode() != 200) {
    RAISEF(kRuntimeError, "error: $0", res.body().toString());
  }

  const auto& buf = res.body();
  auto config = msg::decode<CustomerConfig>(buf);
  auto db_key = StringUtil::format("cfg~$0", config.customer());

  std::unique_lock<std::mutex> lk(mutex_);
  auto txn = db_->startTransaction(false);
  txn->autoAbort();

  txn->update(db_key.data(), db_key.size(), buf.data(), buf.size());
  txn->commit();

  customers_.emplace(config.customer(), new CustomerConfigRef(config));

  for (const auto& cb : on_customer_change_) {
    cb(config);
  }
}

HashMap<String, uint64_t> CustomerDirectory::fetchMasterHeads() const {
  auto uri = URI(
      StringUtil::format(
          "http://$0/analytics/master/heads",
          master_addr_.hostAndPort()));

  http::HTTPClient http;
  auto res = http.executeRequest(http::HTTPRequest::mkGet(uri));
  if (res.statusCode() != 200) {
    RAISEF(kRuntimeError, "error: $0", res.body().toString());
  }

  DefaultCSVInputStream csv(BufferInputStream::fromBuffer(&res.body()), '=');

  HashMap<String, uint64_t> heads;
  Vector<String> row;
  while (csv.readNextRow(&row)) {
    if (row.size() != 2) {
      RAISE(kRuntimeError, "invalid response");
    }

    heads[row[0]] = std::stoull(row[1]);
  }

  return heads;
}

} // namespace cm
<|endoftext|>