//******************************************************************************
//
// OGL Utility / OGLPrimitive
//
// プリミティブ描画クラス
//
// Copyright (C) 2010-2022 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "YNBaseLib.h"
#import "OGLPrimitive.h"
#import "OGLShaders.h"


//******************************************************************************
// パラメータ定義
//******************************************************************************
//バッファアライメント
static const NSUInteger OGLBufferAlignment = 256;


//******************************************************************************
// コンストラクタ
//******************************************************************************
OGLPrimitive::OGLPrimitive(void)
{
	//Metalパイプライン関連
	m_pVertexDescriptor = nil;
	m_pRenderPipelineDescriptor = nil;
	m_MTLRenderPipelineState = nil;
	m_MTLDepthStencilState = nil;
	
	//Metalバッファ
	m_MTLUniformsBuffer = nil;
	m_MTLVertexBuffer = nil;
	m_MTLIndexBuffer = nil;
	
	//頂点情報
	m_VertexSize = 0;
	m_VertexFormat = 0;
	m_PrimitiveType = OGL_POINTS;
	m_PointSize = 1.0f;
	
	//頂点バッファ情報
	m_VertexNum = 0;
	m_IsVertexLocked = false;
	m_pVertexBuffer = NULL;
	m_VertexBufferLockedOffset = 0;
	m_VertexBufferLockedSize = 0;
	
	//インデックスバッファ情報
	m_IndexNum = 0;
	m_IsIndexLocked = false;
	m_pIndexBuffer = NULL;
	m_IndexBufferLockedOffset = 0;
	m_IndexBufferLockedSize = 0;
	
	//描画情報
	memset(&m_Material, 0, sizeof(OGLMATERIAL));
	m_isEnableDepthFunc = true;
	m_isEnableAlphaBlending = true;
	m_CullMode = OGL_CULL_NONE;
	
	return;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
OGLPrimitive::~OGLPrimitive(void)
{
	Release();
}

//******************************************************************************
// 解放
//******************************************************************************
void OGLPrimitive::Release()
{
	//Metalパイプライン関連
	if (m_pVertexDescriptor != nil) {
		[m_pVertexDescriptor release];
		m_pVertexDescriptor = nil;
	}
	if (m_pRenderPipelineDescriptor != nil) {
		[m_pRenderPipelineDescriptor release];
		m_pRenderPipelineDescriptor = nil;
	}
	if (m_MTLRenderPipelineState != nil) {
		[m_MTLRenderPipelineState release];
		m_MTLRenderPipelineState = nil;
	}
	if (m_MTLDepthStencilState != nil) {
		[m_MTLDepthStencilState release];
		m_MTLDepthStencilState = nil;
	}
	
	//Metalバッファ
	if (m_MTLUniformsBuffer != nil) {
		[m_MTLUniformsBuffer release];
		m_MTLUniformsBuffer = nil;
	}
	if (m_pVertexBuffer != NULL) {
		[m_MTLVertexBuffer release];
		m_MTLVertexBuffer = nil;
		free(m_pVertexBuffer);
		m_pVertexBuffer = NULL;
	}
	if (m_pIndexBuffer != NULL) {
		[m_MTLIndexBuffer release];
		m_MTLIndexBuffer = nil;
		free(m_pIndexBuffer);
		m_pIndexBuffer = NULL;
	}
	
	//頂点情報
	m_VertexSize = 0;
	m_VertexFormat = 0;
	m_PrimitiveType = OGL_POINTS;
	m_PointSize = 1.0f;
	
	//頂点バッファ情報
	m_VertexNum = 0;
	m_IsVertexLocked = false;
	m_VertexBufferLockedOffset = 0;
	m_VertexBufferLockedSize = 0;
	
	//インデックスバッファ情報
	m_IndexNum = 0;
	m_IsIndexLocked = false;
	m_IndexBufferLockedOffset = 0;
	m_IndexBufferLockedSize = 0;
	
	//描画情報
	memset(&m_Material, 0, sizeof(OGLMATERIAL));
	m_isEnableDepthFunc = true;
	m_isEnableAlphaBlending = true;
	m_CullMode = OGL_CULL_NONE;
	
	return;
}

//******************************************************************************
// 初期化
//******************************************************************************
int OGLPrimitive::Initialize(
		OGLDevice* pOGLDevice,		//描画デバイス
		OGLsizei vertexSize,		//頂点サイズ(byte)
		unsigned int vertexFormat,	//頂点フォーマット
		OGLenum primitiveType,		//プリミティブ種別
		bool isEnableDepthFunc,		//省略可：深度テスト
		bool isEnableAlphaBlending,	//省略可：アルファブレンディング
		unsigned int cullMode		//省略可：カリング
	)
{
	int result = 0;
	
	Release();
	
	//初期設定
	m_VertexSize = vertexSize;
	m_VertexFormat = vertexFormat;
	m_PrimitiveType = primitiveType;
	m_isEnableDepthFunc = isEnableDepthFunc;
	m_isEnableAlphaBlending = isEnableAlphaBlending;
	m_TransMatrix.Clear();
	_GetDefaultMaterial(&m_Material);
	
	//カリング
	m_CullMode = MTLCullModeNone;
	if (cullMode == OGL_CULL_FACE) {
		m_CullMode = MTLCullModeFront;
	}
	else if (cullMode == OGL_CULL_BACK) {
		m_CullMode = MTLCullModeBack;
	}
	
	//レンダーパイプライン状態生成
	result = _CreateRenderPipelineState(pOGLDevice);
	if (result != 0) goto EXIT;
	
	//深度ステンシル状態生成
	result = _CreateDepthStencilState(pOGLDevice);
	if (result != 0) goto EXIT;
	
	//制御情報バッファ生成
	result = _CreateUniformsBuffer(pOGLDevice);
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// レンダーパイプライン状態生成
//******************************************************************************
int OGLPrimitive::_CreateRenderPipelineState(OGLDevice* pOGLDevice)
{
	int result = 0;
	
	switch (m_VertexFormat) {
		case OGLVERTEX_TYPE_V3N3C:
			result = _CreateRenderPipelineState_V3N3C(pOGLDevice);
			if (result != 0) goto EXIT;
			break;
		case OGLVERTEX_TYPE_V3CT2:
			result = _CreateRenderPipelineState_V3CT2(pOGLDevice);
			if (result != 0) goto EXIT;
			break;
		case OGLVERTEX_TYPE_V3N3CT2:
			result = _CreateRenderPipelineState_V3N3CT2(pOGLDevice);
			if (result != 0) goto EXIT;
			break;
		default:
			result = YN_SET_ERR(@"Program error.", m_PrimitiveType, 0);
			goto EXIT;
			break;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// レンダーパイプライン状態生成：V3N3C
//******************************************************************************
int OGLPrimitive::_CreateRenderPipelineState_V3N3C(OGLDevice* pOGLDevice)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	NSError* pError = nil;
	
	mtlDevice = pOGLDevice->GetMetalDevice();
	
	//頂点記述子生成
	result = _CreateVertexDescriptor_V3N3C();
	if (result != 0) goto EXIT;
	
	//レンダーパイプライン記述子生成
	result = _CreateRenderPipelineDescriptor(
						pOGLDevice,						//描画デバイス
						@"OGLPrimitive_Pipeline_V3N3C",	//パイプラインラベル
						@"vertex_V3N3C",				//シェーダ頂点関数名
						@"fragment_V3N3C",				//シェーダフラグメント関数名
						m_pVertexDescriptor				//頂点記述子
					);
	if (result != 0) goto EXIT;
	
	//レンダーパイプライン状態生成
	m_MTLRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:m_pRenderPipelineDescriptor
																		 error:&pError];
	if (pError != nil) {
		NSLog(@"Failed to created pipeline state, error %@", pError.localizedDescription);
	 	result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// レンダーパイプライン状態生成：V3CT2
//******************************************************************************
int OGLPrimitive::_CreateRenderPipelineState_V3CT2(OGLDevice* pOGLDevice)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	NSError* pError = nil;
	
	mtlDevice = pOGLDevice->GetMetalDevice();
	
	//頂点記述子生成
	result = _CreateVertexDescriptor_V3CT2();
	if (result != 0) goto EXIT;
	
	//レンダーパイプライン記述子生成
	result = _CreateRenderPipelineDescriptor(
						pOGLDevice,						//描画デバイス
						@"OGLPrimitive_Pipeline_V3CT2",	//パイプラインラベル
						@"vertex_V3CT2",				//シェーダ頂点関数名
						@"fragment_V3CT2",				//シェーダフラグメント関数名
						m_pVertexDescriptor				//頂点記述子
					);
	if (result != 0) goto EXIT;
	
	//レンダーパイプライン状態生成
	m_MTLRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:m_pRenderPipelineDescriptor
																		 error:&pError];
	if (pError != nil) {
		NSLog(@"Failed to created pipeline state, error %@", pError.localizedDescription);
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// レンダーパイプライン状態生成：V3N3CT2
//******************************************************************************
int OGLPrimitive::_CreateRenderPipelineState_V3N3CT2(OGLDevice* pOGLDevice)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	NSError* pError = nil;
	
	mtlDevice = pOGLDevice->GetMetalDevice();
	
	//頂点記述子生成
	result = _CreateVertexDescriptor_V3N3CT2();
	if (result != 0) goto EXIT;
	
	//レンダーパイプライン記述子生成
	result = _CreateRenderPipelineDescriptor(
						pOGLDevice,							//描画デバイス
						@"OGLPrimitive_Pipeline_V3N3CT2",	//パイプラインラベル
						@"vertex_V3N3CT2",					//シェーダ頂点関数名
						@"fragment_V3N3CT2",				//シェーダフラグメント関数名
						m_pVertexDescriptor					//頂点記述子
					);
	if (result != 0) goto EXIT;
	
	//レンダーパイプライン状態生成
	m_MTLRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:m_pRenderPipelineDescriptor
																		 error:&pError];
	if (pError != nil) {
		NSLog(@"Failed to created pipeline state, error %@", pError.localizedDescription);
		result = YN_SET_ERR(@"Metal API error.", pError.code, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点記述子生成：V3N3C
//******************************************************************************
int OGLPrimitive::_CreateVertexDescriptor_V3N3C()
{
	int result = 0;
	MTLVertexDescriptor* pVertexDescriptor = nil;
	
	//頂点記述子生成
	pVertexDescriptor = [[MTLVertexDescriptor alloc] init];
	if (pVertexDescriptor == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
	//頂点記述子：属性：頂点属性 0：座標  [0]
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Position].format = MTLVertexFormatFloat3;
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Position].offset = 0;
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Position].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 1：法線  [1]
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Normal].format = MTLVertexFormatFloat3;
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Normal].offset = sizeof(OGLVECTOR3);
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Normal].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 2：色  [2]
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Color].format = MTLVertexFormatUInt;
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Color].offset = sizeof(OGLVECTOR3) * 2;
	pVertexDescriptor.attributes[VertexV3N3C_Attribute_Color].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：レイアウト  [0]
	pVertexDescriptor.layouts[BufferIndex_Vertices].stride = sizeof(OGLVERTEX_V3N3C);
	pVertexDescriptor.layouts[BufferIndex_Vertices].stepRate = 1;
	pVertexDescriptor.layouts[BufferIndex_Vertices].stepFunction = MTLVertexStepFunctionPerVertex;
	
	m_pVertexDescriptor = pVertexDescriptor;
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点記述子生成：V3CT2
//******************************************************************************
int OGLPrimitive::_CreateVertexDescriptor_V3CT2()
{
	int result = 0;
	MTLVertexDescriptor* pVertexDescriptor = nil;
	
	//頂点記述子生成
	pVertexDescriptor = [[MTLVertexDescriptor alloc] init];
	if (pVertexDescriptor == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
	//頂点記述子：属性：頂点属性 0：座標  [0]
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_Position].format = MTLVertexFormatFloat3;
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_Position].offset = 0;
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_Position].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 1：色  [1]
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_Color].format = MTLVertexFormatUInt;
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_Color].offset = sizeof(OGLVECTOR3);
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_Color].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 2：UV座標  [2]
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_TextureCoords].format = MTLVertexFormatFloat2;
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_TextureCoords].offset = sizeof(OGLVECTOR3) + sizeof(OGLuint);
	pVertexDescriptor.attributes[VertexV3CT2_Attribute_TextureCoords].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：レイアウト  [0]
	pVertexDescriptor.layouts[BufferIndex_Vertices].stride = sizeof(OGLVERTEX_V3CT2);
	pVertexDescriptor.layouts[BufferIndex_Vertices].stepRate = 1;
	pVertexDescriptor.layouts[BufferIndex_Vertices].stepFunction = MTLVertexStepFunctionPerVertex;

	m_pVertexDescriptor = pVertexDescriptor;
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点記述子生成：V3N3CT2
//******************************************************************************
int OGLPrimitive::_CreateVertexDescriptor_V3N3CT2()
{
	int result = 0;
	MTLVertexDescriptor* pVertexDescriptor = nil;
	
	//頂点記述子生成
	pVertexDescriptor = [[MTLVertexDescriptor alloc] init];
	if (pVertexDescriptor == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
	//頂点記述子：属性：頂点属性 0：座標  [0]
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Position].format = MTLVertexFormatFloat3;
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Position].offset = 0;
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Position].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 1：法線  [1]
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Normal].format = MTLVertexFormatFloat3;
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Normal].offset = sizeof(OGLVECTOR3);
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Normal].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 2：色  [2]
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Color].format = MTLVertexFormatUInt;
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Color].offset = sizeof(OGLVECTOR3) * 2;
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_Color].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：属性：頂点属性 3：UV座標  [3]
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_TextureCoords].format = MTLVertexFormatFloat2;
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_TextureCoords].offset = sizeof(OGLVECTOR3) * 2 + sizeof(OGLuint);
	pVertexDescriptor.attributes[VertexV3N3CT2_Attribute_TextureCoords].bufferIndex = BufferIndex_Vertices;
	//頂点記述子：レイアウト  [0]
	pVertexDescriptor.layouts[BufferIndex_Vertices].stride = sizeof(OGLVERTEX_V3N3CT2);
	pVertexDescriptor.layouts[BufferIndex_Vertices].stepRate = 1;
	pVertexDescriptor.layouts[BufferIndex_Vertices].stepFunction = MTLVertexStepFunctionPerVertex;
	
	m_pVertexDescriptor = pVertexDescriptor;
	
