#include "stdafx.h"
#include "View3D.h"

#include "MyGLWidget.h"
#include "QuadCamera.h"

#include <LibQtGeoViewerCore/IndexColor.h>
#include <LibQtGeoViewerCore/GLUtil.h>

#include <C2/graph/MaterialSamples.h>

#include <C2/gl/MaterialSetter.h>

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

#include <C2/gl/GlPicker.h>

#include <C2/util/container_func.h>

using namespace lib_gl;

#include <fstream>
#include <sstream>
#include <set>

using namespace std;

#include <boost/timer.hpp>

#include <QApplication>

#include "MouseButtons.h"
#include "PathInfo.h"

#include "GLProjection.h"

#include "Shader/ConstantShader.h"



View3D::View3D(void) :
	m_Widgets(NULL),
	m_Scene(NULL),
	m_ShaderLib(&m_ShaderContext),
	m_PPLib(&m_PPContext),
	m_PPContext(&m_PPBuf)
{
	m_GlewInitialized = false;

	m_LastRenderTime = 0;

	m_CameraReverse = false;

	m_GridAxisScale = 1.0f;

	m_FpsMode = false;
}

bool View3D::InitializeView(MyGLWidget* ParentWidget)
{
	if (!QtViewBase::InitializeView(ParentWidget))
		return false;

	m_Camera.Reset();
	emit CameraMoved();

	m_ControlLight.SetLightIndex( GL_LIGHT0 );
	m_ControlLight.m_Position.set( -1 , 1 , 1 );

	m_HeadLight.SetLightIndex( GL_LIGHT1 );
	m_HeadLight.m_IsDirectional = false;

	SetLightStrengthByStdRatio(0.5f);

	m_BGColor.set(0.75f, 0.75f, 0.8f, 0.0f);

	BeginRender();

	if (glewInit() == GLEW_OK)
	{
		m_GlewInitialized = true;
	}

	GLUtil::TraceVersion();

	EndRender();

	ParentWidget->ActivateGesture();

	return true;
}

void View3D::SetLightStrengthByStdRatio(float rat)
{
	float n2 = rat * 2.0f;

	m_ControlLight.m_Ambient.set( n2 , n2 , n2 , 1.0f );
	m_ControlLight.m_Diffuse.set( rat , rat , rat , 1.0f );
	m_ControlLight.m_Specular.set( rat , rat , rat , 1.0f );

	m_HeadLight.m_Ambient.set( 0.0f , 0.0f , 0.0f , 0.0f );
	m_HeadLight.m_Diffuse.set( rat , rat , rat , 1.0f );
	m_HeadLight.m_Specular.set( rat , rat , rat , 1.0f );
}

void View3D::RegisterScene(SceneMain* scene)
{
	m_Scene = scene;
	m_Scene->AddObserver( this );

	m_ShaderContext.m_Scene = scene;
}

void View3D::FinalizeView(void)
{
	QtViewBase::FinalizeView();

	BeginRender();

	m_RenderMap.clear();

	EndRender();
}


void View3D::BeginRender(void)
{
	GetParentWidget()->makeCurrent();
}

void View3D::EndRender(void)
{
	GetParentWidget()->doneCurrent();
}


void View3D::OnPaint(void)
{
	PaintMain(false);
}

void View3D::PaintMain(bool showOnlyGeom)
{
	DWORD tick_begin = GetTickCount();

	Render3D(m_Camera, showOnlyGeom);

	m_LastRenderTime = GetTickCount() - tick_begin;

	if (!showOnlyGeom)
	{
		RenderBillboard();
	}

	glFlush();

	GLUtil::TraceGLError();
}

void View3D::Render3D(Camera& camera, bool showOnlyGeom)
{
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);

	if (showOnlyGeom)
		return Render3DScene(camera, true);

	if (!showOnlyGeom && IsRequireUpdateShadowmap())
		UpdateShadowmap();

	if (m_Config.m_PPMode == PostProcType::None)
		return Render3DScene(camera, false);

	Render3DWithPostproc(camera);
}

void View3D::UpdatePostProcContext(Camera& camera)
{
	PostprocContext& pcont = m_PPContext;
	pcont.m_Width  = camera.GetViewport().Width;
	pcont.m_Height = camera.GetViewport().Height;

	GLProjection p;
	p.Initialize();
	pcont.m_LookDepth = p.GetPosDepth(m_Camera.m_Manip.m_ViewPos);
}

void View3D::Render3DWithPostproc(Camera& camera)
{
	PostprocInterface* pp = m_PPLib.GetOrCreatePostproc(m_Config.m_PPMode);

	Viewport& vp = camera.GetViewport();
	Viewport vp_pre = vp;

	size_t num_path = pp->NumPath();

	MultPathRenderBuf& buf_ary = *m_PPContext.m_Buffer;

	buf_ary.ResizePathCount(num_path);
	for (size_t i = 0; i < num_path; ++i)
	{
		PostProcBuffer& pb = buf_ary.GetBuf(i);

		vp = vp_pre;
		vp.Width  *= pp->GetImageSizeRatio(i);
		vp.Height *= pp->GetImageSizeRatio(i);

		pb.InitializeOnce(vp.Width, vp.Height);
		pb.RebuildIfResized(vp.Width, vp.Height);
		pb.m_Fbo.BeginFBO();

		Render3DScene(camera, false);

		UpdatePostProcContext(camera);

		pb.m_Fbo.EndFBO();

		vp = vp_pre;
	}

	vp_pre.SetGLViewport();

	pp->BeginRender();
	RenderCurrentTextureToBillboard(0, 0, vp_pre.Width, vp_pre.Height);
	pp->EndRender();
}

void View3D::Render3DScene(Camera& camera, bool showOnlyGeom)
{
	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT);

	camera.SetViewportAndMatrix();

	ClearRenderBuffer();

	SetDefalutLightState(camera);

	DrawEnvImage(camera);

	if (!showOnlyGeom)
		DrawOptions_Before();

	DrawAllGeom(!showOnlyGeom);

	if (!showOnlyGeom)
		DrawOptions_After();

	glPopAttrib();
}

void View3D::DrawOptions_Before(void)
{
	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);

	if(m_Config.m_DrawBBox)
		DrawObjectsBBox();

	if (m_Config.m_DrawLightPosition)
		DrawLightPos(m_ControlLight);

	if (m_Config.m_DrawGround)
		DrawGround();

	if (m_Config.m_DrawAxis)
		DrawAxis();

	glPopAttrib();
}

void View3D::DrawOptions_After(void)
{
	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);

	if (m_Scene->m_CrossSectionConfig.m_LineCS)
		DrawAllGeomCrossSection();

	if (m_Config.m_DrawCameraRecord)
		m_CameraRecord.DrawRecordPos(m_Camera);

	if (m_Scene->m_CrossSectionConfig.m_ShowCutplane)
		DrawCutPlane();

	if (m_Config.m_DrawLookPos)
		DrawLookPos();

	if (m_Scene->m_Cursor3d.ShowCursor)
		Draw3DCursor();

	glPopAttrib();
}

void View3D::DrawEnvImage(Camera& camera)
{
	float radius = camera.m_Projection.GetClipMid();
	const lm::vec3f& center = camera.m_Manip.m_EyePos;
	m_Scene->m_EnvImg.Draw(radius, center);
}

void View3D::DrawGround(void)
{
	glPushAttrib(GL_LINE_BIT);

	glColor3d( 0.4 , 0.4 , 0.4 );
	glLineWidth(1.0f);
	lib_gl::glDrawGridZX( 20 , 1.0f * m_GridAxisScale );

	glPopAttrib();
}

void View3D::DrawAxis(void)
{
	glPushAttrib(GL_LINE_BIT);

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLTransformOnlyRotate();

	glLineWidth(2.0f);
	lib_gl::glDrawGlobalAxis( 10.0f * m_GridAxisScale );

	glPopMatrix();
	glPopAttrib();
}

void View3D::DrawMiniAxis(void)
{
	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLTransformOnlyRotate();

	lib_gl::DrawMiniAxis(60, 3.0f);

	glPopMatrix();
}

void View3D::Draw3DCursor(void)
{
	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT|GL_LINE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glPointSize(5.0f);
	glLineWidth(1.0f);

	const Cursor3D& cursor = m_Scene->m_Cursor3d;
	const lm::vec3f& p = cursor.CursorPos;
	glColor3d(1,0,0);

	//glDrawPoint(p);
	DrawBmpCross(p);

	lm::vec3f c(0.0f, 0.0f, 0.0f);

	if (m_Scene->m_Config.m_EnableAutoCentering)
		c = m_Scene->GetSceneBBox().center();

	if (cursor.ShowAxis)
		Draw3DCursorAxis(p, c);

	if (cursor.ShowCoord)
		Draw3DCursorCoord(p);

	if (cursor.ShowMeasure)
		Draw3DCursorMeasure();

	if (cursor.CheckBaryCoord)
		Draw3DCursorBary();

	glPopAttrib();
	glPopMatrix();
}

