|
|
|
@ -352,11 +352,6 @@ namespace ESMTerrain
|
|
|
|
|
|
|
|
|
|
std::string Storage::getTextureName(UniqueTextureId id)
|
|
|
|
|
{
|
|
|
|
|
// Goes under used terrain blend transitions
|
|
|
|
|
static constexpr char baseTexture[] = "textures\\tx_black_01.dds";
|
|
|
|
|
if (id.first == -1)
|
|
|
|
|
return baseTexture;
|
|
|
|
|
|
|
|
|
|
static constexpr char defaultTexture[] = "textures\\_land_default.dds";
|
|
|
|
|
if (id.first == 0)
|
|
|
|
|
return defaultTexture; // Not sure if the default texture really is hardcoded?
|
|
|
|
@ -385,72 +380,44 @@ namespace ESMTerrain
|
|
|
|
|
|
|
|
|
|
int rowStart = (origin.x() - cellX) * realTextureSize;
|
|
|
|
|
int colStart = (origin.y() - cellY) * realTextureSize;
|
|
|
|
|
int rowEnd = rowStart + chunkSize * (realTextureSize-1) + 1;
|
|
|
|
|
int colEnd = colStart + chunkSize * (realTextureSize-1) + 1;
|
|
|
|
|
|
|
|
|
|
// Save the used texture indices so we know the total number of textures
|
|
|
|
|
// and number of required blend maps
|
|
|
|
|
std::set<UniqueTextureId> textureIndices;
|
|
|
|
|
// Due to the way the blending works, the base layer will bleed between texture transitions so we want it to be a black texture
|
|
|
|
|
// The subsequent passes are added instead of blended, so this gives the correct result
|
|
|
|
|
textureIndices.insert(std::make_pair(-1,0)); // -1 goes to tx_black_01
|
|
|
|
|
|
|
|
|
|
LandCache cache;
|
|
|
|
|
|
|
|
|
|
for (int y=colStart; y<colEnd; ++y)
|
|
|
|
|
for (int x=rowStart; x<rowEnd; ++x)
|
|
|
|
|
{
|
|
|
|
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y, cache);
|
|
|
|
|
textureIndices.insert(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Makes sure the indices are sorted, or rather,
|
|
|
|
|
// retrieved as sorted. This is important to keep the splatting order
|
|
|
|
|
// consistent across cells.
|
|
|
|
|
std::map<UniqueTextureId, int> textureIndicesMap;
|
|
|
|
|
for (std::set<UniqueTextureId>::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
int size = textureIndicesMap.size();
|
|
|
|
|
textureIndicesMap[*it] = size;
|
|
|
|
|
layerList.push_back(getLayerInfo(getTextureName(*it)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// size-1 since the base layer doesn't need blending
|
|
|
|
|
int numBlendmaps = textureIndices.size() - 1;
|
|
|
|
|
|
|
|
|
|
// Second iteration - create and fill in the blend maps
|
|
|
|
|
const int blendmapSize = (realTextureSize-1) * chunkSize + 1;
|
|
|
|
|
// We need to upscale the blendmap 2x with nearest neighbor sampling to look like Vanilla
|
|
|
|
|
const int imageScaleFactor = 2;
|
|
|
|
|
const int blendmapImageSize = blendmapSize * imageScaleFactor;
|
|
|
|
|
|
|
|
|
|
for (int i=0; i<numBlendmaps; ++i)
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<osg::Image> image (new osg::Image);
|
|
|
|
|
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
|
|
|
|
unsigned char* pData = image->data();
|
|
|
|
|
LandCache cache;
|
|
|
|
|
std::map<UniqueTextureId, int> textureIndicesMap;
|
|
|
|
|
|
|
|
|
|
for (int y=0; y<blendmapSize; ++y)
|
|
|
|
|
for (int y=0; y<blendmapSize; ++y)
|
|
|
|
|
{
|
|
|
|
|
for (int x=0; x<blendmapSize; ++x)
|
|
|
|
|
{
|
|
|
|
|
for (int x=0; x<blendmapSize; ++x)
|
|
|
|
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
|
|
|
|
|
std::map<UniqueTextureId, int>::iterator found = textureIndicesMap.find(id);
|
|
|
|
|
if (found == textureIndicesMap.end())
|
|
|
|
|
{
|
|
|
|
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
|
|
|
|
|
assert(textureIndicesMap.find(id) != textureIndicesMap.end());
|
|
|
|
|
int layerIndex = textureIndicesMap.find(id)->second;
|
|
|
|
|
|
|
|
|
|
int alpha = (layerIndex == i+1) ? 255 : 0;
|
|
|
|
|
|
|
|
|
|
int realY = (blendmapSize - y - 1)*imageScaleFactor;
|
|
|
|
|
int realX = x*imageScaleFactor;
|
|
|
|
|
|
|
|
|
|
pData[(realY+0)*blendmapImageSize + realX + 0] = alpha;
|
|
|
|
|
pData[(realY+1)*blendmapImageSize + realX + 0] = alpha;
|
|
|
|
|
pData[(realY+0)*blendmapImageSize + realX + 1] = alpha;
|
|
|
|
|
pData[(realY+1)*blendmapImageSize + realX + 1] = alpha;
|
|
|
|
|
found = textureIndicesMap.insert(std::make_pair(id, textureIndicesMap.size())).first;
|
|
|
|
|
layerList.push_back(getLayerInfo(getTextureName(id)));
|
|
|
|
|
osg::ref_ptr<osg::Image> image (new osg::Image);
|
|
|
|
|
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
|
|
|
|
unsigned char* pData = image->data();
|
|
|
|
|
memset(pData, 0, image->getTotalDataSize());
|
|
|
|
|
blendmaps.push_back(image);
|
|
|
|
|
}
|
|
|
|
|
int layerIndex = found->second;
|
|
|
|
|
unsigned char* pData = blendmaps[layerIndex]->data();
|
|
|
|
|
int realY = (blendmapSize - y - 1)*imageScaleFactor;
|
|
|
|
|
int realX = x*imageScaleFactor;
|
|
|
|
|
pData[((realY+0)*blendmapImageSize + realX + 0)] = 255;
|
|
|
|
|
pData[((realY+1)*blendmapImageSize + realX + 0)] = 255;
|
|
|
|
|
pData[((realY+0)*blendmapImageSize + realX + 1)] = 255;
|
|
|
|
|
pData[((realY+1)*blendmapImageSize + realX + 1)] = 255;
|
|
|
|
|
}
|
|
|
|
|
blendmaps.push_back(image);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (blendmaps.size() == 1)
|
|
|
|
|
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Storage::getHeightAt(const osg::Vec3f &worldPos)
|
|
|
|
|