<commit_before>#include "precompiled.hpp"
#include "util/MetaTypeConverters.hpp"
#include "util/TypeInfo.hpp"

#include <QString>
#include <QtTest>
#include <QList>
#include <QCoreApplication>
#include "ui/Uniforms.hpp"
#include "util/Logging.hpp"

using namespace glm;
using balls::Uniforms;
using balls::UniformCollection;
using balls::UniformInfo;

class TestUniformsTest : public QObject {
  Q_OBJECT

private slots:
  void initTestCase();

  void elapsedTimeIncreases();
  void elapsedTimeIsUInt();
  void filtersEvents();
  void mouseCoordinates();
  void mouseCoordinates_data();
  void receiveUniforms();
  void receiveUniforms_data();
};

void TestUniformsTest::initTestCase() {
  QLoggingCategory::setFilterRules("uniform*=false");
  balls::registerMetaTypeConverters();
  balls::util::types::init();

  QVERIFY(balls::util::types::info.size() > 0);
  QVERIFY(QMetaType::type("vec2") != QMetaType::UnknownType);
}

void TestUniformsTest::receiveUniforms_data() {
  QTest::addColumn<UniformCollection>("beforeCompile");
  QTest::addColumn<UniformCollection>("result");

  QTest::newRow("empty -> empty")
      << UniformCollection {}
      << UniformCollection {};

  QTest::newRow("empty -> one matrix")
  << UniformCollection {
  }
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  };

  QTest::newRow("one matrix -> empty")
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  }
  << UniformCollection {
  };

  QTest::newRow("matrix -> vector")
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  }
  << UniformCollection {
    { "matrix", GL_FLOAT_VEC4, 1 }
  };

  QTest::newRow("rearrange")
  << UniformCollection {
    { "matrix", GL_DOUBLE_MAT4, 1},
    { "scale", GL_FLOAT, 1},
    { "available", GL_BOOL, 1},
  }
  << UniformCollection {
    { "available", GL_BOOL, 1},
    { "matrix", GL_DOUBLE_MAT4, 1},
    { "scale", GL_FLOAT, 1},
  };

  QTest::newRow("rearrange and change types")
  << UniformCollection {
    { "mvp", GL_FLOAT_MAT3, 1 },
    { "color", GL_FLOAT_VEC4, 1 },
    { "repetitions", GL_INT, 1 },
    { "seed", GL_INT_VEC2, 1 },
  }
  << UniformCollection {
    { "color", GL_DOUBLE_VEC4, 1 },
    { "seed", GL_UNSIGNED_INT, 1 },
    { "mvp", GL_FLOAT_MAT3x4, 1 },
    { "repetitions", GL_UNSIGNED_INT, 1 },
  };

  QTest::newRow("unsupported types")
  << UniformCollection {
    { "coords", GL_FLOAT_VEC2, 1 },
    { "threshold", GL_FLOAT, 1 },
  }
  << UniformCollection {
    { "threshold", GL_FLOAT, 1 },
    { "texture", GL_SAMPLER_2D, 1 },
    { "coords", GL_FLOAT_VEC2, 1 },
    { "palette", GL_INT_SAMPLER_1D, 1 },
  };
}

void TestUniformsTest::receiveUniforms() {
  QFETCH(UniformCollection, beforeCompile);
  QFETCH(UniformCollection, result);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  uniforms.receiveUniforms(beforeCompile);

  QCOMPARE(uniforms.uniformInfo(), beforeCompile);

  uniforms.receiveUniforms(result);

  QCOMPARE(uniforms.uniformInfo(), result);
}

void TestUniformsTest::mouseCoordinates_data() {
  QTest::addColumn<QList<QPoint>>("points");

  using QPointList = QList<QPoint>;
  QTest::newRow("basic") << QPointList {{1, 1}, {42, 43}, {100, 99}};
  QTest::newRow("not moving") << QPointList {{0, 0}, {0, 0}, {0, 0}};
  QTest::newRow("negative") << QPointList {{ -1, -43}, { -119, -32}, { -55, -22}};
  QTest::newRow("change signs") << QPointList {{0, 0}, {34, -66}, { -22, 78}, { -99, -101}, {0, 0}, {1, 0}};
}

void TestUniformsTest::mouseCoordinates() {
  QFETCH(QList<QPoint>, points);
  Q_ASSERT(points.length() >= 2);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  for (const QPoint& p : points) {
    QMouseEvent event(QEvent::MouseMove, p, Qt::NoButton, Qt::NoButton,
                      Qt::NoModifier);
    QCoreApplication::sendEvent(&object, &event);
    // WORKAROUND: QTest::mouseMove doesn't actually fire a QMouseEvent (it just sets the mouse cursor)
  }

  ivec2 mousePos = uniforms.property("mousePos").value<ivec2>();
  ivec2 lastMousePos = uniforms.property("lastMousePos").value<ivec2>();

  const QPoint& pos = points.last();
  const QPoint& lastPos = points[points.length() - 2];

  QCOMPARE(mousePos.x, pos.x());
  QCOMPARE(mousePos.y, pos.y());

  QCOMPARE(lastMousePos.x, lastPos.x());
  QCOMPARE(lastMousePos.y, lastPos.y());
}

void TestUniformsTest::filtersEvents() {
  QObject object;
  Uniforms uniforms;
  QSize size {100, 100};
  QResizeEvent resize {size, {2, 2}};

  object.installEventFilter(&uniforms);
  QCoreApplication::sendEvent(&object, &resize);

  ivec2 canvasSize = uniforms.property("canvasSize").value<ivec2>();

  QCOMPARE(canvasSize.x, size.width());
  QCOMPARE(canvasSize.y, size.height());
}

void TestUniformsTest::elapsedTimeIsUInt() {
  Uniforms uniforms;

  QVariant a = uniforms.property("elapsedTime");
  QCOMPARE(a.type(), QVariant::UInt);
}

void TestUniformsTest::elapsedTimeIncreases() {
  Uniforms uniforms;

  QTest::qSleep(50);
  uint a = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint b = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint c = uniforms.property("elapsedTime").toUInt();

  QVERIFY(a != 0);
  QVERIFY(b != 0);
  QVERIFY(c != 0);
  QVERIFY(b > a);
  QVERIFY(c > b);
}

QTEST_MAIN(TestUniformsTest)

#include "tst_TestUniformsTest.moc"
<commit_msg>Add a mouseCoordinates test datum<commit_after>#include "precompiled.hpp"
#include "util/MetaTypeConverters.hpp"
#include "util/TypeInfo.hpp"

#include <QString>
#include <QtTest>
#include <QList>
#include <QCoreApplication>
#include "ui/Uniforms.hpp"
#include "util/Logging.hpp"

using namespace glm;
using balls::Uniforms;
using balls::UniformCollection;
using balls::UniformInfo;

class TestUniformsTest : public QObject {
  Q_OBJECT

private slots:
  void initTestCase();

  void elapsedTimeIncreases();
  void elapsedTimeIsUInt();
  void filtersEvents();
  void mouseCoordinates();
  void mouseCoordinates_data();
  void receiveUniforms();
  void receiveUniforms_data();
};

void TestUniformsTest::initTestCase() {
  QLoggingCategory::setFilterRules("uniform*=false");
  balls::registerMetaTypeConverters();
  balls::util::types::init();

  QVERIFY(balls::util::types::info.size() > 0);
  QVERIFY(QMetaType::type("vec2") != QMetaType::UnknownType);
}

void TestUniformsTest::receiveUniforms_data() {
  QTest::addColumn<UniformCollection>("beforeCompile");
  QTest::addColumn<UniformCollection>("result");

  QTest::newRow("empty -> empty")
      << UniformCollection {}
      << UniformCollection {};

  QTest::newRow("empty -> one matrix")
  << UniformCollection {
  }
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  };

  QTest::newRow("one matrix -> empty")
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  }
  << UniformCollection {
  };

  QTest::newRow("matrix -> vector")
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  }
  << UniformCollection {
    { "matrix", GL_FLOAT_VEC4, 1 }
  };

  QTest::newRow("rearrange")
  << UniformCollection {
    { "matrix", GL_DOUBLE_MAT4, 1},
    { "scale", GL_FLOAT, 1},
    { "available", GL_BOOL, 1},
  }
  << UniformCollection {
    { "available", GL_BOOL, 1},
    { "matrix", GL_DOUBLE_MAT4, 1},
    { "scale", GL_FLOAT, 1},
  };

  QTest::newRow("rearrange and change types")
  << UniformCollection {
    { "mvp", GL_FLOAT_MAT3, 1 },
    { "color", GL_FLOAT_VEC4, 1 },
    { "repetitions", GL_INT, 1 },
    { "seed", GL_INT_VEC2, 1 },
  }
  << UniformCollection {
    { "color", GL_DOUBLE_VEC4, 1 },
    { "seed", GL_UNSIGNED_INT, 1 },
    { "mvp", GL_FLOAT_MAT3x4, 1 },
    { "repetitions", GL_UNSIGNED_INT, 1 },
  };

  QTest::newRow("unsupported types")
  << UniformCollection {
    { "coords", GL_FLOAT_VEC2, 1 },
    { "threshold", GL_FLOAT, 1 },
  }
  << UniformCollection {
    { "threshold", GL_FLOAT, 1 },
    { "texture", GL_SAMPLER_2D, 1 },
    { "coords", GL_FLOAT_VEC2, 1 },
    { "palette", GL_INT_SAMPLER_1D, 1 },
  };
}

void TestUniformsTest::receiveUniforms() {
  QFETCH(UniformCollection, beforeCompile);
  QFETCH(UniformCollection, result);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  uniforms.receiveUniforms(beforeCompile);

  QCOMPARE(uniforms.uniformInfo(), beforeCompile);

  uniforms.receiveUniforms(result);

  QCOMPARE(uniforms.uniformInfo(), result);
}

void TestUniformsTest::mouseCoordinates_data() {
  QTest::addColumn<QList<QPoint>>("points");

  using QPointList = QList<QPoint>;
  QTest::newRow("basic") << QPointList {{1, 1}, {42, 43}, {100, 99}};
  QTest::newRow("one motion") << QPointList {{0, 0}, {77, 74}};
  QTest::newRow("not moving") << QPointList {{0, 0}, {0, 0}, {0, 0}};
  QTest::newRow("negative") << QPointList {{ -1, -43}, { -119, -32}, { -55, -22}};
  QTest::newRow("change signs") << QPointList {{0, 0}, {34, -66}, { -22, 78}, { -99, -101}, {0, 0}, {1, 0}};
}

void TestUniformsTest::mouseCoordinates() {
  QFETCH(QList<QPoint>, points);
  Q_ASSERT(points.length() >= 2);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  for (const QPoint& p : points) {
    QMouseEvent event(QEvent::MouseMove, p, Qt::NoButton, Qt::NoButton,
                      Qt::NoModifier);
    QCoreApplication::sendEvent(&object, &event);
    // WORKAROUND: QTest::mouseMove doesn't actually fire a QMouseEvent (it just sets the mouse cursor)
  }

  ivec2 mousePos = uniforms.property("mousePos").value<ivec2>();
  ivec2 lastMousePos = uniforms.property("lastMousePos").value<ivec2>();

  const QPoint& pos = points.last();
  const QPoint& lastPos = points[points.length() - 2];

  QCOMPARE(mousePos.x, pos.x());
  QCOMPARE(mousePos.y, pos.y());

  QCOMPARE(lastMousePos.x, lastPos.x());
  QCOMPARE(lastMousePos.y, lastPos.y());
}

void TestUniformsTest::filtersEvents() {
  QObject object;
  Uniforms uniforms;
  QSize size {100, 100};
  QResizeEvent resize {size, {2, 2}};

  object.installEventFilter(&uniforms);
  QCoreApplication::sendEvent(&object, &resize);

  ivec2 canvasSize = uniforms.property("canvasSize").value<ivec2>();

  QCOMPARE(canvasSize.x, size.width());
  QCOMPARE(canvasSize.y, size.height());
}

void TestUniformsTest::elapsedTimeIsUInt() {
  Uniforms uniforms;

  QVariant a = uniforms.property("elapsedTime");
  QCOMPARE(a.type(), QVariant::UInt);
}

void TestUniformsTest::elapsedTimeIncreases() {
  Uniforms uniforms;

  QTest::qSleep(50);
  uint a = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint b = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint c = uniforms.property("elapsedTime").toUInt();

  QVERIFY(a != 0);
  QVERIFY(b != 0);
  QVERIFY(c != 0);
  QVERIFY(b > a);
  QVERIFY(c > b);
}

