// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU General Public License(GPL).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

#include "CustomControls.hpp"

using namespace System;
using namespace System::Windows::Forms;
using namespace System::ComponentModel;

namespace UVTexIntegra {

	namespace CustomControls {

		TextLogForm^ VertexColorCustomBakeForm::Console::get() {
			MainEditForm^ owner = dynamic_cast<MainEditForm^>(Owner);
			if (owner == nullptr) return nullptr;
			return owner->Console;
		}

		VertexColorCustomBakeForm::VertexColorCustomBakeForm(
			System::Drawing::Image^ background_image, System::Drawing::Color background_color,
			System::String^ serializedAssemblyCollection, IConfig^ config)
			: CustomBakeForm_Base()
		{
			m_eventLock = DotNetEx::CounterKey::UsingKey();
			m_lock = gcnew System::Object();
			m_drawLock = gcnew System::Object();
		
			m_glShaderProgram = 0;

			pnlGL->GLBackgroundImage = background_image;
			pnlGL->GLBackColor = background_color;
			m_assemblies = gcnew Scripting::AssemblyCollection(config, serializedAssemblyCollection);
			m_config = config;
			m_pUVBuffer = new UVNormalTransformedBuffer();

			cmb->SelectedIndexChanged += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::cmb_SelectedIndexChanged);
			menuItemFile->DropDownOpened += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemFile_DropDownOpened);
			menuItemFile->DropDownClosed += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemFile_DropDownClosed);
			menuItemNewScript->Click += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemNewScript_Click);
			menuItemSaveImage->Click += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemSaveImage_Click);
			menuItemScriptDelete->Click += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemScriptDelete_Click);
			menuItemScriptFileOpen->Click += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemScriptFileOpen_Click);
			menuItemScriptRebuild->Click += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::menuItemScriptRebuild_Click);
			
			pnlGL->GLLayout += gcnew System::EventHandler(this, &VertexColorCustomBakeForm::pnlGL_GLLayout);
			pnlGL->GLPrePaintOnce += gcnew System::ComponentModel::CancelEventHandler(this, &VertexColorCustomBakeForm::pnlGL_GLPrePaintOnce);
			pnlGL->GLPaint += gcnew System::Windows::Forms::PaintEventHandler(this, &VertexColorCustomBakeForm::pnlGL_GLPaint);
			propGrid->PropertyValueChanged += gcnew System::Windows::Forms::PropertyValueChangedEventHandler(this, &VertexColorCustomBakeForm::prop_ValueChanged);

			cmb_Reload();
			cmb_SelectedIndexChanged(nullptr, nullptr);
		}

		VertexColorCustomBakeForm::!VertexColorCustomBakeForm() {
			if (m_assemblies != nullptr) { delete m_assemblies; m_assemblies = nullptr; }
			if (m_pUVBuffer != NULL) { delete m_pUVBuffer; m_pUVBuffer = NULL; }
		}

		System::String^ VertexColorCustomBakeForm::ToAssemblySerializedString() {
			if (m_assemblies == nullptr) return nullptr; return m_assemblies->ToSerializedString();
		}

		array<Scripting::LoadedAssembly::SerializeInformation^>^ VertexColorCustomBakeForm::ToSerializeInfoArray() {
			if (m_assemblies == nullptr) return nullptr; return m_assemblies->ToSerializeInfoArray();
		}

		void VertexColorCustomBakeForm::OnFormClosing(System::Windows::Forms::FormClosingEventArgs^ e)
		{
			MQCLI::Lock lock(m_drawLock);
			OpenTK::Graphics::OpenGL::ErrorCode err = OpenTK::Graphics::OpenGL::ErrorCode::NoError;
			if ((pnlGL->CanFocus) && (m_glShaderProgram != 0))
			{
				pnlGL->GLContext->MakeCurrent(pnlGL->GLWindowInfo);
				if ((err = GL::GetError()) == OpenTK::Graphics::OpenGL::ErrorCode::NoError)
				{
					GL::UseProgram( 0 );
					GL::DeleteProgram(m_glShaderProgram);
					m_glShaderProgram = 0;
				}
			}
		}

		//! @brief UVバッファをリビルドします。
		//! @details 現在選択中のScriptMainが利用されますが、選択状態がnullでも、
		//! 選択状態のオブジェクトについてScriptMainのフィルタ処理がスキップされるだけで、再計算は行われます。
		//! 処理の最後に再ペイントが行われます。
		void VertexColorCustomBakeForm::UVBufferRebuild()
		{
			{
				MQCLI::Lock lock( m_drawLock );
				// rebuild uv buffer
				Scripting::ScriptObject^ obj = dynamic_cast<Scripting::ScriptObject^>(m_assemblies->EditTarget(cmb->SelectedIndex));
				Scripting::ScriptMain^ scriptMain = nullptr; if (obj != nullptr) scriptMain = obj->ScriptMain;
				IDocumentFormat^ doc = nullptr; if (m_config != nullptr) doc = m_config->Document;
				m_pUVBuffer->Clear(doc, scriptMain);
				m_drawFaceBuffer = nullptr;
				if (doc != nullptr)
				{
					System::Collections::Generic::List<UINT>^ IDs = MQCLIEX::GetSelectedObjectIDs(doc->MQDocument, doc->IsRecursiveTrans);
					for each ( UINT id in IDs ) m_pUVBuffer->AddObject(doc->MQDocument->GetObjectFromUniqueID(id), doc->IsRecursiveTrans);
					m_drawFaceBuffer = m_pUVBuffer->GetUVOperationed(scriptMain);
				}
				if (pnlGL->GLPaintSuspend == true) pnlGL->GLPaintSuspend = false;
			}
			pnlGL->Invalidate();
		}

		//! @brief 現在のUVバッファをGraphicsに出力します。
		void VertexColorCustomBakeForm::UVBufferPaintToGraphics(System::Drawing::Graphics^ graphics, System::Drawing::Size size)
		{
			if ((graphics == nullptr) || (m_config == nullptr) || (m_drawFaceBuffer == nullptr) || (pnlGL->CanFocus == false)) return;

			const int SqSize = std::max( size.Width, size.Height );
			const int FboSqSize = MQCLIEX::TryExpLogCeil2(SqSize);

			unsigned int FboHandle = 0;
			unsigned int ColorTexture = 0;
			unsigned int DepthRenderbuffer = 0;
			OpenTK::Graphics::OpenGL::ErrorCode err = OpenTK::Graphics::OpenGL::ErrorCode::NoError;
			OpenTK::Graphics::OpenGL::FramebufferErrorCode err_fb = OpenTK::Graphics::OpenGL::FramebufferErrorCode::FramebufferCompleteExt;

			try
			{
				pnlGL->GLContext->MakeCurrent(pnlGL->GLWindowInfo);
				if ( (err = GL::GetError()) != OpenTK::Graphics::OpenGL::ErrorCode::NoError ) throw gcnew System::Exception("MakeCurrent");
			
				GL::UseProgram( m_glShaderProgram );
				if ( (err = GL::GetError()) != OpenTK::Graphics::OpenGL::ErrorCode::NoError ) throw gcnew System::Exception("UseProgram");

				// Create Color Texture
				GL::GenTextures( 1, ColorTexture );
				GL::BindTexture( TextureTarget::Texture2D, ColorTexture );
				GL::TexParameter( TextureTarget::Texture2D, TextureParameterName::TextureMinFilter, (int) TextureMinFilter::Nearest );
				GL::TexParameter( TextureTarget::Texture2D, TextureParameterName::TextureMagFilter, (int) TextureMagFilter::Nearest );
				GL::TexParameter( TextureTarget::Texture2D, TextureParameterName::TextureWrapS, (int) TextureWrapMode::Clamp );
				GL::TexParameter( TextureTarget::Texture2D, TextureParameterName::TextureWrapT, (int) TextureWrapMode::Clamp );
				GL::TexImage2D( TextureTarget::Texture2D, 0, PixelInternalFormat::Rgba8, FboSqSize, FboSqSize, 0, PixelFormat::Rgba, PixelType::UnsignedByte, IntPtr::Zero );
				if ( (err = GL::GetError()) != OpenTK::Graphics::OpenGL::ErrorCode::NoError ) throw gcnew System::Exception("CreateColorTexture/TexImage2D");
				GL::BindTexture( TextureTarget::Texture2D, 0 ); // prevent feedback, reading and writing to the same image is a bad idea
				if ( (err = GL::GetError()) != OpenTK::Graphics::OpenGL::ErrorCode::NoError ) throw gcnew System::Exception("CreateColorTexture/BindTexture0");
 
				// Create Depth Renderbuffer
				GL::Ext::GenRenderbuffers( 1, DepthRenderbuffer );
				GL::Ext::BindRenderbuffer( RenderbufferTarget::RenderbufferExt, DepthRenderbuffer );
				GL::Ext::RenderbufferStorage(RenderbufferTarget::RenderbufferExt, (RenderbufferStorage)All::DepthComponent16, FboSqSize, FboSqSize);
				if ( (err = GL::GetError()) != OpenTK::Graphics::OpenGL::ErrorCode::NoError ) throw gcnew System::Exception("CreateDepthBuffer/RenderbufferStorage");
 
				// test for GL Error here (might be unsupported format)
 
				// Create a FBO and attach the textures
				GL::Ext::GenFramebuffers( 1, FboHandle );
				GL::Ext::BindFramebuffer( FramebufferTarget::FramebufferExt, FboHandle );
				GL::Ext::FramebufferTexture2D( FramebufferTarget::FramebufferExt, FramebufferAttachment::ColorAttachment0Ext, TextureTarget::Texture2D, ColorTexture, 0 );
				GL::Ext::FramebufferRenderbuffer( FramebufferTarget::FramebufferExt, FramebufferAttachment::DepthAttachmentExt, RenderbufferTarget::RenderbufferExt, DepthRenderbuffer );
				err_fb = GL::Ext::CheckFramebufferStatus( FramebufferTarget::FramebufferExt );
				if ( err_fb != OpenTK::Graphics::OpenGL::FramebufferErrorCode::FramebufferCompleteExt ) throw gcnew System::Exception("CreateFBO/CheckFramebufferStatus");
 
				// since there's only 1 Color buffer attached this is not explicitly required
				GL::DrawBuffer( (DrawBufferMode)FramebufferAttachment::ColorAttachment0Ext );

				//GL::PushAttrib( AttribMask::ViewportBit ); // stores GL::Viewport() parameters
				GL::Viewport( 0, 0, SqSize, SqSize );
 
				// render whatever your heart desires, when done ::::::
			
				GL::ShadeModel(OpenTK::Graphics::OpenGL::ShadingModel::Smooth);
				GL::Enable(EnableCap::DepthTest);
				GL::DepthRange(-1.0, 1.0);
				GL::ClearDepth(1.0);
				GL::Disable(EnableCap::CullFace);
				GL::ClearColor(System::Drawing::Color::FromArgb(m_config->BakeTextureBaseColor));
				GL::Finish();
				GL::Clear(ClearBufferMask::ColorBufferBit | ClearBufferMask::DepthBufferBit);

				GL::DepthFunc(OpenTK::Graphics::OpenGL::DepthFunction::Always);
				for each (CustomControls::Drawing::Face^ face in m_drawFaceBuffer->Faces)
				{
					const int ctFacePointMax = face->Points->Length;
					if ( ctFacePointMax == 3 )
						GL::Begin(OpenTK::Graphics::OpenGL::BeginMode::Triangles);
					else if ( ctFacePointMax == 4 )
						GL::Begin(OpenTK::Graphics::OpenGL::BeginMode::TriangleFan);
					else continue;
					for ( int ct = 0; ct < ctFacePointMax; ct++ ) {
						GL::Color4(face->Points[ct].Color);
						GL::Vertex3(face->Points[ct].Coordinate.X, face->Points[ct].Coordinate.Y, -1.0f);
					}
					GL::End();
				}

				if ( float::Epsilon <= (m_config->BakeTextureBorder) )
				{
					GL::DepthFunc( OpenTK::Graphics::OpenGL::DepthFunction::Lequal );
					
					const int point_fan_count = 64;
					float point_fan[point_fan_count+1][3];
					System::Drawing::Color color;
					for each (CustomControls::Drawing::Edge^ edge in m_drawFaceBuffer->Edges)
					{
						if (edge->OwnerCount != 1) continue;

						const UVCoordinate center(edge->OwnerGravity.X, edge->OwnerGravity.Y);
						const UVCoordinate vCenterToFirst(edge->BeginPoint.X - center.u, edge->BeginPoint.Y - center.v);
						UVCoordinate vFirstToSecond(edge->EndPoint.X - edge->BeginPoint.X, edge->EndPoint.Y - edge->BeginPoint.Y);
						vFirstToSecond.Magnitude(m_config->BakeTextureBorder); // set length
						if (vFirstToSecond.SqrMagnitude() <= double::Epsilon) continue;

						// vCross is cross to edge and outside dircetion from inside triangle
						UVCoordinate vCross( vFirstToSecond.v, -vFirstToSecond.u );
						if ((vCross.u * vCenterToFirst.u + vCross.v * vCenterToFirst.v) < 0.0)
						{
							vCross.u = -vFirstToSecond.v;
							vCross.v = vFirstToSecond.u;
						}

						GL::Begin(OpenTK::Graphics::OpenGL::BeginMode::TriangleFan);
						GL::Color4(edge->BeginPointColor);
						GL::Vertex3(edge->BeginPoint.X, edge->BeginPoint.Y, -1.0f);
						GL::Vertex3(edge->BeginPoint.X + vCross.u, edge->BeginPoint.Y + vCross.v, 0.0);
						GL::Color4(edge->EndPointColor);
						GL::Vertex3(edge->EndPoint.X + vCross.u, edge->EndPoint.Y + vCross.v, 0.0);
						GL::Vertex3(edge->EndPoint.X, edge->EndPoint.Y, -1.0f);
						GL::End();

						for (int ctEdgePoint = 0; ctEdgePoint < 2; ctEdgePoint++)
						{
							if (ctEdgePoint == 0)
							{
								point_fan[0][0] = edge->BeginPoint.X; // x
								point_fan[0][1] = edge->BeginPoint.Y; // y
								color = edge->BeginPointColor;
							}
							else
							{
								point_fan[0][0] = edge->EndPoint.X; // x
								point_fan[0][1] = edge->EndPoint.Y; // y
								color = edge->EndPointColor;
							}
							point_fan[0][2] = -1.0f; // z
							for (int ct = 0; ct < (point_fan_count-1); ct++)
							{
								double angle = (double)(360 * ct) / (double)(point_fan_count-1);
								if ( 180.0 < angle ) angle -= 360.0f;
								const double rad_angle = MQ0x::Math::Deg2Rad(double(angle));
								const double cos = std::cos( rad_angle );
								const double sin = std::sin( rad_angle );
								point_fan[1+ct][0] = float(vCross.u * cos - vCross.v * sin + point_fan[0][0]);
								point_fan[1+ct][1] = float(vCross.u * sin + vCross.v * cos + point_fan[0][1]);
								point_fan[1+ct][2] = float(std::abs(angle) / 180.0);
							}
							point_fan[point_fan_count][0] = point_fan[1][0]; // x
							point_fan[point_fan_count][1] = point_fan[1][1]; // y
							point_fan[point_fan_count][2] = point_fan[1][2]; // z
							GL::Begin(OpenTK::Graphics::OpenGL::BeginMode::TriangleFan);
							GL::Color4(color);
							for (int ct = 0; ct < (point_fan_count + 1); ct++) GL::Vertex3(point_fan[ct]);
							GL::End();
						}
					}
				}
				GL::Finish();

				System::Drawing::Bitmap^ bmp = gcnew System::Drawing::Bitmap(SqSize, SqSize);
				System::Drawing::Imaging::BitmapData^ data = bmp->LockBits(System::Drawing::Rectangle(0,0,SqSize,SqSize),
					System::Drawing::Imaging::ImageLockMode::WriteOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
				GL::ReadPixels(0, 0, SqSize, SqSize, PixelFormat::Bgra, PixelType::UnsignedByte, data->Scan0);
				GL::Finish();
				bmp->UnlockBits(data);
				graphics->DrawImage(bmp, System::Drawing::Rectangle(0, 0, size.Width, size.Height));
				delete data;
				delete bmp;
			}
			catch ( System::Exception^ ex )
			{
				if ( err != OpenTK::Graphics::OpenGL::ErrorCode::NoError )
				{
					MessageBox::Show(ex->Message + ":" + err.ToString(), "OpenGL Error");
					this->Close();
				}
				else if ( err_fb != OpenTK::Graphics::OpenGL::FramebufferErrorCode::FramebufferCompleteExt )
				{
					MessageBox::Show(ex->Message + ":" + err_fb.ToString(), "OpenGL Error");
					this->Close();
				}
				else throw;
			}
			finally
			{
				GL::Ext::BindFramebuffer(FramebufferTarget::FramebufferExt, 0); // return to visible framebuffer
				if (FboHandle != 0) GL::Ext::DeleteFramebuffers(1, &FboHandle);
				if (ColorTexture != 0) GL::DeleteTextures(1, &ColorTexture);
				if (DepthRenderbuffer != 0) GL::Ext::DeleteRenderbuffers(1, &DepthRenderbuffer);
			}

		}

		System::Void VertexColorCustomBakeForm::cmb_Reload()
		{
			DotNetEx::CounterKey::Lock eventLock(m_eventLock);
			const int selectedIndex = cmb->SelectedIndex;
			cmb->Items->Clear();
			if ( m_assemblies != nullptr )
			{
				array<System::String^>^ arry = m_assemblies->DisplayNames;
				if ((arry != nullptr) && (0 < arry->Length))
				{
					cmb->Items->AddRange(arry);
					if ((0 < selectedIndex) && (selectedIndex < cmb->Items->Count)) cmb->SelectedIndex = selectedIndex;
					else cmb->SelectedIndex = 0;
					return;
				}
			}
			cmb->SelectedIndex = -1;
		}
	
		System::Void VertexColorCustomBakeForm::cmb_SelectedIndexChanged(Object^, System::EventArgs^)
		{
			if (0 < m_eventLock->KeyCount) return;
			const int selectedIndex = cmb->SelectedIndex;
			System::Object^ newObj = nullptr;
			if ((0 <= selectedIndex) && (m_assemblies != nullptr))
			{
				array<Scripting::IScriptObject^>^ targets = m_assemblies->EditTargets;
				if (selectedIndex < targets->Length) newObj = targets[selectedIndex];
			}
			if (System::Object::ReferenceEquals(propGrid->SelectedObject, newObj) == false)
			{
				propGrid->SelectedObject = newObj;
				UVBufferRebuild();
			}
		}

		System::Void VertexColorCustomBakeForm::menuItemFile_DropDownClosed(Object^, System::EventArgs^)
		{
			for each ( System::Windows::Forms::ToolStripItem^ item in menuItemFile->DropDownItems )
			{
				if ((item != nullptr) && (item->Enabled == false)) item->Enabled = true;
			}
		}
		
		System::Void VertexColorCustomBakeForm::menuItemFile_DropDownOpened(Object^, System::EventArgs^)
		{
			bool menuItemScriptOperateEnable = false;
			bool menuItemCompilable = false;
			
			Scripting::IScriptObject^ target = m_assemblies->EditTarget(cmb->SelectedIndex);
			menuItemScriptOperateEnable = (target != nullptr);
			menuItemCompilable = (dynamic_cast<Scripting::CompiledAssembly^>(target) != nullptr);

			if ( menuItemScriptRebuild->Enabled != menuItemCompilable ) menuItemScriptRebuild->Enabled = menuItemCompilable;
			if ( menuItemScriptFileOpen->Enabled != menuItemCompilable ) menuItemScriptFileOpen->Enabled = menuItemCompilable;
			if ( menuItemScriptDelete->Enabled != menuItemScriptOperateEnable ) menuItemScriptDelete->Enabled = menuItemScriptOperateEnable;
		}
		
		System::Void VertexColorCustomBakeForm::menuItemNewScript_Click(Object^, System::EventArgs^)
		{
			System::String^ registCurrentDir = System::Environment::CurrentDirectory;
			System::Windows::Forms::OpenFileDialog^ ofd = gcnew System::Windows::Forms::OpenFileDialog();
			System::String^ openFilePath = nullptr;
			int^ lastFilterIndex = nullptr;
			try
			{
				if (System::String::IsNullOrWhiteSpace(OpenFileDialogDirectory))
				{
					DotNetEx::IO::Path^ initialDir = System::Reflection::Assembly::GetExecutingAssembly()->Location;
					initialDir->MultiExtention = nullptr;
					if (initialDir->SystemDirectoryPathExists) ofd->InitialDirectory = initialDir->ToSystemDirectoryPath();
				}
				else ofd->InitialDirectory = OpenFileDialogDirectory;
				ofd->FilterIndex = OpenFileDialogScriptFilterIndex;
				ofd->Filter = "すべてのスクリプト形式 (*.cpp;*.c;*.cs;*.vb;*.js;*.dll)|*.cpp;*.c;*.cs;*.vb;*.js;*.dll|ソースコードファイル (*.cpp;*.c;*.cs;*.vb;*.js)|*.cpp;*.c;*.cs;*.vb;*.js|.Netアセンブリファイル (*.dll)|*.dll";
				ofd->Title = "ScriptMainの記述されたスクリプトプログラムを追加する";
				ofd->RestoreDirectory = true;
				if (ofd->ShowDialog() == System::Windows::Forms::DialogResult::OK)
				{
					openFilePath = ofd->FileName;
					lastFilterIndex = ofd->FilterIndex;
				}
				else return;
			}
			finally { delete ofd; ofd = nullptr; }

			if ( m_assemblies->Contains(openFilePath) )
			{
				MessageBox::Show("このファイルは既に登録されています。");
				return;
			}

			System::String^ dir = nullptr;
			try { dir = System::IO::Path::GetDirectoryName(openFilePath); }
			catch (System::Exception^) { dir = nullptr; }

			if ((System::IO::Directory::Exists(dir) == false) || (System::IO::File::Exists(openFilePath) == false))
			{
				MessageBox::Show(openFilePath, "入力されたパスが無効です。");
				return;
			}

			DotNetEx::IO::Path^ currentDir = System::Environment::CurrentDirectory;
			if (currentDir->Intersect(openFilePath)->HasRelation)
			{
				DotNetEx::IO::Path^ relativeOpenFilePath = currentDir->RelationTo(openFilePath);
				if (relativeOpenFilePath->RelativeFilePathExists) openFilePath = relativeOpenFilePath->ToRelativeFilePath();
			}

			Scripting::LoadedAssembly^ assembly = nullptr;
			try { assembly = m_assemblies->AddAssembly(openFilePath); }
			catch ( Scripting::Exception^ ex )
			{
				assembly = nullptr;
				MessageBox::Show(DotNetEx::String::Extention::ToNestedMultilineString(ex));
			}
			if ( assembly == nullptr )
			{
				System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
				sb->AppendLine("指定のファイルは開けませんでした。");
				sb->AppendLine(openFilePath);
				MessageBox::Show(sb->ToString());
				return;
			}

			if (System::String::IsNullOrWhiteSpace(dir) == false) OpenFileDialogDirectory = dir;
			if (lastFilterIndex != nullptr) OpenFileDialogScriptFilterIndex = *lastFilterIndex;

			cmb_Reload();
			cmb_SelectedIndexChanged(nullptr, nullptr);
		}

		System::Void VertexColorCustomBakeForm::menuItemSaveImage_Click(Object^, System::EventArgs^)
		{
			MQCLI::Lock lock(m_drawLock);

			if (m_config == nullptr) return;

			System::Windows::Forms::SaveFileDialog^ sfd = nullptr;
			System::Windows::Forms::DialogResult ret = System::Windows::Forms::DialogResult::None;
			System::Drawing::Bitmap^ bmp = nullptr;
			System::Drawing::Graphics^ g = nullptr;
			const int w = m_config->BakeTextureImageSize.Width, h = m_config->BakeTextureImageSize.Height;
			if (m_config->Document->MQDocument == NULL)
			{
				MessageBox::Show( "ドキュメントがありません。" );
				return;
			}
			
			if ((w <= 0) || (h <= 0))
			{
				MessageBox::Show( "画像サイズが小さすぎます。" );
				return;
			}

			try
			{
				char buf[MAX_PATH];
				System::String^ directoryPath = nullptr;
				try { directoryPath = System::IO::Path::GetDirectoryName(m_config->BakeTextureLastFilePath); }
				catch (System::Exception^) { directoryPath = nullptr; }
				if (System::IO::Directory::Exists(directoryPath) == false) directoryPath = nullptr;
				if (System::String::IsNullOrEmpty(directoryPath) && MQ_GetSystemPath(buf, MQFOLDER_ROOT)) directoryPath = gcnew System::String(buf);
				sfd = gcnew System::Windows::Forms::SaveFileDialog();
				sfd->SupportMultiDottedExtensions = true;
				sfd->InitialDirectory = directoryPath;
				sfd->Filter = "32bitFullColorPNG (*.png)|*.png";
				if ((ret = sfd->ShowDialog(this)) == System::Windows::Forms::DialogResult::OK) {
					bmp = gcnew System::Drawing::Bitmap(w, h, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
					g = System::Drawing::Graphics::FromImage(bmp);
					g->Clear(System::Drawing::Color::FromArgb(m_config->BakeTextureBaseColor));
					UVBufferPaintToGraphics(g, m_config->BakeTextureImageSize);
					delete g;
					g = nullptr;
					bmp->Save(sfd->FileName, System::Drawing::Imaging::ImageFormat::Png);
				}
			}
			catch (System::Exception^ ex) {
				ret = System::Windows::Forms::DialogResult::None;
				MessageBox::Show( ex->ToString(), "エラーが発生しました。" );
	#if _DEBUG
				throw;
	#endif
			}
			finally
			{
				if (g != nullptr) delete g;
				if (bmp != nullptr) delete bmp;
				if (ret == System::Windows::Forms::DialogResult::OK) m_config->BakeTextureLastFilePath = sfd->FileName;
				if (sfd != nullptr) delete sfd;
			}

		}

		System::Void VertexColorCustomBakeForm::menuItemScriptDelete_Click( Object^, System::EventArgs^ )
		{
			Scripting::IScriptObject^ obj = m_assemblies->EditTarget(cmb->SelectedIndex);
			Scripting::LoadedAssembly^ assembly = dynamic_cast<Scripting::LoadedAssembly^>(obj);
			if ( assembly == nullptr )
			{
				Scripting::ScriptObject^ scriptObject = dynamic_cast<Scripting::ScriptObject^>(obj);
				if ( scriptObject != nullptr ) assembly = scriptObject->Assembly;
			}
			if ( assembly != nullptr )
			{
				System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
				sb->AppendLine("読み込まれたスクリプトの情報を削除しますか？");
				sb->AppendLine("削除するスクリプト：" + assembly->DisplayName );
				if ( dynamic_cast<Scripting::CompiledAssembly^>(obj) == nullptr )
				{
					sb->AppendLine("【注意】");
					sb->AppendLine("DLLから読み込んでいる場合、メタセコイアが実行している間、DLLのリンク状態は継続します。");
					sb->AppendLine("このDLLを書き換える時は、メタセコイアを一旦終了してください。");
				}
				if ( MessageBox::Show(sb->ToString(), "スクリプトの削除", System::Windows::Forms::MessageBoxButtons::OKCancel ) == System::Windows::Forms::DialogResult::OK )
				{
					if ( m_assemblies->RemoveAssembly( assembly ) )
					{
						cmb_Reload();
						cmb_SelectedIndexChanged(nullptr, nullptr);
					}
				}
			}
		}

		System::Void VertexColorCustomBakeForm::menuItemScriptFileOpen_Click( Object^, System::EventArgs^ )
		{
			Scripting::IScriptObject^ obj = m_assemblies->EditTarget(cmb->SelectedIndex);
			Scripting::CompiledAssembly^ assembly = dynamic_cast<Scripting::CompiledAssembly^>(obj);
			System::String^ path = nullptr;
			try {
				if (assembly != nullptr) System::Diagnostics::Process::Start(path = assembly->CodePath);
			}
			catch ( System::Exception^ ) {
				MessageBox::Show(path, gcnew System::String(PLUGIN_STRING) + " - ファイルが開けませんでした。");
			}
		}

		System::Void VertexColorCustomBakeForm::menuItemScriptRebuild_Click( Object^, System::EventArgs^ )
		{
			Scripting::IScriptObject^ obj = m_assemblies->EditTarget(cmb->SelectedIndex);
			Scripting::CompiledAssembly^ assembly = dynamic_cast<Scripting::CompiledAssembly^>(obj);
			try {
				if ((assembly != nullptr) && (assembly->Rebuild()))
				{
					cmb_Reload();
					cmb_SelectedIndexChanged(nullptr, nullptr);
				}
			}
			catch ( Scripting::CompileException^ ex )
			{
				(gcnew CustomControls::CompileResultForm(this))->Show(ex);
				MessageBox::Show("コンパイルに失敗しました。");
			}
			catch ( Scripting::Exception^ ex )
			{
				MessageBox::Show(DotNetEx::String::Extention::ToNestedMultilineString(ex));
			}
		}

		System::Void VertexColorCustomBakeForm::pnlGL_GLLayout(System::Object^, System::EventArgs^)
		{
			if ((CanFocus) && (pnlGL->CanFocus) && (pnlGL->GLPaintSuspend == true)) pnlGL->GLPaintSuspend = false;
		}
		
		System::Void VertexColorCustomBakeForm::pnlGL_GLPaint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)
		{
			MQCLI::Lock lock(m_drawLock);
			UVBufferPaintToGraphics(e->Graphics, pnlGL->GLSize);
			if (pnlGL->GLPaintSuspend == false) pnlGL->GLPaintSuspend = true;
		}

		System::Void VertexColorCustomBakeForm::pnlGL_GLPrePaintOnce(Object^ sender, System::ComponentModel::CancelEventArgs^ e)
		{
			bool isGLFailed = false;

			OpenTK::GLControl^ glCtrl = dynamic_cast<OpenTK::GLControl^>(sender);
			Int32 glVertexShader = 0, glFragmentShader = 0;
			try {
				glCtrl->MakeCurrent();
				glVertexShader = GL::CreateShader(OpenTK::Graphics::OpenGL::ShaderType::VertexShader);
				glFragmentShader = GL::CreateShader(OpenTK::Graphics::OpenGL::ShaderType::FragmentShader);
				GL::ShaderSource(glVertexShader, "void main(void) { gl_Position=gl_Vertex; gl_FrontColor=gl_Color; }");
				GL::ShaderSource(glFragmentShader, "void main(void) { gl_FragColor = gl_Color; }");
				Int32 isCompiledVertexShader = 0;
				GL::CompileShader(glVertexShader);
				GL::GetShader(glVertexShader, OpenTK::Graphics::OpenGL::ShaderParameter::CompileStatus, isCompiledVertexShader);
				if (isCompiledVertexShader == 0)
				{
					System::Int32 length;
					System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
					GL::GetShaderInfoLog(glVertexShader, 4096, length, sb);
					MessageBox::Show(sb->ToString(),"VertexShader CompileError");
				}
				Int32 isCompiledFragmentShader;
				GL::CompileShader(glFragmentShader);
				GL::GetShader(glFragmentShader, OpenTK::Graphics::OpenGL::ShaderParameter::CompileStatus, isCompiledFragmentShader);
				if (isCompiledFragmentShader == 0)
				{
					System::Int32 length;
					System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
					GL::GetShaderInfoLog(glFragmentShader, 4096, length, sb);
					MessageBox::Show(sb->ToString(),"FragmentShader CompileError");
				}
				if ((isCompiledVertexShader!=0) && (isCompiledFragmentShader!=0))
				{
					m_glShaderProgram = GL::CreateProgram();
					GL::AttachShader(m_glShaderProgram, glVertexShader);
					GL::AttachShader(m_glShaderProgram, glFragmentShader);
					GL::LinkProgram(m_glShaderProgram);
					Int32 isLink = 0;
					GL::GetProgram(m_glShaderProgram, OpenTK::Graphics::OpenGL::ProgramParameter::LinkStatus, isLink);
					if (isLink == 0)
					{
						System::Int32 length;
						System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
						GL::GetShaderInfoLog(m_glShaderProgram, 4096, length, sb);
						GL::DeleteProgram(m_glShaderProgram);
						m_glShaderProgram = 0;
						MessageBox::Show(sb->ToString(), "LinkProgram Error");
					}
				}
			}
			catch (System::Exception^ ex)
			{
				MessageBox::Show(ex->ToString());
				e->Cancel = true;
				isGLFailed = true;
			}
			finally
			{
				if (glVertexShader != 0) GL::DeleteShader(glVertexShader);
				if (glFragmentShader != 0) GL::DeleteShader(glFragmentShader);
			}

			if (isGLFailed) { this->Close(); return; }

			UVBufferRebuild();
		}

		System::Void VertexColorCustomBakeForm::prop_ValueChanged(Object^, System::Windows::Forms::PropertyValueChangedEventArgs^) { UVBufferRebuild(); }
	
	}
}