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

#include "MyGLWidget.h"

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

#include <C2/graph/MaterialSamples.h>

#include <C2/gl/OpenGLUT/OpenGlutStringExt.h>
#include <C2/gl/MaterialSetter.h>
#include <C2/gl/GlApiExt.h>
#include <C2/gl/GlGeometryFunctions.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 "CrossSectionRender.h"
#include "CameraMeasure.h"

#include "Shader/ConstantShader.h"

#include <GLUtility/BmpMarker.h>
#include <GLUtility/IndexColor.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_GridAxisScale = 1.0f;

	m_FpsMode = false;
}

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

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

	SetAmbientLightPower(1.0f);
	SetDfSpLightPower(1.0f);

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

// 標準の明るさ1.0として, 比例でシーンの明るさを設定する.
void View3D::SetDfSpLightPower(float rat)
{
	float rh = rat * 0.5f;
	m_ControlLight.SetSpDfPowerN(rh);
	m_HeadLight.SetSpDfPowerN(rh);
}

void View3D::SetAmbientLightPower(float rat)
{
	m_ControlLight.SetAmbientPowerN(rat);
	m_HeadLight.SetAmbientPowerN(0.0f);

	m_Scene->m_ConstantColorStrength = rat;
}

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

	m_ShaderContext.m_Scene = scene;
	m_ShaderContext.m_ViewConfig = &m_Config;
}

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

	m_RenderMap.clear();

	EndRender();

	QtViewBase::FinalizeView();
}


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_Scene->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_Scene->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);
	glSetEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, m_Config.m_EnableCoverageTrans);
	glSetEnable(GL_MULTISAMPLE, m_Config.m_EnableMultisample);

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

	if (m_Config.m_DrawCameraRecord)
		m_CameraRecord.DrawRecordPos(m_Scene->m_Camera);

	glPopAttrib();
}

void View3D::DrawOptions_After(void)
{
	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glSetEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, m_Config.m_EnableCoverageTrans);
	glSetEnable(GL_MULTISAMPLE, m_Config.m_EnableMultisample);

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

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

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

	if (m_Config.m_DrawLookPos)
		DrawLookPos();

	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);
	BmpMarker::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();

	Draw3DCursorBary();

	Draw3DCursorStrokes();

	if (m_Scene->m_CrossSectionConfig.IsFreeCut())
		Draw3DCursorFreecutGuide();

	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::Draw3DCursorStrokes(void)
{
	const Cursor3D& cursor = m_Scene->m_Cursor3d;

	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
	glDisable(GL_LIGHTING);
	if (cursor.TranspStorke)
		glDisable(GL_DEPTH_TEST);
	else
		glEnable(GL_DEPTH_TEST);
	glLineWidth(3.0f);

	glColor3d(0.8, 0.2, 0.1);
	for (const CursorStroke& s : cursor.Strokes)
	{
		if (s.IsEmpty())
			continue;

		bool IsLast = (&s == &cursor.Strokes.back());

		if (IsLast)
			glColor3d(1.0, 0.3, 0.1);

		glBegin(GL_LINE_STRIP);
		for (const lm::vec3f& p : s.GetPoints())
		{
			glVertex3fv(p.v());
		}
		glEnd();

		if (cursor.ShowStrokeLen)
		{
			const lm::vec3f& p = s.GetPoints().back();
			glutBitmapStringVal3f(p, s.GetLength());
		}
	}

	glPopAttrib();
}