QTEST_MAIN(TestUniformsTest)

#include "tst_TestUniformsTest.moc"
<|endoftext|>


<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-2009 Torus Knot Software Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreShaderPrerequisites.h"
#include "OgreShaderRenderState.h"
#include "OgreShaderGenerator.h"
#include "OgreLogManager.h"
#include "OgreShaderProgram.h"
#include "OgreShaderProgramSet.h"
#include "OgreStringConverter.h"
#include "OgreShaderProgramManager.h"
#include "OgreShaderFFPRenderState.h"

namespace Ogre {
namespace RTShader {

//-----------------------------------------------------------------------
RenderState::RenderState()
{
	mSubRenderStateSortValid = false;
	mHashCodeValid			 = false;
	mHashCode				 = 0;
	mLightCountAutoUpdate	 = true;
}

//-----------------------------------------------------------------------
RenderState::~RenderState()
{
	reset();
}

//-----------------------------------------------------------------------
void RenderState::reset()
{
	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		ShaderGenerator::getSingleton().destroySubRenderState(*it);
	}
	mSubRenderStateList.clear();
}

//-----------------------------------------------------------------------
void RenderState::addSubRenderState(SubRenderState* subRenderState)
{
	mSubRenderStateList.push_back(subRenderState);
	mSubRenderStateSortValid = false;
	mHashCodeValid			 = false;
}

//-----------------------------------------------------------------------
void RenderState::removeSubRenderState(SubRenderState* subRenderState)
{
	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		if ((*it) == subRenderState)
		{
			ShaderGenerator::getSingleton().destroySubRenderState(*it);
			mSubRenderStateList.erase(it);
			break;
		}
	}
}

//-----------------------------------------------------------------------
void RenderState::merge(const RenderState& rhs, Pass* srcPass, Pass* dstPass)
{
	SubRenderStateList customSubRenderStates;

	// Sort current render states.
	sortSubRenderStates();

	// Insert all custom sub render states. (I.E Not FFP sub render states).
	for (SubRenderStateConstIterator itSrc=rhs.mSubRenderStateList.begin(); itSrc != rhs.mSubRenderStateList.end(); ++itSrc)
	{
		const SubRenderState* srcSubRenderState = *itSrc;
		bool isCustomSubRenderState = true;

		if (srcSubRenderState->getExecutionOrder() == FFP_TRANSFORM ||
			srcSubRenderState->getExecutionOrder() == FFP_COLOUR ||
			srcSubRenderState->getExecutionOrder() == FFP_LIGHTING ||
			srcSubRenderState->getExecutionOrder() == FFP_TEXTURING ||
			srcSubRenderState->getExecutionOrder() == FFP_FOG)
		{
			isCustomSubRenderState = false;
		}

		// Case it is a custom sub render state.
		if (isCustomSubRenderState)
		{
			bool subStateTypeExists = false;

			// Check if this type of sub render state already exists.
			for (SubRenderStateConstIterator itDst=mSubRenderStateList.begin(); itDst != mSubRenderStateList.end(); ++itDst)
			{
				if ((*itDst)->getType() == srcSubRenderState->getType())
				{
					subStateTypeExists = true;
					break;
				}
			}

			// Case custom sub render state not exits -> add it to custom list.
			if (subStateTypeExists == false)
			{
				SubRenderState* newSubRenderState = NULL;

				newSubRenderState = ShaderGenerator::getSingleton().createSubRenderState(srcSubRenderState->getType());
				*newSubRenderState = *srcSubRenderState;
				customSubRenderStates.push_back(newSubRenderState);
			}
		}
	}

	// Merge the local custom sub render states.
	for (SubRenderStateIterator itSrc=customSubRenderStates.begin(); itSrc != customSubRenderStates.end(); ++itSrc)
	{
		SubRenderState* customSubRenderState = *itSrc;

		if (customSubRenderState->preAddToRenderState(this, srcPass, dstPass))
		{
			addSubRenderState(customSubRenderState);
		}
		else
		{
			ShaderGenerator::getSingleton().destroySubRenderState(customSubRenderState);
		}
	}
}

//-----------------------------------------------------------------------
void RenderState::copyFrom(const RenderState& rhs)
{
	// Avoid copying on self.
	if (this == &rhs)
		return;

	// Reset state.
	reset();

	// Copy sub render states.
	for (SubRenderStateConstIterator it=rhs.mSubRenderStateList.begin(); it != rhs.mSubRenderStateList.end(); ++it)
	{
		const SubRenderState* srcSubRenderState = *it;
		SubRenderState* dstSubRenderState = ShaderGenerator::getSingleton().createSubRenderState(srcSubRenderState->getType());

		*dstSubRenderState = *srcSubRenderState;
		addSubRenderState(dstSubRenderState);
	}

	mSubRenderStateSortValid = rhs.mSubRenderStateSortValid;

	mLightCount[0] = rhs.mLightCount[0];
	mLightCount[1] = rhs.mLightCount[1];
	mLightCount[2] = rhs.mLightCount[2];
	mLightCountAutoUpdate = rhs.mLightCountAutoUpdate;

	mHashCode		= rhs.mHashCode;
	mHashCodeValid	= rhs.mHashCodeValid;
}

//-----------------------------------------------------------------------
RenderState& RenderState::operator=(const RenderState& rhs)
{
	copyFrom(rhs);

	return *this;
}

//-----------------------------------------------------------------------
uint32 RenderState::getHashCode()
{
	if (mHashCodeValid == false)
	{
		sortSubRenderStates();

		mHashCode = 0;

		for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
		{
			SubRenderState* srcSubRenderState = *it;
			uint32 subRenderStateHashCode = srcSubRenderState->getHashCode();

			sh_hash_combine(mHashCode, subRenderStateHashCode);
		}

		mHashCodeValid = true;
	}

	return mHashCode;
}

//-----------------------------------------------------------------------
void RenderState::sortSubRenderStates()
{
	if (mSubRenderStateSortValid == false)
	{
		if (mSubRenderStateList.size() > 1)
			qsort(&mSubRenderStateList[0], mSubRenderStateList.size(), sizeof(SubRenderState*), sSubRenderStateCompare);

		mSubRenderStateSortValid = true;
	}
}

//-----------------------------------------------------------------------
int	RenderState::sSubRenderStateCompare(const void * p0, const void *p1)
{
	SubRenderState* pInstance0 = *((SubRenderState**)p0);
	SubRenderState* pInstance1 = *((SubRenderState**)p1);

	return pInstance0->getExecutionOrder() - pInstance1->getExecutionOrder();
}

//-----------------------------------------------------------------------
bool RenderState::createCpuPrograms(ProgramSet* programSet)
{
	sortSubRenderStates();

	const String baseName = StringConverter::toString(getHashCode());
	Program* vsProgram = ProgramManager::getSingleton().createCpuProgram(GPT_VERTEX_PROGRAM);
	Program* psProgram = ProgramManager::getSingleton().createCpuProgram(GPT_FRAGMENT_PROGRAM);
	RTShader::Function* vsMainFunc = NULL;
	RTShader::Function* psMainFunc = NULL;

	programSet->setCpuVertexProgram(vsProgram);
	programSet->setCpuFragmentProgram(psProgram);

	// Create entry point functions.
	vsMainFunc = vsProgram->createFunction("main", "Vertex Program Entry point", Function::FFT_VS_MAIN);
	vsProgram->setEntryPointFunction(vsMainFunc);

	psMainFunc = psProgram->createFunction("main", "Pixel Program Entry point", Function::FFT_PS_MAIN);
	psProgram->setEntryPointFunction(psMainFunc);

	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		SubRenderState* srcSubRenderState = *it;

		if (false == srcSubRenderState->createCpuSubPrograms(programSet))
		{
			LogManager::getSingleton().stream()	<< "RTShader::RenderState : Could not generate sub render program of type: " << srcSubRenderState->getType();
			return false;
		}
	}

	return true;
}

//-----------------------------------------------------------------------
void RenderState::updateGpuProgramsParams(Renderable* rend, Pass* pass, const AutoParamDataSource* source,
										  const LightList* pLightList)
{
	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		SubRenderState* curSubRenderState = *it;

		curSubRenderState->updateGpuProgramsParams(rend, pass, source, pLightList);
	}
}

//-----------------------------------------------------------------------
void RenderState::setLightCount(const int lightCount[3])
{
	mLightCount[0] = lightCount[0];
	mLightCount[1] = lightCount[1];
	mLightCount[2] = lightCount[2];
}

//-----------------------------------------------------------------------
void RenderState::getLightCount(int lightCount[3]) const
{
	lightCount[0] = mLightCount[0];
	lightCount[1] = mLightCount[1];
	lightCount[2] = mLightCount[2];
}

}
}

<commit_msg>RTSS: Fixed an uninitialized variable crash (mLightCount in RenderState).<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-2009 Torus Knot Software Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreShaderPrerequisites.h"
#include "OgreShaderRenderState.h"
#include "OgreShaderGenerator.h"
#include "OgreLogManager.h"
#include "OgreShaderProgram.h"
#include "OgreShaderProgramSet.h"
#include "OgreStringConverter.h"
#include "OgreShaderProgramManager.h"
#include "OgreShaderFFPRenderState.h"

namespace Ogre {
namespace RTShader {

//-----------------------------------------------------------------------
RenderState::RenderState()
{
	mSubRenderStateSortValid = false;
	mHashCodeValid			 = false;
	mHashCode				 = 0;
	mLightCountAutoUpdate	 = true;

	mLightCount[0]				= 0;
	mLightCount[1]				= 0;
	mLightCount[2]				= 0;
}

//-----------------------------------------------------------------------
RenderState::~RenderState()
{
	reset();
}

//-----------------------------------------------------------------------
void RenderState::reset()
{
	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		ShaderGenerator::getSingleton().destroySubRenderState(*it);
	}
	mSubRenderStateList.clear();
}

//-----------------------------------------------------------------------
void RenderState::addSubRenderState(SubRenderState* subRenderState)
{
	mSubRenderStateList.push_back(subRenderState);
	mSubRenderStateSortValid = false;
	mHashCodeValid			 = false;
}

//-----------------------------------------------------------------------
void RenderState::removeSubRenderState(SubRenderState* subRenderState)
{
	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		if ((*it) == subRenderState)
		{
			ShaderGenerator::getSingleton().destroySubRenderState(*it);
			mSubRenderStateList.erase(it);
			break;
		}
	}
}

//-----------------------------------------------------------------------
void RenderState::merge(const RenderState& rhs, Pass* srcPass, Pass* dstPass)
{
	SubRenderStateList customSubRenderStates;

	// Sort current render states.
	sortSubRenderStates();

	// Insert all custom sub render states. (I.E Not FFP sub render states).
	for (SubRenderStateConstIterator itSrc=rhs.mSubRenderStateList.begin(); itSrc != rhs.mSubRenderStateList.end(); ++itSrc)
	{
		const SubRenderState* srcSubRenderState = *itSrc;
		bool isCustomSubRenderState = true;

		if (srcSubRenderState->getExecutionOrder() == FFP_TRANSFORM ||
			srcSubRenderState->getExecutionOrder() == FFP_COLOUR ||
			srcSubRenderState->getExecutionOrder() == FFP_LIGHTING ||
			srcSubRenderState->getExecutionOrder() == FFP_TEXTURING ||
			srcSubRenderState->getExecutionOrder() == FFP_FOG)
		{
			isCustomSubRenderState = false;
		}

		// Case it is a custom sub render state.
		if (isCustomSubRenderState)
		{
			bool subStateTypeExists = false;

			// Check if this type of sub render state already exists.
			for (SubRenderStateConstIterator itDst=mSubRenderStateList.begin(); itDst != mSubRenderStateList.end(); ++itDst)
			{
				if ((*itDst)->getType() == srcSubRenderState->getType())
				{
					subStateTypeExists = true;
					break;
				}
			}

			// Case custom sub render state not exits -> add it to custom list.
			if (subStateTypeExists == false)
			{
				SubRenderState* newSubRenderState = NULL;

				newSubRenderState = ShaderGenerator::getSingleton().createSubRenderState(srcSubRenderState->getType());
				*newSubRenderState = *srcSubRenderState;
				customSubRenderStates.push_back(newSubRenderState);
			}
		}
	}

	// Merge the local custom sub render states.
	for (SubRenderStateIterator itSrc=customSubRenderStates.begin(); itSrc != customSubRenderStates.end(); ++itSrc)
	{
		SubRenderState* customSubRenderState = *itSrc;

		if (customSubRenderState->preAddToRenderState(this, srcPass, dstPass))
		{
			addSubRenderState(customSubRenderState);
		}
		else
		{
			ShaderGenerator::getSingleton().destroySubRenderState(customSubRenderState);
		}
	}
}