void View3D::Draw3DCursorAxis(const lm::vec3f& p, const lm::vec3f& c)
{
	glBegin(GL_LINES);

	glColor3d(1,0,0);
	glVertex3f(p.x, p.y, p.z);
	glVertex3f(c.x, p.y, p.z);

	glColor3d(0,1,0);
	glVertex3f(p.x, p.y, p.z);
	glVertex3f(p.x, c.y, p.z);

	glColor3d(0,0,1);
	glVertex3f(p.x, p.y, p.z);
	glVertex3f(p.x, p.y, c.z);

	glEnd();
}

void View3D::Draw3DCursorCoord(const lm::vec3f& p)
{
	glColor3d(1,0,0);
	std::ostringstream s;
	s << " " << p.x << " , " << p.y << " , " << p.z;
	glutBitmapString3f(p, s.str().c_str());
}

void View3D::Draw3DCursorMeasure(void)
{
	const Cursor3D& cursor = m_Scene->m_Cursor3d;

	glColor3d(0,1,1);
	glDrawSegment(cursor.MeasurePos, cursor.CursorPos);

	glDrawPoint(cursor.MeasurePos);

	if (cursor.ShowMeasureLen)
	{
		float len = cursor.GetMeasureLength();
		glutBitmapStringVal3f(cursor.GetMidMeasurePos(), len);
	}

	if (cursor.ShowMeasureXYZ)
	{
		const lm::vec3f& p0 = cursor.CursorPos;
		const lm::vec3f& p1 = cursor.MeasurePos;

		glEnable(GL_LINE_STIPPLE);
		glLineStipple(3, 0xaaaa);

		glColor3d(1, 0.2, 0.2);
		glBegin(GL_LINE_STRIP);
		glVertex3f(p0.x, p0.y, p0.z);
		glVertex3f(p1.x, p0.y, p0.z);
		glVertex3f(p1.x, p1.y, p0.z);
		glVertex3f(p1.x, p1.y, p1.z);
		glEnd();

		glColor3d(0.2, 1, 0.2);
		glBegin(GL_LINE_STRIP);
		glVertex3f(p0.x, p0.y, p0.z);
		glVertex3f(p0.x, p1.y, p0.z);
		glVertex3f(p0.x, p1.y, p1.z);
		glVertex3f(p1.x, p1.y, p1.z);
		glEnd();

		glColor3d(0.2, 0.2, 1);
		glBegin(GL_LINE_STRIP);
		glVertex3f(p0.x, p0.y, p0.z);
		glVertex3f(p0.x, p0.y, p1.z);
		glVertex3f(p1.x, p0.y, p1.z);
		glVertex3f(p1.x, p1.y, p1.z);
		glEnd();

		glColor3d(1, 0.2, 0.2);
		glutBitmapStringVal3f(lm::vec3f((p0.x + p1.x) * 0.5f, p0.y, p0.z), fabs(p1.x - p0.x));
		glColor3d(0.2, 1, 0.2);
		glutBitmapStringVal3f(lm::vec3f(p0.x, (p0.y + p1.y) * 0.5f, p0.z), fabs(p1.y - p0.y));
		glColor3d(0.2, 0.2, 1);
		glutBitmapStringVal3f(lm::vec3f(p0.x, p0.y, (p0.z + p1.z) * 0.5f), fabs(p1.z - p0.z));
	}
}

void View3D::Draw3DCursorBary(void)
{
	const Cursor3D& cursor = m_Scene->m_Cursor3d;
	if (!cursor.CloseFace.IsValid())
		return;

	const GeomObject* geo = cursor.CloseFaceObject;
	if (!geo->m_Visible)
		return;

	const lib_geo::BaseMesh& m = geo->m_Mesh;

	const lib_geo::BaryCoord& bc = cursor.CloseFace;

	lm::vec3f pos = m.GetPosFromBaryCoord(bc);

	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
	glDisable(GL_LIGHTING);
	glLineWidth(1.0f);

	const lib_geo::BaseFace& f = m.m_Faces[bc.Fid];

	int vid0 = f.m_VertIds[0];
	int vid1 = f.m_VertIds[1 + bc.Subface];
	int vid2 = f.m_VertIds[2 + bc.Subface];
	const lm::vec3f& v0 = m.m_Verts[vid0];
	const lm::vec3f& v1 = m.m_Verts[vid1];
	const lm::vec3f& v2 = m.m_Verts[vid2];

	glColor3d(1, 0, 0);
	glDrawSegment(pos, cursor.CursorPos);

	glColor3d(1, 0, 0);
	glDrawSegment(pos, v0);
	glColor3d(0, 1, 0);
	glDrawSegment(pos, v1);
	glColor3d(0, 0, 1);
	glDrawSegment(pos, v2);

	glColor3d(1, 0, 0);
	glutBitmapStringVal3f(v0, vid0);
	glColor3d(0, 1, 0);
	glutBitmapStringVal3f(v1, vid1);
	glColor3d(0, 0, 1);
	glutBitmapStringVal3f(v2, vid2);

	{
		glColor3d(1, 0, 0.75);
		std::ostringstream ss;
		ss << bc.Fid << " " << bc.Subface << " " << bc.Bary.x << " " << bc.Bary.y << " " << bc.Bary.z;
		glutBitmapString3f(pos, ss.str().c_str());
	}

	glPopAttrib();
}

void View3D::DrawBmpCross(const lm::vec3f& p)
{
	int Prev_GL_UNPACK_ALIGNMENT  = glGetInteger( GL_UNPACK_ALIGNMENT  );
	int Prev_GL_PACK_ALIGNMENT    = glGetInteger( GL_PACK_ALIGNMENT    );
	int Prev_GL_UNPACK_ROW_LENGTH = glGetInteger( GL_UNPACK_ROW_LENGTH );

	glPixelStorei( GL_UNPACK_ALIGNMENT , 1 );

	static const GLubyte bitmap[8] =
	{
		0x18, // 00011000
		0x18, // 00011000
		0x18, // 00011000
		0xff, // 11111111
		0xff, // 11111111
		0x18, // 00011000
		0x18, // 00011000
		0x18  // 00011000
	};

	glRasterPos3fv(p.v());
	glBitmap(8, 8, 4, 4, 0, 0, bitmap);

	glPixelStorei( GL_UNPACK_ALIGNMENT  , Prev_GL_UNPACK_ALIGNMENT  );
	glPixelStorei( GL_PACK_ALIGNMENT    , Prev_GL_PACK_ALIGNMENT    );
	glPixelStorei( GL_UNPACK_ROW_LENGTH , Prev_GL_UNPACK_ROW_LENGTH );
}

