#include "StdAfx.h"
#include "GeometryRender.h"

#include <LibQtGeoViewerCore/LazyDisp.h>

#include <C2/gl/OpenGLUT/OpenGlutStringExt.h>
#include <C2/gl/MaterialSetter.h>
#include <C2/gl/GlGeometryFunctions.h>

#include <C2/graph/MaterialSamples.h>

#include "VBOBuilder.h"

#include "Shader/ConstantShader.h"

#include <GLUtility/IndexColor.h>

using namespace lib_gl;
using namespace std;



GeometryRender::GeometryRender(MeshBuf* mbuf) :
	m_Mesh(mbuf),
	m_Config(NULL),
	m_SphereClipType(SphereClipType::None),
	m_SphereClipDst(0.0f),
	m_CursorPos(0, 0, 0)
{
}

GeometryRender::~GeometryRender(void)
{
	ReleaseAccBuffer();
}


void GeometryRender::InitializeGeoRender(View3DConfig* config, ShaderLibrary* shaders, SceneMain* scene)
{
	m_Config = config;
	m_Shaders = shaders;
	m_Scene = scene;

	m_LastConfig = *config;
}

void GeometryRender::ReleaseAccBuffer(void)
{
	m_DispList_FaceWire.ReleaseList();
	m_DispList_Vert.ReleaseList();
	m_DispList_Vid.ReleaseList();
	m_DispList_Fid.ReleaseList();

	m_FaceGroupVBO.clear();
}

void GeometryRender::ReleaseIndexDispList(void)
{
	m_DispList_Vid.ReleaseList();
	m_DispList_Fid.ReleaseList();
}


void GeometryRender::DrawSelVertAll(void)
{
	glPushAttrib(GL_LINE_BIT | GL_POINT_BIT | GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glPointSize((float)m_Config->m_PointSize + 4.0f);
	glLineWidth(1.0f);

	m_Mesh->CreateAdjBufOnce();
	lib_geo::BaseMesh& m = m_Mesh->m_Mesh;

	if (m_Config->m_ShowVidTopMost)
		glDisable(GL_DEPTH_TEST);

	for (auto& j : m_Mesh->m_SelVerts)
	{
		if (!j.second)
			continue;

		glColor3d(1,0.25,1);

		int vert_idx = j.first;
		if(vert_idx >= m.m_Verts.size())
			continue;

		const lm::vec3f& v0 = m.m_Verts[vert_idx];
		const lm::vec3f& n = m.m_VertAdj[vert_idx].m_NormalAvg;
		const lm::vec3f v1 = v0 + n * m_Config->m_IndexLineLen;

		glDrawSegment(v0, v1);
		glDrawPoint(v1);

		if(m_Config->m_ShowSelVertCoord || m_Config->m_ShowSelVertIdx)
		{
			std::ostringstream s;

			if(m_Config->m_ShowSelVertIdx)
				s << " " << vert_idx;

			if(m_Config->m_ShowSelVertCoord)
				s << " (" << v1.x << " , " << v1.y << " , " << v1.z << ")";

			glutBitmapString3f(v1, s.str().c_str());
		}
	}

	glPopAttrib();
}

void GeometryRender::DrawGeomVertPick(MeshBuf& mbuf, SceneMain& scene)
{
	GeomObject& obj = *mbuf.GetParent();

	if (m_Config->m_ShowOnlySelect)
	{
		if (!scene.m_Sels.IsInSelects(mbuf))
			return;
	}
	else
	{
		if (!mbuf.IsVisible())
			return;
	}

	if (!obj.m_VertexOnlyMode)
	{
		if(!m_Config->m_DrawVert)
			return;
	}

	const lib_geo::BaseMesh& mesh = mbuf.m_Mesh;

	for (size_t i = 0; i < mesh.m_Verts.size(); ++i)
	{
		glPushName((GLuint)i);
		glDrawPoint(mesh.m_Verts[i]);
		glPopName();
	}
}

//! 選択中オブジェクトに輪郭を描画する
void GeometryRender::DrawGeomHighlight(MeshBuf& mbuf, SceneMain& scene)
{
	const lib_geo::BaseMesh& mesh = mbuf.m_Mesh;

	glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT);
	glDisable(GL_LIGHTING);
	glEnable(GL_STENCIL_TEST);

	if (scene.m_Sels.IsMBufSelected())
		glStencilFunc(GL_NOTEQUAL, mbuf.GetDecWholeIdx() + 1, ~0);
	else
		glStencilFunc(GL_NOTEQUAL, mbuf.GetParent()->GetObjectIndex() + 1, ~0);

	glColor3d(1, 0.5, 0);
	glLineWidth(3.0f);
	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		glBegin(GL_LINE_LOOP);
		for (int vid : f.m_VertIds)
		{
			glVertex3fv(mesh.m_Verts[vid].v());
		}
		glEnd();
	}

	glPopAttrib();
}