EXIT:;
	return result;
}

//******************************************************************************
// レンダーパイプライン記述子生成
//******************************************************************************
int OGLPrimitive::_CreateRenderPipelineDescriptor(
		OGLDevice* pOGLDevice,
		NSString* pPiplineLabel,
		NSString* pVertexFunctionName,
		NSString* pFragmentFunctionName,
		MTLVertexDescriptor* pVertexDescriptor
	)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	id <MTLLibrary> defaultLibrary = nil;
	id <MTLFunction> vertexFunction = nil;
	id <MTLFunction> fragmentFunction = nil;
	MTLRenderPipelineDescriptor* pPipelineStateDescriptor = nil;
	
	mtlDevice = pOGLDevice->GetMetalDevice();
	
	//シェーダ関数取得
	defaultLibrary = [mtlDevice newDefaultLibrary];
	vertexFunction = [defaultLibrary newFunctionWithName:pVertexFunctionName];
	if (vertexFunction == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	fragmentFunction = [defaultLibrary newFunctionWithName:pFragmentFunctionName];
	if (fragmentFunction == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
	//レンダーパイプライン記述子生成
	pPipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
	if (pPipelineStateDescriptor == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	pPipelineStateDescriptor.label = pPiplineLabel;
	//シェーダ関数
	pPipelineStateDescriptor.vertexFunction = vertexFunction;
	pPipelineStateDescriptor.fragmentFunction = fragmentFunction;
	//頂点記述子
	pPipelineStateDescriptor.vertexDescriptor = pVertexDescriptor;
	//ピクセルフォーマット
	pPipelineStateDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
	//アルファブレンド
	pPipelineStateDescriptor.colorAttachments[0].blendingEnabled = m_isEnableAlphaBlending;
	pPipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
	pPipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
	pPipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
	pPipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
	pPipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
	pPipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
	//深度テスト
	pPipelineStateDescriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
	//ステンシル
	pPipelineStateDescriptor.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
	//アンチエイリアス サンプル数
	pPipelineStateDescriptor.sampleCount = pOGLDevice->GetSampleCount();
	
	m_pRenderPipelineDescriptor = pPipelineStateDescriptor;
	
EXIT:;
	return result;
}

//******************************************************************************
// 深度ステンシル状態生成
//******************************************************************************
int OGLPrimitive::_CreateDepthStencilState(OGLDevice* pOGLDevice)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	MTLDepthStencilDescriptor* pDepthStencilDescriptor = nil;
	
	//深度ステンシル記述子生成
	pDepthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
	if (pDepthStencilDescriptor == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	pDepthStencilDescriptor.depthCompareFunction = MTLCompareFunctionLessEqual;	//深度比較方法：同じまたは小さい
	pDepthStencilDescriptor.depthWriteEnabled = m_isEnableDepthFunc; //深度テスト有効/無効
	
	//深度ステンシル状態生成
	mtlDevice = pOGLDevice->GetMetalDevice();
	m_MTLDepthStencilState = [mtlDevice newDepthStencilStateWithDescriptor:pDepthStencilDescriptor];
	if (m_MTLDepthStencilState == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// 制御情報バッファ生成
//******************************************************************************
int OGLPrimitive::_CreateUniformsBuffer(OGLDevice* pOGLDevice)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	
	//バッファ生成
	mtlDevice = pOGLDevice->GetMetalDevice();
	m_MTLUniformsBuffer = [mtlDevice newBufferWithLength:_AlignUp(sizeof(OGLUniforms), OGLBufferAlignment)
												 options:MTLResourceStorageModeShared];
	if (m_MTLUniformsBuffer == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	m_MTLUniformsBuffer.label = @"OGLPrimitive_UniformsBuffer";
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点バッファ生成
//******************************************************************************
int OGLPrimitive::CreateVertexBuffer(
		OGLDevice* pOGLDevice,
		OGLsizei vertexNum
	)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	
	if (m_pVertexBuffer != NULL) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//頂点バッファ生成
	mtlDevice = pOGLDevice->GetMetalDevice();
	m_MTLVertexBuffer = [mtlDevice newBufferWithLength:_AlignUp(m_VertexSize * vertexNum, OGLBufferAlignment)
											   options:MTLResourceStorageModeShared];
	if (m_MTLVertexBuffer == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	m_MTLVertexBuffer.label = @"OGLPrimitive_VertexBuffer";
	
	//リソース配置場所としてメモリを確保
	m_pVertexBuffer = malloc(_AlignUp(m_VertexSize * vertexNum, OGLBufferAlignment));
	if (m_pVertexBuffer == NULL) {
		result = YN_SET_ERR(@"Could not allocate memory.", m_VertexSize, vertexNum);
		[m_MTLVertexBuffer release];
		m_MTLVertexBuffer = nil;
		goto EXIT;
	}
	
	m_VertexNum = vertexNum;

EXIT:;
	return result;
}

//******************************************************************************
// インデックスバッファ生成
//******************************************************************************
int OGLPrimitive::CreateIndexBuffer(
		OGLDevice* pOGLDevice,
		OGLsizei indexNum
	)
{
	int result = 0;
	id <MTLDevice> mtlDevice = nil;
	
	if (m_pIndexBuffer != NULL) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//インデックスバッファ生成
	mtlDevice = pOGLDevice->GetMetalDevice();
	m_MTLIndexBuffer = [mtlDevice newBufferWithLength:_AlignUp(sizeof(OGLuint) * indexNum, OGLBufferAlignment)
											  options:MTLResourceStorageModeShared];
	if (m_MTLIndexBuffer == nil) {
		result = YN_SET_ERR(@"Metal API error.", 0, 0);
		goto EXIT;
	}
	m_MTLIndexBuffer.label = @"OGLPrimitive_IndexBuffer";
	
	//リソース配置場所としてメモリを確保
	m_pIndexBuffer = malloc(_AlignUp(sizeof(OGLuint) * indexNum, OGLBufferAlignment));
	if (m_pIndexBuffer == NULL) {
		result = YN_SET_ERR(@"Could not allocate memory.", sizeof(OGLuint), indexNum);
		[m_MTLIndexBuffer release];
		m_MTLIndexBuffer = nil;
		goto EXIT;
	}
	
	m_IndexNum = indexNum;
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点登録
//******************************************************************************
int OGLPrimitive::SetAllVertex(
		OGLDevice* pOGLDevice,
		void* pVertex
	)
{
	int result = 0;
	void* pBuf = NULL;
	
	//頂点バッファのロック
	result = LockVertex(&pBuf);
	if (result != 0) goto EXIT;
	
	//バッファに頂点データを書き込む
	try {
		memcpy(pBuf, pVertex, (m_VertexSize * m_VertexNum));
	}
	catch (...) {
		result = YN_SET_ERR(@"Memory access error.", (unsigned long)pVertex, m_VertexNum);
		goto EXIT;
	}
	
	//頂点バッファのアンロック
	//  実際はこのタイミングでGPUのメモリに転送する
	result = UnlockVertex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// インデックス登録
//******************************************************************************
int OGLPrimitive::SetAllIndex(
		OGLDevice* pOGLDevice,
		unsigned int* pIndex
	)
{
	int result = 0;
	unsigned int* pBuf = NULL;
	
	//頂点バッファのロック
	result = LockIndex(&pBuf);
	if (result != 0) goto EXIT;
	
	//バッファに頂点データを書き込む
	try {
		memcpy(pBuf, pIndex, (sizeof(OGLuint)* m_IndexNum));
	}
	catch (...) {
		result = YN_SET_ERR(@"Memory access error.", (unsigned long)pIndex, m_IndexNum);
		goto EXIT;
	}
	
	//インデックスバッファのアンロック
	//  実際はこのタイミングでGPUのメモリに転送する
	result = UnlockIndex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// マテリアル登録
//******************************************************************************
void OGLPrimitive::SetMaterial(
		OGLMATERIAL material
	)
{
	m_Material = material;
}

//******************************************************************************
// ポイントサイズ登録
//******************************************************************************
void OGLPrimitive::SetPointSize(
		float pointSize
	)
{
	m_PointSize = pointSize;
}

//******************************************************************************
// 移動
//******************************************************************************
void OGLPrimitive::Transform(
		OGLTransMatrix* pTransMatrix
	)
{
	m_TransMatrix.CopyFrom(pTransMatrix);
}

//******************************************************************************
// 描画
//******************************************************************************
int OGLPrimitive::Draw(
		OGLDevice* pOGLDevice,
		OGLTexture* pTexture,
		OGLsizei drawIndexNum
	)
{
	int result = 0;
	id <MTLRenderCommandEncoder> commandEncoder = nil;
	MTLPrimitiveType primitiveType;
	OGLUniforms uniforms;
	OGLsizei indexNum = 0;
	OGLsizei indexNumMax = 0;
	
	if (m_IsVertexLocked || m_IsIndexLocked) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//頂点が存在しなければ何もしない
	if (m_pVertexBuffer == NULL) goto EXIT;
	
	//マテリアル設定
	pOGLDevice->SetMaterial(_GetMaterialForShader());
	
	//ポイントサイズ設定
	pOGLDevice->SetPointSize(m_PointSize);
	
	//モデル行列
	pOGLDevice->SetModelMatrix(m_TransMatrix.GetModelMatrix());
	
	//制御情報取得
	uniforms = pOGLDevice->GetUniforms();
		
	//インデックス最大数
	indexNumMax = m_IndexNum;
	if (m_pIndexBuffer == NULL) {
		indexNumMax = m_VertexNum;
	}
	
	//描画インデックス数
	//  直接インデックス数を指定された場合はそれに従う
	if (drawIndexNum < 0) {
		indexNum = indexNumMax;
	}
	else {
		indexNum = drawIndexNum;
		if (drawIndexNum > indexNumMax) {
			result = YN_SET_ERR(@"Program error.", drawIndexNum, indexNumMax);
			goto EXIT;
		}
	}
	
	//エンコーダ取得
	commandEncoder = pOGLDevice->GetCurrentCommandEncoder();
	
	//レンダーパイプライン状態登録
	[commandEncoder setRenderPipelineState:m_MTLRenderPipelineState];
	
	//表の向き：右回り
	[commandEncoder setFrontFacingWinding:MTLWindingClockwise];
	
	//カリングモード
	[commandEncoder setCullMode:m_CullMode];
	
	//深度ステンシル状態登録
	[commandEncoder setDepthStencilState:m_MTLDepthStencilState];
	
	//頂点バッファ登録
	[commandEncoder setVertexBuffer:m_MTLVertexBuffer
							 offset:0
							atIndex:BufferIndex_Vertices];
	
	//テクスチャ登録
	if (pTexture != NULL) {
		pTexture->BindTexture(pOGLDevice, TextureIndex0);
	}
	else {
		if (m_VertexFormat != OGLVERTEX_TYPE_V3N3C) {
			result = YN_SET_ERR(@"Program error.", m_VertexFormat, 0);
			goto EXIT;
		}
	}
	
	//制御情報バッファ登録
	memcpy(m_MTLUniformsBuffer.contents, &uniforms, sizeof(OGLUniforms));
	[commandEncoder setVertexBuffer:m_MTLUniformsBuffer
							 offset:0
							atIndex:BufferIndex_Uniforms];
	[commandEncoder setFragmentBuffer:m_MTLUniformsBuffer
							 offset:0
							atIndex:BufferIndex_Uniforms];
	
	//プリミティブ描画指示
	primitiveType = _GetPrimitiveType(m_PrimitiveType);
	if (m_pIndexBuffer != NULL) {
		//インデックス付きプリミティブの描画
		[commandEncoder drawIndexedPrimitives:primitiveType
								  indexCount:indexNum
								   indexType:MTLIndexTypeUInt32
								 indexBuffer:m_MTLIndexBuffer
						   indexBufferOffset:0];
	}
	else {
		//インデックスなしプリミティブの描画
		[commandEncoder drawPrimitives:primitiveType
						   vertexStart:0
						   vertexCount:indexNum];
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// シェーダ用マテリアル取得
//******************************************************************************
OGLMaterial OGLPrimitive::_GetMaterialForShader()
{
	//シェーダ用マテリアル
	OGLMaterial material;
	
	//拡散光
	float3 diffuse = {m_Material.Diffuse.r, m_Material.Diffuse.g, m_Material.Diffuse.b};
	material.diffuseColor = diffuse;
	
	//環境光
	float3 ambient = {m_Material.Ambient.r, m_Material.Ambient.g, m_Material.Ambient.b};
	material.ambientColor= ambient;
	
	//スペキュラ光
	float3 specular = {m_Material.Specular.r, m_Material.Specular.g, m_Material.Specular.b};
	material.specularColor = specular;
	
	//鏡面反射光の鮮明度
	material.specularPower = m_Material.Power;
	
	//発光色はシェーダ側が対応していない
	//m_Material.Emissive;
	
	return material;
}

//******************************************************************************
// 頂点バッファロック
//******************************************************************************
int OGLPrimitive::LockVertex(
		void** pPtrVertex,
		unsigned int offset,	//省略時はゼロ
		unsigned int size		//省略時はゼロ
	)
{
	int result = 0;
	unsigned int lockSize = 0;
	
	if (m_IsVertexLocked) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//ロックサイズ算出
	lockSize = size;
	if (lockSize == 0) {
		lockSize = (m_VertexSize * m_VertexNum) - offset;
	}
	if ((m_VertexSize * m_VertexNum) < (offset + lockSize)) {
		result = YN_SET_ERR(@"Program error.", offset, lockSize);
		goto EXIT;
	}
	
	//頂点バッファのロック位置を取得
	*pPtrVertex = (void*)((unsigned char*)m_pVertexBuffer + offset);
	
	//ロック範囲情報
	m_VertexBufferLockedOffset = offset;
	m_VertexBufferLockedSize = lockSize;
	
	m_IsVertexLocked = true;
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点バッファロック解除
//******************************************************************************
int OGLPrimitive::UnlockVertex()
{
	int result = 0;
	unsigned char* pSrc = NULL;
	unsigned char* pDest = NULL;
	
	if (!m_IsVertexLocked) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//頂点バッファ書き込み
	pSrc = (unsigned char*)m_pVertexBuffer + m_VertexBufferLockedOffset;
	pDest = (unsigned char*)m_MTLVertexBuffer.contents + m_VertexBufferLockedOffset;
	memcpy(pDest, pSrc, m_VertexBufferLockedSize);
	
	m_IsVertexLocked = false;
	
EXIT:;
	return result;
}

//******************************************************************************
// インデックスバッファロック
//******************************************************************************
int OGLPrimitive::LockIndex(
		unsigned int** pPtrIndex,
		unsigned int offset,	//省略時はゼロ
		unsigned int size		//省略時はゼロ
	)
{
	int result = 0;
	unsigned int lockSize = 0;
	
	if (m_IsIndexLocked) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//ロックサイズ算出
	lockSize = size;
	if (lockSize == 0) {
		lockSize = (sizeof(OGLuint) * m_IndexNum) - offset;
	}
	if ((sizeof(OGLuint) * m_IndexNum) < (offset + lockSize)) {
		result = YN_SET_ERR(@"Program error.", offset, lockSize);
		goto EXIT;
	}
	
	//頂点バッファのロック位置を取得
	*pPtrIndex = (unsigned int*)((unsigned char*)m_pIndexBuffer + offset);
	
	//ロック範囲情報
	m_IndexBufferLockedOffset = offset;
	m_IndexBufferLockedSize = lockSize;
	
	m_IsIndexLocked = true;
	
EXIT:;
	return result;
}

//******************************************************************************
// インデックスバッファロック解除
//******************************************************************************
int OGLPrimitive::UnlockIndex()
{
	int result = 0;
	unsigned char* pSrc = NULL;
	unsigned char* pDest = NULL;
	
	if (!m_IsIndexLocked) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//インデックスバッファ書き込み
	pSrc = (unsigned char*)m_pIndexBuffer + m_IndexBufferLockedOffset;
	pDest = (unsigned char*)m_MTLIndexBuffer.contents + m_IndexBufferLockedOffset;
	memcpy(pDest, pSrc, m_IndexBufferLockedSize);
	
	m_IsIndexLocked = false;
	
EXIT:;
	return result;
}

//******************************************************************************
// プリミティブ種別取得
//******************************************************************************
MTLPrimitiveType OGLPrimitive::_GetPrimitiveType(
		OGLenum primitiveType
	)
{
	MTLPrimitiveType type = MTLPrimitiveTypePoint;
	
	switch (m_PrimitiveType) {
		case OGL_POINTS:  //D3DPT_POINTLIST:
			type = MTLPrimitiveTypePoint;
			break;
		case OGL_LINES:  //D3DPT_LINELIST:
			type = MTLPrimitiveTypeLine;
			break;
		case OGL_LINE_STRIP:  //D3DPT_LINESTRIP:
			type = MTLPrimitiveTypeLineStrip;
			break;
		case OGL_TRIANGLES:  //D3DPT_TRIANGLELIST:
			type = MTLPrimitiveTypeTriangle;
			break;
		case OGL_TRIANGLE_STRIP:  //D3DPT_TRIANGLESTRIP:
			type = MTLPrimitiveTypeTriangleStrip;
			break;
		default:
			NSLog(@"Program error.");
			break;
	}
	
	return type;
}

//******************************************************************************
// デフォルトマテリアル
//******************************************************************************
void OGLPrimitive::_GetDefaultMaterial(
		OGLMATERIAL* pMaterial
	)
{
	memset(pMaterial, 0, sizeof(OGLMATERIAL));
	
	//拡散光
	pMaterial->Diffuse.r = 1.0f;
	pMaterial->Diffuse.g = 1.0f;
	pMaterial->Diffuse.b = 1.0f;
	pMaterial->Diffuse.a = 1.0f;
	//環境光：影の色
	pMaterial->Ambient.r = 0.2f; //V1 0.5f;
	pMaterial->Ambient.g = 0.2f; //V1 0.5f;
	pMaterial->Ambient.b = 0.2f; //V1 0.5f;
	pMaterial->Ambient.a = 1.0f;
	//鏡面反射光
	pMaterial->Specular.r = 0.0f;//0.2f;
	pMaterial->Specular.g = 0.0f;//0.2f;
	pMaterial->Specular.b = 0.0f;//0.2f;
	pMaterial->Specular.a = 1.0f;//1.0f;
	//鏡面反射光の鮮明度
	pMaterial->Power = 20.0f; //10.0f;
	//発光色
	pMaterial->Emissive.r = 0.0f;
	pMaterial->Emissive.g = 0.0f;
	pMaterial->Emissive.b = 0.0f;
	pMaterial->Emissive.a = 1.0f;
}

//******************************************************************************
// アライメント
//******************************************************************************
NSUInteger OGLPrimitive::_AlignUp(
		NSUInteger n,
		NSUInteger alignment
	)
{
	return ((n + alignment - 1) / alignment) * alignment;
}