void View3D::Draw3DCursorFreecutGuide(void)
{
	float len = 1.0f / m_Scene->m_WorldTransform.GetScale();
	len *= 0.25f;

	Cursor3D& cursor = m_Scene->m_Cursor3d;
	const lm::vec3f& p = cursor.CursorPos;
	const lm::quat4f& r = cursor.CutplaneRot;
	glColor3d(1, 0, 0);
	glDrawSegment(p, p + r.get_ex() * len);
	glColor3d(0, 1, 0);
	glDrawSegment(p, p + r.get_ey() * len);
	glColor3d(0, 0, 1);
	glDrawSegment(p, p + r.get_ez() * len);
}

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

	if (!cursor.CheckBaryCoord && !cursor.SelCloseMat)
		return;

	if (!cursor.CloseFace.IsValid())
		return;

	const MeshBuf* mbuf = cursor.CloseFaceMBuf;
	if (!mbuf->IsVisible())
		return;

	const lib_geo::BaseMesh& m = mbuf->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.Subface.fid];

	int vid0 = f.m_VertIds[0];
	int vid1 = f.m_VertIds[1 + bc.Subface.subface];
	int vid2 = f.m_VertIds[2 + bc.Subface.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);

	if (cursor.CheckBaryCoord)
	{
		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.Subface.fid << " " << bc.Subface.subface << " " << bc.Bary.x << " " << bc.Bary.y << " " << bc.Bary.z;
		glutBitmapString3f(pos, ss.str().c_str());
	}

	glPopAttrib();
}

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

	const Camera& camera = m_Scene->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)
		CameraMeasure(m_Scene, &m_Scene->m_Camera).Render();
}

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::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);
		lm::vec3f v0 = lm::vector3f::get_zero();
		lm::vec3f v1 = light.m_Position;
		lib_gl::glDrawSegment(v0, v1);
	}
	else
	{
		glColor3d(0.1, 0.5, 0.9);
		glEnable(GL_LINE_STIPPLE);
		glLineStipple(5, 0x5555);

		const lm::vec3f t(0.0, 1.0, 0.0);
		lm::vec3f v0 = light.m_Position;
		lm::vec3f v1 = v0 - lm::dot(t, v0) * t;
		lib_gl::glDrawSegment(v0, v1);
	}

	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_Scene->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)
	{
		glDisable(GL_LIGHTING);
		return;
	}

	m_HeadLight.SetGL();
	glEnable(m_HeadLight.GetLightIndex());

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

	GLfloat bl[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, bl);

	glEnable(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::BeginCutPlaneConfig(bool forCut)
{
	CrossSectionConfig& cs = m_Scene->m_CrossSectionConfig;
	const lm::range3f bbox = m_Scene->GetSceneBBox();

	if (cs.IsFreeCut())
	{
		lm::vec3f p, n;
		m_Scene->m_Cursor3d.GetFreeCutParam(p, n);
		cs.ApplyCutPlane(forCut, bbox, p, n);
	}
	else
	{
		cs.ApplyCutPlaneAxis(forCut, bbox);
	}
}

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

	if (m_Scene->m_CrossSectionConfig.m_Enable)
		BeginCutPlaneConfig(false);

	std::vector<MeshBuf*> meshes;
	GetAllVisibleMeshes(meshes);

	DrawAllGeomMeshStd(UseShader, meshes);

	DrawAllGeomVLinks(meshes);

	if (m_Config.m_DrawBone)
		DrawAllGeomBone();

	if (m_Config.m_DrawPolyline)
		DrawAllGeomPolyline(meshes);

	if (m_Config.m_HighlightSelected)
		DrawGeomMeshHighlight();

	if (m_Config.m_ShowCloseVertInfo)
		DrawCloseVertInfo();

	if (m_Config.m_ShowMeshBound)
		DrawGeomMeshBound();

	if (m_Scene->m_CrossSectionConfig.m_Enable)
		EndCutPlaneConfig();

	glPopMatrix();

	glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
	glPopAttrib();
}

void View3D::DrawAllGeomMeshStd(bool UseShader, std::vector<MeshBuf*>& meshes)
{
	glPushAttrib(GL_LIGHTING_BIT);

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

	if (m_Config.m_EnableFlatShade)
		glShadeModel(GL_FLAT);
	else
		glShadeModel(GL_SMOOTH);

	std::vector<GeometryRender*> Renders;
	Renders.resize(meshes.size());
	for (size_t i = 0; i < meshes.size(); ++i)
	{
		Renders[i] = &GetRender(meshes[i]);
	}

	for (GeometryRender* render : Renders)
	{
		render->DrawMeshMain(*m_Scene, shader);
	}

	for (GeometryRender* render : Renders)
	{
		render->DrawMeshExtAll();
	}

	for (GeometryRender* render : Renders)
	{
		render->DrawSelVertAll();
	}

	glPopAttrib();
}