//! ジオメトリをサーフェス描画する.
void GeometryRender::DrawMeshMain(SceneMain& scene, ShaderInterface* shader)
{
	if (m_Mesh->GetParent()->m_VertexOnlyMode)
		return;

	if (m_Config->m_DrawFace && m_Config->m_DrawWire)
	{
		DrawMeshFaceMain(scene, shader, MR_FILL);
		DrawMeshFaceMain(scene, shader, MR_WIRE_OVERLAY);
	}
	else if (m_Config->m_DrawFace)
	{
		DrawMeshFaceMain(scene, shader, MR_FILL);
	}
	else if (m_Config->m_DrawWire)
	{
		DrawMeshFaceMain(scene, shader, MR_WIRE_SHADE);
	}
}

void GeometryRender::DrawMeshFaceMain(SceneMain& scene, ShaderInterface* shader, MeshRenderMode mode)
{
	glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_LIGHTING_BIT);

	if (m_Config->m_HighlightSelected)
	{
		glEnable(GL_STENCIL_TEST);

		if (scene.m_Sels.IsMBufSelected())
			glStencilFunc(GL_ALWAYS, m_Mesh->GetDecWholeIdx() + 1, ~0);
		else
			glStencilFunc(GL_ALWAYS, m_Mesh->GetParent()->GetObjectIndex() + 1, ~0);

		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	}

	SetMeshDepthOffset(mode);

	if (mode == MR_WIRE_OVERLAY)
	{
		glDisable(GL_LIGHTING);
		glDepthFunc(GL_LEQUAL);

		glColor3ubv(m_Config->m_WireColor.v());
		DrawFace_WireConstantColor(*m_Mesh, scene);
	}
	else
	{
		DrawFace_Fill(*m_Mesh, scene, shader);
	}

	glPopAttrib();
}

void GeometryRender::SetMeshDepthOffset(MeshRenderMode mode)
{
	static const float OFFSET_UNIT = 0.8f;
	static const float O_1 = OFFSET_UNIT * 1.0f;
	static const float O_2 = OFFSET_UNIT * 2.0f;

	if (mode == MR_FILL)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(O_2, O_2);
	}
	else
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glEnable(GL_POLYGON_OFFSET_LINE);
		glPolygonOffset(O_1, O_1);
	}
}


void GeometryRender::DrawFace_WireConstantColor(MeshBuf& mbuf, SceneMain& scene)
{
	if(m_Config->m_EnableWireVBO)
		DrawFace_WireConstantColorVBO(mbuf, scene);
	else
		DrawFace_WireConstantColorRawOrDispList(mbuf, scene);
}

void GeometryRender::DrawFace_WireConstantColorForCS(MeshBuf& mbuf, SceneMain& scene)
{
	// 断面描画時は三角形化は関係ないので, Faceの設定を使う
	if(m_Config->m_EnableFaceVBO)
		DrawFace_WireConstantColorVBO(mbuf, scene);
	else
		DrawFace_WireConstantColorRawOrDispList(mbuf, scene);
}

void GeometryRender::DrawFace_WireConstantColorVBO(MeshBuf& mbuf, SceneMain& scene)
{
	lib_geo::BaseMesh& mesh = mbuf.m_Mesh;
	if (mesh.m_Verts.empty())
		return;

	ConstantShader* shader = m_Shaders->GetConstantShader();
	if (shader == NULL)
		return;

	shader->BeginShader();

	glDisable(GL_LIGHTING);

	std::map<int, FaceGroup>::iterator it;
	for (it = mbuf.m_FaceMatGroup.begin(); it != mbuf.m_FaceMatGroup.end(); ++it)
	{
		const FaceGroup& fg = it->second;
		if (fg.IsEmpty())
			continue;

		DrawFaceGroupGeometry(mesh, fg, GL_POLYGON, shader);
	}

	shader->EndShader();
}

