int TerrainManager::getLtexIndexAt(int cellX, int cellY, int x, int y) { //check texture index falls within the 9 cell bounds //as this function can't cope with anything above that assert(x >= -ESM::Land::LAND_TEXTURE_SIZE && y >= -ESM::Land::LAND_TEXTURE_SIZE && "Trying to get land textures that are out of bounds"); assert(x < 2*ESM::Land::LAND_TEXTURE_SIZE && y < 2*ESM::Land::LAND_TEXTURE_SIZE && "Trying to get land textures that are out of bounds"); if ( x < 0 ) { cellX--; x += ESM::Land::LAND_TEXTURE_SIZE; } else if ( x >= ESM::Land::LAND_TEXTURE_SIZE ) { cellX++; x -= ESM::Land::LAND_TEXTURE_SIZE; } if ( y < 0 ) { cellY--; y += ESM::Land::LAND_TEXTURE_SIZE; } else if ( y >= ESM::Land::LAND_TEXTURE_SIZE ) { cellY++; y -= ESM::Land::LAND_TEXTURE_SIZE; } ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get<ESM::Land>().search(cellX, cellY); if ( land != NULL ) { if (!land->isDataLoaded(ESM::Land::DATA_VTEX)) { land->loadData(ESM::Land::DATA_VTEX); } return land->mLandData ->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; } else { return 0; } }
const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY); if (!land) return NULL; const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; if (!land->isDataLoaded(flags)) land->loadData(flags); // TODO: unload land data when it's no longer needed return land; }
void GlobalMap::render (Loading::Listener* loadingListener) { Ogre::TexturePtr tex; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); // get the size of the world MWWorld::Store<ESM::Cell>::iterator it = esmStore.get<ESM::Cell>().extBegin(); for (; it != esmStore.get<ESM::Cell>().extEnd(); ++it) { if (it->getGridX() < mMinX) mMinX = it->getGridX(); if (it->getGridX() > mMaxX) mMaxX = it->getGridX(); if (it->getGridY() < mMinY) mMinY = it->getGridY(); if (it->getGridY() > mMaxY) mMaxY = it->getGridY(); } mWidth = mCellSize*(mMaxX-mMinX+1); mHeight = mCellSize*(mMaxY-mMinY+1); loadingListener->loadingOn(); loadingListener->setLabel("Creating map"); loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); std::vector<Ogre::uchar> data (mWidth * mHeight * 3); for (int x = mMinX; x <= mMaxX; ++x) { for (int y = mMinY; y <= mMaxY; ++y) { ESM::Land* land = esmStore.get<ESM::Land>().search (x,y); if (land) { int mask = ESM::Land::DATA_WNAM; if (!land->isDataLoaded(mask)) land->loadData(mask); } for (int cellY=0; cellY<mCellSize; ++cellY) { for (int cellX=0; cellX<mCellSize; ++cellX) { int vertexX = static_cast<int>(float(cellX)/float(mCellSize) * 9); int vertexY = static_cast<int>(float(cellY) / float(mCellSize) * 9); int texelX = (x-mMinX) * mCellSize + cellX; int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY); unsigned char r,g,b; float y = 0; if (land && land->mDataTypes & ESM::Land::DATA_WNAM) y = (land->mLandData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f; else y = (SCHAR_MIN << 4) / 2048.f; if (y < 0) { r = static_cast<unsigned char>(14 * y + 38); g = static_cast<unsigned char>(20 * y + 56); b = static_cast<unsigned char>(18 * y + 51); } else if (y < 0.3f) { if (y < 0.1f) y *= 8.f; else { y -= 0.1f; y += 0.8f; } r = static_cast<unsigned char>(66 - 32 * y); g = static_cast<unsigned char>(48 - 23 * y); b = static_cast<unsigned char>(33 - 16 * y); } else { y -= 0.3f; y *= 1.428f; r = static_cast<unsigned char>(34 - 29 * y); g = static_cast<unsigned char>(25 - 20 * y); b = static_cast<unsigned char>(17 - 12 * y); } data[texelY * mWidth * 3 + texelX * 3] = r; data[texelY * mWidth * 3 + texelX * 3+1] = g; data[texelY * mWidth * 3 + texelX * 3+2] = b; } } loadingListener->increaseProgress(); if (land) land->unloadData(); } } Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC); tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8); tex->load(); mOverlayTexture = Ogre::TextureManager::getSingleton().createManual("GlobalMapOverlay", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_A8B8G8R8, Ogre::TU_DYNAMIC, this); clear(); loadingListener->loadingOff(); }
void GlobalMap::render (Loading::Listener* loadingListener) { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); // get the size of the world MWWorld::Store<ESM::Cell>::iterator it = esmStore.get<ESM::Cell>().extBegin(); for (; it != esmStore.get<ESM::Cell>().extEnd(); ++it) { if (it->getGridX() < mMinX) mMinX = it->getGridX(); if (it->getGridX() > mMaxX) mMaxX = it->getGridX(); if (it->getGridY() < mMinY) mMinY = it->getGridY(); if (it->getGridY() > mMaxY) mMaxY = it->getGridY(); } mWidth = mCellSize*(mMaxX-mMinX+1); mHeight = mCellSize*(mMaxY-mMinY+1); loadingListener->loadingOn(); loadingListener->setLabel("Creating map"); loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE); unsigned char* data = image->data(); for (int x = mMinX; x <= mMaxX; ++x) { for (int y = mMinY; y <= mMaxY; ++y) { ESM::Land* land = esmStore.get<ESM::Land>().search (x,y); if (land) { int mask = ESM::Land::DATA_WNAM; if (!land->isDataLoaded(mask)) land->loadData(mask); } const ESM::Land::LandData *landData = land ? land->getLandData (ESM::Land::DATA_WNAM) : 0; for (int cellY=0; cellY<mCellSize; ++cellY) { for (int cellX=0; cellX<mCellSize; ++cellX) { int vertexX = static_cast<int>(float(cellX)/float(mCellSize) * 9); int vertexY = static_cast<int>(float(cellY) / float(mCellSize) * 9); int texelX = (x-mMinX) * mCellSize + cellX; int texelY = (y-mMinY) * mCellSize + cellY; unsigned char r,g,b; float y = 0; if (landData) y = (landData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f; else y = (SCHAR_MIN << 4) / 2048.f; if (y < 0) { r = static_cast<unsigned char>(14 * y + 38); g = static_cast<unsigned char>(20 * y + 56); b = static_cast<unsigned char>(18 * y + 51); } else if (y < 0.3f) { if (y < 0.1f) y *= 8.f; else { y -= 0.1f; y += 0.8f; } r = static_cast<unsigned char>(66 - 32 * y); g = static_cast<unsigned char>(48 - 23 * y); b = static_cast<unsigned char>(33 - 16 * y); } else { y -= 0.3f; y *= 1.428f; r = static_cast<unsigned char>(34 - 29 * y); g = static_cast<unsigned char>(25 - 20 * y); b = static_cast<unsigned char>(17 - 12 * y); } data[texelY * mWidth * 3 + texelX * 3] = r; data[texelY * mWidth * 3 + texelX * 3+1] = g; data[texelY * mWidth * 3 + texelX * 3+2] = b; } } loadingListener->increaseProgress(); if (land) land->unloadData(); } } mBaseTexture = new osg::Texture2D; mBaseTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mBaseTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mBaseTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mBaseTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mBaseTexture->setImage(image); mBaseTexture->setResizeNonPowerOfTwoHint(false); clear(); loadingListener->loadingOff(); }
void TerrainManager::cellAdded(MWWorld::Ptr::CellStore *store) { const int cellX = store->mCell->getGridX(); const int cellY = store->mCell->getGridY(); ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get<ESM::Land>().search(cellX, cellY); if (land == NULL) // no land data means we're not going to create any terrain. return; int dataRequired = ESM::Land::DATA_VHGT | ESM::Land::DATA_VCLR; if (!land->isDataLoaded(dataRequired)) { land->loadData(dataRequired); } //split the cell terrain into four segments const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; for ( int x = 0; x < 2; x++ ) { for ( int y = 0; y < 2; y++ ) { Terrain::ImportData terrainData = mTerrainGroup.getDefaultImportSettings(); const int terrainX = cellX * 2 + x; const int terrainY = cellY * 2 + y; //it makes far more sense to reallocate the memory here, //and let Ogre deal with it due to the issues with deleting //it at the wrong time if using threads (Which Terrain does) terrainData.inputFloat = OGRE_ALLOC_T(float, mLandSize*mLandSize, MEMCATEGORY_GEOMETRY); //copy the height data row by row for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ ) { //the offset of the current segment const size_t yOffset = y * (mLandSize-1) * ESM::Land::LAND_SIZE + //offset of the row terrainCopyY * ESM::Land::LAND_SIZE; const size_t xOffset = x * (mLandSize-1); memcpy(&terrainData.inputFloat[terrainCopyY*mLandSize], &land->mLandData->mHeights[yOffset + xOffset], mLandSize*sizeof(float)); } std::map<uint16_t, int> indexes; initTerrainTextures(&terrainData, cellX, cellY, x * numTextures, y * numTextures, numTextures, indexes, land->mPlugin); if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL) { mTerrainGroup.defineTerrain(terrainX, terrainY, &terrainData); mTerrainGroup.loadTerrain(terrainX, terrainY, true); Terrain* terrain = mTerrainGroup.getTerrain(terrainX, terrainY); initTerrainBlendMaps(terrain, cellX, cellY, x * numTextures, y * numTextures, numTextures, indexes); terrain->setVisibilityFlags(RV_Terrain); terrain->setRenderQueueGroup(RQG_Main); // disable or enable global colour map (depends on available vertex colours) if ( land->mLandData->mUsingColours ) { TexturePtr vertex = getVertexColours(land, cellX, cellY, x*(mLandSize-1), y*(mLandSize-1), mLandSize); mActiveProfile->setGlobalColourMapEnabled(true); mActiveProfile->setGlobalColourMap (terrain, vertex->getName()); } else mActiveProfile->setGlobalColourMapEnabled (false); } } } // when loading from a heightmap, Ogre::Terrain does not update the derived data (normal map, LOD) // synchronously, even if we supply synchronous = true parameter to loadTerrain. // the following to be the only way to make sure derived data is ready when rendering the next frame. while (mTerrainGroup.isDerivedDataUpdateInProgress()) { // we need to wait for this to finish OGRE_THREAD_SLEEP(5); Root::getSingleton().getWorkQueue()->processResponses(); } mTerrainGroup.freeTemporaryResources(); }