void View3D::DrawAllGeomVLinks(std::vector<MeshBuf*>& meshes)
{
	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_CURRENT_BIT);
	glDisable(GL_LIGHTING);

	for (MeshBuf* mbuf : meshes)
	{
		DrawVertexLinker(*mbuf);
	}

	glPopAttrib();
}

void View3D::DrawVertexLinker(MeshBuf& mbuf)
{
	glColor3d(0, 1, 0.5);
	for (VertexLinker& linker : mbuf.m_VertLinkers)
	{
		glBegin(linker.m_IsLoop ? GL_LINE_LOOP : GL_LINE_STRIP);
		for (int vid : linker.m_VertLinks)
		{
			glVertex3fv(mbuf.m_Mesh.m_Verts[vid].v());
		}
		glEnd();
	}
}

void View3D::DrawAllGeomBone(void)
{
	int af = m_Scene->m_CurFrame - 1;

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

	if (m_Config.m_DrawFace)
		glDisable(GL_DEPTH_TEST);

	glLineWidth(3.0f);
	glPointSize(5.0f);

	for (GeomObject& g : m_Scene->m_Objects)
	{
		if (g.m_GTree.HasRoot())
		{
			SceneNode& n = g.m_GTree.GetRoot();
			DrawBone(&n, af, lm::matrix4f::get_identity());
		}
	}

	glPopAttrib();
}

void View3D::DrawBone(SceneNode* bn, int frame, const lm::matrix4f& m)
{
	lm::matrix4f fm = bn->GetFrameTransform(frame);
	lm::matrix4f nf = fm * m;

	if (bn->m_Bone != NULL)
	{
		lm::vec3f v0 = lm::vec3f(0, 0, 0) * m;
		lm::vec3f v1 = lm::vec3f(0, 0, 0) * nf;
		glColor3d(0.5, 0, 0.5);
		glBegin(GL_LINES);
		glVertex3fv(v0.v());
		glVertex3fv(v1.v());
		glEnd();
	}

	glPushMatrix();
	glMultMatrixf(nf.v());

	lm::vec3f eo = lm::vec3f(0,0,0);
	lm::vec3f ex = lm::vec3f(1,0,0) * 0.5f;
	lm::vec3f ey = lm::vec3f(0,1,0) * 0.5f;
	lm::vec3f ez = lm::vec3f(0,0,1) * 0.5f;

	glBegin(GL_LINES);
	glColor3d(1, 0, 0); glVertex3fv(eo.v()); glVertex3fv(ex.v());
	glColor3d(0, 1, 0); glVertex3fv(eo.v()); glVertex3fv(ey.v());
	glColor3d(0, 0, 1); glVertex3fv(eo.v()); glVertex3fv(ez.v());
	glEnd();

	glPopMatrix();

	for (SceneNode& cn : bn->m_Children)
	{
		DrawBone(&cn, frame, nf);
	}
}

void View3D::DrawAllGeomPolyline(std::vector<MeshBuf*>& meshes)
{
	for (MeshBuf* mbuf : meshes)
	{
		DrawPolyline(*mbuf);
	}
}