//-----------------------------------------------------------------------
void RenderState::copyFrom(const RenderState& rhs)
{
	// Avoid copying on self.
	if (this == &rhs)
		return;

	// Reset state.
	reset();

	// Copy sub render states.
	for (SubRenderStateConstIterator it=rhs.mSubRenderStateList.begin(); it != rhs.mSubRenderStateList.end(); ++it)
	{
		const SubRenderState* srcSubRenderState = *it;
		SubRenderState* dstSubRenderState = ShaderGenerator::getSingleton().createSubRenderState(srcSubRenderState->getType());

		*dstSubRenderState = *srcSubRenderState;
		addSubRenderState(dstSubRenderState);
	}

	mSubRenderStateSortValid = rhs.mSubRenderStateSortValid;

	mLightCount[0] = rhs.mLightCount[0];
	mLightCount[1] = rhs.mLightCount[1];
	mLightCount[2] = rhs.mLightCount[2];
	mLightCountAutoUpdate = rhs.mLightCountAutoUpdate;

	mHashCode		= rhs.mHashCode;
	mHashCodeValid	= rhs.mHashCodeValid;
}

//-----------------------------------------------------------------------
RenderState& RenderState::operator=(const RenderState& rhs)
{
	copyFrom(rhs);

	return *this;
}

//-----------------------------------------------------------------------
uint32 RenderState::getHashCode()
{
	if (mHashCodeValid == false)
	{
		sortSubRenderStates();

		mHashCode = 0;

		for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
		{
			SubRenderState* srcSubRenderState = *it;
			uint32 subRenderStateHashCode = srcSubRenderState->getHashCode();

			sh_hash_combine(mHashCode, subRenderStateHashCode);
		}

		mHashCodeValid = true;
	}

	return mHashCode;
}

//-----------------------------------------------------------------------
void RenderState::sortSubRenderStates()
{
	if (mSubRenderStateSortValid == false)
	{
		if (mSubRenderStateList.size() > 1)
			qsort(&mSubRenderStateList[0], mSubRenderStateList.size(), sizeof(SubRenderState*), sSubRenderStateCompare);

		mSubRenderStateSortValid = true;
	}
}

//-----------------------------------------------------------------------
int	RenderState::sSubRenderStateCompare(const void * p0, const void *p1)
{
	SubRenderState* pInstance0 = *((SubRenderState**)p0);
	SubRenderState* pInstance1 = *((SubRenderState**)p1);

	return pInstance0->getExecutionOrder() - pInstance1->getExecutionOrder();
}

//-----------------------------------------------------------------------
bool RenderState::createCpuPrograms(ProgramSet* programSet)
{
	sortSubRenderStates();

	const String baseName = StringConverter::toString(getHashCode());
	Program* vsProgram = ProgramManager::getSingleton().createCpuProgram(GPT_VERTEX_PROGRAM);
	Program* psProgram = ProgramManager::getSingleton().createCpuProgram(GPT_FRAGMENT_PROGRAM);
	RTShader::Function* vsMainFunc = NULL;
	RTShader::Function* psMainFunc = NULL;

	programSet->setCpuVertexProgram(vsProgram);
	programSet->setCpuFragmentProgram(psProgram);

	// Create entry point functions.
	vsMainFunc = vsProgram->createFunction("main", "Vertex Program Entry point", Function::FFT_VS_MAIN);
	vsProgram->setEntryPointFunction(vsMainFunc);

	psMainFunc = psProgram->createFunction("main", "Pixel Program Entry point", Function::FFT_PS_MAIN);
	psProgram->setEntryPointFunction(psMainFunc);

	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		SubRenderState* srcSubRenderState = *it;

		if (false == srcSubRenderState->createCpuSubPrograms(programSet))
		{
			LogManager::getSingleton().stream()	<< "RTShader::RenderState : Could not generate sub render program of type: " << srcSubRenderState->getType();
			return false;
		}
	}

	return true;
}

//-----------------------------------------------------------------------
void RenderState::updateGpuProgramsParams(Renderable* rend, Pass* pass, const AutoParamDataSource* source,
										  const LightList* pLightList)
{
	for (SubRenderStateIterator it=mSubRenderStateList.begin(); it != mSubRenderStateList.end(); ++it)
	{
		SubRenderState* curSubRenderState = *it;

		curSubRenderState->updateGpuProgramsParams(rend, pass, source, pLightList);
	}
}

//-----------------------------------------------------------------------
void RenderState::setLightCount(const int lightCount[3])
{
	mLightCount[0] = lightCount[0];
	mLightCount[1] = lightCount[1];
	mLightCount[2] = lightCount[2];
}

//-----------------------------------------------------------------------
void RenderState::getLightCount(int lightCount[3]) const
{
	lightCount[0] = mLightCount[0];
	lightCount[1] = mLightCount[1];
	lightCount[2] = mLightCount[2];
}

}
}

<|endoftext|>


<commit_before>// ExporterBridge.cpp : Defines the exported functions for the DLL application.
//

#include <unknwn.h>
#include <tchar.h>
#include <msclr\auto_gcroot.h>

#include "stdafx.h"
#include "DayViewUIExtensionBridge.h"
#include "resource.h"

#include "..\..\..\..\ToDoList_Dev\Interfaces\ITasklist.h"
#include "..\..\..\..\ToDoList_Dev\Interfaces\ITransText.h"
#include "..\..\..\..\ToDoList_Dev\Interfaces\IPreferences.h"
#include "..\..\..\..\ToDoList_Dev\Interfaces\UITheme.h"

////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
#	using <..\..\..\Debug\DayViewUIExtensionCore.dll>
#	using <..\..\..\Debug\PluginHelpers.dll> as_friend
#else
#	using <..\..\..\Release\DayViewUIExtensionCore.dll>
#	using <..\..\..\Release\PluginHelpers.dll> as_friend
#endif

////////////////////////////////////////////////////////////////////////////////////////////////

using namespace DayViewUIExtension;
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
using namespace Abstractspoon::Tdl::PluginHelpers;

////////////////////////////////////////////////////////////////////////////////////////////////

// REPLACE THIS WITH NEW GUID!

const LPCWSTR DAYVIEW_GUID = L"4CBCF4EA-7B02-41E1-BE65-3E03025E1FFE";
const LPCWSTR DAYVIEW_NAME = L"Day View";

////////////////////////////////////////////////////////////////////////////////////////////////

CDayViewUIExtensionBridge::CDayViewUIExtensionBridge() : m_hIcon(NULL), m_pTT(nullptr)
{
	HMODULE hMod = LoadLibrary(L"DayViewUIExtensionBridge.dll"); // us

	m_hIcon = ::LoadIcon(hMod, MAKEINTRESOURCE(IDI_DAYVIEW));
}

void CDayViewUIExtensionBridge::Release()
{
	delete this;
}

void CDayViewUIExtensionBridge::SetLocalizer(ITransText* pTT)
{
	if (m_pTT == nullptr)
		m_pTT = pTT;
}

LPCWSTR CDayViewUIExtensionBridge::GetMenuText() const
{
	return DAYVIEW_NAME;
}

HICON CDayViewUIExtensionBridge::GetIcon() const
{
	return m_hIcon;
}

LPCWSTR CDayViewUIExtensionBridge::GetTypeID() const
{
	return DAYVIEW_GUID;
}

IUIExtensionWindow* CDayViewUIExtensionBridge::CreateExtWindow(UINT nCtrlID,
	DWORD nStyle, long nLeft, long nTop, long nWidth, long nHeight, HWND hwndParent)
{
	CDayViewUIExtensionBridgeWindow* pExtWnd = new CDayViewUIExtensionBridgeWindow(m_pTT);

	if (!pExtWnd->Create(nCtrlID, nStyle, nLeft, nTop, nWidth, nHeight, hwndParent))
	{
		delete pExtWnd;
		pExtWnd = NULL;
	}

	return pExtWnd;
}

void CDayViewUIExtensionBridge::SavePreferences(IPreferences* pPrefs, LPCWSTR szKey) const
{
	// TODO
}

void CDayViewUIExtensionBridge::LoadPreferences(const IPreferences* pPrefs, LPCWSTR szKey)
{
	// TODO
}

////////////////////////////////////////////////////////////////////////////////////////////////

CDayViewUIExtensionBridgeWindow::CDayViewUIExtensionBridgeWindow(ITransText* pTT)
	: m_pTT(pTT)
{

}

BOOL CDayViewUIExtensionBridgeWindow::Create(UINT nCtrlID, DWORD nStyle,
	long nLeft, long nTop, long nWidth, long nHeight, HWND hwndParent)
{
	msclr::auto_gcroot<Translator^> trans = gcnew Translator(m_pTT);

	m_wnd = gcnew DayViewUIExtension::DayViewUIExtensionCore(static_cast<IntPtr>(hwndParent), trans.get());

	HWND hWnd = GetHwnd();

	if (hWnd)
	{
		::SetParent(hWnd, hwndParent);
		::SetWindowLong(hWnd, GWL_ID, nCtrlID);
		::MoveWindow(hWnd, nLeft, nTop, nWidth, nHeight, FALSE);

		return true;
	}

	return false;
}

HICON CDayViewUIExtensionBridgeWindow::GetIcon() const
{
	return NULL;
}

LPCWSTR CDayViewUIExtensionBridgeWindow::GetMenuText() const
{
	return DAYVIEW_NAME;
}

LPCWSTR CDayViewUIExtensionBridgeWindow::GetTypeID() const
{
	return DAYVIEW_GUID;
}

bool CDayViewUIExtensionBridgeWindow::SelectTask(DWORD dwTaskID)
{
	return m_wnd->SelectTask(dwTaskID);
}

bool CDayViewUIExtensionBridgeWindow::SelectTasks(const DWORD* pdwTaskIDs, int nTaskCount)
{
	array<UInt32>^ taskIDs = gcnew array<UInt32>(nTaskCount);

	for (int i = 0; i < nTaskCount; i++)
		taskIDs[i] = pdwTaskIDs[i];

	return m_wnd->SelectTasks(taskIDs);
}

void CDayViewUIExtensionBridgeWindow::UpdateTasks(const ITaskList* pTasks, IUI_UPDATETYPE nUpdate, const IUI_ATTRIBUTE* pAttributes, int nNumAttributes)
{
	msclr::auto_gcroot<TaskList^> tasks = gcnew TaskList(pTasks);

	m_wnd->UpdateTasks(tasks.get(), UIExtension::Map(nUpdate), UIExtension::Map(pAttributes, nNumAttributes));
}

bool CDayViewUIExtensionBridgeWindow::WantEditUpdate(IUI_ATTRIBUTE nAttribute) const
{
	return m_wnd->WantEditUpdate(UIExtension::Map(nAttribute));
}

bool CDayViewUIExtensionBridgeWindow::WantSortUpdate(IUI_ATTRIBUTE nAttribute) const
{
	return m_wnd->WantSortUpdate(UIExtension::Map(nAttribute));
}

bool CDayViewUIExtensionBridgeWindow::PrepareNewTask(ITaskList* pTask) const
{
	msclr::auto_gcroot<TaskList^> task = gcnew TaskList(pTask);

	return m_wnd->PrepareNewTask(task.get()->GetFirstTask());
}

bool CDayViewUIExtensionBridgeWindow::ProcessMessage(MSG* pMsg)
{
	return m_wnd->ProcessMessage(IntPtr(pMsg->hwnd),
		pMsg->message,
		pMsg->wParam,
		pMsg->lParam,
		pMsg->time,
		pMsg->pt.x,
		pMsg->pt.y);
}

