| | 5015 | protected void UpdateDerivedDataImpl(Rectangle rect,Rectangle lightmapExtraRect, bool synchronous, byte typeMask) |
| | 5016 | { |
| | 5017 | mDerivedDataUpdateInProgress = true; |
| | 5018 | mDerivedUpdatePendingMask = 0; |
| | 5019 | |
| | 5020 | DerivedDataRequest req = new DerivedDataRequest(); |
| | 5021 | req.terrain = this; |
| | 5022 | req.DirtyRect = rect; |
| | 5023 | req.LightmapExtraDirtyRect = lightmapExtraRect; |
| | 5024 | req.TypeMask = typeMask; |
| | 5025 | if (!mNormalMapRequired) |
| | 5026 | req.TypeMask = (byte)(req.TypeMask & ~DERIVED_DATA_NORMALS); |
| | 5027 | if (!mLightMapRequired) |
| | 5028 | req.TypeMask = (byte)(req.TypeMask & ~DERIVED_DATA_LIGHTMAP); |
| | 5029 | |
| | 5030 | HandleRequest(req); |
| | 5031 | |
| | 5032 | #warning implement workqueue |
| | 5033 | #if false |
| | 5034 | Root::getSingleton().getWorkQueue()->addRequest( |
| | 5035 | WORKQUEUE_CHANNEL, WORKQUEUE_DERIVED_DATA_REQUEST, |
| | 5036 | Any(req), 0, synchronous); |
| | 5037 | #endif |
| | 5038 | } |
| | 5039 | /// <summary> |
| | 5040 | /// |
| | 5041 | /// </summary> |
| | 5042 | /// <param name="vp"></param> |
| | 5043 | protected void CalculateCurrentLod(Viewport vp) |
| | 5044 | { |
| | 5045 | if (mQuadTree != null) |
| | 5046 | { |
| | 5047 | // calculate error terms |
| | 5048 | #warning implement Camera.LodCamera; |
| | 5049 | Camera cam = vp.Camera; |
| | 5050 | |
| | 5051 | // W. de Boer 2000 calculation |
| | 5052 | // A = vp_near / abs(vp_top) |
| | 5053 | // A = 1 / tan(fovy*0.5) (== 1 for fovy=45*2) |
| | 5054 | float A = (float)(1.0f / Math.Tan((double)(cam.FieldOfView * 0.5))); |
| | 5055 | // T = 2 * maxPixelError / vertRes |
| | 5056 | float maxPixelError = 8 * cam.InverseLodBias;//TerrainGlobalOptions.MaxPixelError * cam.InverseLodBias; |
| | 5057 | float T = 2.0f * maxPixelError / (float)vp.ActualHeight; |
| | 5058 | |
| | 5059 | // CFactor = A / T |
| | 5060 | float cFactor = A / T; |
| | 5061 | mQuadTree.CalculateCurrentLod(cam, cFactor); |
| | 5062 | } |
| | 5063 | } |
| | 5064 | /// <summary> |
| | 5065 | /// |
| | 5066 | /// </summary> |
| | 5067 | /// <param name="flags"></param> |
| | 5068 | public static void AddQueryFlag(uint flags) |
| | 5069 | { |
| | 5070 | mQueryFlags |= flags; |
| | 5071 | } |
| | 5072 | /// <summary> |
| | 5073 | /// |
| | 5074 | /// </summary> |
| | 5075 | /// <param name="flags"></param> |
| | 5076 | public static void RemoveQueryFlags(uint flags) |
| | 5077 | { |
| | 5078 | mQueryFlags &= ~flags; |
| | 5079 | } |
| | 5080 | |
| | 5081 | /// <summary> |
| | 5082 | /// |
| | 5083 | /// </summary> |
| | 5084 | /// <param name="index"></param> |
| | 5085 | /// <returns></returns> |
| | 5086 | public Terrain GetNeighbour(NeighbourIndex index) |
| | 5087 | { |
| | 5088 | return mNeighbours[(int)index]; |
| | 5089 | } |
| | 5090 | /// <summary> |
| | 5091 | /// |
| | 5092 | /// </summary> |
| | 5093 | /// <param name="index"></param> |
| | 5094 | /// <param name="neighbour"></param> |
| | 5095 | public void SetNeighbour(NeighbourIndex index, Terrain neighbour) |
| | 5096 | { |
| | 5097 | SetNeighbour(index, neighbour, false, true); |
| | 5098 | } |
| | 5099 | /// <summary> |
| | 5100 | /// |
| | 5101 | /// </summary> |
| | 5102 | /// <param name="index"></param> |
| | 5103 | /// <param name="neighbour"></param> |
| | 5104 | /// <param name="recalculate"></param> |
| | 5105 | /// <param name="notifyOther"></param> |
| | 5106 | public void SetNeighbour(NeighbourIndex index, Terrain neighbour, bool recalculate, bool notifyOther) |
| | 5107 | { |
| | 5108 | if (mNeighbours[(int)index] != neighbour) |
| | 5109 | { |
| | 5110 | // detach existing |
| | 5111 | if (mNeighbours[(int)index] != null && notifyOther) |
| | 5112 | mNeighbours[(int)index].SetNeighbour(GetOppositeNeighbour(index), null, false, false); |
| | 5113 | |
| | 5114 | mNeighbours[(int)index] = neighbour; |
| | 5115 | if (mNeighbours[(int)index] != null && notifyOther) |
| | 5116 | mNeighbours[(int)index].SetNeighbour(GetOppositeNeighbour(index), this, recalculate, notifyOther); |
| | 5117 | |
| | 5118 | if (recalculate) |
| | 5119 | { |
| | 5120 | //recalculate, pass OUR edge rect |
| | 5121 | Rectangle edgerect = new Rectangle(); |
| | 5122 | GetEdgeRect(index, 2, ref edgerect); |
| | 5123 | NeighbourModified(index, edgerect, edgerect); |
| | 5124 | } |
| | 5125 | } |
| | 5126 | } |
| | 5127 | /// <summary> |
| | 5128 | /// |
| | 5129 | /// </summary> |
| | 5130 | /// <param name="index"></param> |
| | 5131 | /// <returns></returns> |
| | 5132 | public NeighbourIndex GetOppositeNeighbour(NeighbourIndex index) |
| | 5133 | { |
| | 5134 | int intindex = (int)index; |
| | 5135 | intindex += (int)(NeighbourIndex.Count) / 2; |
| | 5136 | intindex = intindex % (int)NeighbourIndex.Count; |
| | 5137 | return (NeighbourIndex)intindex; |
| | 5138 | } |
| | 5139 | /// <summary> |
| | 5140 | /// |
| | 5141 | /// </summary> |
| | 5142 | public void NotifyNeighbours() |
| | 5143 | { |
| | 5144 | // There are 3 things that can need updating: |
| | 5145 | // Height at edge - match to neighbour (first one to update 'loses' to other since read-only) |
| | 5146 | // Normal at edge - use heights from across boundary too |
| | 5147 | // Shadows across edge |
| | 5148 | // The extent to which these can affect the current tile vary: |
| | 5149 | // Height at edge - only affected by a change at the adjoining edge / corner |
| | 5150 | // Normal at edge - only affected by a change to the 2 rows adjoining the edge / corner |
| | 5151 | // Shadows across edge - possible effect extends based on the projection of the |
| | 5152 | // neighbour AABB along the light direction (worst case scenario) |
| | 5153 | if (!IsNull(mDirtyGeometryRectForNeighbours)) |
| | 5154 | { |
| | 5155 | Rectangle dirtyRect = new Rectangle(mDirtyGeometryRectForNeighbours); |
| | 5156 | mDirtyGeometryRectForNeighbours = new Rectangle(); |
| | 5157 | // calculate light update rectangle |
| | 5158 | Vector3 lightVec = TerrainGlobalOptions.LightMapDirection; |
| | 5159 | Rectangle lightmapRect = new Rectangle(); |
| | 5160 | WidenRectByVector(lightVec, dirtyRect, MinHeight, MaxHeight, ref lightmapRect); |
| | 5161 | |
| | 5162 | for (int i = 0; i < (int)NeighbourIndex.Count; ++i) |
| | 5163 | { |
| | 5164 | NeighbourIndex ni = (NeighbourIndex)(i); |
| | 5165 | Terrain neighbour = GetNeighbour(ni); |
| | 5166 | if (neighbour == null) |
| | 5167 | continue; |
| | 5168 | |
| | 5169 | // Intersect the incoming rectangles with the edge regions related to this neighbour |
| | 5170 | Rectangle edgeRect = new Rectangle(); |
| | 5171 | GetEdgeRect(ni, 2, ref edgeRect); |
| | 5172 | Rectangle heightEdgeRect = edgeRect.Intersect(dirtyRect); |
| | 5173 | Rectangle lightmapEdgeRect = edgeRect.Intersect(lightmapRect); |
| | 5174 | |
| | 5175 | if (!IsNull(heightEdgeRect) || !IsNull(lightmapRect)) |
| | 5176 | { |
| | 5177 | // ok, we have something valid to pass on |
| | 5178 | Rectangle neighbourHeightEdgeRect = new Rectangle(), neighbourLightmapEdgeRect = new Rectangle(); |
| | 5179 | if (!IsNull(heightEdgeRect)) |
| | 5180 | GetNeighbourEdgeRect(ni, heightEdgeRect, ref neighbourHeightEdgeRect); |
| | 5181 | if (!IsNull(lightmapRect)) |
| | 5182 | GetNeighbourEdgeRect(ni, lightmapEdgeRect, ref neighbourLightmapEdgeRect); |
| | 5183 | |
| | 5184 | neighbour.NeighbourModified(GetOppositeNeighbour(ni), |
| | 5185 | neighbourHeightEdgeRect, neighbourLightmapEdgeRect); |
| | 5186 | |
| | 5187 | } |
| | 5188 | } |
| | 5189 | } |
| | 5190 | } |
| | 5191 | private bool IsNull(Rectangle rect) |
| | 5192 | { |
| | 5193 | return rect.Height == 0 && rect.Left == 0 && rect.Right == 0 && rect.Top == 0 && rect.Width == 0 && rect.Bottom == 0; |
| | 5194 | } |
| | 5195 | /// <summary> |
| | 5196 | /// |
| | 5197 | /// </summary> |
| | 5198 | /// <param name="index"></param> |
| | 5199 | /// <param name="edgeRect"></param> |
| | 5200 | /// <param name="shadowRect"></param> |
| | 5201 | public void NeighbourModified(NeighbourIndex index, Rectangle edgerect, Rectangle shadowrect) |
| | 5202 | { |
| | 5203 | // We can safely assume that we would not have been contacted if it wasn't |
| | 5204 | // important |
| | 5205 | Terrain neighbour = GetNeighbour(index); |
| | 5206 | if (neighbour == null) |
| | 5207 | return; // bogus request |
| | 5208 | |
| | 5209 | bool updateGeom = false; |
| | 5210 | byte updateDerived = 0; |
| | 5211 | |
| | 5212 | |
| | 5213 | if (!IsNull(edgerect)) |
| | 5214 | { |
| | 5215 | // update edges; match heights first, then recalculate normals |
| | 5216 | // reduce to just single line / corner |
| | 5217 | Rectangle heightMatchRect = new Rectangle(); |
| | 5218 | GetEdgeRect(index, 1, ref heightMatchRect); |
| | 5219 | heightMatchRect = heightMatchRect.Intersect(edgerect); |
| | 5220 | |
| | 5221 | for (long y = heightMatchRect.Top; y < heightMatchRect.Bottom; ++y) |
| | 5222 | { |
| | 5223 | for (long x = heightMatchRect.Left; x < heightMatchRect.Right; ++x) |
| | 5224 | { |
| | 5225 | long nx = 0, ny = 0; |
| | 5226 | GetNeighbourPoint(index, x, y, ref nx, ref ny); |
| | 5227 | float neighbourHeight = neighbour.GetHeightAtPoint(nx, ny); |
| | 5228 | if (!Utility.FloatEqual(neighbourHeight, GetHeightAtPoint(x, y), 1e-3f)) |
| | 5229 | { |
| | 5230 | SetHeightAtPoint(x, y, neighbourHeight); |
| | 5231 | if (!updateGeom) |
| | 5232 | { |
| | 5233 | updateGeom = true; |
| | 5234 | updateDerived |= DERIVED_DATA_ALL; |
| | 5235 | } |
| | 5236 | |
| | 5237 | } |
| | 5238 | } |
| | 5239 | } |
| | 5240 | // if we didn't need to update heights, we still need to update normals |
| | 5241 | // because this was called only if neighbor changed |
| | 5242 | if (!updateGeom) |
| | 5243 | { |
| | 5244 | // ideally we would deal with normal dirty rect separately (as we do with |
| | 5245 | // lightmaps) because a dirty geom rectangle will actually grow by one |
| | 5246 | // element in each direction for normals recalculation. However for |
| | 5247 | // the sake of one row/column it's really not worth it. |
| | 5248 | mDirtyDerivedDataRect.Merge(edgerect); |
| | 5249 | updateDerived |= DERIVED_DATA_NORMALS; |
| | 5250 | } |
| | 5251 | } |
| | 5252 | |
| | 5253 | if (!IsNull(shadowrect)) |
| | 5254 | { |
| | 5255 | // update shadows |
| | 5256 | // here we need to widen the rect passed in based on the min/max height |
| | 5257 | // of the *neighbour* |
| | 5258 | Vector3 lightVec = TerrainGlobalOptions.LightMapDirection; |
| | 5259 | Rectangle widenedRect = new Rectangle(); |
| | 5260 | WidenRectByVector(lightVec, shadowrect, neighbour.MinHeight, neighbour.MaxHeight, ref widenedRect); |
| | 5261 | |
| | 5262 | // set the special-case lightmap dirty rectangle |
| | 5263 | mDirtyLightmapFromNeighboursRect.Merge(widenedRect); |
| | 5264 | updateDerived |= DERIVED_DATA_LIGHTMAP; |
| | 5265 | } |
| | 5266 | |
| | 5267 | if (updateGeom) |
| | 5268 | UpdateGeometry(); |
| | 5269 | if (updateDerived != 0) |
| | 5270 | UpdateDerivedData(true, updateDerived); |
| | 5271 | } |
| | 5272 | /// <summary> |
| | 5273 | /// |
| | 5274 | /// </summary> |
| | 5275 | /// <param name="ray"></param> |
| | 5276 | /// <returns></returns> |
| | 5277 | public Terrain RaySelectNeighbour(Ray ray) |
| | 5278 | { |
| | 5279 | return RaySelectNeighbour(ray, 0); |
| | 5280 | } |
| | 5281 | /// <summary> |
| | 5282 | /// |
| | 5283 | /// </summary> |
| | 5284 | /// <param name="ray"></param> |
| | 5285 | /// <param name="distanceLimit"></param> |
| | 5286 | /// <returns></returns> |
| | 5287 | public Terrain RaySelectNeighbour(Ray ray, float distanceLimit) |
| | 5288 | { |
| | 5289 | Real dNear = 0, dFar = 0; |
| | 5290 | Ray localRay = new Ray(ray.Origin - Position, ray.Direction); |
| | 5291 | // Move back half a square - if we're on the edge of the AABB we might |
| | 5292 | // miss the intersection otherwise; it's ok for everywhere else since |
| | 5293 | // we want the far intersection anyway |
| | 5294 | localRay.Origin = localRay.GetPoint(-mWorldSize / mSize * 0.5f); |
| | 5295 | IntersectResult res = new IntersectResult(); |
| | 5296 | if(Utility.Intersects(localRay,AABB,ref dNear,ref dFar)) |
| | 5297 | { |
| | 5298 | // discard out of range |
| | 5299 | if (dFar <= 0 || (distanceLimit != 0 && dFar > distanceLimit)) |
| | 5300 | return null; |
| | 5301 | |
| | 5302 | // we're interested in the exit point |
| | 5303 | // convert to standard form so we can use x/y always |
| | 5304 | Ray terrainRay = new Ray(convertWorldToTerrainAxes(localRay.Origin), |
| | 5305 | convertWorldToTerrainAxes(localRay.Direction)); |
| | 5306 | |
| | 5307 | Vector3 terrainIntersectPos = terrainRay.GetPoint(dFar); |
| | 5308 | Real x = terrainIntersectPos.x; |
| | 5309 | Real y = terrainIntersectPos.y; |
| | 5310 | Real dx = terrainRay.Direction.x; |
| | 5311 | Real dy = terrainRay.Direction.y; |
| | 5312 | |
| | 5313 | if (Utility.FloatEqual(Utility.Abs(x), Utility.Abs(y))) |
| | 5314 | { |
| | 5315 | if (x > 0 && y > 0 && dx > 0 && dy > 0) |
| | 5316 | return GetNeighbour(NeighbourIndex.NorthEast); |
| | 5317 | if (x > 0 && y < 0 && dx > 0 && dy < 0) |
| | 5318 | return GetNeighbour(NeighbourIndex.SouthEast); |
| | 5319 | if (x < 0 && y > 0 && dx < 0 && dy > 0) |
| | 5320 | return GetNeighbour(NeighbourIndex.NorthWest); |
| | 5321 | if (x < 0 && y < 0 && dx < 0 && dy < 0) |
| | 5322 | return GetNeighbour(NeighbourIndex.SouthWest); |
| | 5323 | } |
| | 5324 | if (x > 0 && x > y && dx > 0) |
| | 5325 | return GetNeighbour(NeighbourIndex.East); |
| | 5326 | if (x < 0 && x < y && dx < 0) |
| | 5327 | return GetNeighbour(NeighbourIndex.West); |
| | 5328 | if (y > 0 && y > x && dy > 0) |
| | 5329 | return GetNeighbour(NeighbourIndex.North); |
| | 5330 | if (y < 0 && y < x && dy < 0) |
| | 5331 | return GetNeighbour(NeighbourIndex.South); |
| | 5332 | |
| | 5333 | } |
| | 5334 | return null; |
| | 5335 | } |
| | 5336 | /// <summary> |
| | 5337 | /// |
| | 5338 | /// </summary> |
| | 5339 | /// <param name="prefix"></param> |
| | 5340 | /// <param name="suffix"></param> |
| | 5341 | public void DumpTextures(string prefix, string suffix) |
| | 5342 | { |
| | 5343 | throw new NotImplementedException(); |
| | 5344 | } |
| | 5345 | /// <summary> |
| | 5346 | /// |
| | 5347 | /// </summary> |
| | 5348 | /// <param name="index"></param> |
| | 5349 | /// <param name="range"></param> |
| | 5350 | /// <param name="outRect"></param> |
| | 5351 | public void GetEdgeRect(NeighbourIndex index, long range, ref Rectangle outRect) |
| | 5352 | { |
| | 5353 | // We make the edge rectangle 2 rows / columns at the edge of the tile |
| | 5354 | // 2 because this copes with normal changes and potentially filtered |
| | 5355 | // shadows. |
| | 5356 | // all right / bottom values are exclusive |
| | 5357 | // terrain origin is bottom-left remember so north is highest value |
| | 5358 | |
| | 5359 | // set left/right |
| | 5360 | switch (index) |
| | 5361 | { |
| | 5362 | case NeighbourIndex.East: |
| | 5363 | case NeighbourIndex.NorthEast: |
| | 5364 | case NeighbourIndex.SouthEast: |
| | 5365 | outRect.Left = mSize - range; |
| | 5366 | outRect.Right = mSize; |
| | 5367 | break; |
| | 5368 | case NeighbourIndex.West: |
| | 5369 | case NeighbourIndex.NorthWest: |
| | 5370 | case NeighbourIndex.SouthWest: |
| | 5371 | outRect.Left = 0; |
| | 5372 | outRect.Right = range; |
| | 5373 | break; |
| | 5374 | case NeighbourIndex.North: |
| | 5375 | case NeighbourIndex.South: |
| | 5376 | outRect.Left = 0; |
| | 5377 | outRect.Right = mSize; |
| | 5378 | break; |
| | 5379 | } |
| | 5380 | |
| | 5381 | // set top / bottom |
| | 5382 | switch (index) |
| | 5383 | { |
| | 5384 | case NeighbourIndex.North: |
| | 5385 | case NeighbourIndex.NorthEast: |
| | 5386 | case NeighbourIndex.NorthWest: |
| | 5387 | outRect.Top = mSize - range; |
| | 5388 | outRect.Bottom = mSize; |
| | 5389 | break; |
| | 5390 | case NeighbourIndex.South: |
| | 5391 | case NeighbourIndex.SouthEast: |
| | 5392 | case NeighbourIndex.SouthWest: |
| | 5393 | outRect.Top = 0; |
| | 5394 | outRect.Bottom = range; |
| | 5395 | break; |
| | 5396 | case NeighbourIndex.East: |
| | 5397 | case NeighbourIndex.West: |
| | 5398 | outRect.Top = 0; |
| | 5399 | outRect.Bottom = mSize; |
| | 5400 | break; |
| | 5401 | } |
| | 5402 | } |
| | 5403 | /// <summary> |
| | 5404 | /// |
| | 5405 | /// </summary> |
| | 5406 | /// <param name="index"></param> |
| | 5407 | /// <param name="range"></param> |
| | 5408 | /// <param name="outRect"></param> |
| | 5409 | public void GetNeighbourEdgeRect(NeighbourIndex index, Rectangle inRect, ref Rectangle outRect) |
| | 5410 | { |
| | 5411 | System.Diagnostics.Debug.Assert(mSize == GetNeighbour(index).Size, "Neighbour has not the same size as this instance"); |
| | 5412 | // Basically just reflect the rect |
| | 5413 | // remember index is neighbour relationship from OUR perspective so |
| | 5414 | // arrangement is backwards to getEdgeRect |
| | 5415 | |
| | 5416 | // left/right |
| | 5417 | switch (index) |
| | 5418 | { |
| | 5419 | case NeighbourIndex.East: |
| | 5420 | case NeighbourIndex.NorthEast: |
| | 5421 | case NeighbourIndex.SouthEast: |
| | 5422 | case NeighbourIndex.West: |
| | 5423 | case NeighbourIndex.NorthWest: |
| | 5424 | case NeighbourIndex.SouthWest: |
| | 5425 | outRect.Left = mSize - inRect.Right; |
| | 5426 | outRect.Right = mSize - inRect.Left; |
| | 5427 | break; |
| | 5428 | default: |
| | 5429 | outRect.Left = inRect.Left; |
| | 5430 | outRect.Right = inRect.Right; |
| | 5431 | break; |
| | 5432 | }; |
| | 5433 | |
| | 5434 | // top / bottom |
| | 5435 | switch (index) |
| | 5436 | { |
| | 5437 | case NeighbourIndex.North: |
| | 5438 | case NeighbourIndex.NorthEast: |
| | 5439 | case NeighbourIndex.NorthWest: |
| | 5440 | case NeighbourIndex.South: |
| | 5441 | case NeighbourIndex.SouthWest: |
| | 5442 | case NeighbourIndex.SouthEast: |
| | 5443 | outRect.Top = mSize - inRect.Bottom; |
| | 5444 | outRect.Bottom = mSize - inRect.Top; |
| | 5445 | break; |
| | 5446 | default: |
| | 5447 | outRect.Top = inRect.Top; |
| | 5448 | outRect.Bottom = inRect.Bottom; |
| | 5449 | break; |
| | 5450 | } |
| | 5451 | } |
| | 5452 | /// <summary> |
| | 5453 | /// |
| | 5454 | /// </summary> |
| | 5455 | /// <param name="index"></param> |
| | 5456 | /// <param name="x"></param> |
| | 5457 | /// <param name="y"></param> |
| | 5458 | /// <param name="outX"></param> |
| | 5459 | /// <param name="outY"></param> |
| | 5460 | public void GetNeighbourPoint(NeighbourIndex index, long x, long y, ref long outx, ref long outy) |
| | 5461 | { |
| | 5462 | // Get the index of the point we should be looking at on a neighbour |
| | 5463 | // in order to match up points |
| | 5464 | System.Diagnostics.Debug.Assert(mSize == GetNeighbour(index).Size, "Neighbour has not the same size as this instance"); |
| | 5465 | |
| | 5466 | // left/right |
| | 5467 | switch (index) |
| | 5468 | { |
| | 5469 | case NeighbourIndex.East: |
| | 5470 | case NeighbourIndex.NorthEast: |
| | 5471 | case NeighbourIndex.SouthEast: |
| | 5472 | case NeighbourIndex.West: |
| | 5473 | case NeighbourIndex.NorthWest: |
| | 5474 | case NeighbourIndex.SouthWest: |
| | 5475 | outx = mSize - x - 1; |
| | 5476 | break; |
| | 5477 | default: |
| | 5478 | outx = x; |
| | 5479 | break; |
| | 5480 | } |
| | 5481 | |
| | 5482 | // top / bottom |
| | 5483 | switch (index) |
| | 5484 | { |
| | 5485 | case NeighbourIndex.North: |
| | 5486 | case NeighbourIndex.NorthEast: |
| | 5487 | case NeighbourIndex.NorthWest: |
| | 5488 | case NeighbourIndex.South: |
| | 5489 | case NeighbourIndex.SouthWest: |
| | 5490 | case NeighbourIndex.SouthEast: |
| | 5491 | outy = mSize - y - 1; |
| | 5492 | break; |
| | 5493 | default: |
| | 5494 | outy = y; |
| | 5495 | break; |
| | 5496 | } |
| | 5497 | } |
| | 5498 | /// <summary> |
| | 5499 | /// |
| | 5500 | /// </summary> |
| | 5501 | /// <param name="x"></param> |
| | 5502 | /// <param name="y"></param> |
| | 5503 | /// <param name="outIndex"></param> |
| | 5504 | /// <param name="outX"></param> |
| | 5505 | /// <param name="outY"></param> |
| | 5506 | public void GetNeighbourPointOverflow(long x, long y, out NeighbourIndex outindex, out long outx, out long outy) |
| | 5507 | { |
| | 5508 | outindex = NeighbourIndex.Count; |
| | 5509 | if (x < 0) |
| | 5510 | { |
| | 5511 | outx = x + mSize - 1; |
| | 5512 | if (y < 0) |
| | 5513 | outindex = NeighbourIndex.SouthWest; |
| | 5514 | else if (y >= mSize) |
| | 5515 | outindex = NeighbourIndex.NorthWest; |
| | 5516 | else |
| | 5517 | outindex = NeighbourIndex.West; |
| | 5518 | } |
| | 5519 | else if (x >= mSize) |
| | 5520 | { |
| | 5521 | outx = x - mSize + 1; |
| | 5522 | if (y < 0) |
| | 5523 | outindex = NeighbourIndex.SouthEast; |
| | 5524 | else if (y >= mSize) |
| | 5525 | outindex = NeighbourIndex.NorthEast; |
| | 5526 | else |
| | 5527 | outindex = NeighbourIndex.East; |
| | 5528 | } |
| | 5529 | else |
| | 5530 | outx = x; |
| | 5531 | |
| | 5532 | if (y < 0) |
| | 5533 | { |
| | 5534 | outy = y + mSize - 1; |
| | 5535 | if (x >= 0 && x < mSize) |
| | 5536 | outindex = NeighbourIndex.South; |
| | 5537 | } |
| | 5538 | else if (y >= mSize) |
| | 5539 | { |
| | 5540 | outy = y - mSize + 1; |
| | 5541 | if (x >= 0 && x < mSize) |
| | 5542 | outindex = NeighbourIndex.North; |
| | 5543 | } |
| | 5544 | else |
| | 5545 | outy = y; |
| | 5546 | |
| | 5547 | System.Diagnostics.Debug.Assert(outindex != NeighbourIndex.Count); |
| | 5548 | } |
| | 5549 | /// <summary> |
| | 5550 | /// checks if the given value is power of two. |
| | 5551 | /// </summary> |
| | 5552 | /// <param name="x">the value to check</param> |
| | 5553 | /// <returns>true if the given value is power of two</returns> |
| | 5554 | protected bool IsPowerOfTwo(ulong x) |
| | 5555 | { |
| | 5556 | return (x & (x - 1)) == 0; |
| | 5557 | } |
| | 5558 | #endregion |
| | 5559 | } |
| | 5560 | |
| | 5561 | #region - constants - |
| | 5562 | public static uint TERRAIN_CHUNK_ID = StreamSerializer.MakeIdentifier("TERR"); |
| | 5563 | public static ushort TERRAIN_CHUNK_VERSION = 1; |
| | 5564 | public static uint TERRAINLAYERDECLARATION_CHUNK_ID = StreamSerializer.MakeIdentifier("TDCL"); |
| | 5565 | public static ushort TERRAINLAYERDECLARATION_CHUNK_VERSION = 1; |
| | 5566 | public static uint TERRAINLAYERSAMPLER_CHUNK_ID = StreamSerializer.MakeIdentifier("TSAM"); |
| | 5567 | public static ushort TERRAINLAYERSAMPLER_CHUNK_VERSION = 1; |
| | 5568 | public static uint TERRAINLAYERSAMPLERELEMENT_CHUNK_ID = StreamSerializer.MakeIdentifier("TSEL"); |
| | 5569 | public static ushort TERRAINLAYERSAMPLERELEMENT_CHUNK_VERSION = 1; |
| | 5570 | public static uint TERRAINLAYERINSTANCE_CHUNK_ID = StreamSerializer.MakeIdentifier("TLIN"); |
| | 5571 | public static ushort TERRAINLAYERINSTANCE_CHUNK_VERSION = 1; |
| | 5572 | public static uint TERRAINDERIVEDDATA_CHUNK_ID = StreamSerializer.MakeIdentifier("TDDA"); |
| | 5573 | public static ushort TERRAINDERIVEDDATA_CHUNK_VERSION = 1; |
| | 5574 | // since 129^2 is the greatest power we can address in 16-bit index |
| | 5575 | public static ushort TERRAIN_MAX_BATCH_SIZE = 129; |
| | 5576 | //static ushort WORKQUEUE_CHANNEL = Root::MAX_USER_WORKQUEUE_CHANNEL + 10; |
| | 5577 | public static ushort WORKQUEUE_DERIVED_DATA_REQUEST = 1; |
| | 5578 | public static int LOD_MORPH_CUSTOM_PARAM = 1001; |
| | 5579 | public static byte DERIVED_DATA_DELTAS = 1; |
| | 5580 | public static byte DERIVED_DATA_NORMALS = 2; |
| | 5581 | public static byte DERIVED_DATA_LIGHTMAP = 4; |
| | 5582 | // This MUST match the bitwise OR of all the types above with no extra bits! |
| | 5583 | public static byte DERIVED_DATA_ALL = 7; |
| | 5584 | public static ushort WORKQUEUE_CHANNEL; |
| | 5585 | |
| | 5586 | #endregion |
| | 5587 | |
| | 5588 | #region - fields - |
| | 5589 | /// <summary> |
| | 5590 | /// |
| | 5591 | /// </summary> |
| | 5592 | protected SceneManager mSceneMgr; |
| | 5593 | /// <summary> |
| | 5594 | /// |
| | 5595 | /// </summary> |
| | 5596 | protected SceneNode mRootNode; |
| | 5597 | /// <summary> |
| | 5598 | /// /// The height data (world coords relative to mPos) |
| | 5599 | /// </summary> |
| | 5600 | protected float[] mHeightData; |
| | 5601 | /// <summary> |
| | 5602 | /// /// The delta information defining how a vertex moves before it is removed at a lower LOD |
| | 5603 | /// </summary> |
| | 5604 | protected float[] mDeltaData; |
| | 5605 | /// <summary> |
| | 5606 | /// |
| | 5607 | /// </summary> |
| | 5608 | protected Alignment mAlign; |
| | 5609 | /// <summary> |
| | 5610 | /// |
| | 5611 | /// </summary> |
| | 5612 | protected float mWorldSize; |
| | 5613 | /// <summary> |
| | 5614 | /// |
| | 5615 | /// </summary> |
| | 5616 | protected ushort mSize; |
| | 5617 | /// <summary> |
| | 5618 | /// |
| | 5619 | /// </summary> |
| | 5620 | protected ushort mMaxBatchSize; |
| | 5621 | /// <summary> |
| | 5622 | /// |
| | 5623 | /// </summary> |
| | 5624 | protected ushort mMinBatchSize; |
| | 5625 | /// <summary> |
| | 5626 | /// |
| | 5627 | /// </summary> |
| | 5628 | protected Vector3 mPos; |
| | 5629 | /// <summary> |
| | 5630 | /// |
| | 5631 | /// </summary> |
| | 5632 | protected TerrainQuadTreeNode mQuadTree; |
| | 5633 | /// <summary> |
| | 5634 | /// |
| | 5635 | /// </summary> |
| | 5636 | protected ushort mNumLodLevels; |
| | 5637 | /// <summary> |
| | 5638 | /// |
| | 5639 | /// </summary> |
| | 5640 | protected ushort mNumLodLevelsPerLeafNode; |
| | 5641 | /// <summary> |
| | 5642 | /// |
| | 5643 | /// </summary> |
| | 5644 | protected ushort mTreeDepth; |
| | 5645 | /// <summary> |
| | 5646 | /// Base position in world space, relative to mPos |
| | 5647 | /// </summary> |
| | 5648 | protected float mBase; |
| | 5649 | /// <summary> |
| | 5650 | /// Relationship between one point on the terrain and world size |
| | 5651 | /// </summary> |
| | 5652 | protected float mScale; |
| | 5653 | /// <summary> |
| | 5654 | /// |
| | 5655 | /// </summary> |
| | 5656 | protected TerrainLayerDeclaration mLayerDecl; |
| | 5657 | /// <summary> |
| | 5658 | /// |
| | 5659 | /// </summary> |
| | 5660 | protected List<LayerInstance> mLayers = new List<LayerInstance>(); |
| | 5661 | /// <summary> |
| | 5662 | /// |
| | 5663 | /// </summary> |
| | 5664 | protected FloatList mLayerUVMultiplier = new FloatList(); |
| | 5665 | /// <summary> |
| | 5666 | /// |
| | 5667 | /// </summary> |
| | 5668 | protected float mSkirtSize; |
| | 5669 | /// <summary> |
| | 5670 | /// |
| | 5671 | /// </summary> |
| | 5672 | protected RenderQueueGroupID mRenderQueueGroup; |
| | 5673 | /// <summary> |
| | 5674 | /// |
| | 5675 | /// </summary> |
| | 5676 | protected Rectangle mDirtyGeometryRect; |
| | 5677 | /// <summary> |
| | 5678 | /// |
| | 5679 | /// </summary> |
| | 5680 | protected Rectangle mDirtyDerivedDataRect; |
| | 5681 | /// <summary> |
| | 5682 | /// |
| | 5683 | /// </summary> |
| | 5684 | protected bool mDerivedDataUpdateInProgress; |
| | 5685 | /// <summary> |
| | 5686 | /// if another update is requested while one is already running |
| | 5687 | /// </summary> |
| | 5688 | protected byte mDerivedUpdatePendingMask; |
| | 5689 | /// <summary> |
| | 5690 | /// |
| | 5691 | /// </summary> |
| | 5692 | protected string mMaterialName; |
| | 5693 | /// <summary> |
| | 5694 | /// |
| | 5695 | /// </summary> |
| | 5696 | protected Material mMaterial; |
| | 5697 | /// <summary> |
| | 5698 | /// |
| | 5699 | /// </summary> |
| | 5700 | protected TerrainMaterialGenerator mMaterialGenerator; |
| | 5701 | /// <summary> |
| | 5702 | /// |
| | 5703 | /// </summary> |
| | 5704 | protected long mMaterialGenerationCount; |
| | 5705 | /// <summary> |
| | 5706 | /// |
| | 5707 | /// </summary> |
| | 5708 | protected bool mMaterialDirty; |
| | 5709 | /// <summary> |
| | 5710 | /// |
| | 5711 | /// </summary> |
| | 5712 | protected bool mMaterialParamsDirty; |
| | 5713 | /// <summary> |
| | 5714 | /// |
| | 5715 | /// </summary> |
| | 5716 | protected ushort mLayerBlendMapSize; |
| | 5717 | /// <summary> |
| | 5718 | /// |
| | 5719 | /// </summary> |
| | 5720 | protected ushort mLayerBlendSizeActual; |
| | 5721 | /// <summary> |
| | 5722 | /// |
| | 5723 | /// </summary> |
| | 5724 | protected List<byte> mCpuBlendMapStorage = new List<byte>(); |
| | 5725 | /// <summary> |
| | 5726 | /// |
| | 5727 | /// </summary> |
| | 5728 | protected List<Texture> mBlendTextureList = new List<Texture>(); |
| | 5729 | /// <summary> |
| | 5730 | /// |
| | 5731 | /// </summary> |
| | 5732 | protected List<TerrainLayerBlendMap> mLayerBlendMapList = new List<TerrainLayerBlendMap>(); |
| | 5733 | /// <summary> |
| | 5734 | /// |
| | 5735 | /// </summary> |
| | 5736 | protected ushort mGlobalColorMapSize; |
| | 5737 | /// <summary> |
| | 5738 | /// |
| | 5739 | /// </summary> |
| | 5740 | protected bool mGlobalColorMapEnabled; |
| | 5741 | /// <summary> |
| | 5742 | /// |
| | 5743 | /// </summary> |
| | 5744 | protected Texture mColorMap; |
| | 5745 | /// <summary> |
| | 5746 | /// |
| | 5747 | /// </summary> |
| | 5748 | protected byte[] mCpuColorMapStorage; |
| | 5749 | /// <summary> |
| | 5750 | /// |
| | 5751 | /// </summary> |
| | 5752 | protected ushort mLightmapSize; |
| | 5753 | /// <summary> |
| | 5754 | /// |
| | 5755 | /// </summary> |
| | 5756 | protected ushort mLightmapSizeActual; |
| | 5757 | /// <summary> |
| | 5758 | /// |
| | 5759 | /// </summary> |
| | 5760 | protected Texture mLightMap; |
| | 5761 | /// <summary> |
| | 5762 | /// |
| | 5763 | /// </summary> |
| | 5764 | protected byte[] mCpuLightmapStorage; |
| | 5765 | /// <summary> |
| | 5766 | /// |
| | 5767 | /// </summary> |
| | 5768 | protected ushort mCompositeMapSize; |
| | 5769 | /// <summary> |
| | 5770 | /// |
| | 5771 | /// </summary> |
| | 5772 | protected ushort mCompositeMapSizeActual; |
| | 5773 | /// <summary> |
| | 5774 | /// |
| | 5775 | /// </summary> |
| | 5776 | protected Texture mCompositeMap; |
| | 5777 | /// <summary> |
| | 5778 | /// |
| | 5779 | /// </summary> |
| | 5780 | protected byte[] mCpuCompositeMapStorage; |
| | 5781 | /// <summary> |
| | 5782 | /// |
| | 5783 | /// </summary> |
| | 5784 | protected Rectangle mCompositeMapDirtyRect; |
| | 5785 | /// <summary> |
| | 5786 | /// |
| | 5787 | /// </summary> |
| | 5788 | protected long mCompositeMapUpdateCountdown; |
| | 5789 | /// <summary> |
| | 5790 | /// |
| | 5791 | /// </summary> |
| | 5792 | protected long mLastMillis; |
| | 5793 | /// <summary> |
| | 5794 | /// true if the updates included lightmap changes (widen) |
| | 5795 | /// </summary> |
| | 5796 | protected bool mCompositeMapDirtyRectLightmapUpdate; |
| | 5797 | /// <summary> |
| | 5798 | /// |
| | 5799 | /// </summary> |
| | 5800 | protected Material mCompositeMapMaterial; |
| | 5801 | /// <summary> |
| | 5802 | /// |
| | 5803 | /// </summary> |
| | 5804 | protected static NameGenerator<Texture> msBlendTextureGenerator; |
| | 5805 | /// <summary> |
| | 5806 | /// |
| | 5807 | /// </summary> |
| | 5808 | protected static NameGenerator<Texture> msNormalMapNameGenerator; |
| | 5809 | /// <summary> |
| | 5810 | /// |
| | 5811 | /// </summary> |
| | 5812 | protected static NameGenerator<Texture> msLightmapNameGenerator; |
| | 5813 | /// <summary> |
| | 5814 | /// |
| | 5815 | /// </summary> |
| | 5816 | protected static NameGenerator<Texture> msCompositeMapNameGenerator; |
| | 5817 | /// <summary> |
| | 5818 | /// |
| | 5819 | /// </summary> |
| | 5820 | protected bool mLodMorphRequired; |
| | 5821 | /// <summary> |
| | 5822 | /// |
| | 5823 | /// </summary> |
| | 5824 | protected bool mNormalMapRequired; |
| | 5825 | /// <summary> |
| | 5826 | /// |
| | 5827 | /// </summary> |
| | 5828 | protected bool mLightMapRequired; |
| | 5829 | /// <summary> |
| | 5830 | /// |
| | 5831 | /// </summary> |
| | 5832 | protected bool mLightMapShadowsOnly; |
| | 5833 | /// <summary> |
| | 5834 | /// |
| | 5835 | /// </summary> |
| | 5836 | protected bool mCompositeMapRequired; |
| | 5837 | /// <summary> |
| | 5838 | /// texture storing normals for the whole terrain |
| | 5839 | /// </summary> |
| | 5840 | protected Texture mTerrainNormalMap; |
| | 5841 | /// <summary> |
| | 5842 | /// pending data |
| | 5843 | /// </summary> |
| | 5844 | protected PixelBox mCpuTerrainNormalMap; |
| | 5845 | /// <summary> |
| | 5846 | /// |
| | 5847 | /// </summary> |
| | 5848 | protected Camera mLastLODCamera; |
| | 5849 | /// <summary> |
| | 5850 | /// |
| | 5851 | /// </summary> |
| | 5852 | protected ulong mLastLODFrame; |
| | 5853 | |
| | 5854 | IntPtr mHeightDataPtr; |
| | 5855 | IntPtr mDeltaDataPtr; |
| | 5856 | |
| | 5857 | #endregion |
| | 5858 | |
| | 5859 | #region - properties - |
| | 5860 | /// <summary> |
| | 5861 | /// Get's the scenemanager of the terrain |
| | 5862 | /// </summary> |
| | 5863 | public SceneManager SceneManager |
| | 5864 | { |
| | 5865 | get { return mSceneMgr; } |
| | 5866 | } |
| | 5867 | /// <summary> |
| | 5868 | /// Get a pointer to all the delta data for this terrain. |
| | 5869 | /// </summary> |
| | 5870 | /// <remarks> |
| | 5871 | /// The delta data is a measure at a given vertex of by how much vertically |
| | 5872 | /// a vertex will have to move to reach the point at which it will be |
| | 5873 | /// removed in the next lower LOD. |
| | 5874 | /// </remarks> |
| | 5875 | public float[] DeltaData |
| | 5876 | { |
| | 5877 | get { throw new NotImplementedException(); } |
| | 5878 | } |
| | 5879 | /// <summary> |
| | 5880 | /// Get the requested size of the blend maps used to blend between layers |
| | 5881 | /// for this terrain. |
| | 5882 | /// Note that where hardware limits this, the actual blend maps may be lower |
| | 5883 | /// resolution. This option is derived from TerrainGlobalOptions when the |
| | 5884 | /// terrain is created. |
| | 5885 | /// </summary> |
| | 5886 | public ushort LayerBlendMapSize |
| | 5887 | { |
| | 5888 | get { return mLayerBlendMapSize; } |
| | 5889 | } |
| | 5890 | /// <summary> |
| | 5891 | /// Get'S the AABB (local coords) of the entire terrain |
| | 5892 | /// </summary> |
| | 5893 | public AxisAlignedBox AABB |
| | 5894 | { |
| | 5895 | get |
| | 5896 | { |
| | 5897 | if (mQuadTree == null) |
| | 5898 | return null; |
| | 5899 | else |
| | 5900 | return mQuadTree.AABB; |
| | 5901 | } |
| | 5902 | } |
| | 5903 | /// <summary> |
| | 5904 | /// |
| | 5905 | /// </summary> |
| | 5906 | public ushort Size |
| | 5907 | { |
| | 5908 | get { return mSize; } |
| | 5909 | } |
| | 5910 | /// <summary> |
| | 5911 | /// Get the root scene node for the terrain (internal use only) |
| | 5912 | /// </summary> |
| | 5913 | public SceneNode RootSceneNode |
| | 5914 | { |
| | 5915 | get { return mRootNode; } |
| | 5916 | } |
| | 5917 | /// <summary> |
| | 5918 | /// |
| | 5919 | /// </summary> |
| | 5920 | public Alignment Alignment |
| | 5921 | { |
| | 5922 | get { return mAlign; } |
| | 5923 | } |
| | 5924 | /// <summary> |
| | 5925 | /// |
| | 5926 | /// </summary> |
| | 5927 | public ushort MinBatchSize |
| | 5928 | { |
| | 5929 | get { return mMinBatchSize; } |
| | 5930 | } |
| | 5931 | /// <summary> |
| | 5932 | /// |
| | 5933 | /// </summary> |
| | 5934 | public ushort MaxBatchSize |
| | 5935 | { |
| | 5936 | get { return mMaxBatchSize; } |
| | 5937 | } |
| | 5938 | /// <summary> |
| | 5939 | /// |
| | 5940 | /// </summary> |
| | 5941 | public float WorldSize |
| | 5942 | { |
| | 5943 | get { return mWorldSize; } |
| | 5944 | } |
| | 5945 | /// <summary> |
| | 5946 | /// The default size of 'skirts' used to hide terrain cracks |
| | 5947 | /// (default 10, set for new Terrain using TerrainGlobalOptions) |
| | 5948 | /// </summary> |
| | 5949 | public float SkirtSize |
| | 5950 | { |
| | 5951 | get { return mSkirtSize; } |
| | 5952 | } |
| | 5953 | /// <summary> |
| | 5954 | /// Get's the minimum height of the terrain |
| | 5955 | /// </summary> |
| | 5956 | public float MinHeight |
| | 5957 | { |
| | 5958 | get |
| | 5959 | { |
| | 5960 | if (mQuadTree == null) |
| | 5961 | return 0; |
| | 5962 | else |
| | 5963 | return mQuadTree.MinHeight; |
| | 5964 | } |
| | 5965 | } |
| | 5966 | /// <summary> |
| | 5967 | /// Get's the maximum height of the terrain. |
| | 5968 | /// </summary> |
| | 5969 | public float MaxHeight |
| | 5970 | { |
| | 5971 | get |
| | 5972 | { |
| | 5973 | if (mQuadTree == null) |
| | 5974 | return 0; |
| | 5975 | else |
| | 5976 | return mQuadTree.MaxHeight; |
| | 5977 | } |
| | 5978 | } |
| | 5979 | /// <summary> |
| | 5980 | /// Get's the bounding radius of the entire terrain |
| | 5981 | /// </summary> |
| | 5982 | public float BoundingRadius |
| | 5983 | { |
| | 5984 | get |
| | 5985 | { |
| | 5986 | if (mQuadTree == null) |
| | 5987 | return 0; |
| | 5988 | else |
| | 5989 | return mQuadTree.BoundingRadius; |
| | 5990 | } |
| | 5991 | } |
| | 5992 | |
| | 5993 | /// <summary> |
| | 5994 | /// Get's the material being used for the terrain |
| | 5995 | /// </summary> |
| | 5996 | public Material Material |
| | 5997 | { |
| | 5998 | get |
| | 5999 | { |
| | 6000 | if (mMaterial == null || |
| | 6001 | mMaterialGenerator.ChangeCount != mMaterialGenerationCount || |
| | 6002 | mMaterialDirty) |
| | 6003 | { |
| | 6004 | mMaterial = mMaterialGenerator.Generate(this); |
| | 6005 | mMaterial.Load(); |
| | 6006 | if (mCompositeMapRequired) |
| | 6007 | { |
| | 6008 | mCompositeMapMaterial = mMaterialGenerator.GenerateForCompositeMap(this); |
| | 6009 | mCompositeMapMaterial.Load(); |
| | 6010 | } |
| | 6011 | mMaterialGenerationCount = mMaterialGenerator.ChangeCount; |
| | 6012 | mMaterialDirty = false; |
| | 6013 | } |
| | 6014 | if (mMaterialParamsDirty) |
| | 6015 | { |
| | 6016 | mMaterialGenerator.UpdateParams(mMaterial, this); |
| | 6017 | if (mCompositeMapRequired) |
| | 6018 | mMaterialGenerator.UpdateParamsForCompositeMap(mCompositeMapMaterial, this); |
| | 6019 | } |
| | 6020 | |
| | 6021 | return mMaterial; |
| | 6022 | } |
| | 6023 | } |
| | 6024 | public Material _Material |
| | 6025 | { |
| | 6026 | get |
| | 6027 | { |
| | 6028 | return mMaterial; |
| | 6029 | } |
| | 6030 | } |
| | 6031 | public Material _CompositeMapMaterial |
| | 6032 | { |
| | 6033 | get { return mCompositeMapMaterial; } |
| | 6034 | } |
| | 6035 | /// <summary> |
| | 6036 | /// Get's the material being used for the terrain composite map |
| | 6037 | /// </summary> |
| | 6038 | public Material CompositeMapMaterial |
| | 6039 | { |
| | 6040 | get |
| | 6041 | { |
| | 6042 | // both materials updated together since they change at the same time |
| | 6043 | string matNam = Material.Name; |
| | 6044 | return mCompositeMapMaterial; |
| | 6045 | } |
| | 6046 | } |
| | 6047 | /// <summary> |
| | 6048 | /// Get's the name of the material being used for the terrain |
| | 6049 | /// </summary> |
| | 6050 | public string MaterialName |
| | 6051 | { |
| | 6052 | get { return mMaterialName; } |
| | 6053 | } |
| | 6054 | /// <summary> |
| | 6055 | /// Get a pointer to all the height data for this terrain. |
| | 6056 | /// </summary> |
| | 6057 | /// <remarks> |
| | 6058 | /// The height data is in world coordinates, relative to the position |
| | 6059 | /// of the terrain. |
| | 6060 | /// </remarks> |
| | 6061 | /// <returns></returns> |
| | 6062 | public float[] HeightData |
| | 6063 | { |
| | 6064 | get { return mHeightData; } |
| | 6065 | } |
| | 6066 | /// <summary> |
| | 6067 | /// Request internal implementation options for the terrain material to use, |
| | 6068 | /// in this case vertex morphing information. |
| | 6069 | /// The TerrainMaterialGenerator should call this method to specify the |
| | 6070 | /// options it would like to use when creating a material. Not all the data |
| | 6071 | /// is guaranteed to be up to date on return from this method - for example som |
| | 6072 | /// maps may be generated in the background. However, on return from this method |
| | 6073 | /// all the features that are requested will be referenceable by materials, the |
| | 6074 | /// data may just take a few frames to be fully populated. |
| | 6075 | /// </summary> |
| | 6076 | public bool IsMorphRequired |
| | 6077 | { |
| | 6078 | get { return mLodMorphRequired; } |
| | 6079 | set { mLodMorphRequired = value; } |
| | 6080 | } |
| | 6081 | /// <summary> |
| | 6082 | /// Request internal implementation options for the terrain material to use, |
| | 6083 | /// in this case a terrain-wide normal map. |
| | 6084 | /// The TerrainMaterialGenerator should call this method to specify the |
| | 6085 | /// options it would like to use when creating a material. Not all the data |
| | 6086 | /// is guaranteed to be up to date on return from this method - for example some |
| | 6087 | /// maps may be generated in the background. However, on return from this method |
| | 6088 | /// all the features that are requested will be referenceable by materials, the |
| | 6089 | /// data may just take a few frames to be fully populated. |
| | 6090 | /// </summary> |
| | 6091 | public bool NormalMapRequired |
| | 6092 | { |
| | 6093 | set |
| | 6094 | { |
| | 6095 | if (mNormalMapRequired != value) |
| | 6096 | { |
| | 6097 | mNormalMapRequired = value; |
| | 6098 | // Check NPOT textures supported. We have to use NPOT textures to map |
| | 6099 | // texels to vertices directly! |
| | 6100 | if (!mNormalMapRequired && Root.Instance.RenderSystem |
| | 6101 | .HardwareCapabilities.HasCapability(Capabilities.NonPowerOf2Textures)) |
| | 6102 | { |
| | 6103 | mNormalMapRequired = false; |
| | 6104 | LogManager.Instance.Write(LogMessageLevel.Critical, false, |
| | 6105 | "Terrain: Ignoring request for normal map generation since " + |
| | 6106 | "non-power-of-two texture support is required.", null); |
| | 6107 | } |
| | 6108 | |
| | 6109 | CreateOrDestroyGPUNormalMap(); |
| | 6110 | |
| | 6111 | // if we enabled, generate normal maps |
| | 6112 | if (mNormalMapRequired) |
| | 6113 | { |
| | 6114 | // update derived data for whole terrain, but just normals |
| | 6115 | mDirtyDerivedDataRect = new Rectangle(); |
| | 6116 | mDirtyDerivedDataRect.Left = mDirtyDerivedDataRect.Top = 0; |
| | 6117 | mDirtyDerivedDataRect.Right = mDirtyDerivedDataRect.Bottom = mSize; |
| | 6118 | UpdateDerivedData(false, DERIVED_DATA_NORMALS); |
| | 6119 | } |
| | 6120 | } |
| | 6121 | } |
| | 6122 | } |
| | 6123 | |
| | 6124 | /// <summary> |
| | 6125 | /// Get's or set's the world position of the terrain centre |
| | 6126 | /// </summary> |
| | 6127 | public Vector3 Position |
| | 6128 | { |
| | 6129 | get { return mPos; } |
| | 6130 | set |
| | 6131 | { |
| | 6132 | mPos = value; |
| | 6133 | mRootNode.Position = mPos; |
| | 6134 | UpdateBaseScale(); |
| | 6135 | } |
| | 6136 | } |
| | 6137 | /// <summary> |
| | 6138 | /// Request internal implementation options for the terrain material to use, |
| | 6139 | /// in this case a terrain-wide composite map. |
| | 6140 | /// The TerrainMaterialGenerator should call this method to specify the |
| | 6141 | /// options it would like to use when creating a material. Not all the data |
| | 6142 | /// is guaranteed to be up to date on return from this method - for example some |
| | 6143 | /// maps may be generated in the background. However, on return from this method |
| | 6144 | /// all the features that are requested will be referenceable by materials, the |
| | 6145 | /// data may just take a few frames to be fully populated. |
| | 6146 | /// ------------------------------------------------------ |
| | 6147 | /// compositeMap Whether a terrain-wide composite map is needed. A composite |
| | 6148 | /// map is a texture with all of the blending and lighting baked in, such that |
| | 6149 | /// at distance this texture can be used as an approximation of the multi-layer |
| | 6150 | /// blended material. It is actually up to the material generator to render this |
| | 6151 | /// composite map, because obviously precisely what it looks like depends on what |
| | 6152 | /// the main material looks like. For this reason, the composite map is one piece |
| | 6153 | /// of derived terrain data that is always calculated in the render thread, and |
| | 6154 | /// usually on the GPU. It is expected that if this option is requested, |
| | 6155 | /// the material generator will use it to construct distant LOD techniques. |
| | 6156 | /// </summary> |
| | 6157 | public bool CompositeMapRequired |
| | 6158 | { |
| | 6159 | get { throw new NotImplementedException(); } |
| | 6160 | set |
| | 6161 | { |
| | 6162 | if (mCompositeMapRequired != value) |
| | 6163 | { |
| | 6164 | mCompositeMapRequired = value; |
| | 6165 | CreateOrDestroyGPUCompositeMap(); |
| | 6166 | |
| | 6167 | // if we enabled, generate normal maps |
| | 6168 | if (mCompositeMapRequired) |
| | 6169 | { |
| | 6170 | mCompositeMapDirtyRect.Left = mCompositeMapDirtyRect.Top = 0; |
| | 6171 | mCompositeMapDirtyRect.Right = mCompositeMapDirtyRect.Bottom = mSize; |
| | 6172 | UpdateCompositeMap(); |
| | 6173 | } |
| | 6174 | } |
| | 6175 | } |
| | 6176 | } |
| | 6177 | /// <summary> |
| | 6178 | /// Get's whether a global color map is enabled on this terrain |
| | 6179 | /// </summary> |
| | 6180 | public bool GlobalColorMapEnabled |
| | 6181 | { |
| | 6182 | get { return mGlobalColorMapEnabled; } |
| | 6183 | } |
| | 6184 | /// <summary> |
| | 6185 | /// Get access to the lightmap, if enabled (as requested by the material generator) |
| | 6186 | /// </summary> |
| | 6187 | public Texture LightMap |
| | 6188 | { |
| | 6189 | get { return mLightMap; } |
| | 6190 | } |
| | 6191 | /// <summary> |
| | 6192 | /// Get the requested size of lightmap for this terrain. |
| | 6193 | /// Note that where hardware limits this, the actual lightmap may be lower |
| | 6194 | /// resolution. This option is derived from TerrainGlobalOptions when the |
| | 6195 | /// terrain is created. |
| | 6196 | /// </summary> |
| | 6197 | public ushort LightMapSize |
| | 6198 | { |
| | 6199 | get { return mLightmapSize; } |
| | 6200 | } |
| | 6201 | /// <summary> |
| | 6202 | /// Get's access to the global colour map, if enabled |
| | 6203 | /// </summary> |
| | 6204 | public Texture GlobalColorMap |
| | 6205 | { |
| | 6206 | get { return mColorMap; } |
| | 6207 | } |
| | 6208 | /// <summary> |
| | 6209 | /// Get's the size of the global colour map (if used) |
| | 6210 | /// </summary> |
| | 6211 | public ushort GlobalColorMapSize |
| | 6212 | { |
| | 6213 | get { return mGlobalColorMapSize; } |
| | 6214 | } |
| | 6215 | /// <summary> |
| | 6216 | /// Get's the declaration which describes the layers in this terrain. |
| | 6217 | /// </summary> |
| | 6218 | public TerrainLayerDeclaration LayerDeclaration |
| | 6219 | { |
| | 6220 | get { return mLayerDecl; } |
| | 6221 | } |
| | 6222 | /// <summary> |
| | 6223 | /// Get's the (global) normal map texture |
| | 6224 | /// </summary> |
| | 6225 | public Texture TerrainNormalMap |
| | 6226 | { |
| | 6227 | get { return mTerrainNormalMap; } |
| | 6228 | } |
| | 6229 | /// <summary> |
| | 6230 | /// Get access to the composite map, if enabled (as requested by the material generator) |
| | 6231 | /// </summary> |
| | 6232 | public Texture CompositeMap |
| | 6233 | { |
| | 6234 | get { return mCompositeMap; } |
| | 6235 | } |
| | 6236 | /// <summary> |
| | 6237 | /// Get's the top level of the quad tree which is used to divide up the terrain |
| | 6238 | /// </summary> |
| | 6239 | public TerrainQuadTreeNode QuadTree |
| | 6240 | { |
| | 6241 | get { return mQuadTree; } |
| | 6242 | } |
| | 6243 | /// <summary> |
| | 6244 | /// Get the requested size of composite map for this terrain. |
| | 6245 | /// Note that where hardware limits this, the actual texture may be lower |
| | 6246 | /// resolution. This option is derived from TerrainGlobalOptions when the |
| | 6247 | /// terrain is created. |
| | 6248 | /// </summary> |
| | 6249 | public ushort CompositeMapSize |
| | 6250 | { |
| | 6251 | get { return mCompositeMapSize; } |
| | 6252 | } |
| | 6253 | /// <summary> |
| | 6254 | /// Get's the number of layers in this terrain. |
| | 6255 | /// </summary> |
| | 6256 | public byte LayerCount |
| | 6257 | { |
| | 6258 | get { return (byte)mLayers.Count; } |
| | 6259 | } |
| | 6260 | /// <summary> |
| | 6261 | /// Get the total number of LOD levels in the terrain |
| | 6262 | /// </summary> |
| | 6263 | public ushort NumLodLevels |
| | 6264 | { |
| | 6265 | get { return mNumLodLevels; } |
| | 6266 | } |
| | 6267 | /// <summary> |
| | 6268 | /// Get the number of LOD levels in a leaf of the terrain quadtree |
| | 6269 | /// </summary> |
| | 6270 | public ushort LodLevelsPerLeafCount |
| | 6271 | { |
| | 6272 | get { return mNumLodLevelsPerLeafNode; } |
| | 6273 | } |
| | 6274 | /// <summary> |
| | 6275 | /// Get's or set's the render queue group that this terrain will be rendered into |
| | 6276 | /// </summary> |
| | 6277 | /// <remarks>The default is specified in TerrainGlobalOptions</remarks> |
| | 6278 | public RenderQueueGroupID RenderQueueGroupID |
| | 6279 | { |
| | 6280 | get { return mRenderQueueGroup; } |
| | 6281 | set { mRenderQueueGroup = value; } |
| | 6282 | } |
| | 6283 | |
| | 6284 | /// <summary> |
| | 6285 | /// Get the maximum number of layers supported with the current options. |
| | 6286 | /// </summary> |
| | 6287 | /// <note>When you change the options requested, this value can change. </note> |
| | 6288 | public byte MaxLayers |
| | 6289 | { |
| | 6290 | get { return mMaterialGenerator.GetMaxLayers(this); } |
| | 6291 | } |
| | 6292 | #endregion |
| | 6293 | |
| | 6294 | /// <summary> |
| | 6295 | /// |
| | 6296 | /// </summary> |
| | 6297 | /// <param name="sm"></param> |
| | 6298 | public Terrain(SceneManager sm) |
| | 6299 | { |
| | 6300 | |
| | 6301 | TerrainGlobalOptions.SkirtSize = 10; |
| | 6302 | Vector3 normalized = new Vector3(1, -1, 0); |
| | 6303 | normalized.Normalize(); |
| | 6304 | //TerrainGlobalOptions.LightMapDirection = normalized; |
| | 6305 | TerrainGlobalOptions.CastsDynamicShadows = false; |
| | 6306 | TerrainGlobalOptions.MaxPixelError = 8.0f; |
| | 6307 | TerrainGlobalOptions.RenderQueueGroupID = RenderQueueGroupID.Main; |
| | 6308 | TerrainGlobalOptions.IsUseRayBoxDistanceCalculation = false; |
| | 6309 | TerrainGlobalOptions.DefaultMaterialGenerator = new TerrainMaterialGeneratorA(); |
| | 6310 | TerrainGlobalOptions.LayerBlendMapSize = 1024; |
| | 6311 | TerrainGlobalOptions.DefaultLayerTextureWorldSize = 10; |
| | 6312 | TerrainGlobalOptions.DefaultGlobalColorMapSize = 1024; |
| | 6313 | TerrainGlobalOptions.LightMapSize = 1024; |
| | 6314 | TerrainGlobalOptions.CompositeMapSize = 1024; |
| | 6315 | TerrainGlobalOptions.CompositeMapAmbient = sm.AmbientLight; |
| | 6316 | TerrainGlobalOptions.CompositeMapDiffuse = ColorEx.White; |
| | 6317 | TerrainGlobalOptions.CompositeMapDistance = 3000; |
| | 6318 | mLightMapShadowsOnly = true; |
| | 6319 | msBlendTextureGenerator = new NameGenerator<Texture>("TerrBlend"); |
| | 6320 | TerrainGlobalOptions.DefaultMaterialGenerator.DebugLevel = 0; |
| | 6321 | mSceneMgr = sm; |
| | 6322 | mPos = Vector3.Zero; |
| | 6323 | |
| | 6324 | mRootNode = sm.RootSceneNode.CreateChildSceneNode(); |
| | 6325 | |
| | 6326 | // mSceneMgr.PreFindVisibleObjects += new FindVisibleObject(PreFindVisibleObjects); |
| | 6327 | mSceneMgr.PreFindVisibleObjects += new FindVisibleObjectsEvent(PreFindVisibleObjects); |
| | 6328 | mSceneMgr.PostFindVisibleObjects += new FindVisibleObjectsEvent(mSceneMgr_PostFindVisibleObjects); |
| | 6329 | #warning add SceneManager.AddListerner - or simmilar event approach |
| | 6330 | #warning implement workerqueue here |
| | 6331 | #if false |
| | 6332 | WorkQueue* wq = Root::getSingleton().getWorkQueue(); |
| | 6333 | wq->addRequestHandler(WORKQUEUE_CHANNEL, this); |
| | 6334 | wq->addResponseHandler(WORKQUEUE_CHANNEL, this); |
| | 6335 | #endif |
| | 6336 | // generate a material name, it's important for the terrain material |
| | 6337 | // name to be consistent & unique no matter what generator is being used |
| | 6338 | // so use our own pointer as identifier, use FashHash rather than just casting |
| | 6339 | // the pointer to a long so we support 64-bit pointers |
| | 6340 | mMaterialName = "AxiomTerrain/" + this.GetHashCode(); |
| | 6341 | } |
| | 6342 | |
| | 6343 | void mSceneMgr_PostFindVisibleObjects(SceneManager manager, IlluminationRenderStage stage, Viewport view) |
| | 6344 | { |
| | 6345 | |
| | 6346 | } |
| | 6347 | public void Dispose() |
| | 6348 | { |
| | 6349 | mDerivedDataUpdateInProgress = false; |
| | 6350 | WaitForDerivedProcesses(); |
| | 6351 | #warning delete workerqueue |
| | 6352 | #if false |
| | 6353 | WorkQueue* wq = Root::getSingleton().getWorkQueue(); |
| | 6354 | wq->removeRequestHandler(WORKQUEUE_CHANNEL, this); |
| | 6355 | wq->removeResponseHandler(WORKQUEUE_CHANNEL, this); |
| | 6356 | #endif |
| | 6357 | FreeTemporaryResources(); |
| | 6358 | FreeGPUResources(); |
| | 6359 | FreeCPUResources(); |
| | 6360 | if (mSceneMgr != null) |
| | 6361 | { |
| | 6362 | mSceneMgr.DestroySceneNode(mRootNode.Name); |
| | 6363 | #warning implement scenemanager.removelistener - or simmilar approach |
| | 6364 | } |
| | 6365 | } |
| | 6366 | #region - public functions - |
| | 6367 | /// <summary> |
| | 6368 | /// Convert a position from one space to another with respect to this terrain. |
| | 6369 | /// </summary> |
| | 6370 | /// <param name="inSpace">The space that inPos is expressed as</param> |
| | 6371 | /// <param name="inPos">The incoming position</param> |
| | 6372 | /// <param name="outSpace">The space which outPos should be expressed as</param> |
| | 6373 | /// <param name="outPos"> The output position to be populated</param> |
| | 6374 | public void ConvertPosition(Space inSpace, Vector3 inPos, Space outSpace, ref Vector3 outPos) |
| | 6375 | { |
| | 6376 | ConvertSpace(inSpace, inPos, outSpace, ref outPos, true); |
| | 6377 | } |
| | 6378 | /// <summary> |
| | 6379 | /// Convert a position from one space to another with respect to this terrain. |
| | 6380 | /// </summary> |
| | 6381 | /// <param name="inSpace"> The space that inPos is expressed as</param> |
| | 6382 | /// <param name="inPos">The incoming position</param> |
| | 6383 | /// <param name="outSpace">The space which outPos should be expressed as</param> |
| | 6384 | public Vector3 ConvertPosition(Space inSpace, Vector3 inPos, Space outSpace) |
| | 6385 | { |
| | 6386 | Vector3 ret = Vector3.Zero; |
| | 6387 | ConvertPosition(inSpace, inPos, outSpace, ref ret); |
| | 6388 | return ret; |
| | 6389 | } |
| | 6390 | /// <summary> |
| | 6391 | /// Convert a direction from one space to another with respect to this terrain. |
| | 6392 | /// </summary> |
| | 6393 | /// <param name="inSpace">The space that inDir is expressed as</param> |
| | 6394 | /// <param name="inDir">The incoming direction</param> |
| | 6395 | /// <param name="outSpace">The space which outDir should be expressed as</param> |
| | 6396 | /// <param name="outDir">The output direction to be populated</param> |
| | 6397 | public void ConvertDirection(Space inSpace, Vector3 inDir, Space outSpace, ref Vector3 outDir) |
| | 6398 | { |
| | 6399 | ConvertSpace(inSpace, inDir, outSpace, ref outDir, false); |
| | 6400 | } |
| | 6401 | /// <summary> |
| | 6402 | /// Convert a direction from one space to another with respect to this terrain. |
| | 6403 | /// </summary> |
| | 6404 | /// <param name="inSpace">The space that inDir is expressed as</param> |
| | 6405 | /// <param name="inDir">The incoming direction</param> |
| | 6406 | /// <param name="outSpace">The space which outDir should be expressed as</param> |
| | 6407 | /// <returns>The output direction </returns> |
| | 6408 | public Vector3 ConvertDirection(Space inSpace, Vector3 inDir, Space outSpace) |
| | 6409 | { |
| | 6410 | Vector3 ret = Vector3.Zero; |
| | 6411 | ConvertDirection(inSpace, inDir, outSpace, ref ret); |
| | 6412 | return ret; |
| | 6413 | } |
| | 6414 | /// <summary> |
| | 6415 | /// Save terrain data in native form to a standalone file |
| | 6416 | /// </summary> |
| | 6417 | /// <param name="fileName"></param> |
| | 6418 | /// <note> |
| | 6419 | /// This is a fairly basic way of saving the terrain, to save to a |
| | 6420 | /// file in the resource system, or to insert the terrain data into a |
| | 6421 | /// shared file, use the StreamSerialiser form. |
| | 6422 | /// </note> |
| | 6423 | public void Save(string filename) |
| | 6424 | { |
| | 6425 | FileStream fs = null; |
| | 6426 | try |
| | 6427 | { |
| | 6428 | fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite); |
| | 6429 | } |
| | 6430 | catch (FileNotFoundException f) |
| | 6431 | { |
| | 6432 | throw new FileNotFoundException(string.Format("Can't open {0} for writing. Terrain.Save()", filename)); |
| | 6433 | } |
| | 6434 | finally |
| | 6435 | { |
| | 6436 | if (fs != null) |
| | 6437 | { |
| | 6438 | StreamSerializer sr = new StreamSerializer(fs); |
| | 6439 | Save(sr); |
| | 6440 | } |
| | 6441 | } |
| | 6442 | } |
| | 6443 | /// <summary> |
| | 6444 | /// Save terrain data in native form to a serializing stream |
| | 6445 | /// </summary> |
| | 6446 | /// <param name="stream"></param> |
| | 6447 | public void Save(StreamSerializer stream) |
| | 6448 | { |
| | 6449 | WaitForDerivedProcesses(); |
| | 6450 | |
| | 6451 | stream.WriteChunkBegin(TERRAIN_CHUNK_ID, TERRAIN_CHUNK_VERSION); |
| | 6452 | |
| | 6453 | byte align = (byte)mAlign; |
| | 6454 | stream.Write(align); |
| | 6455 | |
| | 6456 | stream.Write(mSize); |
| | 6457 | stream.Write(mWorldSize); |
| | 6458 | stream.Write(mMaxBatchSize); |
| | 6459 | stream.Write(mMinBatchSize); |
| | 6460 | stream.Write(mPos); |
| | 6461 | for (int i = 0; i < mHeightData.Length; i++) |
| | 6462 | stream.Write(mHeightData[i]); |
| | 6463 | |
| | 6464 | //layer declatation |
| | 6465 | stream.WriteChunkBegin(TERRAINLAYERDECLARATION_CHUNK_ID, TERRAINLAYERDECLARATION_CHUNK_VERSION); |
| | 6466 | //samplers |
| | 6467 | byte numSamplers = (byte)mLayerDecl.Samplers.Count; |
| | 6468 | stream.Write(numSamplers); |
| | 6469 | foreach (TerrainLayerSampler sampler in mLayerDecl.Samplers) |
| | 6470 | { |
| | 6471 | stream.WriteChunkBegin(TERRAINLAYERSAMPLER_CHUNK_ID, TERRAINLAYERSAMPLER_CHUNK_VERSION); |
| | 6472 | stream.Write(sampler.Alias); |
| | 6473 | byte pixFmt = (byte)sampler.Format; |
| | 6474 | stream.Write(pixFmt); |
| | 6475 | stream.WriteChunkEnd(TERRAINLAYERSAMPLER_CHUNK_ID); |
| | 6476 | } |
| | 6477 | //elements |
| | 6478 | byte numElems = (byte)mLayerDecl.Elements.Count; |
| | 6479 | stream.Write(numElems); |
| | 6480 | foreach (TerrainLayerSamplerElement elem in mLayerDecl.Elements) |
| | 6481 | { |
| | 6482 | stream.WriteChunkBegin(TERRAINLAYERSAMPLERELEMENT_CHUNK_ID, TERRAINLAYERSAMPLERELEMENT_CHUNK_VERSION); |
| | 6483 | stream.Write(elem.Source); |
| | 6484 | byte sem = (byte)elem.Semantic; |
| | 6485 | stream.Write(sem); |
| | 6486 | stream.Write(elem.ElementStart); |
| | 6487 | stream.Write(elem.ElementCount); |
| | 6488 | stream.WriteChunkEnd(TERRAINLAYERSAMPLERELEMENT_CHUNK_ID); |
| | 6489 | } |
| | 6490 | stream.WriteChunkEnd(TERRAINLAYERDECLARATION_CHUNK_ID); |
| | 6491 | //layers |
| | 6492 | CheckLayers(false); |
| | 6493 | byte numLayers = (byte)mLayers.Count; |
| | 6494 | stream.Write(numLayers); |
| | 6495 | foreach (LayerInstance inst in mLayers) |
| | 6496 | { |
| | 6497 | stream.WriteChunkBegin(TERRAINLAYERINSTANCE_CHUNK_ID, TERRAINLAYERINSTANCE_CHUNK_VERSION); |
| | 6498 | stream.Write(inst.WorldSize); |
| | 6499 | foreach (string t in inst.TextureNames) |
| | 6500 | stream.Write(t); |
| | 6501 | stream.WriteChunkEnd(TERRAINLAYERINSTANCE_CHUNK_ID); |
| | 6502 | } |
| | 6503 | |
| | 6504 | //packed layer blend data |
| | 6505 | if (mCpuBlendMapStorage.Count > 0) |
| | 6506 | { |
| | 6507 | // save from CPU data if it's there, it means GPU data was never created |
| | 6508 | stream.Write(mLayerBlendMapSize); |
| | 6509 | |
| | 6510 | // load packed cpu data |
| | 6511 | int numBlendTex = (byte)GetBlendTextureCount(numLayers); |
| | 6512 | for (int i = 0; i < numBlendTex; ++i) |
| | 6513 | { |
| | 6514 | PixelFormat fmt = GetBlendTextureFormat((byte)i, numLayers); |
| | 6515 | int channels = PixelUtil.GetNumElemBytes(fmt); |
| | 6516 | int dataSz = channels * mLayerBlendMapSize * mLayerBlendMapSize; |
| | 6517 | byte pData = mCpuBlendMapStorage[i]; |
| | 6518 | stream.Write(pData); |
| | 6519 | stream.Write(dataSz); |
| | 6520 | } |
| | 6521 | } |
| | 6522 | else |
| | 6523 | { |
| | 6524 | if (mLayerBlendMapSize != mLayerBlendSizeActual) |
| | 6525 | { |
| | 6526 | LogManager.Instance.Write("WARNING: blend maps were requested at a size larger than was supported " + |
| | 6527 | "on this hardware, which means the quality has been degraded"); |
| | 6528 | } |
| | 6529 | stream.Write(mLayerBlendSizeActual); |
| | 6530 | unsafe |
| | 6531 | { |
| | 6532 | byte[] tmpData = new byte[mLayerBlendSizeActual * mLayerBlendSizeActual * 4]; |
| | 6533 | fixed (byte* pTmpDataF = tmpData) |
| | 6534 | { |
| | 6535 | foreach (Texture tex in mBlendTextureList) |
| | 6536 | { |
| | 6537 | PixelBox dst = new PixelBox(mLayerBlendSizeActual, mLayerBlendSizeActual, 1, tex.Format, (IntPtr)pTmpDataF); |
| | 6538 | tex.GetBuffer().BlitToMemory(dst); |
| | 6539 | int dataSz = PixelUtil.GetNumElemBytes(tex.Format) |
| | 6540 | * mLayerBlendSizeActual * mLayerBlendSizeActual; |
| | 6541 | stream.Write(tmpData); |
| | 6542 | stream.Write(dataSz); |
| | 6543 | } |
| | 6544 | } |
| | 6545 | } |
| | 6546 | } |
| | 6547 | |
| | 6548 | //other data |
| | 6549 | //normals |
| | 6550 | stream.ReadChunkBegin(TERRAINDERIVEDDATA_CHUNK_ID, TERRAINDERIVEDDATA_CHUNK_VERSION); |
| | 6551 | stream.Write("normalmap"); |
| | 6552 | stream.Write(mSize); |
| | 6553 | if (mCpuTerrainNormalMap != null) |
| | 6554 | { |
| | 6555 | byte[] aData = new byte[mSize * mSize * 3]; |
| | 6556 | IntPtrToArray(mCpuTerrainNormalMap.Data, ref aData); |
| | 6557 | // save from CPU data if it's there, it means GPU data was never created |
| | 6558 | stream.Write(aData); |
| | 6559 | |
| | 6560 | } |
| | 6561 | stream.ReadChunkEnd(TERRAINDERIVEDDATA_CHUNK_ID); |
| | 6562 | |
| | 6563 | //color map |
| | 6564 | if (mGlobalColorMapEnabled) |
| | 6565 | { |
| | 6566 | stream.WriteChunkBegin(TERRAINDERIVEDDATA_CHUNK_ID, TERRAINDERIVEDDATA_CHUNK_VERSION); |
| | 6567 | stream.Write("colormap"); |
| | 6568 | stream.Write(mSize); |
| | 6569 | if (mCpuBlendMapStorage != null) |
| | 6570 | { |
| | 6571 | // save from CPU data if it's there, it means GPU data was never created |
| | 6572 | stream.Write(mCpuColorMapStorage); |
| | 6573 | } |
| | 6574 | else |
| | 6575 | { |
| | 6576 | unsafe |
| | 6577 | { |
| | 6578 | byte[] aData = new byte[mGlobalColorMapSize * mGlobalColorMapSize * 3]; |
| | 6579 | fixed (byte* pDataF = aData) |
| | 6580 | { |
| | 6581 | PixelBox dst = new PixelBox(mGlobalColorMapSize, mGlobalColorMapSize, 1, PixelFormat.BYTE_RGB, (IntPtr)pDataF); |
| | 6582 | mColorMap.GetBuffer().BlitToMemory(dst); |
| | 6583 | stream.Write(aData); |
| | 6584 | } |
| | 6585 | } |
| | 6586 | } |
| | 6587 | stream.WriteChunkEnd(TERRAINDERIVEDDATA_CHUNK_ID); |
| | 6588 | } |
| | 6589 | |
| | 6590 | //ligthmap |
| | 6591 | if (mLightMapRequired) |
| | 6592 | { |
| | 6593 | stream.WriteChunkBegin(TERRAINDERIVEDDATA_CHUNK_ID, TERRAINDERIVEDDATA_CHUNK_VERSION); |
| | 6594 | stream.Write("lightmap"); |
| | 6595 | stream.Write(mLightmapSize); |
| | 6596 | if (mCpuLightmapStorage != null) |
| | 6597 | { |
| | 6598 | // save from CPU data if it's there, it means GPU data was never created |
| | 6599 | stream.Write(mCpuLightmapStorage); |
| | 6600 | } |
| | 6601 | else |
| | 6602 | { |
| | 6603 | unsafe |
| | 6604 | { |
| | 6605 | byte[] aData = new byte[mLightmapSize * mLightmapSize]; |
| | 6606 | fixed (byte* pDataF = aData) |
| | 6607 | { |
| | 6608 | PixelBox dst = new PixelBox(mLightmapSize, mLightmapSize, 1, PixelFormat.L8, (IntPtr)pDataF); |
| | 6609 | mLightMap.GetBuffer().BlitToMemory(dst); |
| | 6610 | stream.Write(aData); |
| | 6611 | } |
| | 6612 | } |
| | 6613 | } |
| | 6614 | stream.WriteChunkEnd(TERRAIN_CHUNK_ID); |
| | 6615 | } |
| | 6616 | |
| | 6617 | // composite map |
| | 6618 | if (mCompositeMapRequired) |
| | 6619 | { |
| | 6620 | stream.WriteChunkBegin(TERRAINDERIVEDDATA_CHUNK_ID, TERRAINDERIVEDDATA_CHUNK_VERSION); |
| | 6621 | stream.Write("compositemap"); |
| | 6622 | stream.Write(mCompositeMapSize); |
| | 6623 | if (mCpuCompositeMapStorage != null) |
| | 6624 | { |
| | 6625 | // save from CPU data if it's there, it means GPU data was never created |
| | 6626 | stream.Write(mCpuCompositeMapStorage); |
| | 6627 | } |
| | 6628 | else |
| | 6629 | { |
| | 6630 | unsafe |
| | 6631 | { |
| | 6632 | // composite map is 4 channel, 3x diffuse, 1x specular mask |
| | 6633 | byte[] aData = new byte[mCompositeMapSize * mCompositeMapSize * 4]; |
| | 6634 | fixed (byte* pDataF = aData) |
| | 6635 | { |
| | 6636 | PixelBox dst = new PixelBox(mCompositeMapSize, mCompositeMapSize, 1, PixelFormat.BYTE_RGB, (IntPtr)pDataF); |
| | 6637 | mCompositeMap.GetBuffer().BlitToMemory(dst); |
| | 6638 | stream.Write(aData); |
| | 6639 | } |
| | 6640 | } |
| | 6641 | } |
| | 6642 | stream.WriteChunkEnd(TERRAINDERIVEDDATA_CHUNK_ID); |
| | 6643 | } |
| | 6644 | |
| | 6645 | //TODO - write deltas |
| | 6646 | |
| | 6647 | stream.WriteChunkEnd(TERRAIN_CHUNK_ID); |
| | 6648 | } |
| | 6649 | /// <summary> |
| | 6650 | /// Prepare the terrain from a standalone file. |
| | 6651 | /// </summary> |
| | 6652 | /// <param name="fileName"></param> |
| | 6653 | /// <note> |
| | 6654 | /// This is safe to do in a background thread as it creates no GPU resources. |
| | 6655 | /// It reads data from a native terrain data chunk. For more advanced uses, |
| | 6656 | /// such as loading from a shared file, use the StreamSerialiser form. |
| | 6657 | /// </note> |
| | 6658 | public bool Prepare(string fileName) |
| | 6659 | { |
| | 6660 | FileStream stream = null; |
| | 6661 | if (ResourceGroupManager.Instance.ResourceExists( |
| | 6662 | ResourceGroupManager.DefaultResourceGroupName, fileName)) |
| | 6663 | { |
| | 6664 | #warning check me! |
| | 6665 | stream = (FileStream)ResourceGroupManager.Instance.OpenResource( |
| | 6666 | fileName, ResourceGroupManager.DefaultResourceGroupName); |
| | 6667 | } |
| | 6668 | else |
| | 6669 | { |
| | 6670 | // try direct |
| | 6671 | if (File.Exists(fileName)) |
| | 6672 | { |
| | 6673 | stream = File.Open(fileName, FileMode.Open); |
| | 6674 | } |
| | 6675 | else |
| | 6676 | { |
| | 6677 | throw new FileNotFoundException(string.Format("'{0}' not found!", fileName)); |
| | 6678 | } |