#include "StdAfx.h"
#include "SceneMain.h"

#include <LibGeo/Mesh/OBJ/ObjMesh.h>
#include <LibGeo/Mesh/STL/StlMesh.h>
#include <LibGeo/Mesh/PLY/PlyMesh.h>
#include <LibGeo/Mesh/MQO/MqoLoader.h>
#include <LibGeo/Path.h>

#include <LibGeo/Mesh/BaseMeshGen.h>

#include <C2/graph/MaterialSamples.h>
#include <C2/gl/GlApiExt.h>

#include <C2/util/string_util.h>

#include <QFileInfo>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

#include <LibQtGeoViewerCore/AssimpExt.h>

#include <sstream>
#include <map>

#include <meshio/pmd.h>

#include <boost/algorithm/string.hpp>



SceneMain::SceneMain(void)
{
	m_SelectedObjectIdx = -1;
	m_SelectedMaterialIdx = -1;
}

void SceneMain::Initialize(void)
{
	//m_DefaultMaterial = lib_graph::MaterialSamples::GetBronze();
	m_DefaultMaterial = lib_graph::MaterialSamples::GetSilver();

	m_DefaultAlphaMaterial = m_DefaultMaterial;
	m_DefaultAlphaMaterial.SetAlphaAll( 0.75f );

	m_DefaultTexture.InitializeTexture();

	ClearObjects();

	m_EnvImg.InitEnvMap();
}

void SceneMain::FinalizeScene(void)
{
	m_DefaultTexture.FinalizeTexture();

	m_Observers.clear();

	m_EnvImg.ClearEnv();
}

void SceneMain::ClearObjects(void)
{
	for (GeomObject& obj : m_Objects)
	{
		obj.ClearObject();
	}

	m_Objects.clear();
	m_Cursor3d.ResetCloseFace();

	ReportDoneEditGeometry();
}


geom::GeomFileFormat SceneMain::GetGeomFileTypeFromPath(const QString& path) const
{
	QString ext = QFileInfo(path).suffix().toLower();
	return GetGeomFileTypeByFileExt(ext);
}

bool SceneMain::IsGeomFile(const QString& path) const
{
	return (GetGeomFileTypeFromPath(path) != GeomFileFormat::None);
}

GeomFileFormat SceneMain::GetGeomFileTypeByFileExt(const QString& ext) const
{
	if(ext == "obj") return GeomFileFormat::Obj;
	if(ext == "stl") return GeomFileFormat::Stl;
	if(ext == "ply") return GeomFileFormat::Ply;
	if(ext == "mqo") return GeomFileFormat::Mqo;
	if(ext == "dae") return GeomFileFormat::Collada;
	if(ext == "pmd") return GeomFileFormat::Pmd;

	return GeomFileFormat::None;
}

void SceneMain::LoadFileAutoFmt(const QString& path)
{
	geom::GeomFileFormat fmt = GetGeomFileTypeFromPath(path);
	if (fmt == GeomFileFormat::None)
		throw FileLoadErrorException("Unknown format");

	if (!LoadFile(path.toLocal8Bit().data(), fmt))
		throw FileLoadErrorException("Failed Load Geometry");
}

bool SceneMain::LoadFile(const std::string& filename, geom::GeomFileFormat fmt)
{
	if (fmt == GeomFileFormat::Obj)
	{
		return LoadObj(filename);
	}
	else if (fmt == GeomFileFormat::Stl)
	{
		return LoadStl(filename);
	}
	else if (fmt == GeomFileFormat::Ply)
	{
		return LoadPly(filename);
	}
	else if (fmt == GeomFileFormat::Mqo)
	{
		return LoadMqo(filename);
	}
	else if (fmt == GeomFileFormat::Collada)
	{
		return LoadCollada(filename);
	}
	else if (fmt == GeomFileFormat::Pmd)
	{
		return LoadPmd(filename);
	}
	else
	{
		assert(false);
		return false;
	}
}