bool CDayViewUIExtensionBridgeWindow::DoAppCommand(IUI_APPCOMMAND nCmd, DWORD dwExtra)
{
	switch (nCmd)
	{
	case IUI_SELECTTASK:
		return m_wnd->SelectTask(dwExtra);

	case IUI_GETNEXTTASK:
		{
			UInt32 taskID = 0;
			DWORD* pTaskID = (DWORD*)dwExtra;

			if (m_wnd->GetTask(UIExtension::GetTask::GetNextTask, taskID))
			{
				*pTaskID = taskID;
				return true;
			}
		}
		break;

	case IUI_GETPREVTASK:
		{
			UInt32 taskID = 0;
			DWORD* pTaskID = (DWORD*)dwExtra;

			if (m_wnd->GetTask(UIExtension::GetTask::GetPrevTask, taskID))
			{
				*pTaskID = taskID;
				return true;
			}
		}
		break;

	case IUI_SELECTFIRSTTASK:
	case IUI_SELECTNEXTTASK:
	case IUI_SELECTNEXTTASKINCLCURRENT:
	case IUI_SELECTPREVTASK:
	case IUI_SELECTLASTTASK:
		return DoAppSelectCommand(nCmd, (const IUISELECTTASK*)dwExtra);
	}

	// all else
	return false;
}

bool CDayViewUIExtensionBridgeWindow::CanDoAppCommand(IUI_APPCOMMAND nCmd, DWORD dwExtra) const
{
	switch (nCmd)
	{
	case IUI_SELECTTASK:
		return true;

	case IUI_GETNEXTTASK:
	case IUI_GETPREVTASK:
	case IUI_SELECTFIRSTTASK:
	case IUI_SELECTNEXTTASK:
	case IUI_SELECTNEXTTASKINCLCURRENT:
	case IUI_SELECTPREVTASK:
	case IUI_SELECTLASTTASK:
		return /*true*/false;
	}

	// all else
	return false;
}

bool CDayViewUIExtensionBridgeWindow::DoAppSelectCommand(IUI_APPCOMMAND nCmd, const IUISELECTTASK* pSelect)
{
	// Sanity check
	if (!pSelect)
	{
		//ASSERT(0);
		return false;
	}

	UIExtension::SelectTask selectWhat;

	switch (nCmd)
	{
	case IUI_SELECTFIRSTTASK:
		selectWhat = UIExtension::SelectTask::SelectFirstTask;
		break;

	case IUI_SELECTNEXTTASK:
		selectWhat = UIExtension::SelectTask::SelectNextTask;
		break;

	case IUI_SELECTNEXTTASKINCLCURRENT:
		selectWhat = UIExtension::SelectTask::SelectNextTaskInclCurrent;
		break;

	case IUI_SELECTPREVTASK:
		selectWhat = UIExtension::SelectTask::SelectPrevTask;
		break;

	case IUI_SELECTLASTTASK:
		selectWhat = UIExtension::SelectTask::SelectLastTask;
		break;

	default:
		return false;
	}

	String^ sWords = gcnew String(pSelect->szWords);

	return m_wnd->SelectTask(sWords, selectWhat, pSelect->bCaseSensitive, pSelect->bWholeWord, pSelect->bFindReplace);
}

bool CDayViewUIExtensionBridgeWindow::GetLabelEditRect(LPRECT pEdit)
{
	return m_wnd->GetLabelEditRect((Int32&)pEdit->left, (Int32&)pEdit->top, (Int32&)pEdit->right, (Int32&)pEdit->bottom);
}

IUI_HITTEST CDayViewUIExtensionBridgeWindow::HitTest(const POINT& ptScreen) const
{
	return UIExtension::Map(m_wnd->HitTest(ptScreen.x, ptScreen.y));
}

void CDayViewUIExtensionBridgeWindow::SetUITheme(const UITHEME* pTheme)
{
	msclr::auto_gcroot<UITheme^> theme = gcnew UITheme(pTheme);

	m_wnd->SetUITheme(theme.get());
}

void CDayViewUIExtensionBridgeWindow::SetReadOnly(bool bReadOnly)
{
	m_wnd->SetReadOnly(bReadOnly);
}

HWND CDayViewUIExtensionBridgeWindow::GetHwnd() const
{
	return static_cast<HWND>(m_wnd->Handle.ToPointer());
}

void CDayViewUIExtensionBridgeWindow::SavePreferences(IPreferences* pPrefs, LPCWSTR szKey) const
{
	msclr::auto_gcroot<Preferences^> prefs = gcnew Preferences(pPrefs);
	msclr::auto_gcroot<String^> key = gcnew String(szKey);

	m_wnd->SavePreferences(prefs.get(), key.get());
}

void CDayViewUIExtensionBridgeWindow::LoadPreferences(const IPreferences* pPrefs, LPCWSTR szKey, bool bAppOnly)
{
	msclr::auto_gcroot<Preferences^> prefs = gcnew Preferences(pPrefs);
	msclr::auto_gcroot<String^> key = gcnew String(szKey);

	m_wnd->LoadPreferences(prefs.get(), key.get(), bAppOnly);
}

<commit_msg>'Day View' -> 'Week Planner'<commit_after>// ExporterBridge.cpp : Defines the exported functions for the DLL application.
//

#include <unknwn.h>
#include <tchar.h>
#include <msclr\auto_gcroot.h>

#include "stdafx.h"
#include "DayViewUIExtensionBridge.h"
#include "resource.h"

#include "..\..\..\..\ToDoList_Dev\Interfaces\ITasklist.h"
#include "..\..\..\..\ToDoList_Dev\Interfaces\ITransText.h"
#include "..\..\..\..\ToDoList_Dev\Interfaces\IPreferences.h"
#include "..\..\..\..\ToDoList_Dev\Interfaces\UITheme.h"

////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
#	using <..\..\..\Debug\DayViewUIExtensionCore.dll>
#	using <..\..\..\Debug\PluginHelpers.dll> as_friend
#else
#	using <..\..\..\Release\DayViewUIExtensionCore.dll>
#	using <..\..\..\Release\PluginHelpers.dll> as_friend
#endif

////////////////////////////////////////////////////////////////////////////////////////////////

using namespace DayViewUIExtension;
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
using namespace Abstractspoon::Tdl::PluginHelpers;

////////////////////////////////////////////////////////////////////////////////////////////////

// REPLACE THIS WITH NEW GUID!

const LPCWSTR DAYVIEW_GUID = L"4CBCF4EA-7B02-41E1-BE65-3E03025E1FFE";
const LPCWSTR DAYVIEW_NAME = L"Week Planner";

////////////////////////////////////////////////////////////////////////////////////////////////

CDayViewUIExtensionBridge::CDayViewUIExtensionBridge() : m_hIcon(NULL), m_pTT(nullptr)
{
	HMODULE hMod = LoadLibrary(L"DayViewUIExtensionBridge.dll"); // us

	m_hIcon = ::LoadIcon(hMod, MAKEINTRESOURCE(IDI_DAYVIEW));
}

void CDayViewUIExtensionBridge::Release()
{
	delete this;
}

void CDayViewUIExtensionBridge::SetLocalizer(ITransText* pTT)
{
	if (m_pTT == nullptr)
		m_pTT = pTT;
}

LPCWSTR CDayViewUIExtensionBridge::GetMenuText() const
{
	return DAYVIEW_NAME;
}

HICON CDayViewUIExtensionBridge::GetIcon() const
{
	return m_hIcon;
}

LPCWSTR CDayViewUIExtensionBridge::GetTypeID() const
{
	return DAYVIEW_GUID;
}

IUIExtensionWindow* CDayViewUIExtensionBridge::CreateExtWindow(UINT nCtrlID,
	DWORD nStyle, long nLeft, long nTop, long nWidth, long nHeight, HWND hwndParent)
{
	CDayViewUIExtensionBridgeWindow* pExtWnd = new CDayViewUIExtensionBridgeWindow(m_pTT);

	if (!pExtWnd->Create(nCtrlID, nStyle, nLeft, nTop, nWidth, nHeight, hwndParent))
	{
		delete pExtWnd;
		pExtWnd = NULL;
	}

	return pExtWnd;
}

void CDayViewUIExtensionBridge::SavePreferences(IPreferences* pPrefs, LPCWSTR szKey) const
{
	// TODO
}

void CDayViewUIExtensionBridge::LoadPreferences(const IPreferences* pPrefs, LPCWSTR szKey)
{
	// TODO
}

////////////////////////////////////////////////////////////////////////////////////////////////

CDayViewUIExtensionBridgeWindow::CDayViewUIExtensionBridgeWindow(ITransText* pTT)
	: m_pTT(pTT)
{

}

BOOL CDayViewUIExtensionBridgeWindow::Create(UINT nCtrlID, DWORD nStyle,
	long nLeft, long nTop, long nWidth, long nHeight, HWND hwndParent)
{
	msclr::auto_gcroot<Translator^> trans = gcnew Translator(m_pTT);

	m_wnd = gcnew DayViewUIExtension::DayViewUIExtensionCore(static_cast<IntPtr>(hwndParent), trans.get());

	HWND hWnd = GetHwnd();

	if (hWnd)
	{
		::SetParent(hWnd, hwndParent);
		::SetWindowLong(hWnd, GWL_ID, nCtrlID);
		::MoveWindow(hWnd, nLeft, nTop, nWidth, nHeight, FALSE);

		return true;
	}

	return false;
}

HICON CDayViewUIExtensionBridgeWindow::GetIcon() const
{
	return NULL;
}

LPCWSTR CDayViewUIExtensionBridgeWindow::GetMenuText() const
{
	return DAYVIEW_NAME;
}

LPCWSTR CDayViewUIExtensionBridgeWindow::GetTypeID() const
{
	return DAYVIEW_GUID;
}

bool CDayViewUIExtensionBridgeWindow::SelectTask(DWORD dwTaskID)
{
	return m_wnd->SelectTask(dwTaskID);
}

bool CDayViewUIExtensionBridgeWindow::SelectTasks(const DWORD* pdwTaskIDs, int nTaskCount)
{
	array<UInt32>^ taskIDs = gcnew array<UInt32>(nTaskCount);

	for (int i = 0; i < nTaskCount; i++)
		taskIDs[i] = pdwTaskIDs[i];

	return m_wnd->SelectTasks(taskIDs);
}

void CDayViewUIExtensionBridgeWindow::UpdateTasks(const ITaskList* pTasks, IUI_UPDATETYPE nUpdate, const IUI_ATTRIBUTE* pAttributes, int nNumAttributes)
{
	msclr::auto_gcroot<TaskList^> tasks = gcnew TaskList(pTasks);

	m_wnd->UpdateTasks(tasks.get(), UIExtension::Map(nUpdate), UIExtension::Map(pAttributes, nNumAttributes));
}

bool CDayViewUIExtensionBridgeWindow::WantEditUpdate(IUI_ATTRIBUTE nAttribute) const
{
	return m_wnd->WantEditUpdate(UIExtension::Map(nAttribute));
}

bool CDayViewUIExtensionBridgeWindow::WantSortUpdate(IUI_ATTRIBUTE nAttribute) const
{
	return m_wnd->WantSortUpdate(UIExtension::Map(nAttribute));
}

bool CDayViewUIExtensionBridgeWindow::PrepareNewTask(ITaskList* pTask) const
{
	msclr::auto_gcroot<TaskList^> task = gcnew TaskList(pTask);

	return m_wnd->PrepareNewTask(task.get()->GetFirstTask());
}

bool CDayViewUIExtensionBridgeWindow::ProcessMessage(MSG* pMsg)
{
	return m_wnd->ProcessMessage(IntPtr(pMsg->hwnd),
		pMsg->message,
		pMsg->wParam,
		pMsg->lParam,
		pMsg->time,
		pMsg->pt.x,
		pMsg->pt.y);
}

bool CDayViewUIExtensionBridgeWindow::DoAppCommand(IUI_APPCOMMAND nCmd, DWORD dwExtra)
{
	switch (nCmd)
	{
	case IUI_SELECTTASK:
		return m_wnd->SelectTask(dwExtra);

	case IUI_GETNEXTTASK:
		{
			UInt32 taskID = 0;
			DWORD* pTaskID = (DWORD*)dwExtra;

			if (m_wnd->GetTask(UIExtension::GetTask::GetNextTask, taskID))
			{
				*pTaskID = taskID;
				return true;
			}
		}
		break;

	case IUI_GETPREVTASK:
		{
			UInt32 taskID = 0;
			DWORD* pTaskID = (DWORD*)dwExtra;

			if (m_wnd->GetTask(UIExtension::GetTask::GetPrevTask, taskID))
			{
				*pTaskID = taskID;
				return true;
			}
		}
		break;

	case IUI_SELECTFIRSTTASK:
	case IUI_SELECTNEXTTASK:
	case IUI_SELECTNEXTTASKINCLCURRENT:
	case IUI_SELECTPREVTASK:
	case IUI_SELECTLASTTASK:
		return DoAppSelectCommand(nCmd, (const IUISELECTTASK*)dwExtra);
	}

	// all else
	return false;
}