void GeometryRender::DrawFace_WireConstantColorRawOrDispList(MeshBuf& mbuf, SceneMain& scene)
{
	LazyDisp ld(m_DispList_FaceWire);

	if (m_Config->m_EnableWireDispList)
	{
		if(ld.CallOrBeginList() == LazyDisp::Called)
			return;
	}

	lib_geo::BaseMesh& mesh = mbuf.m_Mesh;

	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		glBegin(GL_POLYGON);
		for (int vid : f.m_VertIds)
		{
			glVertex3fv( mesh.m_Verts[vid].v() );
		}
		glEnd();
	}
}

void GeometryRender::DrawFace_Fill(MeshBuf& mbuf, SceneMain& scene, ShaderInterface* shader)
{
	if (shader == NULL)
		return;

	lib_geo::BaseMesh& mesh = mbuf.m_Mesh;
	if (mesh.m_Verts.empty())
		return;

	shader->BeginShader();
	SetShaderConfig(shader);

	if (m_Config->m_DoubleSideShading)
		glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);

	std::map<int, FaceGroup>::iterator it;
	for (it = mbuf.m_FaceMatGroup.begin(); it != mbuf.m_FaceMatGroup.end(); ++it)
	{
		const FaceGroup& fg = it->second;
		if (fg.IsEmpty())
			continue;

		const lib_geo::BaseFace& top_face = mesh.m_Faces[fg.GetTopFid()];

		AssignTextures(scene, mbuf, shader, top_face);
		AssignMatcap(scene, mbuf, shader, top_face);

		if (m_Config->m_FBMaterial)
		{
			static lib_graph::Material Mat_F = lib_graph::MaterialSamples::GetRuby();
			static lib_graph::Material Mat_B = lib_graph::MaterialSamples::GetEmerald();

			glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
			glEnable(GL_CULL_FACE);

			glCullFace(GL_FRONT);
			lib_gl::MaterialSetter::SetGL(Mat_B);
			DrawFaceGroupGeometry(mesh, fg, GL_TRIANGLE_FAN, shader);

			glCullFace(GL_BACK);
			lib_gl::MaterialSetter::SetGL(Mat_F);
			DrawFaceGroupGeometry(mesh, fg, GL_TRIANGLE_FAN, shader);

			glPopAttrib();
		}
		else
		{
			const lib_graph::Material* mat = GetFaceMaterial(scene, mbuf, top_face);

			bool IsHilightMat = false;
			if (m_Config->m_HighlightMaterial)
			{
				const Selection3D& sels = scene.m_Sels;

				const GeomObject* obj = mbuf.GetParent();
				bool SelObj = sels.IsSelectedMBuf(obj->GetObjectIndex());
				bool Selmat = mbuf.IsSelectedMat(fg.m_GroupID);
				IsHilightMat = (SelObj && Selmat);
			}

			if (IsHilightMat)
				SetMaterialHilight(*mat);
			else
				lib_gl::MaterialSetter::SetGL(*mat);

			DrawFaceGroupGeometry(mesh, fg, GL_TRIANGLE_FAN, shader);
		}
	}

	shader->EndShader();
}

void GeometryRender::SetShaderConfig(ShaderInterface* shader)
{
	shader->SetDublesideEnabled(m_Config->m_DoubleSideShading);
	shader->SetLightingEnabled(m_Config->m_EnableLighting);
	shader->SetFlatShade(m_Config->m_EnableFlatShade);
	shader->SetTextureSpecularSeparate(m_Config->m_SeparateSpecular);
}

void GeometryRender::SetMaterialHilight(const lib_graph::Material& m)
{
	lib_graph::Material sel_mat = m;
	sel_mat.m_Emission.set(0.5f, 0.0f, 0.0f, 1.0f);
	lib_gl::MaterialSetter::SetGL(sel_mat);

	glColor4d(1.0, 0.5, 0.5, 1.0);
}

GeometryVBO* GeometryRender::GetOrCreateVBO(lib_geo::BaseMesh& mesh, const FaceGroup& fg)
{
	std::map<int, GeometryVBO>::iterator it;
	it = m_FaceGroupVBO.find(fg.m_GroupID);
	if (it != m_FaceGroupVBO.end())
		return &it->second;

	GeometryVBO* vbo = &m_FaceGroupVBO[fg.m_GroupID];
	GeomVBOBuilder builder;
	builder.CreateFromFaceGroup(vbo, mesh, fg);

	return vbo;
}

