void Chart::indicator(const Ogre::Real chartX, const Ogre::Real chartY)
{
	const std::string chartXString = Ogre::StringConverter::toString(chartX).substr(0, std::min(Ogre::StringConverter::toString(chartX).size(), (std::string::size_type)4));
	const std::string chartYString = Ogre::StringConverter::toString(chartY).substr(0, std::min(Ogre::StringConverter::toString(chartY).size(), (std::string::size_type)3));
	const Ogre::Vector3 x = chartToScreen( Ogre::Vector2(chartX, mMin.y) );
	const Ogre::Vector3 y = chartToScreen( Ogre::Vector2(mMin.x, chartY) );
	const Ogre::ColourValue magenta(1,0,1);

	// indicator
    Ogre::ManualObject *chartIndicator = mSceneMgr->createManualObject();
    chartIndicator->setUseIdentityProjection(true);
    chartIndicator->setUseIdentityView(true);
    chartIndicator->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
    chartIndicator->setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);
    chartIndicator->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_STRIP);
    chartIndicator->position( x );
	chartIndicator->colour( magenta );
    chartIndicator->position( chartToScreen( Ogre::Vector2(chartX, chartY) ) );
	chartIndicator->colour( magenta );
    chartIndicator->position( y );
	chartIndicator->colour( magenta );
    chartIndicator->end();

	// text of values on the sides of the axes
	if(first)
	{
		TextRenderer::getSingleton().addTextBox("txt6" + rndIDString, chartXString, x.x - 0.01 * chartXString.size(), x.y - 0.035, 20, 20, magenta);
		TextRenderer::getSingleton().addTextBox("txt7" + rndIDString, chartYString, y.x - 0.035 - 0.01 * chartYString.size(), y.y - 0.0025, 20, 20, magenta);
		first = false;
	} 
	else
	{
		TextRenderer::getSingleton().removeTextBox("txt6" + rndIDString);
		TextRenderer::getSingleton().removeTextBox("txt7" + rndIDString);
		TextRenderer::getSingleton().addTextBox("txt6" + rndIDString, chartXString, x.x - 0.01 * chartXString.size(), x.y - 0.035, 20, 20, magenta);
		TextRenderer::getSingleton().addTextBox("txt7" + rndIDString, chartYString, y.x - 0.035 - 0.01 * chartYString.size(), y.y - 0.0025, 20, 20, magenta);
	}

    if (mChartIndicator)
    {
        mChartSceneNode->detachObject(mChartIndicator);
        mSceneMgr->destroyManualObject(mChartIndicator);
    }
    mChartIndicator = chartIndicator;
    mChartSceneNode->attachObject(mChartIndicator);

    mChartSceneNode->needUpdate();
}
Ogre::SceneNode * Chart::attachTo(Ogre::SceneManager &sceneMgr)
{
    mSceneMgr = &sceneMgr;

    if (mLineNames.size() < 3)
    {
        OGRE_EXCEPT(Ogre::Exception::ERR_INVALID_STATE, "I need exactly three line names. Yeah, it\'s hardcoded ;)", "Chart::attachTo()");
    }

    Ogre::LogManager::getSingletonPtr()->logMessage("mpoints: " + Ogre::StringConverter::toString(mPoints.size()));
    Ogre::LogManager::getSingletonPtr()->logMessage("msize: " + Ogre::StringConverter::toString(mSize));
    Ogre::LogManager::getSingletonPtr()->logMessage("mchartunitstep: " + Ogre::StringConverter::toString(mChartUnitStep));
    Ogre::LogManager::getSingletonPtr()->logMessage("mmax: " + Ogre::StringConverter::toString(mMax));
    Ogre::LogManager::getSingletonPtr()->logMessage("mmin: " + Ogre::StringConverter::toString(mMin));

    // chart quad bg
    Ogre::ManualObject *chartQuad = sceneMgr.createManualObject();
    chartQuad->setUseIdentityProjection(true);
    chartQuad->setUseIdentityView(true);
    chartQuad->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
    chartQuad->setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);
    chartQuad->begin("Chart/Background", Ogre::RenderOperation::OT_TRIANGLE_LIST);
    chartQuad->position(mOffset.x, mOffset.y, 0.5);
    chartQuad->position(mOffset.x + mSize.x, mOffset.y, 0.5);
    chartQuad->position(mOffset.x + mSize.x, mOffset.y + mSize.y, 0.5);
    chartQuad->position(mOffset.x, mOffset.y + mSize.y, 0.5);
    chartQuad->position(mOffset.x, mOffset.y, 0.5);
    chartQuad->position(mOffset.x + mSize.x, mOffset.y + mSize.y, 0.5);
    chartQuad->end();

    // chart y axis
    Ogre::ManualObject *chartAxisY = sceneMgr.createManualObject();
    chartAxisY->setUseIdentityProjection(true);
    chartAxisY->setUseIdentityView(true);
    chartAxisY->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
    chartAxisY->setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);
    chartAxisY->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
    chartAxisY->position(mOffset.x + mPadding.x, mOffset.y + mSize.y - 0.02, 0.0);
	chartAxisY->colour( 0.7, 0.7, 0.7 );
    chartAxisY->position(mOffset.x + mPadding.x, mOffset.y + 0.02, 0.0);
	chartAxisY->colour( 0.7, 0.7, 0.7 );
    chartAxisY->end();

    // chart x axis
    Ogre::ManualObject *chartAxisX = sceneMgr.createManualObject();
    chartAxisX->setUseIdentityProjection(true);
    chartAxisX->setUseIdentityView(true);
    chartAxisX->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
    chartAxisX->setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);
    chartAxisX->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
    chartAxisX->position(mOffset.x + 0.02, mOffset.y + mPadding.y, 0.0);
	chartAxisX->colour( 0.7, 0.7, 0.7 );
    chartAxisX->position(mOffset.x + mSize.x - 0.02, mOffset.y + mPadding.y, 0.0);
	chartAxisX->colour( 0.7, 0.7, 0.7 );
    chartAxisX->end();

    // lines
    const Ogre::ColourValue colours[] = { Ogre::ColourValue(1.0, 0.2, 0.2),
                                          Ogre::ColourValue(0.0, 0.9, 0.0),
                                          Ogre::ColourValue(0.2, 0.2, 1.0) };

    Ogre::ManualObject *lines = sceneMgr.createManualObject();
    lines->setUseIdentityProjection(true);
    lines->setUseIdentityView(true);
    lines->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
    lines->setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);
    lines->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_STRIP);
    for (std::vector<ChartPointsVector>::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it)
    {
        for (ChartPointsVector::const_iterator jt = it->begin(); jt != it->end(); ++jt)
        {
            lines->position(chartToScreen(*jt));
            lines->colour(colours[it - mPoints.begin()]);
        }
    }
    lines->end();

	// caption text
	const Ogre::Real rndID = Ogre::Math::RangeRandom(0, 9999999999) + Ogre::Math::RangeRandom(0, 9999999999);
	TextRenderer::getSingleton().addTextBox("txt0" + Ogre::StringConverter::toString(rndID+ Ogre::Math::RangeRandom(0, 999999)), mCaption, mOffset.x + mSize.x * 0.40, mOffset.y + mSize.y, 100, 20, Ogre::ColourValue::Black);

    // text above the lines	
    TextRenderer::getSingleton().addTextBox("txt1" + Ogre::StringConverter::toString(rndID+ Ogre::Math::RangeRandom(0, 999999)), mLineNames[0], mOffset.x + mPadding.x + 0.0025, mOffset.y + mSize.y - mPadding.y + 0.04, 100, 20, colours[0]);
    TextRenderer::getSingleton().addTextBox("txt2" + Ogre::StringConverter::toString(rndID+ Ogre::Math::RangeRandom(0, 999999)), mLineNames[1], mOffset.x + mPadding.x + 0.0025 + (mSize.x - mPadding.x * 2.0) * 0.35, mOffset.y + mSize.y - mPadding.y + 0.04, 100, 20, colours[1]);
    TextRenderer::getSingleton().addTextBox("txt3" + Ogre::StringConverter::toString(rndID+ Ogre::Math::RangeRandom(0, 999999)), mLineNames[2], mOffset.x + mPadding.x + 0.0025 + (mSize.x - mPadding.x * 2.0) * 0.75, mOffset.y + mSize.y - mPadding.y + 0.04, 100, 20, colours[2]);

    // text on y axis (1)
    TextRenderer::getSingleton().addTextBox("txt4" + Ogre::StringConverter::toString(rndID+ Ogre::Math::RangeRandom(0, 999999)), "1", mOffset.x + mPadding.x - 0.02, mOffset.y + mSize.y - mPadding.y, 20, 20, Ogre::ColourValue::Black);

    // text on x axis
	Ogre::Real rndID2 = Ogre::Math::RangeRandom(0, 9999999999) + Ogre::Math::RangeRandom(0, 9999999999);
    for (std::vector<ChartPointsVector>::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it)
    {
		Ogre::Real rndID3 = Ogre::Math::RangeRandom(0, 9999999999) + Ogre::Math::RangeRandom(0, 9999999999);
        for (ChartPointsVector::const_iterator jt = it->begin(); jt != it->end(); ++jt)
        {
            const Ogre::Real rndID4 = Ogre::Math::RangeRandom(0, 9999999999) + Ogre::Math::RangeRandom(0, 9999999999);
            const Ogre::Vector3 chartVec = chartToScreen(*jt);
			const std::string xString = Ogre::StringConverter::toString(jt->x).substr(0, std::min(Ogre::StringConverter::toString(jt->x).size(), (std::string::size_type)3));

            TextRenderer::getSingleton().addTextBox("txt100" + Ogre::StringConverter::toString(rndID2) + Ogre::StringConverter::toString(rndID3) + Ogre::StringConverter::toString(rndID4), xString, chartVec.x - 0.01 * xString.size(), mOffset.y + mPadding.y - 0.0025, 20, 20, Ogre::ColourValue::Black);
        }
    }

    mChartSceneNode = sceneMgr.getRootSceneNode()->createChildSceneNode();
    mChartSceneNode->attachObject(chartQuad);
    mChartSceneNode->attachObject(chartAxisY);
    mChartSceneNode->attachObject(chartAxisX);
    mChartSceneNode->attachObject(lines);

	setVisible(false);

    return mChartSceneNode;
}
int DecalManager::addTerrainSplineDecal(Ogre::SimpleSpline *spline, float width, Ogre::Vector2 numSeg, Ogre::Vector2 uvSeg, Ogre::String materialname, float ground_offset, Ogre::String export_fn, bool debug)
{
#if 0
	Ogre::ManualObject *mo = gEnv->ogreSceneManager->createManualObject();
	String oname = mo->getName();
	SceneNode *mo_node = terrain_decals_snode->createChildSceneNode();

	mo->begin(materialname, Ogre::RenderOperation::OT_TRIANGLE_LIST);
	AxisAlignedBox *aab=new AxisAlignedBox();

	int offset = 0;

	// how width is the road?
	float delta_width = width / numSeg.x;

	float steps_len = 1.0f / numSeg.x;

	for (int l = 0; l<=numSeg.x; l++)
	{
		// get current position on that spline
		Vector3 pos_cur  = spline->interpolate(steps_len * (float)l);
		Vector3 pos_next = spline->interpolate(steps_len * (float)(l + 1));
        Ogre::Vector3 direction = (pos_next - pos_cur);
		if (l == numSeg.x)
		{
			// last segment uses previous position
			pos_next = spline->interpolate(steps_len * (float)(l - 1));
			direction = (pos_cur - pos_next);
		}


		for (int w = 0; w<=numSeg.y; w++)
		{
			// build vector for the width
			Vector3 wn = direction.normalisedCopy().crossProduct(Vector3::UNIT_Y);
			
			// calculate the offset, spline in the middle
			Vector3 offset = (-0.5 * wn * width) + (w/numSeg.y) * wn * width;

			// push everything together
			Ogre::Vector3 pos = pos_cur + offset;
			// get ground height there
			pos.y = hfinder->getHeightAt(pos.x, pos.z) + ground_offset;

			// add the position to the mesh
			mo->position(pos);
			aab->merge(pos);
			mo->textureCoord(l/(Ogre::Real)numSeg.x*uvSeg.x, w/(Ogre::Real)numSeg.y*uvSeg.y);
			mo->normal(Vector3::UNIT_Y);
		}
	}

	bool reverse = false;
	for (int n1 = 0; n1<numSeg.x; n1++)
	{
		for (int n2 = 0; n2<numSeg.y; n2++)
		{
			if (reverse)
			{
				mo->index(offset+0);
				mo->index(offset+(numSeg.y+1));
				mo->index(offset+1);
				mo->index(offset+1);
				mo->index(offset+(numSeg.y+1));
				mo->index(offset+(numSeg.y+1)+1);
			}
			else
			{
				mo->index(offset+0);
				mo->index(offset+1);
				mo->index(offset+(numSeg.y+1));
				mo->index(offset+1);
				mo->index(offset+(numSeg.y+1)+1);
				mo->index(offset+(numSeg.y+1));
			}
			offset++;
		}
		offset++;
	}
	offset+=numSeg.y+1;

	mo->end();
	mo->setBoundingBox(*aab);
	// some optimizations
	mo->setCastShadows(false);
	mo->setDynamic(false);
	delete(aab);

	MeshPtr mesh = mo->convertToMesh(oname+"_mesh");

	// build edgelist
	mesh->buildEdgeList();

	// remove the manualobject again, since we dont need it anymore
	gEnv->ogreSceneManager->destroyManualObject(mo);

	unsigned short src, dest;
	if (!mesh->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
	{
		mesh->buildTangentVectors(VES_TANGENT, src, dest);
	}
	
	Entity *ent = gEnv->ogreSceneManager->createEntity(oname+"_ent", oname+"_mesh");
	mo_node->attachObject(ent);

	mo_node->setVisible(true);
	//mo_node->showBoundingBox(true);
	mo_node->setPosition(Vector3::ZERO); //(position.x, 0, position.z));


	if (!export_fn.empty())
	{
		MeshSerializer *ms = new MeshSerializer();
		ms->exportMesh(mesh.get(), export_fn);
		LOG("spline mesh exported as " + export_fn);
		delete(ms);
	}

	// TBD: RTSS
	//Ogre::RTShader::ShaderGenerator::getSingleton().createShaderBasedTechnique(materialname, Ogre::MaterialManager::DEFAULT_SCHEME_NAME, Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
	//Ogre::RTShader::ShaderGenerator::getSingleton().invalidateMaterial(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, materialname);
	RTSSgenerateShadersForMaterial(materialname, "");

#endif
	return 0;
}
int DecalManager::addTerrainDecal(Ogre::Vector3 position, Ogre::Vector2 size, Ogre::Vector2 numSeg, Ogre::Real rotation, Ogre::String materialname, Ogre::String normalname)
{
#if 0
	Ogre::ManualObject *mo = gEnv->ogreSceneManager->createManualObject();
	String oname = mo->getName();
	SceneNode *mo_node = terrain_decals_snode->createChildSceneNode();

	mo->begin(materialname, Ogre::RenderOperation::OT_TRIANGLE_LIST);
	AxisAlignedBox *aab=new AxisAlignedBox();
	float uTile = 1, vTile = 1;

	Vector3 normal = Vector3(0,1,0); // UP
	
	int offset = 0;
	float ground_dist = 0.001f;

	Ogre::Vector3 vX = normal.perpendicular();
	Ogre::Vector3 vY = normal.crossProduct(vX);
	Ogre::Vector3 delta1 = size.x / numSeg.x * vX;
	Ogre::Vector3 delta2 = size.y / numSeg.y * vY;
	// build one corner of the square
	Ogre::Vector3 orig = -0.5*size.x*vX - 0.5*size.y*vY;

	for (int i1 = 0; i1<=numSeg.x; i1++)
		for (int i2 = 0; i2<=numSeg.y; i2++)
		{
			Vector3 pos = orig+i1*delta1+i2*delta2 + position;
			pos.y = hfinder->getHeightAt(pos.x, pos.z) + ground_dist;
			mo->position(pos);
			aab->merge(pos);
			mo->textureCoord(i1/(Ogre::Real)numSeg.x*uTile, i2/(Ogre::Real)numSeg.y*vTile);
			mo->normal(normal);
		}

	bool reverse = false;
	if (delta1.crossProduct(delta2).dotProduct(normal)>0)
		reverse= true;
	for (int n1 = 0; n1<numSeg.x; n1++)
	{
		for (int n2 = 0; n2<numSeg.y; n2++)
		{
			if (reverse)
			{
				mo->index(offset+0);
				mo->index(offset+(numSeg.y+1));
				mo->index(offset+1);
				mo->index(offset+1);
				mo->index(offset+(numSeg.y+1));
				mo->index(offset+(numSeg.y+1)+1);
			}
			else
			{
				mo->index(offset+0);
				mo->index(offset+1);
				mo->index(offset+(numSeg.y+1));
				mo->index(offset+1);
				mo->index(offset+(numSeg.y+1)+1);
				mo->index(offset+(numSeg.y+1));
			}
			offset++;
		}
		offset++;
	}
	offset+=numSeg.y+1;

	mo->end();
	mo->setBoundingBox(*aab);
	// some optimizations
	mo->setCastShadows(false);
	mo->setDynamic(false);
	delete(aab);

	MeshPtr mesh = mo->convertToMesh(oname+"_mesh");

	// build edgelist
	mesh->buildEdgeList();

	// remove the manualobject again, since we dont need it anymore
	gEnv->ogreSceneManager->destroyManualObject(mo);

	unsigned short src, dest;
	if (!mesh->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
	{
		mesh->buildTangentVectors(VES_TANGENT, src, dest);
	}
	
	Entity *ent = gEnv->ogreSceneManager->createEntity(oname+"_ent", oname+"_mesh");
	mo_node->attachObject(ent);

	mo_node->setVisible(true);
	//mo_node->showBoundingBox(true);
	mo_node->setPosition(Vector3::ZERO); //(position.x, 0, position.z));


	// RTSS
	//Ogre::RTShader::ShaderGenerator::getSingleton().createShaderBasedTechnique(materialname, Ogre::MaterialManager::DEFAULT_SCHEME_NAME, Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
	//Ogre::RTShader::ShaderGenerator::getSingleton().invalidateMaterial(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, materialname);
	RTSSgenerateShadersForMaterial(materialname, normalname);

#endif
	return 0;
}
void AssetLoader::readModel(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* scnNode, const aiScene* scene, aiNode* nd)
{
	for (size_t n = 0; n < nd->mNumChildren; n++) 
	{	
		aiNode* cnd = nd->mChildren[n];
		Ogre::SceneNode* cnode = createChildSceneNodeWithTransform(scnNode, cnd);

		for (size_t i = 0; i < cnd->mNumMeshes; i++) {
			aiMesh* m = scene->mMeshes[cnd->mMeshes[i]];	
			aiMaterial* mat = scene->mMaterials[m->mMaterialIndex];

			std::string nodeName = getFullPathName(cnd);
			Ogre::MaterialPtr omat = createMaterial(Ogre::String(nodeName), m->mMaterialIndex, mat);

			aiVector3D* vec  = m->mVertices;
			aiVector3D* norm = m->mNormals;
			aiVector3D* uv   = m->mTextureCoords[0];
			aiColor4D*  vcol = NULL;	
			if (m->HasVertexColors(0)) vcol = m->mColors[0];

			// 頂点情報の読み込み
			Ogre::AxisAlignedBox aab;
			Ogre::ManualObject* mobj = new Ogre::ManualObject(nodeName+"_MObj");
			mobj->begin(omat->getName());
			//mobj->begin("Ogre/Skin");
			for (size_t n = 0; n < m->mNumVertices; n ++) {
				Ogre::Vector3 position(vec->x, vec->y, vec->z);
				aab.merge(position);

				mobj->position(vec->x, vec->y, vec->z); 
				vec++;  

				mobj->normal(norm->x, norm->y, norm->z);
				norm++; 

				if (uv)   { 
					mobj->textureCoord(uv->x, uv->y);        
					uv++;   
				}
				if (vcol) { 
					mobj->colour(vcol->r, vcol->g, vcol->b, vcol->a); 
					vcol++;
				}
			}

			// ポリゴンの構築
			for (size_t n = 0; n < m->mNumFaces; n++) {
				aiFace* face = &m->mFaces[n];
				for (size_t k = 0; k < face->mNumIndices; k++) {
					mobj->index(face->mIndices[k]);
				}
			}
			mobj->end();
			mobj->setBoundingBox(aab);

			Ogre::String meshName(nodeName+"_Mesh");
			mobj->convertToMesh(meshName);
			delete mobj;

			Ogre::String entityName(nodeName+"_Entity");
			std::cout << "entity: " << entityName << std::endl;
			Ogre::Entity* ent = sceneMgr->createEntity(entityName, meshName);
			cnode->attachObject(ent);
		}

		readModel(sceneMgr, cnode, scene, cnd);
	}
}