void View3D::DrawLookPos(void)
{
	glLineWidth(1.0f);
	glColor3d(0,0,0);

	const Camera& camera = m_Camera;
	const lib_gl::CameraManipulator& manip = camera.m_Manip;

	float dist = manip.GetDistanceToLookPos();
	lib_gl::glDrawCrossPoint(manip.m_ViewPos, dist);

	glPushAttrib(GL_ENABLE_BIT);
	glDepthMask(GL_FALSE);
	glDisable(GL_LIGHTING);
	glDisable(GL_CULL_FACE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	lm::vec3f lp = manip.m_ViewPos;
	lm::vec3f r = manip.GetBinormal();
	lm::vec3f u = manip.GetUp();

	glColor4d(0, 1, 1, 0.2);

	glBegin(GL_QUADS);
	glVertex3fv((lp + r * dist).v());
	glVertex3fv((lp + u * dist).v());
	glVertex3fv((lp - r * dist).v());
	glVertex3fv((lp - u * dist).v());
	glEnd();

	glDepthMask(GL_TRUE);
	glPopAttrib();
}

ShaderInterface* View3D::GetPrimaryShader(void)
{
	return m_ShaderLib.GetShader(m_Config.m_ShaderMode);
}

void View3D::SetShaderResources(void)
{
	ShaderInterface* shader = GetPrimaryShader();
	if (shader == NULL)
		return;
}

void View3D::RenderBillboard(void)
{
	RenderSelRange();
	
	if (m_Config.m_DrawMiniAxis)
		DrawMiniAxis();

	if (m_Config.m_ShowRenderTime)
		DrawRenderTime();

	if (m_Config.m_DrawRenderRange)
		RenderClipRange();

	if (m_Config.m_DrawShadowmapBuf)
		RenderShadowmapBuf();

	if (m_Config.m_DrawCameraMeasure)
		RenderCameraMeasure();
}

void View3D::RenderSelRange(void)
{
	glColor3d(1, 0, 0);
	m_SelRange.DrawRange(m_Viewport);

	glColor3d(0, 0, 1);
	m_ZoomSelRange.DrawRange(m_Viewport);
	m_ZoomSelRange.DrawCenterCross(m_Viewport);
	m_ZoomSelRange.DrawTitle(m_Viewport, "zoom");
}

bool View3D::IsRequireUpdateShadowmap()
{
	if(!m_Scene->m_ShadowConfig.m_EnableShadow)
		return false;

	ShaderInterface* shader = GetPrimaryShader();
	if (shader == NULL)
		return false;

	return shader->IsRequireShadowBuf();
}

void View3D::RenderShadowmapBuf(void)
{
	if (!IsRequireUpdateShadowmap())
		return;

	m_ShaderContext.m_ShadowBuf.m_ShadowBuffer.Bind();
	RenderCurrentTextureToBillboard(0, 0, 100, 100);
}

void View3D::RenderCameraMeasure(void)
{
	if (m_Camera.m_ProjMode != gl::Camera::PROJ_ORTHO)
		return;

	float rect_dst = m_Camera.m_Manip.GetDistanceToLookPos();
	if (rect_dst <= 0.0f)
		return;

	float w, h;
	m_Camera.m_Projection.GetViewRangeOrtho(rect_dst, w, h);

	float world_scale = m_Scene->m_WorldTransform.GetScale();
	w /= world_scale;
	h /= world_scale;

	glPushAttrib(GL_ENABLE_BIT | GL_TRANSFORM_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glColor3d(0, 1, 1);
	lm::vec2f a(1.0f, 0.0f);
	lm::vec2f b(0.0f, 1.0f);
	RenderCameraMeasureAxis(a, b, w);
	RenderCameraMeasureAxis(b, a, h);

	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	glPopAttrib();
}

void View3D::RenderCameraMeasureAxis(const lm::vec2f& axis_a, const lm::vec2f& axis_b, float abs_len)
{
	lm::vec2f axis_ar = -axis_a;
	lm::vec2f p0 = axis_a * 0.9f;
	lm::vec2f p1 = axis_ar * 0.9f;
	lm::vec2f d = 0.05f * axis_b;
	lm::vec2f dl = 0.02f * axis_b;

	glBegin(GL_LINES);
	glVertex2fv(axis_a.v());
	glVertex2fv(axis_ar.v());
	glEnd();

	float gauge_pos = abs_len * 0.9f;
	float unit = float(gauge_pos);
	if (unit <= 0.0f)
		return;

	float ul = log10(unit);
	float un = floor(ul);
	unit = pow(10.0f, un) / 1.0f;

	float count_prd = gauge_pos / unit;
	if (count_prd <= 3.0f)
		unit /= 10.0f;

	glBegin(GL_LINES);
	float f = unit;
	while(f < gauge_pos)
	{
		lm::vec2f p = axis_a * f / abs_len;
		glVertex2fv((p - dl).v());
		glVertex2fv((p + dl).v());
		glVertex2fv((-p - dl).v());
		glVertex2fv((-p + dl).v());
		f += unit;
	}
	glEnd();

	glBegin(GL_LINES);
	glVertex2fv((p0 - d).v());
	glVertex2fv((p0 + d).v());
	glVertex2fv((p1 - d).v());
	glVertex2fv((p1 + d).v());
	glVertex2fv((p0 * 0.5f - d).v());
	glVertex2fv((p0 * 0.5f + d).v());
	glVertex2fv((p1 * 0.5f - d).v());
	glVertex2fv((p1 * 0.5f + d).v());
	glEnd();

	glutBitmapStringVal2f(-p0 + d, gauge_pos);
	glutBitmapStringVal2f(-p0 * 0.5f + d, gauge_pos * 0.5f);
}

void View3D::DrawLightPos(const lib_gl::Light& light)
{
	glPushAttrib(GL_ENABLE_BIT);

	glLineWidth(1.0f);
	glPointSize(7.0f);

	if (light.m_IsDirectional)
	{
		glColor3d( 0.9 , 0.5 , 0.1 );
	}
	else
	{
		glColor3d( 0.1 , 0.5 , 0.9 );
		glEnable(GL_LINE_STIPPLE);
		glLineStipple(5, 0x5555);
	}

	lib_gl::glDrawSegment(lm::vector3f::get_zero(), light.m_Position);

	glDrawPoint(light.m_Position);

	glPopAttrib();
}

void View3D::RenderClipRange(void)
{
	glPushAttrib( GL_ENABLE_BIT | GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_SCISSOR_BIT );

	glEnable(GL_SCISSOR_TEST);

	Camera camera = m_Camera;
	int qw = m_Viewport.Width / 4;
	int qh = m_Viewport.Height / 4;
	int vw = (std::min)(qw, qh);
	camera.m_Projection.m_Viewport.SetViewport(m_Viewport.Width - vw - 1, 0, vw, vw);

	camera.GetViewport().SetGLScissor();

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);

	camera.GetViewport().SetGLViewport();

	lib_gl::CameraManipulator& m = camera.m_Manip;
	lib_gl::PersProjection& p = camera.m_Projection;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, p.m_Far, -p.m_Far * 0.5, p.m_Far * 0.5, 0, p.m_Far * 2);

	lm::vec3f vp = m.m_EyePos + m.GetBinormal() * p.m_Far;

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt( vp.x , vp.y , vp.z , m.m_EyePos.x , m.m_EyePos.y , m.m_EyePos.z , m.m_Up.x , m.m_Up.y , m.m_Up.z );

	ClearRenderBuffer();

	SetDefalutLightState(camera);

	DrawAllGeom(true);

	DrawClipRangeGraph(camera);

	glPopAttrib();
}

void View3D::DrawClipRangeGraph(Camera& camera)
{
	lib_gl::CameraManipulator& m = camera.m_Manip;
	lib_gl::PersProjection& p = camera.m_Projection;

	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);

	glLineWidth(1.0f);
	glPointSize(3.0f);

	lm::vec3f u = m.m_Up * p.m_Far;
	lm::vec3f np = (m.m_EyePos + m.GetFront() * p.m_Near);

	glColor3d(0.8,0.4,0);
	glDrawSegment(m.m_ViewPos + u, m.m_ViewPos - u);

	glColor3d(1,0,0);
	glDrawSegment(m.m_EyePos, m.m_EyePos + m.GetFront() * p.m_Far);
	glDrawSegment(np + u, np - u);

	if (camera.m_ProjMode == Camera::PROJ_PERS)
	{
		lm::vec3f al0 = m.GetFront();
		lm::vec3f al1 = m.GetFront();
		al0.rotate(p.m_Fovy *  0.5f * M_PI / 180.0, m.GetBinormal());
		al1.rotate(p.m_Fovy * -0.5f * M_PI / 180.0, m.GetBinormal());
		glDrawSegment(m.m_EyePos, m.m_EyePos + al0 * p.m_Far);
		glDrawSegment(m.m_EyePos, m.m_EyePos + al1 * p.m_Far);
	}

	glColor3d(0,0,0);
	glDisable(GL_SCISSOR_TEST);
	lib_gl::glDrawViewportBorder(1.0f);

	glColor3d(0,0,0);
	glDrawPoint(0,0,0);
}

void View3D::ClearRenderBuffer(void)
{
	glClearStencil(0);
	glClearColor(m_BGColor.r(), m_BGColor.g(), m_BGColor.b(), m_BGColor.a());
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}

void View3D::SetDefalutLightState(const Camera& camera)
{
	m_HeadLight.m_Position = camera.m_Manip.m_EyePos;

	if (m_Config.m_EnableLighting)
	{
		m_HeadLight.SetGL();
		glEnable( m_HeadLight.GetLightIndex() );

		m_ControlLight.m_IsDirectional = m_Config.m_LightIsDirectional;
		m_ControlLight.SetGL();
		glEnable( m_ControlLight.GetLightIndex() );

		glEnable(GL_LIGHTING);
	}
	else
	{
		glDisable(GL_LIGHTING);
	}
}

void View3D::SetDefalutCullState(void)
{
	if (m_Config.m_EnableCullFace)
	{
		glEnable(GL_CULL_FACE);

		if(m_Config.m_CullAngle_F)
			glCullFace(GL_FRONT);
		else
			glCullFace(GL_BACK);
	}
}


//! 断面によるクリッピングの設定
void View3D::BeginClipPlaneConfig(void)
{
	if (!m_Scene->m_CrossSectionConfig.m_Enable)
		return;

	if (m_Scene->m_CrossSectionConfig.IsFreeCut())
	{
		lm::vec3f p, n;
		m_Scene->GetFreeCutParam(p, n);
		m_Scene->m_CrossSectionConfig.ApplyClipPlane(p, n);
	}
	else
	{
		float t, b;
		GetClipLimit(t, b);
		m_Scene->m_CrossSectionConfig.ApplyClipPlaneAxis(t, b);
	}
}

void View3D::EndClipPlaneConfig(void)
{
	if (m_Scene->m_CrossSectionConfig.m_Enable)
		m_Scene->m_CrossSectionConfig.DisableGLClipPlane();
}

//! 断面の線表示設定
void View3D::BeginCutPlaneConfig(void)
{
	if (m_Scene->m_CrossSectionConfig.IsFreeCut())
	{
		lm::vec3f p, n;
		m_Scene->GetFreeCutParam(p, n);
		m_Scene->m_CrossSectionConfig.ApplyCutPlane(p, n);
	}
	else
	{
		float t, b;
		GetClipLimit(t, b);
		m_Scene->m_CrossSectionConfig.ApplyCutPlaneAxis(t, b);
	}
}

void View3D::EndCutPlaneConfig(void)
{
	m_Scene->m_CrossSectionConfig.DisableGLClipPlane();
}

