GLvoid drawScene(GLvoid)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// テクスチャの更新
	ovrvision.Capture(OVR::Camqt::OV_CAMQT_DMS);
	glFinish();
//	int64 start = cv::getTickCount();
#ifdef HAND_TEXTURE
	ovrvision.UpdateSkinTextures(textureIDs[0], textureIDs[1]); 
#else
	ovrvision.UpdateImageTextures(textureIDs[0], textureIDs[1]);
#endif // DEBUG

//	int64 stop = cv::getTickCount();
//	double usec = (stop - start) * 1000000 / cv::getTickFrequency();
//	printf("%f usec\n", usec);
//	usec = cv::getTickFrequency();
	
#ifdef _DEBUG
	cv::Mat left(size.height, size.width, CV_8UC4);
	cv::Mat right(size.height, size.width, CV_8UC4);
	ovrvision.InspectTextures(left.data, right.data, 3); // Get HSV images
	cv::imshow("Left", left);
	cv::imshow("Right", right);
#endif

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, textureIDs[0]);

	// テクスチャー貼り付け
	glBegin(GL_QUADS);
	glTexCoord2i(0, 0);
	glVertex3f(-1.0f, 1.0f, 0.0f);
	glTexCoord2i(1, 0);
	glVertex3f(1.0f, 1.0f, 0.0f);
	glTexCoord2i(1, 1);
	glVertex3f(1.0f, -1.0f, 0.0f);
	glTexCoord2i(0, 1);
	glVertex3f(-1.0f, -1.0f, 0.0f);
	glEnd();
	glFinish();

	// platform depend function
	SWAPBUFFERS();
}
/*****************************************************************************
UINT __stdcall ThreadProc_Calc(VOID * pParam)
	pParam	: (IN/OUT) THREAD_PARAMS_CALC struct pointer special for this thread

Return Value:
	returns 0

Notes:
- requests jobs from the queue and calculates hashes until the queue is empty
- spawns up to three additional threads, one for each hash value
- performs asynchronous I/O with two buffers -> one buffer is filled while the hash-threads
  work on the other buffer
- if an error occured, GetLastError() is saved in the current pFileinfo->dwError
- what has be calculated is determined by bDoCalculate[HASH_TYPE_CRC32]/bDoCalculate[HASH_TYPE_MD5]/bDoCalculate[HASH_TYPE_ED2K] of the
  current job
*****************************************************************************/
UINT __stdcall ThreadProc_Calc(VOID * pParam)
{
	THREAD_PARAMS_CALC * CONST pthread_params_calc = (THREAD_PARAMS_CALC *)pParam;
	CONST HWND * CONST arrHwnd = pthread_params_calc->arrHwnd;
	SHOWRESULT_PARAMS * CONST pshowresult_params = pthread_params_calc->pshowresult_params;

	BOOL bDoCalculate[NUM_HASH_TYPES];

	QWORD qwStart, qwStop, wqFreq;
	HANDLE hFile;
	BYTE *readBuffer = (BYTE *)malloc(MAX_BUFFER_SIZE_CALC);
	BYTE *calcBuffer = (BYTE *)malloc(MAX_BUFFER_SIZE_CALC);
	BYTE *tempBuffer;
	DWORD readWords[2];
	DWORD *dwBytesReadRb = &readWords[0];
	DWORD *dwBytesReadCb = &readWords[1];
	DWORD *dwBytesReadTb;
	BOOL bSuccess;
	BOOL bFileDone;
	BOOL bAsync;

    HANDLE hEvtThreadGo[NUM_HASH_TYPES];
    HANDLE hEvtThreadReady[NUM_HASH_TYPES];

	HANDLE hEvtReadDone;
	OVERLAPPED olp;
	ZeroMemory(&olp,sizeof(olp));

    HANDLE hThread[NUM_HASH_TYPES];
	
	HANDLE hEvtReadyHandles[NUM_HASH_TYPES];
	DWORD cEvtReadyHandles;

    THREAD_PARAMS_HASHCALC calcParams[NUM_HASH_TYPES];

	lFILEINFO *fileList;
	list<FILEINFO*> finalList;

	if(readBuffer == NULL || calcBuffer == NULL) {
		ShowErrorMsg(arrHwnd[ID_MAIN_WND],GetLastError());
		ExitProcess(1);
	}
	

	// set some UI stuff:
	// - disable action buttons while in thread
	EnableWindowsForThread(arrHwnd, FALSE);

	ShowResult(arrHwnd, NULL, pshowresult_params);
	
	while((fileList = SyncQueue.popQueue()) != NULL) {

        cEvtReadyHandles = 0;

        for(int i=0;i<NUM_HASH_TYPES;i++) {
		    bDoCalculate[i]	= !fileList->bCalculated[i] && fileList->bDoCalculate[i];

            if(bDoCalculate[i]) {
			    fileList->bCalculated[i] = TRUE;
			    hEvtThreadGo[i] = CreateEvent(NULL,FALSE,FALSE,NULL);
			    hEvtThreadReady[i] = CreateEvent(NULL,FALSE,FALSE,NULL);
			    if(hEvtThreadGo[i] == NULL || hEvtThreadReady[i] == NULL) {
				    ShowErrorMsg(arrHwnd[ID_MAIN_WND],GetLastError());
				    ExitProcess(1);
			    }
			    hEvtReadyHandles[cEvtReadyHandles] = hEvtThreadReady[i];
			    cEvtReadyHandles++;
			    calcParams[i].bFileDone = &bFileDone;
			    calcParams[i].hHandleGo = hEvtThreadGo[i];
			    calcParams[i].hHandleReady = hEvtThreadReady[i];
			    calcParams[i].buffer = &calcBuffer;
			    calcParams[i].dwBytesRead = &dwBytesReadCb;
		    }
        }

		hEvtReadDone = CreateEvent(NULL,FALSE,FALSE,NULL);
		if(hEvtReadDone == NULL) {
			ShowErrorMsg(arrHwnd[ID_MAIN_WND],GetLastError());
			ExitProcess(1);
		}

		QueryPerformanceFrequency((LARGE_INTEGER*)&wqFreq);

		if(g_program_options.bEnableQueue && g_pstatus.bHaveComCtrlv6) {
			if(fileList->iGroupId==0)
				InsertGroupIntoListView(arrHwnd[ID_LISTVIEW],fileList);
			else
				RemoveGroupItems(arrHwnd[ID_LISTVIEW],fileList->iGroupId);
		}

		for(list<FILEINFO>::iterator it=fileList->fInfos.begin();it!=fileList->fInfos.end();it++)
		{
			pthread_params_calc->pFileinfo_cur = &(*it);
			pthread_params_calc->qwBytesReadCurFile = 0;
			
			FILEINFO& curFileInfo = (*it);

			if ( (curFileInfo.dwError == NO_ERROR) && cEvtReadyHandles > 0)
			{

                DisplayStatusOverview(arrHwnd[ID_EDIT_STATUS]);

				QueryPerformanceCounter((LARGE_INTEGER*) &qwStart);
				hFile = CreateFile(curFileInfo.szFilename,
						GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN , 0);
				if(hFile == INVALID_HANDLE_VALUE){
					curFileInfo.dwError = GetLastError();
                } else {

				    bFileDone = FALSE;

                    for(int i=0;i<NUM_HASH_TYPES;i++) {
                        if(bDoCalculate[i]) {
                            ResetEvent(hEvtThreadGo[i]);
                            ResetEvent(hEvtThreadReady[i]);
                            calcParams[i].result = &curFileInfo.hashInfo[i].r;
					        hThread[i] = CreateThread(NULL,0,hash_function[i],&calcParams[i],0,NULL);
					        if(hThread[i] == NULL) {
						        ShowErrorMsg(arrHwnd[ID_MAIN_WND],GetLastError());
						        ExitProcess(1);
					        }
                        }
				    }

				    ZeroMemory(&olp,sizeof(olp));
				    olp.hEvent = hEvtReadDone;
				    olp.Offset = 0;
				    olp.OffsetHigh = 0;
				    bSuccess = ReadFile(hFile, readBuffer, MAX_BUFFER_SIZE_CALC, dwBytesReadRb, &olp);
				    if(!bSuccess && (GetLastError()==ERROR_IO_PENDING))
					    bAsync = TRUE;
				    else
					    bAsync = FALSE;

				    do {
					    if(bAsync)
						    bSuccess = GetOverlappedResult(hFile,&olp,dwBytesReadRb,TRUE);
					    if(!bSuccess && (GetLastError() != ERROR_HANDLE_EOF)) {
						    curFileInfo.dwError = GetLastError();
						    bFileDone = TRUE;
					    }
					    pthread_params_calc->qwBytesReadCurFile  += *dwBytesReadRb; //for progress bar
					    pthread_params_calc->qwBytesReadAllFiles += *dwBytesReadRb;

					    olp.Offset = pthread_params_calc->qwBytesReadCurFile & 0xffffffff;
					    olp.OffsetHigh = (pthread_params_calc->qwBytesReadCurFile >> 32) & 0xffffffff;
    					
					    WaitForMultipleObjects(cEvtReadyHandles,hEvtReadyHandles,TRUE,INFINITE);
					    SWAPBUFFERS();
					    bSuccess = ReadFile(hFile, readBuffer, MAX_BUFFER_SIZE_CALC, dwBytesReadRb, &olp);
					    if(!bSuccess && (GetLastError()==ERROR_IO_PENDING))
						    bAsync = TRUE;
					    else
						    bAsync = FALSE;

					    if(*dwBytesReadCb<MAX_BUFFER_SIZE_CALC || pthread_params_calc->signalExit)
						    bFileDone=TRUE;

                        for(int i=0;i<NUM_HASH_TYPES;i++) {
                            if(bDoCalculate[i])
						        SetEvent(hEvtThreadGo[i]);
                        }

				    } while(!bFileDone);

				    WaitForMultipleObjects(cEvtReadyHandles,hEvtReadyHandles,TRUE,INFINITE);

				    if(hFile != NULL)
					    CloseHandle(hFile);

                    for(int i=0;i<NUM_HASH_TYPES;i++) {
                        if(bDoCalculate[i])
					        CloseHandle(hThread[i]);
                    }

				    if(pthread_params_calc->signalExit)
					    break;

				    QueryPerformanceCounter((LARGE_INTEGER*) &qwStop);
				    curFileInfo.fSeconds = (float)((qwStop - qwStart) / (float)wqFreq);
                }
			}

            curFileInfo.status = InfoToIntValue(&curFileInfo);
			SetFileInfoStrings(&curFileInfo,fileList);

            if(!g_pstatus.bHideVerified || curFileInfo.status != STATUS_OK) {
			    InsertItemIntoList(arrHwnd[ID_LISTVIEW], &curFileInfo,fileList);
            }

            SyncQueue.getDoneList();
            SyncQueue.adjustErrorCounters(&curFileInfo,1);
            SyncQueue.releaseDoneList();

			ShowResult(arrHwnd, &curFileInfo, pshowresult_params);
		}

        for(int i=0;i<NUM_HASH_TYPES;i++) {
            if(bDoCalculate[i]) {
		        CloseHandle(hEvtThreadGo[i]);
			    CloseHandle(hEvtThreadReady[i]);
            }
        }

		if(pthread_params_calc->signalExit)
			break;

		if(fileList->uiCmdOpts!=CMD_NORMAL) {
			for(list<FILEINFO>::iterator it=fileList->fInfos.begin();it!=fileList->fInfos.end();it++) {
				finalList.push_back(&(*it));
			}
			finalList.sort(ListPointerCompFunction);
			switch(fileList->uiCmdOpts) {
				case CMD_SFV:
				case CMD_MD5:
				case CMD_SHA1:
                case CMD_SHA256:
                case CMD_SHA512:
					CreateChecksumFiles(arrHwnd,fileList->uiCmdOpts,&finalList);
					break;
				case CMD_NAME:
                    ActionHashIntoFilename(arrHwnd, TRUE, &finalList, HASH_TYPE_CRC32);
					break;
				case CMD_NTFS:
					ActionCrcIntoStream(arrHwnd,TRUE,&finalList);
					break;
				default:
					break;
			}
			finalList.clear();
		}

		SyncQueue.addToList(fileList);

	}