void CMiniMap::SetCameraPos() { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); CVector3D target; GetMouseWorldCoordinates(target.X, target.Z); target.Y = terrain->GetExactGroundLevel(target.X, target.Z); g_Game->GetView()->MoveCameraTarget(target); }
// This requires m_DistanceHeightmap to be defined properly. void WaterManager::CreateWaveMeshes() { size_t SideSize = m_MapSize*2; CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); for (WaveObject* const& obj : m_ShoreWaves) { if (obj->m_VBvertices) g_VBMan.Release(obj->m_VBvertices); delete obj; } m_ShoreWaves.clear(); if (m_ShoreWaves_VBIndices) { g_VBMan.Release(m_ShoreWaves_VBIndices); m_ShoreWaves_VBIndices = NULL; } if (m_Waviness < 5.0f && m_WaterType != L"ocean") return; // First step: get the points near the coast. std::set<int> CoastalPointsSet; for (size_t z = 1; z < SideSize-1; ++z) for (size_t x = 1; x < SideSize-1; ++x) if (fabs(m_DistanceHeightmap[z*SideSize + x]-1.0f) < 0.2f) CoastalPointsSet.insert(z*SideSize + x); // Second step: create chains out of those coastal points. static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } }; std::vector<std::deque<CoastalPoint> > CoastalPointsChains; while (!CoastalPointsSet.empty()) { int index = *(CoastalPointsSet.begin()); int x = index % SideSize; int y = (index - x ) / SideSize; std::deque<CoastalPoint> Chain; Chain.push_front(CoastalPoint(index,CVector2D(x*2,y*2))); // Erase us. CoastalPointsSet.erase(CoastalPointsSet.begin()); // We're our starter points. At most we can have 2 points close to us. // We'll pick the first one and look for its neighbors (he can only have one new) // Up until we either reach the end of the chain, or ourselves. // Then go down the other direction if there is any. int neighbours[2] = { -1, -1 }; int nbNeighb = 0; for (int i = 0; i < 8; ++i) { if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize)) { if (nbNeighb < 2) neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize; ++nbNeighb; } } if (nbNeighb > 2) continue; for (int i = 0; i < 2; ++i) { if (neighbours[i] == -1) continue; // Move to our neighboring point int xx = neighbours[i] % SideSize; int yy = (neighbours[i] - xx ) / SideSize; int indexx = xx + yy*SideSize; int endedChain = false; if (i == 0) Chain.push_back(CoastalPoint(indexx,CVector2D(xx*2,yy*2))); else Chain.push_front(CoastalPoint(indexx,CVector2D(xx*2,yy*2))); // If there's a loop we'll be the "other" neighboring point already so check for that. // We'll readd at the end/front the other one to have full squares. if (CoastalPointsSet.count(indexx) == 0) break; CoastalPointsSet.erase(indexx); // Start checking from there. while(!endedChain) { bool found = false; nbNeighb = 0; for (int p = 0; p < 8; ++p) { if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize)) { if (nbNeighb >= 2) { CoastalPointsSet.erase(xx + yy*SideSize); continue; } ++nbNeighb; // We've found a new point around us. // Move there xx = xx + around[p][0]; yy = yy + around[p][1]; indexx = xx + yy*SideSize; if (i == 0) Chain.push_back(CoastalPoint(indexx,CVector2D(xx*2,yy*2))); else Chain.push_front(CoastalPoint(indexx,CVector2D(xx*2,yy*2))); CoastalPointsSet.erase(xx + yy*SideSize); found = true; break; } } if (!found) endedChain = true; } } if (Chain.size() > 10) CoastalPointsChains.push_back(Chain); } // (optional) third step: Smooth chains out. // This is also really dumb. for (size_t i = 0; i < CoastalPointsChains.size(); ++i) { // Bump 1 for smoother. for (int p = 0; p < 3; ++p) { for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j) { CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position; CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f; } } } // Fourth step: create waves themselves, using those chains. We basically create subchains. size_t waveSizes = 14; // maximal size in width. // Construct indices buffer (we can afford one for all of them) std::vector<GLushort> water_indices; for (size_t a = 0; a < waveSizes-1;++a) { for (size_t rect = 0; rect < 7; ++rect) { water_indices.push_back(a*9 + rect); water_indices.push_back(a*9 + 9 + rect); water_indices.push_back(a*9 + 1 + rect); water_indices.push_back(a*9 + 9 + rect); water_indices.push_back(a*9 + 10 + rect); water_indices.push_back(a*9 + 1 + rect); } } // Generic indexes, max-length m_ShoreWaves_VBIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); m_ShoreWaves_VBIndices->m_Owner->UpdateChunkVertices(m_ShoreWaves_VBIndices, &water_indices[0]); float diff = (rand() % 50) / 5.0f; for (size_t i = 0; i < CoastalPointsChains.size(); ++i) { for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j) { if (CoastalPointsChains[i].size()- 1 - j < waveSizes) break; size_t width = waveSizes; // First pass to get some parameters out. float outmost = 0.0f; // how far to move on the shore. float avgDepth = 0.0f; int sign = 1; CVector2D firstPerp(0,0), perp(0,0), lastPerp(0,0); for (size_t a = 0; a < waveSizes;++a) { lastPerp = perp; perp = CVector2D(0,0); int nb = 0; CVector2D pos = CoastalPointsChains[i][j+a].position; CVector2D posPlus; CVector2D posMinus; if (a > 0) { ++nb; posMinus = CoastalPointsChains[i][j+a-1].position; perp += pos-posMinus; } if (a < waveSizes-1) { ++nb; posPlus = CoastalPointsChains[i][j+a+1].position; perp += posPlus-pos; } perp /= nb; perp = CVector2D(-perp.Y,perp.X).Normalized(); if (a == 0) firstPerp = perp; if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f) { width = a+1; break; } if (m_BlurredNormalMap[ (int)(pos.X/4) + (int)(pos.Y/4)*m_MapSize].Y < 0.9) { width = a-1; break; } if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight) sign = -1; avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight; float localOutmost = -2.0f; while (localOutmost < 0.0f) { float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight; if (depth < 0.0f || depth > 0.6f) localOutmost += 0.2f; else break; } outmost += localOutmost; } if (width < 5) { j += 6; continue; } outmost /= width; if (outmost > -0.5f) { j += 3; continue; } outmost = -0.5f + outmost * m_Waviness/10.0f; avgDepth /= width; if (avgDepth > -1.3f) { j += 3; continue; } // we passed the checks, we can create a wave of size "width". WaveObject* shoreWave = new WaveObject; std::vector<SWavesVertex> vertices; shoreWave->m_Width = width; shoreWave->m_TimeDiff = diff; diff += (rand() % 100) / 25.0f + 4.0f; for (size_t a = 0; a < width;++a) { CVector2D perp = CVector2D(0,0); int nb = 0; CVector2D pos = CoastalPointsChains[i][j+a].position; CVector2D posPlus; CVector2D posMinus; if (a > 0) { ++nb; posMinus = CoastalPointsChains[i][j+a-1].position; perp += pos-posMinus; } if (a < waveSizes-1) { ++nb; posPlus = CoastalPointsChains[i][j+a+1].position; perp += posPlus-pos; } perp /= nb; perp = CVector2D(-perp.Y,perp.X).Normalized(); SWavesVertex point[9]; float baseHeight = 0.04f; float halfWidth = (width-1.0f)/2.0f; float sideNess = sqrtf(clamp( (halfWidth - fabsf(a-halfWidth))/3.0f, 0.0f,1.0f)); point[0].m_UV[0] = a; point[0].m_UV[1] = 8; point[1].m_UV[0] = a; point[1].m_UV[1] = 7; point[2].m_UV[0] = a; point[2].m_UV[1] = 6; point[3].m_UV[0] = a; point[3].m_UV[1] = 5; point[4].m_UV[0] = a; point[4].m_UV[1] = 4; point[5].m_UV[0] = a; point[5].m_UV[1] = 3; point[6].m_UV[0] = a; point[6].m_UV[1] = 2; point[7].m_UV[0] = a; point[7].m_UV[1] = 1; point[8].m_UV[0] = a; point[8].m_UV[1] = 0; point[0].m_PerpVect = perp; point[1].m_PerpVect = perp; point[2].m_PerpVect = perp; point[3].m_PerpVect = perp; point[4].m_PerpVect = perp; point[5].m_PerpVect = perp; point[6].m_PerpVect = perp; point[7].m_PerpVect = perp; point[8].m_PerpVect = perp; static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f }; static const float perpT2[9] = { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f }; static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f }; static const float perpT4[9] = { 2.0f, 2.1f, 1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f }; static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 }; static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 }; static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 }; for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost), pos.Y+sign*perp.Y*(perpT1[t]+outmost)); point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT1[t]+outmost)); } for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost), pos.Y+sign*perp.Y*(perpT2[t]+outmost)); point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT2[t]+outmost)); } for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); } for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost), pos.Y+sign*perp.Y*(perpT4[t]+outmost)); point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT4[t]+outmost)); } vertices.push_back(point[8]); vertices.push_back(point[7]); vertices.push_back(point[6]); vertices.push_back(point[5]); vertices.push_back(point[4]); vertices.push_back(point[3]); vertices.push_back(point[2]); vertices.push_back(point[1]); vertices.push_back(point[0]); shoreWave->m_AABB += point[8].m_SplashPosition; shoreWave->m_AABB += point[8].m_BasePosition; shoreWave->m_AABB += point[0].m_SplashPosition; shoreWave->m_AABB += point[0].m_BasePosition; shoreWave->m_AABB += point[4].m_ApexPosition; } if (sign == 1) { // Let's do some fancy reversing. std::vector<SWavesVertex> reversed; for (int a = width-1; a >= 0; --a) { for (size_t t = 0; t < 9; ++t) reversed.push_back(vertices[a*9+t]); } vertices = reversed; } j += width/2-1; shoreWave->m_VBvertices = g_VBMan.Allocate(sizeof(SWavesVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); shoreWave->m_VBvertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBvertices, &vertices[0]); m_ShoreWaves.push_back(shoreWave); } } }
/////////////////////////////////////////////////////////////////// // Calculate our binary heightmap from the terrain heightmap. void WaterManager::RecomputeDistanceHeightmap() { size_t SideSize = m_MapSize*2; if (m_DistanceHeightmap == NULL) m_DistanceHeightmap = new float[SideSize*SideSize]; CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); // Create a manhattan-distance heightmap. // This is currently upsampled by a factor of 2 to get more precision // This could be refined to only be done near the coast itself, but it's probably not necessary. for (size_t z = 0; z < SideSize; ++z) { float level = SideSize; for (size_t x = 0; x < SideSize; ++x) m_DistanceHeightmap[z*SideSize + x] = terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight ? level = 0.f : ++level; level = SideSize; for (size_t x = SideSize-1; x != (size_t)-1; --x) { if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight) level = 0.f; else { ++level; if (level < m_DistanceHeightmap[z*SideSize + x]) m_DistanceHeightmap[z*SideSize + x] = level; } } } for (size_t x = 0; x < SideSize; ++x) { float level = SideSize; for (size_t z = 0; z < SideSize; ++z) { if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight) level = 0.f; else if (level > m_DistanceHeightmap[z*SideSize + x]) level = m_DistanceHeightmap[z*SideSize + x]; else { ++level; if (level < m_DistanceHeightmap[z*SideSize + x]) m_DistanceHeightmap[z*SideSize + x] = level; } } level = SideSize; for (size_t z = SideSize-1; z != (size_t)-1; --z) { if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight) level = 0.f; else if (level > m_DistanceHeightmap[z*SideSize + x]) level = m_DistanceHeightmap[z*SideSize + x]; else { ++level; if (level < m_DistanceHeightmap[z*SideSize + x]) m_DistanceHeightmap[z*SideSize + x] = level; } } } }
/////////////////////////////////////////////////////////////////// // Create information about the terrain and wave vertices. void WaterManager::CreateSuperfancyInfo(CSimulation2* simulation) { if (m_VBWaves) { g_VBMan.Release(m_VBWaves); m_VBWaves = NULL; } if (m_VBWavesIndices) { g_VBMan.Release(m_VBWavesIndices); m_VBWavesIndices = NULL; } CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); ssize_t mapSize = terrain->GetVerticesPerSide(); CmpPtr<ICmpWaterManager> cmpWaterManager(*simulation, SYSTEM_ENTITY); if (!cmpWaterManager) return; // REALLY shouldn't happen and will most likely crash. // Using this to get some more optimization on circular maps CmpPtr<ICmpRangeManager> cmpRangeManager(*simulation, SYSTEM_ENTITY); if (!cmpRangeManager) return; bool circular = cmpRangeManager->GetLosCircular(); float mSize = mapSize*mapSize; float halfSize = (mapSize/2.0); // Warning: this won't work with multiple water planes m_WaterHeight = cmpWaterManager->GetExactWaterLevel(0,0); // TODO: change this whenever we incrementally update because it's def. not too efficient delete[] m_WaveX; delete[] m_WaveZ; delete[] m_DistanceToShore; delete[] m_FoamFactor; m_WaveX = new float[mapSize*mapSize]; m_WaveZ = new float[mapSize*mapSize]; m_DistanceToShore = new float[mapSize*mapSize]; m_FoamFactor = new float[mapSize*mapSize]; u16* heightmap = terrain->GetHeightMap(); // some temporary stuff for wave intensity // not really used too much right now. u8* waveForceHQ = new u8[mapSize*mapSize]; u16 waterHeightInu16 = m_WaterHeight/HEIGHT_SCALE; // used to cache terrain normals since otherwise we'd recalculate them a lot (I'm blurring the "normal" map). // this might be updated to actually cache in the terrain manager but that's not for now. CVector3D* normals = new CVector3D[mapSize*mapSize]; // calculate wave force (not really used right now) // and puts into "normals" the terrain normal at that point // so as to avoid recalculating terrain normals too often. for (ssize_t i = 0; i < mapSize; ++i) { for (ssize_t j = 0; j < mapSize; ++j) { normals[j*mapSize + i] = terrain->CalcExactNormal(((float)i)*4.0f,((float)j)*4.0f); if (circular && (i-halfSize)*(i-halfSize)+(j-halfSize)*(j-halfSize) > mSize) { waveForceHQ[j*mapSize + i] = 255; continue; } u8 color = 0; for (int v = 0; v <= 18; v += 3){ if (j-v >= 0 && i-v >= 0 && heightmap[(j-v)*mapSize + i-v] > waterHeightInu16) { if (color == 0) color = 5; else color++; } } waveForceHQ[j*mapSize + i] = 255 - color * 40; } } // this creates information for waves and stores it in float arrays. PatchRData then puts it in the vertex info for speed. for (ssize_t i = 0; i < mapSize; ++i) { for (ssize_t j = 0; j < mapSize; ++j) { if (circular && (i-halfSize)*(i-halfSize)+(j-halfSize)*(j-halfSize) > mSize) { m_WaveX[j*mapSize + i] = 0.0f; m_WaveZ[j*mapSize + i] = 0.0f; m_DistanceToShore[j*mapSize + i] = 100; m_FoamFactor[j*mapSize + i] = 0.0f; continue; } float depth = m_WaterHeight - heightmap[j*mapSize + i]*HEIGHT_SCALE; int distanceToShore = 10000; // calculation of the distance to the shore. // TODO: this is fairly dumb, though it returns a good result // Could be sped up a fair bit. if (depth >= 0) { // check in the square around. for (int xx = -5; xx <= 5; ++xx) { for (int yy = -5; yy <= 5; ++yy) { if (i+xx >= 0 && i + xx < mapSize) if (j + yy >= 0 && j + yy < mapSize) { float hereDepth = m_WaterHeight - heightmap[(j+yy)*mapSize + (i+xx)]*HEIGHT_SCALE; if (hereDepth < 0 && xx*xx + yy*yy < distanceToShore) distanceToShore = xx*xx + yy*yy; } } } // refine the calculation if we're close enough if (distanceToShore < 9) { for (float xx = -2.5f; xx <= 2.5f; ++xx) { for (float yy = -2.5f; yy <= 2.5f; ++yy) { float hereDepth = m_WaterHeight - terrain->GetExactGroundLevel( (i+xx)*4, (j+yy)*4 ); if (hereDepth < 0 && xx*xx + yy*yy < distanceToShore) distanceToShore = xx*xx + yy*yy; } } } } else { for (int xx = -2; xx <= 2; ++xx) { for (int yy = -2; yy <= 2; ++yy) { float hereDepth = m_WaterHeight - terrain->GetVertexGroundLevel(i+xx, j+yy); if (hereDepth > 0) distanceToShore = 0; } } } // speedup with default values for land squares if (distanceToShore == 10000) { m_WaveX[j*mapSize + i] = 0.0f; m_WaveZ[j*mapSize + i] = 0.0f; m_DistanceToShore[j*mapSize + i] = 100; m_FoamFactor[j*mapSize + i] = 0.0f; continue; } // We'll compute the normals and the "water raise", to know about foam // Normals are a pretty good calculation but it's slow since we normalize so much. CVector3D normal; int waterRaise = 0; for (int xx = -4; xx <= 4; xx += 2) // every 2 tile is good enough. { for (int yy = -4; yy <= 4; yy += 2) { if (j+yy < mapSize && i+xx < mapSize && i+xx >= 0 && j+yy >= 0) normal += normals[(j+yy)*mapSize + (i+xx)]; if (terrain->GetVertexGroundLevel(i+xx,j+yy) < heightmap[j*mapSize + i]*HEIGHT_SCALE) waterRaise += heightmap[j*mapSize + i]*HEIGHT_SCALE - terrain->GetVertexGroundLevel(i+xx,j+yy); } } // normalizes the terrain info to avoid foam moving at too different speeds. normal *= 0.012345679f; normal[1] = 0.1f; normal = normal.Normalized(); m_WaveX[j*mapSize + i] = normal[0]; m_WaveZ[j*mapSize + i] = normal[2]; // distance is /5.0 to be a [0,1] value. m_DistanceToShore[j*mapSize + i] = sqrtf(distanceToShore)/5.0f; // TODO: this can probably be cached as I'm integer here. // computing the amount of foam I want depth = clamp(depth,0.0f,10.0f); float foamAmount = (waterRaise/255.0f) * (1.0f - depth/10.0f) * (waveForceHQ[j*mapSize+i]/255.0f) * (m_Waviness/8.0f); foamAmount += clamp(m_Waviness/2.0f - distanceToShore,0.0f,m_Waviness/2.0f)/(m_Waviness/2.0f) * clamp(m_Waviness/9.0f,0.3f,1.0f); foamAmount = foamAmount > 1.0f ? 1.0f: foamAmount; m_FoamFactor[j*mapSize + i] = foamAmount; } } delete[] normals; delete[] waveForceHQ; // TODO: The rest should be cleaned up // okay let's create the waves squares. i'll divide the map in arbitrary squares // For each of these squares, check if waves are needed. // If yes, look for the best positionning (in order to have a nice blending with the shore) // Then clean-up: remove squares that are too close to each other std::vector<CVector2D> waveSquares; int size = 8; // I think this is the size of the squares. for (int i = 0; i < mapSize/size; ++i) { for (int j = 0; j < mapSize/size; ++j) { int landTexel = 0; int waterTexel = 0; CVector3D avnormal (0.0f,0.0f,0.0f); CVector2D landPosition(0.0f,0.0f); CVector2D waterPosition(0.0f,0.0f); for (int xx = 0; xx < size; ++xx) { for (int yy = 0; yy < size; ++yy) { if (terrain->GetVertexGroundLevel(i*size+xx,j*size+yy) > m_WaterHeight) { landTexel++; landPosition += CVector2D(i*size+xx,j*size+yy); } else { waterPosition += CVector2D(i*size+xx,j*size+yy); waterTexel++; avnormal += terrain->CalcExactNormal( (i*size+xx)*4.0f,(j*size+yy)*4.0f); } } } if (landTexel < size/2) continue; landPosition /= landTexel; waterPosition /= waterTexel; avnormal[1] = 1.0f; avnormal.Normalize(); avnormal[1] = 0.0f; // this should help ensure that the shore is pretty flat. if (avnormal.Length() <= 0.2f) continue; // To get the best position for squares, I start at the mean "ocean" position // And step by step go to the mean "land" position. I keep the position where I change from water to land. // If this never happens, the square is scrapped. if (terrain->GetExactGroundLevel(waterPosition.X*4.0f,waterPosition.Y*4.0f) > m_WaterHeight) continue; CVector2D squarePos(-1,-1); for (u8 i = 0; i < 40; i++) { squarePos = landPosition * (i/40.0f) + waterPosition * (1.0f-(i/40.0f)); if (terrain->GetExactGroundLevel(squarePos.X*4.0f,squarePos.Y*4.0f) > m_WaterHeight) break; } if (squarePos.X == -1) continue; u8 enter = 1; // okaaaaaay. Got a square. Check for proximity. for (unsigned long i = 0; i < waveSquares.size(); i++) { if ( CVector2D(waveSquares[i]-squarePos).LengthSquared() < 80) { enter = 0; break; } } if (enter == 1) waveSquares.push_back(squarePos); } } // Actually create the waves' meshes. std::vector<SWavesVertex> waves_vertex_data; std::vector<GLushort> waves_indices; // loop through each square point. Look in the square around it, calculate the normal // create the square. for (unsigned long i = 0; i < waveSquares.size(); i++) { CVector2D pos(waveSquares[i]); CVector3D avgnorm(0.0f,0.0f,0.0f); for (int xx = -size/2; xx < size/2; ++xx) { for (int yy = -size/2; yy < size/2; ++yy) { avgnorm += terrain->CalcExactNormal((pos.X+xx)*4.0f,(pos.Y+yy)*4.0f); } } avgnorm[1] = 0.1f; // okay crank out a square. // we have the direction of the square. We'll get the perpendicular vector too CVector2D perp(-avgnorm[2],avgnorm[0]); perp = perp.Normalized(); avgnorm = avgnorm.Normalized(); SWavesVertex vertex[4]; vertex[0].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y + perp.Y*(size/2.2f) - avgnorm[2]*1.0f); vertex[0].m_Position *= 4.0f; vertex[0].m_Position.Y = m_WaterHeight + 1.0f; vertex[0].m_UV[1] = 1; vertex[0].m_UV[0] = 0; vertex[1].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y - perp.Y*(size/2.2f) - avgnorm[2]*1.0f); vertex[1].m_Position *= 4.0f; vertex[1].m_Position.Y = m_WaterHeight + 1.0f; vertex[1].m_UV[1] = 1; vertex[1].m_UV[0] = 1; vertex[3].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y + perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f)); vertex[3].m_Position *= 4.0f; vertex[3].m_Position.Y = m_WaterHeight + 1.0f; vertex[3].m_UV[1] = 0; vertex[3].m_UV[0] = 0; vertex[2].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y - perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f)); vertex[2].m_Position *= 4.0f; vertex[2].m_Position.Y = m_WaterHeight + 1.0f; vertex[2].m_UV[1] = 0; vertex[2].m_UV[0] = 1; waves_indices.push_back(waves_vertex_data.size()); waves_vertex_data.push_back(vertex[0]); waves_indices.push_back(waves_vertex_data.size()); waves_vertex_data.push_back(vertex[1]); waves_indices.push_back(waves_vertex_data.size()); waves_vertex_data.push_back(vertex[2]); waves_indices.push_back(waves_vertex_data.size()); waves_vertex_data.push_back(vertex[3]); } // no vertex buffers if no data generated if (waves_indices.empty()) return; // waves // allocate vertex buffer m_VBWaves = g_VBMan.Allocate(sizeof(SWavesVertex), waves_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); m_VBWaves->m_Owner->UpdateChunkVertices(m_VBWaves, &waves_vertex_data[0]); // Construct indices buffer m_VBWavesIndices = g_VBMan.Allocate(sizeof(GLushort), waves_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); m_VBWavesIndices->m_Owner->UpdateChunkVertices(m_VBWavesIndices, &waves_indices[0]); }