void View3D::GetClipLimit(float& t, float& b) const
{
	const lm::range3f bbox = m_Scene->GetSceneBBox();
	lm::vec3f vt = bbox.max_point();
	lm::vec3f vb = bbox.min_point();

	lm::vec3f ax = m_Scene->m_CrossSectionConfig.GetCutAxis();
	t = dot(vt, ax);
	b = dot(vb, ax);
}


void View3D::DrawRenderTime(void)
{
	int vw = m_Viewport.Width;
	int vh = m_Viewport.Height;

	glColor3d(0, 0, 0);
	if (m_LastRenderTime == 0)
		glutBitmapBillboardString(vw, vh, 0, "-");
	else
		glutBitmapBillboardStringVal(vw, vh, 0, 1000.0f / (float)m_LastRenderTime);
}

void View3D::DrawAllGeom(bool UseShader)
{
	SetShaderResources();

	glPushAttrib(GL_ENABLE_BIT);
	glSetEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, m_Config.m_EnableCoverageTrans);
	glSetEnable(GL_MULTISAMPLE, m_Config.m_EnableMultisample);

	//glSampleCoverageARB(0, GL_FALSE);

	SetDefalutCullState();

	// テクスチャ行列はプッシュしない
	m_Scene->m_TextureTransform.SetGLTransform();

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	BeginClipPlaneConfig();

	DrawAllGeomMeshStd(UseShader);

	if (m_Config.m_HighlightSelected)
		DrawGeomMeshHighlight();

	if (m_Config.m_ShowCloseVertInfo)
		DrawCloseVertInfo();

	if (m_Config.m_DrawPolyline)
		DrawAllGeomPolyline();

	glPopMatrix();

	EndClipPlaneConfig();

	glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
	glPopAttrib();
}

void View3D::DrawAllGeomMeshStd(bool UseShader)
{
	glPushAttrib(GL_LIGHTING_BIT);

	ShaderInterface* shader;
	if (UseShader)
		shader = m_ShaderLib.GetShader(m_Config.m_ShaderMode);
	else
		shader = m_ShaderLib.GetShader(ShaderType::Default);

	bool enable_flat = m_Config.m_EnableFlatShade;
	if (enable_flat)
		glShadeModel(GL_FLAT);
	else
		glShadeModel(GL_SMOOTH);

	for (GeomObject& obj : m_Scene->m_Objects)
	{
		if(!IsVisibleObject(obj))
			continue;

		GeometryRender& render = GetRender(&obj);

		render.DrawMeshMain(obj, *m_Scene, shader);

		render.DrawMeshExtAll(obj);

		render.DrawSelVertAll(obj);

		DrawVertexLinker(obj);
	}

	glPopAttrib();
}

void View3D::DrawAllGeomPolyline(void)
{
	for (GeomObject& obj : m_Scene->m_Objects)
	{
		DrawPolyline(obj);
	}
}

void View3D::DrawPolyline(GeomObject& obj)
{
	if(!IsVisibleObject(obj))
		return;

	lib_geo::BaseMesh& m = obj.m_Mesh;
	if (m.m_Polylines.empty())
		return;

	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
	glLineWidth(1.0f);
	glDisable(GL_LIGHTING);

	PolylineContext& pc = obj.m_PolylineContext;

	glColor3d(0.6, 0.4, 0.4);
	for (const lib_geo::BasePolyline& pl : m.m_Polylines)
	{
		DrawPolylineGeom(obj, pl);
	}

	if (m_Config.m_DrawPolylineLen)
	{
		if(!pc.HasLengthInfo())
			pc.CreateLengthInfo(obj);

		assert(m.m_Polylines.size() == pc.LenInfo.size());

		if (m_Config.m_DrawPolylineLenWhl)
		{
			if (!m.m_Polylines.empty())
			{
				float len_sum = 0.0f;
				for (size_t j = 0; j < m.m_Polylines.size(); ++j)
				{
					len_sum += pc.LenInfo[j].Length;
				}

				glutBitmapStringVal3f(pc.LenInfo.front().TargetPos, len_sum);
			}
		}
		else
		{
			for (size_t j = 0; j < m.m_Polylines.size(); ++j)
			{
				const PolylineLenInfo& leninfo = pc.LenInfo[j];

				glutBitmapStringVal3f(leninfo.TargetPos, leninfo.Length);
			}
		}
	}

	glPopAttrib();
}

void View3D::DrawPolylineHighlight(GeomObject& obj)
{
	const lib_geo::BaseMesh& m = obj.m_Mesh;
	if(!IsVisibleObject(obj))
		return;

	if(m.m_Polylines.empty())
		return;

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

	glStencilFunc(GL_NOTEQUAL, obj.m_ObjectIndex + 1, ~0);

	glColor3d(1, 0.5, 0);
	glLineWidth(3.0f);
	for (const lib_geo::BasePolyline& pl : m.m_Polylines)
	{
		DrawPolylineGeom(obj, pl);
	}

	glPopAttrib();
}

void View3D::DrawPolylineGeom(const GeomObject& obj, const lib_geo::BasePolyline& pl)
{
	const lib_geo::BaseMesh& m = obj.m_Mesh;

	glBegin(GL_LINE_STRIP);
	for(int vid : pl.m_VertIds)
	{
		glVertex3fv(m.m_Verts[vid].v());
	}
	glEnd();
}

void View3D::DrawAllGeomMeshIndexColor(int index_offset)
{
	for (size_t i = 0; i < m_Scene->m_Objects.size(); ++i)
	{
		GeomObject& obj = m_Scene->m_Objects[i];
		if(!IsVisibleObject(obj))
			continue;

		GeometryRender& render = GetRender(&obj);

		render.DrawFaceIndexColor(obj, *m_Scene, (unsigned int)(i + index_offset));
	}
}

std::vector<int> View3D::PickObjectAroundMouse(const QPoint& pos, int sel_range)
{
	int index_offset = 1;

	BeginRender();

	m_Camera.SetViewportAndMatrix();

	glClearColor(0, 0, 0, 255);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_DEPTH_TEST);

	SetDefalutCullState();

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	BeginClipPlaneConfig();

	DrawAllGeomMeshIndexColor(index_offset);

	glPopMatrix();

	EndClipPlaneConfig();

	glPopAttrib();

	glFlush();

	QImage bmp = GetParentWidget()->grabFrameBuffer();

	EndRender();

	std::vector<int> picks;

	int h = sel_range;
	for (int i = -h; i < h; ++i)
	{
		for (int j = -h; j < h; ++j)
		{
			int x = pos.x() + i;
			int y = pos.y() + j;

			if(!IsInRange(x, y, bmp))
				continue;

			int idx = QColorToIndex(bmp.pixel(x, y));
			if (idx >= index_offset)
				picks.push_back(idx - index_offset);
		}
	}

	util::vector_unique(picks);

	return picks;
}

bool View3D::IsInRange(int x, int y, const QImage& bmp) const
{
	if(x < 0 || y < 0)
		return false;

	if(x >= bmp.width() || y >= bmp.height())
		return false;

	return true;
}

int View3D::QColorToIndex(const QColor& c) const
{
	int r = c.red();
	int g = c.green();
	int b = c.blue();

	int idx = 0;
	idx = idx * 0x100 + b;
	idx = idx * 0x100 + g;
	idx = idx * 0x100 + r;

	return idx;
}

void View3D::DrawGeomMeshHighlight(void)
{
	GeomObject* obj = m_Scene->GetPrimaryObject();
	if(obj == NULL)
		return;

	GetRender(obj).DrawGeomHighlight(*obj, *m_Scene);

	if(m_Config.m_DrawPolyline)
		DrawPolylineHighlight(*obj);
}

void View3D::DrawVertexLinker(GeomObject& obj)
{
	for (VertexLinker& linker : obj.m_VertLinkers)
	{
		glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_CURRENT_BIT);
		glDisable(GL_LIGHTING);
		glColor3d(0, 1, 0.5);

		glBegin(linker.m_IsLoop ? GL_LINE_LOOP : GL_LINE_STRIP);
		for (int vid : linker.m_VertLinks)
		{
			glVertex3fv(obj.m_Mesh.m_Verts[vid].v());
		}
		glEnd();

		glPopAttrib();
	}
}

void View3D::DrawAllGeomCrossSection(void)
{
	const CrossSectionConfig& cs_config = m_Scene->m_CrossSectionConfig;

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_CULL_FACE);

	if (cs_config.m_Transparent)
	{
		glDisable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
	}

	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	std::vector<GeomObject*> objects = GetVisibleObjects();

	DrawAllGeomCrossSectionLine(objects);

	bool enable_ch = cs_config.m_EnableConvexHull;
	DrawAllGeomCsConvexHull(objects);

	bool show_len = cs_config.m_EnableShowLength;
	bool show_chlen = cs_config.m_EnableShowCHLength && cs_config.m_EnableConvexHull;
	if (show_len || show_chlen)
		DrawAllGeomCsLengthInfo(objects);

	glPopAttrib();

	glPopMatrix();
}

void View3D::DrawAllGeomCrossSectionLine(std::vector<GeomObject*>& objects)
{
	const CrossSectionConfig& cs_config = m_Scene->m_CrossSectionConfig;

	BeginCutPlaneConfig();

	for (GeomObject* obj : objects)
	{
		if (cs_config.m_MultiColor)
			glColor4fv(IndexColor::GetColor(obj->m_ObjectIndex).v());
		else
			glColor3d(1, 0, 0);

		GetRender(obj).DrawFace_WireConstantColorForCS(*obj, *m_Scene);
	}

	EndCutPlaneConfig();
}

