int lua_TerrainPatch_getMaterialCount(lua_State* state) { // Get the number of parameters. int paramCount = lua_gettop(state); // Attempt to match the parameters to a valid binding. switch (paramCount) { case 1: { if ((lua_type(state, 1) == LUA_TUSERDATA)) { TerrainPatch* instance = getInstance(state); unsigned int result = instance->getMaterialCount(); // Push the return value onto the stack. lua_pushunsigned(state, result); return 1; } lua_pushstring(state, "lua_TerrainPatch_getMaterialCount - Failed to match the given parameters to a valid function signature."); lua_error(state); break; } default: { lua_pushstring(state, "Invalid number of parameters (expected 1)."); lua_error(state); break; } } return 0; }
void Terrain::UpdatePatchLod(TerrainPatch* patch) { Geometry* geometry = patch->GetGeometry(); // All LOD levels except the coarsest have 16 versions for stitching unsigned lodLevel = patch->GetLodLevel(); unsigned drawRangeIndex = lodLevel << 4; if (lodLevel < numLodLevels_ - 1) { TerrainPatch* north = patch->GetNorthPatch(); TerrainPatch* south = patch->GetSouthPatch(); TerrainPatch* west = patch->GetWestPatch(); TerrainPatch* east = patch->GetEastPatch(); if (north && north->GetLodLevel() > lodLevel) drawRangeIndex |= STITCH_NORTH; if (south && south->GetLodLevel() > lodLevel) drawRangeIndex |= STITCH_SOUTH; if (west && west->GetLodLevel() > lodLevel) drawRangeIndex |= STITCH_WEST; if (east && east->GetLodLevel() > lodLevel) drawRangeIndex |= STITCH_EAST; } if (drawRangeIndex < drawRanges_.Size()) geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[drawRangeIndex].first_, drawRanges_[drawRangeIndex].second_, false); }
float Camera::getActualHeight(vec3 objectPos) { float actualY; vec3 tempObjectPos = objectPos; vec3 tempCameraPos = position; tempObjectPos.y = 0; tempCameraPos.y = 0; // (gridSize-2)*patchSize is the maximum distance, -3 for safety. if(Norm(tempCameraPos - tempObjectPos) < (float)(2*patchSize)) { float xPosition,zPosition; if(objectPos.x < 0.0) { xPosition = blendedSize-1*(fmod(-1*objectPos.x,(float)(blendedSize))); } else { xPosition = fmod(objectPos.x,(float)(blendedSize)); } if(objectPos.z < 0.0) { zPosition = blendedSize-1*(fmod(-1*objectPos.z,(float)(blendedSize))); } else { zPosition = fmod(objectPos.z,(float)(blendedSize)); } std::vector<TerrainPatch*> objectActualPatchRow; TerrainPatch *objectActualPatch; int patchXIndex = floor((objectPos.x - xPosition)/(float)(blendedSize)); int patchZIndex = floor((objectPos.z - zPosition)/(float)(blendedSize)); for(int i = 0; i < gridSize; i++){ if(terrainVector->at(i).at(0)->yGrid == patchZIndex){ objectActualPatchRow = terrainVector->at(i); } } // Can trig out of range in objectActualPatchRow and probably // earlier in terrainVector also. for(int i = 0; i < gridSize; i++){ if(objectActualPatchRow.at(i)->xGrid == patchXIndex){ objectActualPatch = objectActualPatchRow.at(i); } } actualY = objectActualPatch->calcHeight(xPosition,zPosition); } else actualY = 90.0; return actualY; }
int lua_TerrainPatch_getBoundingBox(lua_State* state) { // Get the number of parameters. int paramCount = lua_gettop(state); // Attempt to match the parameters to a valid binding. switch (paramCount) { case 2: { if ((lua_type(state, 1) == LUA_TUSERDATA) && lua_type(state, 2) == LUA_TBOOLEAN) { // Get parameter 1 off the stack. bool param1 = gameplay::ScriptUtil::luaCheckBool(state, 2); TerrainPatch* instance = getInstance(state); void* returnPtr = (void*)&(instance->getBoundingBox(param1)); if (returnPtr) { gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject)); object->instance = returnPtr; object->owns = false; luaL_getmetatable(state, "BoundingBox"); lua_setmetatable(state, -2); } else { lua_pushnil(state); } return 1; } lua_pushstring(state, "lua_TerrainPatch_getBoundingBox - Failed to match the given parameters to a valid function signature."); lua_error(state); break; } default: { lua_pushstring(state, "Invalid number of parameters (expected 2)."); lua_error(state); break; } } return 0; }
int lua_TerrainPatch_cameraChanged(lua_State* state) { // Get the number of parameters. int paramCount = lua_gettop(state); // Attempt to match the parameters to a valid binding. switch (paramCount) { case 2: { if ((lua_type(state, 1) == LUA_TUSERDATA) && (lua_type(state, 2) == LUA_TUSERDATA || lua_type(state, 2) == LUA_TTABLE || lua_type(state, 2) == LUA_TNIL)) { // Get parameter 1 off the stack. bool param1Valid; gameplay::ScriptUtil::LuaArray<Camera> param1 = gameplay::ScriptUtil::getObjectPointer<Camera>(2, "Camera", false, ¶m1Valid); if (!param1Valid) { lua_pushstring(state, "Failed to convert parameter 1 to type 'Camera'."); lua_error(state); } TerrainPatch* instance = getInstance(state); instance->cameraChanged(param1); return 0; } lua_pushstring(state, "lua_TerrainPatch_cameraChanged - Failed to match the given parameters to a valid function signature."); lua_error(state); break; } default: { lua_pushstring(state, "Invalid number of parameters (expected 2)."); lua_error(state); break; } } return 0; }
void TerrainPatch::SetIndexSimplification() { TerrainPatch* west = m_parentTerrain->GetWestNeighborOf ( m_column, m_row ); TerrainPatch* east = m_parentTerrain->GetEastNeighborOf ( m_column, m_row ); TerrainPatch* north = m_parentTerrain->GetNorthNeighborOf ( m_column, m_row ); TerrainPatch* south = m_parentTerrain->GetSouthNeighborOf ( m_column, m_row ); m_mipLevels[ (int) m_LOD]->SetIndexBlockSimplification( (west != NULL) ? (int)( (int)west->LOD() - (int)m_LOD) : 0, (east != NULL) ? (int)( (int)east->LOD() - (int)m_LOD) : 0, (north != NULL) ? (int)( (int)north->LOD() - (int)m_LOD) : 0, (south != NULL) ? (int)( (int)south->LOD() - (int)m_LOD) : 0); }
void TerrainFunction::AddFunction(TerrainPatch& patch) const { // Read all frequently used data from pGrid. const vec2& world_south_west(patch.GetSouthWest()); const vec2& world_north_east(patch.GetNorthEast()); const int vertex_count_x = patch.GetVertexRes(); const int vertex_count_y = patch.GetVertexRes(); const float grid_width = patch.GetPatchSize(); const float grid_height = patch.GetPatchSize(); float a; // Calculate start X vertex index. int vertex_minimum_x; a = position_.x-outer_radius_; if (a <= world_south_west.x) { vertex_minimum_x = 0; } else { vertex_minimum_x = (int)::floor((a-world_south_west.x) * vertex_count_x / grid_width); } // Calculate end X vertex index. int vertex_maximum_x; a = position_.x+outer_radius_; if (a >= world_north_east.x) { vertex_maximum_x = vertex_count_x; } else { vertex_maximum_x = (int)::ceil((a-world_south_west.x) * vertex_count_x / grid_width); } // Calculate start Y vertex index. int vertex_minimum_y; a = position_.y-outer_radius_; if (a <= world_south_west.y) { vertex_minimum_y = 0; } else { vertex_minimum_y = (int)::floor((a-world_south_west.y) * vertex_count_y / grid_height); } // Calculate end Y vertex index. int vertex_maximum_y; a = position_.y+outer_radius_; if (a >= world_north_east.y) { vertex_maximum_y = vertex_count_y; } else { vertex_maximum_y = (int)::ceil((a-world_south_west.y) * vertex_count_y / grid_height); } patch.IterateOverPatch(*this, vertex_minimum_x, vertex_maximum_x, vertex_minimum_y, vertex_maximum_y); }
void Terrain::CreateGeometry() { recreateTerrain_ = false; if (!node_) return; URHO3D_PROFILE(CreateTerrainGeometry); unsigned prevNumPatches = patches_.Size(); // Determine number of LOD levels unsigned lodSize = (unsigned)patchSize_; numLodLevels_ = 1; while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < maxLodLevels_) { lodSize >>= 1; ++numLodLevels_; } // Determine total terrain size patchWorldSize_ = Vector2(spacing_.x_ * (float)patchSize_, spacing_.z_ * (float)patchSize_); bool updateAll = false; if (heightMap_) { numPatches_ = IntVector2((heightMap_->GetWidth() - 1) / patchSize_, (heightMap_->GetHeight() - 1) / patchSize_); numVertices_ = IntVector2(numPatches_.x_ * patchSize_ + 1, numPatches_.y_ * patchSize_ + 1); patchWorldOrigin_ = Vector2(-0.5f * (float)numPatches_.x_ * patchWorldSize_.x_, -0.5f * (float)numPatches_.y_ * patchWorldSize_.y_); if (numVertices_ != lastNumVertices_ || lastSpacing_ != spacing_ || patchSize_ != lastPatchSize_) updateAll = true; unsigned newDataSize = (unsigned)(numVertices_.x_ * numVertices_.y_); // Create new height data if terrain size changed if (!heightData_ || updateAll) heightData_ = new float[newDataSize]; // Ensure that the source (unsmoothed) data exists if smoothing is active if (smoothing_ && (!sourceHeightData_ || updateAll)) { sourceHeightData_ = new float[newDataSize]; updateAll = true; } else if (!smoothing_) sourceHeightData_.Reset(); } else { numPatches_ = IntVector2::ZERO; numVertices_ = IntVector2::ZERO; patchWorldOrigin_ = Vector2::ZERO; heightData_.Reset(); sourceHeightData_.Reset(); } lastNumVertices_ = numVertices_; lastPatchSize_ = patchSize_; lastSpacing_ = spacing_; // Remove old patch nodes which are not needed if (updateAll) { URHO3D_PROFILE(RemoveOldPatches); PODVector<Node*> oldPatchNodes; node_->GetChildrenWithComponent<TerrainPatch>(oldPatchNodes); for (PODVector<Node*>::Iterator i = oldPatchNodes.Begin(); i != oldPatchNodes.End(); ++i) { bool nodeOk = false; Vector<String> coords = (*i)->GetName().Substring(6).Split('_'); if (coords.Size() == 2) { int x = ToInt(coords[0]); int z = ToInt(coords[1]); if (x < numPatches_.x_ && z < numPatches_.y_) nodeOk = true; } if (!nodeOk) node_->RemoveChild(*i); } } // Keep track of which patches actually need an update PODVector<bool> dirtyPatches((unsigned)(numPatches_.x_ * numPatches_.y_)); for (unsigned i = 0; i < dirtyPatches.Size(); ++i) dirtyPatches[i] = updateAll; patches_.Clear(); if (heightMap_) { // Copy heightmap data const unsigned char* src = heightMap_->GetData(); float* dest = smoothing_ ? sourceHeightData_ : heightData_; unsigned imgComps = heightMap_->GetComponents(); unsigned imgRow = heightMap_->GetWidth() * imgComps; IntRect updateRegion(-1, -1, -1, -1); if (imgComps == 1) { URHO3D_PROFILE(CopyHeightData); for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) { float newHeight = (float)src[imgRow * (numVertices_.y_ - 1 - z) + x] * spacing_.y_; if (updateAll) *dest = newHeight; else { if (*dest != newHeight) { *dest = newHeight; GrowUpdateRegion(updateRegion, x, z); } } ++dest; } } } else { URHO3D_PROFILE(CopyHeightData); // If more than 1 component, use the green channel for more accuracy for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) { float newHeight = ((float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x] + (float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_; if (updateAll) *dest = newHeight; else { if (*dest != newHeight) { *dest = newHeight; GrowUpdateRegion(updateRegion, x, z); } } ++dest; } } } // If updating a region of the heightmap, check which patches change if (!updateAll) { int lodExpand = 1 << (numLodLevels_ - 1); // Expand the right & bottom 1 pixel more, as patches share vertices at the edge updateRegion.left_ -= lodExpand; updateRegion.right_ += lodExpand + 1; updateRegion.top_ -= lodExpand; updateRegion.bottom_ += lodExpand + 1; int sX = Max(updateRegion.left_ / patchSize_, 0); int eX = Min(updateRegion.right_ / patchSize_, numPatches_.x_ - 1); int sY = Max(updateRegion.top_ / patchSize_, 0); int eY = Min(updateRegion.bottom_ / patchSize_, numPatches_.y_ - 1); for (int y = sY; y <= eY; ++y) { for (int x = sX; x <= eX; ++x) dirtyPatches[y * numPatches_.x_ + x] = true; } } patches_.Reserve((unsigned)(numPatches_.x_ * numPatches_.y_)); bool enabled = IsEnabledEffective(); { URHO3D_PROFILE(CreatePatches); // Create patches and set node transforms for (int z = 0; z < numPatches_.y_; ++z) { for (int x = 0; x < numPatches_.x_; ++x) { String nodeName = "Patch_" + String(x) + "_" + String(z); Node* patchNode = node_->GetChild(nodeName); if (!patchNode) { // Create the patch scene node as local and temporary so that it is not unnecessarily serialized to either // file or replicated over the network patchNode = node_->CreateChild(nodeName, LOCAL); patchNode->SetTemporary(true); } patchNode->SetPosition(Vector3(patchWorldOrigin_.x_ + (float)x * patchWorldSize_.x_, 0.0f, patchWorldOrigin_.y_ + (float)z * patchWorldSize_.y_)); TerrainPatch* patch = patchNode->GetComponent<TerrainPatch>(); if (!patch) { patch = patchNode->CreateComponent<TerrainPatch>(); patch->SetOwner(this); patch->SetCoordinates(IntVector2(x, z)); // Copy initial drawable parameters patch->SetEnabled(enabled); patch->SetMaterial(material_); patch->SetDrawDistance(drawDistance_); patch->SetShadowDistance(shadowDistance_); patch->SetLodBias(lodBias_); patch->SetViewMask(viewMask_); patch->SetLightMask(lightMask_); patch->SetShadowMask(shadowMask_); patch->SetZoneMask(zoneMask_); patch->SetMaxLights(maxLights_); patch->SetCastShadows(castShadows_); patch->SetOccluder(occluder_); patch->SetOccludee(occludee_); } patches_.Push(WeakPtr<TerrainPatch>(patch)); } } } // Create the shared index data if (updateAll) CreateIndexData(); // Create vertex data for patches. First update smoothing to ensure normals are calculated correctly across patch borders if (smoothing_) { URHO3D_PROFILE(UpdateSmoothing); for (unsigned i = 0; i < patches_.Size(); ++i) { if (dirtyPatches[i]) { TerrainPatch* patch = patches_[i]; const IntVector2& coords = patch->GetCoordinates(); int startX = coords.x_ * patchSize_; int endX = startX + patchSize_; int startZ = coords.y_ * patchSize_; int endZ = startZ + patchSize_; for (int z = startZ; z <= endZ; ++z) { for (int x = startX; x <= endX; ++x) { float smoothedHeight = ( GetSourceHeight(x - 1, z - 1) + GetSourceHeight(x, z - 1) * 2.0f + GetSourceHeight(x + 1, z - 1) + GetSourceHeight(x - 1, z) * 2.0f + GetSourceHeight(x, z) * 4.0f + GetSourceHeight(x + 1, z) * 2.0f + GetSourceHeight(x - 1, z + 1) + GetSourceHeight(x, z + 1) * 2.0f + GetSourceHeight(x + 1, z + 1) ) / 16.0f; heightData_[z * numVertices_.x_ + x] = smoothedHeight; } } } } } for (unsigned i = 0; i < patches_.Size(); ++i) { TerrainPatch* patch = patches_[i]; if (dirtyPatches[i]) { CreatePatchGeometry(patch); CalculateLodErrors(patch); } SetPatchNeighbors(patch); } } // Send event only if new geometry was generated, or the old was cleared if (patches_.Size() || prevNumPatches) { using namespace TerrainCreated; VariantMap& eventData = GetEventDataMap(); eventData[P_NODE] = node_; node_->SendEvent(E_TERRAINCREATED, eventData); } }
void Terrain::CreateGeometry() { recreateTerrain_ = false; if (!node_) return; PROFILE(CreateTerrainGeometry); unsigned prevNumPatches = patches_.Size(); // Determine number of LOD levels unsigned lodSize = patchSize_; numLodLevels_ = 1; while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < MAX_LOD_LEVELS) { lodSize >>= 1; ++numLodLevels_; } // Determine total terrain size patchWorldSize_ = Vector2(spacing_.x_ * (float)patchSize_, spacing_.z_ * (float)patchSize_); if (heightMap_) { numPatches_ = IntVector2((heightMap_->GetWidth() - 1) / patchSize_, (heightMap_->GetHeight() - 1) / patchSize_); numVertices_ = IntVector2(numPatches_.x_ * patchSize_ + 1, numPatches_.y_ * patchSize_ + 1); patchWorldOrigin_ = Vector2(-0.5f * (float)numPatches_.x_ * patchWorldSize_.x_, -0.5f * (float)numPatches_.y_ * patchWorldSize_.y_); heightData_ = new float[numVertices_.x_ * numVertices_.y_]; } else { numPatches_ = IntVector2::ZERO; numVertices_ = IntVector2::ZERO; patchWorldOrigin_ = Vector2::ZERO; heightData_.Reset(); } // Remove old patch nodes which are not needed PODVector<Node*> oldPatchNodes; node_->GetChildrenWithComponent<TerrainPatch>(oldPatchNodes); for (PODVector<Node*>::Iterator i = oldPatchNodes.Begin(); i != oldPatchNodes.End(); ++i) { bool nodeOk = false; Vector<String> coords = (*i)->GetName().Substring(6).Split('_'); if (coords.Size() == 2) { int x = ToInt(coords[0]); int z = ToInt(coords[1]); if (x < numPatches_.x_ && z < numPatches_.y_) nodeOk = true; } if (!nodeOk) node_->RemoveChild(*i); } patches_.Clear(); if (heightMap_) { // Copy heightmap data const unsigned char* src = heightMap_->GetData(); float* dest = heightData_; unsigned imgComps = heightMap_->GetComponents(); unsigned imgRow = heightMap_->GetWidth() * imgComps; if (imgComps == 1) { for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) *dest++ = (float)src[imgRow * (numVertices_.y_ - 1 - z) + x] * spacing_.y_; } } else { // If more than 1 component, use the green channel for more accuracy for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) *dest++ = ((float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x] + (float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_; } } if (smoothing_) SmoothHeightMap(); patches_.Reserve(numPatches_.x_ * numPatches_.y_); bool enabled = IsEnabledEffective(); // Create patches and set node transforms for (int z = 0; z < numPatches_.y_; ++z) { for (int x = 0; x < numPatches_.x_; ++x) { String nodeName = "Patch_" + String(x) + "_" + String(z); Node* patchNode = node_->GetChild(nodeName); if (!patchNode) { // Create the patch scene node as local and temporary so that it is not unnecessarily serialized to either // file or replicated over the network patchNode = node_->CreateChild(nodeName, LOCAL); patchNode->SetTemporary(true); } patchNode->SetPosition(Vector3(patchWorldOrigin_.x_ + (float)x * patchWorldSize_.x_, 0.0f, patchWorldOrigin_.y_ + (float)z * patchWorldSize_.y_)); TerrainPatch* patch = patchNode->GetOrCreateComponent<TerrainPatch>(); patch->SetOwner(this); patch->SetCoordinates(IntVector2(x, z)); // Copy initial drawable parameters patch->SetEnabled(enabled); patch->SetMaterial(material_); patch->SetDrawDistance(drawDistance_); patch->SetShadowDistance(shadowDistance_); patch->SetLodBias(lodBias_); patch->SetViewMask(viewMask_); patch->SetLightMask(lightMask_); patch->SetShadowMask(shadowMask_); patch->SetZoneMask(zoneMask_); patch->SetMaxLights(maxLights_); patch->SetCastShadows(castShadows_); patch->SetOccluder(occluder_); patch->SetOccludee(occludee_); patches_.Push(WeakPtr<TerrainPatch>(patch)); } } // Create the shared index data CreateIndexData(); // Create vertex data for patches for (Vector<WeakPtr<TerrainPatch> >::Iterator i = patches_.Begin(); i != patches_.End(); ++i) { CreatePatchGeometry(*i); CalculateLodErrors(*i); SetNeighbors(*i); } } // Send event only if new geometry was generated, or the old was cleared if (patches_.Size() || prevNumPatches) { using namespace TerrainCreated; VariantMap& eventData = GetEventDataMap(); eventData[P_NODE] = node_; node_->SendEvent(E_TERRAINCREATED, eventData); } }
void TerrainFunctionGroup::AddFunctions(TerrainPatch& patch) const { // No optimizations like the ones in TerrainFunction are possible. patch.IterateOverPatch(*this, 0, patch.GetVertexRes(), 0, patch.GetVertexRes()); }