void View3D::DrawPolyline(MeshBuf& mbuf)
{
	lib_geo::BaseMesh& m = mbuf.m_Mesh;
	if (m.m_Polylines.empty())
		return;

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

	PolylineContext& pc = mbuf.m_PolylineContext;

	glColor3d(0.6, 0.4, 0.4);
	for (size_t i = 0; i < m.m_Polylines.size(); ++i)
	{
		const lib_geo::BasePolyline& pl = m.m_Polylines[i];
		DrawPolylineGeom(mbuf, pl);

		if (m_Config.m_DrawPolylineIdx)
		{
			if (!pl.m_PLVids.empty())
			{
				lm::vec3f v;

				size_t hl = pl.m_PLVids.size() / 2;
				if (pl.m_PLVids.size() >= 2)
				{
					int vid0 = pl.m_PLVids[hl - 1];
					int vid1 = pl.m_PLVids[hl];
					const lm::vec3f& v0 = mbuf.m_Mesh.m_Verts[vid0];
					const lm::vec3f& v1 = mbuf.m_Mesh.m_Verts[vid1];
					v = (v0 + v1) * 0.5f;
				}
				else
				{
					int vid = pl.m_PLVids[hl];
					v = mbuf.m_Mesh.m_Verts[vid];
				}
				glutBitmapStringVal3f(v, i);
			}
		}
	}

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

		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(MeshBuf& mbuf)
{
	GeomObject& obj = *mbuf.GetParent();
	const lib_geo::BaseMesh& m = mbuf.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);

	if (m_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::BasePolyline& pl : m.m_Polylines)
	{
		DrawPolylineGeom(mbuf, pl);
	}

	glPopAttrib();
}

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

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

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

		for (MeshBuf& mbuf : obj.m_MeshAry)
		{
			if (!mbuf.IsVisible())
				continue;

			unsigned int id = (unsigned int)(mbuf.GetDecWholeIdx() + index_offset);
			GeometryRender& render = GetRender(&mbuf);
			render.DrawFaceIndexColor(mbuf, *m_Scene, id);
		}
	}
}

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

	BeginRender();

	Camera& cam = m_Scene->m_Camera;

	m_FboPicker.InitializeOnce(cam.GetViewport().Width, cam.GetViewport().Height);
	m_FboPicker.RebuildIfResized(cam.GetViewport().Width, cam.GetViewport().Height);

	m_FboPicker.BeginPick();

	cam.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();

	if (m_Scene->m_CrossSectionConfig.m_Enable)
		BeginCutPlaneConfig(false);

	DrawAllGeomMeshIndexColor(index_offset);

	if (m_Scene->m_CrossSectionConfig.m_Enable)
		EndCutPlaneConfig();

	glPopMatrix();

	glPopAttrib();

	glFlush();

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

	m_FboPicker.EndPick();

	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)
{
	std::vector<MeshBuf*> meshes;
	m_Scene->GetSelectedMeshes(meshes);

	for (MeshBuf* mbuf : meshes)
	{
		GeometryRender& render = GetRender(mbuf);
		render.DrawGeomHighlight(*mbuf, *m_Scene);
	}

	if (m_Config.m_DrawPolyline)
	{
		for (MeshBuf* mbuf : meshes)
		{
			DrawPolylineHighlight(*mbuf);
		}
	}
}

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

	DrawAllGeomCrossSectionLine();

	glPopAttrib();

	glPopMatrix();
}

void View3D::GetAllVisibleMeshes(std::vector<MeshBuf*>& meshe)
{
	for (GeomObject& obj : m_Scene->m_Objects)
	{
		for (MeshBuf& mb : obj.m_MeshAry)
		{
			if (IsVisibleMBuf(mb))
				meshe.push_back(&mb);
		}
	}
}

bool View3D::IsVisibleMBuf(const MeshBuf& mbuf) const
{
	if (m_Config.m_ShowOnlySelect)
	{
		return m_Scene->m_Sels.IsInSelects(mbuf);
	}
	else
	{
		return mbuf.IsVisible();
	}
}