//! 指定したフェースグループの形状描画を実行
void GeometryRender::DrawFaceGroupGeometry(lib_geo::BaseMesh& mesh, const FaceGroup& fg, GLenum primitive_type, ShaderInterface* shader)
{
	if (m_Config->m_EnableFaceVBO)
		return DrawFaceGroupGeometry_VBO(mesh, fg, primitive_type, shader);

	DrawFaceGroupGeometry_STD(mesh, fg, primitive_type, shader);
}

void GeometryRender::DrawFaceGroupGeometry_VBO(lib_geo::BaseMesh& mesh, const FaceGroup& fg, GLenum primitive_type, ShaderInterface* shader)
{
	GeometryVBO* vbo = GetOrCreateVBO(mesh, fg);
	vbo->DrawVBOAll(GL_TRIANGLES, shader);
}

void GeometryRender::DrawFaceGroupGeometry_STD(lib_geo::BaseMesh& mesh, const FaceGroup& fg, GLenum primitive_type, ShaderInterface* shader)
{
	for (int fid : fg.m_Fids)
	{
		lib_geo::BaseFace& f = mesh.m_Faces[fid];
		glBegin(primitive_type);

		for (size_t j = 0; j < f.m_VertIds.size(); ++j)
		{
			if (f.HasUV())
				glTexCoord2fv(mesh.m_UVs[f.m_UVIds[j]].v());

			if (f.HasNormal())
				glNormal3fv(mesh.m_Normals[f.m_NormIds[j]].v());

			glVertex3fv(mesh.m_Verts[f.m_VertIds[j]].v());
		}

		glEnd();
	}
}

void GeometryRender::AssignTextures(SceneMain& scene, MeshBuf& mbuf, ShaderInterface* shader, const lib_geo::BaseFace& f) const
{
	gl::GlTexture* tex_c = NULL;
	gl::GlTexture* tex_n = NULL;
	if (m_Config->m_EnableTexture)
	{
		if (!mbuf.HasTexture())
		{
			tex_c = GetSceneFixedTexture(scene);
		}
		else
		{
			geom::GeomTextureSet* texture = mbuf.GetTextureFromFace(f);
			if (texture != NULL)
			{
				if (texture->HasActiveColorTexture())
					tex_c = texture->GetTexColor();

				if (texture->HasActiveNormalTexture())
					tex_n = texture->GetTexNormal();
			}
		}
	}

	if (tex_c != NULL)
	{
		tex_c->BindGL();
		glColor4d(1, 1, 1, 1);
	}

	bool TextureEnabled = (tex_c != NULL);
	glSetEnable(GL_TEXTURE_2D, TextureEnabled);
	shader->SetColorTextureEnabled(TextureEnabled);

	shader->SetNormalTexture(tex_n);
	if (tex_n != NULL)
	{
		if (mbuf.m_Mesh.HasUV())
		{
			if (!mbuf.m_Mesh.HasTangent())
				mbuf.m_Mesh.CreateTangentsByUV();
		}
	}
}

gl::GlTexture* GeometryRender::GetSceneFixedTexture(SceneMain& scene) const
{
	if (!m_Config->m_UseFixTexture)
		return NULL;

	if (!scene.m_DefaultTexture.HasTextureObject())
		return NULL;

	return &scene.m_DefaultTexture;
}

void GeometryRender::AssignMatcap(SceneMain& scene, MeshBuf& mbuf, ShaderInterface* shader, const lib_geo::BaseFace& f) const
{
	geom::GeomTextureSet* texture = mbuf.GetTextureFromFace(f);
	if (texture != NULL)
	{
		if (texture->TexMatcap.HasImage())
		{
			shader->SetMatcap(&texture->TexMatcap);
			return;
		}
	}

	if (scene.m_MatcapImg.HasImage())
	{
		shader->SetMatcap(&scene.m_MatcapImg);
		return;
	}

	shader->SetMatcap(NULL);
}