bool SceneMain::LoadObj(const std::string& filename)
{
	ObjSplitConfig split_config = m_ObjSplitMode;

	lib_geo::ObjMesh obj_mesh;
	if (!obj_mesh.Load(filename))
		return false;

	if (split_config == ObjSplit_Object)
	{
		std::vector<lib_geo::BaseMesh> mesh_ary;
		obj_mesh.ConvertToBaseMeshEachObject( mesh_ary );
		if(mesh_ary.empty())
			return true;

		AddObjMeshsToSceneEachObject(filename, obj_mesh, mesh_ary);
	}
	else if (split_config == ObjSplit_Group)
	{
		std::vector<lib_geo::BaseMesh> mesh_ary;
		obj_mesh.ConvertToBaseMeshEachGroup( mesh_ary );
		if(mesh_ary.empty())
			return true;

		AddObjMeshsToSceneEachGroup(filename, obj_mesh, mesh_ary);
	}
	else
	{
		GeomObject* geom = new GeomObject();
		AddObject(geom);

		obj_mesh.ConvertToBaseMesh( geom->m_Mesh );

		OnPostLoadObj(geom, obj_mesh);

		SetObjectNameByFilepath(*geom, filename);

		geom->m_FileFormat = GeomFileFormat::Obj;
		geom->m_FilePath = filename;
		AdditionalInitializeOfGeom(geom);
	}

	ReportDoneEditGeometry();

	return true;
}

void SceneMain::OnPostLoadObj(GeomObject* geom, const lib_geo::ObjMesh& obj_mesh)
{
	if (!geom->m_Mesh.HasNormal())
	{
		geom->m_Mesh.CreateNormalsEachVerts();
		geom->m_Mesh.UpdateNormal();
	}

	// テクスチャの設定
	if (obj_mesh.m_MaterialGroups.empty())
		return;

	// 複数 material group には未対応
	const lib_geo::ObjMaterialGroup& mat_grp = obj_mesh.m_MaterialGroups[0];

	geom->CreateTextureBuf(mat_grp.m_Materials.size());
	for (size_t i = 0; i < mat_grp.m_Materials.size() ; ++i)
	{
		const lib_geo::ObjMaterial& mat_source = mat_grp.m_Materials[i];
		const std::string& filepath = mat_source.GetTextureFilePath();
		const std::string name = QFileInfo(filepath.c_str()).fileName().toLocal8Bit().data();
		geom->InitColorTexture(i, filepath, name, m_TexConfig);
	}
}

void SceneMain::AddObjMeshsToSceneEachObject(const std::string& filename, lib_geo::ObjMesh& obj_mesh, std::vector<lib_geo::BaseMesh>& mesh_ary)
{
	for(size_t i = 0; i < mesh_ary.size(); ++i)
	{
		GeomObject* geom = new GeomObject();
		AddObject(geom);

		geom->m_Mesh = mesh_ary[i];

		OnPostLoadObj(geom, obj_mesh);

		const std::string& object_name = obj_mesh.m_Objects[i].m_Name;
		if(object_name.empty())
			SetObjectNameByFilepath(*geom, filename);
		else
			geom->m_Name = object_name;

		AdditionalInitializeOfGeom(geom);
	}
}

void SceneMain::AddObjMeshsToSceneEachGroup(const std::string& filename, lib_geo::ObjMesh& obj_mesh, std::vector<lib_geo::BaseMesh>& mesh_ary)
{
	for(size_t i = 0; i < mesh_ary.size(); ++i)
	{
		GeomObject* geom = new GeomObject();
		AddObject(geom);

		geom->m_Mesh = mesh_ary[i];

		OnPostLoadObj(geom, obj_mesh);

		const std::string& object_name = obj_mesh.m_Groups[i].m_Name;
		if(object_name.empty())
			SetObjectNameByFilepath(*geom, filename);
		else
			geom->m_Name = object_name;

		AdditionalInitializeOfGeom(geom);
	}
}

GeomObject* SceneMain::CreateNewGeometry(void)
{
	GeomObject* geom = new GeomObject();
	AddObject(geom);

	return geom;
}

bool SceneMain::LoadStl(const std::string& filename)
{
	GeomObject* geom = CreateNewGeometry();

	lib_geo::StlMesh stl_mesh;
	if( stl_mesh.Load( filename ) )
	{
		stl_mesh.ConvertToBaseMesh( geom->m_Mesh );
	}

	if( stl_mesh.ExistZeroLengthNormal() )
	{
		geom->m_Mesh.CreateNormalsEachVerts();
		geom->m_Mesh.UpdateNormal();
	}

	SetObjectNameByFilepath(*geom, filename);

	geom->m_FileFormat = GeomFileFormat::Stl;
	geom->m_FilePath = filename;
	AdditionalInitializeOfGeom(geom);

	ReportDoneEditGeometry();

	return true;
}

