D3DXVECTOR3 TERRAIN::GetNormal(int x, int y) { //Neighboring map nodes (D, B, C, F, H, G) INTPOINT mp[] = {INTPOINT(x-1, y), INTPOINT(x, y-1), INTPOINT(x+1, y-1), INTPOINT(x+1, y), INTPOINT(x, y+1), INTPOINT(x-1, y+1)}; //if there's an invalid map node return (0, 1, 0) if(!Within(mp[0]) || !Within(mp[1]) || !Within(mp[2]) || !Within(mp[3]) || !Within(mp[4]) || !Within(mp[5])) return D3DXVECTOR3(0.0f, 1.0f, 0.0f); //Calculate the normals of the 6 neighboring planes D3DXVECTOR3 normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); for(int i=0;i<6;i++) { D3DXPLANE plane; D3DXPlaneFromPoints(&plane, &GetWorldPos(INTPOINT(x, y)), &GetWorldPos(mp[i]), &GetWorldPos(mp[(i + 1) % 6])); normal += D3DXVECTOR3(plane.a, plane.b, plane.c); } D3DXVec3Normalize(&normal, &normal); return normal; }
void DynamicVoronoi::setObstacle(int x, int y) { dataCell c = data[x][y]; if(isOccupied(x,y,c)) return; addList.push_back(INTPOINT(x,y)); c.obstX = x; c.obstY = y; data[x][y] = c; }
HRESULT APPLICATION::Update(float deltaTime) { if(m_pHeightMap == NULL) //Create Heightmap { //Load heightmap from file m_pHeightMap = new HEIGHTMAP(m_pDevice, INTPOINT(100, 100)); if(FAILED(m_pHeightMap->LoadFromFile("images/abe.jpg"))) { debug.Print("Failed to load from file"); Quit(); } //Create particles to visualize the heightmap if(FAILED(m_pHeightMap->CreateParticles())) { debug.Print("Failed to create particles"); Quit(); } } else { //Control camera m_angle += deltaTime * 0.5f; D3DXMATRIX matWorld, matView, matProj; D3DXVECTOR2 centre = m_pHeightMap->GetCentre(); D3DXVECTOR3 Eye = D3DXVECTOR3(centre.x + cos(m_angle) * centre.x * 2.0f, m_pHeightMap->m_maxHeight * 8.0f, -centre.y + sin(m_angle) * centre.y * 2.0f); D3DXVECTOR3 Lookat = D3DXVECTOR3(centre.x, 0.0f, -centre.y); D3DXMatrixIdentity(&matWorld); D3DXMatrixLookAtLH(&matView, &Eye, &Lookat, &D3DXVECTOR3(0.0f, 1.0f, 0.0f)); D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.3333f, 1.0f, 1000.0f ); m_pDevice->SetTransform( D3DTS_WORLD, &matWorld ); m_pDevice->SetTransform( D3DTS_VIEW, &matView ); m_pDevice->SetTransform( D3DTS_PROJECTION, &matProj ); //Change heightmap image if(KEYDOWN(VK_SPACE)) { m_image++; if(m_image > 2)m_image = 0; if(m_image == 0)m_pHeightMap->LoadFromFile("images/abe.jpg"); if(m_image == 1)m_pHeightMap->LoadFromFile("images/smiley.bmp"); if(m_image == 2)m_pHeightMap->LoadFromFile("images/heightmap.jpg"); m_pHeightMap->CreateParticles(); Sleep(300); } } if(KEYDOWN(VK_ESCAPE)) Quit(); return S_OK; }
void DynamicVoronoi::removeObstacle(int x, int y) { dataCell c = data[x][y]; if(isOccupied(x,y,c) == false) return; removeList.push_back(INTPOINT(x,y)); c.obstX = invalidObstData; c.obstY = invalidObstData; c.queueing = bwQueued; data[x][y] = c; }
void TERRAIN::GenerateRandomTerrain(int numPatches) { try { Release(); //Create two heightmaps and multiply them m_pHeightMap = new HEIGHTMAP(m_size, 20.0f); HEIGHTMAP hm2(m_size, 2.0f); m_pHeightMap->CreateRandomHeightMap(rand()%2000, 1.0f, 0.7f, 7); hm2.CreateRandomHeightMap(rand()%2000, 2.5f, 0.8f, 3); hm2.Cap(hm2.m_maxHeight * 0.4f); *m_pHeightMap *= hm2; hm2.Release(); //Add objects HEIGHTMAP hm3(m_size, 1.0f); hm3.CreateRandomHeightMap(rand()%1000, 5.5f, 0.9f, 7); for(int y=0;y<m_size.y;y++) for(int x=0;x<m_size.x;x++) { if(m_pHeightMap->GetHeight(x, y) == 0.0f && hm3.GetHeight(x, y) > 0.7f && rand()%6 == 0) AddObject(0, INTPOINT(x, y)); //Tree else if(m_pHeightMap->GetHeight(x, y) >= 1.0f && hm3.GetHeight(x, y) > 0.9f && rand()%20 == 0) AddObject(1, INTPOINT(x, y)); //Stone } hm3.Release(); InitPathfinding(); CreatePatches(numPatches); CalculateAlphaMaps(); CalculateLightMap(false); } catch(...) { debug.Print("Error in TERRAIN::GenerateRandomTerrain()"); } }
INTPOINT GetScreenPos(D3DXVECTOR3 pos, IDirect3DDevice9* Device) { D3DXVECTOR3 screenPos; D3DVIEWPORT9 Viewport; D3DXMATRIX Projection, View, World; Device->GetViewport(&Viewport); Device->GetTransform(D3DTS_VIEW, &View); Device->GetTransform(D3DTS_PROJECTION, &Projection); D3DXMatrixIdentity(&World); D3DXVec3Project(&screenPos, &pos, &Viewport, &Projection, &View, &World); return INTPOINT((int)screenPos.x, (int)screenPos.y); }
void DynamicVoronoi::checkVoro(int x, int y, int nx, int ny, dataCell& c, dataCell& nc) { if (nc.obstX!=invalidObstData) { if (abs(c.obstX-nc.obstX) > 1 || abs(c.obstY-nc.obstY) > 1) { //compute dist from x,y to obstacle of nx,ny int dxy_x = x-nc.obstX; int dxy_y = y-nc.obstY; int sqdxy = dxy_x*dxy_x + dxy_y*dxy_y; int stability_xy = sqdxy - c.sqdist; if (sqdxy - c.sqdist<0) return; //compute dist from nx,ny to obstacle of x,y int dnxy_x = nx - c.obstX; int dnxy_y = ny - c.obstY; int sqdnxy = dnxy_x*dnxy_x + dnxy_y*dnxy_y; int stability_nxy = sqdnxy - nc.sqdist; if (sqdnxy - nc.sqdist <0) return; //which cell is added to the Voronoi diagram? if(stability_xy <= stability_nxy) { if (c.voronoi != free) { c.voronoi = free; reviveVoroNeighbors(x,y); pruneQueue.push(INTPOINT(x,y)); } } if(stability_nxy <= stability_xy) { if (nc.voronoi != free) { nc.voronoi = free; reviveVoroNeighbors(nx,ny); pruneQueue.push(INTPOINT(nx,ny)); } } } } }
void DynamicVoronoi::reviveVoroNeighbors(int &x, int &y) { for (int dx=-1; dx<=1; dx++) { int nx = x+dx; if (nx<=0 || nx>=sizeX-1) continue; for (int dy=-1; dy<=1; dy++) { if (dx==0 && dy==0) continue; int ny = y+dy; if (ny<=0 || ny>=sizeY-1) continue; dataCell nc = data[nx][ny]; if (nc.sqdist != INT_MAX && !nc.needsRaise && (nc.voronoi == voronoiKeep || nc.voronoi == voronoiPrune)) { nc.voronoi = free; data[nx][ny] = nc; pruneQueue.push(INTPOINT(nx,ny)); } } } }
void TERRAIN::UpdatePathfinding(RECT *r) { if(r == NULL) { InitPathfinding(); return; } //Connect maptiles using the neightbors[] pointers for(int y=r->top; y<=r->bottom; y++) for(int x=r->left; x<=r->right; x++) { MAPTILE *tile = GetTile(x, y); if(tile != NULL && tile->m_walkable) { //Clear old connections for(int i=0;i<8;i++) tile->m_pNeighbors[i] = NULL; //Possible m_pNeighbors INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1), INTPOINT(x-1, y), INTPOINT(x+1, y), INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)}; //For each neighbor for(int i=0;i<8;i++) if(Within(p[i])) { MAPTILE *neighbor = GetTile(p[i]); //Connect tiles if the neighbor is walkable if(neighbor != NULL && neighbor->m_walkable) tile->m_pNeighbors[i] = neighbor; } } } CreateTileSets(); }
bool PlaceOk(int buildType, INTPOINT mp, TERRAIN *terrain) { if(terrain == NULL)return false; BUILDING b(buildType, 0, true, mp, NULL, NULL, false, NULL); RECT r = b.GetMapRect(2); for(int y=r.top;y<=r.bottom;y++) for(int x=r.left;x<=r.right;x++) { //Building must be within map borders if(!terrain->Within(INTPOINT(x,y)))return false; MAPTILE *tile = terrain->GetTile(x, y); if(tile == NULL)return false; //The terrain must be level and walkable if(tile->m_type != 0 || !tile->m_walkable)return false; } return true; }
HRESULT APPLICATION::Init(HINSTANCE hInstance, int width, int height, bool windowed) { debug.Print("Application initiated"); //Create Window Class WNDCLASS wc; memset(&wc, 0, sizeof(WNDCLASS)); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)::DefWindowProc; wc.hInstance = hInstance; wc.lpszClassName = "D3DWND"; //Register Class and Create new Window RegisterClass(&wc); m_mainWindow = CreateWindow("D3DWND", "Example 5.6: Frustum Culling", WS_EX_TOPMOST, 0, 0, width, height, 0, 0, hInstance, 0); SetCursor(NULL); ShowWindow(m_mainWindow, SW_SHOW); UpdateWindow(m_mainWindow); //Create IDirect3D9 Interface IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION); if(d3d9 == NULL) { debug.Print("Direct3DCreate9() - FAILED"); return E_FAIL; } //Check that the Device supports what we need from it D3DCAPS9 caps; d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps); //Hardware Vertex Processing or not? int vp = 0; if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //Check vertex & pixelshader versions if(caps.VertexShaderVersion < D3DVS_VERSION(2, 0) || caps.PixelShaderVersion < D3DPS_VERSION(2, 0)) { debug.Print("Warning - Your graphic card does not support vertex and pixelshaders version 2.0"); } //Set D3DPRESENT_PARAMETERS D3DPRESENT_PARAMETERS d3dpp; d3dpp.BackBufferWidth = width; d3dpp.BackBufferHeight = height; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 1; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = m_mainWindow; d3dpp.Windowed = windowed; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //Create the IDirect3DDevice9 if(FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_mainWindow, vp, &d3dpp, &m_pDevice))) { debug.Print("Failed to create IDirect3DDevice9"); return E_FAIL; } //Release IDirect3D9 interface d3d9->Release(); D3DXCreateFont(m_pDevice, 18, 0, 0, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial", &m_pFont); //Create m_light ::ZeroMemory(&m_light, sizeof(m_light)); m_light.Type = D3DLIGHT_DIRECTIONAL; m_light.Ambient = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f); m_light.Diffuse = D3DXCOLOR(0.9f, 0.9f, 0.9f, 1.0f); m_light.Specular = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f); m_light.Direction = D3DXVECTOR3(0.0f, -1.0f, 0.0f); m_pDevice->SetLight(0, &m_light); m_pDevice->LightEnable(0, true); //Set sampler state for(int i=0;i<4;i++) { m_pDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); m_pDevice->SetSamplerState(i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); m_pDevice->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_POINT); } //Init camera m_camera.Init(m_pDevice); //Load objects LoadObjectResources(m_pDevice); //Create 2D Line D3DXCreateLine(m_pDevice, &m_pLine); //Create city m_city.Init(INTPOINT(25, 25)); m_camera.m_focus = m_city.GetCenter(); //Init mouse m_mouse.InitMouse(m_pDevice, m_mainWindow); return S_OK; }
void DynamicVoronoi::prune() { // filler assert(open->empty()); assert(open->empty()); while(!pruneQueue.empty()) { INTPOINT p = pruneQueue.front(); pruneQueue.pop(); int x = p.x; int y = p.y; if (data[x][y].voronoi==occupied) continue; if (data[x][y].voronoi==freeQueued) continue; data[x][y].voronoi = freeQueued; open->push(data[x][y].sqdist, p); /* tl t tr l c r bl b br */ dataCell tr,tl,br,bl; tr = data[x+1][y+1]; tl = data[x-1][y+1]; br = data[x+1][y-1]; bl = data[x-1][y-1]; dataCell r,b,t,l; r = data[x+1][y]; l = data[x-1][y]; t = data[x][y+1]; b = data[x][y-1]; if (x+2<sizeX && r.voronoi==occupied) { // fill to the right if (tr.voronoi!=occupied && br.voronoi!=occupied && data[x+2][y].voronoi!=occupied) { r.voronoi = freeQueued; open->push(r.sqdist, INTPOINT(x+1,y)); data[x+1][y] = r; } } if (x-2>=0 && l.voronoi==occupied) { // fill to the left if (tl.voronoi!=occupied && bl.voronoi!=occupied && data[x-2][y].voronoi!=occupied) { l.voronoi = freeQueued; open->push(l.sqdist, INTPOINT(x-1,y)); data[x-1][y] = l; } } if (y+2<sizeY && t.voronoi==occupied) { // fill to the top if (tr.voronoi!=occupied && tl.voronoi!=occupied && data[x][y+2].voronoi!=occupied) { t.voronoi = freeQueued; open->push(t.sqdist, INTPOINT(x,y+1)); data[x][y+1] = t; } } if (y-2>=0 && b.voronoi==occupied) { // fill to the bottom if (br.voronoi!=occupied && bl.voronoi!=occupied && data[x][y-2].voronoi!=occupied) { b.voronoi = freeQueued; open->push(b.sqdist, INTPOINT(x,y-1)); data[x][y-1] = b; } } } while(!open->empty()) { INTPOINT p = open->pop(); dataCell c = data[p.x][p.y]; int v = c.voronoi; if (v!=freeQueued && v!=voronoiRetry) { continue; } markerMatchResult r = markerMatch(p.x,p.y); if (r==pruned) c.voronoi = voronoiPrune; else if (r==keep) c.voronoi = voronoiKeep; else { // r==retry c.voronoi = voronoiRetry; pruneQueue.push(p); } data[p.x][p.y] = c; if (open->empty()) { while (!pruneQueue.empty()) { INTPOINT p = pruneQueue.front(); pruneQueue.pop(); open->push(data[p.x][p.y].sqdist, p); } } } }
void DynamicVoronoi::update(bool updateRealDist) { // ADD NEW OBSTACLES for (unsigned int i=0; i<addList.size(); i++) { INTPOINT p = addList[i]; int x = p.x; int y = p.y; dataCell c = data[x][y]; if(c.queueing != fwQueued) { if (updateRealDist) c.dist = 0; c.sqdist = 0; c.obstX = x; c.obstY = y; c.queueing = fwQueued; c.voronoi = occupied; data[x][y] = c; open->push(0, INTPOINT(x,y)); } } // REMOVE OLD OBSTACLES for (unsigned int i=0; i<removeList.size(); i++) { INTPOINT p = removeList[i]; int x = p.x; int y = p.y; dataCell c = data[x][y]; if (isOccupied(x,y,c)==true) continue; // obstacle was removed and reinserted open->push(0, INTPOINT(x,y)); if (updateRealDist) c.dist = maxDist; c.sqdist = maxDist_squared; c.needsRaise = true; data[x][y] = c; } removeList.clear(); addList.clear(); while (!open->empty()) { INTPOINT p = open->pop(); int x = p.x; int y = p.y; dataCell c = data[x][y]; if(c.queueing==fwProcessed) continue; if (c.needsRaise) { // RAISE for (int dx=-1; dx<=1; dx++) { int nx = x+dx; if (nx<=0 || nx>=sizeX-1) continue; for (int dy=-1; dy<=1; dy++) { if (dx==0 && dy==0) continue; int ny = y+dy; if (ny<=0 || ny>=sizeY-1) continue; dataCell nc = data[nx][ny]; if (nc.obstX!=invalidObstData && !nc.needsRaise) { if(!isOccupied(nc.obstX,nc.obstY,data[nc.obstX][nc.obstY])) { open->push(nc.sqdist, INTPOINT(nx,ny)); nc.queueing = fwQueued; nc.needsRaise = true; nc.obstX = invalidObstData; nc.obstY = invalidObstData; if (updateRealDist) nc.dist = maxDist; nc.sqdist = maxDist_squared; data[nx][ny] = nc; } else { if(nc.queueing != fwQueued) { open->push(nc.sqdist, INTPOINT(nx,ny)); nc.queueing = fwQueued; data[nx][ny] = nc; } } } } } c.needsRaise = false; c.queueing = bwProcessed; data[x][y] = c; } else if (c.obstX != invalidObstData && isOccupied(c.obstX,c.obstY,data[c.obstX][c.obstY])) { // LOWER c.queueing = fwProcessed; c.voronoi = occupied; for (int dx=-1; dx<=1; dx++) { int nx = x+dx; if (nx<=0 || nx>=sizeX-1) continue; for (int dy=-1; dy<=1; dy++) { if (dx==0 && dy==0) continue; int ny = y+dy; if (ny<=0 || ny>=sizeY-1) continue; dataCell nc = data[nx][ny]; if(!nc.needsRaise) { int distx = nx-c.obstX; int disty = ny-c.obstY; int newSqDistance = distx*distx + disty*disty; if(newSqDistance > maxDist_squared) newSqDistance = maxDist_squared; bool overwrite = (newSqDistance < nc.sqdist); if(!overwrite && newSqDistance==nc.sqdist) { if (nc.obstX == invalidObstData || isOccupied(nc.obstX,nc.obstY,data[nc.obstX][nc.obstY])==false) overwrite = true; } if (overwrite) { if(newSqDistance < maxDist_squared) { open->push(newSqDistance, INTPOINT(nx,ny)); nc.queueing = fwQueued; } if (updateRealDist) { nc.dist = sqrt((double) newSqDistance); } nc.sqdist = newSqDistance; nc.obstX = c.obstX; nc.obstY = c.obstY; } else { checkVoro(x,y,nx,ny,c,nc); } data[nx][ny] = nc; } } } } data[x][y] = c; } }
void TERRAIN::InitPathfinding() { try { //Read maptile heights & types from heightmap for(int y=0;y<m_size.y;y++) for(int x=0;x<m_size.x;x++) { MAPTILE *tile = GetTile(x, y); if(m_pHeightMap != NULL)tile->m_height = m_pHeightMap->GetHeight(x, y); tile->m_mappos = INTPOINT(x, y); if(tile->m_height < 0.3f) tile->m_type = 0; //Grass else if(tile->m_height < 7.0f) tile->m_type = 1; //Stone else tile->m_type = 2; //Snow } //Calculate tile cost as a function of the height variance for(int y=0;y<m_size.y;y++) for(int x=0;x<m_size.x;x++) { MAPTILE *tile = GetTile(x, y); if(tile != NULL) { //Possible m_pNeighbors INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1), INTPOINT(x-1, y), INTPOINT(x+1, y), INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)}; float variance = 0.0f; int nr = 0; //For each neighbor for(int i=0;i<8;i++) if(Within(p[i])) { MAPTILE *neighbor = GetTile(p[i]); if(neighbor != NULL) { float v = neighbor->m_height - tile->m_height; variance += (v * v); nr++; } } //Cost = height variance variance /= (float)nr; tile->m_cost = variance + 0.1f; if(tile->m_cost > 1.0f)tile->m_cost = 1.0f; //If the tile cost is less than 1.0f, then we can walk on the tile tile->m_walkable = tile->m_cost < 0.5f; } } //Make maptiles with objects on them not walkable for(int i=0;i<(int)m_objects.size();i++) { MAPTILE *tile = GetTile(m_objects[i].m_mappos); if(tile != NULL) { tile->m_walkable = false; tile->m_cost = 1.0f; } } //Connect maptiles using the neightbors[] pointers for(int y=0;y<m_size.y;y++) for(int x=0;x<m_size.x;x++) { MAPTILE *tile = GetTile(x, y); if(tile != NULL && tile->m_walkable) { //Clear old connections for(int i=0;i<8;i++) tile->m_pNeighbors[i] = NULL; //Possible m_pNeighbors INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1), INTPOINT(x-1, y), INTPOINT(x+1, y), INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)}; //For each neighbor for(int i=0;i<8;i++) if(Within(p[i])) { MAPTILE *neighbor = GetTile(p[i]); //Connect tiles if the neighbor is walkable if(neighbor != NULL && neighbor->m_walkable) tile->m_pNeighbors[i] = neighbor; } } } CreateTileSets(); } catch(...) { debug.Print("Error in InitPathfinding()"); } }