bool CDayViewUIExtensionBridgeWindow::CanDoAppCommand(IUI_APPCOMMAND nCmd, DWORD dwExtra) const
{
	switch (nCmd)
	{
	case IUI_SELECTTASK:
		return true;

	case IUI_GETNEXTTASK:
	case IUI_GETPREVTASK:
	case IUI_SELECTFIRSTTASK:
	case IUI_SELECTNEXTTASK:
	case IUI_SELECTNEXTTASKINCLCURRENT:
	case IUI_SELECTPREVTASK:
	case IUI_SELECTLASTTASK:
		return /*true*/false;
	}

	// all else
	return false;
}

bool CDayViewUIExtensionBridgeWindow::DoAppSelectCommand(IUI_APPCOMMAND nCmd, const IUISELECTTASK* pSelect)
{
	// Sanity check
	if (!pSelect)
	{
		//ASSERT(0);
		return false;
	}

	UIExtension::SelectTask selectWhat;

	switch (nCmd)
	{
	case IUI_SELECTFIRSTTASK:
		selectWhat = UIExtension::SelectTask::SelectFirstTask;
		break;

	case IUI_SELECTNEXTTASK:
		selectWhat = UIExtension::SelectTask::SelectNextTask;
		break;

	case IUI_SELECTNEXTTASKINCLCURRENT:
		selectWhat = UIExtension::SelectTask::SelectNextTaskInclCurrent;
		break;

	case IUI_SELECTPREVTASK:
		selectWhat = UIExtension::SelectTask::SelectPrevTask;
		break;

	case IUI_SELECTLASTTASK:
		selectWhat = UIExtension::SelectTask::SelectLastTask;
		break;

	default:
		return false;
	}

	String^ sWords = gcnew String(pSelect->szWords);

	return m_wnd->SelectTask(sWords, selectWhat, pSelect->bCaseSensitive, pSelect->bWholeWord, pSelect->bFindReplace);
}

bool CDayViewUIExtensionBridgeWindow::GetLabelEditRect(LPRECT pEdit)
{
	return m_wnd->GetLabelEditRect((Int32&)pEdit->left, (Int32&)pEdit->top, (Int32&)pEdit->right, (Int32&)pEdit->bottom);
}

IUI_HITTEST CDayViewUIExtensionBridgeWindow::HitTest(const POINT& ptScreen) const
{
	return UIExtension::Map(m_wnd->HitTest(ptScreen.x, ptScreen.y));
}

void CDayViewUIExtensionBridgeWindow::SetUITheme(const UITHEME* pTheme)
{
	msclr::auto_gcroot<UITheme^> theme = gcnew UITheme(pTheme);

	m_wnd->SetUITheme(theme.get());
}

void CDayViewUIExtensionBridgeWindow::SetReadOnly(bool bReadOnly)
{
	m_wnd->SetReadOnly(bReadOnly);
}

HWND CDayViewUIExtensionBridgeWindow::GetHwnd() const
{
	return static_cast<HWND>(m_wnd->Handle.ToPointer());
}

void CDayViewUIExtensionBridgeWindow::SavePreferences(IPreferences* pPrefs, LPCWSTR szKey) const
{
	msclr::auto_gcroot<Preferences^> prefs = gcnew Preferences(pPrefs);
	msclr::auto_gcroot<String^> key = gcnew String(szKey);

	m_wnd->SavePreferences(prefs.get(), key.get());
}

void CDayViewUIExtensionBridgeWindow::LoadPreferences(const IPreferences* pPrefs, LPCWSTR szKey, bool bAppOnly)
{
	msclr::auto_gcroot<Preferences^> prefs = gcnew Preferences(pPrefs);
	msclr::auto_gcroot<String^> key = gcnew String(szKey);

	m_wnd->LoadPreferences(prefs.get(), key.get(), bAppOnly);
}

<|endoftext|>


<commit_before>#include "ntp1tokenlistmodel.h"
#include "boost/thread/future.hpp"
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include <QIcon>
#include <QImage>

const std::string NTP1TokenListModel::WalletFileName = "NTP1Wallet.json";

QString NTP1TokenListModel::__getTokenName(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::fromStdString(theWallet->getTokenName(index));
}

QString NTP1TokenListModel::__getTokenId(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::fromStdString(theWallet->getTokenId(index));
}

QString NTP1TokenListModel::__getTokenDescription(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::fromStdString(theWallet->getTokenDescription(index));
}

QString NTP1TokenListModel::__getTokenBalance(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::number(theWallet->getTokenBalance(index));
}

QIcon NTP1TokenListModel::__getTokenIcon(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    const std::string& iconData = theWallet->getTokenIcon(index);
    if (iconData.empty() || NTP1Wallet::IconHasErrorContent(iconData)) {
        return QIcon();
    }
    QImage iconImage;
    iconImage.loadFromData((const uchar*)iconData.c_str(), iconData.size());
    return QIcon(QPixmap::fromImage(iconImage));
}

void NTP1TokenListModel::UpdateWalletBalances(boost::shared_ptr<NTP1Wallet>                  wallet,
                                              boost::promise<boost::shared_ptr<NTP1Wallet>>& promise)
{
    try {
        wallet->update();
        promise.set_value(wallet);
    } catch (...) {
        promise.set_exception(boost::current_exception());
    }
}

NTP1TokenListModel::NTP1TokenListModel()
    : ntp1WalletTxUpdater(boost::make_shared<NTP1WalletTxUpdater>(this))
{
    ntp1wallet             = boost::make_shared<NTP1Wallet>();
    walletLocked           = false;
    walletUpdateRunning    = false;
    walletUpdateEnderTimer = new QTimer(this);
    loadWalletFromFile();
    connect(walletUpdateEnderTimer, &QTimer::timeout, this, &NTP1TokenListModel::endWalletUpdate);
    walletUpdateEnderTimer->start(1000);
    reloadBalances();
    boost::thread t(&NTP1TokenListModel::SetupNTP1WalletTxUpdaterToWallet, this);
    t.detach();
}

NTP1TokenListModel::~NTP1TokenListModel() { ntp1WalletTxUpdater.reset(); }

void NTP1TokenListModel::reloadBalances()
{
    boost::lock_guard<boost::recursive_mutex> lg(walletUpdateBeginLock);
    beginWalletUpdate();
}

void NTP1TokenListModel::beginWalletUpdate()
{
    if (!walletUpdateRunning) {
        emit signal_walletUpdateRunning(true);
        walletUpdateRunning                  = true;
        boost::shared_ptr<NTP1Wallet> wallet = boost::make_shared<NTP1Wallet>(*ntp1wallet);
        updateWalletPromise                  = boost::promise<boost::shared_ptr<NTP1Wallet>>();
        updateWalletFuture                   = updateWalletPromise.get_future();
        boost::thread t(boost::bind(&NTP1TokenListModel::UpdateWalletBalances, wallet,
                                    boost::ref(updateWalletPromise)));
        t.detach();
    }
}

void NTP1TokenListModel::endWalletUpdate()
{
    if (walletUpdateRunning && updateWalletFuture.is_ready()) {
        try {
            boost::shared_ptr<NTP1Wallet> wallet = updateWalletFuture.get();
            if (!(*wallet == *ntp1wallet)) {
                beginResetModel();
                boost::atomic_store(&ntp1wallet, wallet);
                saveWalletToFile();
                endResetModel();
            }
        } catch (std::exception& ex) {
            printf("Error while updating NTP1 balances: %s", ex.what());
        }
        walletUpdateRunning = false;
        emit signal_walletUpdateRunning(false);
    }
}

int NTP1TokenListModel::rowCount(const QModelIndex& parent) const
{
    return ntp1wallet->getNumberOfTokens();
}

int NTP1TokenListModel::columnCount(const QModelIndex& parent) const { return 3; }

QVariant NTP1TokenListModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= static_cast<int>(ntp1wallet->getNumberOfTokens()))
        return QVariant();

    if (role == NTP1TokenListModel::AmountRole) {
        return __getTokenBalance(index.row(), ntp1wallet);
    }
    if (role == NTP1TokenListModel::TokenDescriptionRole) {
        return __getTokenDescription(index.row(), ntp1wallet);
    }
    if (role == NTP1TokenListModel::TokenNameRole) {
        return __getTokenName(index.row(), ntp1wallet);
    }
    if (role == Qt::DecorationRole) {
        return QVariant(__getTokenIcon(index.row(), ntp1wallet));
    }
    return QVariant();
}

void NTP1TokenListModel::saveWalletToFile()
{
    try {
        if (!ntp1wallet) {
            throw std::runtime_error("Error: NTP1 wallet is a null pointer!");
        }

        // The temp file here ensures that the target file is not tampered with before a successful
        // writing happens This is helpful to avoid corrupt files in cases such as diskspace issues
        srand(time(NULL));
        boost::filesystem::path tempFile = GetDataDir() / WalletFileName;
        tempFile.replace_extension(".json." + ToString(rand()));
        boost::filesystem::path permFile = GetDataDir() / WalletFileName;
        ntp1wallet->exportToFile(tempFile);
        if (boost::filesystem::exists(permFile))
            boost::filesystem::remove(permFile);
        boost::filesystem::rename(tempFile, permFile);
    } catch (std::exception& ex) {
        printf("Failed at exporting wallet data. Error says %s", ex.what());
    }
}

void NTP1TokenListModel::loadWalletFromFile()
{
    try {
        if (!ntp1wallet) {
            throw std::runtime_error("Error: NTP1 wallet is a null pointer!");
        }
        boost::filesystem::path file = GetDataDir() / WalletFileName;
        if (boost::filesystem::exists(file)) {
            ntp1wallet->importFromFile(file);
        } else {
            printf("NTP1 wallet not found. One will be created soon.");
        }
    } catch (std::exception& ex) {
        ntp1wallet->clear();
        printf("Failed at exporting wallet data. Error says %s", ex.what());
    }
}

boost::shared_ptr<NTP1Wallet> NTP1TokenListModel::getCurrentWallet() const
{
    return boost::atomic_load(&ntp1wallet);
}
<commit_msg>Wallet fix for a possible race condition issue<commit_after>#include "ntp1tokenlistmodel.h"
#include "boost/thread/future.hpp"
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include <QIcon>
#include <QImage>

const std::string NTP1TokenListModel::WalletFileName = "NTP1Wallet.json";

QString NTP1TokenListModel::__getTokenName(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::fromStdString(theWallet->getTokenName(index));
}

QString NTP1TokenListModel::__getTokenId(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::fromStdString(theWallet->getTokenId(index));
}

QString NTP1TokenListModel::__getTokenDescription(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::fromStdString(theWallet->getTokenDescription(index));
}

QString NTP1TokenListModel::__getTokenBalance(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    return QString::number(theWallet->getTokenBalance(index));
}

QIcon NTP1TokenListModel::__getTokenIcon(int index, boost::shared_ptr<NTP1Wallet> theWallet)
{
    const std::string& iconData = theWallet->getTokenIcon(index);
    if (iconData.empty() || NTP1Wallet::IconHasErrorContent(iconData)) {
        return QIcon();
    }
    QImage iconImage;
    iconImage.loadFromData((const uchar*)iconData.c_str(), iconData.size());
    return QIcon(QPixmap::fromImage(iconImage));
}

void NTP1TokenListModel::UpdateWalletBalances(boost::shared_ptr<NTP1Wallet>                  wallet,
                                              boost::promise<boost::shared_ptr<NTP1Wallet>>& promise)
{
    try {
        wallet->update();
        promise.set_value(wallet);
    } catch (...) {
        promise.set_exception(boost::current_exception());
    }
}

NTP1TokenListModel::NTP1TokenListModel()
    : ntp1WalletTxUpdater(boost::make_shared<NTP1WalletTxUpdater>(this))
{
    ntp1wallet             = boost::make_shared<NTP1Wallet>();
    walletLocked           = false;
    walletUpdateRunning    = false;
    walletUpdateEnderTimer = new QTimer(this);
    loadWalletFromFile();
    connect(walletUpdateEnderTimer, &QTimer::timeout, this, &NTP1TokenListModel::endWalletUpdate);
    walletUpdateEnderTimer->start(1000);
    reloadBalances();
    boost::thread t(&NTP1TokenListModel::SetupNTP1WalletTxUpdaterToWallet, this);
    t.detach();
}