bool SceneMain::LoadPly(const std::string& filename)
{
	lib_geo::ply::PlyMesh ply_mesh;
	if( !ply_mesh.Load( filename ) )
		return false;

	GeomObject* geom = CreateNewGeometry();
	ply_mesh.ConvertToBaseMesh(geom->m_Mesh);

	geom->m_Mesh.CreateNormalsEachVerts();
	geom->m_Mesh.UpdateNormal();

	SetObjectNameByFilepath(*geom, filename);

	geom->m_FileFormat = GeomFileFormat::Ply;
	geom->m_FilePath = filename;
	AdditionalInitializeOfGeom(geom);

	ReportDoneEditGeometry();

	return true;
}

bool SceneMain::LoadMqo(const std::string& filename)
{
	GeomObject* geom = CreateNewGeometry();

	lib_geo::MqoObject mqo_obj;
	lib_geo::MqoLoader loader;
	loader.Load( mqo_obj , filename );

	mqo_obj.ConvertToBaseMesh(geom->m_Mesh);

	// テクスチャの設定
	geom->CreateTextureBuf(mqo_obj.m_Materials.size());
	for (size_t i = 0; i < mqo_obj.m_Materials.size(); ++i)
	{
		const lib_geo::MqoMaterial& mat_source = mqo_obj.m_Materials[i];
		const std::string& filepath = mat_source.GetTextureFilePath();
		const std::string& name = mat_source.m_Name;

		geom->InitColorTexture(i, filepath, name, m_TexConfig);
	}

	SetObjectNameByFilepath(*geom, filename);

	geom->m_FileFormat = GeomFileFormat::Mqo;
	geom->m_FilePath = filename;
	AdditionalInitializeOfGeom(geom);

	ReportDoneEditGeometry();

	return true;
}

void CreateBoneTree(std::map<std::string, Bone*>& bone_map, Bone* b, aiNode* n)
{
	ae::ConvertMat(b->m_NodeTrans, n->mTransformation);

	for(int i = 0; i < n->mNumChildren; ++i)
	{
		aiNode* nc = n->mChildren[i];
		Bone* bc = bone_map[nc->mName.C_Str()];
		b->m_Children.push_back(bc);
		CreateBoneTree(bone_map, bc, nc);
	}
}