void View3D::DrawAllGeomCsConvexHull(std::vector<GeomObject*>& objects)
{
	const CrossSectionConfig& cs_config = m_Scene->m_CrossSectionConfig;

	for (GeomObject* obj : objects)
	{
		if (cs_config.m_EnableConvexHull)
		{
			glColor3d(0.5, 1, 1);
			DrawCsConvexHull(obj->m_CrossSection);
		}

		for (const lib_geo::CrossSection& cs : obj->m_CrossSectionLog)
		{
			if (cs_config.m_EnableConvexHull)
			{
				glColor3d(0.5, 1, 1);
				DrawCsConvexHull(cs);
			}

			lib_graph::color4f c;
			if (cs_config.m_MultiColor)
				c = IndexColor::GetColor(obj->m_ObjectIndex);
			else
				c.set(1.0, 0.0, 0.0, 1.0);

			c *= 0.9f;

			glColor3fv(c.v());

			DrawCrossSectinPath(cs);
		}
	}
}

void View3D::DrawAllGeomCsLengthInfo(std::vector<GeomObject*>& objects)
{
	glColor3d(0.5, 1, 1);

	for (GeomObject* obj : objects)
	{
		DrawCsLengthInfo(obj->m_CrossSection);

		for (const lib_geo::CrossSection& cs : obj->m_CrossSectionLog)
		{
			DrawCsLengthInfo(cs);
		}
	}
}

void View3D::DrawCrossSectinPath(const lib_geo::CrossSection& cs)
{
	for (const lib_geo::CrossSectionUnit& csu : cs.m_Sections)
	{
		glBegin(GL_LINES);
		for (const lib_geo::SectionSegment& seg : csu.m_Segments)
		{
			glVertex3fv(seg.p0.v());
			glVertex3fv(seg.p1.v());
		}
		glEnd();
	}
}

void View3D::DrawCsConvexHull(const lib_geo::CrossSection& cs)
{
	for (const lib_geo::CrossSectionUnit& csu : cs.m_Sections)
	{
		glBegin(GL_LINE_LOOP);
		for (const lm::vec3f& v : csu.m_ConvexVerts)
		{
			glVertex3fv(v.v());
		}
		glEnd();
	}
}

void View3D::DrawCsLengthInfo(const lib_geo::CrossSection& cs)
{
	const CrossSectionConfig& cs_config = m_Scene->m_CrossSectionConfig;
	bool show_len = cs_config.m_EnableShowLength;
	bool show_chlen = cs_config.m_EnableShowCHLength && cs_config.m_EnableConvexHull;

	for (const lib_geo::CrossSectionUnit& csu : cs.m_Sections)
	{
		std::ostringstream s;
		if (show_len && show_chlen)
			s << csu.m_Length << " , " << csu.m_ConvexLength;
		else if (show_len)
			s << csu.m_Length;
		else
			s << csu.m_ConvexLength;

		glutBitmapString3f(csu.m_Range.max_point(), s.str().c_str());
	}
}

void View3D::DrawCutPlane(void) const
{
	const CrossSectionConfig& cs_config = m_Scene->m_CrossSectionConfig;

	if (cs_config.IsFreeCut())
		return;

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	lm::vec3f ax = cs_config.GetCutAxis();
	lm::range3f tb = m_Scene->GetSceneBBox();

	const lm::vec3f& vt = tb.max_point();
	const lm::vec3f& vb = tb.min_point();

	float t = dot(vt, ax);
	float b = dot(vb, ax);

	float cutpos = cs_config.GetCurrentAxisCutPos();
	float h = (b + (t - b) * cs_config.GetCurrentAxisCutPos());

	lm::vec3f p0, p1, p2, p3;
	for (int s = 0; s < 2; ++s)
	{
		glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
		glDisable(GL_LIGHTING);

		if (s)
		{
			glDisable(GL_CULL_FACE);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glDepthMask(GL_FALSE);

			glColor4d(1, 0.2, 0.8, 0.15);
			glBegin(GL_QUADS);
		}
		else
		{
			glLineWidth(3.0f);
			glColor3d(1 ,0.2, 0.8);
			glBegin(GL_LINE_LOOP);
		}

		if (cs_config.IsCutX())
		{
			glVertex3f(h, vb.y, vb.z);
			glVertex3f(h, vt.y, vb.z);
			glVertex3f(h, vt.y, vt.z);
			glVertex3f(h, vb.y, vt.z);
		}
		else if (cs_config.IsCutY())
		{
			glVertex3f(vb.x, h, vb.z);
			glVertex3f(vb.x, h, vt.z);
			glVertex3f(vt.x, h, vt.z);
			glVertex3f(vt.x, h, vb.z);
		}
		else if (cs_config.IsCutZ())
		{
			glVertex3f(vb.x, vb.y, h);
			glVertex3f(vt.x, vb.y, h);
			glVertex3f(vt.x, vt.y, h);
			glVertex3f(vb.x, vt.y, h);
		}

		glEnd();

		if (s)
		{
			glDepthMask(GL_TRUE);
		}

		glPopAttrib();
	}

	float rt, rb;
	GetClipLimit(rt, rb);

	glColor3d(0.2 ,1, 0.8);
	float para = cs_config.GetCurrentAxisCutPos();
	float para_h = (rt - rb) * para;
	if (cs_config.IsCutX())
	{
		glutBitmapStringVal3f(lm::vec3f(h, vt.y, vt.z), para_h);
	}
	else if (cs_config.IsCutY())
	{
		glutBitmapStringVal3f(lm::vec3f(vt.x, h, vt.z), para_h);
	}
	else if (cs_config.IsCutZ())
	{
		glutBitmapStringVal3f(lm::vec3f(vt.x, vt.y, h), para_h);
	}

	glPopMatrix();
}

void View3D::DrawObjectsBBox(void) const
{
	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	glPushAttrib(GL_POINT_BIT | GL_LINE_BIT | GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glPointSize((float)m_Config.m_PointSize + 4.0f);
	glLineWidth(1.0f);

	glColor3d(1,0,1);
	for (const GeomObject& obj : m_Scene->m_Objects)
	{
		if (!IsVisibleObject(obj))
			continue;

		const lm::range3f& bbox = obj.GetBBox();
		lib_gl::glDrawRangeLine(bbox);

		if (m_Config.m_DrawBBoxRange)
		{
			std::ostringstream s;
			const lm::vec3f& t = bbox.max_point();
			const lm::vec3f& b = bbox.min_point();
			float wx = bbox.length_x();
			float wy = bbox.length_y();
			float wz = bbox.length_z();
			s << " ( " << wx << " , " << wy << " , " << wz << " )";
			glutBitmapStringVal3f(t - lm::vec3f(wx * 0.5f, 0.0f, 0.0f), wx);
			glutBitmapStringVal3f(t - lm::vec3f(0.0f, wy * 0.5f, 0.0f), wy);
			glutBitmapStringVal3f(t - lm::vec3f(0.0f, 0.0f, wz * 0.5f), wz);
		}
	}

	glColor3d(0,1,1);
	lib_gl::glDrawRangeLine(m_Scene->GetSceneBBox());

	glPopAttrib();

	glPopMatrix();
}

void View3D::DrawSelVert(GeomObject& obj, int vert_idx)
{
	obj.CreateAdjBufOnce();

	lib_geo::BaseMesh& m = obj.m_Mesh;
	if (vert_idx >= m.m_Verts.size())
		return;

	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);

	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_DEPTH_TEST);

	std::ostringstream s;
	s << " " << vert_idx;
	glutBitmapString3f(v1, s.str().c_str());
	glPopAttrib();
}

void View3D::PickMouseClosePoint(int x, int y)
{
	PickMouseClosePointMain(m_Camera, false, x, y);
}

int ludif(unsigned int a, unsigned int b)
{
	if (a >= b)
		return (int)(a - b);
	else
		return -(int)(b - a);
}

void View3D::PickMouseClosePointMain(Camera& camera, bool scissor, int x, int y)
{
	bool SelectTransparent = m_Config.m_PickTransparent;

	BeginRender();

	glPushAttrib( GL_ENABLE_BIT | GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT );

	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_SCISSOR_TEST);

	static const int pick_range = 10;

	unsigned int depth;
	depth = glaGetDepthui(x, y);

	camera.SetViewportAndMatrix();

	glSetPickMatrixProjection( (double)x , (double)y , (double)pick_range , (double)pick_range );

	ClearRenderBuffer();

	SetDefalutCullState();

	lib_gl::GlPicker picker(100);
	picker.BeginPick();

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLTransform();

	for (size_t i = 0; i < m_Scene->m_Objects.size(); ++i)
	{
		GeomObject& obj = m_Scene->m_Objects[i];

		glPushName((GLuint)i);
		GetRender(&obj).DrawGeomVertPick(obj, *m_Scene);
		glPopName();
	}

	glPopMatrix();

	if (picker.EndPick())
	{
		// 最も浅い位置にある要素を選択
		lib_gl::GlPickedObject* picked;
		picked = picker.GetMinDepthObject();
		if (picked->GetNumLayer() == 2)
		{
			int obj_idx = picked->GetLayerName(0);
			int vid = picked->GetLayerName(1);

			const lm::vec3f& v = m_Scene->m_Objects[obj_idx].m_Mesh.m_Verts[vid];

			GLuint db = picked->GetMaxDepth();

			bool IsPicked = true;
			if (!SelectTransparent)
			{
				if(db > depth)
					IsPicked = false;
			}

			if(IsPicked)
				m_CloseVertInfo.SetIndex(obj_idx, vid);
		}
	}

	glPopAttrib();

	EndRender();
}


