Example #1
0
 /// <summary>
 /// Write the csg into a mesh.
 /// </summary>
 internal static void Tessellate(this Csg.Solid csg, ref Mesh mesh)
 {
     foreach (var p in csg.Polygons)
     {
         p.AddToMesh(ref mesh);
     }
     mesh.ComputeNormals();
 }
Example #2
0
 /// <summary>
 /// Write the csg into a mesh.
 /// </summary>
 internal static void Tessellate(this Csg.Solid csg, ref Mesh mesh, Transform transform = null, Color color = default(Color))
 {
     foreach (var p in csg.Polygons)
     {
         p.AddToMesh(ref mesh);
     }
     mesh.ComputeNormals();
 }
Example #3
0
        /// <summary>
        /// Triangulate this csg and pack the triangulated data into buffers
        /// appropriate for use with gltf.
        /// </summary>
        internal static GraphicsBuffers Tessellate(this Csg.Solid csg)
        {
            var buffers = new GraphicsBuffers();

            ushort iCursor = 0;

            (Vector3 U, Vector3 V)basis;

            const float SEARCH_RADIUS = 0.001f;

            // Setup the octree to fit around the csg.
            // This requires one expensive initialization.
            var verts  = csg.Polygons.SelectMany(p => p.Vertices).Select(v => v.Pos.ToElementsVector()).ToArray();
            var bounds = new BBox3(verts);
            var center = bounds.Center();
            var origin = new Point((float)center.X, (float)center.Y, (float)center.Z);
            var size   = (float)bounds.Max.DistanceTo(bounds.Min);
            var octree = new PointOctree <(Vector3 position, Vector3 normal, ushort index)>(size, origin, SEARCH_RADIUS);

            foreach (var p in csg.Polygons)
            {
                var vertexIndices = new ushort[p.Vertices.Count];

                var a = p.Vertices[0].Pos.ToElementsVector();
                var b = p.Vertices[1].Pos.ToElementsVector();
                var c = p.Vertices[2].Pos.ToElementsVector();
                basis = ComputeBasisAndNormalForTriangle(a, b, c, out Vector3 normal);

                // Anything with 3 vertices is a triangle. Manually
                // tesselate triangles. For everything else, use
                // the tessellator.
                if (p.Vertices.Count > 2 && p.Vertices.Count <= 3)
                {
                    for (var i = 0; i < p.Vertices.Count; i++)
                    {
                        var v = p.Vertices[i];

                        var op = new Point((float)v.Pos.X, (float)v.Pos.Y, (float)v.Pos.Z);
                        var ep = v.Pos.ToElementsVector();
                        if (TryGetExistingVertex(op, ep, octree, normal, SEARCH_RADIUS, out ushort vertexIndex))
                        {
                            vertexIndices[i] = vertexIndex;
                            continue;
                        }

                        vertexIndices[i] = iCursor;
                        iCursor++;

                        var uu = basis.U.Dot(ep);
                        var vv = basis.V.Dot(ep);
                        buffers.AddVertex(ep, normal, new UV(uu, vv));

                        octree.Add((ep, normal, vertexIndices[i]), op);
Example #4
0
        /// <summary>
        /// Get the computed csg solid.
        /// The csg is centered on the origin by default.
        /// </summary>
        /// <param name="transformed">Should the csg be transformed by the element's transform?</param>
        internal Csg.Solid GetFinalCsgFromSolids(bool transformed = false)
        {
            // To properly compute csgs, all solid operation csgs need
            // to be transformed into their final position. Then the csgs
            // can be computed and by default the final csg will have the inverse of the
            // geometric element's transform applied to "reset" it.
            // The transforms applied to each node in the glTF will then
            // ensure that the elements are correctly transformed.
            Csg.Solid csg = new Csg.Solid();

            var solids = Representation.SolidOperations.Where(op => op.IsVoid == false)
                         .Select(op => TransformedSolidOperation(op))
                         .ToArray();
            var voids = Representation.SolidOperations.Where(op => op.IsVoid == true)
                        .Select(op => TransformedSolidOperation(op))
                        .ToArray();

            if (this is IHasOpenings)
            {
                var openingContainer = (IHasOpenings)this;
                voids = voids.Concat(openingContainer.Openings.SelectMany(o => o.Representation.SolidOperations
                                                                          .Where(op => op.IsVoid == true)
                                                                          .Select(op => op._csg.Transform(o.Transform.ToMatrix4x4())))).ToArray();
            }
            // Don't try CSG booleans if we only have one one solid.
            if (solids.Count() == 1)
            {
                csg = solids.First();
            }
            else
            {
                csg = csg.Union(solids);
            }
            if (voids.Count() > 0)
            {
                csg = csg.Substract(voids);
            }

            if (Transform == null || transformed)
            {
                return(csg);
            }
            else
            {
                var inverse = new Transform(Transform);
                inverse.Invert();

                csg = csg.Transform(inverse.ToMatrix4x4());
                return(csg);
            }
        }
Example #5
0
        public CsgBenchmarks()
        {
            var line    = new Line(new Vector3(0, 0, 0), new Vector3(10, 0, 5));
            var profile = Polygon.Rectangle(Units.InchesToMeters(10), Units.InchesToMeters(20));

            _beam = new Beam(line, profile, BuiltInMaterials.Steel);
            for (var i = 0.0; i <= 1.0; i += 1.0 / (double)NumberOfHoles)
            {
                var t  = line.TransformAt(i);
                var lt = new Transform(t.Origin, t.ZAxis, t.XAxis.Negate());
                lt.Move(lt.ZAxis * -0.5);
                var hole = new Extrude(Polygon.Rectangle(0.1, 0.1), 1.0, Vector3.ZAxis, true)
                {
                    LocalTransform = lt
                };
                _beam.Representation.SolidOperations.Add(hole);
            }
            _csg = _beam.GetFinalCsgFromSolids();
        }
Example #6
0
        public static MeshData FromSolid(Csg.Solid solid, string name)
        {
            var vertices = solid.Polygons
                           .SelectMany(polygon => polygon.Vertices)
                           .Select(vertex =>
                                   new Vector3((float)vertex.Pos.X, (float)vertex.Pos.Y, (float)vertex.Pos.Z)
                                   ).ToArray();

            var normals = solid.Polygons.SelectMany(polygon => {
                var faceNormal = polygon.Plane.Normal;
                var normal     = new Vector3(
                    (float)faceNormal.X,
                    (float)faceNormal.Y,
                    (float)faceNormal.Z
                    );

                return(polygon.Vertices.Select(_ => normal));
            }).ToArray();

            var vi      = 0;
            var indices = solid.Polygons.SelectMany(face => {
                var faceIndices = new List <ushort>();
                for (var v = 2; v < face.Vertices.Count; v++)
                {
                    faceIndices.Add((ushort)(vi));
                    faceIndices.Add((ushort)(vi + v - 1));
                    faceIndices.Add((ushort)(vi + v));
                }
                vi += face.Vertices.Count;
                return(faceIndices);
            }).ToArray();

            var builder = new MeshBuilder().WithFrontFaceClockwise(false);

            for (int i = 0; i < vertices.Length; i++)
            {
                builder.WithVertex(new VertexPositionNormal(vertices[i], normals[i]));
            }
            builder.WithIndices(indices);

            return(builder.Build <VertexPositionNormal>(name));
        }
Example #7
0
        /// <summary>
        /// Triangulate this csg and pack the triangulated data into buffers
        /// appropriate for use with gltf.
        /// </summary>
        internal static void Tessellate(this Csg.Solid csg, out byte[] vertexBuffer,
                                        out byte[] indexBuffer, out byte[] normalBuffer, out byte[] colorBuffer, out byte[] uvBuffer,
                                        out double[] vmax, out double[] vmin, out double[] nmin, out double[] nmax,
                                        out float[] cmin, out float[] cmax, out ushort imin, out ushort imax, out double[] uvmin, out double[] uvmax)
        {
            var tessellations = new Tess[csg.Polygons.Count];

            var fi = 0;

            foreach (var p in csg.Polygons)
            {
                var tess = new Tess();
                tess.NoEmptyPolygons = true;
                tess.AddContour(p.Vertices.ToContourVertices());

                tess.Tessellate(WindingRule.Positive, LibTessDotNet.Double.ElementType.Polygons, 3);
                tessellations[fi] = tess;
                fi++;
            }

            var floatSize  = sizeof(float);
            var ushortSize = sizeof(ushort);

            var vertexCount = tessellations.Sum(t => t.VertexCount);
            var indexCount  = tessellations.Sum(t => t.Elements.Length);

            vertexBuffer = new byte[vertexCount * floatSize * 3];
            normalBuffer = new byte[vertexCount * floatSize * 3];
            indexBuffer  = new byte[indexCount * ushortSize];
            uvBuffer     = new byte[vertexCount * floatSize * 2];

            // Vertex colors are not used in this context currently.
            colorBuffer = new byte[0];
            cmin        = new float[0];
            cmax        = new float[0];

            vmax = new double[3] {
                double.MinValue, double.MinValue, double.MinValue
            };
            vmin = new double[3] {
                double.MaxValue, double.MaxValue, double.MaxValue
            };
            nmin = new double[3] {
                double.MaxValue, double.MaxValue, double.MaxValue
            };
            nmax = new double[3] {
                double.MinValue, double.MinValue, double.MinValue
            };

            uvmin = new double[2] {
                double.MaxValue, double.MaxValue
            };
            uvmax = new double[2] {
                double.MinValue, double.MinValue
            };

            imax = ushort.MinValue;
            imin = ushort.MaxValue;

            var vi  = 0;
            var ii  = 0;
            var uvi = 0;

            var iCursor = 0;

            (Vector3 U, Vector3 V)basis;

            for (var i = 0; i < tessellations.Length; i++)
            {
                var tess = tessellations[i];
                if (tess.ElementCount == 0)
                {
                    continue;
                }
                var a   = tess.Vertices[tess.Elements[0]].Position.ToVector3();
                var b   = tess.Vertices[tess.Elements[1]].Position.ToVector3();
                var c   = tess.Vertices[tess.Elements[2]].Position.ToVector3();
                var tmp = (b - a).Unitized();
                var n   = tmp.Cross(c - a).Unitized();

                // Calculate the texture space basis vectors
                // from the first triangle. This is acceptable
                // for planar faces.
                // TODO: Update this when we support non-planar faces.
                // https://gamedev.stackexchange.com/questions/172352/finding-texture-coordinates-for-plane
                basis = n.ComputeDefaultBasisVectors();

                for (var j = 0; j < tess.Vertices.Length; j++)
                {
                    var v = tess.Vertices[j];
                    var p = v.Position.ToVector3();

                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)p.X), 0, vertexBuffer, vi, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)p.Y), 0, vertexBuffer, vi + floatSize, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)p.Z), 0, vertexBuffer, vi + 2 * floatSize, floatSize);

                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)n.X), 0, normalBuffer, vi, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)n.Y), 0, normalBuffer, vi + floatSize, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)n.Z), 0, normalBuffer, vi + 2 * floatSize, floatSize);

                    var uu = basis.U.Dot(p);
                    var vv = basis.V.Dot(p);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)uu), 0, uvBuffer, uvi, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)vv), 0, uvBuffer, uvi + floatSize, floatSize);

                    uvi += 2 * floatSize;
                    vi  += 3 * floatSize;

                    vmax[0] = Math.Max(vmax[0], v.Position.X);
                    vmax[1] = Math.Max(vmax[1], v.Position.Y);
                    vmax[2] = Math.Max(vmax[2], v.Position.Z);
                    vmin[0] = Math.Min(vmin[0], v.Position.X);
                    vmin[1] = Math.Min(vmin[1], v.Position.Y);
                    vmin[2] = Math.Min(vmin[2], v.Position.Z);

                    nmax[0] = Math.Max(nmax[0], n.X);
                    nmax[1] = Math.Max(nmax[1], n.Y);
                    nmax[2] = Math.Max(nmax[2], n.Z);
                    nmin[0] = Math.Min(nmin[0], n.X);
                    nmin[1] = Math.Min(nmin[1], n.Y);
                    nmin[2] = Math.Min(nmin[2], n.Z);

                    uvmax[0] = Math.Max(uvmax[0], uu);
                    uvmax[1] = Math.Max(uvmax[1], vv);
                    uvmin[0] = Math.Min(uvmin[0], uu);
                    uvmin[1] = Math.Min(uvmin[1], vv);
                }

                for (var k = 0; k < tess.Elements.Length; k++)
                {
                    var t     = tess.Elements[k];
                    var index = (ushort)(t + iCursor);
                    System.Buffer.BlockCopy(BitConverter.GetBytes(index), 0, indexBuffer, ii, ushortSize);
                    imax = Math.Max(imax, index);
                    imin = Math.Min(imin, index);
                    ii  += ushortSize;
                }

                iCursor = imax + 1;
            }
        }