bool SceneMain::LoadCollada(const std::string& filename)
{
	Assimp::Importer importer;
	const aiScene* scene = importer.ReadFile(filename, 0);

	if(!scene)
		return false;

	std::map<std::string, Bone*> bone_map;

	bool HasAnimation = (scene->mNumAnimations > 0);

	for(int i = 0; i < scene->mNumMeshes; ++i)
	{
		aiMesh* mesh = scene->mMeshes[i];
		GeomObject* geom = CreateNewGeometry();

		geom->m_Mesh.m_Verts.resize(mesh->mNumVertices);
		for(int j = 0; j < mesh->mNumVertices; ++j)
		{
			const aiVector3D& v = mesh->mVertices[j];
			geom->m_Mesh.m_Verts[j].set(v.x, v.y, v.z);
		}

		geom->m_Mesh.m_Faces.resize(mesh->mNumFaces);
		for(int j = 0; j < mesh->mNumFaces; ++j)
		{
			const aiFace& sf = mesh->mFaces[j];
			lib_geo::BaseFace& df = geom->m_Mesh.m_Faces[j];

			df.m_VertIds.resize(sf.mNumIndices);
			for(int k = 0; k < sf.mNumIndices; ++k)
			{
				df.m_VertIds[k] = sf.mIndices[k];
			}
		}

		geom->m_BoneAnimation.m_Bones.resize(mesh->mNumBones);
		for(int j = 0; j < mesh->mNumBones; ++j)
		{
			const aiBone* s_bone = mesh->mBones[j];
			Bone& d_bone = geom->m_BoneAnimation.m_Bones[j];
			d_bone.m_Name = s_bone->mName.C_Str();

			d_bone.m_Weights.resize(s_bone->mNumWeights);
			for(int k = 0; k < s_bone->mNumWeights; ++k)
			{
				d_bone.m_Weights[k].m_Vid = s_bone->mWeights[k].mVertexId;
				d_bone.m_Weights[k].m_Weight = s_bone->mWeights[k].mWeight;
			}

			ae::ConvertMat(d_bone.m_Offset, s_bone->mOffsetMatrix);

			bone_map[d_bone.m_Name] = &d_bone;
		}

		geom->m_Mesh.CreateNormalsEachVerts();
		geom->m_Mesh.UpdateNormal();

		geom->m_Name = mesh->mName.C_Str();
		if(geom->m_Name.empty())
		{
			std::ostringstream oss;
			oss << GetFileTitle(filename) << "-" << i;
			geom->m_Name = oss.str();
		}

		if(HasAnimation)
		{
			geom->m_BoneAnimation.m_SrcVertPos = geom->m_Mesh.m_Verts;
		}

		const aiNode* root = scene->mRootNode;
		for(int j = 0; j < root->mNumChildren; ++j)
		{
			aiNode* n = root->mChildren[j];
			for(int k = 0; k < n->mNumChildren; ++k)
			{
				aiNode* cn = n->mChildren[k];
				Bone* b_root = bone_map[cn->mName.C_Str()];

				BoneRoot rb;
				rb.m_Bone = b_root;
				ae::ConvertMat(rb.m_Transform, cn->mTransformation);
				geom->m_BoneAnimation.m_RootNodes.push_back(rb);

				CreateBoneTree(bone_map, b_root, n->mChildren[k]);
			}
		}

		AdditionalInitializeOfGeom(geom);
	}

	if(HasAnimation)
	{
		const aiAnimation* anim = scene->mAnimations[0];
		for(int j = 0; j < anim->mNumChannels; ++j)
		{
			aiNodeAnim* ch = anim->mChannels[j];
			Bone* bone = bone_map[ch->mNodeName.C_Str()];
			if(bone == NULL)
				continue;

			bone->m_Translate.resize(ch->mNumPositionKeys);
			for(int k = 0; k < ch->mNumPositionKeys; ++k)
			{
				ae::ConvertVec(bone->m_Translate[k], ch->mPositionKeys[k].mValue);
			}

			bone->m_Scale.resize(ch->mNumScalingKeys);
			for(int k = 0; k < ch->mNumScalingKeys; ++k)
			{
				ae::ConvertVec(bone->m_Scale[k], ch->mScalingKeys[k].mValue);
			}

			bone->m_Rotate.resize(ch->mNumRotationKeys);
			for(int k = 0; k < ch->mNumRotationKeys; ++k)
			{
				ae::ConvertQuat(bone->m_Rotate[k], ch->mRotationKeys[k].mValue);
			}
		}
	}

	ReportDoneEditGeometry();

	return true;
}

void AppendWeight(std::vector<float>& vw, std::vector<lm::vec3f>& vp, lm::matrix4f& mat_parent, GeomObject& g, Bone* b, int frame)
{
	lm::matrix4f md;
	if(frame == -1)
		md = b->m_NodeTrans;
	else
		md = b->GetFrameMatrix(frame);

	md = md * mat_parent;

	for(size_t j = 0; j < b->m_Weights.size(); ++j)
	{
		int vid = b->m_Weights[j].m_Vid;
		float weight = b->m_Weights[j].m_Weight;

		lm::vec3f v = g.m_BoneAnimation.m_SrcVertPos[vid];

		v = v * b->m_Offset * md;
		vw[vid] += weight;
		vp[vid] += v * weight;
	}

	for(size_t i = 0; i < b->m_Children.size(); ++i)
	{
		AppendWeight(vw, vp, md, g, b->m_Children[i], frame);
	}
}

void SceneMain::SetFrame(int frame)
{
	GeomObject& g = m_Objects[0];
	lib_geo::BaseMesh& m = g.m_Mesh;

	m.m_Verts = g.m_BoneAnimation.m_SrcVertPos;

	std::vector<float> vw(m.m_Verts.size(), 0.0f);
	std::vector<lm::vec3f> vp(m.m_Verts.size());

	for(size_t i = 0; i < g.m_BoneAnimation.m_RootNodes.size(); ++i)
	{
		BoneRoot& br = g.m_BoneAnimation.m_RootNodes[i];
		lm::matrix4f mi = br.m_Transform;
		AppendWeight(vw, vp, mi, g, br.m_Bone, frame);
	}

	for(size_t i = 0; i < m.m_Verts.size(); ++i)
	{
		if(vw[i] > 0.0f)
			m.m_Verts[i] = vp[i] / vw[i];
	}

	m.UpdateNormal();
}