NTP1TokenListModel::~NTP1TokenListModel() { ntp1WalletTxUpdater.reset(); }

void NTP1TokenListModel::reloadBalances()
{
    boost::lock_guard<boost::recursive_mutex> lg(walletUpdateBeginLock);
    beginWalletUpdate();
}

void NTP1TokenListModel::beginWalletUpdate()
{
    if (!walletUpdateRunning) {
        emit signal_walletUpdateRunning(true);
        walletUpdateRunning                  = true;
        boost::shared_ptr<NTP1Wallet> wallet = boost::make_shared<NTP1Wallet>(*ntp1wallet);
        updateWalletPromise                  = boost::promise<boost::shared_ptr<NTP1Wallet>>();
        updateWalletFuture                   = updateWalletPromise.get_future();
        boost::thread t(boost::bind(&NTP1TokenListModel::UpdateWalletBalances, wallet,
                                    boost::ref(updateWalletPromise)));
        t.detach();
    }
}

void NTP1TokenListModel::endWalletUpdate()
{
    if (walletUpdateRunning && updateWalletFuture.is_ready()) {
        try {
            boost::shared_ptr<NTP1Wallet> wallet = updateWalletFuture.get();
            // the nullptr check is done after having a nullptr once happen.
            // Although this should never happen, having it doesn't hurt and is safer
            if ((wallet.get() != nullptr) && !(*wallet == *ntp1wallet)) {
                beginResetModel();
                boost::atomic_store(&ntp1wallet, wallet);
                saveWalletToFile();
                endResetModel();
            }
        } catch (std::exception& ex) {
            printf("Error while updating NTP1 balances: %s", ex.what());
        }
        walletUpdateRunning = false;
        emit signal_walletUpdateRunning(false);
    }
}

int NTP1TokenListModel::rowCount(const QModelIndex& parent) const
{
    return ntp1wallet->getNumberOfTokens();
}

int NTP1TokenListModel::columnCount(const QModelIndex& parent) const { return 3; }

QVariant NTP1TokenListModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= static_cast<int>(ntp1wallet->getNumberOfTokens()))
        return QVariant();

    if (role == NTP1TokenListModel::AmountRole) {
        return __getTokenBalance(index.row(), ntp1wallet);
    }
    if (role == NTP1TokenListModel::TokenDescriptionRole) {
        return __getTokenDescription(index.row(), ntp1wallet);
    }
    if (role == NTP1TokenListModel::TokenNameRole) {
        return __getTokenName(index.row(), ntp1wallet);
    }
    if (role == Qt::DecorationRole) {
        return QVariant(__getTokenIcon(index.row(), ntp1wallet));
    }
    return QVariant();
}

void NTP1TokenListModel::saveWalletToFile()
{
    try {
        if (!ntp1wallet) {
            throw std::runtime_error("Error: NTP1 wallet is a null pointer!");
        }

        // The temp file here ensures that the target file is not tampered with before a successful
        // writing happens This is helpful to avoid corrupt files in cases such as diskspace issues
        srand(time(NULL));
        boost::filesystem::path tempFile = GetDataDir() / WalletFileName;
        tempFile.replace_extension(".json." + ToString(rand()));
        boost::filesystem::path permFile = GetDataDir() / WalletFileName;
        ntp1wallet->exportToFile(tempFile);
        if (boost::filesystem::exists(permFile))
            boost::filesystem::remove(permFile);
        boost::filesystem::rename(tempFile, permFile);
    } catch (std::exception& ex) {
        printf("Failed at exporting wallet data. Error says %s", ex.what());
    }
}

void NTP1TokenListModel::loadWalletFromFile()
{
    try {
        if (!ntp1wallet) {
            throw std::runtime_error("Error: NTP1 wallet is a null pointer!");
        }
        boost::filesystem::path file = GetDataDir() / WalletFileName;
        if (boost::filesystem::exists(file)) {
            ntp1wallet->importFromFile(file);
        } else {
            printf("NTP1 wallet not found. One will be created soon.");
        }
    } catch (std::exception& ex) {
        ntp1wallet->clear();
        printf("Failed at exporting wallet data. Error says %s", ex.what());
    }
}

boost::shared_ptr<NTP1Wallet> NTP1TokenListModel::getCurrentWallet() const
{
    return boost::atomic_load(&ntp1wallet);
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/plugins/ppapi/quota_file_io.h"

#include <algorithm>

#include "base/stl_util.h"
#include "base/message_loop_proxy.h"
#include "base/task.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"

using base::PlatformFile;
using base::PlatformFileError;
using quota::StorageType;

namespace webkit {
namespace ppapi {

namespace {
StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
  switch (type) {
    case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
      return quota::kStorageTypePersistent;
    case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
      return quota::kStorageTypeTemporary;
    default:
      return quota::kStorageTypeUnknown;
  }
  NOTREACHED();
  return quota::kStorageTypeUnknown;
}
}  // namespace

class QuotaFileIO::PendingOperationBase {
 public:
  virtual ~PendingOperationBase() {}

  // Either one of Run() or DidFail() is called (the latter is called when
  // there was more than one error during quota queries).
  virtual void Run() = 0;
  virtual void DidFail(PlatformFileError error) = 0;

 protected:
  PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation)
      : quota_io_(quota_io), is_will_operation_(is_will_operation) {
    DCHECK(quota_io_);
    quota_io_->WillUpdate();
  }

  QuotaFileIO* quota_io_;
  const bool is_will_operation_;
};

class QuotaFileIO::WriteOperation : public PendingOperationBase {
 public:
  WriteOperation(QuotaFileIO* quota_io,
                 bool is_will_operation,
                 int64_t offset,
                 const char* buffer,
                 int32_t bytes_to_write,
                 WriteCallback* callback)
      : PendingOperationBase(quota_io, is_will_operation),
        offset_(offset),
        bytes_to_write_(bytes_to_write),
        callback_(callback),
        finished_(false),
        status_(base::PLATFORM_FILE_OK),
        bytes_written_(0),
        callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
        runnable_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
    if (!is_will_operation) {
      // TODO(kinuko): check the API convention if we really need to keep a
      // copy of the buffer during the async write operations.
      buffer_.reset(new char[bytes_to_write]);
      memcpy(buffer_.get(), buffer, bytes_to_write);
    }
  }
  virtual ~WriteOperation() {}
  virtual void Run() OVERRIDE {
    DCHECK(quota_io_);
    if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
      DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
      return;
    }
    if (is_will_operation_) {
      // Assuming the write will succeed.
      DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_);
      return;
    }
    DCHECK(buffer_.get());
    if (!base::FileUtilProxy::Write(
            quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(),
            quota_io_->file_, offset_, buffer_.get(), bytes_to_write_,
            callback_factory_.NewCallback(&WriteOperation::DidFinish))) {
      DidFail(base::PLATFORM_FILE_ERROR_FAILED);
      return;
    }
  }

  virtual void DidFail(PlatformFileError error) OVERRIDE {
    DidFinish(error, 0);
  }

  bool finished() const { return finished_; }

  virtual void WillRunCallback() {
    base::MessageLoopProxy::current()->PostTask(
        FROM_HERE, runnable_factory_.NewRunnableMethod(
            &WriteOperation::RunCallback));
  }

 private:
  void DidFinish(PlatformFileError status, int bytes_written) {
    finished_ = true;
    status_ = status;
    bytes_written_ = bytes_written;
    int64_t max_offset =
        (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
    // This may delete itself by calling RunCallback.
    quota_io_->DidWrite(this, max_offset);
  }

  virtual void RunCallback() {
    DCHECK(callback_.get());
    callback_->Run(status_, bytes_written_);
    callback_.reset();
    delete this;
  }

  const int64_t offset_;
  scoped_array<char> buffer_;
  const int32_t bytes_to_write_;
  scoped_ptr<WriteCallback> callback_;
  bool finished_;
  PlatformFileError status_;
  int64_t bytes_written_;
  base::ScopedCallbackFactory<WriteOperation> callback_factory_;
  ScopedRunnableMethodFactory<WriteOperation> runnable_factory_;
};

class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
 public:
  SetLengthOperation(QuotaFileIO* quota_io,
                     bool is_will_operation,
                     int64_t length,
                     StatusCallback* callback)
      : PendingOperationBase(quota_io, is_will_operation),
        length_(length),
        callback_(callback),
        callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}

  virtual ~SetLengthOperation() {}

  virtual void Run() OVERRIDE {
    DCHECK(quota_io_);
    if (quota_io_->CheckIfExceedsQuota(length_)) {
      DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
      return;
    }
    if (is_will_operation_) {
      DidFinish(base::PLATFORM_FILE_OK);
      return;
    }
    if (!base::FileUtilProxy::Truncate(
            quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(),
            quota_io_->file_, length_,
            callback_factory_.NewCallback(&SetLengthOperation::DidFinish))) {
      DidFail(base::PLATFORM_FILE_ERROR_FAILED);
      return;
    }
  }

  virtual void DidFail(PlatformFileError error) OVERRIDE {
    DidFinish(error);
  }

 private:
  void DidFinish(PlatformFileError status) {
    quota_io_->DidSetLength(status, length_);
    DCHECK(callback_.get());
    callback_->Run(status);
    callback_.reset();
    delete this;
  }

  int64_t length_;
  scoped_ptr<StatusCallback> callback_;
  base::ScopedCallbackFactory<SetLengthOperation> callback_factory_;
};

// QuotaFileIO --------------------------------------------------------------

QuotaFileIO::QuotaFileIO(
    PluginInstance* instance,
    PlatformFile file,
    const GURL& file_url,
    PP_FileSystemType type)
    : instance_(instance),
      file_(file),
      file_url_(file_url),
      storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
      cached_file_size_(0),
      cached_available_space_(0),
      outstanding_quota_queries_(0),
      outstanding_errors_(0),
      max_written_offset_(0),
      inflight_operations_(0),
      callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
  DCHECK(instance_);
  DCHECK_NE(base::kInvalidPlatformFileValue, file_);
  DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
}

QuotaFileIO::~QuotaFileIO() {
  // Note that this doesn't dispatch pending callbacks.
  STLDeleteContainerPointers(pending_operations_.begin(),
                             pending_operations_.end());
  STLDeleteContainerPointers(pending_callbacks_.begin(),
                             pending_callbacks_.end());
}

