void doTerrainModify( Terrain* terrain, const Vector3& centrepos, Real timeElapsed ) { Vector3 tsPos; terrain->getTerrainPosition( centrepos, &tsPos ); #if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS if( mInputContext.isKeyDown( OIS::KC_EQUALS ) || mInputContext.isKeyDown( OIS::KC_ADD ) || mInputContext.isKeyDown( OIS::KC_MINUS ) || mInputContext.isKeyDown( OIS::KC_SUBTRACT ) ) { switch( mMode ) { case MODE_EDIT_HEIGHT: { // we need point coords Real terrainSize = (terrain->getSize() - 1); long startx = (tsPos.x - mBrushSizeTerrainSpace) * terrainSize; long starty = (tsPos.y - mBrushSizeTerrainSpace) * terrainSize; long endx = (tsPos.x + mBrushSizeTerrainSpace) * terrainSize; long endy = (tsPos.y + mBrushSizeTerrainSpace) * terrainSize; startx = std::max( startx, 0L ); starty = std::max( starty, 0L ); endx = std::min( endx, (long) terrainSize ); endy = std::min( endy, (long) terrainSize ); for( long y = starty; y <= endy; ++y ) { for( long x = startx; x <= endx; ++x ) { Real tsXdist = (x / terrainSize) - tsPos.x; Real tsYdist = (y / terrainSize) - tsPos.y; Real weight = std::min( (Real)1.0, Math::Sqrt( tsYdist * tsYdist + tsXdist * tsXdist ) / Real( 0.5 * mBrushSizeTerrainSpace ) ); weight = 1.0 - (weight * weight); float addedHeight = weight * 250.0 * timeElapsed; float newheight; if( mInputContext.isKeyDown( OIS::KC_EQUALS ) || mInputContext.isKeyDown( OIS::KC_ADD ) ) newheight = terrain->getHeightAtPoint( x, y ) + addedHeight; else newheight = terrain->getHeightAtPoint( x, y ) - addedHeight; terrain->setHeightAtPoint( x, y, newheight ); } } if( mHeightUpdateCountDown == 0 ) mHeightUpdateCountDown = mHeightUpdateRate; } break; case MODE_EDIT_BLEND: { TerrainLayerBlendMap* layer = terrain->getLayerBlendMap( mLayerEdit ); // we need image coords Real imgSize = terrain->getLayerBlendMapSize(); long startx = (tsPos.x - mBrushSizeTerrainSpace) * imgSize; long starty = (tsPos.y - mBrushSizeTerrainSpace) * imgSize; long endx = (tsPos.x + mBrushSizeTerrainSpace) * imgSize; long endy = (tsPos.y + mBrushSizeTerrainSpace) * imgSize; startx = std::max( startx, 0L ); starty = std::max( starty, 0L ); endx = std::min( endx, (long) imgSize ); endy = std::min( endy, (long) imgSize ); for( long y = starty; y <= endy; ++y ) { for( long x = startx; x <= endx; ++x ) { Real tsXdist = (x / imgSize) - tsPos.x; Real tsYdist = (y / imgSize) - tsPos.y; Real weight = std::min( (Real)1.0, Math::Sqrt( tsYdist * tsYdist + tsXdist * tsXdist ) / Real( 0.5 * mBrushSizeTerrainSpace ) ); weight = 1.0 - (weight * weight); float paint = weight * timeElapsed; size_t imgY = imgSize - y; float val; if( mInputContext.isKeyDown( OIS::KC_EQUALS ) || mInputContext.isKeyDown( OIS::KC_ADD ) ) val = layer->getBlendValue( x, imgY ) + paint; else val = layer->getBlendValue( x, imgY ) - paint; val = Math::Clamp( val, 0.0f, 1.0f ); layer->setBlendValue( x, imgY, val ); } } layer->update(); } break; case MODE_NORMAL: case MODE_COUNT: break; }; } #endif }
void TerrainGeometryManager::initBlendMaps(int x, int z, Ogre::Terrain* terrain ) { bool debugBlendMaps = BOPT("DebugBlendMaps", false); int layerCount = terrain->getLayerCount(); for (int i = 1; i < layerCount; i++) { blendLayerInfo_t &bi = blendInfo[i]; if(bi.blendMapTextureFilename.empty()) continue; Ogre::Image img; //std::pair<uint8,uint8> textureIndex = terrain->getLayerBlendTextureIndex(i); //uint8 bti = terrain->getBlendTextureIndex(i); try { img.load(bi.blendMapTextureFilename, ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); } catch(Exception &e) { LOG("Error loading blendmap: " + bi.blendMapTextureFilename + " : " + e.getFullDescription()); continue; } TerrainLayerBlendMap *blendmap = terrain->getLayerBlendMap(i); // resize that blending map so it will fit Ogre::uint32 blendmapSize = terrain->getLayerBlendMapSize(); if (img.getWidth() != blendmapSize) img.resize(blendmapSize, blendmapSize); // now to the ugly part float* ptr = blendmap->getBlendPointer(); for (Ogre::uint32 z = 0; z != blendmapSize; z++) { for (Ogre::uint32 x = 0; x != blendmapSize; x++) { Ogre::ColourValue c = img.getColourAt(x, z, 0); float alpha = bi.alpha; if (bi.blendMode == 'R') *ptr++ = c.r * alpha; else if (bi.blendMode == 'G') *ptr++ = c.g * alpha; else if (bi.blendMode == 'B') *ptr++ = c.b * alpha; else if (bi.blendMode == 'A') *ptr++ = c.a * alpha; } } blendmap->dirty(); blendmap->update(); } if (debugBlendMaps) { for (int i = 1; i < layerCount; i++) { Ogre::TerrainLayerBlendMap* blendMap = terrain->getLayerBlendMap(i); Ogre::uint32 blendmapSize = terrain->getLayerBlendMapSize(); Ogre::Image img; unsigned short *idata = OGRE_ALLOC_T(unsigned short, blendmapSize * blendmapSize, Ogre::MEMCATEGORY_RESOURCE); float scale = 65535.0f; for (unsigned int x = 0; x < blendmapSize; x++) for (unsigned int z = 0; z < blendmapSize; z++) idata[x + z * blendmapSize] = (unsigned short)(blendMap->getBlendValue(x, blendmapSize - z) * scale); img.loadDynamicImage((Ogre::uchar*)(idata), blendmapSize, blendmapSize, Ogre::PF_L16); std::string fileName = "blendmap_layer_" + Ogre::StringConverter::toString(i) + ".png"; img.save(fileName); OGRE_FREE(idata, Ogre::MEMCATEGORY_RESOURCE); } } }
void TerrainManager::initTerrainBlendMaps(Terrain* terrain, int cellX, int cellY, int fromX, int fromY, int size, const std::map<uint16_t, int>& indexes) { assert(terrain != NULL && "Must have valid terrain"); assert(fromX >= 0 && fromY >= 0 && "Can't get a terrain texture on terrain outside the current cell"); assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE && fromY+size <= ESM::Land::LAND_TEXTURE_SIZE && "Can't get a terrain texture on terrain outside the current cell"); //size must be a power of 2 as we do divisions with a power of 2 number //that need to result in an integer for correct splatting assert( (size & (size - 1)) == 0 && "Size must be a power of 2"); const int blendMapSize = terrain->getLayerBlendMapSize(); const int splatSize = blendMapSize / size; //zero out every map std::map<uint16_t, int>::const_iterator iter; for ( iter = indexes.begin(); iter != indexes.end(); ++iter ) { float* pBlend = terrain->getLayerBlendMap(iter->second) ->getBlendPointer(); memset(pBlend, 0, sizeof(float) * blendMapSize * blendMapSize); } //covert the ltex data into a set of blend maps for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ ) { for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ ) { const uint16_t ltexIndex = getLtexIndexAt(cellX, cellY, texX, texY); //check if it is the base texture (which isn't in the map) and //if it is don't bother altering the blend map for it if ( indexes.find(ltexIndex) == indexes.end() ) { continue; } //while texX is the splat index relative to the entire cell, //relX is relative to the current segment we are splatting const int relX = texX - fromX; const int relY = texY - fromY; const int layerIndex = indexes.find(ltexIndex)->second; float* const pBlend = terrain->getLayerBlendMap(layerIndex) ->getBlendPointer(); for ( int y = -1; y < splatSize + 1; y++ ) { for ( int x = -1; x < splatSize + 1; x++ ) { //Note: Y is reversed const int splatY = blendMapSize - 1 - relY * splatSize - y; const int splatX = relX * splatSize + x; if ( splatX >= 0 && splatX < blendMapSize && splatY >= 0 && splatY < blendMapSize ) { const int index = (splatY)*blendMapSize + splatX; if ( y >= 0 && y < splatSize && x >= 0 && x < splatSize ) { pBlend[index] = 1; } else { //this provides a transition shading but also //rounds off the corners slightly pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f); } } } } } } for ( int i = 1; i < terrain->getLayerCount(); i++ ) { TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i); blend->dirty(); blend->update(); } }