int SceneMain::GetMaxFrame(void) const
{
	size_t max_frame = 0;
	for(size_t i = 0; i < m_Objects.size(); ++i)
	{
		const GeomObject& g = m_Objects[i];
		for(size_t j = 0; j < g.m_BoneAnimation.m_Bones.size(); ++j)
		{
			const Bone& b = g.m_BoneAnimation.m_Bones[j];
			max_frame = (std::max)(max_frame, b.m_Translate.size());
			max_frame = (std::max)(max_frame, b.m_Scale.size());
			max_frame = (std::max)(max_frame, b.m_Rotate.size());
		}
	}

	return max_frame;
}


bool SceneMain::LoadPmd(const std::string& filename)
{
	meshio::pmd::IO pm;
	if (!pm.read(filename.c_str()))
		return false;

	GeomObject* geom = CreateNewGeometry();

	lib_geo::BaseMesh& m = geom->m_Mesh;
	m.m_Verts.reserve(pm.vertices.size());
	m.m_Normals.reserve(pm.vertices.size());
	m.m_UVs.reserve(pm.vertices.size());
	for (const meshio::pmd::Vertex& pv : pm.vertices)
	{
		const meshio::Vector3& p = pv.pos;
		const meshio::Vector3& n = pv.normal;
		const meshio::Vector2& t = pv.uv;
		m.m_Verts.push_back(lm::vec3f(p.x, p.y, p.z));
		m.m_Normals.push_back(lm::vec3f(n.x, n.y, n.z));
		m.m_UVs.push_back(lm::vec2f(t.x, t.y));
	}

	size_t num_face = pm.indices.size() / 3;
	m.m_Faces.resize(num_face);
	for (size_t i = 0; i < num_face; ++i)
	{
		lib_geo::BaseFace& df = m.m_Faces[i];
		df.m_VertIds.resize(3);
		df.m_VertIds[0] = pm.indices[i * 3 + 0];
		df.m_VertIds[1] = pm.indices[i * 3 + 1];
		df.m_VertIds[2] = pm.indices[i * 3 + 2];
		df.m_NormIds = df.m_VertIds;
		df.m_UVIds = df.m_VertIds;
	}

	std::string dir = lib_geo::Path::GetParentDirPath(filename);
	m.m_Materials.resize(pm.materials.size());
	geom->CreateTextureBuf(pm.materials.size());
	for (size_t i = 0; i < pm.materials.size(); ++i)
	{
		const meshio::pmd::Material& sm = pm.materials[i];
		lib_geo::BaseMaterial& dm = m.m_Materials[i];
		dm.m_Ambient.set(sm.ambient.r, sm.ambient.g, sm.ambient.b, 1.0f);
		dm.m_Diffuse.set(sm.diffuse.r, sm.diffuse.g, sm.diffuse.b, sm.diffuse.a);
		dm.m_Specular.set(sm.specular.r, sm.specular.g, sm.specular.b, 1.0f);
		dm.m_Shininess = sm.shinness;

		std::string tex = sm.texture.str();
		std::vector<std::string> texlist;
		util::String::Split(tex, texlist, '*');

		for (const std::string& texfile : texlist)
		{
			std::string path = dir + texfile;
			std::string::size_type p = path.find_last_of('.');
			if (p == std::string::npos)
				continue;

			std::string ext = path.substr(p);
			boost::algorithm::to_lower(ext);
			if (ext != ".png" &&
				ext != ".bmp" &&
				ext != ".jpg" &&
				ext != ".jpeg" &&
				ext != ".tga")
				continue;

			geom->InitColorTexture(i, path, texfile, m_TexConfig);
			break;
		}

		std::ostringstream ss;
		ss << "mat_" << i;
		dm.m_Name = ss.str();
	}

	int vs_idx = 0;
	for (size_t i = 0; i < pm.materials.size(); ++i)
	{
		int vd = vs_idx + pm.materials[i].vertex_count;

		int vs = vs_idx;
		vs_idx = vd;

		vs /= 3;
		vd /= 3;

		for (int j = vs; j < vd; ++j)
		{
			m.m_Faces[j].m_MatIdx = (int)i;
		}
	}

	SetObjectNameByFilepath(*geom, filename);

	geom->m_FileFormat = GeomFileFormat::Pmd;
	geom->m_FilePath = filename;
	AdditionalInitializeOfGeom(geom);

	ReportDoneEditGeometry();

	return true;
}


