/// <summary> /// Duplicate of the inlined version!!!!!! DANGEROUS /// </summary> /// <param name="grid"></param> /// <param name="signs"></param> /// <param name="cube"></param> /// <returns></returns> public static Vector3 calculateQefPoint(AbstractHermiteGrid grid, bool[] signs, Point3 cube) { //TODO: this can be optimized probably! var changingEdges = grid.GetAllEdgeIds().Where(e => { var ids = grid.GetEdgeVertexIds(e); return(signs[ids[0]] != signs[ids[1]]); }).ToArray(); var positions = changingEdges.Select(e => grid.GetEdgeIntersectionCubeLocal(cube, e)).ToArray(); var normals = changingEdges.Select(e => grid.GetEdgeNormal(cube, e)).ToArray(); var meanIntersectionPoint = positions.Aggregate((a, b) => a + b) * (1f / positions.Length); var leastsquares = QEFCalculator.CalculateCubeQEF(normals, positions, meanIntersectionPoint); var qefPoint = new Vector3(leastsquares[0], leastsquares[1], leastsquares[2]); if (qefPoint[0] < 0 || qefPoint[1] < 0 || qefPoint[2] < 0 || qefPoint[0] > 1 || qefPoint[1] > 1 || qefPoint[2] > 1) { qefPoint = meanIntersectionPoint; // I found someone online who does this too: http://ngildea.blogspot.be/2014/11/implementing-dual-contouring.html //TODO: should probably fix the QEF, maybe by removing singular values //ERROR! //throw new InvalidOperationException("QEF returned solution outside of cube"); } return(qefPoint); }
public IMesh buildMesh(AbstractHermiteGrid grid) { var rawMesh = buildRawMesh(grid); var builder = new MeshBuilder(); var mat = builder.CreateMaterial(); mat.ColoredMaterial = true; mat.DiffuseColor = Color.Green.dx().xna(); builder.AddCustom(rawMesh.Positions, rawMesh.Normals, rawMesh.Texcoords); var mesh = builder.CreateMesh(); return(mesh); }
public RawMeshData buildRawMesh(AbstractHermiteGrid grid) { var vertices = new List <Vector3>(); var indices = new List <int>(); var algo = new DualContouringAlgorithm(); algo.GenerateSurface(vertices, indices, grid); var triangleNormals = generateTriangleNormals(indices, vertices); var ret = new RawMeshData(indices.Select(i => vertices[i].dx()).ToArray(), indices.Select((index, numIndex) => triangleNormals[numIndex / 3].dx()).ToArray(), indices.Select(i => new Vector2().dx()).ToArray(), indices.Select(i => new Vector3().dx()).ToArray()); return(ret); }
public DifferenceGrid(AbstractHermiteGrid a, AbstractHermiteGrid b, Point3 offset) { this.a = a; this.b = b; this.offset = offset; }
public DifferenceGrid(AbstractHermiteGrid a, AbstractHermiteGrid b) : this(a, b, new Point3()) { }
private static void createQEFVertices(List <Vector3> vertices, AbstractHermiteGrid grid, Dictionary <Point3, int> vIndex) { var cubeSigns = new bool[8]; var edgeVertexIds = grid.GetAllEdgeIds().Select(e => grid.GetEdgeVertexIds(e)).ToArray(); var edgeOffsets = grid.GetAllEdgeIds().Select(e => grid.GetEdgeOffsets(e)).ToArray(); int changingEdgeCount = 0; int[] changingEdges = new int[12]; Vector3[] positions = new Vector3[12]; Vector3[] normals = new Vector3[12]; grid.ForEachCube(curr => { grid.GetCubeSigns(curr, cubeSigns); bool allTrue = true; bool allFalse = true; for (int i = 0; i < 8; i++) { var sign = cubeSigns[i]; allTrue = sign && allTrue; allFalse = !sign && allFalse; } if (allTrue || allFalse) { return; // no sign changes } //if ( cubeSigns.All( v => v ) || !cubeSigns.Any( v => v ) ) return; // no sign changes changingEdgeCount = 0; for (int i = 0; i < edgeVertexIds.Length; i++) { var ids = edgeVertexIds[i]; if (cubeSigns[ids[0]] == cubeSigns[ids[1]]) { continue; } changingEdges[changingEdgeCount] = i; changingEdgeCount++; } for (int i = 0; i < changingEdgeCount; i++) { var iEdgeId = changingEdges[i]; var iEdgeOffsets = edgeOffsets[iEdgeId]; var iEdgeData = grid.getEdgeData(curr, iEdgeId); positions[i] = Vector3.Lerp(iEdgeOffsets[0], iEdgeOffsets[1], iEdgeData.W); normals[i] = iEdgeData.TakeXYZ(); } var meanIntersectionPoint = new Vector3(); for (int i = 0; i < changingEdgeCount; i++) { meanIntersectionPoint = meanIntersectionPoint + positions[i]; } meanIntersectionPoint = meanIntersectionPoint * (1f / changingEdgeCount); var leastsquares = QEFCalculator.CalculateCubeQEF(normals, positions, changingEdgeCount, meanIntersectionPoint); var qefPoint1 = new Vector3(); qefPoint1 = new Vector3(leastsquares[0], leastsquares[1], leastsquares[2]); if (qefPoint1[0] < 0 || qefPoint1[1] < 0 || qefPoint1[2] < 0 || qefPoint1[0] > 1 || qefPoint1[1] > 1 || qefPoint1[2] > 1) { qefPoint1 = meanIntersectionPoint; // I found someone online who does this too: http://ngildea.blogspot.be/2014/11/implementing-dual-contouring.html //TODO: should probably fix the QEF, maybe by removing singular values //ERROR! //throw new InvalidOperationException("QEF returned solution outside of cube"); } vIndex[curr] = vertices.Count; vertices.Add(qefPoint1 + (Vector3)curr.ToVector3()); }); }
private static void buildTriangleIndices(List <int> indices, AbstractHermiteGrid grid, Dictionary <Point3, int> vIndex, List <DCVoxelMaterial> triangleMaterials) { // Possible quads var offsets = new[] { Point3.UnitX(), Point3.UnitY(), Point3.UnitZ(), }; var rights = new[] { Point3.UnitY(), Point3.UnitZ(), Point3.UnitX(), }; var ups = new[] { Point3.UnitZ(), Point3.UnitX(), Point3.UnitY(), }; var unitEdges = offsets.Select(o => grid.GetEdgeId(new Point3(), o)).ToArray(); //TODO: should be unit test for (int i = 0; i < 3; i++) { Debug.Assert(grid.GetEdgeOffsets(unitEdges[i])[0] == new Point3()); Debug.Assert(grid.GetEdgeOffsets(unitEdges[i])[1] == offsets[i]); } grid.ForEachCube(o => { if (!vIndex.ContainsKey(o)) { return; // No sign changes so no relevant edges here } for (int i = 0; i < 3; i++) { var edgeId = unitEdges[i]; if (!grid.HasEdgeData(o, edgeId)) { continue; } // Generate quad var right = rights[i]; var up = ups[i]; DCVoxelMaterial mat; var signs = grid.GetEdgeSigns(o, edgeId); // Face towards air by swapping right and up if (signs[1]) { var swap = right; right = up; up = swap; mat = grid.GetMaterial(o + offsets[i]); } else { mat = grid.GetMaterial(o); } // build quad faces var a = o - right; var b = o - up; var ab = o - right - up; if (!new[] { a, b, ab }.All(vIndex.ContainsKey)) { continue; // This should never happen unless on the side of the field, maybe add a check for this? } indices.AddRange(new[] { vIndex[o], vIndex[a], vIndex[ab] }); indices.AddRange(new[] { vIndex[o], vIndex[ab], vIndex[b] }); triangleMaterials.Add(mat); triangleMaterials.Add(mat); } }); }
public void GenerateSurface(List <Vector3> vertices, List <int> indices, List <DCVoxelMaterial> triangleMaterials, AbstractHermiteGrid grid) { var vIndex = new Dictionary <Point3, int>(); createQEFVertices(vertices, grid, vIndex); buildTriangleIndices(indices, grid, vIndex, triangleMaterials); }
public void GenerateSurface(List <Vector3> vertices, List <int> indices, AbstractHermiteGrid grid) { var mats = new List <DCVoxelMaterial>(); GenerateSurface(vertices, indices, mats, grid); }
public static HermiteDataGrid CopyGrid(AbstractHermiteGrid grid) { var ret = new HermiteDataGrid(); Point3 storageSize = grid.Dimensions + new Point3(1, 1, 1); ret.cells = new Array3D <Vertex>(storageSize); for (int x = 0; x < storageSize.X; x++) { for (int y = 0; y < storageSize.Y; y++) { for (int z = 0; z < storageSize.Z; z++) { var p = new Point3(x, y, z); ret.cells[p] = new Vertex() { Sign = grid.GetSign(p), Material = grid.GetMaterial(p) /*EdgeData = ret.dirs.Select(dir => * { * var edgeId = grid.GetEdgeId(p, p + dir); * return grid.HasEdgeData(p, edgeId) ? grid.getEdgeData(p, edgeId) : new Vector4(); * }).ToArray()*/ }; } } } //ret.ForEachGridPoint(p => // { // ret.cells[p] = new Vertex() // { // Sign = grid.GetSign(p), // /*EdgeData = ret.dirs.Select(dir => // { // var edgeId = grid.GetEdgeId(p, p + dir); // return grid.HasEdgeData(p, edgeId) ? grid.getEdgeData(p, edgeId) : new Vector4(); // }).ToArray()*/ // }; // }); var dirs = ret.dirs; var dirEdges = dirs.Select(i => ret.GetEdgeId(new Point3(), i)).ToArray(); ret.ForEachGridPoint(p => { var gridPointP = ret.cells.GetFast(p.X, p.Y, p.Z); gridPointP.EdgeData = new Vector4[3]; for (int i = 0; i < 3; i++) { var dir = dirs[i]; var edgeId = dirEdges[i]; Point3 endPoint = p + dir; // Optimization: direclty read from already constructed data if (!ret.cells.InArray(endPoint)) { continue; } if (gridPointP.Sign == ret.cells.GetFast(endPoint.X, endPoint.Y, endPoint.Z).Sign) { continue; } if (!grid.HasEdgeData(p, edgeId)) { // This can normally not happen, since we check if there is a sign difference by looking at the already evaluated density points. // If this would be true there is some problem with the manual determining of the existence of an edge. //throw new InvalidOperationException(); continue; } gridPointP.EdgeData[i] = grid.getEdgeData(p, edgeId); } /*val.EdgeData = ret.dirs.Select( dir => * { * * } ).ToArray();*/ ret.cells[p] = gridPointP; }); return(ret); }
public UnionGrid(AbstractHermiteGrid a, AbstractHermiteGrid b) : this(a, b, new Point3()) { }
public UnionGrid(AbstractHermiteGrid a, AbstractHermiteGrid b, Point3 offset) { _offset = offset; this.a = a; this.b = b; }