const lib_graph::Material* GeometryRender::GetFaceMaterial(SceneMain& scene, const MeshBuf& mbuf, const lib_geo::BaseFace& f) const
{
	if (m_Config->m_IndexMaterial)
	{
		static bool mat_initialized = false;
		static lib_graph::Material index_mats[6];

		if (!mat_initialized)
		{
			for (int i = 0; i < 6; ++i)
			{
				index_mats[i] = lib_graph::MaterialSamples::GetSilver();
				index_mats[i].m_Diffuse = IndexColor::GetColor(i);
			}
			mat_initialized = true;
		}

		return &index_mats[mbuf.m_WholeIndex % 6];
	}

	if (m_Config->m_UseFixMaterial)
		return &scene.m_DefaultMaterial;

	int mat_idx = f.m_MatIdx;
	if (0 <= mat_idx && mat_idx < (int)mbuf.m_Mesh.m_Materials.size())
	{
		return &mbuf.m_Mesh.m_Materials[mat_idx];
	}
	return &scene.m_DefaultMaterial;
}


void GeometryRender::DrawFaceIndexColor(MeshBuf& mbuf, SceneMain& scene, unsigned int idx)
{
	if (mbuf.GetParent()->m_VertexOnlyMode)
		return;

	lib_geo::BaseMesh& mesh = mbuf.m_Mesh;
	if (mesh.m_Verts.empty())
		return;

	if (!m_Config->m_DrawFace && m_Config->m_DrawWire)
		return;

	glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_MULTISAMPLE);
	glDisable(GL_POLYGON_SMOOTH);
	glDisable(GL_LINE_SMOOTH);
	if(m_Config->m_DrawFace)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	SetGlIndexColor(idx);

	std::map<int, FaceGroup>::iterator it;
	for(it = mbuf.m_FaceMatGroup.begin(); it != mbuf.m_FaceMatGroup.end(); ++it)
	{
		const FaceGroup& fg = it->second;
		if(fg.IsEmpty())
			continue;

		DrawFaceGroupGeometry(mesh, fg, GL_TRIANGLE_FAN, m_Shaders->GetConstantShader());
	}

	glPopAttrib();
}

void GeometryRender::SetGlIndexColor(unsigned int idx) const
{
	glColor3ub( ((idx)&0xFF), (((idx)>>8)&0xFF), (((idx)>>16)&0xFF) );

	//// for test
	//switch(idx)
	//{
	//case 0 :return glColor3d(1,0,0);
	//case 1 :return glColor3d(0,1,0);
	//case 2 :return glColor3d(0,0,1);
	//default:
	//	return;
	//}
}


void GeometryRender::SetGLNormalColor(const lm::vec3f& n) const
{
	glColor3f( (n.x + 1.0f) * 0.5f, (n.y + 1.0f) * 0.5f, (n.z + 1.0f) * 0.5f);
}


void GeometryRender::DrawMeshExtAll(void)
{
	lib_geo::BaseMesh& mesh = m_Mesh->m_Mesh;

	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT);
	glDisable(GL_LIGHTING);

	if (m_Mesh->GetParent()->m_VertexOnlyMode || m_Config->m_DrawVert)
		DrawVert(mesh);

	if (m_Config->m_DrawVertNormal)
		DrawVertNormal(mesh);

	if (m_Config->m_DrawFid)
		DrawMeshFid(mesh);

	glPopAttrib();
}

void GeometryRender::DrawMeshFid(lib_geo::BaseMesh& mesh)
{
	glColor3d(1,1,0);
	glPointSize((float)m_Config->m_PointSize);

	if (m_Config->m_ShowVidTopMost)
		glDisable(GL_DEPTH_TEST);

	lib_gl::GlDisplayList& disp_list = m_DispList_Fid;
	if (disp_list.IsEnable())
	{
		disp_list.CallList();
		return;
	}

	disp_list.GenerateList();
	disp_list.BeginCompieAndExecute();

	const IndexRangeConfig& vr = m_Scene->m_IndexVisrange;
	size_t vt = mesh.m_Faces.size() * vr.beginRatio;
	size_t ve = mesh.m_Faces.size() * vr.endRatio;
	for (size_t i = vt; i < ve; ++i)
	{
		const lib_geo::BaseFace& f = mesh.m_Faces[i];
		lm::vec3f c = mesh.GetFaceCenter(f);
		lm::vec3f n = mesh.GetFaceNormal(f);

		lm::vec3f p = c + n * m_Config->m_IndexLineLen;

		glDrawSegment(c, p);
		glDrawPoint(c);
		glutBitmapStringVal3f(p, i);
	}

	disp_list.EndList();
}