void View3D::DrawAllGeomCrossSectionLine(void)
{
	// TODO: インテルチップ対応.

	std::vector<MeshBuf*> mbs;
	GetAllVisibleMeshes(mbs);

	const CrossSectionConfig& cs_config = m_Scene->m_CrossSectionConfig;

	BeginCutPlaneConfig(true);

	for (MeshBuf* mbuf : mbs)
	{
		GeomObject* obj = mbuf->GetParent();
		if (cs_config.m_MultiColor)
			glColor4fv(IndexColor::GetColorRev(mbuf->m_WholeIndex).v());
		else
			glColor3d(1, 0, 0);

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

	EndCutPlaneConfig();

	CrossSectionRender cs(m_Scene);

	cs.DrawAllGeomCsConvexHull(mbs);

	bool show_len = cs_config.m_EnableShowLength;
	bool show_chlen = cs_config.m_EnableShowCHLength && cs_config.m_EnableConvexHull;
	if (show_len || show_chlen)
	{
		cs.DrawAllGeomCsLengthInfo(mbs);
	}
}

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 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.GetGeomBBox(false);
		lib_gl::glDrawRangeLine(bbox);

		if (m_Config.m_DrawBBoxRange)
		{
			std::ostringstream s;
			const lm::vec3f& t = bbox.max_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(MeshBuf& mbuf, int vert_idx)
{
	mbuf.CreateAdjBufOnce();

	lib_geo::BaseMesh& m = mbuf.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)
{
	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 = glaGetDepthui(x, y);

	m_Scene->m_Camera.SetViewportAndMatrix();

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

	ClearRenderBuffer();

	SetDefalutCullState();

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

	DrawSelection_VertSel();

	if (picker.EndPick())
		GetSelVertsFromPickBuf(picker, depth);

	glPopAttrib();

	EndRender();
}

void View3D::DrawSelection_VertSel(void)
{
	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);
		for (size_t j = 0; j < obj.m_MeshAry.size(); ++j)
		{
			glPushName((GLuint)j);
			MeshBuf& mbuf = obj.m_MeshAry[j];
			GetRender(&mbuf).DrawGeomVertPick(mbuf, *m_Scene);
			glPopName();
		}
		glPopName();
	}

	glPopMatrix();
}

void View3D::GetSelVertsFromPickBuf(lib_gl::GlPicker& picker, unsigned int depth)
{
	bool SelectTransparent = m_Config.m_PickTransparent;

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

		GLuint db = picked->GetMaxDepth();

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

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


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

	m_Scene->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_Scene->m_Camera.m_CameraReverse = CheckCameraIsReverse();
	}

	MouseMove_OnCursorControl(e, QPointF(0, 0));
}

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

