void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row) { if (col == ESM::Land::LAND_SIZE-1) { ++cellY; col = 0; } if (row == ESM::Land::LAND_SIZE-1) { ++cellX; row = 0; } ESM::Land* land = getLand(cellX, cellY); if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { color.r() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; color.b() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; } else { color.r() = 1; color.g() = 1; color.b() = 1; } }
Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, int x, int y) { // For the first/last row/column, we need to get the texture from the neighbour cell // to get consistent blending at the borders --x; if (x < 0) { --cellX; x += ESM::Land::LAND_TEXTURE_SIZE; } if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? { ++cellY; y -= ESM::Land::LAND_TEXTURE_SIZE; } assert(x<ESM::Land::LAND_TEXTURE_SIZE); assert(y<ESM::Land::LAND_TEXTURE_SIZE); ESM::Land* land = getLand(cellX, cellY); if (land && (land->mDataTypes&ESM::Land::DATA_VTEX)) { int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin return std::make_pair(tex, land->mPlugin); } else return std::make_pair(0,0); }
void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row) { while (col >= ESM::Land::LAND_SIZE-1) { ++cellY; col -= ESM::Land::LAND_SIZE-1; } while (row >= ESM::Land::LAND_SIZE-1) { ++cellX; row -= ESM::Land::LAND_SIZE-1; } while (col < 0) { --cellY; col += ESM::Land::LAND_SIZE-1; } while (row < 0) { --cellX; row += ESM::Land::LAND_SIZE-1; } ESM::Land* land = getLand(cellX, cellY); if (land && land->mDataTypes&ESM::Land::DATA_VNML) { normal.x() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; normal.z() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; normal.normalize(); } else normal = osg::Vec3f(0,0,1); }
bool Storage::getMinMaxHeights(float size, const osg::Vec2f ¢er, float &min, float &max) { assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); assert(origin.x() == (int) origin.x()); assert(origin.y() == (int) origin.y()); int cellX = static_cast<int>(origin.x()); int cellY = static_cast<int>(origin.y()); const ESM::Land* land = getLand(cellX, cellY); if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) return false; min = std::numeric_limits<float>::max(); max = -std::numeric_limits<float>::max(); for (int row=0; row<ESM::Land::LAND_SIZE; ++row) { for (int col=0; col<ESM::Land::LAND_SIZE; ++col) { float h = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; if (h > max) max = h; if (h < min) min = h; } } return true; }
QString RiskCard::getCardName() { QString ret = ""; switch( getSymbol() ) { case INFANTRY: ret = "I-"; break; case CAVALRY: ret = "C-"; break; case ARTILLERY: ret = "A-"; break; } ret.append( getLand()->getLandName() ); return( ret ); }
float Storage::getHeightAt(const osg::Vec3f &worldPos) { int cellX = static_cast<int>(std::floor(worldPos.x() / 8192.f)); int cellY = static_cast<int>(std::floor(worldPos.y() / 8192.f)); ESM::Land* land = getLand(cellX, cellY); if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) return -2048; // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell float nX = (worldPos.x() - (cellX * 8192))/8192.f; float nY = (worldPos.y() - (cellY * 8192))/8192.f; // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; float invFactor = 1.0f / factor; int startX = static_cast<int>(nX * factor); int startY = static_cast<int>(nY * factor); int endX = startX + 1; int endY = startY + 1; endX = std::min(endX, ESM::Land::LAND_SIZE-1); endY = std::min(endY, ESM::Land::LAND_SIZE-1); // now get points in terrain space (effectively rounding them to boundaries) float startXTS = startX * invFactor; float startYTS = startY * invFactor; float endXTS = endX * invFactor; float endYTS = endY * invFactor; // get parametric from start coord to next point float xParam = (nX - startXTS) * factor; float yParam = (nY - startYTS) * factor; /* For even / odd tri strip rows, triangles are this shape: even odd 3---2 3---2 | / | | \ | 0---1 0---1 */ // Build all 4 positions in normalized cell space, using point-sampled height osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment if (true) { // odd row bool secondTri = ((1.0 - yParam) > xParam); if (secondTri) plane = osg::Plane(v0, v1, v3); else plane = osg::Plane(v1, v2, v3); } /* else { // even row bool secondTri = (yParam > xParam); if (secondTri) plane.redefine(v0, v2, v3); else plane.redefine(v0, v1, v2); } */ // Solve plane equation for z return (-plane.getNormal().x() * nX -plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * 8192; }
void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals, osg::ref_ptr<osg::Vec4Array> colours) { // LOD level n means every 2^n-th vertex is kept size_t increment = 1 << lodLevel; osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); assert(origin.x() == (int) origin.x()); assert(origin.y() == (int) origin.y()); int startX = static_cast<int>(origin.x()); int startY = static_cast<int>(origin.y()); size_t numVerts = static_cast<size_t>(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); positions->resize(numVerts*numVerts); normals->resize(numVerts*numVerts); colours->resize(numVerts*numVerts); osg::Vec3f normal; osg::Vec4f color; float vertY = 0; float vertX = 0; float vertY_ = 0; // of current cell corner for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) { float vertX_ = 0; // of current cell corner for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) { ESM::Land* land = getLand(cellX, cellY); if (land && !(land->mDataTypes&ESM::Land::DATA_VHGT)) land = NULL; int rowStart = 0; int colStart = 0; // Skip the first row / column unless we're at a chunk edge, // since this row / column is already contained in a previous cell if (colStart == 0 && vertY_ != 0) colStart += increment; if (rowStart == 0 && vertX_ != 0) rowStart += increment; vertY = vertY_; for (int col=colStart; col<ESM::Land::LAND_SIZE; col += increment) { vertX = vertX_; for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment) { float height = -2048; if (land) height = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE + row]; (*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)] = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, (vertY / float(numVerts - 1) - 0.5f) * size * 8192, height); if (land && land->mDataTypes&ESM::Land::DATA_VNML) { normal.x() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; normal.z() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; normal.normalize(); } else normal = osg::Vec3f(0,0,1); // Normals apparently don't connect seamlessly between cells if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixNormal(normal, cellX, cellY, col, row); // some corner normals appear to be complete garbage (z < 0) if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) averageNormal(normal, cellX, cellY, col, row); assert(normal.z() > 0); (*normals)[static_cast<unsigned int>(vertX*numVerts + vertY)] = normal; if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { color.r() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; color.b() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; } else { color.r() = 1; color.g() = 1; color.b() = 1; } // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixColour(color, cellX, cellY, col, row); color.a() = 1; (*colours)[static_cast<unsigned int>(vertX*numVerts + vertY)] = color; ++vertX; } ++vertY; } vertX_ = vertX; } vertY_ = vertY; assert(vertX_ == numVerts); // Ensure we covered whole area } assert(vertY_ == numVerts); // Ensure we covered whole area }
void LLViewerRegion::unpackRegionHandshake() { LLMessageSystem *msg = gMessageSystem; U32 region_flags; U8 sim_access; std::string sim_name; LLUUID sim_owner; BOOL is_estate_manager; F32 water_height; F32 billable_factor; LLUUID cache_id; msg->getU32 ("RegionInfo", "RegionFlags", region_flags); msg->getU8 ("RegionInfo", "SimAccess", sim_access); msg->getString ("RegionInfo", "SimName", sim_name); msg->getUUID ("RegionInfo", "SimOwner", sim_owner); msg->getBOOL ("RegionInfo", "IsEstateManager", is_estate_manager); msg->getF32 ("RegionInfo", "WaterHeight", water_height); msg->getF32 ("RegionInfo", "BillableFactor", billable_factor); msg->getUUID ("RegionInfo", "CacheID", cache_id ); setRegionFlags(region_flags); setSimAccess(sim_access); setRegionNameAndZone(sim_name); setOwner(sim_owner); setIsEstateManager(is_estate_manager); setWaterHeight(water_height); setBillableFactor(billable_factor); setCacheID(cache_id); LLUUID region_id; msg->getUUID("RegionInfo2", "RegionID", region_id); setRegionID(region_id); // Retrieve the CR-53 (Homestead/Land SKU) information S32 classID = 0; S32 cpuRatio = 0; std::string coloName; std::string productSKU; std::string productName; // the only reasonable way to decide if we actually have any data is to // check to see if any of these fields have positive sizes if (msg->getSize("RegionInfo3", "ColoName") > 0 || msg->getSize("RegionInfo3", "ProductSKU") > 0 || msg->getSize("RegionInfo3", "ProductName") > 0) { msg->getS32 ("RegionInfo3", "CPUClassID", classID); msg->getS32 ("RegionInfo3", "CPURatio", cpuRatio); msg->getString ("RegionInfo3", "ColoName", coloName); msg->getString ("RegionInfo3", "ProductSKU", productSKU); msg->getString ("RegionInfo3", "ProductName", productName); mClassID = classID; mCPURatio = cpuRatio; mColoName = coloName; mProductSKU = productSKU; mProductName = productName; } LLVLComposition *compp = getComposition(); if (compp) { LLUUID tmp_id; msg->getUUID("RegionInfo", "TerrainDetail0", tmp_id); compp->setDetailTextureID(0, tmp_id); msg->getUUID("RegionInfo", "TerrainDetail1", tmp_id); compp->setDetailTextureID(1, tmp_id); msg->getUUID("RegionInfo", "TerrainDetail2", tmp_id); compp->setDetailTextureID(2, tmp_id); msg->getUUID("RegionInfo", "TerrainDetail3", tmp_id); compp->setDetailTextureID(3, tmp_id); F32 tmp_f32; msg->getF32("RegionInfo", "TerrainStartHeight00", tmp_f32); compp->setStartHeight(0, tmp_f32); msg->getF32("RegionInfo", "TerrainStartHeight01", tmp_f32); compp->setStartHeight(1, tmp_f32); msg->getF32("RegionInfo", "TerrainStartHeight10", tmp_f32); compp->setStartHeight(2, tmp_f32); msg->getF32("RegionInfo", "TerrainStartHeight11", tmp_f32); compp->setStartHeight(3, tmp_f32); msg->getF32("RegionInfo", "TerrainHeightRange00", tmp_f32); compp->setHeightRange(0, tmp_f32); msg->getF32("RegionInfo", "TerrainHeightRange01", tmp_f32); compp->setHeightRange(1, tmp_f32); msg->getF32("RegionInfo", "TerrainHeightRange10", tmp_f32); compp->setHeightRange(2, tmp_f32); msg->getF32("RegionInfo", "TerrainHeightRange11", tmp_f32); compp->setHeightRange(3, tmp_f32); // If this is an UPDATE (params already ready, we need to regenerate // all of our terrain stuff, by if (compp->getParamsReady()) { getLand().dirtyAllPatches(); } else { compp->setParamsReady(); } } // Now that we have the name, we can load the cache file // off disk. loadObjectCache(); // After loading cache, signal that simulator can start // sending data. // TODO: Send all upstream viewer->sim handshake info here. LLHost host = msg->getSender(); msg->newMessage("RegionHandshakeReply"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); msg->nextBlock("RegionInfo"); msg->addU32("Flags", 0x0 ); msg->sendReliable(host); }