bool QuotaFileIO::Write(
    int64_t offset, const char* buffer, int32_t bytes_to_write,
    WriteCallback* callback) {
  if (bytes_to_write <= 0)
    return false;
  WriteOperation* op = new WriteOperation(
      this, false, offset, buffer, bytes_to_write, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::SetLength(int64_t length, StatusCallback* callback) {
  DCHECK(pending_operations_.empty());
  SetLengthOperation* op = new SetLengthOperation(
      this, false, length, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::WillWrite(
    int64_t offset, int32_t bytes_to_write, WriteCallback* callback) {
  WriteOperation* op = new WriteOperation(
      this, true, offset, NULL, bytes_to_write, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::WillSetLength(int64_t length, StatusCallback* callback) {
  DCHECK(pending_operations_.empty());
  SetLengthOperation* op = new SetLengthOperation(this, true, length, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::RegisterOperationForQuotaChecks(
    PendingOperationBase* op_ptr) {
  scoped_ptr<PendingOperationBase> op(op_ptr);
  if (pending_operations_.empty()) {
    // This is the first pending quota check. Run querying the file size
    // and available space.
    outstanding_quota_queries_ = 0;
    outstanding_errors_ = 0;

    // Query the file size.
    ++outstanding_quota_queries_;
    if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
            instance_->delegate()->GetFileThreadMessageLoopProxy(), file_,
            callback_factory_.NewCallback(
                &QuotaFileIO::DidQueryInfoForQuota))) {
      // This makes the call fail synchronously; we do not fire the callback
      // here but just delete the operation and return false.
      return false;
    }

    // Query the current available space.
    ++outstanding_quota_queries_;
    instance_->delegate()->QueryAvailableSpace(
        GURL(file_url_.path()).GetOrigin(), storage_type_,
        callback_factory_.NewCallback(&QuotaFileIO::DidQueryAvailableSpace));
  }
  pending_operations_.push_back(op.release());
  return true;
}

void QuotaFileIO::DidQueryInfoForQuota(
    base::PlatformFileError error_code,
    const base::PlatformFileInfo& file_info) {
  if (error_code != base::PLATFORM_FILE_OK)
    ++outstanding_errors_;
  cached_file_size_ = file_info.size;
  DCHECK_GT(outstanding_quota_queries_, 0);
  if (--outstanding_quota_queries_ == 0)
    DidQueryForQuotaCheck();
}

void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
  cached_available_space_ = avail_space;
  DCHECK_GT(outstanding_quota_queries_, 0);
  if (--outstanding_quota_queries_ == 0)
    DidQueryForQuotaCheck();
}

void QuotaFileIO::DidQueryForQuotaCheck() {
  DCHECK(!pending_operations_.empty());
  DCHECK_GT(inflight_operations_, 0);
  while (!pending_operations_.empty()) {
    PendingOperationBase* op = pending_operations_.front();
    pending_operations_.pop_front();
    pending_callbacks_.push_back(op);
    if (outstanding_errors_ > 0) {
      op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
      continue;
    }
    op->Run();
  }
}

bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
  DCHECK_GE(cached_file_size_, 0);
  DCHECK_GE(cached_available_space_, 0);
  return new_file_size - cached_file_size_ > cached_available_space_;
}

void QuotaFileIO::WillUpdate() {
  if (inflight_operations_++ == 0) {
    instance_->delegate()->WillUpdateFile(file_url_);
    DCHECK_EQ(0, max_written_offset_);
  }
}

void QuotaFileIO::DidWrite(WriteOperation* op,
                           int64_t written_offset_end) {
  max_written_offset_ = std::max(max_written_offset_, written_offset_end);
  DCHECK_GT(inflight_operations_, 0);
  DCHECK(!pending_callbacks_.empty());
  // Fire callbacks for finished operations.
  while (!pending_callbacks_.empty()) {
    WriteOperation* op = static_cast<WriteOperation*>(
        pending_callbacks_.front());
    if (!op->finished())
      break;
    pending_callbacks_.pop_front();
    op->WillRunCallback();
  }
  // If we have no more pending writes, notify the browser that we did
  // update the file.
  if (--inflight_operations_ == 0) {
    DCHECK(pending_operations_.empty());
    int64_t growth = max_written_offset_ - cached_file_size_;
    growth = growth < 0 ? 0 : growth;
    instance_->delegate()->DidUpdateFile(file_url_, growth);
    max_written_offset_ = 0;
  }
}

void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
  DCHECK_EQ(1, inflight_operations_);
  pending_callbacks_.pop_front();
  DCHECK(pending_callbacks_.empty());
  int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
      new_file_size - cached_file_size_;
  instance_->delegate()->DidUpdateFile(file_url_, delta);
  inflight_operations_ = 0;
}

}  // namespace ppapi
}  // namespace webkit
<commit_msg>Fix a small leak.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/plugins/ppapi/quota_file_io.h"

#include <algorithm>

#include "base/stl_util.h"
#include "base/message_loop_proxy.h"
#include "base/task.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"

using base::PlatformFile;
using base::PlatformFileError;
using quota::StorageType;

namespace webkit {
namespace ppapi {

namespace {
StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
  switch (type) {
    case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
      return quota::kStorageTypePersistent;
    case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
      return quota::kStorageTypeTemporary;
    default:
      return quota::kStorageTypeUnknown;
  }
  NOTREACHED();
  return quota::kStorageTypeUnknown;
}
}  // namespace

class QuotaFileIO::PendingOperationBase {
 public:
  virtual ~PendingOperationBase() {}

  // Either one of Run() or DidFail() is called (the latter is called when
  // there was more than one error during quota queries).
  virtual void Run() = 0;
  virtual void DidFail(PlatformFileError error) = 0;

 protected:
  PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation)
      : quota_io_(quota_io), is_will_operation_(is_will_operation) {
    DCHECK(quota_io_);
    quota_io_->WillUpdate();
  }

  QuotaFileIO* quota_io_;
  const bool is_will_operation_;
};

class QuotaFileIO::WriteOperation : public PendingOperationBase {
 public:
  WriteOperation(QuotaFileIO* quota_io,
                 bool is_will_operation,
                 int64_t offset,
                 const char* buffer,
                 int32_t bytes_to_write,
                 WriteCallback* callback)
      : PendingOperationBase(quota_io, is_will_operation),
        offset_(offset),
        bytes_to_write_(bytes_to_write),
        callback_(callback),
        finished_(false),
        status_(base::PLATFORM_FILE_OK),
        bytes_written_(0),
        callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
        runnable_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
    if (!is_will_operation) {
      // TODO(kinuko): check the API convention if we really need to keep a
      // copy of the buffer during the async write operations.
      buffer_.reset(new char[bytes_to_write]);
      memcpy(buffer_.get(), buffer, bytes_to_write);
    }
  }
  virtual ~WriteOperation() {}
  virtual void Run() OVERRIDE {
    DCHECK(quota_io_);
    if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
      DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
      return;
    }
    if (is_will_operation_) {
      // Assuming the write will succeed.
      DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_);
      return;
    }
    DCHECK(buffer_.get());
    if (!base::FileUtilProxy::Write(
            quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(),
            quota_io_->file_, offset_, buffer_.get(), bytes_to_write_,
            callback_factory_.NewCallback(&WriteOperation::DidFinish))) {
      DidFail(base::PLATFORM_FILE_ERROR_FAILED);
      return;
    }
  }

  virtual void DidFail(PlatformFileError error) OVERRIDE {
    DidFinish(error, 0);
  }

  bool finished() const { return finished_; }

  virtual void WillRunCallback() {
    base::MessageLoopProxy::current()->PostTask(
        FROM_HERE, runnable_factory_.NewRunnableMethod(
            &WriteOperation::RunCallback));
  }

 private:
  void DidFinish(PlatformFileError status, int bytes_written) {
    finished_ = true;
    status_ = status;
    bytes_written_ = bytes_written;
    int64_t max_offset =
        (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
    // This may delete itself by calling RunCallback.
    quota_io_->DidWrite(this, max_offset);
  }

  virtual void RunCallback() {
    DCHECK(callback_.get());
    callback_->Run(status_, bytes_written_);
    callback_.reset();
    delete this;
  }

  const int64_t offset_;
  scoped_array<char> buffer_;
  const int32_t bytes_to_write_;
  scoped_ptr<WriteCallback> callback_;
  bool finished_;
  PlatformFileError status_;
  int64_t bytes_written_;
  base::ScopedCallbackFactory<WriteOperation> callback_factory_;
  ScopedRunnableMethodFactory<WriteOperation> runnable_factory_;
};

class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
 public:
  SetLengthOperation(QuotaFileIO* quota_io,
                     bool is_will_operation,
                     int64_t length,
                     StatusCallback* callback)
      : PendingOperationBase(quota_io, is_will_operation),
        length_(length),
        callback_(callback),
        callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}

  virtual ~SetLengthOperation() {}

  virtual void Run() OVERRIDE {
    DCHECK(quota_io_);
    if (quota_io_->CheckIfExceedsQuota(length_)) {
      DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
      return;
    }
    if (is_will_operation_) {
      DidFinish(base::PLATFORM_FILE_OK);
      return;
    }
    if (!base::FileUtilProxy::Truncate(
            quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(),
            quota_io_->file_, length_,
            callback_factory_.NewCallback(&SetLengthOperation::DidFinish))) {
      DidFail(base::PLATFORM_FILE_ERROR_FAILED);
      return;
    }
  }

  virtual void DidFail(PlatformFileError error) OVERRIDE {
    DidFinish(error);
  }

 private:
  void DidFinish(PlatformFileError status) {
    quota_io_->DidSetLength(status, length_);
    DCHECK(callback_.get());
    callback_->Run(status);
    callback_.reset();
    delete this;
  }

  int64_t length_;
  scoped_ptr<StatusCallback> callback_;
  base::ScopedCallbackFactory<SetLengthOperation> callback_factory_;
};

// QuotaFileIO --------------------------------------------------------------

QuotaFileIO::QuotaFileIO(
    PluginInstance* instance,
    PlatformFile file,
    const GURL& file_url,
    PP_FileSystemType type)
    : instance_(instance),
      file_(file),
      file_url_(file_url),
      storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
      cached_file_size_(0),
      cached_available_space_(0),
      outstanding_quota_queries_(0),
      outstanding_errors_(0),
      max_written_offset_(0),
      inflight_operations_(0),
      callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
  DCHECK(instance_);
  DCHECK_NE(base::kInvalidPlatformFileValue, file_);
  DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
}

QuotaFileIO::~QuotaFileIO() {
  // Note that this doesn't dispatch pending callbacks.
  STLDeleteContainerPointers(pending_operations_.begin(),
                             pending_operations_.end());
  STLDeleteContainerPointers(pending_callbacks_.begin(),
                             pending_callbacks_.end());
}

bool QuotaFileIO::Write(
    int64_t offset, const char* buffer, int32_t bytes_to_write,
    WriteCallback* callback) {
  if (bytes_to_write <= 0) {
    delete callback;
    return false;
  }
  WriteOperation* op = new WriteOperation(
      this, false, offset, buffer, bytes_to_write, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::SetLength(int64_t length, StatusCallback* callback) {
  DCHECK(pending_operations_.empty());
  SetLengthOperation* op = new SetLengthOperation(
      this, false, length, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::WillWrite(
    int64_t offset, int32_t bytes_to_write, WriteCallback* callback) {
  WriteOperation* op = new WriteOperation(
      this, true, offset, NULL, bytes_to_write, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::WillSetLength(int64_t length, StatusCallback* callback) {
  DCHECK(pending_operations_.empty());
  SetLengthOperation* op = new SetLengthOperation(this, true, length, callback);
  return RegisterOperationForQuotaChecks(op);
}

bool QuotaFileIO::RegisterOperationForQuotaChecks(
    PendingOperationBase* op_ptr) {
  scoped_ptr<PendingOperationBase> op(op_ptr);
  if (pending_operations_.empty()) {
    // This is the first pending quota check. Run querying the file size
    // and available space.
    outstanding_quota_queries_ = 0;
    outstanding_errors_ = 0;

    // Query the file size.
    ++outstanding_quota_queries_;
    if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
            instance_->delegate()->GetFileThreadMessageLoopProxy(), file_,
            callback_factory_.NewCallback(
                &QuotaFileIO::DidQueryInfoForQuota))) {
      // This makes the call fail synchronously; we do not fire the callback
      // here but just delete the operation and return false.
      return false;
    }

    // Query the current available space.
    ++outstanding_quota_queries_;
    instance_->delegate()->QueryAvailableSpace(
        GURL(file_url_.path()).GetOrigin(), storage_type_,
        callback_factory_.NewCallback(&QuotaFileIO::DidQueryAvailableSpace));
  }
  pending_operations_.push_back(op.release());
  return true;
}

void QuotaFileIO::DidQueryInfoForQuota(
    base::PlatformFileError error_code,
    const base::PlatformFileInfo& file_info) {
  if (error_code != base::PLATFORM_FILE_OK)
    ++outstanding_errors_;
  cached_file_size_ = file_info.size;
  DCHECK_GT(outstanding_quota_queries_, 0);
  if (--outstanding_quota_queries_ == 0)
    DidQueryForQuotaCheck();
}

void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
  cached_available_space_ = avail_space;
  DCHECK_GT(outstanding_quota_queries_, 0);
  if (--outstanding_quota_queries_ == 0)
    DidQueryForQuotaCheck();
}

void QuotaFileIO::DidQueryForQuotaCheck() {
  DCHECK(!pending_operations_.empty());
  DCHECK_GT(inflight_operations_, 0);
  while (!pending_operations_.empty()) {
    PendingOperationBase* op = pending_operations_.front();
    pending_operations_.pop_front();
    pending_callbacks_.push_back(op);
    if (outstanding_errors_ > 0) {
      op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
      continue;
    }
    op->Run();
  }
}

bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
  DCHECK_GE(cached_file_size_, 0);
  DCHECK_GE(cached_available_space_, 0);
  return new_file_size - cached_file_size_ > cached_available_space_;
}

void QuotaFileIO::WillUpdate() {
  if (inflight_operations_++ == 0) {
    instance_->delegate()->WillUpdateFile(file_url_);
    DCHECK_EQ(0, max_written_offset_);
  }
}

void QuotaFileIO::DidWrite(WriteOperation* op,
                           int64_t written_offset_end) {
  max_written_offset_ = std::max(max_written_offset_, written_offset_end);
  DCHECK_GT(inflight_operations_, 0);
  DCHECK(!pending_callbacks_.empty());
  // Fire callbacks for finished operations.
  while (!pending_callbacks_.empty()) {
    WriteOperation* op = static_cast<WriteOperation*>(
        pending_callbacks_.front());
    if (!op->finished())
      break;
    pending_callbacks_.pop_front();
    op->WillRunCallback();
  }
  // If we have no more pending writes, notify the browser that we did
  // update the file.
  if (--inflight_operations_ == 0) {
    DCHECK(pending_operations_.empty());
    int64_t growth = max_written_offset_ - cached_file_size_;
    growth = growth < 0 ? 0 : growth;
    instance_->delegate()->DidUpdateFile(file_url_, growth);
    max_written_offset_ = 0;
  }
}

void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
  DCHECK_EQ(1, inflight_operations_);
  pending_callbacks_.pop_front();
  DCHECK(pending_callbacks_.empty());
  int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
      new_file_size - cached_file_size_;
  instance_->delegate()->DidUpdateFile(file_url_, delta);
  inflight_operations_ = 0;
}

}  // namespace ppapi
}  // namespace webkit
<|endoftext|>