void View3D::SelectClosestVertOnMouseDown(QMouseEvent *e)
{
	if (!m_CloseVertInfo.IsValidSelect())
		return;

	int obj_idx = m_CloseVertInfo.GetClosestObjectIdx();
	int mesh_idx = m_CloseVertInfo.GetClosestMid();
	int vert_idx = m_CloseVertInfo.GetClosestVid();
	GeomObject& obj = m_Scene->m_Objects[obj_idx];
	MeshBuf& mbuf = obj.m_MeshAry[mesh_idx];
	mbuf.SwapVertSelect(vert_idx);

	m_Widgets->UpdateAll();
}

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

	int id = vs.front();
	int sel_obj = MeshBuf::DecodeWholeIdx_Obj(id);
	int sel_mesh = MeshBuf::DecodeWholeIdx_Mesh(id);
	emit SelectedObjectChanged(sel_obj, sel_mesh);
}

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 , diff ) ) 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_Scene->m_Camera;
	lib_gl::CameraManipulator& manip = camera.m_Manip;
	lib_gl::CameraManipulator manip_pre = manip;

	float camera_dist = manip.GetDistanceToLookPos();

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

	float mx = diff.x() / m_Viewport.Width;
	float my = diff.y() / m_Viewport.Height;

	if (mouse.Mid || (mouse.Left && mouse.Right))
	{
		LockMouseMoveDir(m, mx, my);
		mx *= camera_dist;
		my *= camera_dist;
		mx = -mx;

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

			my *= camera_dist;

			manip.Dolly(my);
		}
		else
		{
			LockMouseMoveDir(m, mx, my);
			mx *= camera_dist;
			my *= camera_dist;
			mx = -mx;

			manip.Track(mx, 0.0f, my);
		}
	}
	else if (mouse.Left)
	{
		LockMouseMoveDir(m, mx, my);
		mx *= float(M_PI);
		my *= float(M_PI);
		mx = -mx;
		my = -my;

		if (!m_FpsMode)
		{
			if (m_Scene->m_Camera.m_CameraReverse)
				mx *= -1.0f;

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

	if (m_Config.m_SyncLightTocamera)
	{
		lm::vec3f px = manip_pre.GetBinormal();
		lm::vec3f py = manip_pre.GetUp();
		lm::vec3f pz = manip_pre.GetFront();
		lm::vec3f nx = manip.GetBinormal();
		lm::vec3f ny = manip.GetUp();
		lm::vec3f nz = manip.GetFront();

		lib_gl::Light& light = m_ControlLight;
		if (light.m_IsDirectional)
		{
			lm::vec3f ld = light.m_Position.get_normalize();
			float dst = light.m_Position.length();

			lm::vec3f lt = OrthoConvert::ToLocal(px, py, pz, ld);
			lm::vec3f lg = OrthoConvert::ToGlobal(nx, ny, nz, lt);

			light.m_Position = lg * dst;
		}
		else
		{
			lm::vec3f lt = OrthoConvert::ToLocal(px, py, pz, light.m_Position);
			light.m_Position = OrthoConvert::ToGlobal(nx, ny, nz, lt);
		}
	}

	emit CameraMoved();

	RepaintParent();
	return true;
}

void View3D::LockMouseMoveDir(Modifier& m, float& mx, float& my)
{
	if (m.Ctrl)
	{
		if (m.Alt)
			mx = 0;
		else
			my = 0;
	}
}

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

	Cursor3D& cursor = m_Scene->m_Cursor3d;

	if (!cursor.ShowCursor)
		return false;

	bool ControlCutAng = false;
	if (m_Scene->m_CrossSectionConfig.IsFreeCut())
	{
		if (m.Flag_AS())
		{
			if (mouse.IsRightOnly() || mouse.IsLeftOnly())
				ControlCutAng = true;
		}
	}

	if (!ControlCutAng)
	{
		if (!m.IsShiftOnly())
			return false;
	}

	bool ControlMeasure = false;

	if (!ControlCutAng)
	{
		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 (ControlCutAng)
	{
		const float speed = 5.0f;

		float nx = diff.x() / m_Viewport.Width;
		float ny = diff.y() / m_Viewport.Height;

		lib_gl::CameraManipulator& manip = m_Scene->m_Camera.m_Manip;

		const lm::vec3f& ax = manip.GetUp();
		const lm::vec3f& ay = manip.GetBinormal();
		const lm::vec3f& az = manip.GetFront();
		lm::quat4f& q = cursor.CutplaneRot;
		if (mouse.IsLeftOnly())
		{
			q.mult_rotate(ax, nx * speed);
			q.mult_rotate(ay, ny * speed);
		}
		else
		{
			q.mult_rotate(az, nx * speed);
		}

		q.normalize();
	}
	else 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();
		}

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

		cursor.MeasurePos += dst - cursor.CursorPos;
		cursor.CursorPos = dst;

		if (cursor.RecordStroke)
			cursor.PushStroke();

		emit CursorMoved();
	}

	cursor.ResetCloseFace();

	TestOnCursorColseElement();

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

	RepaintParent();
	return true;
}

void View3D::TestOnCursorColseElement(void)
{
	Cursor3D& cursor = m_Scene->m_Cursor3d;
	if (!cursor.CheckBaryCoord && !cursor.SelCloseMat)
		return;

	PickedMeshSubface picked;
	if (!PickFace3D(picked, cursor.CursorPos))
		return;

	cursor.CloseFaceMBuf = picked.mbuf;

	if (cursor.CheckBaryCoord)
		picked.mbuf->m_Mesh.CalcBaryCoord(picked.closePos, picked.subf.fid, picked.subf.subface, cursor.CloseFace);

	if (cursor.SelCloseMat)
	{
		if (!cursor.CheckBaryCoord)
			cursor.CloseFace.SetMidSubface(picked.subf);
	}

	int matIdx = -1;
	if (cursor.SelCloseMat)
	{
		const lib_geo::BaseFace& f = picked.mbuf->m_Mesh.m_Faces[picked.subf.fid];
		matIdx = f.m_MatIdx;
	}

	emit SelectedObjectChanged(picked.mbuf->GetParent()->GetObjectIndex(), picked.mbuf->m_MBufIdx);

	if (matIdx != -1)
		emit SelectedMatChanged(matIdx);
}