std::string SceneMain::GetFileTitle(const std::string& filepath) const
{
	std::wstring name = util::String::ToUnicode(filepath);

	std::wstring::size_type ext_pos = std::wstring::npos;
	ext_pos = name.find_last_of(L"\\");
	if(ext_pos == std::wstring::npos)
		ext_pos = name.find_last_of(L"/");

	if(ext_pos != std::wstring::npos)
		name = name.substr(ext_pos + 1);

	return util::String::ToMultiByte(name);
}

//! ファイルパスからオブジェクト名を設定する
void SceneMain::SetObjectNameByFilepath(GeomObject& geom, const std::string& filepath) const
{
	geom.m_Name = GetFileTitle(filepath);
}


void SceneMain::AddSampleShape(void)
{
	GeomObject* geom = CreateNewGeometry();

	lib_geo::BaseMesh& mesh = geom->m_Mesh;

	lib_geo::BaseMeshGen::GenSphere( mesh , 1.0f , 20 , 20 );

	geom->m_Name = "sample_sphere";

	geom->CreateTextureBuf(1);

	AdditionalInitializeOfGeom(geom);
}

void SceneMain::AddGroundPlane(void)
{
	GeomObject* geom = CreateNewGeometry();

	lib_geo::BaseMesh& mesh = geom->m_Mesh;

	int slice = 32;
	float width = 10.0f;
	lib_geo::BaseMeshGen::GenPlaneZX( mesh , width , width , slice , slice );

	geom->m_Name = "ground";

	AdditionalInitializeOfGeom(geom);
}

void SceneMain::AdditionalInitializeOfGeom(GeomObject* geom)
{
	geom->InitializeBufferCommon();
	UpdateTransform();
}

void SceneMain::AddObject(GeomObject* obj)
{
	m_Objects.push_back(obj);
	RefreshObjectIndex();
}


void SceneMain::UpdateTransform(void)
{
	SceneTransform& t = m_WorldTransform;

	t.SetScale(1.0);
	t.m_Translate.set_zero();

	lm::range3f r = GetSceneBBox();
	if (!r.is_valid())
		return;

	if( m_Config.m_EnableAutoCentering )
	{
		t.m_Translate = -r.mid_point();
	}

	if( m_Config.m_EnableAutoReisze )
	{
		t.SetScale(1.0f / r.max_length());
	}
}


void SceneMain::LoadDefaultMatTexture(const std::string& filename)
{
	m_DefaultTexture.LoadTextureFromFile(filename.c_str(), true);
	m_DefaultTexture.SetTextureGLAndReleaseImage(m_TexConfig);
}


//! オブサーバをセット.
//! 所有権は持たない.
void SceneMain::AddObserver(SceneObserver* observer)
{
	m_Observers.push_back( observer );
}

bool SceneMain::RemoveObserver(size_t idx)
{
	assert( idx < m_Observers.size() );
	if( idx < m_Observers.size() )
		return false;

	std::vector<SceneObserver*>::iterator i = m_Observers.begin();
	std::advance( i , idx );
	m_Observers.erase( i );

	return true;
}

bool SceneMain::RemoveObserver(SceneObserver* registered_observer)
{
	for( size_t i = 0 ; i < m_Observers.size() ; ++i )
	{
		if( m_Observers[i] == registered_observer )
			return RemoveObserver( i );
	}

	assert(false);
	return false;
}


void SceneMain::ReportDoneEditGeometry(void)
{
	for( size_t i = 0 ; i < m_Observers.size() ; ++i )
	{
		m_Observers[i]->OnGeometryBuild(*this);
	}
}

lm::range3f SceneMain::GetSceneBBox(void) const
{
	lm::range3f bbox;
	bbox.clear();

	for (const GeomObject& obj : m_Objects)
	{
		bbox.expand(obj.GetBoundingBox());
	}

	return bbox;
}

