public void WindUpdate(uint frame) { float h2o = (float)_scene.RegionInfo.RegionSettings.WaterHeight; float gnd; float sunpos; // Simulate time passing on fixed sun regions. // Otherwise use the region's local sun position. if (_scene.RegionInfo.RegionSettings.FixedSun) sunpos = (float)((DateTime.Now.TimeOfDay.TotalSeconds / 600.0) % 24.0); else sunpos = (float)(_scene.RegionInfo.RegionSettings.SunPosition % 24.0); // Sun position is quantized by the simulator to about once every three seconds. // Add a fudge factor to fill in the steps. if (LastSunPos != sunpos) { LastSunPos = sunpos; SunFudge = 0.0f; } else { SunFudge += 0.002f; } sunpos += SunFudge; RunningStat rsCell = new RunningStat(); // Based on the Prevailing wind algorithm // Inspired by Kanker Greenacre // Modified by Balpien Hammerer to account for terrain turbulence, winds aloft and wind setters // Wind Direction double ThetaWD = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeFlutter) % (Math.PI * 2.0); double offset = Math.Sin(ThetaWD) * Math.Sin(ThetaWD*2) * Math.Sin(ThetaWD*9) * Math.Cos(ThetaWD*4); double windDir = m_avgWindDirection * (Math.PI/180.0f) + (m_varWindDirection * (Math.PI/180.0f) * offset); // Wind Speed double ThetaWS = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeAloft) % (Math.PI * 2.0); offset = Math.Sin(ThetaWS) * Math.Sin(ThetaWS*4) + (Math.Sin(ThetaWS*13) / 3.0); double windSpeed = (m_avgWindStrength + (m_varWindStrength * offset)); if (windSpeed < 0) { windSpeed = -windSpeed; windDir += Math.PI; } // Water Direction double ThetaHD = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeFlutter) % (Math.PI * 2.0); double woffset = Math.Sin(ThetaHD) * Math.Sin(ThetaHD*2) * Math.Sin(ThetaHD*9) * Math.Cos(ThetaHD*4); double waterDir = m_avgWaterDirection * (Math.PI/180.0f) + (m_varWaterDirection * (Math.PI/180.0f) * woffset); // Water Speed double ThetaHS = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeSurge) % (Math.PI * 2.0); woffset = Math.Sin(ThetaHS) * Math.Sin(ThetaHS*3) * Math.Sin(ThetaHS*9) * Math.Cos(ThetaHS*4) * Math.Cos(ThetaHS*13); double waterSpeed = (m_avgWaterStrength + (m_varWaterStrength * woffset)); if (waterSpeed < 0) { waterSpeed = -waterSpeed; waterDir += Math.PI; } //m_log.DebugFormat("[{0}] sunpos={1} water={2} dir={3} ThetaHD={4} ThetaHS={5} wo={6}", Name, sunpos, waterSpeed, waterDir, ThetaHD, ThetaHS, woffset); //m_log.DebugFormat("[{0}] sunpos={1} wind={2} dir={3} theta1={4} theta2={5}", Name, sunpos, windSpeed, windDir, theta1, theta2); // Set the predominate wind in each cell, but examine the terrain heights // to adjust the winds. Elevation and the pattern of heights in each 16m // cell infuence the ground winds. for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { rsCell.Clear(); // Compute terrain statistics. They are needed later. for (int iy = 0; iy < 16; iy++) { for (int ix = 0; ix < 16; ix++) { // For the purpose of these computations, it is the above water height that matters. // Any ground below water is treated as zero. gnd = Math.Max(_scene.PhysicsScene.TerrainChannel.GetRawHeightAt(x * 16 + ix, y * 16 + iy) - h2o, 0); rsCell.Push(gnd); } } // Look for the range of heights in the cell and the overall skewness. Use this // to determine ground induced deflection. It is a rough approximation of // ground wind turbulance. The max height is used later to determine the boundary layer. float cellrange = (float)Math.Max((rsCell.Max() - rsCell.Min()) / 5.0f, 1.0); float cellskew = (float)rsCell.Skewness(); m_ranges[y * 16 + x] = (float)(rsCell.Max() - rsCell.Min()); m_skews[y * 16 + x] = cellskew; m_maxheights[y * 16 + x] = (float)rsCell.Max(); // Begin with winds aloft,starting with the fixed wind value set by wind setters. Vector2 wind = m_windsAloft[y * 16 + x]; // Update the cell with default zephyr winds aloft (no turbulence) if the wind speed is not fixed. if ((m_options[y * 16 + x] & WindConstants.WindSpeedFixed) == 0) { // Compute the winds aloft (no turbulence) wind.X = (float)Math.Cos(windDir); wind.Y = (float)Math.Sin(windDir); //wind.Normalize(); wind.X *= (float)windSpeed; wind.Y *= (float)windSpeed; m_windsAloft[y * 16 + x] = wind; } // Compute ground winds (apply terrain turbulence) from the winds aloft cell. if ((m_options[y * 16 + x] & WindConstants.WindSpeedTurbulence) != 0) { double speed = Math.Sqrt(wind.X * wind.X + wind.Y * wind.Y); wind = wind / (float)speed; double dir = Math.Atan2(wind.Y, wind.X); wind.X = (float)Math.Cos((Math.PI * cellskew * 0.333 * cellrange) + dir); wind.Y = (float)Math.Sin((Math.PI * cellskew * 0.333 * cellrange) + dir); //wind.Normalize(); wind.X *= (float)speed; wind.Y *= (float)speed; } m_windsGround[y * 16 + x] = wind; // Update the cell with default zephyr water currents if the speed is not fixed. if ((m_options[y * 16 + x] & WindConstants.WindSpeedFixed) == 0) { // Compute the winds aloft (no turbulence) wind.X = (float)Math.Cos(waterDir); wind.Y = (float)Math.Sin(waterDir); //wind.Normalize(); wind.X *= (float)waterSpeed; wind.Y *= (float)waterSpeed; m_waterCurrent[y * 16 + x] = wind; } //m_log.DebugFormat("[ZephyrWind] speed={0} dir={1} skew={2} range={3} wind={4}", windSpeed, windDir, cellskew, cellrange, wind); } } // Send the updated wind data to the physics engine. _scene.PhysicsScene.SendPhysicsWindData(m_waterCurrent, m_windsGround, m_windsAloft, m_ranges, m_maxheights); }
public void WindUpdate(uint frame) { float h2o = (float)_scene.RegionInfo.RegionSettings.WaterHeight; float gnd; float sunpos; // Simulate time passing on fixed sun regions. // Otherwise use the region's local sun position. if (_scene.RegionInfo.RegionSettings.FixedSun) { sunpos = (float)((DateTime.Now.TimeOfDay.TotalSeconds / 600.0) % 24.0); } else { sunpos = (float)(_scene.RegionInfo.RegionSettings.SunPosition % 24.0); } // Sun position is quantized by the simulator to about once every three seconds. // Add a fudge factor to fill in the steps. if (LastSunPos != sunpos) { LastSunPos = sunpos; SunFudge = 0.0f; } else { SunFudge += 0.002f; } sunpos += SunFudge; RunningStat rsCell = new RunningStat(); // Based on the Prevailing wind algorithm // Inspired by Kanker Greenacre // Modified by Balpien Hammerer to account for terrain turbulence, winds aloft and wind setters // Wind Direction double ThetaWD = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeFlutter) % (Math.PI * 2.0); double offset = Math.Sin(ThetaWD) * Math.Sin(ThetaWD * 2) * Math.Sin(ThetaWD * 9) * Math.Cos(ThetaWD * 4); double windDir = m_avgWindDirection * (Math.PI / 180.0f) + (m_varWindDirection * (Math.PI / 180.0f) * offset); // Wind Speed double ThetaWS = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeAloft) % (Math.PI * 2.0); offset = Math.Sin(ThetaWS) * Math.Sin(ThetaWS * 4) + (Math.Sin(ThetaWS * 13) / 3.0); double windSpeed = (m_avgWindStrength + (m_varWindStrength * offset)); if (windSpeed < 0) { windSpeed = -windSpeed; windDir += Math.PI; } // Water Direction double ThetaHD = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeFlutter) % (Math.PI * 2.0); double woffset = Math.Sin(ThetaHD) * Math.Sin(ThetaHD * 2) * Math.Sin(ThetaHD * 9) * Math.Cos(ThetaHD * 4); double waterDir = m_avgWaterDirection * (Math.PI / 180.0f) + (m_varWaterDirection * (Math.PI / 180.0f) * woffset); // Water Speed double ThetaHS = (sunpos / 24.0 * (2.0 * Math.PI) * m_rateChangeSurge) % (Math.PI * 2.0); woffset = Math.Sin(ThetaHS) * Math.Sin(ThetaHS * 3) * Math.Sin(ThetaHS * 9) * Math.Cos(ThetaHS * 4) * Math.Cos(ThetaHS * 13); double waterSpeed = (m_avgWaterStrength + (m_varWaterStrength * woffset)); if (waterSpeed < 0) { waterSpeed = -waterSpeed; waterDir += Math.PI; } //m_log.DebugFormat("[{0}] sunpos={1} water={2} dir={3} ThetaHD={4} ThetaHS={5} wo={6}", Name, sunpos, waterSpeed, waterDir, ThetaHD, ThetaHS, woffset); //m_log.DebugFormat("[{0}] sunpos={1} wind={2} dir={3} theta1={4} theta2={5}", Name, sunpos, windSpeed, windDir, theta1, theta2); // Set the predominate wind in each cell, but examine the terrain heights // to adjust the winds. Elevation and the pattern of heights in each 16m // cell infuence the ground winds. for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { rsCell.Clear(); // Compute terrain statistics. They are needed later. for (int iy = 0; iy < 16; iy++) { for (int ix = 0; ix < 16; ix++) { // For the purpose of these computations, it is the above water height that matters. // Any ground below water is treated as zero. gnd = Math.Max(_scene.PhysicsScene.TerrainChannel.GetRawHeightAt(x * 16 + ix, y * 16 + iy) - h2o, 0); rsCell.Push(gnd); } } // Look for the range of heights in the cell and the overall skewness. Use this // to determine ground induced deflection. It is a rough approximation of // ground wind turbulance. The max height is used later to determine the boundary layer. float cellrange = (float)Math.Max((rsCell.Max() - rsCell.Min()) / 5.0f, 1.0); float cellskew = (float)rsCell.Skewness(); m_ranges[y * 16 + x] = (float)(rsCell.Max() - rsCell.Min()); m_skews[y * 16 + x] = cellskew; m_maxheights[y * 16 + x] = (float)rsCell.Max(); // Begin with winds aloft,starting with the fixed wind value set by wind setters. Vector2 wind = m_windsAloft[y * 16 + x]; // Update the cell with default zephyr winds aloft (no turbulence) if the wind speed is not fixed. if ((m_options[y * 16 + x] & WindConstants.WindSpeedFixed) == 0) { // Compute the winds aloft (no turbulence) wind.X = (float)Math.Cos(windDir); wind.Y = (float)Math.Sin(windDir); //wind.Normalize(); wind.X *= (float)windSpeed; wind.Y *= (float)windSpeed; m_windsAloft[y * 16 + x] = wind; } // Compute ground winds (apply terrain turbulence) from the winds aloft cell. if ((m_options[y * 16 + x] & WindConstants.WindSpeedTurbulence) != 0) { double speed = Math.Sqrt(wind.X * wind.X + wind.Y * wind.Y); wind = wind / (float)speed; double dir = Math.Atan2(wind.Y, wind.X); wind.X = (float)Math.Cos((Math.PI * cellskew * 0.333 * cellrange) + dir); wind.Y = (float)Math.Sin((Math.PI * cellskew * 0.333 * cellrange) + dir); //wind.Normalize(); wind.X *= (float)speed; wind.Y *= (float)speed; } m_windsGround[y * 16 + x] = wind; // Update the cell with default zephyr water currents if the speed is not fixed. if ((m_options[y * 16 + x] & WindConstants.WindSpeedFixed) == 0) { // Compute the winds aloft (no turbulence) wind.X = (float)Math.Cos(waterDir); wind.Y = (float)Math.Sin(waterDir); //wind.Normalize(); wind.X *= (float)waterSpeed; wind.Y *= (float)waterSpeed; m_waterCurrent[y * 16 + x] = wind; } //m_log.DebugFormat("[ZephyrWind] speed={0} dir={1} skew={2} range={3} wind={4}", windSpeed, windDir, cellskew, cellrange, wind); } } // Send the updated wind data to the physics engine. _scene.PhysicsScene.SendPhysicsWindData(m_waterCurrent, m_windsGround, m_windsAloft, m_ranges, m_maxheights); }