void View3D::OnResize(int width, int height)
{
	m_Viewport.SetWidthHeight( width , height );

	m_Camera.m_Projection.m_Viewport = m_Viewport;
}

void View3D::OnMousePress(QMouseEvent *e)
{
	m_LastMousePos = e->localPos();

	Modifier m(e);

	MouseButtons mouse(e);

	if (m_Config.m_FlipMouseMR)
		std::swap(mouse.Mid, mouse.Right);

	if ((!m.Alt && m.Ctrl && m.Shift) && mouse.Left)
	{
		// 最近傍点をスナップ中にAlt+LBDownで選択点追加
		SelectClosestVertOnMouseDown(e);

		m_SelRange.ReleaseSelect();
		m_SelRange.SetFirst(e->pos());

		SelectObjectOnMouseDown(e);
	}
	else
	{
		if(m_SelRange.IsOnSelect())
		{
			m_SelRange.ReleaseSelect();
			RepaintParent();
		}
	}

	if (mouse.Right)
	{
		if (!m.Alt && m.Ctrl && m.Shift)
		{
			m_ZoomSelRange.ReleaseSelect();
			m_ZoomSelRange.SetFirst(e->pos());
		}
	}
	else
	{
		if (m_ZoomSelRange.IsOnSelect())
		{
			m_ZoomSelRange.ReleaseSelect();
			RepaintParent();
		}
	}

	if (mouse.Left)
	{
		m_CameraReverse = CheckCameraIsReverse();
	}

	MouseMove_OnCursorControl(e);
}

bool View3D::CheckCameraIsReverse(void) const
{
	return (m_Camera.m_Manip.GetUp().y < 0.0f);
}

void View3D::SelectClosestVertOnMouseDown(QMouseEvent *e)
{
	if(m_CloseVertInfo.IsValidSelect())
	{
		int obj_idx = m_CloseVertInfo.GetClosestObjectIdx();
		int vert_idx = m_CloseVertInfo.GetClosestVid();
		GeomObject& obj = m_Scene->m_Objects[obj_idx];
		obj.SwapVertSelect(vert_idx);

		m_Widgets->UpdateAll();
	}
}

void View3D::SelectObjectOnMouseDown(QMouseEvent *e)
{
	std::vector<int> vs = PickObjectAroundMouse(e->pos(), 2);

	if(vs.empty())
		return;

	emit SelectedObjectChanged(vs.front());
}

void View3D::OnMouseRelease(QMouseEvent *e)
{
	Modifier m(e);

	if(m_SelRange.IsOnSelect())
	{
		SelectVertsSelRange(!m.Shift);

		m_SelRange.ReleaseSelect();
		RepaintParent();
	}

	if(m_ZoomSelRange.IsOnSelect())
	{
		ZoomSelRange();

		m_ZoomSelRange.ReleaseSelect();
		RepaintParent();
	}
}

void View3D::OnMouseDoubleClick(QMouseEvent *e)
{
}


void View3D::OnMouseMove(QMouseEvent *e)
{
	QPointF diff = e->localPos() - m_LastMousePos;
	m_LastMousePos = e->localPos();

	if( MouseMove_OnSelRange        ( e , diff ) ) return;
	if( MouseMove_OnZoomSelRange    ( e , diff ) ) return;
	if( MouseMove_OnUpdateCloseVert ( e , diff ) ) return;
	if( MouseMove_OnLightControl    ( e , diff ) ) return;
	if( MouseMove_OnCameraControl   ( e , diff ) ) return;
	if( MouseMove_OnCursorControl   ( e )        ) return;
}

bool View3D::MouseMove_OnSelRange(QMouseEvent* e, QPointF& diff)
{
	if (!m_SelRange.IsOnSelect())
		return false;

	Modifier m(e);

	if (!m.Alt && m.Ctrl && m.Shift)
	{
		m_SelRange.SetSecond(e->pos());
		RepaintParent();
		return true;
	}

	return false;
}

bool View3D::MouseMove_OnZoomSelRange(QMouseEvent* e, QPointF& diff)
{
	if (!m_ZoomSelRange.IsOnSelect())
		return false;

	Modifier m(e);

	if (!m.Alt && m.Ctrl && m.Shift)
	{
		m_ZoomSelRange.SetSecond(e->pos());
		RepaintParent();
		return true;
	}

	return false;
}

bool View3D::MouseMove_OnUpdateCloseVert(QMouseEvent* e, QPointF& diff)
{
	UpdateCloseVert(e);

	// クリップに成功しても他のハンドラのブロックはしない
	return false;
}

bool View3D::UpdateCloseVert(QMouseEvent* e)
{
	m_CloseVertInfo.Reset();

	if(!m_Config.m_ShowCloseVertInfo)
		return false;

	PickMouseClosePoint(e->x(), m_Viewport.Height - e->y() - 1);
	RepaintParent();

	return true;
}

bool View3D::MouseMove_OnCameraControl(QMouseEvent* e, QPointF& diff)
{
	MouseButtons mouse(e);

	if (m_Config.m_FlipMouseMR)
		std::swap(mouse.Mid, mouse.Right);

	Modifier m(e);

	Camera& camera = m_Camera;
	lib_gl::CameraManipulator& manip = camera.m_Manip;

	float camera_dist = manip.GetDistanceToLookPos();

	if (m.Shift || m.IsShiftOnly())
		return false;

	if (mouse.Mid || (mouse.Left && mouse.Right))
	{
		lm::vec2f d;
		d.x =  diff.x() / m_Viewport.Width;
		d.y = -diff.y() / m_Viewport.Height;

		float mx = -d.x * camera_dist;
		float my = -d.y * camera_dist;

		if (m.Ctrl)
		{
			if (m.Alt)
				mx = 0;
			else
				my = 0;
		}

		manip.Track( mx , my );
	}
	else if (mouse.Right)
	{
		if (!m_FpsMode)
		{
			if (!m.IsNone())
				return false;

			float my = camera_dist * diff.y() / m_Viewport.Height;
			manip.Dolly(my);
		}
		else
		{
			lm::vec2f d;
			d.x =  diff.x() / m_Viewport.Width;
			d.y = -diff.y() / m_Viewport.Height;

			float mx = -d.x * camera_dist;
			float my = -d.y * camera_dist;

			if (m.Ctrl)
			{
				if (m.Alt)
					mx = 0;
				else
					my = 0;
			}

			manip.Track(mx, 0.0f, my);
		}
	}
	else if (mouse.Left)
	{
		float mx = -float(M_PI) * diff.x() / m_Viewport.Width;
		float my = -float(M_PI) * diff.y() / m_Viewport.Height;

		if (m.Ctrl)
		{
			if (m.Alt)
				mx = 0;
			else
				my = 0;
		}

		if (!m_FpsMode)
		{
			if (m_CameraReverse)
				mx *= -1.0f;

			manip.Tumble( mx , my );
		}
		else
		{
			manip.Rotate( mx , my );
		}
	}
	else
		return false;

	emit CameraMoved();

	RepaintParent();
	return true;
}

bool View3D::MouseMove_OnCursorControl(QMouseEvent* e)
{
	MouseButtons mouse(e);
	Modifier m(e);

	Cursor3D& cursor = m_Scene->m_Cursor3d;

	if (!cursor.ShowCursor)
		return false;

	if (!m.IsShiftOnly())
		return false;

	bool ControlMeasure = false;

	if(cursor.ShowMeasure)
	{
		if (mouse.IsRightOnly())
			ControlMeasure = true;
		else if(!mouse.IsLeftOnly())
			return false;
	}
	else
	{
		if (!mouse.IsLeftOnly())
			return false;
	}

	double mx = e->localPos().x();
	double my = e->localPos().y();

	if (ControlMeasure)
	{
		cursor.ResetMeasure();
		cursor.MeasurePos = Move3DCursor(cursor.MeasurePos, mx, my);
	}
	else
	{
		util::Optional<lm::vec3f> depth_pos;
		if (cursor.CursorDepth)
		{
			BeginRender();
			depth_pos = GetGeomDepht3DPos(mx, my);
			EndRender();
		}

		if (depth_pos.IsValid())
			cursor.CursorPos = m_Scene->m_WorldTransform.InverseTransformVec(depth_pos.Get());
		else
			cursor.CursorPos = Move3DCursor(cursor.CursorPos, mx, my);
	}

	cursor.ResetCloseFace();

	if (cursor.CheckBaryCoord)
	{
		GeomObject* obj = m_Scene->GetPrimaryObject();
		if (obj != NULL)
		{
			cursor.CloseFaceObject = obj;
			int fid, subface;
			lm::vec3f close_pos;
			if (obj->m_Mesh.GetClosestSubfacePos(cursor.CursorPos, fid, subface, close_pos))
			{
				obj->m_Mesh.CalcBaryCoord(close_pos, fid, subface, cursor.CloseFace);
			}
		}
	}

	UpdateCursorCutNormal();

	if (m_Scene->m_CrossSectionConfig.IsFreeCut())
		m_Scene->UpdateCrossSectionIfRequire(false);

	RepaintParent();
	return true;
}

