Adding initial files
This commit is contained in:
367
axios/Common/TextureTools/MSTerrain.cs
Normal file
367
axios/Common/TextureTools/MSTerrain.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Collision;
|
||||
using FarseerPhysics.Factories;
|
||||
|
||||
namespace FarseerPhysics.Common
|
||||
{
|
||||
public enum Decomposer
|
||||
{
|
||||
Bayazit,
|
||||
CDT,
|
||||
Earclip,
|
||||
Flipcode,
|
||||
Seidel,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the specified color is inside the terrain.
|
||||
/// </summary>
|
||||
public delegate bool TerrainTester(Color color);
|
||||
|
||||
/// <summary>
|
||||
/// Simple class to maintain a terrain.
|
||||
/// </summary>
|
||||
public class MSTerrain
|
||||
{
|
||||
/// <summary>
|
||||
/// World to manage terrain in.
|
||||
/// </summary>
|
||||
public World World;
|
||||
|
||||
/// <summary>
|
||||
/// Center of terrain in world units.
|
||||
/// </summary>
|
||||
public Vector2 Center;
|
||||
|
||||
/// <summary>
|
||||
/// Width of terrain in world units.
|
||||
/// </summary>
|
||||
public float Width;
|
||||
|
||||
/// <summary>
|
||||
/// Height of terrain in world units.
|
||||
/// </summary>
|
||||
public float Height;
|
||||
|
||||
/// <summary>
|
||||
/// Points per each world unit used to define the terrain in the point cloud.
|
||||
/// </summary>
|
||||
public int PointsPerUnit;
|
||||
|
||||
/// <summary>
|
||||
/// Points per cell.
|
||||
/// </summary>
|
||||
public int CellSize;
|
||||
|
||||
/// <summary>
|
||||
/// Points per sub cell.
|
||||
/// </summary>
|
||||
public int SubCellSize;
|
||||
|
||||
/// <summary>
|
||||
/// Number of iterations to perform in the Marching Squares algorithm.
|
||||
/// Note: More then 3 has almost no effect on quality.
|
||||
/// </summary>
|
||||
public int Iterations = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Decomposer to use when regenerating terrain. Can be changed on the fly without consequence.
|
||||
/// Note: Some decomposerers are unstable.
|
||||
/// </summary>
|
||||
public Decomposer Decomposer;
|
||||
|
||||
/// <summary>
|
||||
/// Point cloud defining the terrain.
|
||||
/// </summary>
|
||||
private sbyte[,] _terrainMap;
|
||||
|
||||
/// <summary>
|
||||
/// Generated bodies.
|
||||
/// </summary>
|
||||
private List<Body>[,] _bodyMap;
|
||||
|
||||
private float _localWidth;
|
||||
private float _localHeight;
|
||||
private int _xnum;
|
||||
private int _ynum;
|
||||
private AABB _dirtyArea;
|
||||
private Vector2 _topLeft;
|
||||
|
||||
public MSTerrain(World world, AABB area)
|
||||
{
|
||||
World = world;
|
||||
Width = area.Extents.X * 2;
|
||||
Height = area.Extents.Y * 2;
|
||||
Center = area.Center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the terrain for use.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
// find top left of terrain in world space
|
||||
_topLeft = new Vector2(Center.X - (Width * 0.5f), Center.Y - (-Height * 0.5f));
|
||||
|
||||
// convert the terrains size to a point cloud size
|
||||
_localWidth = Width * PointsPerUnit;
|
||||
_localHeight = Height * PointsPerUnit;
|
||||
|
||||
_terrainMap = new sbyte[(int)_localWidth + 1, (int)_localHeight + 1];
|
||||
|
||||
for (int x = 0; x < _localWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < _localHeight; y++)
|
||||
{
|
||||
_terrainMap[x, y] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
_xnum = (int)(_localWidth / CellSize);
|
||||
_ynum = (int)(_localHeight / CellSize);
|
||||
_bodyMap = new List<Body>[_xnum, _ynum];
|
||||
|
||||
// make sure to mark the dirty area to an infinitely small box
|
||||
_dirtyArea = new AABB(new Vector2(float.MaxValue, float.MaxValue), new Vector2(float.MinValue, float.MinValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a texture to the terrain using the specified TerrainTester.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to apply.</param>
|
||||
/// <param name="position">Top left position of the texture relative to the terrain.</param>
|
||||
/// <param name="tester">Delegate method used to determine what colors should be included in the terrain.</param>
|
||||
public void ApplyTexture(Texture2D texture, Vector2 position, TerrainTester tester)
|
||||
{
|
||||
Color[] colorData = new Color[texture.Width * texture.Height];
|
||||
|
||||
texture.GetData(colorData);
|
||||
|
||||
for (int y = (int)position.Y; y < texture.Height + (int)position.Y; y++)
|
||||
{
|
||||
for (int x = (int)position.X; x < texture.Width + (int)position.X; x++)
|
||||
{
|
||||
if (x >= 0 && x < _localWidth && y >= 0 && y < _localHeight)
|
||||
{
|
||||
bool inside = tester(colorData[((y - (int)position.Y) * texture.Width) + (x - (int)position.X)]);
|
||||
|
||||
if (!inside)
|
||||
_terrainMap[x, y] = 1;
|
||||
else
|
||||
_terrainMap[x, y] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate terrain
|
||||
for (int gy = 0; gy < _ynum; gy++)
|
||||
{
|
||||
for (int gx = 0; gx < _xnum; gx++)
|
||||
{
|
||||
//remove old terrain object at grid cell
|
||||
if (_bodyMap[gx, gy] != null)
|
||||
{
|
||||
for (int i = 0; i < _bodyMap[gx, gy].Count; i++)
|
||||
{
|
||||
World.RemoveBody(_bodyMap[gx, gy][i]);
|
||||
}
|
||||
}
|
||||
|
||||
_bodyMap[gx, gy] = null;
|
||||
|
||||
//generate new one
|
||||
GenerateTerrain(gx, gy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a texture to the terrain using the specified TerrainTester.
|
||||
/// </summary>
|
||||
/// <param name="position">Top left position of the texture relative to the terrain.</param>
|
||||
public void ApplyData(sbyte[,] data, Vector2 position)
|
||||
{
|
||||
for (int y = (int)position.Y; y < data.GetUpperBound(1) + (int)position.Y; y++)
|
||||
{
|
||||
for (int x = (int)position.X; x < data.GetUpperBound(0) + (int)position.X; x++)
|
||||
{
|
||||
if (x >= 0 && x < _localWidth && y >= 0 && y < _localHeight)
|
||||
{
|
||||
_terrainMap[x, y] = data[x, y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate terrain
|
||||
for (int gy = 0; gy < _ynum; gy++)
|
||||
{
|
||||
for (int gx = 0; gx < _xnum; gx++)
|
||||
{
|
||||
//remove old terrain object at grid cell
|
||||
if (_bodyMap[gx, gy] != null)
|
||||
{
|
||||
for (int i = 0; i < _bodyMap[gx, gy].Count; i++)
|
||||
{
|
||||
World.RemoveBody(_bodyMap[gx, gy][i]);
|
||||
}
|
||||
}
|
||||
|
||||
_bodyMap[gx, gy] = null;
|
||||
|
||||
//generate new one
|
||||
GenerateTerrain(gx, gy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a texture to an sbtye array compatible with ApplyData().
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to convert.</param>
|
||||
/// <param name="tester"></param>
|
||||
/// <returns></returns>
|
||||
public static sbyte[,] ConvertTextureToData(Texture2D texture, TerrainTester tester)
|
||||
{
|
||||
sbyte[,] data = new sbyte[texture.Width, texture.Height];
|
||||
Color[] colorData = new Color[texture.Width * texture.Height];
|
||||
|
||||
texture.GetData(colorData);
|
||||
|
||||
for (int y = 0; y < texture.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < texture.Width; x++)
|
||||
{
|
||||
bool inside = tester(colorData[(y * texture.Width) + x]);
|
||||
|
||||
if (!inside)
|
||||
data[x, y] = 1;
|
||||
else
|
||||
data[x, y] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify a single point in the terrain.
|
||||
/// </summary>
|
||||
/// <param name="location">World location to modify. Automatically clipped.</param>
|
||||
/// <param name="value">-1 = inside terrain, 1 = outside terrain</param>
|
||||
public void ModifyTerrain(Vector2 location, sbyte value)
|
||||
{
|
||||
// find local position
|
||||
// make position local to map space
|
||||
Vector2 p = location - _topLeft;
|
||||
|
||||
// find map position for each axis
|
||||
p.X = p.X * _localWidth / Width;
|
||||
p.Y = p.Y * -_localHeight / Height;
|
||||
|
||||
if (p.X >= 0 && p.X < _localWidth && p.Y >= 0 && p.Y < _localHeight)
|
||||
{
|
||||
_terrainMap[(int)p.X, (int)p.Y] = value;
|
||||
|
||||
// expand dirty area
|
||||
if (p.X < _dirtyArea.LowerBound.X) _dirtyArea.LowerBound.X = p.X;
|
||||
if (p.X > _dirtyArea.UpperBound.X) _dirtyArea.UpperBound.X = p.X;
|
||||
|
||||
if (p.Y < _dirtyArea.LowerBound.Y) _dirtyArea.LowerBound.Y = p.Y;
|
||||
if (p.Y > _dirtyArea.UpperBound.Y) _dirtyArea.UpperBound.Y = p.Y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerate the terrain.
|
||||
/// </summary>
|
||||
public void RegenerateTerrain()
|
||||
{
|
||||
//iterate effected cells
|
||||
var gx0 = (int)(_dirtyArea.LowerBound.X / CellSize);
|
||||
var gx1 = (int)(_dirtyArea.UpperBound.X / CellSize) + 1;
|
||||
if (gx0 < 0) gx0 = 0;
|
||||
if (gx1 > _xnum) gx1 = _xnum;
|
||||
var gy0 = (int)(_dirtyArea.LowerBound.Y / CellSize);
|
||||
var gy1 = (int)(_dirtyArea.UpperBound.Y / CellSize) + 1;
|
||||
if (gy0 < 0) gy0 = 0;
|
||||
if (gy1 > _ynum) gy1 = _ynum;
|
||||
|
||||
for (int gx = gx0; gx < gx1; gx++)
|
||||
{
|
||||
for (int gy = gy0; gy < gy1; gy++)
|
||||
{
|
||||
//remove old terrain object at grid cell
|
||||
if (_bodyMap[gx, gy] != null)
|
||||
{
|
||||
for (int i = 0; i < _bodyMap[gx, gy].Count; i++)
|
||||
{
|
||||
World.RemoveBody(_bodyMap[gx, gy][i]);
|
||||
}
|
||||
}
|
||||
|
||||
_bodyMap[gx, gy] = null;
|
||||
|
||||
//generate new one
|
||||
GenerateTerrain(gx, gy);
|
||||
}
|
||||
}
|
||||
|
||||
_dirtyArea = new AABB(new Vector2(float.MaxValue, float.MaxValue), new Vector2(float.MinValue, float.MinValue));
|
||||
}
|
||||
|
||||
private void GenerateTerrain(int gx, int gy)
|
||||
{
|
||||
float ax = gx * CellSize;
|
||||
float ay = gy * CellSize;
|
||||
|
||||
List<Vertices> polys = MarchingSquares.DetectSquares(new AABB(new Vector2(ax, ay), new Vector2(ax + CellSize, ay + CellSize)), SubCellSize, SubCellSize, _terrainMap, Iterations, true);
|
||||
if (polys.Count == 0) return;
|
||||
|
||||
_bodyMap[gx, gy] = new List<Body>();
|
||||
|
||||
// create the scale vector
|
||||
Vector2 scale = new Vector2(1f / PointsPerUnit, 1f / -PointsPerUnit);
|
||||
|
||||
// create physics object for this grid cell
|
||||
foreach (var item in polys)
|
||||
{
|
||||
// does this need to be negative?
|
||||
item.Scale(ref scale);
|
||||
item.Translate(ref _topLeft);
|
||||
item.ForceCounterClockWise();
|
||||
Vertices p = FarseerPhysics.Common.PolygonManipulation.SimplifyTools.CollinearSimplify(item);
|
||||
List<Vertices> decompPolys = new List<Vertices>();
|
||||
|
||||
switch (Decomposer)
|
||||
{
|
||||
case Decomposer.Bayazit:
|
||||
decompPolys = Decomposition.BayazitDecomposer.ConvexPartition(p);
|
||||
break;
|
||||
case Decomposer.CDT:
|
||||
decompPolys = Decomposition.CDTDecomposer.ConvexPartition(p);
|
||||
break;
|
||||
case Decomposer.Earclip:
|
||||
decompPolys = Decomposition.EarclipDecomposer.ConvexPartition(p);
|
||||
break;
|
||||
case Decomposer.Flipcode:
|
||||
decompPolys = Decomposition.FlipcodeDecomposer.ConvexPartition(p);
|
||||
break;
|
||||
case Decomposer.Seidel:
|
||||
decompPolys = Decomposition.SeidelDecomposer.ConvexPartition(p, 0.001f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (Vertices poly in decompPolys)
|
||||
{
|
||||
if (poly.Count > 2)
|
||||
_bodyMap[gx, gy].Add(BodyFactory.CreatePolygon(World, poly, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
800
axios/Common/TextureTools/MarchingSquares.cs
Normal file
800
axios/Common/TextureTools/MarchingSquares.cs
Normal file
@@ -0,0 +1,800 @@
|
||||
using System.Collections.Generic;
|
||||
using FarseerPhysics.Collision;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FarseerPhysics.Common
|
||||
{
|
||||
// Ported by Matthew Bettcher - Feb 2011
|
||||
|
||||
/*
|
||||
Copyright (c) 2010, Luca Deltodesco
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions
|
||||
and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of the nape project nor the names of its contributors may be used to endorse
|
||||
or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
public static class MarchingSquares
|
||||
{
|
||||
/// <summary>
|
||||
/// Marching squares over the given domain using the mesh defined via the dimensions
|
||||
/// (wid,hei) to build a set of polygons such that f(x,y) less than 0, using the given number
|
||||
/// 'bin' for recursive linear inteprolation along cell boundaries.
|
||||
///
|
||||
/// if 'comb' is true, then the polygons will also be composited into larger possible concave
|
||||
/// polygons.
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
/// <param name="cellWidth"></param>
|
||||
/// <param name="cellHeight"></param>
|
||||
/// <param name="f"></param>
|
||||
/// <param name="lerpCount"></param>
|
||||
/// <param name="combine"></param>
|
||||
/// <returns></returns>
|
||||
public static List<Vertices> DetectSquares(AABB domain, float cellWidth, float cellHeight, sbyte[,] f,
|
||||
int lerpCount, bool combine)
|
||||
{
|
||||
CxFastList<GeomPoly> ret = new CxFastList<GeomPoly>();
|
||||
|
||||
List<Vertices> verticesList = new List<Vertices>();
|
||||
|
||||
//NOTE: removed assignments as they were not used.
|
||||
List<GeomPoly> polyList;
|
||||
GeomPoly gp;
|
||||
|
||||
int xn = (int)(domain.Extents.X * 2 / cellWidth);
|
||||
bool xp = xn == (domain.Extents.X * 2 / cellWidth);
|
||||
int yn = (int)(domain.Extents.Y * 2 / cellHeight);
|
||||
bool yp = yn == (domain.Extents.Y * 2 / cellHeight);
|
||||
if (!xp) xn++;
|
||||
if (!yp) yn++;
|
||||
|
||||
sbyte[,] fs = new sbyte[xn + 1, yn + 1];
|
||||
GeomPolyVal[,] ps = new GeomPolyVal[xn + 1, yn + 1];
|
||||
|
||||
//populate shared function lookups.
|
||||
for (int x = 0; x < xn + 1; x++)
|
||||
{
|
||||
int x0;
|
||||
if (x == xn) x0 = (int)domain.UpperBound.X;
|
||||
else x0 = (int)(x * cellWidth + domain.LowerBound.X);
|
||||
for (int y = 0; y < yn + 1; y++)
|
||||
{
|
||||
int y0;
|
||||
if (y == yn) y0 = (int)domain.UpperBound.Y;
|
||||
else y0 = (int)(y * cellHeight + domain.LowerBound.Y);
|
||||
fs[x, y] = f[x0, y0];
|
||||
}
|
||||
}
|
||||
|
||||
//generate sub-polys and combine to scan lines
|
||||
for (int y = 0; y < yn; y++)
|
||||
{
|
||||
float y0 = y * cellHeight + domain.LowerBound.Y;
|
||||
float y1;
|
||||
if (y == yn - 1) y1 = domain.UpperBound.Y;
|
||||
else y1 = y0 + cellHeight;
|
||||
GeomPoly pre = null;
|
||||
for (int x = 0; x < xn; x++)
|
||||
{
|
||||
float x0 = x * cellWidth + domain.LowerBound.X;
|
||||
float x1;
|
||||
if (x == xn - 1) x1 = domain.UpperBound.X;
|
||||
else x1 = x0 + cellWidth;
|
||||
|
||||
gp = new GeomPoly();
|
||||
|
||||
int key = MarchSquare(f, fs, ref gp, x, y, x0, y0, x1, y1, lerpCount);
|
||||
if (gp.Length != 0)
|
||||
{
|
||||
if (combine && pre != null && (key & 9) != 0)
|
||||
{
|
||||
combLeft(ref pre, ref gp);
|
||||
gp = pre;
|
||||
}
|
||||
else
|
||||
ret.Add(gp);
|
||||
ps[x, y] = new GeomPolyVal(gp, key);
|
||||
}
|
||||
else
|
||||
gp = null;
|
||||
pre = gp;
|
||||
}
|
||||
}
|
||||
if (!combine)
|
||||
{
|
||||
polyList = ret.GetListOfElements();
|
||||
|
||||
foreach (GeomPoly poly in polyList)
|
||||
{
|
||||
verticesList.Add(new Vertices(poly.Points.GetListOfElements()));
|
||||
}
|
||||
|
||||
return verticesList;
|
||||
}
|
||||
|
||||
//combine scan lines together
|
||||
for (int y = 1; y < yn; y++)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < xn)
|
||||
{
|
||||
GeomPolyVal p = ps[x, y];
|
||||
|
||||
//skip along scan line if no polygon exists at this point
|
||||
if (p == null)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
//skip along if current polygon cannot be combined above.
|
||||
if ((p.Key & 12) == 0)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
//skip along if no polygon exists above.
|
||||
GeomPolyVal u = ps[x, y - 1];
|
||||
if (u == null)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
//skip along if polygon above cannot be combined with.
|
||||
if ((u.Key & 3) == 0)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
float ax = x * cellWidth + domain.LowerBound.X;
|
||||
float ay = y * cellHeight + domain.LowerBound.Y;
|
||||
|
||||
CxFastList<Vector2> bp = p.GeomP.Points;
|
||||
CxFastList<Vector2> ap = u.GeomP.Points;
|
||||
|
||||
//skip if it's already been combined with above polygon
|
||||
if (u.GeomP == p.GeomP)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
//combine above (but disallow the hole thingies
|
||||
CxFastListNode<Vector2> bi = bp.Begin();
|
||||
while (Square(bi.Elem().Y - ay) > Settings.Epsilon || bi.Elem().X < ax) bi = bi.Next();
|
||||
|
||||
//NOTE: Unused
|
||||
//Vector2 b0 = bi.elem();
|
||||
Vector2 b1 = bi.Next().Elem();
|
||||
if (Square(b1.Y - ay) > Settings.Epsilon)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool brk = true;
|
||||
CxFastListNode<Vector2> ai = ap.Begin();
|
||||
while (ai != ap.End())
|
||||
{
|
||||
if (VecDsq(ai.Elem(), b1) < Settings.Epsilon)
|
||||
{
|
||||
brk = false;
|
||||
break;
|
||||
}
|
||||
ai = ai.Next();
|
||||
}
|
||||
if (brk)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
CxFastListNode<Vector2> bj = bi.Next().Next();
|
||||
if (bj == bp.End()) bj = bp.Begin();
|
||||
while (bj != bi)
|
||||
{
|
||||
ai = ap.Insert(ai, bj.Elem()); // .clone()
|
||||
bj = bj.Next();
|
||||
if (bj == bp.End()) bj = bp.Begin();
|
||||
u.GeomP.Length++;
|
||||
}
|
||||
//u.p.simplify(float.Epsilon,float.Epsilon);
|
||||
//
|
||||
ax = x + 1;
|
||||
while (ax < xn)
|
||||
{
|
||||
GeomPolyVal p2 = ps[(int)ax, y];
|
||||
if (p2 == null || p2.GeomP != p.GeomP)
|
||||
{
|
||||
ax++;
|
||||
continue;
|
||||
}
|
||||
p2.GeomP = u.GeomP;
|
||||
ax++;
|
||||
}
|
||||
ax = x - 1;
|
||||
while (ax >= 0)
|
||||
{
|
||||
GeomPolyVal p2 = ps[(int)ax, y];
|
||||
if (p2 == null || p2.GeomP != p.GeomP)
|
||||
{
|
||||
ax--;
|
||||
continue;
|
||||
}
|
||||
p2.GeomP = u.GeomP;
|
||||
ax--;
|
||||
}
|
||||
ret.Remove(p.GeomP);
|
||||
p.GeomP = u.GeomP;
|
||||
|
||||
x = (int)((bi.Next().Elem().X - domain.LowerBound.X) / cellWidth) + 1;
|
||||
//x++; this was already commented out!
|
||||
}
|
||||
}
|
||||
|
||||
polyList = ret.GetListOfElements();
|
||||
|
||||
foreach (GeomPoly poly in polyList)
|
||||
{
|
||||
verticesList.Add(new Vertices(poly.Points.GetListOfElements()));
|
||||
}
|
||||
|
||||
return verticesList;
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/** Linearly interpolate between (x0 to x1) given a value at these coordinates (v0 and v1)
|
||||
such as to approximate value(return) = 0
|
||||
**/
|
||||
|
||||
private static int[] _lookMarch = {
|
||||
0x00, 0xE0, 0x38, 0xD8, 0x0E, 0xEE, 0x36, 0xD6, 0x83, 0x63, 0xBB, 0x5B, 0x8D,
|
||||
0x6D, 0xB5, 0x55
|
||||
};
|
||||
|
||||
private static float Lerp(float x0, float x1, float v0, float v1)
|
||||
{
|
||||
float dv = v0 - v1;
|
||||
float t;
|
||||
if (dv * dv < Settings.Epsilon)
|
||||
t = 0.5f;
|
||||
else t = v0 / dv;
|
||||
return x0 + t * (x1 - x0);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/** Recursive linear interpolation for use in marching squares **/
|
||||
|
||||
private static float Xlerp(float x0, float x1, float y, float v0, float v1, sbyte[,] f, int c)
|
||||
{
|
||||
float xm = Lerp(x0, x1, v0, v1);
|
||||
if (c == 0)
|
||||
return xm;
|
||||
|
||||
sbyte vm = f[(int)xm, (int)y];
|
||||
|
||||
if (v0 * vm < 0)
|
||||
return Xlerp(x0, xm, y, v0, vm, f, c - 1);
|
||||
|
||||
return Xlerp(xm, x1, y, vm, v1, f, c - 1);
|
||||
}
|
||||
|
||||
/** Recursive linear interpolation for use in marching squares **/
|
||||
|
||||
private static float Ylerp(float y0, float y1, float x, float v0, float v1, sbyte[,] f, int c)
|
||||
{
|
||||
float ym = Lerp(y0, y1, v0, v1);
|
||||
if (c == 0)
|
||||
return ym;
|
||||
|
||||
sbyte vm = f[(int)x, (int)ym];
|
||||
|
||||
if (v0 * vm < 0)
|
||||
return Ylerp(y0, ym, x, v0, vm, f, c - 1);
|
||||
|
||||
return Ylerp(ym, y1, x, vm, v1, f, c - 1);
|
||||
}
|
||||
|
||||
/** Square value for use in marching squares **/
|
||||
|
||||
private static float Square(float x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
private static float VecDsq(Vector2 a, Vector2 b)
|
||||
{
|
||||
Vector2 d = a - b;
|
||||
return d.X * d.X + d.Y * d.Y;
|
||||
}
|
||||
|
||||
private static float VecCross(Vector2 a, Vector2 b)
|
||||
{
|
||||
return a.X * b.Y - a.Y * b.X;
|
||||
}
|
||||
|
||||
/** Look-up table to relate polygon key with the vertices that should be used for
|
||||
the sub polygon in marching squares
|
||||
**/
|
||||
|
||||
/** Perform a single celled marching square for for the given cell defined by (x0,y0) (x1,y1)
|
||||
using the function f for recursive interpolation, given the look-up table 'fs' of
|
||||
the values of 'f' at cell vertices with the result to be stored in 'poly' given the actual
|
||||
coordinates of 'ax' 'ay' in the marching squares mesh.
|
||||
**/
|
||||
|
||||
private static int MarchSquare(sbyte[,] f, sbyte[,] fs, ref GeomPoly poly, int ax, int ay, float x0, float y0,
|
||||
float x1, float y1, int bin)
|
||||
{
|
||||
//key lookup
|
||||
int key = 0;
|
||||
sbyte v0 = fs[ax, ay];
|
||||
if (v0 < 0) key |= 8;
|
||||
sbyte v1 = fs[ax + 1, ay];
|
||||
if (v1 < 0) key |= 4;
|
||||
sbyte v2 = fs[ax + 1, ay + 1];
|
||||
if (v2 < 0) key |= 2;
|
||||
sbyte v3 = fs[ax, ay + 1];
|
||||
if (v3 < 0) key |= 1;
|
||||
|
||||
int val = _lookMarch[key];
|
||||
if (val != 0)
|
||||
{
|
||||
CxFastListNode<Vector2> pi = null;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
Vector2 p;
|
||||
if ((val & (1 << i)) != 0)
|
||||
{
|
||||
if (i == 7 && (val & 1) == 0)
|
||||
poly.Points.Add(p = new Vector2(x0, Ylerp(y0, y1, x0, v0, v3, f, bin)));
|
||||
else
|
||||
{
|
||||
if (i == 0) p = new Vector2(x0, y0);
|
||||
else if (i == 2) p = new Vector2(x1, y0);
|
||||
else if (i == 4) p = new Vector2(x1, y1);
|
||||
else if (i == 6) p = new Vector2(x0, y1);
|
||||
|
||||
else if (i == 1) p = new Vector2(Xlerp(x0, x1, y0, v0, v1, f, bin), y0);
|
||||
else if (i == 5) p = new Vector2(Xlerp(x0, x1, y1, v3, v2, f, bin), y1);
|
||||
|
||||
else if (i == 3) p = new Vector2(x1, Ylerp(y0, y1, x1, v1, v2, f, bin));
|
||||
else p = new Vector2(x0, Ylerp(y0, y1, x0, v0, v3, f, bin));
|
||||
|
||||
pi = poly.Points.Insert(pi, p);
|
||||
}
|
||||
poly.Length++;
|
||||
}
|
||||
}
|
||||
//poly.simplify(float.Epsilon,float.Epsilon);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/** Used in polygon composition to composit polygons into scan lines
|
||||
Combining polya and polyb into one super-polygon stored in polya.
|
||||
**/
|
||||
|
||||
private static void combLeft(ref GeomPoly polya, ref GeomPoly polyb)
|
||||
{
|
||||
CxFastList<Vector2> ap = polya.Points;
|
||||
CxFastList<Vector2> bp = polyb.Points;
|
||||
CxFastListNode<Vector2> ai = ap.Begin();
|
||||
CxFastListNode<Vector2> bi = bp.Begin();
|
||||
|
||||
Vector2 b = bi.Elem();
|
||||
CxFastListNode<Vector2> prea = null;
|
||||
while (ai != ap.End())
|
||||
{
|
||||
Vector2 a = ai.Elem();
|
||||
if (VecDsq(a, b) < Settings.Epsilon)
|
||||
{
|
||||
//ignore shared vertex if parallel
|
||||
if (prea != null)
|
||||
{
|
||||
Vector2 a0 = prea.Elem();
|
||||
b = bi.Next().Elem();
|
||||
|
||||
Vector2 u = a - a0;
|
||||
//vec_new(u); vec_sub(a.p.p, a0.p.p, u);
|
||||
Vector2 v = b - a;
|
||||
//vec_new(v); vec_sub(b.p.p, a.p.p, v);
|
||||
float dot = VecCross(u, v);
|
||||
if (dot * dot < Settings.Epsilon)
|
||||
{
|
||||
ap.Erase(prea, ai);
|
||||
polya.Length--;
|
||||
ai = prea;
|
||||
}
|
||||
}
|
||||
|
||||
//insert polyb into polya
|
||||
bool fst = true;
|
||||
CxFastListNode<Vector2> preb = null;
|
||||
while (!bp.Empty())
|
||||
{
|
||||
Vector2 bb = bp.Front();
|
||||
bp.Pop();
|
||||
if (!fst && !bp.Empty())
|
||||
{
|
||||
ai = ap.Insert(ai, bb);
|
||||
polya.Length++;
|
||||
preb = ai;
|
||||
}
|
||||
fst = false;
|
||||
}
|
||||
|
||||
//ignore shared vertex if parallel
|
||||
ai = ai.Next();
|
||||
Vector2 a1 = ai.Elem();
|
||||
ai = ai.Next();
|
||||
if (ai == ap.End()) ai = ap.Begin();
|
||||
Vector2 a2 = ai.Elem();
|
||||
Vector2 a00 = preb.Elem();
|
||||
Vector2 uu = a1 - a00;
|
||||
//vec_new(u); vec_sub(a1.p, a0.p, u);
|
||||
Vector2 vv = a2 - a1;
|
||||
//vec_new(v); vec_sub(a2.p, a1.p, v);
|
||||
float dot1 = VecCross(uu, vv);
|
||||
if (dot1 * dot1 < Settings.Epsilon)
|
||||
{
|
||||
ap.Erase(preb, preb.Next());
|
||||
polya.Length--;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
prea = ai;
|
||||
ai = ai.Next();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CxFastList from nape physics
|
||||
|
||||
#region Nested type: CxFastList
|
||||
|
||||
/// <summary>
|
||||
/// Designed as a complete port of CxFastList from CxStd.
|
||||
/// </summary>
|
||||
internal class CxFastList<T>
|
||||
{
|
||||
// first node in the list
|
||||
private CxFastListNode<T> _head;
|
||||
private int _count;
|
||||
|
||||
/// <summary>
|
||||
/// Iterator to start of list (O(1))
|
||||
/// </summary>
|
||||
public CxFastListNode<T> Begin()
|
||||
{
|
||||
return _head;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterator to end of list (O(1))
|
||||
/// </summary>
|
||||
public CxFastListNode<T> End()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns first element of list (O(1))
|
||||
/// </summary>
|
||||
public T Front()
|
||||
{
|
||||
return _head.Elem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// add object to list (O(1))
|
||||
/// </summary>
|
||||
public CxFastListNode<T> Add(T value)
|
||||
{
|
||||
CxFastListNode<T> newNode = new CxFastListNode<T>(value);
|
||||
if (_head == null)
|
||||
{
|
||||
newNode._next = null;
|
||||
_head = newNode;
|
||||
_count++;
|
||||
return newNode;
|
||||
}
|
||||
newNode._next = _head;
|
||||
_head = newNode;
|
||||
|
||||
_count++;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// remove object from list, returns true if an element was removed (O(n))
|
||||
/// </summary>
|
||||
public bool Remove(T value)
|
||||
{
|
||||
CxFastListNode<T> head = _head;
|
||||
CxFastListNode<T> prev = _head;
|
||||
|
||||
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
||||
|
||||
if (head != null)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
do
|
||||
{
|
||||
// if we are on the value to be removed
|
||||
if (comparer.Equals(head._elt, value))
|
||||
{
|
||||
// then we need to patch the list
|
||||
// check to see if we are removing the _head
|
||||
if (head == _head)
|
||||
{
|
||||
_head = head._next;
|
||||
_count--;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// were not at the head
|
||||
prev._next = head._next;
|
||||
_count--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// cache the current as the previous for the next go around
|
||||
prev = head;
|
||||
head = head._next;
|
||||
} while (head != null);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// pop element from head of list (O(1)) Note: this does not return the object popped!
|
||||
/// There is good reason to this, and it regards the Alloc list variants which guarantee
|
||||
/// objects are released to the object pool. You do not want to retrieve an element
|
||||
/// through pop or else that object may suddenly be used by another piece of code which
|
||||
/// retrieves it from the object pool.
|
||||
/// </summary>
|
||||
public CxFastListNode<T> Pop()
|
||||
{
|
||||
return Erase(null, _head);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// insert object after 'node' returning an iterator to the inserted object.
|
||||
/// </summary>
|
||||
public CxFastListNode<T> Insert(CxFastListNode<T> node, T value)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return Add(value);
|
||||
}
|
||||
CxFastListNode<T> newNode = new CxFastListNode<T>(value);
|
||||
CxFastListNode<T> nextNode = node._next;
|
||||
newNode._next = nextNode;
|
||||
node._next = newNode;
|
||||
|
||||
_count++;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// removes the element pointed to by 'node' with 'prev' being the previous iterator,
|
||||
/// returning an iterator to the element following that of 'node' (O(1))
|
||||
/// </summary>
|
||||
public CxFastListNode<T> Erase(CxFastListNode<T> prev, CxFastListNode<T> node)
|
||||
{
|
||||
// cache the node after the node to be removed
|
||||
CxFastListNode<T> nextNode = node._next;
|
||||
if (prev != null)
|
||||
prev._next = nextNode;
|
||||
else if (_head != null)
|
||||
_head = _head._next;
|
||||
else
|
||||
return null;
|
||||
|
||||
_count--;
|
||||
return nextNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// whether the list is empty (O(1))
|
||||
/// </summary>
|
||||
public bool Empty()
|
||||
{
|
||||
if (_head == null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// computes size of list (O(n))
|
||||
/// </summary>
|
||||
public int Size()
|
||||
{
|
||||
CxFastListNode<T> i = Begin();
|
||||
int count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
count++;
|
||||
} while (i.Next() != null);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// empty the list (O(1) if CxMixList, O(n) otherwise)
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
CxFastListNode<T> head = _head;
|
||||
while (head != null)
|
||||
{
|
||||
CxFastListNode<T> node2 = head;
|
||||
head = head._next;
|
||||
node2._next = null;
|
||||
}
|
||||
_head = null;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns true if 'value' is an element of the list (O(n))
|
||||
/// </summary>
|
||||
public bool Has(T value)
|
||||
{
|
||||
return (Find(value) != null);
|
||||
}
|
||||
|
||||
// Non CxFastList Methods
|
||||
public CxFastListNode<T> Find(T value)
|
||||
{
|
||||
// start at head
|
||||
CxFastListNode<T> head = _head;
|
||||
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
||||
if (head != null)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (comparer.Equals(head._elt, value))
|
||||
{
|
||||
return head;
|
||||
}
|
||||
head = head._next;
|
||||
} while (head != _head);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
if (head._elt == null)
|
||||
{
|
||||
return head;
|
||||
}
|
||||
head = head._next;
|
||||
} while (head != _head);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<T> GetListOfElements()
|
||||
{
|
||||
List<T> list = new List<T>();
|
||||
|
||||
CxFastListNode<T> iter = Begin();
|
||||
|
||||
if (iter != null)
|
||||
{
|
||||
do
|
||||
{
|
||||
list.Add(iter._elt);
|
||||
iter = iter._next;
|
||||
} while (iter != null);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: CxFastListNode
|
||||
|
||||
internal class CxFastListNode<T>
|
||||
{
|
||||
internal T _elt;
|
||||
internal CxFastListNode<T> _next;
|
||||
|
||||
public CxFastListNode(T obj)
|
||||
{
|
||||
_elt = obj;
|
||||
}
|
||||
|
||||
public T Elem()
|
||||
{
|
||||
return _elt;
|
||||
}
|
||||
|
||||
public CxFastListNode<T> Next()
|
||||
{
|
||||
return _next;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Stuff
|
||||
|
||||
#region Nested type: GeomPoly
|
||||
|
||||
internal class GeomPoly
|
||||
{
|
||||
public int Length;
|
||||
public CxFastList<Vector2> Points;
|
||||
|
||||
public GeomPoly()
|
||||
{
|
||||
Points = new CxFastList<Vector2>();
|
||||
Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: GeomPolyVal
|
||||
|
||||
private class GeomPolyVal
|
||||
{
|
||||
/** Associated polygon at coordinate **/
|
||||
/** Key of original sub-polygon **/
|
||||
public int Key;
|
||||
public GeomPoly GeomP;
|
||||
|
||||
public GeomPolyVal(GeomPoly geomP, int K)
|
||||
{
|
||||
GeomP = geomP;
|
||||
Key = K;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
1338
axios/Common/TextureTools/TextureConverter.cs
Normal file
1338
axios/Common/TextureTools/TextureConverter.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user