Example #8
0
        /// <summary>
        /// Triangulate this csg and pack the triangulated data into buffers
        /// appropriate for use with gltf.
        /// </summary>
        internal static void Tessellate(this Csg.Solid csg, out byte[] vertexBuffer,
                                        out byte[] indexBuffer, out byte[] normalBuffer, out byte[] colorBuffer, out byte[] uvBuffer,
                                        out double[] vmax, out double[] vmin, out double[] nmin, out double[] nmax,
                                        out float[] cmin, out float[] cmax, out ushort imin, out ushort imax, out double[] uvmin, out double[] uvmax)
        {
            var tessellations = new Tess[csg.Polygons.Count];

            var fi = 0;

            foreach (var p in csg.Polygons)
            {
                var tess = new Tess();
                tess.NoEmptyPolygons = true;
                tess.AddContour(p.Vertices.ToContourVertices());

                tess.Tessellate(WindingRule.Positive, LibTessDotNet.Double.ElementType.Polygons, 3);
                tessellations[fi] = tess;
                fi++;
            }

            var floatSize  = sizeof(float);
            var ushortSize = sizeof(ushort);

            var vertexCount = tessellations.Sum(t => t.VertexCount);
            var indexCount  = tessellations.Sum(t => t.Elements.Length);

            vertexBuffer = new byte[vertexCount * floatSize * 3];
            normalBuffer = new byte[vertexCount * floatSize * 3];
            indexBuffer  = new byte[indexCount * ushortSize];
            uvBuffer     = new byte[vertexCount * floatSize * 2];

            // Vertex colors are not used in this context currently.
            colorBuffer = new byte[0];
            cmin        = new float[0];
            cmax        = new float[0];

            vmax = new double[3] {
                double.MinValue, double.MinValue, double.MinValue
            };
            vmin = new double[3] {
                double.MaxValue, double.MaxValue, double.MaxValue
            };
            nmin = new double[3] {
                double.MaxValue, double.MaxValue, double.MaxValue
            };
            nmax = new double[3] {
                double.MinValue, double.MinValue, double.MinValue
            };

            // TODO: Set this properly when solids get UV coordinates.
            uvmin = new double[2] {
                0, 0
            };
            uvmax = new double[2] {
                0, 0
            };

            imax = ushort.MinValue;
            imin = ushort.MaxValue;

            var vi  = 0;
            var ii  = 0;
            var uvi = 0;

            var iCursor = 0;

            for (var i = 0; i < tessellations.Length; i++)
            {
                var tess = tessellations[i];
                if (tess.ElementCount == 0)
                {
                    continue;
                }
                var a = tess.Vertices[tess.Elements[0]].Position.ToVector3();
                var b = tess.Vertices[tess.Elements[1]].Position.ToVector3();
                var c = tess.Vertices[tess.Elements[2]].Position.ToVector3();
                var n = (b - a).Cross(c - a).Unitized();

                for (var j = 0; j < tess.Vertices.Length; j++)
                {
                    var v = tess.Vertices[j];

                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)v.Position.X), 0, vertexBuffer, vi, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)v.Position.Y), 0, vertexBuffer, vi + floatSize, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)v.Position.Z), 0, vertexBuffer, vi + 2 * floatSize, floatSize);

                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)n.X), 0, normalBuffer, vi, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)n.Y), 0, normalBuffer, vi + floatSize, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((float)n.Z), 0, normalBuffer, vi + 2 * floatSize, floatSize);

                    // TODO: Update Solids to use something other than UV = {0,0}.
                    System.Buffer.BlockCopy(BitConverter.GetBytes(0f), 0, uvBuffer, uvi, floatSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes(0f), 0, uvBuffer, uvi + floatSize, floatSize);

                    uvi += 2 * floatSize;
                    vi  += 3 * floatSize;

                    vmax[0] = Math.Max(vmax[0], v.Position.X);
                    vmax[1] = Math.Max(vmax[1], v.Position.Y);
                    vmax[2] = Math.Max(vmax[2], v.Position.Z);
                    vmin[0] = Math.Min(vmin[0], v.Position.X);
                    vmin[1] = Math.Min(vmin[1], v.Position.Y);
                    vmin[2] = Math.Min(vmin[2], v.Position.Z);

                    nmax[0] = Math.Max(nmax[0], n.X);
                    nmax[1] = Math.Max(nmax[1], n.Y);
                    nmax[2] = Math.Max(nmax[2], n.Z);
                    nmin[0] = Math.Min(nmin[0], n.X);
                    nmin[1] = Math.Min(nmin[1], n.Y);
                    nmin[2] = Math.Min(nmin[2], n.Z);

                    // uvmax[0] = Math.Max(uvmax[0], 0);
                    // uvmax[1] = Math.Max(uvmax[1], 0);
                    // uvmin[0] = Math.Min(uvmin[0], 0);
                    // uvmin[1] = Math.Min(uvmin[1], 0);
                }

                for (var k = 0; k < tess.Elements.Length; k++)
                {
                    var t     = tess.Elements[k];
                    var index = (ushort)(t + iCursor);
                    System.Buffer.BlockCopy(BitConverter.GetBytes(index), 0, indexBuffer, ii, ushortSize);
                    imax = Math.Max(imax, index);
                    imin = Math.Min(imin, index);
                    ii  += ushortSize;
                }

                iCursor = imax + 1;
            }
        }
Example #9
0
 /// <summary>
 /// Construct a CsgTessellationTargetProvider.
 /// </summary>
 /// <param name="csg"></param>
 public CsgTessellationTargetProvider(Csg.Solid csg)
 {
     this.csg = csg;
 }
Example #10
0
 /// <summary>
 /// Triangulate this csg and pack the triangulated data into buffers
 /// appropriate for use with gltf.
 /// </summary>
 internal static GraphicsBuffers Tessellate(this Csg.Solid csg,
                                            bool mergeVertices = false,
                                            Func <(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null)