#include "dxstdafx.h" #include ".\pathmapeffect.h" #include "Material.hpp" #include "TexturedMaterial.h" #include "WoodMaterial.hpp" #include "TriangleMesh.h" #include "Transformed.h" #include "Entity.h" #include "DepthRenderStrategy.h" #include "FinalCompositionRenderStrategy.h" #include "SubEntity.h" #include "Entity.h" #include "Mesh.h" #include "L.h" #include "shlwapi.h" #include #include #define MAXNCLUSTERS 512 //#define GENERATE_PATH_MAPS //! D3D vector-matrix multiplication D3DVECTOR operator *(const D3DVECTOR& v, const D3DMATRIX& m) { D3DVECTOR r; r.x = v.x * m._11 + v.y * m._21 + v.z * m._31 + m._41; r.y = v.x * m._12 + v.y * m._22 + v.z * m._32 + m._42; r.z = v.x * m._13 + v.y * m._23 + v.z * m._33 + m._43; float h = v.x * m._14 + v.y * m._24 + v.z * m._34 + m._44; if(h > 0.00001f || h < 0.00001f) { r.x /= h; r.y /= h; r.z /= h; } return r; } D3DXVECTOR3 operator *(const D3DXVECTOR3& v, const D3DXMATRIX& m) { D3DXVECTOR3 r; r.x = v.x * m._11 + v.y * m._21 + v.z * m._31 + m._41; r.y = v.x * m._12 + v.y * m._22 + v.z * m._32 + m._42; r.z = v.x * m._13 + v.y * m._23 + v.z * m._33 + m._43; float h = v.x * m._14 + v.y * m._24 + v.z * m._34 + m._44; if(h > 0.00001f || h < 0.00001f) { r.x /= h; r.y /= h; r.z /= h; } return r; } //! method name strings to be displayed on screen const wchar_t* PathMapEffect::Method::methodNames[10] = { L"PRM", L"PRM texture", L"_", L"_" , NULL, NULL, NULL, NULL, NULL, NULL }; //! method constants const PathMapEffect::Method PathMapEffect::Method::PRM(0); const PathMapEffect::Method PathMapEffect::Method::SHOWTEX(1); const PathMapEffect::Method PathMapEffect::Method::LAST(2); //! constructor PathMapEffect::PathMapEffect(LPDIRECT3DDEVICE9 device, char* prmDirectory, char* meshDirectory, char* mediaDirectory, char* levelFileName, char* materialFileName, bool segmentMeshes, bool computePRM, bool uniformSampling, bool atlasGen, unsigned int nEntryPoints, unsigned int nClusters, unsigned int depthMapResolution ) :method(Method::PRM) { cruiseLoop = 0; loopPos = 0.0; D3DXMatrixIdentity(&shipPos); testClusterId = 0; NRADIONS = 4096 * (nEntryPoints / 4096); NCLUSTERS = nClusters; DEPTHMAPRES = depthMapResolution; this->SEGMENTMESHES = segmentMeshes; this->UNIFORMSAMPLING = uniformSampling; weights = new float[NCLUSTERS]; clusterLenghts = new unsigned int[NCLUSTERS]; clusterSweepCurrentIndex = 0; rayId = 1; this->device = device; //first person camera allows more freedom of movement camera = new CFirstPersonCamera(); lightCamera = new CFirstPersonCamera(); HRESULT hr; DWORD effectCompileFlag=0; LPD3DXBUFFER compilationErrors; if(FAILED( hr = D3DXCreateEffectFromFile( device, L"pathMap.fx", NULL, NULL, 0, NULL, &effect, &compilationErrors) )){ MessageBoxA( NULL, (LPSTR)compilationErrors->GetBufferPointer(), "Failed to load effect file!", MB_OK); exit(-1); } //store buffers so we can reset them after rendering to texture render targets device->GetRenderTarget(0, &frameColorBuffer); device->GetDepthStencilSurface(&frameDepthStencilBuffer); wcscpy(this->mediaDirectory, L::l+mediaDirectory); wcscpy(this->meshDirectory, L::l+meshDirectory); wcscpy(this->prmDirectory, L::l+prmDirectory); //load empty texture wcscpy(this->levelFileName, L::l+mediaDirectory); wcscat(this->levelFileName, L"\\"); wcscat(this->levelFileName, L::l+levelFileName); wcscpy(this->materialFileName, L::l+mediaDirectory); wcscat(this->materialFileName, L"\\"); wcscat(this->materialFileName, L::l+materialFileName); wchar_t emptyTextureName[256]; wcscpy(emptyTextureName, L::l+mediaDirectory); wcscat(emptyTextureName, L"\\"); wcscat(emptyTextureName, L"empty.bmp"); D3DXCreateTextureFromFile(device, emptyTextureName, &emptyTexture); D3DXCreateTextureFromFile(device, L"media\\fighter.jpg", &shipBrdfTexture); D3DXLoadMeshFromX(L"media\\fighter.x", D3DXMESH_MANAGED, device, NULL, //adjacency NULL, //material NULL, //shader NULL, &shipMesh); //set up a scene xMaterials = XMLNode::openFileHelper(this->materialFileName, L"materials"); // loadScene("media\\space.txt"); loadScene(LC::c-this->levelFileName); // loadMesh( L"media\\fighter.x", 0, "", 1); //compute the surface area of the complete geometry. useful for random sampling. sumSurfaceArea = 0.0; std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { sumSurfaceArea += (*entityIterator)->getSurfaceArea(); entityIterator++; } //initialize camera // camera->SetViewParams( &D3DXVECTOR3(0.0f, 0.0f, 4.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f)); camera->SetViewParams( &D3DXVECTOR3(0.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 0.0f, 1.0f)); D3DSURFACE_DESC fbdesc; frameColorBuffer->GetDesc(&fbdesc); camera->SetProjParams( D3DX_PI/2, (float)fbdesc.Width / fbdesc.Height, 0.1f, 300.0f ); //set up spotlight lightCamera->SetViewParams( &D3DXVECTOR3(5.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f)); lightCamera->SetProjParams( D3DX_PI/1.9, 1.0f, 0.1f, 300.0f ); //create ray tracing kd-tree containing ray-traceable versions of entities { std::vector rayTraceEntities; std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->gatherRayTraceEntities(rayTraceEntities); entityIterator++; } Intersectable** objs = (Intersectable**)&*rayTraceEntities.begin(); kdtree = new KDTree(objs, rayTraceEntities.size()); } //create dummy render target texture for depth map rendering device->CreateTexture(DEPTHMAPRES, DEPTHMAPRES, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &fakeTexture, NULL); fakeTexture->GetSurfaceLevel(0, &fakeSurface); //create a depthstencil texture for depth map rendering device->CreateTexture(DEPTHMAPRES, DEPTHMAPRES, 1, D3DUSAGE_DEPTHSTENCIL, D3DFMT_D24X8, D3DPOOL_DEFAULT, &depthMapTexture, NULL); depthMapTexture->GetSurfaceLevel(0, &depthMapDepthStencilBuffer); if(computePRM) precompute(); else { loadPathMaps(); } if(computePRM || segmentMeshes) saveScene("processedMeshes\\processed.level"); //create a texture for radion data (will be used for weight computations on the GPU) device->CreateTexture(2 * NRADIONS / 4096, 4096, 1, 0, D3DFMT_A32B32G32R32F, D3DPOOL_DEFAULT, &radionTexture, NULL); radionTexture->GetSurfaceLevel(0, &radionSurface); //fill texture with data uploadRadions(); //create weights render target device->CreateTexture(4096, NRADIONS / 4096, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &weightsTexture, NULL); weightsTexture->GetSurfaceLevel(0, &weightsSurface); //create a sytem memory duplicate of the weights render target to be able to read back weights data device->CreateTexture(4096, NRADIONS / 4096, 1, 0, D3DFMT_R32F, D3DPOOL_SYSTEMMEM, &sysMemWeightsTexture, NULL); sysMemWeightsTexture->GetSurfaceLevel(0, &sysMemWeightsSurface); //create aggr weights render target device->CreateTexture(MAXNCLUSTERS, 1, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &aggrWeightsTexture, NULL); aggrWeightsTexture->GetSurfaceLevel(0, &aggrWeightsSurface); //create a vertex buffer to be able to visualize entry radion positions device->CreateVertexBuffer(NRADIONS * sizeof(float) * 6, D3DUSAGE_WRITEONLY, D3DFVF_XYZ | D3DFVF_NORMAL, D3DPOOL_DEFAULT, &starterVertexBuffer, NULL); void* pData; starterVertexBuffer->Lock(0, 0, &pData, 0); fillRadionPosArray(pData); starterVertexBuffer->Unlock(); if(computePRM) savePathMaps(); } PathMapEffect::~PathMapEffect(void) { shipMesh->Release(); shipBrdfTexture->Release(); delete weights; delete clusterLenghts; //release all resources allocated in the constructor starterVertexBuffer->Release(); depthMapTexture->Release(); depthMapDepthStencilBuffer->Release(); fakeTexture->Release(); fakeSurface->Release(); sysMemWeightsTexture->Release(); sysMemWeightsSurface->Release(); weightsTexture->Release(); weightsSurface->Release(); aggrWeightsTexture->Release(); aggrWeightsSurface->Release(); radionTexture->Release(); radionSurface->Release(); delete kdtree; emptyTexture->Release(); releaseTextures(); releaseMeshes(); releaseEntities(); frameColorBuffer->Release(); frameDepthStencilBuffer->Release(); effect->Release(); delete camera; delete lightCamera; } void PathMapEffect::loadMesh(DWORD fileType, LPCWSTR fileName, LPCWSTR ogreName, int prmAtlasSize, const char* name, int dividePcs, bool generateUV, bool generateTBN, unsigned int originalAtlasTexCoordIndex) { wchar_t mFileName[512]; wcscpy(mFileName, this->mediaDirectory); wcscat(mFileName, L"\\"); wcscat(mFileName, fileName); Mesh* mesh = new Mesh(this, fileType, mFileName, ogreName, prmAtlasSize, name, dividePcs, generateUV, generateTBN, originalAtlasTexCoordIndex); meshes.push_back(mesh); } LPDIRECT3DTEXTURE9 PathMapEffect::loadTexture(LPCWSTR fileName, Material** rayTraceMaterial) { if(fileName == NULL || wcscmp(fileName, L"") == 0) return NULL; int texIndex = 0; std::vector::iterator nameIterator = materialTextureFileNames.begin(); while(nameIterator != materialTextureFileNames.end()) { if(wcscmp( *nameIterator, fileName) == 0) { *rayTraceMaterial = rayTraceMaterials.at(texIndex); return materialTextures.at(texIndex); } nameIterator++; texIndex++; } wchar_t* mediaFileName = new wchar_t[wcslen(fileName) + 64]; //we assume the x file is the media folder wcscpy(mediaFileName, this->mediaDirectory); wcscat(mediaFileName, L"\\"); wcscat(mediaFileName, fileName); LPDIRECT3DTEXTURE9 tex; D3DXIMAGE_INFO bobo; if(S_OK != D3DXCreateTextureFromFileEx(device, mediaFileName, D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_FROM_FILE, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, &bobo, NULL, &tex )) { MessageBox(NULL, mediaFileName, L"Could not load texture file!", MB_OK); return NULL; } delete mediaFileName; materialTextures.push_back(tex); wchar_t* storedName = new wchar_t[wcslen(fileName)+1]; wcscpy(storedName, fileName); materialTextureFileNames.push_back(storedName); if(rayTraceMaterial != NULL) { D3DLOCKED_RECT lrect; HRESULT hr = tex->LockRect(0, &lrect, NULL, D3DLOCK_READONLY); LPDIRECT3DSURFACE9 texsurf; tex->GetSurfaceLevel(0, &texsurf); D3DSURFACE_DESC desc; texsurf->GetDesc(&desc); *rayTraceMaterial = new TexturedMaterial(lrect.pBits, lrect.Pitch, desc.Width, desc.Height); texsurf->Release(); tex->UnlockRect(0); } rayTraceMaterials.push_back(*rayTraceMaterial); return tex; } void PathMapEffect::releaseTextures() { std::vector::iterator i = materialTextures.begin(); while(i != materialTextures.end()) { (*i)->Release(); i++; } std::vector::iterator z = rayTraceMaterials.begin(); while(z != rayTraceMaterials.end()) { delete (*z); z++; } std::vector::iterator c = materialTextureFileNames.begin(); while(c != materialTextureFileNames.end()) { delete [] (*c); c++; } } void PathMapEffect::releaseMeshes() { std::vector::iterator i = meshes.begin(); while(i != meshes.end()) { delete *i; i++; } } void PathMapEffect::renderWithPRM() { // 1. pass: render depth DepthRenderStrategy depthRenderStrategy(this); depthRenderStrategy.camera = *lightCamera; renderScene(depthRenderStrategy); // 2. pass: compute weights // simple parallel computation for all pixels: no 3D, depth, shadows, only a full-texture quad HRESULT hr = device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); hr = device->SetRenderState(D3DRS_STENCILENABLE, false); hr = device->SetRenderTarget(0,weightsSurface); hr = device->SetDepthStencilSurface(NULL); D3DXVECTOR3 lightPos = *lightCamera->GetEyePt(); D3DXVECTOR3 lightDir = *lightCamera->GetWorldAhead(); lightDir /= D3DXVec3Length( &lightDir ); float ffl = parameters->Get(fLightScale) * 100.0f; D3DXVECTOR3 lightPower(ffl, ffl, ffl); device->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); if( SUCCEEDED( device->BeginScene() ) ){ if( effect != NULL ){ D3DXHANDLE hTechnique = NULL; hTechnique = effect->GetTechniqueByName((LPSTR)"ComputeWeights"); if(hTechnique==NULL){ return; } effect->SetTechnique( hTechnique ); UINT nPasses = 0; effect->Begin( &nPasses, 0 ); for(UINT j=0;jBeginPass(j); hr = effect->SetTexture("radions", radionTexture); static float opttme[9] = {0.5f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.5f + (0.5f / DEPTHMAPRES), 0.5f + (0.5f / DEPTHMAPRES), 1.0f}; //set global params effect->SetFloatArray("occProjToTexMatrix", opttme, 9); if(parameters->Get(bCruise)) effect->SetMatrix("occWorldToProjMatrix", &(shipPosInverse * *lightCamera->GetProjMatrix())); else effect->SetMatrix("occWorldToProjMatrix", &(*lightCamera->GetViewMatrix() * *lightCamera->GetProjMatrix())); effect->SetTexture("depthMap", depthMapTexture); hr = effect->SetInt("nRadionColumns", NRADIONS / 4096); hr = effect->SetFloatArray("lightPower", (float*)&lightPower, 3); hr = effect->SetFloatArray("lightPos", (float*)&lightPos, 3); hr = effect->SetFloatArray("lightDir", (float*)&lightDir, 3); effect->CommitChanges(); renderFullScreen(); effect->EndPass(); } effect->End(); } device->EndScene(); } // read the weights back D3DLOCKED_RECT rData; hr = device->GetRenderTargetData(weightsSurface, sysMemWeightsSurface); hr = sysMemWeightsSurface->LockRect(&rData, NULL, D3DLOCK_READONLY); float* allEntryWeights = (float*)rData.pBits; // average weights per cluster float sumWeightsAll = 0.0; unsigned int iRadion = 0; for(int iCluster=0; iCluster < NCLUSTERS; iCluster++) { weights[iCluster] = 0.0; float clusterrad = 0.0; for(int clusterSummer=0; clusterSummer < clusterLenghts[iCluster]; clusterSummer++, iRadion++) { float radrad = bushStarters[iRadion].radiance.sum(); weights[iCluster] += allEntryWeights[iRadion] * radrad; clusterrad += radrad; } if(clusterrad > 0.01) weights[iCluster] /= clusterrad; //(double)clusterLenghts[iCluster]; else weights[iCluster] = 0.0f; // if(iCluster != TEST_CLUST) // weights[iCluster] = 0.0; // if(weights[iCluster] > 0.005) // weights[iCluster] = 0.005; sumWeightsAll += weights[iCluster]; } sysMemWeightsSurface->UnlockRect(); // use backface culling, depth test hr = device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); hr = device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); /* for(int testi=0; testi < NCLUSTERS; testi++) weights[testi] = 0.000; weights[testClusterId] = 0.05;*/ // 3. pass: render scene using weights to combine PRM texture atlases FinalCompositionRenderStrategy finalCompositionRenderStrategy(this); finalCompositionRenderStrategy.lightDir = lightDir; finalCompositionRenderStrategy.lightPos = lightPos; finalCompositionRenderStrategy.lightPower = lightPower; renderScene(finalCompositionRenderStrategy); if(!parameters->Get(bLookFromLight)) if( SUCCEEDED( device->BeginScene() ) ) { effect->SetTechnique("torch"); UINT nPasses; effect->Begin(&nPasses, 0); effect->BeginPass(0); D3DXMATRIX scaleShip; D3DXMatrixScaling(&scaleShip, 0.02f, 0.02f, 0.02f); effect->SetTexture("brdfMap", shipBrdfTexture); effect->SetMatrix("modelToProjMatrix", &(scaleShip * shipPos * *camera->GetViewMatrix() * *camera->GetProjMatrix() )); effect->CommitChanges(); shipMesh->DrawSubset(0); effect->EndPass(); effect->End(); device->EndScene(); } } void PathMapEffect::renderFullScreen(float depth) { float fLeftU = 0.0f, fTopV = 0.0f, fRightU = 1.0f, fBottomV = 1.0f; D3DSURFACE_DESC dtdsdRT; PDIRECT3DSURFACE9 pSurfRT; // Acquire render target width and height device->GetRenderTarget(0, &pSurfRT); pSurfRT->GetDesc(&dtdsdRT); pSurfRT->Release(); // Ensure that we're directly mapping texels to pixels by offset by 0.5 // For more info see the doc page titled "Directly Mapping Texels to Pixels" FLOAT fWidth5 = (FLOAT)dtdsdRT.Width - 0.5f; FLOAT fHeight5 = (FLOAT)dtdsdRT.Height - 0.5f; // Draw the quad struct D3DVERTEXQUAD{ D3DXVECTOR3 pos; D3DXVECTOR2 tex0; }; D3DVERTEXQUAD svQuad[4]; svQuad[0].pos=D3DXVECTOR3(-1, 1, depth); svQuad[0].tex0 = D3DXVECTOR2(fLeftU, fTopV); svQuad[1].pos = D3DXVECTOR3(1, 1, depth); svQuad[1].tex0 = D3DXVECTOR2(fRightU, fTopV); svQuad[2].pos = D3DXVECTOR3(-1, -1, depth); svQuad[2].tex0 = D3DXVECTOR2(fLeftU, fBottomV); svQuad[3].pos = D3DXVECTOR3(1, -1, depth); svQuad[3].tex0 = D3DXVECTOR2(fRightU, fBottomV); device->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE); device->SetFVF(D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)); device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, svQuad, sizeof(D3DVERTEXQUAD)); } void PathMapEffect::move(float fElapsedTime) { clusterSweepCurrentIndex += fElapsedTime * 2.0f; if(clusterSweepCurrentIndex > NCLUSTERS) clusterSweepCurrentIndex -= NCLUSTERS; loopPos += fElapsedTime * 0.3; if(loopPos > Vector::PI) { loopPos -= Vector::PI; if(cruiseLoop == 1) cruiseLoop = 0; else cruiseLoop = 1; /* if(cruiseLoop == 1 || cruiseLoop == 3 || cruiseLoop == 5) { cruiseLoop = (rand() % 3) * 2; } else if(cruiseLoop == 0 || cruiseLoop == 8) { cruiseLoop = 1 + (rand() % 2) * 5; } else if(cruiseLoop == 4 || cruiseLoop == 6) { cruiseLoop = 5 + (rand() % 2) * 2; } else if(cruiseLoop == 2 || cruiseLoop == 7) { cruiseLoop = 3 + (rand() % 2) * 5; }*/ } D3DXMATRIX mm, markerPos; D3DXMatrixIdentity(&markerPos); /* if(cruiseLoop < 6) { //to loop D3DXMatrixRotationAxis(&mm, &D3DXVECTOR3(0, 1, 0), (cruiseLoop / 2) * Vector::PI * 0.66666666666); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //pull in D3DXMatrixTranslation(&mm, 17.0, 0, 0); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //orbit D3DXMatrixRotationAxis(&mm, &D3DXVECTOR3(0, 0, 1), (cruiseLoop%2 != 0)?(loopPos + Vector::PI):loopPos); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //push out D3DXMatrixTranslation(&mm, -17.0, 0, 0); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //raise D3DXMatrixTranslation(&mm, 4.0, 0, 0); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //turn nose D3DXMatrixRotationAxis(&mm, &D3DXVECTOR3(1, 0, 0), Vector::PI / 2.0); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); } else { //orbit D3DXMatrixRotationAxis(&mm, &D3DXVECTOR3(0, 1, 0), -(cruiseLoop - 6) %3 * Vector::PI * 0.6666666666 - loopPos * 0.666666666666666); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //push out D3DXMatrixTranslation(&mm, 30.0, 0, 0); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); //turn nose // D3DXMatrixRotationAxis(&mm, &D3DXVECTOR3(0, 1, 0), Vector::PI / 2.0); // D3DXMatrixMultiply(&markerPos, &mm, &markerPos); } */ D3DXMatrixTranslation(&markerPos, loopPos * 30.0 - 20.0, 5.0, 0); //turn nose D3DXMatrixRotationAxis(&mm, &D3DXVECTOR3(0, 1, 0), Vector::PI / 2.0); D3DXMatrixMultiply(&markerPos, &mm, &markerPos); shipPos = markerPos;// * 0.05 + shipPos * 0.95; D3DXMatrixLookAtLH(&shipPosInverse, &(D3DXVECTOR3(0, 0, 0) * shipPos), &(D3DXVECTOR3(0, 0, 1) * shipPos), &(D3DXVECTOR3(0, 1, 0) * shipPos - D3DXVECTOR3(0, 0, 0) * shipPos)); D3DXMatrixInverse(&shipPos, NULL, &shipPosInverse); if(parameters->Get(bCruise)) { lightCamera->SetViewParams( &(D3DXVECTOR3(0, 0, 0) * shipPos), &(D3DXVECTOR3(0, 0, 1) * shipPos) ); } if(parameters->Get(bTurbo)) { camera->FrameMove(fElapsedTime * 10.0f); lightCamera->FrameMove(fElapsedTime * 10.0f); } else { camera->FrameMove(fElapsedTime); lightCamera->FrameMove(fElapsedTime); } } LRESULT PathMapEffect::handleMessage( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg == WM_KEYDOWN) { switch(wParam) { case 'C': testClusterId = (testClusterId + NCLUSTERS - 1) % NCLUSTERS; break; case 'Z': testClusterId = (testClusterId + 1) % NCLUSTERS; break; } } if(parameters->Get(bMoveLight)) return lightCamera->HandleMessages(hWnd, uMsg, wParam, lParam); else return camera->HandleMessages(hWnd, uMsg, wParam, lParam); } void PathMapEffect::addUiParameters(Parameters* parameters) { PathMapEffect::parameters = parameters; parameters->Add( bLookFromLight, "Look from light", 1); parameters->Add( bMoveLight, "Move light", 1); parameters->Add( bDots, "Entry points", 1); parameters->Add( bTurbo, "Turbo", 1); parameters->Add( bCruise, "Cruise", 1); parameters->Add( fLightScale, "Tone scale", 100, '1', '2', convertLightScale); parameters->Add( fIndirectLightingMode, "Lighting mode", 3, '3', '4', convertInt4); parameters->Add( fTorchDistance, "Torch distance", 100, '5', '6', convertTorchDistance); } void PathMapEffect::setUiParameterDefaults(Parameters* parameters) { } Parameters* PathMapEffect::parameters = NULL; void PathMapEffect::render() { renderWithPRM(); if(parameters->Get(bDots)) { float psf = 8.0f; HRESULT hr = device->SetRenderState(D3DRS_POINTSIZE, *((DWORD*)&psf)); hr = device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); if( SUCCEEDED( device->BeginScene() ) ) { D3DXHANDLE hTechnique = NULL; hTechnique = effect->GetTechniqueByName((LPSTR)"Dots"); effect->SetTechnique( hTechnique ); UINT nPasses = 0; effect->Begin( &nPasses, 0 ); for(UINT j=0;jBeginPass(j); hr = effect->SetTexture("depthMap",depthMapTexture); if(parameters->Get(bLookFromLight)) { if(parameters->Get(bCruise)) effect->SetMatrix("worldToProjMatrix", &(shipPosInverse * *lightCamera->GetProjMatrix() )); else effect->SetMatrix("worldToProjMatrix", &(*lightCamera->GetViewMatrix() * *lightCamera->GetProjMatrix() )); } else effect->SetMatrix("worldToProjMatrix", &(*camera->GetViewMatrix() * *camera->GetProjMatrix() )); static float opttme[9] = {0.5f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.5f + (0.5f / DEPTHMAPRES), 0.5f + (0.5f / DEPTHMAPRES), 1.0f}; effect->SetFloatArray("occProjToTexMatrix", opttme, 9); D3DXVECTOR3 lightPos = *lightCamera->GetEyePt(); hr = effect->SetFloatArray("lightPos", (float*)&lightPos, 3); effect->CommitChanges(); hr = device->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL); hr = device->SetStreamSource(0, starterVertexBuffer, 0, sizeof(float) * 6); UINT offset = 0; for(int iClusterDraw=0; iClusterDraw < NCLUSTERS; iClusterDraw++) { // hr = effect->SetFloat("aClusterWeight", weights[iClusterDraw] * 10.0); hr = effect->SetFloat("aClusterWeight", (float)iClusterDraw); effect->CommitChanges(); // if(iClusterDraw - clusterSweepCurrentIndex > -1.0 && // iClusterDraw - clusterSweepCurrentIndex < 0.0 ) // if(iClusterDraw == testClusterId) device->DrawPrimitive(D3DPT_POINTLIST, offset, clusterLenghts[iClusterDraw]); offset += clusterLenghts[iClusterDraw]; } effect->EndPass(); } effect->End(); } device->EndScene(); } } void PathMapEffect::releaseEntities() { std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { delete *entityIterator; entityIterator++; } } void PathMapEffect::sampleSphereDirection(Vector& outDir) { do{ outDir = Vector(-1.0 + 2.0 * (float)rand() / RAND_MAX, -1.0 + 2.0 * (float)rand() / RAND_MAX, -1.0 + 2.0 * (float)rand() / RAND_MAX); }while(outDir.norm2() > 1.0); outDir.normalize(); } void PathMapEffect::sampleShootingDiffuseDirection(int depth, const Vector& normal, Vector& outDir) { Vector u, v; if(fabsf(normal[0]) < 0.9f) { u.set(0.0f, -normal[2], normal[1]); u *= 1.0f / sqrtf(normal[2] * normal[2] + normal[1] * normal[1]); } else { u.set(normal[2], 0.0f, -normal[0]); u *= 1.0f / sqrtf(normal[2] * normal[2] + normal[0] * normal[0]); } v.setCrossProduct(normal, u); float phi = 2.0f * 3.14159265358979323846f * (float)rand() / RAND_MAX; float xi2 = (float)rand() / RAND_MAX; float cosPhi = cos(phi); float sinPhi = sin(phi); float cosTheta = sqrtf(xi2); float sinTheta = sqrtf(1.0f - xi2); outDir.setScaled(sinTheta * cosPhi, v); outDir.addScaled(sinTheta * sinPhi, u); outDir.addScaled(cosTheta, normal); } void PathMapEffect::precompute() { // generate entry points for(unsigned int cRad=0; cRad < NRADIONS; cRad++) { Radion starter; if(UNIFORMSAMPLING) sampleSurfaceRadionUniform(starter); else sampleSurfaceRadion(starter); bushStarters.push_back(starter); } // sort entry radions into clusters Radion* entryArray = (Radion*)&*bushStarters.begin(); clusterRadions(entryArray, bushStarters.size(), 0); clusterRadionsKMeans(); // for every entity, find the most relevant clusters srand(0xef23ab32); std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->findNearClusters(bushStarters, NCLUSTERS); (*entityIterator)->clearPRM(); entityIterator++; } // for every entry radion, shoot a bush of radions, and add their // contributions to all PRMs of all entities unsigned int iCluster = 0; std::vector bushRadions; for(int iStarter = 0; iStarter < NRADIONS; ) { bushRadions.clear(); for(int iric=0; iric < clusterLenghts[iCluster]; iric++, iStarter++) shootRadionBush(bushStarters[iStarter], bushRadions); std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->renderPRM(bushRadions, iCluster); entityIterator++; } iCluster++; } //scale down multiple pixel writes entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->normalizePRM(); entityIterator++; } } int compareX(const void* a, const void* b) { if( ((const Radion*)a)->position.x < ((const Radion*)b)->position.x ) return -1; else return 1; } int compareY(const void* a, const void* b) { if( ((const Radion*)a)->position.y < ((const Radion*)b)->position.y ) return -1; else return 1; } int compareZ(const void* a, const void* b) { if( ((const Radion*)a)->position.z < ((const Radion*)b)->position.z ) return -1; else return 1; } int compareDirX(const void* a, const void* b) { if( ((const Radion*)a)->normal.x < ((const Radion*)b)->normal.x ) return -1; else return 1; } int compareDirY(const void* a, const void* b) { if( ((const Radion*)a)->normal.y < ((const Radion*)b)->normal.y ) return -1; else return 1; } int compareDirZ(const void* a, const void* b) { if( ((const Radion*)a)->normal.z < ((const Radion*)b)->normal.z ) return -1; else return 1; } void PathMapEffect::clusterRadionsKMeans() { Radion centroids[MAXNCLUSTERS]; std::vector clusters[MAXNCLUSTERS]; //initial clusters: uniform length for(unsigned int i=0; i> 1; clusterLenghts[i-1] -= clusterLenghts[i-1] >> 1; } } for(int i=NCLUSTERS - 1; i >= 0; i--) { if(clusterLenghts[i] == 0) { clusterLenghts[i] = clusterLenghts[i+1] >> 1; clusterLenghts[i+1] -= clusterLenghts[i+1] >> 1; } } } } int PathMapEffect::clusterRadions(Radion* partition, int psize, char axis) { /* if(psize < 2) return 1; if(axis == 0) qsort(partition, psize, sizeof(Radion), compareDirX); else if(axis == 1) qsort(partition, psize, sizeof(Radion), compareDirY); else if(axis == 2) qsort(partition, psize, sizeof(Radion), compareDirZ); else if(axis == 3) qsort(partition, psize, sizeof(Radion), compareX); else if(axis == 4) qsort(partition, psize, sizeof(Radion), compareY); else if(axis == 5) qsort(partition, psize, sizeof(Radion), compareZ); clusterRadions(partition, psize >> 1, (axis+1)%6); clusterRadions(partition + (psize >> 1), psize - (psize >> 1), (axis+1)%6); return 1; /*/ if(psize < 2) return 1; if(axis == 0) qsort(partition, psize, sizeof(Radion), compareX); else if(axis == 1) qsort(partition, psize, sizeof(Radion), compareY); else if(axis == 2) qsort(partition, psize, sizeof(Radion), compareZ); clusterRadions(partition, psize >> 1, (axis+1)%3); clusterRadions(partition + (psize >> 1), psize - (psize >> 1), (axis+1)%3); return 1;//*/ } void PathMapEffect::shootRadionBush(Radion& starter, std::vector& bushRadions) { bushRadions.push_back(starter); int nPhotonsShotForLight = 1; //branching factor for(int iPhoton = 0; iPhoton < nPhotonsShotForLight; iPhoton++) { unsigned int depth = 0; ray.id = rayId++; ray.isShadowRay = false; ray.origin = starter.position; Vector power = starter.radiance; float prob = starter.probability; prob *= nPhotonsShotForLight; power *= 1.0f / nPhotonsShotForLight; sampleShootingDiffuseDirection(depth, starter.normal, ray.dir); for(;;depth++) { kdtree->traverse(ray, hitRec, 0.0f, FLT_MAX); if(hitRec.isIntersect) { Radion nr; nr.position = hitRec.point; nr.normal = hitRec.normal; nr.radiance = power; nr.radiance %= hitRec.material->getTextureDiffuseBrdf(hitRec.texUV); nr.probability = prob; bushRadions.push_back(nr); } else break; if(depth >= 3) break; float rrRandom = (float)rand() / RAND_MAX; float diffuseAlbedo = hitRec.material->getTextureDiffuseAlbedo(hitRec.texUV); float idealAlbedo = hitRec.material->getTextureIdealAlbedo(hitRec.texUV); float refractiveAlbedo = hitRec.material->getRefractiveAlbedo(); if(rrRandom < diffuseAlbedo) { ray.id = rayId++; ray.isShadowRay = false; ray.origin = hitRec.point; power %= hitRec.material->getTextureDiffuseBrdf(hitRec.texUV); power *= 1.0f / diffuseAlbedo; prob *= diffuseAlbedo; sampleShootingDiffuseDirection(depth, hitRec.normal, ray.dir); } else { rrRandom -= diffuseAlbedo; if(rrRandom < idealAlbedo) { ray.dir.setIdealReflectedDirection(ray.dir, hitRec.normal); ray.id = rayId++; ray.isShadowRay = false; ray.origin = hitRec.point; power %= hitRec.material->getIdealBrdf(); power *= 1.0f / idealAlbedo; prob *= idealAlbedo; } else break; } } } } void PathMapEffect::sampleSurfaceRadion(Radion& starter) { float randa = ((double)rand() / RAND_MAX) * entities.size(); float uptonowSurfaceArea = 0.0; Entity* e = NULL; std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { uptonowSurfaceArea += 1.0f; if(uptonowSurfaceArea >= randa) { e = *entityIterator; break; } entityIterator++; } const Material* m = e->sampleSurface(starter); float surfa = starter.radiance.z; Vector rtexc = starter.radiance; starter.radiance = m->getTextureDiffuseBrdf(rtexc); starter.radiance *= sumSurfaceArea / NRADIONS; starter.probability = (float)NRADIONS / (surfa * entities.size()); } void PathMapEffect::sampleSurfaceRadionUniform(Radion& starter) { float randa = ((double)rand() / RAND_MAX) * sumSurfaceArea; float uptonowSurfaceArea = 0.0; Entity* e = NULL; std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { uptonowSurfaceArea += (*entityIterator)->getSurfaceArea(); if(uptonowSurfaceArea >= randa) { e = *entityIterator; break; } entityIterator++; } const Material* m = e->sampleSurface(starter); float surfa = starter.radiance.z; Vector rtexc = starter.radiance; starter.radiance = m->getTextureDiffuseBrdf(rtexc); starter.radiance *= sumSurfaceArea / NRADIONS; starter.probability = (float)NRADIONS / sumSurfaceArea; } void PathMapEffect::uploadRadions() { int nRadions = bushStarters.size(); struct TMR{ float pos[4]; float dir[4]; // float pow[4]; }; TMR* tm = new TMR[nRadions]; for(int i=0; iprmDirectory); std::fstream entryPointFile(eFileName, std::ios::out); entryPointFile << "nEntryPoints "; entryPointFile << NRADIONS; entryPointFile << "\n"; for(unsigned int u=0; u < NRADIONS; u++) { entryPointFile << bushStarters.at(u); entryPointFile << "\n"; } entryPointFile << "Clusters "; entryPointFile << NCLUSTERS; entryPointFile << "\n"; for(unsigned int c=0; c < NCLUSTERS; c++) { // entryPointFile.write((char*)&clusterLenghts[c], sizeof(unsigned int)); entryPointFile << clusterLenghts[c] << '\n'; } entryPointFile << "\n"; entryPointFile.flush(); entryPointFile.close(); unsigned int iEntity = 0; std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->savePRM(); entityIterator++; iEntity++; } } void PathMapEffect::loadPathMaps() { /* std::fstream entryPointFile("prm\\prmEntryPoints.dat", std::ios::in | std::ios::binary); bushStarters.clear(); for(unsigned int u=0; u < NRADIONS; u++) { Radion ri; entryPointFile >> ri; bushStarters.push_back(ri); } for(unsigned int c=0; c < NCLUSTERS; c++) { entryPointFile.read((char*)&clusterLenghts[c], sizeof(unsigned int)); } entryPointFile.close();*/ char eFileName[512]; sprintf(eFileName, "%s\\prmEntryPoints.text", LC::c-this->prmDirectory); std::fstream entryPointFile(eFileName, std::ios::in); char keyword[256]; unsigned int redrad; entryPointFile >> keyword; //nradions entryPointFile >> redrad; bushStarters.clear(); for(unsigned int u=0; u < NRADIONS; u++) { Radion ri; entryPointFile >> ri; bushStarters.push_back(ri); } entryPointFile >> keyword; //ncluster entryPointFile >> redrad; for(unsigned int c=0; c < NCLUSTERS; c++) { entryPointFile >> clusterLenghts[c]; } entryPointFile.close(); srand(0xef23ab32); unsigned int iEntity = 0; std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->findNearClusters(bushStarters, NCLUSTERS); (*entityIterator)->loadPRM(); entityIterator++; iEntity++; } } void PathMapEffect::loadScene(const char* sceneFileName) { std::fstream sceneFile(sceneFileName, std::ios::in); char keyword[256]; while(!sceneFile.eof()) { sceneFile >> keyword; if(strcmp(keyword, "mesh") == 0) { int meshAtlasSize = 128; bool needsUV = true; unsigned int originalAtlasTexCoordIndex = 0; bool needsTBN = false; int dividePcs = 1; char meshname[256]; wchar_t* wide = NULL; wchar_t* wideXML = NULL; wchar_t* wideOgre = NULL; Vector color = Vector::RGBWHITE; sceneFile >> meshname; if(strcmp(meshname, "{") == 0) strcpy(meshname, "mesh"); while(strcmp(keyword, "}") != 0 && !sceneFile.eof()) { sceneFile >> keyword; if(strcmp(keyword, "xfile") == 0) { sceneFile >> keyword; if(keyword) { wide = L::clone(keyword); } } if(strcmp(keyword, "ogreXMLfile") == 0) { sceneFile >> keyword; if(keyword) { wideXML = L::clone(keyword); } } if(strcmp(keyword, "ogrefile") == 0) { sceneFile >> keyword; if(keyword) { wideOgre = L::clone(keyword); } } if(strcmp(keyword, "pathmapresolution") == 0) { sceneFile >> meshAtlasSize; } if(strcmp(keyword, "divide") == 0) { sceneFile >> dividePcs; } if(strcmp(keyword, "atlascoord") == 0) { needsUV = false; sceneFile >> originalAtlasTexCoordIndex; } } if(wideXML) { loadMesh(Mesh::OgreXMLMesh, wideXML, wideOgre, meshAtlasSize, meshname, dividePcs, needsUV, needsTBN, originalAtlasTexCoordIndex); delete [] wideXML; } else if(wide) { loadMesh(Mesh::DirectXMesh, wide, wideOgre, meshAtlasSize, meshname, dividePcs, needsUV, needsTBN, originalAtlasTexCoordIndex); delete [] wide; } else if(wideOgre) delete [] wideOgre; } if(strcmp(keyword, "entity") == 0) { char entityName[256]; char prmFileName[256]; Mesh* baseMesh = NULL; D3DXMATRIX trafo; D3DXMatrixIdentity(&trafo); int nNearClusters = 16; sceneFile >> entityName; if(strcmp(entityName, "{") == 0) strcpy(entityName, "noname"); while(strcmp(keyword, "}") != 0 && !sceneFile.eof()) { sceneFile >> keyword; if(strcmp(keyword, "pathmapfile") == 0) { sceneFile >> keyword; strcpy(prmFileName, keyword); } if(strcmp(keyword, "mesh") == 0) { sceneFile >> keyword; std::vector::iterator i = meshes.begin(); while(i != meshes.end()) { if(strcmp((*i)->getName(), keyword) == 0) { baseMesh = *i; break; } i++; } } if(strcmp(keyword, "transformation") == 0) { for(int i=0; i<16; i++) sceneFile >> trafo.m[i%4][i/4]; } if(strcmp(keyword, "pathmapclusters") == 0) { sceneFile >> nNearClusters; } } if(baseMesh) { Entity* e = new Entity(baseMesh, entityName, prmFileName, trafo, nNearClusters); entities.push_back(e); } } } sceneFile.close(); } void PathMapEffect::renderScene(const RenderStrategy& renderStrategy) { renderStrategy.applyTargets(); renderStrategy.applyRenderState(); if( SUCCEEDED( device->BeginScene() ) ) { renderStrategy.applyTechnique(); UINT nPasses; effect->Begin( &nPasses, 0 ); for(UINT j=0;jBeginPass(j); //loop through all entities std::vector::iterator entityIterator = entities.begin(); while(entityIterator != entities.end()) { (*entityIterator)->drawAllSubEntities(renderStrategy); entityIterator++; } effect->EndPass(); } effect->End(); device->EndScene(); } renderStrategy.resetRenderState(); } void PathMapEffect::saveScene(const char* sceneFileName) { std::ofstream psf(sceneFileName); std::vector::iterator iMesh = meshes.begin(); while(iMesh != meshes.end()) { (*iMesh)->saveSceneInfo(psf); iMesh++; } std::vector::iterator iEntity = entities.begin(); while(iEntity != entities.end()) { (*iEntity)->saveSceneInfo(psf); iEntity++; } } const wchar_t* PathMapEffect::getCurrentMethodName() { int lmode = parameters->GetInt(fIndirectLightingMode); if(lmode == 3) return L"PRM only"; if(lmode == 2) return L"direct + PRM"; if(lmode == 1) return L"direct + ambient"; if(lmode == 0) return L"direct only"; } wchar_t* PathMapEffect::getCruiseLoop() { static wchar_t s[4]; s[1] = 0; s[0] = L'0' + cruiseLoop; return s; }