//----------------------------------------------------------------------------- // File: PixelShader.cpp // // Desc: Example code showing how to use pixel shaders in D3D // // Copyright (c) 1997-2001 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #define STRICT #include "stdafx.h" #include #include #include #include #include #include "resource.h" #include "D3DApp.h" #include "D3DUtil.h" #include "DXUtil.h" #include "PixelShader.h" //----------------------------------------------------------------------------- // Global data and objects //----------------------------------------------------------------------------- CApp g_App; CAppForm* g_AppFormView = NULL; TCHAR* g_strAppTitle = _T("MFCPixelShader"); //----------------------------------------------------------------------------- // The MFC macros are all listed here //----------------------------------------------------------------------------- IMPLEMENT_DYNCREATE( CAppDoc, CDocument ) IMPLEMENT_DYNCREATE( CAppFrameWnd, CFrameWnd ) IMPLEMENT_DYNCREATE( CAppForm, CFormView ) BEGIN_MESSAGE_MAP( CApp, CWinApp ) //{{AFX_MSG_MAP(CD3DApp) //}}AFX_MSG_MAP END_MESSAGE_MAP() BEGIN_MESSAGE_MAP( CAppForm, CFormView ) //{{AFX_MSG_MAP(CAppForm) ON_WM_HSCROLL() ON_COMMAND( IDC_VIEWFULLSCREEN, OnToggleFullScreen ) ON_COMMAND( IDM_CHANGEDEVICE, OnChangeDevice ) ON_BN_CLICKED( IDC_OPEN, OnOpenPixelShaderFile ) ON_BN_CLICKED( IDC_PRESET_0, OnPresets ) ON_BN_CLICKED( IDC_PRESET_1, OnPresets ) ON_BN_CLICKED( IDC_PRESET_2, OnPresets ) ON_BN_CLICKED( IDC_PRESET_3, OnPresets ) ON_BN_CLICKED( IDC_PRESET_4, OnPresets ) ON_BN_CLICKED( IDC_PRESET_5, OnPresets ) ON_EN_CHANGE( IDC_INSTRUCTIONS, OnChangeInstructions ) //}}AFX_MSG_MAP END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CAppDoc, CDocument) //{{AFX_MSG_MAP(CAppDoc) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CAppFrameWnd, CFrameWnd) //{{AFX_MSG_MAP(CAppFrameWnd) //}}AFX_MSG_MAP END_MESSAGE_MAP() //----------------------------------------------------------------------------- // Function prototypes and global (or static) variables //----------------------------------------------------------------------------- struct CUSTOMVERTEX { FLOAT x, y, z; DWORD color1, color2; FLOAT tu1, tv1; FLOAT tu2, tv2; }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_SPECULAR|D3DFVF_TEX2) static CUSTOMVERTEX g_Vertices[]= { // Color 0 (diffuse) is a color gradient // Color 1 (specular) is a greyscale ramp // x y z diffuse specular u1 v1 u2 v2 { -1.0f, -1.0f, 0.0f, 0xff00ffff, 0xffffffff, 1.0f, 1.0f, 1.0f, 1.0f }, { +1.0f, -1.0f, 0.0f, 0xffffff00, 0xffffffff, 0.0f, 1.0f, 0.0f, 1.0f }, { +1.0f, +1.0f, 0.0f, 0xffff0000, 0xff000000, 0.0f, 0.0f, 0.0f, 0.0f }, { -1.0f, +1.0f, 0.0f, 0xff0000ff, 0xff000000, 1.0f, 0.0f, 1.0f, 0.0f }, }; inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); } //----------------------------------------------------------------------------- // Name: AdjustWindowForChange() // Desc: Adjusts the window properties for windowed or fullscreen mode //----------------------------------------------------------------------------- HRESULT CAppForm::AdjustWindowForChange() { if( m_bWindowed ) { ::ShowWindow( m_hwndRenderFullScreen, SW_HIDE ); CD3DApplication::m_hWnd = m_hwndRenderWindow; } else { if( ::IsIconic( m_hwndRenderFullScreen ) ) ::ShowWindow( m_hwndRenderFullScreen, SW_RESTORE ); ::ShowWindow( m_hwndRenderFullScreen, SW_SHOW ); CD3DApplication::m_hWnd = m_hwndRenderFullScreen; } return S_OK; } //----------------------------------------------------------------------------- // Name: FullScreenWndProc() // Desc: The WndProc funtion used when the app is in fullscreen mode. This is // needed simply to trap the ESC key. //----------------------------------------------------------------------------- LRESULT CALLBACK FullScreenWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { if( msg == WM_CLOSE ) { // User wants to exit, so go back to windowed mode and exit for real g_AppFormView->OnToggleFullScreen(); g_App.GetMainWnd()->PostMessage( WM_CLOSE, 0, 0 ); } if( msg == WM_SETCURSOR ) { SetCursor( NULL ); } if( msg == WM_KEYUP && wParam == VK_ESCAPE ) { // User wants to leave fullscreen mode g_AppFormView->OnToggleFullScreen(); } return DefWindowProc( hWnd, msg, wParam, lParam ); } //----------------------------------------------------------------------------- // Name: CheckForLostFullscreen() // Desc: If fullscreen and device was lost (probably due to alt-tab), // automatically switch to windowed mode //----------------------------------------------------------------------------- HRESULT CAppForm::CheckForLostFullscreen() { HRESULT hr; if( m_bWindowed ) return S_OK; if( FAILED( hr = m_pd3dDevice->TestCooperativeLevel() ) ) ForceWindowed(); return S_OK; } //----------------------------------------------------------------------------- // Name: PreCreateWindow() // Desc: Change the window style (so it cannot maximize or be sized) before // the main frame window is created. //----------------------------------------------------------------------------- BOOL CAppFrameWnd::PreCreateWindow( CREATESTRUCT& cs ) { cs.style = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX; return CFrameWnd::PreCreateWindow( cs ); } //----------------------------------------------------------------------------- // Name: InitInstance() // Desc: This is the main entry point for the application. The MFC window stuff // is initialized here. See also the main initialization routine for the // CAppForm class, which is called indirectly from here. //----------------------------------------------------------------------------- BOOL CApp::InitInstance() { // Asscociate the MFC app with the frame window and doc/view classes AddDocTemplate( new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CAppDoc), RUNTIME_CLASS(CAppFrameWnd), RUNTIME_CLASS(CAppForm) ) ); // Dispatch commands specified on the command line (req'd by MFC). This // also initializes the the CAppDoc, CAppFrameWnd, and CAppForm classes. CCommandLineInfo cmdInfo; ParseCommandLine( cmdInfo ); if( !ProcessShellCommand( cmdInfo ) ) return FALSE; if( !g_AppFormView->IsReady() ) return FALSE; g_AppFormView->GetParentFrame()->RecalcLayout(); g_AppFormView->ResizeParentToFit( FALSE ); m_pMainWnd->SetWindowText( g_strAppTitle ); m_pMainWnd->UpdateWindow(); return TRUE; } //----------------------------------------------------------------------------- // Name: OnIdle() // Desc: Uses idle time to render the 3D scene. //----------------------------------------------------------------------------- BOOL CApp::OnIdle( LONG ) { // Do not render if the app is minimized if( m_pMainWnd->IsIconic() ) return FALSE; // Update and render a frame if( g_AppFormView->IsReady() ) { g_AppFormView->CheckForLostFullscreen(); g_AppFormView->RenderScene(); } // Keep requesting more idle time return TRUE; } //----------------------------------------------------------------------------- // Name: CAppForm() // Desc: Constructor for the dialog resource form //----------------------------------------------------------------------------- CAppForm::CAppForm() :CFormView( IDD_FORMVIEW ) { g_AppFormView = this; m_pTexture0 = NULL; m_pTexture1 = NULL; m_pQuadVB = NULL; m_hPixelShader = NULL; m_pD3DXBufShader = NULL; } //----------------------------------------------------------------------------- // Name: ~CAppForm() // Desc: Destructor for the dialog resource form. Shuts down the app //----------------------------------------------------------------------------- CAppForm::~CAppForm() { Cleanup3DEnvironment(); } //----------------------------------------------------------------------------- // Name: OnInitialUpdate() // Desc: When the AppForm object is created, this function is called to // initialize it. Here we getting access ptrs to some of the controls, // and setting the initial state of some of them as well. //----------------------------------------------------------------------------- VOID CAppForm::OnInitialUpdate() { CFormView::OnInitialUpdate(); // Save static reference to the render window m_hwndRenderWindow = GetDlgItem(IDC_RENDERVIEW)->GetSafeHwnd(); // Register a class for a fullscreen window WNDCLASS wndClass = { CS_HREDRAW | CS_VREDRAW, FullScreenWndProc, 0, 0, NULL, NULL, NULL, (HBRUSH)GetStockObject(WHITE_BRUSH), NULL, _T("Fullscreen Window") }; RegisterClass( &wndClass ); // We create the fullscreen window (not visible) at startup, so it can // be the focus window. The focus window can only be set at CreateDevice // time, not in a Reset, so ToggleFullscreen wouldn't work unless we have // already set up the fullscreen focus window. m_hwndRenderFullScreen = CreateWindow( _T("Fullscreen Window"), NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, GetTopLevelParent()->GetSafeHwnd(), 0L, NULL, 0L ); // Note that for the MFC samples, the device window and focus window // are not the same. CD3DApplication::m_hWnd = m_hwndRenderWindow; CD3DApplication::m_hWndFocus = m_hwndRenderFullScreen; CD3DApplication::Create( AfxGetInstanceHandle() ); // Set up the form's UI ((CButton*)GetDlgItem(IDC_PRESET_0))->SetCheck(TRUE); OnPresets(); } //----------------------------------------------------------------------------- // Name: SetPixelShader() // Desc: //----------------------------------------------------------------------------- HRESULT CAppForm::SetPixelShader( TCHAR* strOpcodes ) { HRESULT hr; LPD3DXBUFFER pBuffer = NULL; SAFE_RELEASE( m_pD3DXBufShader ); // Build a DWORD array of opcodes from the text string hr = D3DXAssembleShader( strOpcodes, lstrlen(strOpcodes), 0, NULL, &m_pD3DXBufShader, &pBuffer ); if( FAILED(hr) ) { if( pBuffer != NULL) { TCHAR* pstr; TCHAR strOut[4096]; TCHAR* pstrOut; // Need to replace \n with \r\n so edit box shows newlines properly pstr = (TCHAR*)pBuffer->GetBufferPointer(); strOut[0] = _T('\0'); pstrOut = strOut; for( int i = 0; i < 4096; i++ ) { if( *pstr == _T('\n') ) *pstrOut++ = _T('\r'); *pstrOut = *pstr; if( *pstr == _T('\0') ) break; if( i == 4095 ) *pstrOut = _T('\0'); pstrOut++; pstr++; } // remove any blank lines at the end while( strOut[lstrlen(strOut) - 1] == _T('\n') || strOut[lstrlen(strOut) - 1] == _T('\r') ) { strOut[lstrlen(strOut) - 1] = _T('\0'); } SetDlgItemText( IDC_COMPRESULT, strOut ); SAFE_RELEASE( pBuffer ); } return hr; } else { SAFE_RELEASE( pBuffer ); // Delete old pixel shader if( m_hPixelShader ) m_pd3dDevice->DeletePixelShader( m_hPixelShader ); m_hPixelShader = NULL; // Create new pixel shader hr = m_pd3dDevice->CreatePixelShader( (DWORD*)m_pD3DXBufShader->GetBufferPointer(), &m_hPixelShader ); if( FAILED(hr) ) { ((CStatic*)GetDlgItem(IDC_COMPRESULT))->SetWindowText( _T("Failure (D3D)")); return hr; } else { ((CStatic*)GetDlgItem(IDC_COMPRESULT))->SetWindowText( _T("Success")); } } return S_OK; } //----------------------------------------------------------------------------- // Name: OneTimeSceneInit() // Desc: Called during initial app startup, this function performs all the // permanent initialization. //----------------------------------------------------------------------------- HRESULT CAppForm::OneTimeSceneInit() { return S_OK; } //----------------------------------------------------------------------------- // Name: FrameMove() // Desc: Called once per frame, the call is the entry point for animating // the scene. //----------------------------------------------------------------------------- HRESULT CAppForm::FrameMove() { // Move the camera along an ellipse D3DXVECTOR3 from( 3*sinf(m_fTime/3), 3*cosf(m_fTime/3), 5.0f ); D3DXVECTOR3 at( 0.0f, 0.0f, 0.0f ); D3DXVECTOR3 up( 0.0f, 1.0f, 0.0f ); D3DXMATRIX matWorld; D3DXMatrixIdentity( &matWorld ); m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); D3DXMATRIX matView; D3DXMatrixLookAtLH( &matView, &from, &at, &up ); m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 0.5f, 1000.0f ); m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); return S_OK; } //----------------------------------------------------------------------------- // Name: Render() // Desc: Called once per frame, the call is the entry point for 3d // rendering. This function sets up render states, clears the // viewport, and renders the scene. //----------------------------------------------------------------------------- HRESULT CAppForm::Render() { // Clear the viewport m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L ); // Begin the scene if( SUCCEEDED( m_pd3dDevice->BeginScene() ) ) { // Set device state m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); m_pd3dDevice->SetRenderState( D3DRS_CLIPPING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); // Set the textures m_pd3dDevice->SetTexture( 0, m_pTexture0 ); m_pd3dDevice->SetTexture( 1, m_pTexture1 ); // Set the pixel shader constants FLOAT fPixelShaderConstants[8][4] = { // Red Green Blue Alpha { 1.0f, 0.0f, 0.0f, 1.0f, }, // red { 0.0f, 1.0f, 0.0f, 1.0f, }, // green { 0.0f, 0.0f, 1.0f, 1.0f, }, // blue { 1.0f, 1.0f, 0.0f, 1.0f, }, // yellow { 0.0f, 1.0f, 1.0f, 1.0f, }, // cyan { 1.0f, 0.0f, 1.0f, 1.0f, }, // purple { 1.0f, 1.0f, 1.0f, 1.0f, }, // white { 0.0f, 0.0f, 0.0f, 1.0f, }, // black }; m_pd3dDevice->SetPixelShaderConstant( 0, (VOID*)fPixelShaderConstants, 8 ); // Render the quad with the pixel shader m_pd3dDevice->SetStreamSource( 0, m_pQuadVB, sizeof(CUSTOMVERTEX) ); m_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX ); m_pd3dDevice->SetPixelShader( m_hPixelShader ); m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 ); // End the scene. m_pd3dDevice->EndScene(); } return S_OK; } //----------------------------------------------------------------------------- // Name: InitDeviceObjects() // Desc: Initialize scene objects. //----------------------------------------------------------------------------- HRESULT CAppForm::InitDeviceObjects() { HRESULT hr; // Create some textures if( FAILED( D3DUtil_CreateTexture( m_pd3dDevice, _T("DX5_Logo.bmp"), &m_pTexture0, D3DFMT_R5G6B5 ) ) ) { return E_FAIL; } if( FAILED( D3DUtil_CreateTexture( m_pd3dDevice, _T("Tree01S.tga"), &m_pTexture1, D3DFMT_R5G6B5 ) ) ) { return E_FAIL; } // Create quad VB hr = m_pd3dDevice->CreateVertexBuffer( 4*sizeof(CUSTOMVERTEX), D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &m_pQuadVB ); if( FAILED(hr) ) return hr; return S_OK; } //----------------------------------------------------------------------------- // Name: RestoreDeviceObjects() // Desc: //----------------------------------------------------------------------------- HRESULT CAppForm::RestoreDeviceObjects() { HRESULT hr; // Fill the quad VB CUSTOMVERTEX* pVertices = NULL; hr = m_pQuadVB->Lock( 0, 4*sizeof(CUSTOMVERTEX), (BYTE**)&pVertices, 0 ); if( FAILED(hr) ) return hr; for( DWORD i=0; i<4; i++ ) pVertices[i] = g_Vertices[i]; m_pQuadVB->Unlock(); // Delete old pixel shader (redundant if InvalidateDeviceObjects is called first) if( m_hPixelShader ) m_pd3dDevice->DeletePixelShader( m_hPixelShader ); m_hPixelShader = NULL; // Create new pixel shader if( m_pD3DXBufShader ) hr = m_pd3dDevice->CreatePixelShader( (DWORD*)m_pD3DXBufShader->GetBufferPointer(), &m_hPixelShader ); return S_OK; } //----------------------------------------------------------------------------- // Name: InvalidateDeviceObjects() // Desc: Called when the device-dependent objects are about to be lost. //----------------------------------------------------------------------------- HRESULT CAppForm::InvalidateDeviceObjects() { if( m_hPixelShader ) m_pd3dDevice->DeletePixelShader( m_hPixelShader ); m_hPixelShader = NULL; return S_OK; } //----------------------------------------------------------------------------- // Name: DeleteDeviceObjects() // Desc: Called when the app is exiting, or the device is being changed, // this function deletes any device dependent objects. //----------------------------------------------------------------------------- HRESULT CAppForm::DeleteDeviceObjects() { SAFE_RELEASE( m_pTexture0 ); SAFE_RELEASE( m_pTexture1 ); SAFE_RELEASE( m_pQuadVB ); SAFE_RELEASE( m_pD3DXBufShader ); return S_OK; } //----------------------------------------------------------------------------- // Name: FinalCleanup() // Desc: Called before the app exits, this function gives the app the chance // to cleanup after itself. //----------------------------------------------------------------------------- HRESULT CAppForm::FinalCleanup() { return S_OK; } //----------------------------------------------------------------------------- // Name: ConfirmDevice() // Desc: Called during device intialization, this code checks the device // for some minimum set of capabilities //----------------------------------------------------------------------------- HRESULT CAppForm::ConfirmDevice( D3DCAPS8* pCaps, DWORD dwBehavior, D3DFORMAT Format ) { if( D3DSHADER_VERSION_MAJOR( pCaps->PixelShaderVersion ) < 1 ) return E_FAIL; return S_OK; } //----------------------------------------------------------------------------- // The remaining code handles the UI for the MFC-based app. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Name: OnChangeDevice() // Desc: Use hit the "Change Device.." button. Display the dialog for the user // to select a new device/mode, and call Change3DEnvironment to // use the new device/mode. //----------------------------------------------------------------------------- VOID CAppForm::OnChangeDevice() { UserSelectNewDevice(); GeneratePixelShaderOpcodes(); } //----------------------------------------------------------------------------- // Name: OnToggleFullScreen() // Desc: Called when user toggles the fullscreen mode //----------------------------------------------------------------------------- void CAppForm::OnToggleFullScreen() { ToggleFullscreen(); GeneratePixelShaderOpcodes(); } //----------------------------------------------------------------------------- // Name: OnHScroll() // Desc: Called when the user moves any scroll bar. Check which scrollbar was // moved, and extract the appropiate value to a global variable. //----------------------------------------------------------------------------- void CAppForm::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { CFormView::OnHScroll(nSBCode, nPos, pScrollBar); } //----------------------------------------------------------------------------- // Name: UpdateUIForDeviceCapabilites() // Desc: Whenever we get a new device, call this function to enable/disable the // appropiate UI controls to match the device's capabilities. //----------------------------------------------------------------------------- VOID CAppForm::UpdateUIForDeviceCapabilites() { // Check the capabilities of the device // Update the UI checkbox states } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- void CAppForm::OnPresets() { DWORD dwPreset = 0L; dwPreset = ((CButton*)GetDlgItem(IDC_PRESET_0))->GetCheck() ? 0 : dwPreset; dwPreset = ((CButton*)GetDlgItem(IDC_PRESET_1))->GetCheck() ? 1 : dwPreset; dwPreset = ((CButton*)GetDlgItem(IDC_PRESET_2))->GetCheck() ? 2 : dwPreset; dwPreset = ((CButton*)GetDlgItem(IDC_PRESET_3))->GetCheck() ? 3 : dwPreset; dwPreset = ((CButton*)GetDlgItem(IDC_PRESET_4))->GetCheck() ? 4 : dwPreset; dwPreset = ((CButton*)GetDlgItem(IDC_PRESET_5))->GetCheck() ? 5 : dwPreset; CEdit* pEdit = (CEdit*)GetDlgItem(IDC_INSTRUCTIONS); pEdit->SetWindowText( g_strPixelShaderPresets[dwPreset] ); // Generate the opcodes and pass them to the app if( m_pd3dDevice != NULL ) GeneratePixelShaderOpcodes(); } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- void CAppForm::GeneratePixelShaderOpcodes() { TCHAR strOpcodes[2048] = _T(""); CEdit* pEdit = (CEdit*)GetDlgItem(IDC_INSTRUCTIONS); pEdit->GetWindowText(strOpcodes, 2048); SetPixelShader( strOpcodes ); } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- void CAppForm::OnOpenPixelShaderFile() { ((CButton*)GetDlgItem(IDC_PRESET_0))->SetCheck( FALSE ); ((CButton*)GetDlgItem(IDC_PRESET_1))->SetCheck( FALSE ); ((CButton*)GetDlgItem(IDC_PRESET_2))->SetCheck( FALSE ); ((CButton*)GetDlgItem(IDC_PRESET_3))->SetCheck( FALSE ); ((CButton*)GetDlgItem(IDC_PRESET_4))->SetCheck( FALSE ); ((CButton*)GetDlgItem(IDC_PRESET_5))->SetCheck( FALSE ); static TCHAR g_strFileName[MAX_PATH] = _T(""); static TCHAR g_strInitialDir[MAX_PATH] = _T(""); TCHAR strFileName[MAX_PATH] = _T(""); TCHAR strBuffer[81]; // Display the OpenFileName dialog. Then, try to load the specified file OPENFILENAME ofn = { sizeof(OPENFILENAME), NULL, NULL, _T("Pixel Shader Text Files (.txt)\0*.txt\0\0"), NULL, 0, 1, strFileName, MAX_PATH, g_strFileName, MAX_PATH, g_strInitialDir, _T("Open Pixel Shader File"), OFN_FILEMUSTEXIST, 0, 1, NULL, 0, NULL, NULL }; if( FALSE == GetOpenFileName( &ofn ) ) return; FILE* file = fopen( strFileName, _T("r") ); if( file == NULL ) return; // Fill the list box with the preset's pixel shader instructions CEdit* pEdit = (CEdit*)GetDlgItem(IDC_INSTRUCTIONS); pEdit->SetSel(0, -1); while( fgets( strBuffer, 80, file ) ) { // Remove trailing newline char if( strBuffer[max(1,strlen(strBuffer))-1] == '\n' ) strBuffer[max(1,strlen(strBuffer))-1] = '\0'; pEdit->ReplaceSel(strBuffer); pEdit->ReplaceSel("\r\n"); } fclose(file); // Generater the opcodes and pass them to the app GeneratePixelShaderOpcodes(); } void CAppForm::OnChangeInstructions() { GeneratePixelShaderOpcodes(); }