bool View3D::PickFace3D(PickedMeshSubface& picked, const lm::vec3f& pos)
{
	std::vector<MeshBuf*> meshes;
	GetAllVisibleMeshes(meshes);
	if (meshes.empty())
		return false;

	float close_disq = (std::numeric_limits<float>::max)();
	for (MeshBuf* mb : meshes)
	{
		lib_geo::SubfaceIdx fs;
		lm::vec3f p;
		if (!mb->m_Mesh.GetClosestSubfacePos(pos, fs, p))
			continue;

		float dsq = (pos - p).square_length();
		if (dsq < close_disq)
		{
			close_disq = dsq;
			picked.mbuf = mb;
			picked.subf = fs;
			picked.closePos = p;
		}
	}
	if (picked.mbuf == NULL)
		return false;

	return true;
}

bool View3D::PickFace2D(PickedMeshSubface& picked, int px, int py)
{
	util::Optional<lm::vec3f> depth_pos;
	depth_pos = GetGeomDepht3DPos(px, py);

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

	lm::vec3f p3d = m_Scene->m_WorldTransform.InverseTransformVec(depth_pos.Get());
	PickFace3D(picked, p3d);
	return true;
}

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

	BeginRender();

	m_Scene->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_Scene->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_Scene->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, 0.0f);

	emit CameraMoved();
	RepaintParent();
}

void View3D::OnGesturePinch(QPinchGesture *gesture)
{
	m_Scene->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_Scene->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.Flag_A())
	{
		int step = (e->delta() > 0) ? -1 : 1;
		emit SequenceStepped(step);
	}
	else 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;
		emit CameraMoved();
		RepaintParent();
	}
	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);

	UpdateModifierTip(e, m);

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

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

	UpdateModifierTip(e, m);
}

void View3D::UpdateModifierTip(QKeyEvent* e, const Modifier& m)
{
	//if (!IsModifierEvent(e))
	//	return;

	//if (m.Flag_C())
	//{
	//	emit StatusTipChanged(tr("L=rot camera horizontal"));
	//}
	//else if (m.Flag_SC())
	//{
	//	emit StatusTipChanged(tr("L=range select, R=range zoom"));
	//}
	//else if (m.Flag_S())
	//{
	//	emit StatusTipChanged(tr("L=move cursor, R=move measure"));
	//}
	//else if (m.Flag_A())
	//{
	//	emit StatusTipChanged(tr("L=rot light, R=translate light"));
	//}
	//else
	//{
	//	emit StatusTipChanged(tr("L=rot camera, M=translate camera, R=forward back camera"));
	//}
}

bool View3D::IsModifierEvent(QKeyEvent* e)
{
	switch (e->key())
	{
	case Qt::Key_Alt     :
	case Qt::Key_Shift   :
	case Qt::Key_Control :
		return true;
	default:
		return false;
	}
}

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

	e->accept();

	return true;
}


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

void View3D::ClearRenderMap(void)
{
	BeginRender();

	m_RenderMap.clear();

	EndRender();
}

GeometryRender& View3D::GetRender(MeshBuf* mbuf)
{
	boost::ptr_map<const MeshBuf*, GeometryRender>& rm = m_RenderMap;

	boost::ptr_map<const MeshBuf*, GeometryRender>::iterator found;
	found = rm.find(mbuf);
	if (found != rm.end())
		return *found->second;

	GeometryRender* new_render = new GeometryRender(mbuf);

	const MeshBuf* cm = const_cast<const MeshBuf*>(mbuf);
	rm.insert(cm, new_render);

	new_render->InitializeGeoRender(&m_Config, &m_ShaderLib, m_Scene);
	return *new_render;
}


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

	glColor3d(0,0,0);

	lib_gl::glDrawCenterCross(1.0f);
}


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


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