bool GeometryRender::CheckSphereClipUpdated(void)
{
	Cursor3D& cursor = m_Scene->m_Cursor3d;
	if (m_SphereClipType != cursor.SphereClip)
		return true;
	if (m_SphereClipDst != m_Scene->GetCursorSphereClipLen())
		return true;
	if (m_CursorPos != cursor.CursorPos)
		return true;
	return false;
}

void GeometryRender::UpdateSphereClipCache(void)
{
	Cursor3D& cursor = m_Scene->m_Cursor3d;
	m_SphereClipType = cursor.SphereClip;
	m_SphereClipDst = m_Scene->GetCursorSphereClipLen();
	m_CursorPos = cursor.CursorPos;
}

void GeometryRender::DrawVert(const lib_geo::BaseMesh& mesh)
{
	glPointSize((float)m_Config->m_PointSize);

	glColor3d(0, 1, 1);

	if (CheckSphereClipUpdated())
	{
		UpdateSphereClipCache();
		m_DispList_Vert.ReleaseList();
		m_DispList_Vid.ReleaseList();
	}

	DrawVertPoint(mesh);

	glPushAttrib(GL_ENABLE_BIT);
	if (m_Config->m_ShowVidTopMost)
		glDisable(GL_DEPTH_TEST);

	if (m_Config->m_DrawVid)
		DrawVertVid(*m_Mesh);

	glPopAttrib();
}

void GeometryRender::DrawVertPoint(const lib_geo::BaseMesh& mesh)
{
	LazyDisp ld(m_DispList_Vert);
	if (ld.CallOrBeginList() == LazyDisp::Called)
		return;

	lm::range3f scene_bb;
	if (m_Scene->m_Cursor3d.IsCircleClipActive())
		scene_bb = m_Scene->GetSceneBBox();

	glBegin(GL_POINTS);
	for (const lm::vec3f& v : mesh.m_Verts)
	{
		if (m_Scene->m_Cursor3d.IsCircleClipActive())
		{
			if (m_Scene->IsOutOfSphereClip(scene_bb, v))
				continue;
		}

		glVertex3fv(v.v());
	}
	glEnd();
}

void GeometryRender::DrawVertVid(MeshBuf& mbuf)
{
	glLineWidth(1.0f);

	mbuf.CreateAdjBufOnce();
	lib_geo::BaseMesh& m = mbuf.m_Mesh;

	LazyDisp ld(m_DispList_Vid);
	if (ld.CallOrBeginList() == LazyDisp::Called)
		return;

	lm::range3f scene_bb;
	if (m_Scene->m_Cursor3d.IsCircleClipActive())
		scene_bb = m_Scene->GetSceneBBox();

	const IndexRangeConfig& vr = m_Scene->m_IndexVisrange;
	size_t vt = m.m_Verts.size() * vr.beginRatio;
	size_t ve = m.m_Verts.size() * vr.endRatio;
	for (size_t i = vt; i < ve; ++i)
	{
		const lm::vec3f& v0 = m.m_Verts[i];
		const lm::vec3f& n = m.m_VertAdj[i].m_NormalAvg;
		const lm::vec3f& v1 = v0 + n * m_Config->m_IndexLineLen;

		if (m_Scene->m_Cursor3d.IsCircleClipActive())
		{
			if (m_Scene->IsOutOfSphereClip(scene_bb, v0))
				continue;
		}

		std::ostringstream s;
		s << " " << i;

		glDrawSegment(v0, v1);
		glutBitmapString3f(v1, s.str().c_str());
	}
}

void GeometryRender::DrawVertNormal(const lib_geo::BaseMesh& mesh)
{
	float LineLen = m_Config->m_NormalLength * m_Mesh->GetBBox().max_length();

	glLineWidth(1.0f);
	glColor3d(0, 0.5, 1);
	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		for (size_t j = 0; j < f.m_NormIds.size(); ++j)
		{
			int vi = f.m_VertIds[j];
			int ni = f.m_NormIds[j];

			const lm::vec3f& v = mesh.m_Verts[vi];
			const lm::vec3f& n = mesh.m_Normals[ni];
			glDrawSegment(v, v + n * LineLen);
		}
	}
}
