public void DoRectUpdate(int mipLevel, ClipStackEntry stackEntry, RectangleI srcRegion, RectangleI dstRegion) { // get an array of texture data int elementCount = dstRegion.Width * dstRegion.Height; uint[] dstData = new uint[elementCount]; uint[] srcData = _textureData[mipLevel]; // make local copies of the rectangle components for quick access int srcExtentX = srcRegion.Extent.X; int srcExtentY = srcRegion.Extent.Y; int srcIndex = srcRegion.Point.X + (srcRegion.Point.Y * (int)stackEntry.ScaleFactor * stackEntry.Texture.Width); for (int y = 0; y < srcExtentY; y++) { Array.Copy(srcData, srcIndex, dstData, y * srcExtentX, srcExtentX); srcIndex += (int)stackEntry.ScaleFactor * stackEntry.Texture.Width; } // send the new data to the stack entry texture MSXNA.Rectangle dstTextureRect = new MSXNA.Rectangle(dstRegion.X, dstRegion.Y, dstRegion.Width, dstRegion.Height); // replace the specified rect of texture data on the texture #if XBOX stackEntry.Texture.SetData<uint>(0, dstTextureRect, dstData, 0, elementCount, SetDataOptions.None); #else stackEntry.Texture.SetData<uint>(0, dstTextureRect, dstData, 0, elementCount, SetDataOptions.Discard); #endif }
public void DoRectUpdate(int mipLevel, ClipStackEntry stackEntry, RectangleI srcRegion, RectangleI dstRegion) { // toggle updateToggle _updateToggle = (_updateToggle + 1) % 3; // find the index to start at int index = (stackEntry.Texture.Width * dstRegion.Point.Y) + dstRegion.Point.X; // get an array of texture data int elementCount = dstRegion.Width * dstRegion.Height; uint[] dstData = new uint[elementCount]; // make local copies of the rectangle components for quick access int srcPointX = srcRegion.Point.X; int srcPointY = srcRegion.Point.Y; int srcExtentX = srcRegion.Extent.X; int srcExtentY = srcRegion.Extent.Y; float scaleFactor = stackEntry.ScaleFactor; for (int y = 0; y < srcExtentY; y++) { for (int x = 0; x < srcExtentX; x++) { uint color = 0xFF000000; int realX = x + srcPointX; int realY = y + srcPointY; int xFlag = realX & 4; int yFlag = realY & 4; // checker pattern if ((xFlag ^ yFlag) != 0) color += 0xFF; // gradient based on x position across master texture color += (uint)((float)realX / (float)(512f * scaleFactor) * 255) << 8; // toggle colors switch (_updateToggle) { case 0: color += 0xFF0000; break; case 1: color += 0xA00000; break; } //(uint)(_updateToggle ? 0xFF0000 : 0x00); //color += 0xFF; dstData[(srcExtentX * y) + x] = color; } } // send the new data to the stack entry texture Rectangle dstTextureRect = new Rectangle(dstRegion.X, dstRegion.Y, dstRegion.Width, dstRegion.Height); stackEntry.Texture.SetData<uint>(0, dstTextureRect, dstData, 0, elementCount, SetDataOptions.None); }
/// <summary> /// Tests if this rectangle overlaps another rectangle. /// </summary> /// <param name="rect">The rectangle to test against.</param> /// <returns></returns> public bool Overlaps(RectangleI rect) { RectangleI test = new RectangleI(this); return test.Intersect(rect); }
/// <summary> /// Returns whether or not this rectangle intersects with another rectangle. /// </summary> /// <param name="rect">The rectangle to test intersections with.</param> /// <returns>True if the rectangles intersect.</returns> public bool IntersectsWith(RectangleI rect) { if (_point.X + _extent.X < rect._point.X) return false; if (_point.Y + _extent.Y < rect._point.Y) return false; if (rect._point.X + rect._extent.X < _point.X) return false; if (rect._point.Y + rect._extent.Y < _point.Y) return false; return true; }
/// <summary> /// Set this rectangle to the intersection of it and another rectangle. /// </summary> /// <param name="clipRect">The rectangle to intersect with.</param> /// <returns>Whether or not the resulting rectangle is valid.</returns> public bool Intersect(RectangleI clipRect) { int bottomLX = (int)MathHelper.Min(_point.X + _extent.X, clipRect.X + clipRect.Width); int bottomLY = (int)MathHelper.Min(_point.Y + _extent.Y, clipRect.Y + clipRect.Height); _point.X = (int)MathHelper.Max(_point.X, clipRect.X); _point.Y = (int)MathHelper.Max(_point.Y, clipRect.Y); _extent.X = bottomLX - _point.X; _extent.Y = bottomLY - _point.Y; return IsValid; }
/// <summary> /// Returns whether or not this rectangle entirely contains another rectangle. /// </summary> /// <param name="rect">The rectangle to test against.</param> /// <returns>True if the rectangle is entirely contained.</returns> public bool Contains(RectangleI rect) { if (_point.X <= rect.Point.X && _point.Y <= rect.Point.Y) if (rect.Point.X + rect.Extent.X <= _point.X + _extent.X) if (rect.Point.Y + rect.Extent.Y <= _point.Y + _extent.Y) return true; return false; }
/// <summary> /// Creates a rectangle from another rectangle. /// </summary> /// <param name="rectangle"></param> public RectangleI(RectangleI rectangle) { _point = rectangle.Point; _extent = rectangle.Extent; }
public void DoRectUpdate(int mipLevel, ClipStackEntry stackEntry, RectangleI srcRegion, RectangleI dstRegion) { // get a local reference to the GFXDevice's graphics device GraphicsDevice device = GFXDevice.Instance.Device; if (device.IsDisposed) return; // calculate the new destination texture coordinates // (custom viewports currently not fully supported on the XBox, so we use this method instead. // this is just as fast, so probably keep using this.) RectangleF dstCoords = new RectangleF( (float)dstRegion.X / (float)stackEntry.Texture.Width, (float)dstRegion.Y / (float)stackEntry.Texture.Height, (float)dstRegion.Width / (float)stackEntry.Texture.Width, (float)dstRegion.Height / (float)stackEntry.Texture.Height ); // calculate the new texture coordinates RectangleF texCoords = new RectangleF( (float)srcRegion.X / ((float)stackEntry.Texture.Width * stackEntry.ScaleFactor), (float)srcRegion.Y / ((float)stackEntry.Texture.Height * stackEntry.ScaleFactor), (float)srcRegion.Width / ((float)stackEntry.Texture.Width * stackEntry.ScaleFactor), (float)srcRegion.Height / ((float)stackEntry.Texture.Height * stackEntry.ScaleFactor) ); // custom viewports currently bugged on XBOX _UpdateRenderQuad(texCoords, dstCoords); // init the effect for this render _blender.SetupEffect(_toTexRenderState, null); // draw the quad while (_blender.SetupPass()) { try { device.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleStrip, _renderQuadVB, 0, 2); } catch (Exception x) { break; } } // cleanup the effect after render _blender.CleanupEffect(); }
/// <summary> /// Clips a given rectangle against a source texture and returns a list of rects that the rect will wrap to. /// </summary> /// <param name="gridSpacing">The grid over which to wrap the rectangle. Normally _clipMapSize.</param> /// <param name="rect">The rectangle to wrap around the grid.</param> /// <returns>The resultant list of rectangles from wrapping the texture to the grid.</returns> protected void _ClipAgainstGrid(int gridSpacing, RectangleI rect, ref List<RectangleI> rectList) { // check against X grids... int startX = rect.Point.X; int endX = rect.Point.X + rect.Extent.X; int gridMask = ~(gridSpacing - 1); int startGridX = startX & gridMask; int endGridX = endX & gridMask; _clipRectBuffer.Clear(); // check X... if (startGridX != endGridX && endX - endGridX > 0) { // we have a clip: split against the grid multiple and store _clipRectBuffer.Add(new RectangleI(startX, rect.Point.Y, endGridX - startX, rect.Extent.Y)); _clipRectBuffer.Add(new RectangleI(endGridX, rect.Point.Y, endX - endGridX, rect.Extent.Y)); } else { // copy the original rect _clipRectBuffer.Add(rect); } // now, check Y for the one or two rects we have from above. for (int i = 0; i < _clipRectBuffer.Count; i++) { // Figure our extent and grid information. int startY = _clipRectBuffer[i].Point.Y; int endY = _clipRectBuffer[i].Point.Y + rect.Extent.Y; int startGridY = startY & gridMask; int endGridY = endY & gridMask; if (startGridY != endGridY && endY - endGridY > 0) { // we have a clip: split against the grid multiple and store rectList.Add(new RectangleI(_clipRectBuffer[i].Point.X, endGridY, _clipRectBuffer[i].Extent.X, endY - endGridY)); rectList.Add(new RectangleI(_clipRectBuffer[i].Point.X, startY, _clipRectBuffer[i].Extent.X, endGridY - startY)); } else { // copy the current rect rectList.Add(_clipRectBuffer[i]); } } }
/// <summary> /// Calculate the list of rects that need to be updated when moving a clip level from one source area to another. /// </summary> /// <param name="oldData">The area the clip level is moving from.</param> /// <param name="newData">The area the clip level is moving to.</param> /// <returns>A list of rectangles that need to be updated.</returns> protected List<RectangleI> _CalculateModuloDeltaBounds(RectangleI oldData, RectangleI newData) { // sanity checking Assert.Fatal(oldData.Point.X >= 0 && oldData.Point.Y >= 0 && oldData.IsValid, "ClipMap.CalculateModuloDeltaBounds - Negative oldData origin or bad rect."); Assert.Fatal(newData.Point.X >= 0 && newData.Point.Y >= 0 && newData.IsValid, "ClipMap.CalculateModuloDeltaBounds - Negative newData origin or bad rect."); Assert.Fatal(newData.Extent == oldData.Extent, "ClipMap.CalculateModuloDeltaBounts - Mismatching extents, can only work with matching extents."); _updateRectList.Clear(); // easiest case - if they're the same then do nothing if (oldData.Point == newData.Point) return _updateRectList; // easy case - if there's no overlap then it's all new if (!oldData.Overlaps(newData)) { // clip out to return buffer, and we're done _ClipAgainstGrid(_clipMapSize, newData, ref _updateRectList); return _updateRectList; } // calculate some useful values for both X and Y. delta is used a lot // in determining bounds, and the boundary values are important for // determining where to start copying new data in. int xDelta = newData.Point.X - oldData.Point.X; int yDelta = newData.Point.Y - oldData.Point.Y; int xBoundary = (oldData.Point.X + oldData.Extent.X) % _clipMapSize; int yBoundary = (oldData.Point.Y + oldData.Extent.Y) % _clipMapSize; Assert.Fatal(xBoundary % _clipMapSize == oldData.Point.X % _clipMapSize, "ClipMap.CalculateModuleDeltaBounds - We assume that left and right of the dataset are identical (ie, it's periodical on size of clipmap!) (x)"); Assert.Fatal(yBoundary % _clipMapSize == oldData.Point.Y % _clipMapSize, "ClipMap.CalculateModuleDeltaBounds - We assume that left and right of the dataset are identical (ie, it's periodical on size of clipmap!) (y)"); // now, let's build up our rects. we have one rect if we are moving // on the X or Y axis, two if both. we dealt with the no-move case // previously. if (xDelta == 0) { // moving on Y! so generate and store clipped results. RectangleI yRect = new RectangleI(); if (yDelta < 0) { // we need to generate the box from right of old to right of new yRect.Point = newData.Point; yRect.Extent = new Point(_clipMapSize, -yDelta); } else { // we need to generate the box from left of old to left of new yRect.Point = new Point(newData.Point.X, (oldData.Point.Y + oldData.Extent.Y)); yRect.Extent = new Point(_clipMapSize, yDelta); } // clip out to return buffer, and we're done _ClipAgainstGrid(_clipMapSize, yRect, ref _updateRectList); return _updateRectList; } else if (yDelta == 0) { // moving on X! So generate and store clipped results. RectangleI xRect = new RectangleI(); if (xDelta < 0) { // We need to generate the box from right of old to right of new. xRect.Point = newData.Point; xRect.Extent = new Point(-xDelta, _clipMapSize); } else { // we need to generate the box from left of old to left of new. xRect.Point = new Point((oldData.Point.X + oldData.Extent.X), newData.Point.Y); xRect.Extent = new Point(xDelta, _clipMapSize); } // clip out to return buffer, and we're done _ClipAgainstGrid(_clipMapSize, xRect, ref _updateRectList); return _updateRectList; } else { // Both! We have an L shape. So let's do the bulk of it in one rect, // and the remainder in the other. We'll choose X as the dominant axis. // // a-----b---------c going from e to a. // | | | // | | | // d-----e---------f So the dominant rect is abgh and the passive // | | | rect is bcef. Obviously depending on delta we // | | | have to switch things around a bit. // | | | y+ ^ // | | | | // g-----h---------i x+-> | RectangleI xRect = new RectangleI(); RectangleI yRect = new RectangleI(); if (xDelta < 0) { // case in the diagram xRect.Point = newData.Point; xRect.Extent = new Point(-xDelta, _clipMapSize); // set up what of yRect we know too yRect.Point = new Point(xRect.Point.X + xRect.Extent.X, 0); yRect.Extent = new Point(_clipMapSize + xDelta, 0); } else { // opposite of case in diagram xRect.Point = new Point(oldData.Point.X + oldData.Extent.X, newData.Point.Y); xRect.Extent = new Point(xDelta, _clipMapSize); // set up what of yRect we know too yRect.Point = new Point((xRect.Point.X + xRect.Extent.X) - _clipMapSize, 0); yRect.Extent = new Point(_clipMapSize - xRect.Extent.X, 0); } if (yDelta < 0) { // case in the diagram. yRect.Point = new Point(yRect.Point.X, newData.Point.Y); yRect.Extent = new Point(yRect.Extent.X, -yDelta); } else { // case in the diagram. yRect.Point = new Point(yRect.Point.X, oldData.Point.Y + oldData.Extent.Y); yRect.Extent = new Point(yRect.Extent.X, yDelta); } // make sure we don't overlap. Assert.Fatal(!yRect.Overlaps(xRect), "ClipMap.CalculateModuloDeltaBounds() - overlap in result rects - should not happen!"); // now run them through the clipper and we're done _ClipAgainstGrid(_clipMapSize, xRect, ref _updateRectList); _ClipAgainstGrid(_clipMapSize, yRect, ref _updateRectList); return _updateRectList; } }
/// <summary> /// Updates any neccesary clip levels to be centered on the specified position. If you're using split-screen /// or any complex post-processing effects use the Center property rather than directly calling this method. /// That will allow the clip map to recenter during UpdateAnimation rather than immediately update. Otherwise /// you risk corrupting your render targets on platforms such as XBox that don't support multiple render /// targets. /// </summary> /// <param name="position">The new clip center of the clip map.</param> public void Recenter(Vector2 position) { #if DEBUG Profiler.Instance.StartBlock("ClipMap.Recenter"); #endif _center = position; _needsRecenter = false; // update our budget texel upload budget _maxTexelUploadPerRecenter = _clipMapSize * _clipMapSize; Assert.Fatal((int)TorqueUtil.GetLog2(_clipMapSize) % 1f == 0f, "ClipMap.Recenter - Requires a power of 2 clipmap size."); // do toroidal updates on each entry of the clipstack // calculate the new texel at most detailed level. Vector2 texelCenterF = position * (float)_clipMapSize * _clipLevels[0].ScaleFactor; Point texelCenter = new Point((int)Math.Floor(texelCenterF.X), (int)Math.Floor(texelCenterF.Y)); // Note how many we were at so we can cut off at the right time. int lastTexelsUpdated = _texelsUpdated; // create a list to store desired data in List<RectangleI> desiredData; // For each texture... for (int i = _clipStackDepth - 2; i >= 0; i--) { ClipStackEntry stackEntry = _clipLevels[i]; // calculate new center point for this texture texelCenterF = position * (float)_clipMapSize * stackEntry.ScaleFactor; int texelMin = _clipMapSize / 2; int texelMax = (int)((float)(_clipMapSize) * stackEntry.ScaleFactor) - texelMin; // get the top left corner Point texelTopLeft = new Point((int)MathHelper.Clamp((int)(Math.Floor(texelCenterF.X)), texelMin, texelMax) - texelMin, (int)MathHelper.Clamp((int)(Math.Floor(texelCenterF.Y)), texelMin, texelMax) - texelMin); // prevent very small updates - the RT changes are costly Point delta = new Point(stackEntry.ToroidalOffset.X - texelTopLeft.X, stackEntry.ToroidalOffset.Y - texelTopLeft.Y); if (Math.Abs(delta.X) <= _minUpdateDelta && Math.Abs(delta.Y) <= _minUpdateDelta) continue; // this + current toroid offset tells us what regions have to be blasted RectangleI oldData = new RectangleI(stackEntry.ToroidalOffset, new Point(_clipMapSize, _clipMapSize)); RectangleI newData = new RectangleI(texelTopLeft, new Point(_clipMapSize, _clipMapSize)); // get the bounds of the new texture data we want to copy desiredData = _CalculateModuloDeltaBounds(oldData, newData); //Console.WriteLine("Level {0}: {1} rects", i, desiredData.Count); Assert.Fatal(desiredData.Count < 8, "ClipMap.Recenter - Got too many rects back from CalculateModuloDeltaBounds."); // update the clip stack entry's clip center and toroidal offset stackEntry.ClipCenter = position; stackEntry.ToroidalOffset = texelTopLeft; // update any regions we found if (desiredData.Count > 0) { //jk 9-14 temp, causes crash when enter debug mode //jk for (int t = 0, c = GFXDevice.Instance.Device.GraphicsDeviceCapabilities.MaxSimultaneousTextures; t < c; t++) GFXDevice.Instance.Device.Textures[t] = null; //jk _imageCache.BeginRectUpdates(i, stackEntry); // update regions for (int j = 0; j < desiredData.Count; j++) { Assert.Fatal(desiredData[j].IsValid, "ClipMap.Recenter - got an invalid rect."); // Note the rect, so we can then wrap and let the image cache do its thing. RectangleI srcRegion = desiredData[j]; desiredData[j] = new RectangleI(new Point(srcRegion.Point.X % _clipMapSize, srcRegion.Point.Y % _clipMapSize), desiredData[j].Extent); Assert.Fatal(newData.Contains(srcRegion), "ClipMap.Recenter - got update buffer outside of expected new data bounds."); _totalUpdates++; _texelsUpdated += srcRegion.Extent.X * srcRegion.Extent.Y; _imageCache.DoRectUpdate(i, stackEntry, srcRegion, desiredData[j]); } _imageCache.FinishRectUpdates(i, stackEntry); } // check if we've overrun our budget. if ((_texelsUpdated - lastTexelsUpdated) > _maxTexelUploadPerRecenter) { _needsRecenter = true; break; } } #if DEBUG Profiler.Instance.EndBlock("ClipMap.Recenter"); #endif }
/// <summary> /// Fills each clip stack entry with texture data. This should be called once the clip stack and image cache have been initialized. /// </summary> /// <returns>True if the operation completed successfully.</returns> public bool FillWithTextureData() { // get the interest center Vector2 texelCenterF; List<RectangleI> desiredData = new List<RectangleI>(); // first generate our desired rects for each level for (int i = 0; i < _clipStackDepth; i++) { // get this stack entry ClipStackEntry stackEntry = _clipLevels[i]; // calculate new center point for this texture. texelCenterF = stackEntry.ClipCenter * (float)_clipMapSize * stackEntry.ScaleFactor; int texelMin = _clipMapSize / 2; int texelMax = (int)((float)_clipMapSize * stackEntry.ScaleFactor) - texelMin; Point texelTopLeft = new Point((int)MathHelper.Clamp((int)(Math.Floor(texelCenterF.Y)), texelMin, texelMax) - texelMin, (int)MathHelper.Clamp((int)(Math.Floor(texelCenterF.X)), texelMin, texelMax) - texelMin); desiredData.Add(new RectangleI(texelTopLeft, new Point(_clipMapSize, _clipMapSize))); } // upload all the textures... for (int i = 0; i < _clipStackDepth; i++) { ClipStackEntry stackEntry = _clipLevels[i]; _updateRectList.Clear(); _ClipAgainstGrid(_clipMapSize, desiredData[i], ref _updateRectList); Assert.Fatal(_updateRectList.Count < 8, "ClipMap.FillWithTextureData - Got too many rects back!"); if (_updateRectList.Count > 0) { _imageCache.BeginRectUpdates(i, stackEntry); for (int j = 0; j < _updateRectList.Count; j++) { RectangleI srcRegion = _updateRectList[j]; _updateRectList[j] = new RectangleI(new Point(srcRegion.Point.X % _clipMapSize, srcRegion.Point.Y % _clipMapSize), _updateRectList[j].Extent); _imageCache.DoRectUpdate(i, stackEntry, srcRegion, _updateRectList[j]); } _imageCache.FinishRectUpdates(i, stackEntry); } stackEntry.ToroidalOffset = desiredData[i].Point; } // success! return true; }