/* 07_two_texture_accesses.c - OpenGL-based very simple vertex program example using Cg program from Chapter 2 of "The Cg Tutorial" (Addison-Wesley, ISBN 0321194969). */ #include <windows.h> #include <stdio.h> #include <d3d9.h> /* Can't include this? Is DirectX SDK installed? */ #include <Cg/cg.h> /* Can't include this? Is Cg Toolkit installed! */ #include <Cg/cgD3D9.h> #include "DXUT.h" /* DirectX Utility Toolkit (part of the DirectX SDK) */ #pragma comment(lib, "dxguid.lib") #pragma comment(lib, "d3d9.lib") static CGcontext myCgContext; static CGprofile myCgVertexProfile, myCgFragmentProfile; static CGprogram myCgVertexProgram, myCgFragmentProgram; static CGparameter myCgVertexParam_leftSeparation, myCgVertexParam_rightSeparation, myCgFragmentParam_decal; static bool myAnimating = false; static float mySeparation = 0.1f, mySeparationVelocity = 0.005f; static PDIRECT3DVERTEXBUFFER9 myVertexBuffer = NULL; static PDIRECT3DTEXTURE9 myTexture = NULL; static const WCHAR *myProgramNameW = L"07_two_texture_accesses"; static const char *myProgramName = "07_two_texture_accesses", *myVertexProgramFileName = "C3E5v_twoTextures.cg", /* Page 83 */ *myVertexProgramName = "C3E5v_twoTextures", *myFragmentProgramFileName = "C3E6f_twoTextures.cg", /* Page 85 */ *myFragmentProgramName = "C3E6f_twoTextures"; static const unsigned char myDemonTextureImage[3*(128*128+64*64+32*32+16*16+8*8+4*4+2*2+1*1)] = { /* RGB8 image data for a mipmapped 128x128 demon texture */ #include "demon_image.h" }; static void checkForCgError(const char *situation) { char buffer[4096]; CGerror error; const char *string = cgGetLastErrorString(&error); if (error != CG_NO_ERROR) { if (error == CG_COMPILER_ERROR) { sprintf(buffer, "Program: %s\n" "Situation: %s\n" "Error: %s\n\n" "Cg compiler output...\n", myProgramName, situation, string); OutputDebugStringA(buffer); OutputDebugStringA(cgGetLastListing(myCgContext)); sprintf(buffer, "Program: %s\n" "Situation: %s\n" "Error: %s\n\n" "Check debug output for Cg compiler output...", myProgramName, situation, string); MessageBoxA(0, buffer, "Cg compilation error", MB_OK | MB_ICONSTOP | MB_TASKMODAL); } else { sprintf(buffer, "Program: %s\n" "Situation: %s\n" "Error: %s", myProgramName, situation, string); MessageBoxA(0, buffer, "Cg runtime error", MB_OK | MB_ICONSTOP | MB_TASKMODAL); } exit(1); } } /* Forward declared DXUT callbacks registered by WinMain. */ static HRESULT CALLBACK OnResetDevice(IDirect3DDevice9*, const D3DSURFACE_DESC*, void*); static void CALLBACK OnFrameRender(IDirect3DDevice9*, double, float, void*); static void CALLBACK OnLostDevice(void*); static void CALLBACK OnFrameMove(IDirect3DDevice9*, double, float, void*); static void CALLBACK KeyboardProc(UINT, bool, bool, void*); INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { myCgContext = cgCreateContext(); checkForCgError("creating context"); cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING); DXUTSetCallbackDeviceReset(OnResetDevice); DXUTSetCallbackDeviceLost(OnLostDevice); DXUTSetCallbackFrameRender(OnFrameRender); DXUTSetCallbackFrameMove(OnFrameMove); DXUTSetCallbackKeyboard(KeyboardProc); /* Parse command line, handle default hotkeys, and show messages. */ DXUTInit(); DXUTCreateWindow(myProgramNameW); /* Display 400x400 window. */ DXUTCreateDevice(D3DADAPTER_DEFAULT, true, 400, 400); DXUTMainLoop(); cgDestroyProgram(myCgVertexProgram); checkForCgError("destroying vertex program"); cgDestroyProgram(myCgFragmentProgram); checkForCgError("destroying fragment program"); cgDestroyContext(myCgContext); return DXUTGetExitCode(); } static void createCgPrograms() { const char **profileOpts; /* Determine the best profiles once a device to be set. */ myCgVertexProfile = cgD3D9GetLatestVertexProfile(); checkForCgError("getting latest vertex profile"); profileOpts = cgD3D9GetOptimalOptions(myCgVertexProfile); checkForCgError("getting latest vertex profile options"); myCgVertexProgram = cgCreateProgramFromFile( myCgContext, /* Cg runtime context */ CG_SOURCE, /* Program in human-readable form */ myVertexProgramFileName, /* Name of file containing program */ myCgVertexProfile, /* Profile: OpenGL ARB vertex program */ myVertexProgramName, /* Entry function name */ profileOpts); /* Pass optimal compiler options */ checkForCgError("creating vertex program from file"); myCgVertexParam_leftSeparation = cgGetNamedParameter(myCgVertexProgram, "leftSeparation"); checkForCgError("could not get leftSeparation parameter"); myCgVertexParam_rightSeparation = cgGetNamedParameter(myCgVertexProgram, "rightSeparation"); checkForCgError("could not get rightSeparation parameter"); /* Determine the best profile once a device to be set. */ myCgFragmentProfile = cgD3D9GetLatestPixelProfile(); checkForCgError("getting latest fragment profile"); profileOpts = cgD3D9GetOptimalOptions(myCgFragmentProfile); checkForCgError("getting latest fragment profile options"); myCgFragmentProgram = cgCreateProgramFromFile( myCgContext, /* Cg runtime context */ CG_SOURCE, /* Program in human-readable form */ myFragmentProgramFileName, /* Name of file containing program */ myCgFragmentProfile, /* Profile: OpenGL ARB vertex program */ myFragmentProgramName, /* Entry function name */ profileOpts); /* No extra compiler options */ checkForCgError("creating fragment program from file"); myCgFragmentParam_decal = cgGetNamedParameter(myCgFragmentProgram, "decal"); checkForCgError("getting decal parameter"); } struct MY_V3F_T2F { FLOAT x, y, z; FLOAT s, t; }; static HRESULT initVertexBuffer(IDirect3DDevice9* pDev) { /* Initialize three vertices for rendering a triangle. */ static const MY_V3F_T2F triangleVertices[] = { { -0.8f, 0.8f, 0.0f, 0, 0 }, /* st=(0,0) */ { 0.8f, 0.8f, 0.0f, 1, 0 }, /* st=(1,0) */ { 0.0f, -0.8f, 0.0f, 0.5f, 1 } /* st=(0.5,1)*/ }; if (FAILED(pDev->CreateVertexBuffer(sizeof(triangleVertices), 0, D3DFVF_XYZ|D3DFVF_TEX1, D3DPOOL_DEFAULT, &myVertexBuffer, NULL))) { return E_FAIL; } void* pVertices; if (FAILED(myVertexBuffer->Lock(0, 0, /* map entire buffer */ &pVertices, 0))) { return E_FAIL; } memcpy(pVertices, triangleVertices, sizeof(triangleVertices)); myVertexBuffer->Unlock(); return S_OK; } static HRESULT initTexture(IDirect3DDevice9* pDev) { D3DLOCKED_RECT lockedRect; if (FAILED(pDev->CreateTexture(128, 128, 0, D3DUSAGE_AUTOGENMIPMAP, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &myTexture, NULL))) { return E_FAIL; } if (FAILED(myTexture->LockRect(0, &lockedRect, 0, 0))) { return E_FAIL; } DWORD *texel = (DWORD*) lockedRect.pBits; for (int i=0; i<128*128*3; i+=3) { *texel++ = myDemonTextureImage[i+0] << 16 | myDemonTextureImage[i+1] << 8 | myDemonTextureImage[i+2]; } myTexture->UnlockRect(0); myTexture->GenerateMipSubLevels(); return S_OK; } HRESULT CALLBACK OnResetDevice(IDirect3DDevice9* pDev, const D3DSURFACE_DESC* backBuf, void* userContext) { cgD3D9SetDevice(pDev); checkForCgError("setting Direct3D device"); static int firstTime = 1; if (firstTime) { /* Cg runtime resources such as CGprogram and CGparameter handles survive a device reset so we just need to compile a Cg program just once. We do however need to unload Cg programs with cgD3DUnloadProgram upon when a Direct3D device is lost and load Cg programs every Direct3D device reset with cgD3D9UnloadProgram. */ createCgPrograms(); firstTime = 0; } /* false below means "with parameter shadowing" */ cgD3D9LoadProgram(myCgVertexProgram, false, 0); checkForCgError("loading vertex program"); /* false below means "with parameter shadowing" */ cgD3D9LoadProgram(myCgFragmentProgram, false, 0); checkForCgError("loading fragment program"); if (FAILED(initVertexBuffer(pDev))) { return E_FAIL; } if (FAILED(initTexture(pDev))) { return E_FAIL; } return S_OK; } static void CALLBACK OnFrameRender(IDirect3DDevice9* pDev, double time, float elapsedTime, void* userContext) { pDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(0.1f, 0.3f, 0.6f, 0.0f), 1.0f, 0); if (SUCCEEDED(pDev->BeginScene())) { if (mySeparation > 0) { /* Separate in the horizontal direction. */ cgSetParameter2f(myCgVertexParam_leftSeparation, -mySeparation, 0); cgSetParameter2f(myCgVertexParam_rightSeparation, mySeparation, 0); } else { /* Separate in the vertical direction. */ cgSetParameter2f(myCgVertexParam_leftSeparation, 0, -mySeparation); cgSetParameter2f(myCgVertexParam_rightSeparation, 0, mySeparation); } cgD3D9BindProgram(myCgVertexProgram); checkForCgError("binding vertex program"); cgD3D9SetTexture(myCgFragmentParam_decal, myTexture); cgD3D9BindProgram(myCgFragmentProgram); checkForCgError("binding fragment program"); /* Render the stars. */ pDev->SetStreamSource(0, myVertexBuffer, 0, sizeof(MY_V3F_T2F)); pDev->SetFVF(D3DFVF_XYZ|D3DFVF_TEX1); pDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); pDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); pDev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); pDev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); pDev->EndScene(); } } static void CALLBACK OnFrameMove(IDirect3DDevice9* pDev, double time, float elapsedTime, void* userContext) { if (myAnimating) { if (mySeparation > 0.4f) { mySeparationVelocity = -0.005f; } else if (mySeparation < -0.4f) { mySeparationVelocity = 0.005f; } mySeparation += mySeparationVelocity; } } static void CALLBACK KeyboardProc(UINT nChar, bool keyDown, bool altDown, void* userContext) { if (keyDown) { switch (nChar) { case ' ': myAnimating = !myAnimating; break; } } } static void CALLBACK OnLostDevice(void* userContext) { myVertexBuffer->Release(); myTexture->Release(); cgD3D9SetDevice(NULL); }