lm::vec3f View3D::Move3DCursor(lm::vec3f& cp, double px, double py)
{
	lm::vec3f dst;

	BeginRender();

	m_Camera.SetViewportAndMatrix();

	glPushMatrix();
	m_Scene->m_WorldTransform.SetGLWorldConfig();

	GLProjection p;
	p.Initialize();

	GLdouble wx, wy, wz;
	p.Project(cp, wx, wy, wz);

	wx = px;
	wy = p.GetViewport()[3] - py - 1;

	p.Unproject(wx, wy, wz, dst);

	glPopMatrix();

	EndRender();

	return dst;
}

bool View3D::MouseMove_OnLightControl(QMouseEvent* e, QPointF& diff)
{
	Modifier m(e);

	if (!m.IsAltOnly())
		return false;

	float dx =  (float)diff.x() / (float)m_Viewport.Width;
	float dy = -(float)diff.y() / (float)m_Viewport.Height;

	lib_gl::Light& light_ = m_ControlLight;

	MouseButtons mouse(e);

	if (m_Config.m_FlipMouseMR)
		std::swap(mouse.Mid, mouse.Right);

	if(mouse.Left)
	{
		const lm::vec3f gy(0.0f, 1.0f, 0.0f);
		light_.m_Position.rotate( dx * float(M_PI) , gy );
		lm::vector3f ax = cross( light_.m_Position , gy );
		ax.normalize();
		light_.m_Position.rotate( dy * float(M_PI) , ax );
	}
	else if(mouse.Mid)
	{
		float light_dist = light_.m_Position.length();
		light_dist -= dy;
		light_dist = (std::max)( 0.01f , light_dist );
		light_.m_Position.normalize();
		light_.m_Position *= light_dist;
	}
	else if(mouse.Right)
	{
		float dx =  diff.x() / m_Viewport.Width;
		float dy = -diff.y() / m_Viewport.Height;

		const lib_gl::CameraManipulator& manip = m_Camera.m_Manip;
		lm::vec3f n = manip.GetUp();
		lm::vec3f b = manip.GetBinormal();

		lm::vec3f move = b * dx + n * dy;
		m_ControlLight.m_Position += move;
	}
	else return false;

	RepaintParent();
	return true;
}


void View3D::OnGesturePan(QPanGesture *gesture)
{
	lib_gl::CameraManipulator& manip = m_Camera.m_Manip;

	if(gesture->state() == Qt::GestureStarted)
	{
		int x = gesture->hotSpot().x();
		int y = gesture->hotSpot().y();
		QPoint lp = GetParentWidget()->mapFromGlobal(QPoint(x, y));
	}

	float dx =  gesture->delta().x() / m_Viewport.Width;
	float dy = -gesture->delta().y() / m_Viewport.Height;

	float camera_dist = manip.GetDistanceToLookPos();

	float mx = -dx * camera_dist;
	float my = -dy * camera_dist;

	manip.Track( mx , my );

	emit CameraMoved();
	RepaintParent();
}

void View3D::OnGesturePinch(QPinchGesture *gesture)
{
	m_Camera.m_Manip.DollyByRatio(1.0f / gesture->lastScaleFactor());

	emit CameraMoved();
	RepaintParent();
}


void View3D::OnWheel(QWheelEvent *e)
{
	if( e->delta() == 0 )
		return;

	Camera& camera = m_Camera;
	lib_gl::CameraManipulator& manip = camera.m_Manip;
	lib_gl::PersProjection&    proj  = camera.m_Projection;

	float camera_dist = manip.GetDistanceToLookPos();

	Modifier m(e);

	if (m.Ctrl)
	{
		float zoom_size = 1.1f;

		if( e->delta() > 0)
			zoom_size = 1.0f / zoom_size;

		if(m.Shift)
			proj.m_Near *= zoom_size;
		else
			proj.m_Far *= zoom_size;
	}
	else
	{
		const float zoom_size = 0.1f;
		float zoom = zoom_size * camera_dist;
		if( e->delta() < 0 )
			zoom *= -1.0f;

		manip.Dolly( zoom );
	}

	emit CameraMoved();
	RepaintParent();
}

void View3D::OnKeyPress(QKeyEvent *e)
{
	Modifier m(e);

	if(KeyPressOnRotCamera(e, m))
		return;
}

bool View3D::KeyPressOnRotCamera(QKeyEvent *e, const Modifier& m)
{
	if (!m.Ctrl || m.Shift)
		return false;

	double RotSpeed = M_PI * 1 / 180;
	if (m.Alt)
		RotSpeed *= 5.0;

	lib_gl::CameraManipulator& manip = m_Camera.m_Manip;

	lm::vec2f rot;
	if(e->key() == Qt::Key_4)
		rot.set(-(float)RotSpeed, 0.0);
	else if(e->key() == Qt::Key_6)
		rot.set((float)RotSpeed, 0.0);
	else if(e->key() == Qt::Key_8)
		rot.set(0.0, -(float)RotSpeed);
	else if(e->key() == Qt::Key_2)
		rot.set(0.0, (float)RotSpeed);
	else
		return false;

	if (CheckCameraIsReverse())
		rot.x = -rot.x;

	manip.Tumble(rot.x, rot.y);

	emit CameraMoved();
	RepaintParent();
	return true;
}


void View3D::OnGeometryBuild(const SceneMain& scene)
{
	BeginRender();

	m_RenderMap.clear();

	EndRender();
}

GeometryRender& View3D::GetRender(const GeomObject* geom)
{
	boost::ptr_map<const GeomObject*, GeometryRender>::iterator found;
	found = m_RenderMap.find(geom);
	if (found != m_RenderMap.end())
		return *found->second;

	GeometryRender& new_render = m_RenderMap[geom];
	new_render.InitializeRender(&m_Config, &m_ShaderLib);
	return new_render;
}


//! プライマリビューポートの中心を通る十字を表示する
void View3D::DrawCenterCross(void)
{
	m_Viewport.SetGLViewport();

	glColor3d(0,0,0);

	lib_gl::glDrawCenterCross(1.0f);
}


//! OpenGL用バッファのクリア
void View3D::ReleaseRenderbuffer(GeomObject* obj)
{
	GetRender(obj).ReleaseAccBuffer();
}


void View3D::RecordCamera(void)
{
	m_CameraRecord.RecordCamera(m_Camera);
}

void View3D::MoveToRecordedCamera(int camera_idx, bool animation)
{
	lib_gl::CameraManipulator& manip = m_Camera.m_Manip;

	lib_gl::CameraManipulator src = manip;
	const lib_gl::CameraManipulator& dst = m_CameraRecord.m_Records[camera_idx];

	if(animation)
	{
		static const int AnimationCount = 30;

		for(size_t i = 0; i < AnimationCount; ++i)
		{
			float n = (float)i / (float)(AnimationCount - 1);
			manip.SetSlerp(src, dst, n);

			emit CameraMoved();
			RepaintParent();
			Sleep(10);
		}
	}
	else
	{
		manip.SetSlerp(src, dst, 1.0f);

		emit CameraMoved();
		RepaintParent();
	}
}

void View3D::RemoveRecordedCamera(int camera_idx)
{
	m_CameraRecord.RemoveRecordedCamera(camera_idx);
}

void View3D::ClearRecordedCamera(void)
{
	m_CameraRecord.Clear();
}


void View3D::ResetPrimaryObjectLists(void)
{
	GeomObject* obj = m_Scene->GetPrimaryObject();
	if (obj == NULL)
		return;

	MyGLWidget* w = GetParentWidget();
	w->makeCurrent();
	ReleaseRenderbuffer(obj);
	w->doneCurrent();
}

void View3D::ReleaseAllRenderBuffer(void)
{
	MyGLWidget* w = GetParentWidget();

	w->makeCurrent();
	for (GeomObject& obj : m_Scene->m_Objects)
	{
		ReleaseRenderbuffer(&obj);
	}
	w->doneCurrent();
}