lm::range3f SceneMain::GetSceneTransformedBBox(void) const
{
	lm::range3f bbox = GetSceneBBox();

	lm::range3f tbb;
	tbb.clear();
	tbb.expand(m_WorldTransform.TransformVec(bbox.min_point()));
	tbb.expand(m_WorldTransform.TransformVec(bbox.max_point()));

	return tbb;
}


GeomObject* SceneMain::GetPrimaryObject(void)
{
	if (m_SelectedObjectIdx < 0 || m_Objects.size() <= (int)m_SelectedObjectIdx)
		return NULL;

	return &m_Objects[m_SelectedObjectIdx];
}

const GeomObject* SceneMain::GetPrimaryObject(void) const
{
	if (m_SelectedObjectIdx < 0 || m_Objects.size() <= (int)m_SelectedObjectIdx)
		return NULL;

	return &m_Objects[m_SelectedObjectIdx];
}


void SceneMain::RemoveItem(int sel_idx)
{
	boost::ptr_vector<GeomObject>::iterator i;
	i = m_Objects.begin();
	i += sel_idx;

	if (&(*i) == m_Cursor3d.CloseFaceObject)
	{
		m_Cursor3d.ResetCloseFace();
	}

	m_Objects.erase(i);

	RefreshObjectIndex();
}


void SceneMain::CreateSampleTexture(SampleTextureBuilder::TextureType tex_type)
{
	SDL_Surface* tex = SampleTextureBuilder::CreateTexture(tex_type, 256);
	if(tex == NULL)
		return;

	m_DefaultTexture.SetFromSDLSurface( tex );
	m_DefaultTexture.SetTextureGLAndReleaseImage(m_TexConfig);
}


void SceneMain::RefreshObjectIndex(void)
{
	for (size_t i = 0; i < m_Objects.size(); ++i)
	{
		m_Objects[i].m_ObjectIndex = (int)i;
	}
}


bool SceneMain::ReloadObject(GeomObject* obj)
{
	if(!obj->IsFileObject())
		return false;

	int idx = FindObjectIdx(obj);
	if (idx == -1)
	{
		assert(false);
		return false;
	}

	geom::GeomFileFormat fmt = obj->m_FileFormat;
	const std::string filename = obj->m_FilePath;

	RemoveItem(idx);

	return LoadFile(filename, fmt);

	return true;
}

int SceneMain::FindObjectIdx(const GeomObject* obj) const
{
	for (const GeomObject& o : m_Objects)
	{
		if(&o == obj)
			return o.m_ObjectIndex;
	}

	return -1;
}


void SceneMain::ShowAllObjects(void)
{
	for (GeomObject& obj : m_Objects)
	{
		obj.m_Visible = false;
	}
}

void SceneMain::ClearAllVertSelect(void)
{
	for (GeomObject& obj : m_Objects)
	{
		obj.ClearSelect();
	}
}

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

	for (GeomObject& obj : m_Objects)
	{
		if (obj.m_Visible)
			ov.push_back(&obj);
	}

	return ov;
}


void SceneMain::AddCrossSectionRecord()
{
	UpdateCrossSectionIfRequire(true);

	for (GeomObject& obj : m_Objects)
	{
		obj.m_CrossSectionLog.push_back(lib_geo::CrossSection());
		std::swap(obj.m_CrossSectionLog.back(), obj.m_CrossSection);
	}
}

void SceneMain::ClearCrossSectionRecord()
{
	for (GeomObject& obj : m_Objects)
	{
		obj.m_CrossSectionLog.clear();
	}
}

void SceneMain::GetFreeCutParam(lm::vec3f& p, lm::vec3f& n)
{
	Cursor3D& c = m_Cursor3d;
	p = c.CursorPos;
	n = c.MeasureNorm;
}

void SceneMain::UpdateCrossSectionIfRequire(bool force_update)
{
	const CrossSectionConfig& conf = m_CrossSectionConfig;

	if (!force_update)
	{
		if (!conf.IsRequireUpdateCS())
			return;
	}

	lib_geo::Plane cutplane;

	if (conf.IsFreeCut())
		GetFreeCutParam(cutplane.origin, cutplane.normal);
	else
		conf.GetCutPlane(GetSceneBBox(), cutplane);

	bool cs_group = conf.m_EnableCrossSectionGroup;

	for (GeomObject& obj : m_Objects)
	{
		obj.UpdateCrossSection(cs_group, cutplane);
	}
}