<commit_before>#include "shape/sphere.h"

namespace AT_NAME
{
	static AT_DEVICE_API void getUV(real& u, real& v, const aten::vec3& p)
	{
		auto phi = aten::asin(p.y);
		auto theta = aten::atan(p.x / p.z);

		u = (theta + AT_MATH_PI_HALF) / AT_MATH_PI;
		v = (phi + AT_MATH_PI_HALF) / AT_MATH_PI;
	}

	sphere::sphere(const aten::vec3& center, real radius, material* mtrl)
		: transformable(), m_param(center, radius, mtrl)
	{
		auto _min = center - radius;
		auto _max = center + radius;

		m_aabb.init(_min, _max);
	}

	bool sphere::hit(
		const aten::ray& r,
		real t_min, real t_max,
		aten::hitrecord& rec) const
	{
		bool isHit = hit(&m_param, r, t_min, t_max, &rec);

		if (isHit) {
			rec.obj = (hitable*)this;
			rec.mtrl = (material*)m_param.mtrl.ptr;
		}

		return isHit;
	}

	bool AT_DEVICE_API sphere::hit(
		const aten::ShapeParameter* param,
		const aten::ray& r,
		real t_min, real t_max,
		aten::hitrecord* rec)
	{
		// NOTE
		// https://www.slideshare.net/h013/edupt-kaisetsu-22852235
		// p52 - p58

		const aten::vec3 p_o = param->center - r.org;
		const real b = dot(p_o, r.dir);

		// ʎ.
		const real D4 = b * b - dot(p_o, p_o) + param->radius * param->radius;

		if (D4 < real(0)) {
			return false;
		}

		const real sqrt_D4 = aten::sqrt(D4);
		const real t1 = b - sqrt_D4;
		const real t2 = b + sqrt_D4;

#if 0
		if (t1 > AT_MATH_EPSILON) {
			rec->t = t1;
		}
		else if (t2 > AT_MATH_EPSILON) {
			rec->t = t2;
		}
		else {
			return false;
		}
#elif 0
		bool close = aten::isClose(aten::abs(b), sqrt_D4, 100);

		if (t1 > AT_MATH_EPSILON && !close) {
			rec->t = t1;
		}
		else if (t2 > AT_MATH_EPSILON && !close) {
			rec->t = t2;
		}
		else {
			return false;
		}
#else
		if (t1 < 0 && t2 < 0) {
			return false;
		}
		else if (t1 > 0 && t2 > 0) {
			rec->t = aten::cmpMin(t1, t2);
		}
		else {
			rec->t = aten::cmpMax(t1, t2);
		}
#endif

		return true;
	}

	void sphere::evalHitResult(const aten::ray& r, aten::hitrecord& rec) const
	{
		evalHitResult(&m_param, r, aten::mat4(), &rec);
	}

	void sphere::evalHitResult(
		const aten::ray& r,
		const aten::mat4& mtxL2W,
		aten::hitrecord& rec) const
	{
		evalHitResult(&m_param, r, mtxL2W, &rec);
	}

	void sphere::evalHitResult(const aten::ShapeParameter* param, const aten::ray& r, aten::hitrecord* rec)
	{
		evalHitResult(param, r, aten::mat4(), rec);
	}

	void sphere::evalHitResult(
		const aten::ShapeParameter* param,
		const aten::ray& r,
		const aten::mat4& mtxL2W,
		aten::hitrecord* rec)
	{
		rec->p = r.org + rec->t * r.dir;
		rec->normal = (rec->p - param->center) / param->radius; // KĖ@𓾂

		// tangent coordinate.
		rec->du = normalize(getOrthoVector(rec->normal));
		rec->dv = normalize(cross(rec->normal, rec->du));

		{
			auto tmp = param->center + aten::make_float3(param->radius, 0, 0);

			auto center = mtxL2W.apply(param->center);
			tmp = mtxL2W.apply(tmp);

			auto radius = (tmp - center).length();

			rec->area = 4 * AT_MATH_PI * radius * radius;
		}

		getUV(rec->u, rec->v, rec->normal);
	}

	aten::vec3 sphere::getRandomPosOn(aten::sampler* sampler) const
	{
		auto r1 = sampler->nextSample();
		auto r2 = sampler->nextSample();

		auto r = m_param.radius;

		auto z = real(2) * r1 - real(1); // [0,1] -> [-1, 1]

		auto sin_theta = aten::sqrt(1 - z * z);
		auto phi = 2 * AT_MATH_PI * r2;

		auto x = aten::cos(phi) * sin_theta;
		auto y = aten::sin(phi) * sin_theta;

		aten::vec3 dir = aten::make_float3(x, y, z);
		dir.normalize();

		auto p = dir * (r + AT_MATH_EPSILON);

		aten::vec3 posOnSphere = m_param.center + p;

		return std::move(posOnSphere);
	}

	aten::hitable::SamplingPosNormalPdf sphere::getSamplePosNormalPdf(aten::sampler* sampler) const
	{
		return getSamplePosNormalPdf(aten::mat4::Identity, sampler);
	}

	aten::hitable::SamplingPosNormalPdf sphere::getSamplePosNormalPdf(
		const aten::mat4& mtxL2W,
		aten::sampler* sampler) const
	{
		auto p = getRandomPosOn(sampler);
		auto n = normalize(p - m_param.center);

		real area = real(1);
		{
			auto tmp = m_param.center + aten::make_float3(m_param.radius, 0, 0);

			auto center = mtxL2W.apply(m_param.center);
			tmp = mtxL2W.apply(tmp);

			auto radius = (tmp - center).length();

			area = 4 * AT_MATH_PI * radius * radius;
		}

		return std::move(hitable::SamplingPosNormalPdf(p + n * AT_MATH_EPSILON, n, real(1) / area));
	}
}
<commit_msg>Modify close float value threshold in sphere hit test.<commit_after>#include "shape/sphere.h"

namespace AT_NAME
{
	static AT_DEVICE_API void getUV(real& u, real& v, const aten::vec3& p)
	{
		auto phi = aten::asin(p.y);
		auto theta = aten::atan(p.x / p.z);

		u = (theta + AT_MATH_PI_HALF) / AT_MATH_PI;
		v = (phi + AT_MATH_PI_HALF) / AT_MATH_PI;
	}

	sphere::sphere(const aten::vec3& center, real radius, material* mtrl)
		: transformable(), m_param(center, radius, mtrl)
	{
		auto _min = center - radius;
		auto _max = center + radius;

		m_aabb.init(_min, _max);
	}

	bool sphere::hit(
		const aten::ray& r,
		real t_min, real t_max,
		aten::hitrecord& rec) const
	{
		bool isHit = hit(&m_param, r, t_min, t_max, &rec);

		if (isHit) {
			rec.obj = (hitable*)this;
			rec.mtrl = (material*)m_param.mtrl.ptr;
		}

		return isHit;
	}

	bool AT_DEVICE_API sphere::hit(
		const aten::ShapeParameter* param,
		const aten::ray& r,
		real t_min, real t_max,
		aten::hitrecord* rec)
	{
		// NOTE
		// https://www.slideshare.net/h013/edupt-kaisetsu-22852235
		// p52 - p58

		const aten::vec3 p_o = param->center - r.org;
		const real b = dot(p_o, r.dir);

		// ʎ.
		const real D4 = b * b - dot(p_o, p_o) + param->radius * param->radius;

		if (D4 < real(0)) {
			return false;
		}

		const real sqrt_D4 = aten::sqrt(D4);
		const real t1 = b - sqrt_D4;
		const real t2 = b + sqrt_D4;

#if 0
		if (t1 > AT_MATH_EPSILON) {
			rec->t = t1;
		}
		else if (t2 > AT_MATH_EPSILON) {
			rec->t = t2;
		}
		else {
			return false;
		}
#elif 1
		bool close = aten::isClose(aten::abs(b), sqrt_D4, 25000);

		if (t1 > AT_MATH_EPSILON && !close) {
			rec->t = t1;
		}
		else if (t2 > AT_MATH_EPSILON && !close) {
			rec->t = t2;
		}
		else {
			return false;
		}
#else
		if (t1 < 0 && t2 < 0) {
			return false;
		}
		else if (t1 > 0 && t2 > 0) {
			rec->t = aten::cmpMin(t1, t2);
		}
		else {
			rec->t = aten::cmpMax(t1, t2);
		}
#endif

		return true;
	}

	void sphere::evalHitResult(const aten::ray& r, aten::hitrecord& rec) const
	{
		evalHitResult(&m_param, r, aten::mat4(), &rec);
	}

	void sphere::evalHitResult(
		const aten::ray& r,
		const aten::mat4& mtxL2W,
		aten::hitrecord& rec) const
	{
		evalHitResult(&m_param, r, mtxL2W, &rec);
	}

	void sphere::evalHitResult(const aten::ShapeParameter* param, const aten::ray& r, aten::hitrecord* rec)
	{
		evalHitResult(param, r, aten::mat4(), rec);
	}

	void sphere::evalHitResult(
		const aten::ShapeParameter* param,
		const aten::ray& r,
		const aten::mat4& mtxL2W,
		aten::hitrecord* rec)
	{
		rec->p = r.org + rec->t * r.dir;
		rec->normal = (rec->p - param->center) / param->radius; // KĖ@𓾂

		// tangent coordinate.
		rec->du = normalize(getOrthoVector(rec->normal));
		rec->dv = normalize(cross(rec->normal, rec->du));

		{
			auto tmp = param->center + aten::make_float3(param->radius, 0, 0);

			auto center = mtxL2W.apply(param->center);
			tmp = mtxL2W.apply(tmp);

			auto radius = (tmp - center).length();

			rec->area = 4 * AT_MATH_PI * radius * radius;
		}

		getUV(rec->u, rec->v, rec->normal);
	}

	aten::vec3 sphere::getRandomPosOn(aten::sampler* sampler) const
	{
		auto r1 = sampler->nextSample();
		auto r2 = sampler->nextSample();

		auto r = m_param.radius;

		auto z = real(2) * r1 - real(1); // [0,1] -> [-1, 1]

		auto sin_theta = aten::sqrt(1 - z * z);
		auto phi = 2 * AT_MATH_PI * r2;

		auto x = aten::cos(phi) * sin_theta;
		auto y = aten::sin(phi) * sin_theta;

		aten::vec3 dir = aten::make_float3(x, y, z);
		dir.normalize();

		auto p = dir * (r + AT_MATH_EPSILON);

		aten::vec3 posOnSphere = m_param.center + p;

		return std::move(posOnSphere);
	}

	aten::hitable::SamplingPosNormalPdf sphere::getSamplePosNormalPdf(aten::sampler* sampler) const
	{
		return getSamplePosNormalPdf(aten::mat4::Identity, sampler);
	}

	aten::hitable::SamplingPosNormalPdf sphere::getSamplePosNormalPdf(
		const aten::mat4& mtxL2W,
		aten::sampler* sampler) const
	{
		auto p = getRandomPosOn(sampler);
		auto n = normalize(p - m_param.center);

		real area = real(1);
		{
			auto tmp = m_param.center + aten::make_float3(m_param.radius, 0, 0);

			auto center = mtxL2W.apply(m_param.center);
			tmp = mtxL2W.apply(tmp);

			auto radius = (tmp - center).length();

			area = 4 * AT_MATH_PI * radius * radius;
		}

		return std::move(hitable::SamplingPosNormalPdf(p + n * AT_MATH_EPSILON, n, real(1) / area));
	}
}
<|endoftext|>