void View3D::UpdateShadowmap(void)
{
	glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT);
	glSetEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, m_Config.m_EnableCoverageTrans);
	glSetEnable(GL_MULTISAMPLE, m_Config.m_EnableMultisample);

	ShadowBuffer* shadowbuf = &m_ShaderContext.m_ShadowBuf;

	shadowbuf->InitializeShadowbufferOnce();

	shadowbuf->BeginCreateShadowmap();

	glViewport(0, 0, shadowbuf->GetWidth(), shadowbuf->GetHeight());

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	PushAndIdentityGLModelProjMatrix();

	// projectionにまとめて射影行列を生成する
	glMatrixMode(GL_PROJECTION);

	lm::range3f sr = m_Scene->GetSceneTransformedBBox();

	float MapWidth = (sr.max_point() - sr.min_point()).length();
	lm::vec3f map_mid = sr.mid_point();

	lm::vec3f project_dir = m_ControlLight.m_Position.get_normalize();

	shadowbuf->SetGLShadowMatrix(project_dir, map_mid, MapWidth, MapWidth);

	m_Scene->m_WorldTransform.SetGLTransform();

	shadowbuf->UpdateShadowMatrix();

	glMatrixMode(GL_MODELVIEW);

	DrawAllGeomForShadowbuf();

	PopGLModelProjMatrix();

	shadowbuf->EndCreateShadowmap();

	glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
	glPopAttrib();
}

void View3D::DrawAllGeomForShadowbuf(void)
{
	for (GeomObject& obj : m_Scene->m_Objects)
	{
		if (!IsVisibleObject(obj))
			continue;

		if (obj.m_VertexOnlyMode)
			continue;

		GeometryRender& render = GetRender(&obj);
		render.DrawFace_Fill(obj, *m_Scene, m_ShaderLib.GetConstantShader());
	}
}

void View3D::PushAndIdentityGLModelProjMatrix(void)
{
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
}

void View3D::PopGLModelProjMatrix(void)
{
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}

void View3D::PushAndIdentityGLAllMatrix(void)
{
	PushAndIdentityGLModelProjMatrix();
	glMatrixMode(GL_TEXTURE);
	glPushMatrix();
	glLoadIdentity();
}

void View3D::PopGLAllMatrix(void)
{
	glMatrixMode(GL_TEXTURE);
	glPopMatrix();
	PopGLModelProjMatrix();
}

void View3D::RenderCurrentTextureToBillboard(int left, int top, int width, int height)
{
	glPushAttrib(GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_POLYGON_BIT);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);

	PushAndIdentityGLAllMatrix();

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	double dw = (double)m_Viewport.Width / 2.0;
	double dh = (double)m_Viewport.Height / 2.0;
	double l = -1 + left / dw;
	double r = l + width / dw;
	double t = 1 - top / dh;
	double b = t - height / dh;

	glColor3d(1,1,1);
	glBegin(GL_QUADS);
	glTexCoord2d(0,0); glVertex3d(l, b, 0);
	glTexCoord2d(1,0); glVertex3d(r, b, 0);
	glTexCoord2d(1,1); glVertex3d(r, t, 0);
	glTexCoord2d(0,1); glVertex3d(l, t, 0);
	glEnd();

	PopGLAllMatrix();

	glPopAttrib();
}


void View3D::RepaintParent(void)
{
	GetParentWidget()->repaint();
}


void View3D::SelectVertsSelRange(bool select_add)
{
}


void View3D::DrawCloseVertInfo(void)
{
	glPushAttrib(GL_LINE_BIT | GL_POINT_BIT | GL_ENABLE_BIT);
	glPointSize((float)m_Config.m_PointSize + 4.0f);
	glLineWidth(1.0f);
	glDisable(GL_LIGHTING);

	if(m_CloseVertInfo.IsValidSelect())
	{
		int obj_idx  = m_CloseVertInfo.GetClosestObjectIdx();
		int vert_idx = m_CloseVertInfo.GetClosestVid();

		glColor3d(1, 0.8, 0.25);

		if(obj_idx < m_Scene->m_Objects.size())
		{
			GeomObject& obj = m_Scene->m_Objects[obj_idx];
			DrawSelVert(obj, vert_idx);
		}
	}

	glPopAttrib();
}


void View3D::ZoomSelRange(void)
{
	const SelRange& sel = m_ZoomSelRange;

	Camera& camera = m_Camera;
	const lib_gl::PersProjection& proj = camera.m_Projection;
	lib_gl::CameraManipulator& manip = camera.m_Manip;

	float rect_dst = manip.GetDistanceToLookPos();
	if (rect_dst <= 0.0f)
		return;

	int vw = m_Viewport.Width;
	int vh = m_Viewport.Height;

	QPoint cp = sel.GetCenter();
	if (!LookPixelDepth(cp.x(), cp.y()))
	{
		float rx, ry;
		if(m_Camera.m_ProjMode == gl::Camera::PROJ_PERS)
			proj.GetViewRange(rect_dst, rx, ry);
		else
			proj.GetViewRangeOrtho(rect_dst, rx, ry);

		rx *= 0.5f;
		ry *= 0.5f;

		float ncx = ((float)cp.x() / (float)vw - 0.5f);
		float ncy = ((float)cp.y() / (float)vh - 0.5f);

		float dx =  rx * ncx * 2.0f;
		float dy = -ry * ncy * 2.0f;

		manip.Track(dx, dy, 0.0);
	}

	static const int MIN_ZOOM_RANGE = 4;

	int iw = sel.GetWidth();
	int ih = sel.GetHeight();
	if (iw > MIN_ZOOM_RANGE && ih > MIN_ZOOM_RANGE)
	{
		float sx = (float)iw / (float)vw;
		float sy = (float)ih / (float)vh;

		lm::vec3f& v = manip.m_ViewPos;
		lm::vec3f& e = manip.m_EyePos;

		e = (e - v) * (std::max)(sx, sy) + v;
	}

	emit CameraMoved();
}


bool View3D::IsVisibleObject(const GeomObject& obj) const
{
	if (m_Config.m_ShowOnlySelect)
	{
		return (m_Scene->GetPrimaryObject() == &obj);
	}
	else
	{
		return obj.m_Visible;
	}
}

std::vector<GeomObject*> View3D::GetVisibleObjects(void)
{
	std::vector<GeomObject*> ov;
	ov.reserve(m_Scene->m_Objects.size());

	for (GeomObject& obj : m_Scene->m_Objects)
	{
		if (IsVisibleObject(obj))
			ov.push_back(&obj);
	}

	return ov;
}


float View3D::GetGeomDepth(int x, int y)
{
	PaintMain(true);

	return lib_gl::glaGetDepthf(x, y);
}

util::Optional<lm::vec3f> View3D::GetGeomDepht3DPos(int x, int y)
{
	int ry = GetParentWidget()->height() - y - 1;

	util::Optional<lm::vec3f> pos;
	float z = GetGeomDepth(x, ry);
	if (z != 1.0f)
	{
		pos = lib_gl::glaUnProjectExt((double)x, (double)ry, (double)z);
	}

	return pos;
}


void View3D::LookDepth(void)
{
	lib_gl::Viewport& vp = m_Camera.GetViewport();
	int x = (vp.GetRight() + vp.Left) / 2;
	int y = (vp.GetTop() + vp.Bottom) / 2;

	LookPixelDepth(x, y);

	emit CameraMoved();
	RepaintParent();
}

bool View3D::LookPixelDepth(int x, int y)
{
	BeginRender();

	util::Optional<lm::vec3f> c = GetGeomDepht3DPos(x, y);

	EndRender();

	if (!c.IsValid())
		return false;

	m_Camera.m_Manip.TranslateViewPosTo(c.Get());

	return true;
}

void View3D::Look3DCursor(void)
{
	lm::vec3f cp = m_Scene->m_Cursor3d.CursorPos;
	cp = m_Scene->m_WorldTransform.TransformVec(cp);

	m_Camera.m_Manip.TranslateViewPosTo(cp);

	emit CameraMoved();
	RepaintParent();
}

void View3D::ResetCursorMeasure(void)
{
	m_Scene->m_Cursor3d.ResetMeasure();
	UpdateCursorCutNormal();

	RepaintParent();
}

void View3D::UpdateCursorCutNormal(void)
{
	lm::vec3f f = m_Camera.m_Manip.GetFront();
	lm::vec3f o(0.0f, 0.0f, 0.0f);
	f = m_Scene->m_WorldTransform.InverseTransformVec(f);
	o = m_Scene->m_WorldTransform.InverseTransformVec(o);

	m_Scene->m_Cursor3d.UpdateNormal((f - o).get_normalize());
}


void View3D::MoveCaemraTo(ViewPoint vp)
{
	m_Camera.ResetViewPoint(vp);

	emit CameraMoved();
	RepaintParent();
}

void View3D::MoveLookPosToCenter(void)
{
	if(m_Scene->m_Objects.empty())
	{
		m_Camera.LookOrigin();
	}
	else
	{
		lm::vec3f c = m_Scene->GetSceneTransformedBBox().center();

		m_Camera.LookOrigin();
		m_Camera.m_Manip.Translate(c);
	}

	emit CameraMoved();
	RepaintParent();
}

void View3D::MoveLookPosToOrigin(void)
{
	m_Camera.LookOrigin();

	emit CameraMoved();
	RepaintParent();
}

void View3D::AdjustCameraDistAuto(void)
{
	lm::range3f r = m_Scene->GetSceneTransformedBBox();
	if(!r.is_valid())
		r.expand(lm::vec3f(0.0f, 0.0f, 0.0f));

	m_Camera.ResetViewKeepAngle( r );

	emit CameraMoved();
	RepaintParent();
}