void View3D::MoveToRecordedCamera(int camera_idx, bool animation)
{
	lib_gl::CameraManipulator& manip = m_Scene->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)
{
	MeshBuf* mbuf = m_Scene->GetPrimaryMeshbuf();
	if (mbuf == NULL)
		return;

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

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

	w->makeCurrent();
	for (GeomObject& obj : m_Scene->m_Objects)
	{
		for (MeshBuf& mbuf : obj.m_MeshAry)
		{
			ReleaseRenderbuffer(&mbuf);
		}
	}
	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;

		for (MeshBuf& mbuf : obj.m_MeshAry)
		{
			if (!mbuf.IsVisible())
				continue;

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

void View3D::PushAndIdentityGLModelProjMatrix(void)
{
	PushBeginGLMat(GL_PROJECTION);
	PushBeginGLMat(GL_MODELVIEW);
}

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

void View3D::PushAndIdentityGLAllMatrix(void)
{
	PushAndIdentityGLModelProjMatrix();
	PushBeginGLMat(GL_TEXTURE);
}

void View3D::PopGLAllMatrix(void)
{
	PopEndGLMat(GL_TEXTURE);
	PopGLModelProjMatrix();
}

void View3D::PushBeginGLMat(GLenum mode)
{
	glMatrixMode(mode);
	glPushMatrix();
	glLoadIdentity();
}

void View3D::PopEndGLMat(GLenum mode)
{
	glMatrixMode(mode);
	glPopMatrix();
}

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)
{
	if (!m_CloseVertInfo.IsValidSelect())
		return;

	int obj_idx  = m_CloseVertInfo.GetClosestObjectIdx();
	int mesh_idx = m_CloseVertInfo.GetClosestMid();
	int vert_idx = m_CloseVertInfo.GetClosestVid();
	if (obj_idx >= m_Scene->m_Objects.size())
		return;

	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_Config.m_ShowVidTopMost)
		glDisable(GL_DEPTH_TEST);

	glColor3d(1, 0.8, 0.25);

	GeomObject& obj = m_Scene->m_Objects[obj_idx];
	DrawSelVert(obj.m_MeshAry[mesh_idx], vert_idx);

	glPopAttrib();
}

void View3D::DrawGeomMeshBound(void)
{
	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glLineWidth(3.0f);

	for (GeomObject& geo : m_Scene->m_Objects)
	{
		if (!geo.m_Visible)
			continue;
		for (MeshBuf& mb : geo.m_MeshAry)
		{
			if (!mb.m_Visible)
				continue;

			lib_geo::BaseMesh& m = mb.m_Mesh;
			if (!m.HasEdge())
			{
				m.CreateEdgeFromFace();
				m.CreateAdjBuffers();
			}

			glBegin(GL_LINES);
			glColor3d(0.2, 0.0, 0.0);
			for (lib_geo::BaseEdge& e : m.m_Edges)
			{
				if (!e.IsBound())
					continue;
				int vid0 = e.m_EdgeVids[0];
				int vid1 = e.m_EdgeVids[1];
				glVertex3fv(m.m_Verts[vid0].v());
				glVertex3fv(m.m_Verts[vid1].v());
			}
			glEnd();
		}
	}

	glPopAttrib();
}


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

	Camera& camera = m_Scene->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 (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;
	}
}


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_Scene->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_Scene->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_Scene->m_Camera.m_Manip.TranslateViewPosTo(cp);

	emit CameraMoved();
	RepaintParent();
}

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


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

	emit CameraMoved();
	RepaintParent();
}

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

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

	emit CameraMoved();
	RepaintParent();
}

void View3D::MoveLookPosToOrigin(void)
{
	m_Scene->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_Scene->m_Camera.ResetViewKeepAngle( r );

	emit CameraMoved();
	RepaintParent();
}

void View3D::ReleaseAllIndexList(void)
{
	BeginRender();

	for (auto r = m_RenderMap.begin(); r != m_RenderMap.end(); ++r)
	{
		GeometryRender* render = r->second;
		render->ReleaseIndexDispList();
	}

	EndRender();
}
